public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-5685] c++: Implement LWG3396 Clarify point of reference for source_location::current() [PR80780, PR93093]
@ 2020-12-02 23:41 Jakub Jelinek
  0 siblings, 0 replies; only message in thread
From: Jakub Jelinek @ 2020-12-02 23:41 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:ba3d8dffcc1c23b30370ab24fc20d09cff005d7b

commit r11-5685-gba3d8dffcc1c23b30370ab24fc20d09cff005d7b
Author: Jakub Jelinek <jakub@redhat.com>
Date:   Thu Dec 3 00:25:51 2020 +0100

    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  <jakub@redhat.com>
    
            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) <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.

Diff:
---
 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(-)

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<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;
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 <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 ();
+}
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 <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 ();
+}
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 <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 ();
+}
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 <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 ();
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-12-02 23:41 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-02 23:41 [gcc r11-5685] c++: Implement LWG3396 Clarify point of reference for source_location::current() [PR80780, PR93093] Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).