On Wed, Dec 02, 2020 at 10:42:51AM -0500, Jason Merrill via Gcc-patches wrote: > 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:
Oops, I'm terribly sorry, didn't scroll down beyond the Please. Implemented now yours and Marek's suggestions. > > + > > + 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. The point was possible inline namespaces between std and source_location. But Marek's suggestion to use decl_in_std_namespace_p for that looks right. Tested on x86_64-linux, ok for trunk if it passes full bootstrap/regtest on x86_64-linux and i686-linux? 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 (immediate_invocation_p): New function. (build_over_call): Use it. * 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-02 18:05:46.497197545 +0100 +++ gcc/cp/cp-tree.h 2020-12-02 18:07:49.395815083 +0100 @@ -7422,6 +7422,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-02 18:05:46.498197533 +0100 +++ gcc/cp/tree.c 2020-12-02 18:27:55.711332497 +0100 @@ -2993,6 +2993,32 @@ 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)) + || !id_equal (DECL_NAME (fndecl), "current")) + return false; + + tree source_location = DECL_CONTEXT (fndecl); + if (TYPE_NAME (source_location) == NULL_TREE + || TREE_CODE (TYPE_NAME (source_location)) != TYPE_DECL + || TYPE_IDENTIFIER (source_location) == NULL_TREE + || !id_equal (TYPE_IDENTIFIER (source_location), + "source_location") + || !decl_in_std_namespace_p (TYPE_NAME (source_location))) + return false; + + return true; +} + struct bot_data { splay_tree target_remap; --- gcc/cp/call.c.jj 2020-12-02 14:42:46.533121244 +0100 +++ gcc/cp/call.c 2020-12-02 18:27:18.607747289 +0100 @@ -8540,6 +8540,25 @@ build_trivial_dtor_call (tree instance, instance, clobber); } +/* Return true if a call to FN with number of arguments NARGS + is an immediate invocation. */ + +static bool +immediate_invocation_p (tree fn, int nargs) +{ + return (TREE_CODE (fn) == FUNCTION_DECL + && DECL_IMMEDIATE_FUNCTION_P (fn) + && cp_unevaluated_operand == 0 + && (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) + /* As an exception, we defer std::source_location::current () + invocations until genericization because LWG3396 mandates + special behavior for it. */ + && (nargs > 1 || !source_location_current_p (fn))); +} + /* Subroutine of the various build_*_call functions. Overload resolution has chosen a winning candidate CAND; build up a CALL_EXPR accordingly. ARGS is a TREE_LIST of the unconverted arguments to the call. FLAGS is a @@ -8607,13 +8626,7 @@ build_over_call (struct z_candidate *can addr, nargs, argarray); if (TREE_THIS_VOLATILE (fn) && cfun) current_function_returns_abnormally = 1; - if (TREE_CODE (fn) == FUNCTION_DECL - && DECL_IMMEDIATE_FUNCTION_P (fn) - && cp_unevaluated_operand == 0 - && (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)) + if (immediate_invocation_p (fn, nargs)) { tree obj_arg = NULL_TREE, exprimm = expr; if (DECL_CONSTRUCTOR_P (fn)) @@ -9251,13 +9264,7 @@ build_over_call (struct z_candidate *can if (TREE_CODE (fn) == ADDR_EXPR) { tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0)); - if (TREE_CODE (fndecl) == FUNCTION_DECL - && DECL_IMMEDIATE_FUNCTION_P (fndecl) - && cp_unevaluated_operand == 0 - && (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)) + if (immediate_invocation_p (fndecl, nargs)) { tree obj_arg = NULL_TREE; if (DECL_CONSTRUCTOR_P (fndecl)) --- gcc/cp/constexpr.c.jj 2020-12-02 14:42:46.551121032 +0100 +++ gcc/cp/constexpr.c 2020-12-02 18:13:16.405150137 +0100 @@ -1332,7 +1332,12 @@ 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)); + { + temp_override<tree> ovr (current_function_decl); + if (ctx->call && ctx->call->fundef) + current_function_decl = ctx->call->fundef->decl; + return fold_builtin_source_location (EXPR_LOCATION (t)); + } int strops = 0; int strret = 0; --- gcc/cp/cp-gimplify.c.jj 2020-12-02 14:42:46.568120832 +0100 +++ gcc/cp/cp-gimplify.c 2020-12-02 18:25:30.380955596 +0100 @@ -1374,6 +1374,14 @@ cp_genericize_r (tree *stmt_p, int *walk break; } + if (tree fndecl = cp_get_callee_fndecl (stmt)) + if (DECL_IMMEDIATE_FUNCTION_P (fndecl)) + { + gcc_assert (source_location_current_p (fndecl)); + *stmt_p = cxx_constant_value (stmt); + 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 18:07:49.398815049 +0100 +++ gcc/testsuite/g++.dg/cpp2a/srcloc15.C 2020-12-02 18:07:49.398815049 +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 18:07:49.398815049 +0100 +++ gcc/testsuite/g++.dg/cpp2a/srcloc16.C 2020-12-02 18:07:49.398815049 +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 18:07:49.398815049 +0100 +++ gcc/testsuite/g++.dg/cpp2a/srcloc17.C 2020-12-02 18:07:49.398815049 +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 18:07:49.398815049 +0100 +++ gcc/testsuite/g++.dg/cpp2a/srcloc18.C 2020-12-02 18:07:49.398815049 +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