On 12/2/20 7:13 AM, Jakub Jelinek wrote:
On Tue, Dec 01, 2020 at 04:05:22PM -0500, Jason Merrill via Gcc-patches wrote:
Or simpler might be to always defer immediate evaluation of
source_location::current() until genericize time.
That works.
I had to change constexpr.c too so that it temporarily adjusts
current_function_decl from the constexpr evaluation context, but we do the
same already from __builtin_FUNCTION ().
Tested on x86_64-linux and i686-linux, ok for trunk if it passes full
bootstrap/regtest?
For the non-std::source_location::current() immediate evaluation, I'll just
file a PR and put there all details.
Looks like you missed my minor comments within the earlier patch; I
should have mentioned that there were some. Repeating them below:
2020-12-02 Jakub Jelinek <ja...@redhat.com>
LWG3396 Clarify point of reference for source_location::current()
PR c++/80780
PR c++/93093
* cp-tree.h (source_location_current_p): Declare.
* tree.c (source_location_current_p): New function.
* call.c (build_over_call): Don't evaluate calls to immediate
function std::source_location::current ().
* constexpr.c (cxx_eval_builtin_function_call): Temporarily set
current_function_decl from ctx->call->fundef->decl if any.
* cp-gimplify.c (cp_genericize_r) <case CALL_EXPR>: Fold calls
to immediate function std::source_location::current ().
* g++.dg/cpp2a/srcloc15.C: New test.
* g++.dg/cpp2a/srcloc16.C: New test.
* g++.dg/cpp2a/srcloc17.C: New test.
* g++.dg/cpp2a/srcloc18.C: New test.
--- gcc/cp/cp-tree.h.jj 2020-12-01 16:13:01.716818440 +0100
+++ gcc/cp/cp-tree.h 2020-12-02 11:49:25.984376796 +0100
@@ -7413,6 +7413,7 @@ extern tree bind_template_template_parm
extern tree array_type_nelts_total (tree);
extern tree array_type_nelts_top (tree);
extern bool array_of_unknown_bound_p (const_tree);
+extern bool source_location_current_p (tree);
extern tree break_out_target_exprs (tree, bool = false);
extern tree build_ctor_subob_ref (tree, tree, tree);
extern tree replace_placeholders (tree, tree, bool * = NULL);
--- gcc/cp/tree.c.jj 2020-12-01 16:13:01.717818429 +0100
+++ gcc/cp/tree.c 2020-12-02 11:53:19.032696933 +0100
@@ -2968,6 +2968,37 @@ array_type_nelts_total (tree type)
return sz;
}
+/* Return true if FNDECL is std::source_location::current () method. */
+
+bool
+source_location_current_p (tree fndecl)
+{
+ gcc_checking_assert (TREE_CODE (fndecl) == FUNCTION_DECL
+ && DECL_IMMEDIATE_FUNCTION_P (fndecl));
+ if (DECL_NAME (fndecl) == NULL_TREE
+ || TREE_CODE (TREE_TYPE (fndecl)) != FUNCTION_TYPE
+ || TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != RECORD_TYPE
+ || DECL_CONTEXT (fndecl) != TREE_TYPE (TREE_TYPE (fndecl)))
+ return false;
+
+ if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "current") != 0)
You can use id_equal for comparing identifiers to strings.
+ return false;
+
+ tree source_location = DECL_CONTEXT (fndecl);
+ if (TYPE_NAME (source_location) == NULL_TREE
+ || TREE_CODE (TYPE_NAME (source_location)) != TYPE_DECL
+ || DECL_NAME (TYPE_NAME (source_location)) == NULL_TREE
+ || strcmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (source_location))),
+ "source_location") != 0)
+ return false;
TYPE_IDENTIFIER would also make this shorter.
+
+ tree decl
+ = lookup_qualified_name (std_node,
+ DECL_NAME (TYPE_NAME (source_location)),
+ LOOK_want::TYPE, tf_none);
+ return TYPE_NAME (source_location) == decl;
Why not TYPE_CONTEXT (source_location) == std_node? I don't think we
need to do name lookup here.
+}
+
struct bot_data
{
splay_tree target_remap;
--- gcc/cp/call.c.jj 2020-12-01 16:13:01.714818462 +0100
+++ gcc/cp/call.c 2020-12-02 12:25:25.379566607 +0100
@@ -8613,7 +8613,8 @@ build_over_call (struct z_candidate *can
&& (current_function_decl == NULL_TREE
|| !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
&& (current_binding_level->kind != sk_function_parms
- || !current_binding_level->immediate_fn_ctx_p))
+ || !current_binding_level->immediate_fn_ctx_p)
+ && (nargs > 1 || !source_location_current_p (fn)))
{
tree obj_arg = NULL_TREE, exprimm = expr;
if (DECL_CONSTRUCTOR_P (fn))
@@ -9257,7 +9258,8 @@ build_over_call (struct z_candidate *can
&& (current_function_decl == NULL_TREE
|| !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
&& (current_binding_level->kind != sk_function_parms
- || !current_binding_level->immediate_fn_ctx_p))
+ || !current_binding_level->immediate_fn_ctx_p)
+ && (nargs > 1 || !source_location_current_p (fndecl)))
Please factor this condition out into a separate function.
{
tree obj_arg = NULL_TREE;
if (DECL_CONSTRUCTOR_P (fndecl))
--- gcc/cp/constexpr.c.jj 2020-11-26 16:22:24.250407040 +0100
+++ gcc/cp/constexpr.c 2020-12-02 12:35:11.458852298 +0100
@@ -1332,7 +1332,14 @@ cxx_eval_builtin_function_call (const co
}
if (fndecl_built_in_p (fun, CP_BUILT_IN_SOURCE_LOCATION, BUILT_IN_FRONTEND))
- return fold_builtin_source_location (EXPR_LOCATION (t));
+ {
+ tree save_cur_fn = current_function_decl;
+ if (ctx->call && ctx->call->fundef)
+ current_function_decl = ctx->call->fundef->decl;
+ t = fold_builtin_source_location (EXPR_LOCATION (t));
+ current_function_decl = save_cur_fn;
+ return t;
+ }
int strops = 0;
int strret = 0;
--- gcc/cp/cp-gimplify.c.jj 2020-11-26 16:22:24.250407040 +0100
+++ gcc/cp/cp-gimplify.c 2020-12-02 12:26:21.596922205 +0100
@@ -1374,6 +1374,15 @@ cp_genericize_r (tree *stmt_p, int *walk
break;
}
+ if (call_expr_nargs (stmt) == 0)
+ if (tree fndecl = cp_get_callee_fndecl (stmt))
+ if (DECL_IMMEDIATE_FUNCTION_P (fndecl)
+ && source_location_current_p (fndecl))
I'd think we would want to do this for any immediate function call that
happens to make it this far. Maybe make the source_location_current_p
test an assert instead of a condition?
+ {
+ *stmt_p = cxx_constant_value (stmt, NULL_TREE);
+ break;
+ }
+
if (!wtd->no_sanitize_p
&& sanitize_flags_p ((SANITIZE_NULL
| SANITIZE_ALIGNMENT | SANITIZE_VPTR)))
--- gcc/testsuite/g++.dg/cpp2a/srcloc15.C.jj 2020-12-02 11:49:25.987376761
+0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc15.C 2020-12-02 12:48:20.592828477
+0100
@@ -0,0 +1,119 @@
+// { dg-do run { target c++20 } }
+
+namespace std {
+ struct source_location {
+ struct __impl {
+ const char *_M_file_name;
+ const char *_M_function_name;
+ unsigned int _M_line, _M_column;
+ };
+ const __impl *__ptr;
+ constexpr source_location () : __ptr (nullptr) {}
+ static consteval source_location
+ current (const void *__p = __builtin_source_location ()) {
+ source_location __ret;
+ __ret.__ptr = static_cast <const __impl *> (__p);
+ return __ret;
+ }
+ constexpr const char *file_name () const {
+ return __ptr ? __ptr->_M_file_name : "";
+ }
+ constexpr const char *function_name () const {
+ return __ptr ? __ptr->_M_function_name : "";
+ }
+ constexpr unsigned line () const {
+ return __ptr ? __ptr->_M_line : 0;
+ }
+ constexpr unsigned column () const {
+ return __ptr ? __ptr->_M_column : 0;
+ }
+ };
+}
+
+using namespace std;
+
+constexpr source_location
+foo (const source_location x = source_location::current ())
+{
+ return x;
+}
+
+struct S {
+ const char *func;
+ unsigned line = 0;
+ source_location loc = source_location::current ();
+
+ constexpr S (int l, source_location loc = source_location::current ())
+ : func(__FUNCTION__), line(l), loc(loc)
+ {}
+
+ constexpr S (double)
+ : func(__FUNCTION__), line(__LINE__)
+ // ^ column 38
+ {}
+};
+
+constexpr bool
+cmp (const char *p, const char *q)
+{
+ for (; *p && *q; p++, q++)
+ if (*p != *q)
+ return true;
+ return *p || *q;
+}
+
+constexpr bool
+bar ()
+{
+ int line = __LINE__;
+ source_location a = foo ();
+ source_location b = source_location::current ();
+ source_location c = foo ();
+ // ^ column 28
+ // ^ column 49
+ const source_location *d[3] = { &a, &b, &c };
+ const char *file1 = __FILE__;
+ const char *function1 = __FUNCTION__;
+ for (int j = 0; j < 3; j++)
+ {
+ int i= 0;
+ if (cmp (d[j]->file_name (), file1))
+ return false;
+ if (cmp (d[j]->function_name (), function1))
+ return false;
+ if (d[j]->line () != line + j + 1)
+ return false;
+ if (d[j]->column () != (j == 1 ? 49 : 28))
+ return false;
+ }
+
+ S e = __LINE__;
+ // ^ column 9
+ S f = 1.0;
+ if (cmp (e.loc.file_name (), file1))
+ return false;
+ if (cmp (f.loc.file_name (), file1))
+ return false;
+ if (cmp (e.loc.function_name (), function1))
+ return false;
+ if (cmp (f.loc.function_name (), f.func))
+ return false;
+ if (e.loc.line () != e.line)
+ return false;
+ if (f.loc.line () != f.line)
+ return false;
+ if (e.loc.column () != 9)
+ return false;
+ if (f.loc.column () != 38)
+ return false;
+ return true;
+}
+
+static_assert (bar ());
+
+int
+main ()
+{
+ if (!bar ())
+ __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp2a/srcloc16.C.jj 2020-12-02 11:49:25.987376761
+0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc16.C 2020-12-02 12:27:30.649130680
+0100
@@ -0,0 +1,97 @@
+// { dg-do run { target c++20 } }
+
+namespace std {
+ struct source_location {
+ struct __impl {
+ const char *_M_file_name;
+ const char *_M_function_name;
+ unsigned int _M_line, _M_column;
+ };
+ const __impl *__ptr;
+ constexpr source_location () : __ptr (nullptr) {}
+ static consteval source_location
+ current (const void *__p = __builtin_source_location ()) {
+ source_location __ret;
+ __ret.__ptr = static_cast <const __impl *> (__p);
+ return __ret;
+ }
+ constexpr const char *file_name () const {
+ return __ptr ? __ptr->_M_file_name : "";
+ }
+ constexpr const char *function_name () const {
+ return __ptr ? __ptr->_M_function_name : "";
+ }
+ constexpr unsigned line () const {
+ return __ptr ? __ptr->_M_line : 0;
+ }
+ constexpr unsigned column () const {
+ return __ptr ? __ptr->_M_column : 0;
+ }
+ };
+}
+
+using namespace std;
+
+struct S
+{
+ source_location a = source_location::current ();
+ source_location b = source_location::current ();
+ source_location c = source_location ();
+ constexpr S () { c = source_location::current (); }
+};
+
+struct T
+{
+ int t;
+ source_location u = source_location::current ();
+ int v = __builtin_LINE ();
+};
+
+constexpr S s;
+constexpr T t = { 1 };
+
+constexpr bool
+cmp (const char *p, const char *q)
+{
+ for (; *p && *q; p++, q++)
+ if (*p != *q)
+ return true;
+ return *p || *q;
+}
+
+constexpr bool
+foo ()
+{
+ T u = { 2 };
+ source_location v = source_location::current ();
+ if (cmp (s.a.file_name (), s.c.file_name ())
+ || cmp (s.b.file_name (), s.c.file_name ())
+ || cmp (t.u.file_name (), s.c.file_name ())
+ || cmp (u.u.file_name (), s.c.file_name ())
+ || cmp (v.file_name (), s.c.file_name ())
+ || cmp (s.a.function_name (), s.c.function_name ())
+ || cmp (s.b.function_name (), s.c.function_name ())
+ || cmp (t.u.function_name (), "")
+ || cmp (u.u.function_name (), v.function_name ())
+ || s.a.line () != s.c.line ()
+ || s.b.line () != s.c.line ()
+ || t.u.line () != t.v
+ || u.u.line () + 1 != v.line ()
+ || s.a.column () != 18
+ || s.b.column () != 18
+ || s.c.column () != 50
+ || t.u.column () != 21
+ || u.u.column () != 13
+ || v.column () != 49)
+ return false;
+ return true;
+}
+
+static_assert (foo ());
+
+int
+main ()
+{
+ if (!foo ())
+ __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp2a/srcloc17.C.jj 2020-12-02 12:50:12.956543583
+0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc17.C 2020-12-02 12:58:26.113904280
+0100
@@ -0,0 +1,122 @@
+// { dg-do run { target c++20 } }
+
+namespace std {
+ struct source_location {
+ struct __impl {
+ const char *_M_file_name;
+ const char *_M_function_name;
+ unsigned int _M_line, _M_column;
+ };
+ const __impl *__ptr;
+ constexpr source_location () : __ptr (nullptr) {}
+ static consteval source_location
+ current (const void *__p = __builtin_source_location ()) {
+ source_location __ret;
+ __ret.__ptr = static_cast <const __impl *> (__p);
+ return __ret;
+ }
+ constexpr const char *file_name () const {
+ return __ptr ? __ptr->_M_file_name : "";
+ }
+ constexpr const char *function_name () const {
+ return __ptr ? __ptr->_M_function_name : "";
+ }
+ constexpr unsigned line () const {
+ return __ptr ? __ptr->_M_line : 0;
+ }
+ constexpr unsigned column () const {
+ return __ptr ? __ptr->_M_column : 0;
+ }
+ };
+}
+
+using namespace std;
+
+template <int N>
+constexpr source_location
+foo (const source_location x = source_location::current ())
+{
+ return x;
+}
+
+template <int N>
+struct S {
+ const char *func;
+ unsigned line = 0;
+ source_location loc = source_location::current ();
+
+ constexpr S (int l, source_location loc = source_location::current ())
+ : func(__FUNCTION__), line(l), loc(loc)
+ {}
+
+ constexpr S (double)
+ : func(__FUNCTION__), line(__LINE__)
+ // ^ column 38
+ {}
+};
+
+constexpr bool
+cmp (const char *p, const char *q)
+{
+ for (; *p && *q; p++, q++)
+ if (*p != *q)
+ return true;
+ return *p || *q;
+}
+
+template <int N>
+constexpr bool
+bar ()
+{
+ int line = __LINE__;
+ source_location a = foo<N> ();
+ source_location b = source_location::current ();
+ source_location c = foo<N> ();
+ // ^ column 30
+ // ^ column 48
+ const source_location *d[3] = { &a, &b, &c };
+ const char *file1 = __FILE__;
+ const char *function1 = b.function_name ();
+ for (int j = 0; j < 3; j++)
+ {
+ int i= 0;
+ if (cmp (d[j]->file_name (), file1))
+ return false;
+ if (cmp (d[j]->function_name (), function1))
+ return false;
+ if (d[j]->line () != line + j + 1)
+ return false;
+ if (d[j]->column () != (j == 1 ? 48 : 30))
+ return false;
+ }
+
+ S<N> e = __LINE__;
+ // ^ column 8
+ S<N> f = 1.0;
+ if (cmp (e.loc.file_name (), file1))
+ return false;
+ if (cmp (f.loc.file_name (), file1))
+ return false;
+ if (cmp (e.loc.function_name (), function1))
+ return false;
+ if (cmp (f.loc.function_name (), f.func))
+ return false;
+ if (e.loc.line () != e.line)
+ return false;
+ if (f.loc.line () != f.line)
+ return false;
+ if (e.loc.column () != 8)
+ return false;
+ if (f.loc.column () != 38)
+ return false;
+ return true;
+}
+
+static_assert (bar<0> ());
+
+int
+main ()
+{
+ if (!bar<0> ())
+ __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp2a/srcloc18.C.jj 2020-12-02 12:52:10.990193861
+0100
+++ gcc/testsuite/g++.dg/cpp2a/srcloc18.C 2020-12-02 13:01:26.128844951
+0100
@@ -0,0 +1,100 @@
+// { dg-do run { target c++20 } }
+
+namespace std {
+ struct source_location {
+ struct __impl {
+ const char *_M_file_name;
+ const char *_M_function_name;
+ unsigned int _M_line, _M_column;
+ };
+ const __impl *__ptr;
+ constexpr source_location () : __ptr (nullptr) {}
+ static consteval source_location
+ current (const void *__p = __builtin_source_location ()) {
+ source_location __ret;
+ __ret.__ptr = static_cast <const __impl *> (__p);
+ return __ret;
+ }
+ constexpr const char *file_name () const {
+ return __ptr ? __ptr->_M_file_name : "";
+ }
+ constexpr const char *function_name () const {
+ return __ptr ? __ptr->_M_function_name : "";
+ }
+ constexpr unsigned line () const {
+ return __ptr ? __ptr->_M_line : 0;
+ }
+ constexpr unsigned column () const {
+ return __ptr ? __ptr->_M_column : 0;
+ }
+ };
+}
+
+using namespace std;
+
+template <int N>
+struct S
+{
+ source_location a = source_location::current ();
+ source_location b = source_location::current ();
+ source_location c = source_location ();
+ constexpr S () { c = source_location::current (); }
+};
+
+template <int N>
+struct T
+{
+ int t;
+ source_location u = source_location::current ();
+ int v = __builtin_LINE ();
+};
+
+constexpr S<0> s;
+constexpr T<0> t = { 1 };
+
+constexpr bool
+cmp (const char *p, const char *q)
+{
+ for (; *p && *q; p++, q++)
+ if (*p != *q)
+ return true;
+ return *p || *q;
+}
+
+template <int N>
+constexpr bool
+foo ()
+{
+ T<N> u = { 2 };
+ source_location v = source_location::current ();
+ if (cmp (s.a.file_name (), s.c.file_name ())
+ || cmp (s.b.file_name (), s.c.file_name ())
+ || cmp (t.u.file_name (), s.c.file_name ())
+ || cmp (u.u.file_name (), s.c.file_name ())
+ || cmp (v.file_name (), s.c.file_name ())
+ || cmp (s.a.function_name (), s.c.function_name ())
+ || cmp (s.b.function_name (), s.c.function_name ())
+ || cmp (t.u.function_name (), "")
+ || cmp (u.u.function_name (), v.function_name ())
+ || s.a.line () != s.c.line ()
+ || s.b.line () != s.c.line ()
+ || t.u.line () != t.v
+ || u.u.line () + 1 != v.line ()
+ || s.a.column () != 18
+ || s.b.column () != 18
+ || s.c.column () != 49
+ || t.u.column () != 24
+ || u.u.column () != 8
+ || v.column () != 48)
+ return false;
+ return true;
+}
+
+static_assert (foo<1> ());
+
+int
+main ()
+{
+ if (!foo<1> ())
+ __builtin_abort ();
+}
Jakub