From ba3d8dffcc1c23b30370ab24fc20d09cff005d7b Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Thu, 3 Dec 2020 00:25:51 +0100 Subject: [PATCH] c++: Implement LWG3396 Clarify point of reference for source_location::current() [PR80780, PR93093] While std::source_location::current () is static consteval source_location current() noexcept; in the standard, it also says with LWG3396: "Any call to current that appears as a default member initializer ([class.mem]), or as a subexpression thereof, should correspond to the location of the constructor definition or aggregate initialization that uses the default member initializer. Any call to current that appears as a default argument ([dcl.fct.default]), or as a subexpression thereof, should correspond to the location of the invocation of the function that uses the default argument ([expr.call])." so it must work as compiler magic rather than normal immediate functions, in particular we need to defer its evaluation when parsing default arguments or nsdmis. This patch actually defers evaluation of all the calls to std::source_location::current () until genericization (or constant expression evaluation when called from constant expression contexts). 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 (). 2020-12-03 Jakub Jelinek 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 to resolve LWG3396. * 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) : 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/call.c | 35 +++++--- gcc/cp/constexpr.c | 7 +- gcc/cp/cp-gimplify.c | 8 ++ gcc/cp/cp-tree.h | 1 + gcc/cp/tree.c | 26 ++++++ gcc/testsuite/g++.dg/cpp2a/srcloc15.C | 119 +++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/srcloc16.C | 97 ++++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/srcloc17.C | 122 ++++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/srcloc18.C | 100 +++++++++++++++++++++ 9 files changed, 500 insertions(+), 15 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/srcloc15.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/srcloc16.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/srcloc17.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/srcloc18.C diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 3a6ad1332a7..f1e0bcb796b 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -8540,6 +8540,25 @@ build_trivial_dtor_call (tree instance, bool no_ptr_deref) 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 *cand, int flags, tsubst_flags_t complain) 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 *cand, int flags, tsubst_flags_t complain) 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)) diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 9a1a1db1267..cd34e8e7eb4 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1332,7 +1332,12 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, } if (fndecl_built_in_p (fun, CP_BUILT_IN_SOURCE_LOCATION, BUILT_IN_FRONTEND)) - return fold_builtin_source_location (EXPR_LOCATION (t)); + { + temp_override 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; diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index 064a44ca3e5..84b8d16e742 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -1374,6 +1374,14 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) 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))) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 156bd6c8650..d77ec119be4 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7478,6 +7478,7 @@ extern tree bind_template_template_parm (tree, tree); 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); diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 5932777be04..4d9efb74744 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -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; diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc15.C b/gcc/testsuite/g++.dg/cpp2a/srcloc15.C new file mode 100644 index 00000000000..30e58451211 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/srcloc15.C @@ -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 (__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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc16.C b/gcc/testsuite/g++.dg/cpp2a/srcloc16.C new file mode 100644 index 00000000000..c8bd28169d2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/srcloc16.C @@ -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 (__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 (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc17.C b/gcc/testsuite/g++.dg/cpp2a/srcloc17.C new file mode 100644 index 00000000000..16704d0d33e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/srcloc17.C @@ -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 (__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 +constexpr source_location +foo (const source_location x = source_location::current ()) +{ + return x; +} + +template +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 +constexpr bool +bar () +{ + int line = __LINE__; + source_location a = foo (); + source_location b = source_location::current (); + source_location c = foo (); + // ^ 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 e = __LINE__; + // ^ column 8 + 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 () != 8) + return false; + if (f.loc.column () != 38) + return false; + return true; +} + +static_assert (bar<0> ()); + +int +main () +{ + if (!bar<0> ()) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/srcloc18.C b/gcc/testsuite/g++.dg/cpp2a/srcloc18.C new file mode 100644 index 00000000000..7e685ba93a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/srcloc18.C @@ -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 (__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 +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 +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 +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 () != 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 (); +} -- 2.30.2