public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Track lifetimes in constant evaluation [PR70331,...]
@ 2023-03-28 11:32 Nathaniel Shead
  2023-03-28 11:32 ` [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Nathaniel Shead
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Nathaniel Shead @ 2023-03-28 11:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: Nathaniel Shead

The first patch fixes PR70331, PR96630, and PR98675 regarding
constexpr evaluation not correctly diagnosing uses of dangling
references and pointers. The following two patches clean up and improve
some of the errors that are generated by this change, but aren't
strictly necessary.

This is my first change to the frontend, so any feedback is welcome. In
particular, I'm not sure if the way I went about the fix was correct:
for simplicity and efficiency's sake I added a new flag to
tree_decl_common, but this feels somewhat overkill for what is otherwise
a very locally useful property. 

Another option here would be to add a new lookup table to
constexpr_global_ctx which tracks what decls are in lifetime, but I
expect that to negatively impact performance. I haven't done any
benchmarks on this however.

Bootstrapped and regression tested on x86_64 linux.

---

Nathaniel Shead (3):
  c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675]
  c++: Improve constexpr error for dangling local variables
  c++: Improve location information in constexpr evaluation

 gcc/cp/constexpr.cc                           | 149 ++++++++++++------
 gcc/cp/module.cc                              |   2 +
 gcc/cp/semantics.cc                           |   5 +-
 gcc/cp/typeck.cc                              |   5 +-
 gcc/print-tree.cc                             |   4 +
 gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C  |  10 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C  |   2 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C  |   2 +-
 gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C  |   3 +-
 .../g++.dg/cpp1y/constexpr-lifetime1.C        |  14 ++
 .../g++.dg/cpp1y/constexpr-lifetime2.C        |  20 +++
 .../g++.dg/cpp1y/constexpr-lifetime3.C        |  13 ++
 .../g++.dg/cpp1y/constexpr-lifetime4.C        |  11 ++
 .../g++.dg/cpp1y/constexpr-lifetime5.C        |  11 ++
 gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C |   4 +-
 gcc/testsuite/g++.dg/cpp1y/pr68180.C          |   4 +-
 .../g++.dg/cpp1z/constexpr-lambda6.C          |   4 +-
 gcc/testsuite/g++.dg/cpp2a/bit-cast11.C       |  10 +-
 gcc/testsuite/g++.dg/cpp2a/bit-cast12.C       |  10 +-
 gcc/testsuite/g++.dg/cpp2a/bit-cast14.C       |  14 +-
 gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C  |   4 +-
 .../g++.dg/cpp2a/constexpr-dynamic17.C        |   5 +-
 gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C  |   5 +-
 gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C  |   6 +-
 gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C   |  10 +-
 gcc/testsuite/g++.dg/ext/constexpr-vla2.C     |   4 +-
 gcc/testsuite/g++.dg/ext/constexpr-vla3.C     |   4 +-
 gcc/testsuite/g++.dg/ubsan/pr63956.C          |   4 +-
 .../g++.dg/warn/Wreturn-local-addr-6.C        |   3 -
 gcc/tree-core.h                               |   5 +-
 gcc/tree.h                                    |   6 +
 .../25_algorithms/equal/constexpr_neg.cc      |   7 +-
 32 files changed, 247 insertions(+), 113 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C

-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675]
  2023-03-28 11:32 [PATCH 0/3] Track lifetimes in constant evaluation [PR70331,...] Nathaniel Shead
@ 2023-03-28 11:32 ` Nathaniel Shead
  2023-03-28 11:50   ` [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331, PR96630, PR98675] Jakub Jelinek
  2023-03-28 11:32 ` [PATCH 2/3] c++: Improve constexpr error for dangling local variables Nathaniel Shead
  2023-03-28 11:32 ` [PATCH 3/3] c++: Improve location information in constexpr evaluation Nathaniel Shead
  2 siblings, 1 reply; 6+ messages in thread
From: Nathaniel Shead @ 2023-03-28 11:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: Nathaniel Shead

This adds rudimentary lifetime tracking in C++ constexpr contexts,
allowing the compiler to report errors with using values after their
backing has gone out of scope. We don't yet handle other ways of ending
lifetimes (e.g. explicit destructor calls).

	PR c++/70331
	PR c++/96630
	PR c++/98675

gcc/cp/ChangeLog:

	* constexpr.cc (constexpr_global_ctx::put_value): Mark value as
        in lifetime.
        (constexpr_global_ctx::remove_value): Mark value as expired.
        (cxx_eval_call_expression): Remove comment that is no longer
        applicable.
	(non_const_var_error): Add check for expired values.
	(cxx_eval_constant_expression): Add checks for expired values.
        Forget local variables at end of bind expressions. Forget
        temporaries at end of cleanup points.
	* module.cc (trees_out::core_bools): Write out the new flag.
	(trees_in::core_bools): Read in the new flag.

gcc/ChangeLog:

	* tree-core.h (struct tree_decl_common): New flag to check if
        value lifetime has expired.
	* tree.h (DECL_EXPIRED): Access the new flag.
	* print-tree.cc (print_node): Print the new flag.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/constexpr-ice20.C: Update error raised by test.
	* g++.dg/cpp1y/constexpr-lifetime1.C: New test.
	* g++.dg/cpp1y/constexpr-lifetime2.C: New test.
	* g++.dg/cpp1y/constexpr-lifetime3.C: New test.
	* g++.dg/cpp1y/constexpr-lifetime4.C: New test.
	* g++.dg/cpp1y/constexpr-lifetime5.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
 gcc/cp/constexpr.cc                           | 66 +++++++++++++++----
 gcc/cp/module.cc                              |  2 +
 gcc/print-tree.cc                             |  4 ++
 gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C  |  2 +-
 .../g++.dg/cpp1y/constexpr-lifetime1.C        | 13 ++++
 .../g++.dg/cpp1y/constexpr-lifetime2.C        | 20 ++++++
 .../g++.dg/cpp1y/constexpr-lifetime3.C        | 13 ++++
 .../g++.dg/cpp1y/constexpr-lifetime4.C        | 11 ++++
 .../g++.dg/cpp1y/constexpr-lifetime5.C        | 11 ++++
 gcc/tree-core.h                               |  5 +-
 gcc/tree.h                                    |  6 ++
 11 files changed, 138 insertions(+), 15 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 3de60cfd0f8..28e3f891fb8 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -1185,10 +1185,17 @@ public:
   void put_value (tree t, tree v)
   {
     bool already_in_map = values.put (t, v);
+    if (!already_in_map && DECL_P (t))
+      DECL_EXPIRED (t) = false;
     if (!already_in_map && modifiable)
       modifiable->add (t);
   }
-  void remove_value (tree t) { values.remove (t); }
+  void remove_value (tree t)
+  {
+    if (DECL_P (t))
+      DECL_EXPIRED (t) = true;
+    values.remove (t);
+  }
 };
 
 /* Helper class for constexpr_global_ctx.  In some cases we want to avoid
@@ -3157,10 +3164,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  for (tree save_expr : save_exprs)
 	    ctx->global->remove_value (save_expr);
 
-	  /* Remove the parms/result from the values map.  Is it worth
-	     bothering to do this when the map itself is only live for
-	     one constexpr evaluation?  If so, maybe also clear out
-	     other vars from call, maybe in BIND_EXPR handling?  */
+	  /* Remove the parms/result from the values map.  */
 	  ctx->global->remove_value (res);
 	  for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
 	    ctx->global->remove_value (parm);
@@ -5708,6 +5712,13 @@ non_const_var_error (location_t loc, tree r, bool fundef_p)
 	inform (DECL_SOURCE_LOCATION (r), "allocated here");
       return;
     }
+  if (DECL_EXPIRED (r))
+    {
+      if (constexpr_error (loc, fundef_p, "accessing object outside its "
+			   "lifetime"))
+	inform (DECL_SOURCE_LOCATION (r), "declared here");
+      return;
+    }
   if (!constexpr_error (loc, fundef_p, "the value of %qD is not usable in "
 			"a constant expression", r))
     return;
@@ -6995,7 +7006,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       else
 	{
 	  if (!ctx->quiet)
-	    error ("%qE is not a constant expression", t);
+	    non_const_var_error (loc, t, /*fundef_p*/false);
 	  *non_constant_p = true;
 	}
       break;
@@ -7048,6 +7059,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  r = build_constructor (TREE_TYPE (t), NULL);
 	  TREE_CONSTANT (r) = true;
 	}
+      else if (DECL_EXPIRED (t))
+	{
+	  if (!ctx->quiet)
+	    non_const_var_error (loc, r, /*fundef_p*/false);
+	  *non_constant_p = true;
+	  break;
+	}
       else if (ctx->strict)
 	r = decl_really_constant_value (t, /*unshare_p=*/false);
       else
@@ -7093,7 +7111,15 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       else
 	{
 	  if (!ctx->quiet)
-	    error ("%qE is not a constant expression", t);
+	    {
+	      if (DECL_EXPIRED (r))
+		{
+		  error_at (loc, "accessing object outside its lifetime");
+		  inform (DECL_SOURCE_LOCATION (r), "declared here");
+		}
+	      else
+		error_at (loc, "%qE is not a constant expression", t);
+	    }
 	  *non_constant_p = true;
 	}
       break;
@@ -7315,17 +7341,28 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	auto_vec<tree, 2> cleanups;
 	vec<tree> *prev_cleanups = ctx->global->cleanups;
 	ctx->global->cleanups = &cleanups;
-	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
+
+	auto_vec<tree, 10> save_exprs;
+	constexpr_ctx new_ctx = *ctx;
+	new_ctx.save_exprs = &save_exprs;
+
+	r = cxx_eval_constant_expression (&new_ctx, TREE_OPERAND (t, 0),
 					  lval,
 					  non_constant_p, overflow_p,
 					  jump_target);
+
 	ctx->global->cleanups = prev_cleanups;
 	unsigned int i;
 	tree cleanup;
 	/* Evaluate the cleanups.  */
 	FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
-	  cxx_eval_constant_expression (ctx, cleanup, vc_discard,
+	  cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard,
 					non_constant_p, overflow_p);
+
+	/* Forget SAVE_EXPRs and TARGET_EXPRs created by this
+	   full-expression.  */
+	for (tree save_expr : save_exprs)
+	  ctx->global->remove_value (save_expr);
       }
       break;
 
@@ -7831,10 +7868,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 				      non_constant_p, overflow_p, jump_target);
 
     case BIND_EXPR:
-      return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
-					   lval,
-					   non_constant_p, overflow_p,
-					   jump_target);
+      r = cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
+					lval,
+					non_constant_p, overflow_p,
+					jump_target);
+      for (tree decl = BIND_EXPR_VARS (t); decl; decl = DECL_CHAIN (decl))
+	ctx->global->remove_value (decl);
+      return r;
 
     case PREINCREMENT_EXPR:
     case POSTINCREMENT_EXPR:
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index ac2fe66b080..e8b001424a0 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -5435,6 +5435,7 @@ trees_out::core_bools (tree t)
       WB (t->decl_common.decl_read_flag);
       WB (t->decl_common.decl_nonshareable_flag);
       WB (t->decl_common.decl_not_flexarray);
+      WB (t->decl_common.decl_expired_flag);
     }
 
   if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS))
@@ -5580,6 +5581,7 @@ trees_in::core_bools (tree t)
       RB (t->decl_common.decl_read_flag);
       RB (t->decl_common.decl_nonshareable_flag);
       RB (t->decl_common.decl_not_flexarray);
+      RB (t->decl_common.decl_expired_flag);
     }
 
   if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS))
diff --git a/gcc/print-tree.cc b/gcc/print-tree.cc
index 1f3afcbbc86..ce15f4508d7 100644
--- a/gcc/print-tree.cc
+++ b/gcc/print-tree.cc
@@ -495,6 +495,10 @@ print_node (FILE *file, const char *prefix, tree node, int indent,
 	  && DECL_BY_REFERENCE (node))
 	fputs (" passed-by-reference", file);
 
+      if ((code == VAR_DECL || code == PARM_DECL || code == RESULT_DECL)
+	  && DECL_EXPIRED (node))
+	fputs (" expired", file);
+
       if (CODE_CONTAINS_STRUCT (code, TS_DECL_WITH_VIS)  && DECL_DEFER_OUTPUT (node))
 	fputs (" defer-output", file);
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
index e2d4853a284..ebaa95e5324 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
@@ -4,4 +4,4 @@
 typedef bool (*Function)(int);
 constexpr bool check(int x, Function p) { return p(x); }  // { dg-message "in .constexpr. expansion of" }
 
-static_assert(check(2, check), "");  // { dg-error "conversion|constant|in .constexpr. expansion of" }
+static_assert(check(2, check), "");  // { dg-error "conversion|constant|lifetime|in .constexpr. expansion of" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C
new file mode 100644
index 00000000000..43aa7c974c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C
@@ -0,0 +1,13 @@
+// PR c++/96630
+// { dg-do compile { target c++14 } }
+
+struct S {
+  int x = 0;
+  constexpr const int& get() const { return x; }
+};
+
+constexpr const int& test() {
+  auto local = S{};  // { dg-message "note: declared here" }
+  return local.get();
+}
+constexpr int x = test();  // { dg-error "accessing object outside its lifetime" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C
new file mode 100644
index 00000000000..22cd919fcda
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C
@@ -0,0 +1,20 @@
+// PR c++/98675
+// { dg-do compile { target c++14 } }
+
+struct S {
+  int x = 0;
+  constexpr const int& get() const { return x; }
+};
+
+constexpr int error() {
+  const auto& local = S{}.get();  // { dg-message "note: declared here" }
+  return local;
+}
+constexpr int x = error();  // { dg-error "accessing object outside its lifetime" }
+
+constexpr int ok() {
+  // temporary should only be destroyed after end of full-expression
+  auto local = S{}.get();
+  return local;
+}
+constexpr int y = ok();
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C
new file mode 100644
index 00000000000..6329f8cf6c6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C
@@ -0,0 +1,13 @@
+// PR c++/70331
+// { dg-do compile { target c++14 } }
+
+constexpr int f(int i) {
+  int *p = &i;
+  if (i == 0) {
+    int j = 123;  // { dg-message "note: declared here" }
+    p = &j;
+  }
+  return *p;
+}
+
+constexpr int i = f(0);  // { dg-error "accessing object outside its lifetime" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C
new file mode 100644
index 00000000000..181a1201663
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++14 } }
+
+constexpr const double& test() {
+  const double& local = 3.0;  // { dg-message "note: declared here" }
+  return local;
+}
+
+static_assert(test() == 3.0, "");  // { dg-error "constant|accessing object outside its lifetime" }
+
+// no deference, shouldn't error
+static_assert((test(), true), "");
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C
new file mode 100644
index 00000000000..a4bc71d890a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++14 } }
+// { dg-options "-Wno-return-local-addr" }
+
+constexpr const int& id(int x) { return x; }
+
+constexpr bool test() {
+  const int& y = id(3);
+  return y == 3;
+}
+
+constexpr bool x = test();  // { dg-error "" }
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index fd2be57b78c..f814b427a38 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1834,7 +1834,10 @@ struct GTY(()) tree_decl_common {
   /* In FIELD_DECL, this is DECL_NOT_FLEXARRAY.  */
   unsigned int decl_not_flexarray : 1;
 
-  /* 13 bits unused.  */
+  /* In VAR_DECL, PARM_DECL, or RESULT_DECL, this is DECL_EXPIRED.  */
+  unsigned int decl_expired_flag : 1;
+
+  /* 12 bits unused.  */
 
   /* UID for points-to sets, stable over copying from inlining.  */
   unsigned int pt_uid;
diff --git a/gcc/tree.h b/gcc/tree.h
index 91375f9652f..194d49ed1f7 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2697,6 +2697,12 @@ extern tree vector_element_bits_tree (const_tree);
    ??? Need to figure out some way to check this isn't a PARM_DECL.  */
 #define DECL_INITIAL(NODE) (DECL_COMMON_CHECK (NODE)->decl_common.initial)
 
+/* Used in a VAR_DECL, PARM_DECL, or RESULT_DECL to indicate whether
+   this declaration is currently in lifetime for constant evaluation
+   purposes.  */
+#define DECL_EXPIRED(NODE) \
+  (DECL_COMMON_CHECK(NODE)->decl_common.decl_expired_flag)
+
 /* Holds the size of the datum, in bits, as a tree expression.
    Need not be constant and may be null.  May be less than TYPE_SIZE
    for a C++ FIELD_DECL representing a base class subobject with its
-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/3] c++: Improve constexpr error for dangling local variables
  2023-03-28 11:32 [PATCH 0/3] Track lifetimes in constant evaluation [PR70331,...] Nathaniel Shead
  2023-03-28 11:32 ` [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Nathaniel Shead
@ 2023-03-28 11:32 ` Nathaniel Shead
  2023-03-28 11:32 ` [PATCH 3/3] c++: Improve location information in constexpr evaluation Nathaniel Shead
  2 siblings, 0 replies; 6+ messages in thread
From: Nathaniel Shead @ 2023-03-28 11:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: Nathaniel Shead

Currently, when typeck discovers that a return statement will refer to a
local variable it rewrites to return a null pointer. This causes the
error messages for using the return value in a constant expression to be
unhelpful, especially for reference return values.

This patch removes this "optimisation". Relying on this raises a warning
by default and causes UB anyway, so there should be no issue in doing
so. We also suppress additional warnings from later passes that detect
this as a dangling pointer, since we've already indicated this anyway.

gcc/cp/ChangeLog:

	* semantics.cc (finish_return_stmt): Suppress dangling pointer
        reporting on return statement if already reported.
	* typeck.cc (check_return_expr): Don't set return expression to
        zero for dangling addresses.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp1y/constexpr-lifetime5.C: Test reported message is
        correct.
	* g++.dg/warn/Wreturn-local-addr-6.C: Remove check for return
        value optimisation.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
 gcc/cp/semantics.cc                              | 5 ++++-
 gcc/cp/typeck.cc                                 | 5 +++--
 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C | 4 ++--
 gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C | 3 ---
 4 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 87c2e8a7111..14b4b7f4ce1 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -1246,7 +1246,10 @@ finish_return_stmt (tree expr)
 
   r = build_stmt (input_location, RETURN_EXPR, expr);
   if (no_warning)
-    suppress_warning (r, OPT_Wreturn_type);
+    {
+      suppress_warning (r, OPT_Wreturn_type);
+      suppress_warning (r, OPT_Wdangling_pointer_);
+    }
   r = maybe_cleanup_point_expr_void (r);
   r = add_stmt (r);
 
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index afb956087ce..a7d642e2029 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -11235,8 +11235,9 @@ check_return_expr (tree retval, bool *no_warning)
       else if (!processing_template_decl
 	       && maybe_warn_about_returning_address_of_local (retval, loc)
 	       && INDIRECT_TYPE_P (valtype))
-	retval = build2 (COMPOUND_EXPR, TREE_TYPE (retval), retval,
-			 build_zero_cst (TREE_TYPE (retval)));
+	/* Suppress the Wdangling-pointer warning in the return statement
+	   that would otherwise occur.  */
+	*no_warning = true;
     }
 
   if (processing_template_decl)
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C
index a4bc71d890a..ad3ef579f63 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C
@@ -1,11 +1,11 @@
 // { dg-do compile { target c++14 } }
 // { dg-options "-Wno-return-local-addr" }
 
-constexpr const int& id(int x) { return x; }
+constexpr const int& id(int x) { return x; }  // { dg-message "note: declared here" }
 
 constexpr bool test() {
   const int& y = id(3);
   return y == 3;
 }
 
-constexpr bool x = test();  // { dg-error "" }
+constexpr bool x = test();  // { dg-error "accessing object outside its lifetime" }
diff --git a/gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C b/gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C
index fae8b7e766f..ec8e241d83e 100644
--- a/gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C
+++ b/gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C
@@ -24,6 +24,3 @@ return_addr_local_as_intref (void)
 
   return (const intptr_t&)a;   // { dg-warning "\\\[-Wreturn-local-addr]" } */
 }
-
-/* Verify that the return value has been replaced with zero:
-  { dg-final { scan-tree-dump-times "return 0;" 2 "optimized" } } */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 3/3] c++: Improve location information in constexpr evaluation
  2023-03-28 11:32 [PATCH 0/3] Track lifetimes in constant evaluation [PR70331,...] Nathaniel Shead
  2023-03-28 11:32 ` [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Nathaniel Shead
  2023-03-28 11:32 ` [PATCH 2/3] c++: Improve constexpr error for dangling local variables Nathaniel Shead
@ 2023-03-28 11:32 ` Nathaniel Shead
  2 siblings, 0 replies; 6+ messages in thread
From: Nathaniel Shead @ 2023-03-28 11:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: Nathaniel Shead

This patch caches the current expression's location information in the
constexpr_global_ctx struct, which allows subexpressions that have lost
location information to still provide accurate diagnostics. Also
rewrites a number of  'error' calls as 'error_at' to provide more
specific location information.

The primary effect of this change is that many errors within evaluation
of a constexpr function will now point at the offending expression (with
expansion tracing information) rather than just the outermost call.

gcc/cp/ChangeLog:

	* constexpr.cc (constexpr_global_ctx): New field for cached
	tree location, defaulting to input_location.
	(cxx_eval_internal_function): Fall back to ctx->global->loc
	rather than input_location.
	(modifying_const_object_error): Likewise.
	(cxx_eval_dynamic_cast_fn): Likewise.
	(eval_and_check_array_index): Likewise.
	(cxx_eval_array_reference): Likewise.
	(cxx_eval_bit_field_ref): Likewise.
	(cxx_eval_component_reference): Likewise.
	(cxx_eval_indirect_ref): Likewise.
	(cxx_eval_store_expression): Likewise.
	(cxx_eval_increment_expression): Likewise.
	(cxx_eval_loop_expr): Likewise.
	(cxx_eval_binary_expression): Likewise.
	(cxx_eval_constant_expression): Cache location of trees for use
        in errors, and prefer it instead of input_location.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/constexpr-48089.C: Updated diagnostic locations.
	* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
	* g++.dg/cpp0x/constexpr-ice20.C: Likewise.
	* g++.dg/cpp1y/constexpr-89481.C: Likewise.
	* g++.dg/cpp1y/constexpr-lifetime1.C: Likewise.
	* g++.dg/cpp1y/constexpr-lifetime2.C: Likewise.
	* g++.dg/cpp1y/constexpr-lifetime3.C: Likewise.
	* g++.dg/cpp1y/constexpr-lifetime4.C: Likewise.
	* g++.dg/cpp1y/constexpr-lifetime5.C: Likewise.
	* g++.dg/cpp1y/constexpr-union5.C: Likewise.
	* g++.dg/cpp1y/pr68180.C: Likewise.
	* g++.dg/cpp1z/constexpr-lambda6.C: Likewise.
	* g++.dg/cpp2a/bit-cast11.C: Likewise.
	* g++.dg/cpp2a/bit-cast12.C: Likewise.
	* g++.dg/cpp2a/bit-cast14.C: Likewise.
	* g++.dg/cpp2a/constexpr-98122.C: Likewise.
	* g++.dg/cpp2a/constexpr-dynamic17.C: Likewise.
	* g++.dg/cpp2a/constexpr-init1.C: Likewise.
	* g++.dg/cpp2a/constexpr-new12.C: Likewise.
	* g++.dg/cpp2a/constexpr-new3.C: Likewise.
	* g++.dg/ext/constexpr-vla2.C: Likewise.
	* g++.dg/ext/constexpr-vla3.C: Likewise.
	* g++.dg/ubsan/pr63956.C: Likewise.

libstdc++/ChangeLog:

	* testsuite/25_algorithms/equal/constexpr_neg.cc: Updated
	diagnostics locations.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
 gcc/cp/constexpr.cc                           | 83 +++++++++++--------
 gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C  | 10 +--
 gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C  |  2 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C  |  4 +-
 gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C  |  3 +-
 .../g++.dg/cpp1y/constexpr-lifetime1.C        |  1 +
 .../g++.dg/cpp1y/constexpr-lifetime2.C        |  4 +-
 .../g++.dg/cpp1y/constexpr-lifetime3.C        |  4 +-
 .../g++.dg/cpp1y/constexpr-lifetime4.C        |  2 +-
 .../g++.dg/cpp1y/constexpr-lifetime5.C        |  4 +-
 gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C |  4 +-
 gcc/testsuite/g++.dg/cpp1y/pr68180.C          |  4 +-
 .../g++.dg/cpp1z/constexpr-lambda6.C          |  4 +-
 gcc/testsuite/g++.dg/cpp2a/bit-cast11.C       | 10 +--
 gcc/testsuite/g++.dg/cpp2a/bit-cast12.C       | 10 +--
 gcc/testsuite/g++.dg/cpp2a/bit-cast14.C       | 14 ++--
 gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C  |  4 +-
 .../g++.dg/cpp2a/constexpr-dynamic17.C        |  5 +-
 gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C  |  5 +-
 gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C  |  6 +-
 gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C   | 10 +--
 gcc/testsuite/g++.dg/ext/constexpr-vla2.C     |  4 +-
 gcc/testsuite/g++.dg/ext/constexpr-vla3.C     |  4 +-
 gcc/testsuite/g++.dg/ubsan/pr63956.C          |  4 +-
 .../25_algorithms/equal/constexpr_neg.cc      |  7 +-
 25 files changed, 111 insertions(+), 101 deletions(-)

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 28e3f891fb8..31884220b4e 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -1165,10 +1165,12 @@ public:
   hash_set<tree> *modifiable;
   /* Number of heap VAR_DECL deallocations.  */
   unsigned heap_dealloc_count;
+  /* Current location in case subtree has no location information.  */
+  location_t loc;
   /* Constructor.  */
   constexpr_global_ctx ()
     : constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr),
-      heap_dealloc_count (0) {}
+      heap_dealloc_count (0), loc (input_location) {}
 
  tree get_value (tree t)
   {
@@ -2108,7 +2110,7 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
 
     default:
       if (!ctx->quiet)
-	error_at (cp_expr_loc_or_input_loc (t),
+	error_at (cp_expr_loc_or_loc (t, ctx->global->loc),
 		  "call to internal function %qE", t);
       *non_constant_p = true;
       return t;
@@ -2123,7 +2125,7 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
 
   if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
     {
-      location_t loc = cp_expr_loc_or_input_loc (t);
+      location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc);
       tree type = TREE_TYPE (TREE_TYPE (t));
       tree result = fold_binary_loc (loc, opcode, type,
 				     fold_convert_loc (loc, type, arg0),
@@ -2159,9 +2161,9 @@ clear_no_implicit_zero (tree ctor)
    EXPR is the MODIFY_EXPR expression performing the modification.  */
 
 static void
-modifying_const_object_error (tree expr, tree obj)
+modifying_const_object_error (const constexpr_ctx* ctx, tree expr, tree obj)
 {
-  location_t loc = cp_expr_loc_or_input_loc (expr);
+  location_t loc = cp_expr_loc_or_loc (expr, ctx->global->loc);
   auto_diagnostic_group d;
   error_at (loc, "modifying a const object %qE is not allowed in "
 	    "a constant expression", TREE_OPERAND (expr, 0));
@@ -2353,7 +2355,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
   tree obj = CALL_EXPR_ARG (call, 0);
   tree type = CALL_EXPR_ARG (call, 2);
   HOST_WIDE_INT hint = int_cst_value (CALL_EXPR_ARG (call, 3));
-  location_t loc = cp_expr_loc_or_input_loc (call);
+  location_t loc = cp_expr_loc_or_loc (call, ctx->global->loc);
 
   /* Get the target type of the dynamic_cast.  */
   gcc_assert (TREE_CODE (type) == ADDR_EXPR);
@@ -3651,7 +3653,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
       && integer_zerop (lhs) && !integer_zerop (rhs))
     {
       if (!ctx->quiet)
-	error ("arithmetic involving a null pointer in %qE", lhs);
+	error_at (loc, "arithmetic involving a null pointer in %qE", lhs);
       *non_constant_p = true;
       return t;
     }
@@ -4144,7 +4146,7 @@ eval_and_check_array_index (const constexpr_ctx *ctx,
 			    tree t, bool allow_one_past,
 			    bool *non_constant_p, bool *overflow_p)
 {
-  location_t loc = cp_expr_loc_or_input_loc (t);
+  location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc);
   tree ary = TREE_OPERAND (t, 0);
   t = TREE_OPERAND (t, 1);
   tree index = cxx_eval_constant_expression (ctx, t, vc_prvalue,
@@ -4182,6 +4184,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 			  value_cat lval,
 			  bool *non_constant_p, bool *overflow_p)
 {
+  location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc);
   tree oldary = TREE_OPERAND (t, 0);
   tree ary = cxx_eval_constant_expression (ctx, oldary,
 					   lval,
@@ -4269,7 +4272,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 	 building; if there's no initializer for this element yet,
 	 that's an error.  */
       if (!ctx->quiet)
-	error ("accessing uninitialized array element");
+	error_at (loc, "accessing uninitialized array element");
       *non_constant_p = true;
       return t;
     }
@@ -4318,13 +4321,14 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     lval,
 					     non_constant_p, overflow_p);
+  location_t loc = cp_expr_loc_or_loc (whole, ctx->global->loc);
   if (*non_constant_p)
     return t;
   if (INDIRECT_REF_P (whole)
       && integer_zerop (TREE_OPERAND (whole, 0)))
     {
       if (!ctx->quiet)
-	error ("dereferencing a null pointer in %qE", orig_whole);
+	error_at (loc, "dereferencing a null pointer in %qE", orig_whole);
       *non_constant_p = true;
       return t;
     }
@@ -4343,7 +4347,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
   if (TREE_CODE (whole) != CONSTRUCTOR)
     {
       if (!ctx->quiet)
-	error ("%qE is not a constant expression", orig_whole);
+	error_at (loc, "%qE is not a constant expression", orig_whole);
       *non_constant_p = true;
       return t;
     }
@@ -4351,7 +4355,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
       && DECL_MUTABLE_P (part))
     {
       if (!ctx->quiet)
-	error ("mutable %qD is not usable in a constant expression", part);
+	error_at (loc, "mutable %qD is not usable in a constant expression", part);
       *non_constant_p = true;
       return t;
     }
@@ -4381,10 +4385,10 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
 	{
 	  constructor_elt *cep = CONSTRUCTOR_ELT (whole, 0);
 	  if (cep->value == NULL_TREE)
-	    error ("accessing uninitialized member %qD", part);
+	    error_at (loc, "accessing uninitialized member %qD", part);
 	  else
-	    error ("accessing %qD member instead of initialized %qD member in "
-		   "constant expression", part, cep->index);
+	    error_at (loc, "accessing %qD member instead of initialized %qD member "
+		      "in constant expression", part, cep->index);
 	}
       *non_constant_p = true;
       return t;
@@ -4403,7 +4407,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
 	 building; if there's no initializer for this member yet, that's an
 	 error.  */
       if (!ctx->quiet)
-	error ("accessing uninitialized member %qD", part);
+	error_at (loc, "accessing uninitialized member %qD", part);
       *non_constant_p = true;
       return t;
     }
@@ -4431,6 +4435,7 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     lval,
 					     non_constant_p, overflow_p);
+  location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc);
   tree start, field, value;
   unsigned HOST_WIDE_INT i;
 
@@ -4443,7 +4448,7 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
       && TREE_CODE (whole) != CONSTRUCTOR)
     {
       if (!ctx->quiet)
-	error ("%qE is not a constant expression", orig_whole);
+	error_at (loc, "%qE is not a constant expression", orig_whole);
       *non_constant_p = true;
     }
   if (*non_constant_p)
@@ -4455,7 +4460,7 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
 				 TREE_OPERAND (t, 1), TREE_OPERAND (t, 2)))
 	return r;
       if (!ctx->quiet)
-	error ("%qE is not a constant expression", orig_whole);
+	error_at (loc, "%qE is not a constant expression", orig_whole);
       *non_constant_p = true;
       return t;
     }
@@ -5604,6 +5609,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 		       value_cat lval,
 		       bool *non_constant_p, bool *overflow_p)
 {
+  location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc);
   tree orig_op0 = TREE_OPERAND (t, 0);
   bool empty_base = false;
 
@@ -5634,7 +5640,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
       if (!lval && integer_zerop (op0))
 	{
 	  if (!ctx->quiet)
-	    error ("dereferencing a null pointer");
+	    error_at (loc, "dereferencing a null pointer");
 	  *non_constant_p = true;
 	  return t;
 	}
@@ -5653,8 +5659,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 			  (TREE_TYPE (TREE_TYPE (sub)), TREE_TYPE (t)));
 	      /* DR 1188 says we don't have to deal with this.  */
 	      if (!ctx->quiet)
-		error_at (cp_expr_loc_or_input_loc (t),
-			  "accessing value of %qE through a %qT glvalue in a "
+		error_at (loc, "accessing value of %qE through a %qT glvalue in a "
 			  "constant expression", build_fold_indirect_ref (sub),
 			  TREE_TYPE (t));
 	      *non_constant_p = true;
@@ -5901,6 +5906,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 			   value_cat lval,
 			   bool *non_constant_p, bool *overflow_p)
 {
+  location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc);
   constexpr_ctx new_ctx = *ctx;
 
   tree init = TREE_OPERAND (t, 1);
@@ -6025,7 +6031,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
       /* A constant-expression cannot modify objects from outside the
 	 constant-expression.  */
       if (!ctx->quiet)
-	error ("modification of %qE is not a constant expression", object);
+	error_at (loc, "modification of %qE is not a constant expression", object);
       *non_constant_p = true;
       return t;
     }
@@ -6123,7 +6129,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 	  if (cxx_dialect < cxx20)
 	    {
 	      if (!ctx->quiet)
-		error_at (cp_expr_loc_or_input_loc (t),
+		error_at (cp_expr_loc_or_loc (t, ctx->global->loc),
 			  "change of the active member of a union "
 			  "from %qD to %qD",
 			  CONSTRUCTOR_ELT (*valp, 0)->index,
@@ -6136,7 +6142,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 	      /* Diagnose changing the active union member while the union
 		 is in the process of being initialized.  */
 	      if (!ctx->quiet)
-		error_at (cp_expr_loc_or_input_loc (t),
+		error_at (cp_expr_loc_or_loc (t, ctx->global->loc),
 			  "change of the active member of a union "
 			  "from %qD to %qD during initialization",
 			  CONSTRUCTOR_ELT (*valp, 0)->index,
@@ -6219,7 +6225,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
       if (fail)
 	{
 	  if (!ctx->quiet)
-	    modifying_const_object_error (t, const_object_being_modified);
+	    modifying_const_object_error (ctx, t, const_object_being_modified);
 	  *non_constant_p = true;
 	  return t;
 	}
@@ -6376,6 +6382,8 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
   tree offset = TREE_OPERAND (t, 1);
   gcc_assert (TREE_CONSTANT (offset));
 
+  location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc);
+
   /* OFFSET is constant, but perhaps not constant enough.  We need to
      e.g. bash FLOAT_EXPRs to REAL_CSTs.  */
   offset = fold_simple (offset);
@@ -6423,8 +6431,7 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
     VERIFY_CONSTANT (mod);
 
   /* Storing the modified value.  */
-  tree store = build2_loc (cp_expr_loc_or_loc (t, input_location),
-			   MODIFY_EXPR, type, op, mod);
+  tree store = build2_loc (loc, MODIFY_EXPR, type, op, mod);
   mod = cxx_eval_constant_expression (ctx, store, lval,
 				      non_constant_p, overflow_p);
   ggc_free (store);
@@ -6597,6 +6604,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
 		    bool *non_constant_p, bool *overflow_p,
 		    tree *jump_target)
 {
+  location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc);
   constexpr_ctx new_ctx = *ctx;
   tree local_target;
   if (!jump_target)
@@ -6686,7 +6694,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
       if (++count >= constexpr_loop_limit)
 	{
 	  if (!ctx->quiet)
-	    error_at (cp_expr_loc_or_input_loc (t),
+	    error_at (loc,
 		      "%<constexpr%> loop iteration count exceeds limit of %d "
 		      "(use %<-fconstexpr-loop-limit=%> to increase the limit)",
 		      constexpr_loop_limit);
@@ -6945,7 +6953,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       return t;
     }
 
-  location_t loc = cp_expr_loc_or_input_loc (t);
+  /* Track current location, propagating down from parent calls
+     in case this expression has no location information.  */
+  location_t loc = cp_expr_loc_or_loc (t, ctx->global->loc);
+  ctx->global->loc = loc;
 
   STRIP_ANY_LOCATION_WRAPPER (t);
 
@@ -6968,8 +6979,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  && !integer_zerop (t))
 	{
 	  if (!ctx->quiet)
-	    error ("value %qE of type %qT is not a constant expression",
-		   t, TREE_TYPE (t));
+	    error_at (loc, "value %qE of type %qT is not a constant expression",
+		      t, TREE_TYPE (t));
 	  *non_constant_p = true;
 	}
 
@@ -7217,8 +7228,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	    if (!ctx->quiet)
 	      {
 		auto_diagnostic_group d;
-		error ("temporary of non-literal type %qT in a "
-		       "constant expression", type);
+		error_at (loc, "temporary of non-literal type %qT in a "
+			  "constant expression", type);
 		explain_non_literal_class (type);
 	      }
 	    *non_constant_p = true;
@@ -8020,8 +8031,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	if (function_concept_p (tmpl))
 	  {
 	    if (!ctx->quiet)
-	      error_at (cp_expr_loc_or_input_loc (t),
-			"function concept must be called");
+	      error_at (loc, "function concept must be called");
 	    r = error_mark_node;
 	    break;
 	  }
@@ -8116,6 +8126,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       break;
     }
 
+  /* Reset current location in case it was modified in child calls.  */
+  ctx->global->loc = loc;
+
   if (r == error_mark_node)
     *non_constant_p = true;
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C
index 4574eb83ff7..11630f26ffe 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C
@@ -10,11 +10,11 @@
 // R() is well-formed because i is initialized before j.
 
 struct s {
-  constexpr s() : v(v) { }
+  constexpr s() : v(v) { } // { dg-error "accessing uninitialized member" }
   int v;
 };
 
-constexpr s bang;		// { dg-error "|" }
+constexpr s bang;  // { dg-message "in .constexpr. expansion" }
 
 struct R {
   int i,j;
@@ -26,14 +26,14 @@ constexpr R r;			// { dg-bogus "" }
 // Ill-formed (no diagnostic required)
 struct T {
   int i;
-  constexpr int f() { return i; }
+  constexpr int f() { return i; }  // { dg-error "accessing uninitialized member" }
   constexpr T(): i(0) { }
-  constexpr T(const T& t) : i(f()) { } // { dg-message "" }
+  constexpr T(const T& t) : i(f()) { }  // { dg-message "in .constexpr. expansion" }
 };
 
 constexpr T t1;
 // Ill-formed (diagnostic required)
-constexpr T t2(t1);		// { dg-message "" }
+constexpr T t2(t1);		// { dg-message "in .constexpr. expansion" }
 
 // Well-formed
 struct U {
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
index 5eedf42ba36..50c676c56cd 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C
@@ -16,7 +16,7 @@ int main()
 struct complex 			// { dg-message "no .constexpr. constructor" "" { target { ! implicit_constexpr } } }
 {
   complex(double r, double i) : re(r), im(i) { }
-  constexpr double real() const { return re; } // { dg-error "not a literal type" "" { target c++11_only } }
+  constexpr double real() const { return re; } // { dg-error "not a literal type|not usable in a constant expression" "" { target { ! implicit_constexpr } } }
   double imag() const { return im; }
 
 private:
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
index ebaa95e5324..e4e3bf865cd 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
@@ -2,6 +2,6 @@
 // { dg-do compile { target c++11 } }
 
 typedef bool (*Function)(int);
-constexpr bool check(int x, Function p) { return p(x); }  // { dg-message "in .constexpr. expansion of" }
+constexpr bool check(int x, Function p) { return p(x); }  // { dg-error "lifetime" }
 
-static_assert(check(2, check), "");  // { dg-error "conversion|constant|lifetime|in .constexpr. expansion of" }
+static_assert(check(2, check), "");  // { dg-error "conversion|constant|in .constexpr. expansion of" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C
index 8ac4ef0fd36..6f8f6a8038e 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89481.C
@@ -6,7 +6,7 @@ foo ()
 {
   union U { long long a; int b[2]; } u { 5LL };
   u.b[1] = 4;		// { dg-error "change of the active member of a union from" "" { target c++17_down } }
-  return u.b[0];
+  return u.b[0];	// { dg-error "accessing uninitialized array element" "" { target c++2a } }
 }
 
 constexpr int
@@ -19,6 +19,5 @@ bar ()
 
 static_assert (foo () == 0, "");	// { dg-error "non-constant condition for static assertion" }
 					// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 }
-					// { dg-error "accessing uninitialized array element" "" { target c++2a } .-2 }
 static_assert (bar () == 4, "");	// { dg-error "non-constant condition for static assertion" "" { target c++17_down } }
 					// { dg-message "in 'constexpr' expansion of" "" { target c++17_down } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C
index 43aa7c974c1..f79f1611d5f 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime1.C
@@ -11,3 +11,4 @@ constexpr const int& test() {
   return local.get();
 }
 constexpr int x = test();  // { dg-error "accessing object outside its lifetime" }
+
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C
index 22cd919fcda..2f5ae8db6d5 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime2.C
@@ -8,9 +8,9 @@ struct S {
 
 constexpr int error() {
   const auto& local = S{}.get();  // { dg-message "note: declared here" }
-  return local;
+  return local;  // { dg-error "accessing object outside its lifetime" }
 }
-constexpr int x = error();  // { dg-error "accessing object outside its lifetime" }
+constexpr int x = error();  // { dg-message "in .constexpr. expansion" }
 
 constexpr int ok() {
   // temporary should only be destroyed after end of full-expression
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C
index 6329f8cf6c6..53785521d05 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime3.C
@@ -7,7 +7,7 @@ constexpr int f(int i) {
     int j = 123;  // { dg-message "note: declared here" }
     p = &j;
   }
-  return *p;
+  return *p;  // { dg-error "accessing object outside its lifetime" }
 }
 
-constexpr int i = f(0);  // { dg-error "accessing object outside its lifetime" }
+constexpr int i = f(0);  // { dg-message "in .constexpr. expansion" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C
index 181a1201663..4302da1eddc 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime4.C
@@ -5,7 +5,7 @@ constexpr const double& test() {
   return local;
 }
 
-static_assert(test() == 3.0, "");  // { dg-error "constant|accessing object outside its lifetime" }
+static_assert(test() == 3.0, "");  // { dg-error "non-constant condition|accessing object outside its lifetime" }
 
 // no deference, shouldn't error
 static_assert((test(), true), "");
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C
index ad3ef579f63..a12920c8fba 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C
@@ -5,7 +5,7 @@ constexpr const int& id(int x) { return x; }  // { dg-message "note: declared he
 
 constexpr bool test() {
   const int& y = id(3);
-  return y == 3;
+  return y == 3;  // { dg-error "accessing object outside its lifetime" }
 }
 
-constexpr bool x = test();  // { dg-error "accessing object outside its lifetime" }
+constexpr bool x = test();  // { dg-message "in .constexpr. expansion" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C
index 55fe9fa2f0b..3d76345d564 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C
@@ -8,8 +8,8 @@ union U {
 };
 
 constexpr int foo(U *up) {
-  up->a++;
+  up->a++; // { dg-error "accessing uninitialized member" }
   return {42};
 }
 
-extern constexpr U u = {}; // { dg-error "accessing uninitialized member" }
+extern constexpr U u = {}; // { dg-message "in .constexpr. expansion" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr68180.C b/gcc/testsuite/g++.dg/cpp1y/pr68180.C
index 9e6e5e984f9..8de1ef3936b 100644
--- a/gcc/testsuite/g++.dg/cpp1y/pr68180.C
+++ b/gcc/testsuite/g++.dg/cpp1y/pr68180.C
@@ -6,11 +6,11 @@ typedef float __attribute__( ( vector_size( 16 ) ) ) float32x4_t;
 constexpr float32x4_t fill(float x) {
   float32x4_t v{0};
   constexpr auto vs = sizeof(v)/sizeof(v[0]);
-  for (auto i=0U; i<vs; ++i) v[i]=i;
+  for (auto i=0U; i<vs; ++i) v[i]=i; // { dg-error "not a constant" }
   return v+x;
 }
 
 float32x4_t foo(float32x4_t x) {
-  constexpr float32x4_t v = fill(1.f); // { dg-error "not a constant||in .constexpr. expansion of " }
+  constexpr float32x4_t v = fill(1.f); // { dg-message "in .constexpr. expansion of " }
   return x+v;
 }
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C
index 214d3821299..c46c2d4c7fe 100644
--- a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda6.C
@@ -1,7 +1,7 @@
 // Testcase from P0170R1
 // { dg-do compile { target c++17 } }
 
-auto monoid = [](auto v) { return [=] { return v; }; };
+auto monoid = [](auto v) { return [=] { return v; }; };  // { dg-error "not usable in a constant expression" }
 auto add = [](auto m1) constexpr {
   auto ret = m1();
   return [=](auto m2) mutable {
@@ -22,7 +22,7 @@ int main()
   // member function call operator can not perform an lvalue-to-rvalue conversion
   // on one of its subobjects (that represents its capture) in a constant
   // expression.
-  auto two = monoid(2);
+  auto two = monoid(2);  // { dg-message "not declared .constexpr." }
   if (!(two() == 2)) __builtin_abort(); // OK, not a constant expression.
   static_assert(add(one)(one)() == two()); // { dg-error "|in .constexpr. expansion of " } two() is not a constant expression
   static_assert(add(one)(one)() == monoid(2)()); // OK
diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C
index a3eb31bc6c7..760c9ca40b4 100644
--- a/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C
+++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast11.C
@@ -28,7 +28,7 @@ f3 ()
 {
   T t = { 1, 2 };
   S s = __builtin_bit_cast (S, t);
-  return s.a[1] == 0;
+  return s.a[1] == 0;		// { dg-error "accessing uninitialized array element" }
 }
 
 constexpr bool
@@ -52,12 +52,12 @@ f6 ()
 {
   W t = { 1, 2 };
   V s = __builtin_bit_cast (V, t);
-  return s.b.a[1] == 1;
+  return s.b.a[1] == 1;		// { dg-error "accessing uninitialized array element" }
 }
 
 constexpr bool a = f1 ();
 constexpr bool b = f2 ();
-constexpr bool c = f3 ();	// { dg-error "accessing uninitialized array element" }
-constexpr bool d = f4 ();
+constexpr bool c = f3 ();	// { dg-message "in .constexpr. expansion" }
+constexpr bool d = f4 ();	// { dg-message "in .constexpr. expansion" }
 constexpr bool e = f5 ();
-constexpr bool f = f6 ();	// { dg-error "accessing uninitialized array element" }
+constexpr bool f = f6 ();	// { dg-message "in .constexpr. expansion" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast12.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast12.C
index 9c699dd55f0..e205bc6a8c1 100644
--- a/gcc/testsuite/g++.dg/cpp2a/bit-cast12.C
+++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast12.C
@@ -33,7 +33,7 @@ f3 ()
 {
   T t = { 1, 2 };
   S s = __builtin_bit_cast (S, t);
-  return s.a[1] == 0;
+  return s.a[1] == 0;		// { dg-error "accessing uninitialized array element" }
 }
 
 constexpr bool
@@ -57,12 +57,12 @@ f6 ()
 {
   W t = { 1, 2 };
   V s = __builtin_bit_cast (V, t);
-  return s.b.a[1] == 1;
+  return s.b.a[1] == 1;		// { dg-error "accessing uninitialized array element" }
 }
 
 constexpr bool a = f1 ();
 constexpr bool b = f2 ();
-constexpr bool c = f3 ();	// { dg-error "accessing uninitialized array element" }
-constexpr bool d = f4 ();
+constexpr bool c = f3 ();	// { dg-message "in .constexpr. expansion" }
+constexpr bool d = f4 ();	// { dg-message "in .constexpr. expansion" }
 constexpr bool e = f5 ();
-constexpr bool f = f6 ();	// { dg-error "accessing uninitialized array element" }
+constexpr bool f = f6 ();	// { dg-message "in .constexpr. expansion" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast14.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast14.C
index 5e185919be4..e0cc9a39702 100644
--- a/gcc/testsuite/g++.dg/cpp2a/bit-cast14.C
+++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast14.C
@@ -44,7 +44,7 @@ f5 ()
 {
   T1 t = { 0, 0, 0, 0, 0, 0, 0 };
   S s = __builtin_bit_cast (S, t);
-  unsigned char a = s.a;
+  unsigned char a = s.a;		// { dg-error "accessing uninitialized member" }
   return true;
 }
 
@@ -53,7 +53,7 @@ f6 ()
 {
   T2 t = { 0, 0, 0, 0, 0, 0, 0 };
   S s = __builtin_bit_cast (S, t);
-  unsigned char b = s.b;
+  unsigned char b = s.b;		// { dg-error "accessing uninitialized member" }
   return true;
 }
 
@@ -62,14 +62,14 @@ f7 ()
 {
   T3 t = { 0, 0, 0, 0, 0, 0, 0 };
   S s = __builtin_bit_cast (S, t);
-  unsigned char c = s.c;
+  unsigned char c = s.c;		// { dg-error "accessing uninitialized member" }
   return true;
 }
 
 constexpr bool a = f1 ();
 constexpr bool b = f2 ();
 constexpr bool c = f3 ();
-constexpr bool d = f4 ();
-constexpr bool e = f5 ();	// { dg-error "accessing uninitialized member" }
-constexpr bool f = f6 ();	// { dg-error "accessing uninitialized member" }
-constexpr bool g = f7 ();	// { dg-error "accessing uninitialized member" }
+constexpr bool d = f4 ();	// { dg-message "in .constexpr. expansion" }
+constexpr bool e = f5 ();	// { dg-message "in .constexpr. expansion" }
+constexpr bool f = f6 ();	// { dg-message "in .constexpr. expansion" }
+constexpr bool g = f7 ();	// { dg-message "in .constexpr. expansion" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C
index 01bdfa5bd4d..b0c91d5ef97 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-98122.C
@@ -9,7 +9,7 @@ bar ()
 {
   V f { .b = 42 };
   constexpr auto m = &V::a;
-  return (f.*m) == 42;
+  return (f.*m) == 42;  // { dg-error "accessing 'V::a' member instead of initialized 'V::b' member in constant expression" }
 }
 
 constexpr bool
@@ -21,5 +21,5 @@ baz ()
 }
 
 static_assert (bar (), "");	// { dg-error "non-constant condition for static assertion" }
-				// { dg-error "accessing 'V::a' member instead of initialized 'V::b' member in constant expression" "" { target *-*-* } .-1 }
+				// { dg-message "in .constexpr. expansion" "" { target *-*-* } .-1 }
 static_assert (baz (), "");
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C
index a26678e6ed7..28facf192df 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dynamic17.C
@@ -25,8 +25,7 @@ struct D : B, A {
 
 constexpr B::B(V* v, A* a)
 {
-  dynamic_cast<B*>(a);
+  dynamic_cast<B*>(a); // { dg-error "accessing uninitialized member" }
 }
 
-constexpr D d; // { dg-error "accessing uninitialized member" }
-// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 }
+constexpr D d; // { dg-message "in 'constexpr' expansion of" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
index e56ecfed48a..b4e39b6f928 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init1.C
@@ -52,11 +52,10 @@ constexpr int
 fn5 ()
 {
   struct S { int a = 9; int b; } s;
-  return s.b;
+  return s.b; // { dg-error "accessing uninitialized member" }
 }
 
-constexpr int b = fn5 (); // { dg-error "accessing uninitialized member" }
-// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+constexpr int b = fn5 (); // { dg-message "in .constexpr. expansion of" }
 
 constexpr int
 fn6 ()
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C
index 5a3d06a5fab..832782e1427 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new12.C
@@ -17,11 +17,11 @@ struct B : A {
 constexpr int
 foo ()
 {
-  A *a = new B ();
+  A *a = new B ();  // { dg-message "allocated here" }
   a->a = 4;
   delete a;
-  int r = a->foo ();
+  int r = a->foo ();  // { dg-error "constant expression" }
   return r;
 }
 
-constexpr auto a = foo ();	// { dg-error "constant expression" }
+constexpr auto a = foo ();  // { dg-message "in .constexpr. expansion" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C
index 70b841208f8..3ba440fec53 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-new3.C
@@ -45,11 +45,10 @@ constexpr bool
 f5 ()
 {
   int *p = new int;		// { dg-message "allocated here" }
-  return *p == 1;
+  return *p == 1;		// { dg-error "the content of uninitialized storage is not usable in a constant expression" }
 }
 
-constexpr auto v5 = f5 ();	// { dg-error "the content of uninitialized storage is not usable in a constant expression" }
-				// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 }
+constexpr auto v5 = f5 (); 	// { dg-message "in 'constexpr' expansion of" }
 
 constexpr bool
 f6 ()
@@ -57,11 +56,10 @@ f6 ()
   int *p = new int (2);		// { dg-message "allocated here" }
   int *q = p;
   delete p;
-  return *q == 2;
+  return *q == 2;		// { dg-error "use of allocated storage after deallocation in a constant expression" }
 }
 
-constexpr auto v6 = f6 ();	// { dg-error "use of allocated storage after deallocation in a constant expression" }
-				// { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1  }
+constexpr auto v6 = f6 (); 	// { dg-message "in 'constexpr' expansion of" }
 
 constexpr int *
 f7 ()
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla2.C b/gcc/testsuite/g++.dg/ext/constexpr-vla2.C
index d4ea7c58c0d..e09a27af3de 100644
--- a/gcc/testsuite/g++.dg/ext/constexpr-vla2.C
+++ b/gcc/testsuite/g++.dg/ext/constexpr-vla2.C
@@ -4,7 +4,7 @@
 constexpr int
 fn_bad (int n)
 {
-  __extension__ int a [n] = { 0 };
+  __extension__ int a [n] = { 0 };  // { dg-error "array subscript" }
   int z = a [0] + (n ? fn_bad (n - 1) : 0); // { dg-message "in .constexpr. expansion of " } 
   return z;
 }
@@ -18,4 +18,4 @@ fn_ok (int n)
 }
 
 constexpr int i1 = fn_ok (3);
-constexpr int i2 = fn_bad (3); // { dg-error "array subscript|in .constexpr. expansion of " }
+constexpr int i2 = fn_bad (3); // { dg-message "in .constexpr. expansion of " }
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla3.C b/gcc/testsuite/g++.dg/ext/constexpr-vla3.C
index 538b576a825..6f9daa1897f 100644
--- a/gcc/testsuite/g++.dg/ext/constexpr-vla3.C
+++ b/gcc/testsuite/g++.dg/ext/constexpr-vla3.C
@@ -4,11 +4,11 @@
 constexpr int
 foo (int n)
 {
-  __extension__ int a[n] = { 1, 2, 3, 4, 5, 6 };
+  __extension__ int a[n] = { 1, 2, 3, 4, 5, 6 }; // { dg-error "array subscript" }
   int z = 0;
   for (int i = 0; i <= n; ++i)
     z += a[i];
   return z;
 }
 
-constexpr int n = foo (3); // { dg-error "array subscript|in .constexpr. expansion of " }
+constexpr int n = foo (3); // { dg-message "in .constexpr. expansion of " }
diff --git a/gcc/testsuite/g++.dg/ubsan/pr63956.C b/gcc/testsuite/g++.dg/ubsan/pr63956.C
index 3a1596e6e2e..0771732ef00 100644
--- a/gcc/testsuite/g++.dg/ubsan/pr63956.C
+++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C
@@ -100,13 +100,13 @@ constexpr int
 fn7 (const int *a, int b)
 {
   if (b != 3)
-    return fn6 (*a, b);
+    return fn6 (*a, b); // { dg-error "null pointer" }
   return 7;
 }
 
 constexpr int n1 = 7;
 constexpr int n2 = fn7 (&n1, 5);
-constexpr int n3 = fn7 ((const int *) 0, 8);  // { dg-error "null pointer|in .constexpr. expansion of " }
+constexpr int n3 = fn7 ((const int *) 0, 8);  // { dg-message "in .constexpr. expansion of " }
 
 constexpr int
 fn8 (int i)
diff --git a/libstdc++-v3/testsuite/25_algorithms/equal/constexpr_neg.cc b/libstdc++-v3/testsuite/25_algorithms/equal/constexpr_neg.cc
index 34ca5c4805c..fd89ac0e166 100644
--- a/libstdc++-v3/testsuite/25_algorithms/equal/constexpr_neg.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/equal/constexpr_neg.cc
@@ -32,7 +32,7 @@ test01()
   return outa;
 }
 
-static_assert(test01()); // { dg-error "outside the bounds" }
+static_assert(test01()); // { dg-error "non-constant condition" }
 
 constexpr bool
 test02()
@@ -44,7 +44,8 @@ test02()
   return outa;
 }
 
-static_assert(test02()); // { dg-error "outside the bounds" }
+static_assert(test02()); // { dg-error "non-constant condition" }
 
-// { dg-prune-output "non-constant condition" }
+// Errors occuring within <algorithm> internals:
+// { dg-error "outside the bounds of array" "" { target *-*-* } 0 }
 // { dg-prune-output "in 'constexpr'" }
-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331, PR96630, PR98675]
  2023-03-28 11:32 ` [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Nathaniel Shead
@ 2023-03-28 11:50   ` Jakub Jelinek
  2023-03-28 12:04     ` Nathaniel Shead
  0 siblings, 1 reply; 6+ messages in thread
From: Jakub Jelinek @ 2023-03-28 11:50 UTC (permalink / raw)
  To: Nathaniel Shead; +Cc: gcc-patches

On Tue, Mar 28, 2023 at 10:32:28PM +1100, Nathaniel Shead via Gcc-patches wrote:
> 	* tree-core.h (struct tree_decl_common): New flag to check if
>         value lifetime has expired.
> 	* tree.h (DECL_EXPIRED): Access the new flag.
> 	* print-tree.cc (print_node): Print the new flag.
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -1834,7 +1834,10 @@ struct GTY(()) tree_decl_common {
>    /* In FIELD_DECL, this is DECL_NOT_FLEXARRAY.  */
>    unsigned int decl_not_flexarray : 1;
>  
> -  /* 13 bits unused.  */
> +  /* In VAR_DECL, PARM_DECL, or RESULT_DECL, this is DECL_EXPIRED.  */
> +  unsigned int decl_expired_flag : 1;
> +
> +  /* 12 bits unused.  */
>  
>    /* UID for points-to sets, stable over copying from inlining.  */
>    unsigned int pt_uid;
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -2697,6 +2697,12 @@ extern tree vector_element_bits_tree (const_tree);
>     ??? Need to figure out some way to check this isn't a PARM_DECL.  */
>  #define DECL_INITIAL(NODE) (DECL_COMMON_CHECK (NODE)->decl_common.initial)
>  
> +/* Used in a VAR_DECL, PARM_DECL, or RESULT_DECL to indicate whether
> +   this declaration is currently in lifetime for constant evaluation
> +   purposes.  */
> +#define DECL_EXPIRED(NODE) \
> +  (DECL_COMMON_CHECK(NODE)->decl_common.decl_expired_flag)
> +
>  /* Holds the size of the datum, in bits, as a tree expression.
>     Need not be constant and may be null.  May be less than TYPE_SIZE
>     for a C++ FIELD_DECL representing a base class subobject with its

While it is true that tree_decl_common has some spare bits, this support
is for an implementation detail of the C++ FE and as such, I think it is
highly preferred to use some bit in the lang specific data structures
rather than wasting bits that we could need later on for things that will
be needed in the middle-end.
struct lang_decl_base I think also has 12 spare bits...

Rest I'll defer to C++ maintainers (also whether this is appropriate now
or should be deferred for GCC 14 stage1).

	Jakub


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331, PR96630, PR98675]
  2023-03-28 11:50   ` [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331, PR96630, PR98675] Jakub Jelinek
@ 2023-03-28 12:04     ` Nathaniel Shead
  0 siblings, 0 replies; 6+ messages in thread
From: Nathaniel Shead @ 2023-03-28 12:04 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On Tue, Mar 28, 2023 at 10:50 PM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Tue, Mar 28, 2023 at 10:32:28PM +1100, Nathaniel Shead via Gcc-patches wrote:
> >       * tree-core.h (struct tree_decl_common): New flag to check if
> >         value lifetime has expired.
> >       * tree.h (DECL_EXPIRED): Access the new flag.
> >       * print-tree.cc (print_node): Print the new flag.
> > --- a/gcc/tree-core.h
> > +++ b/gcc/tree-core.h
> > @@ -1834,7 +1834,10 @@ struct GTY(()) tree_decl_common {
> >    /* In FIELD_DECL, this is DECL_NOT_FLEXARRAY.  */
> >    unsigned int decl_not_flexarray : 1;
> >
> > -  /* 13 bits unused.  */
> > +  /* In VAR_DECL, PARM_DECL, or RESULT_DECL, this is DECL_EXPIRED.  */
> > +  unsigned int decl_expired_flag : 1;
> > +
> > +  /* 12 bits unused.  */
> >
> >    /* UID for points-to sets, stable over copying from inlining.  */
> >    unsigned int pt_uid;
> > --- a/gcc/tree.h
> > +++ b/gcc/tree.h
> > @@ -2697,6 +2697,12 @@ extern tree vector_element_bits_tree (const_tree);
> >     ??? Need to figure out some way to check this isn't a PARM_DECL.  */
> >  #define DECL_INITIAL(NODE) (DECL_COMMON_CHECK (NODE)->decl_common.initial)
> >
> > +/* Used in a VAR_DECL, PARM_DECL, or RESULT_DECL to indicate whether
> > +   this declaration is currently in lifetime for constant evaluation
> > +   purposes.  */
> > +#define DECL_EXPIRED(NODE) \
> > +  (DECL_COMMON_CHECK(NODE)->decl_common.decl_expired_flag)
> > +
> >  /* Holds the size of the datum, in bits, as a tree expression.
> >     Need not be constant and may be null.  May be less than TYPE_SIZE
> >     for a C++ FIELD_DECL representing a base class subobject with its
>
> While it is true that tree_decl_common has some spare bits, this support
> is for an implementation detail of the C++ FE and as such, I think it is
> highly preferred to use some bit in the lang specific data structures
> rather than wasting bits that we could need later on for things that will
> be needed in the middle-end.
> struct lang_decl_base I think also has 12 spare bits...
>
> Rest I'll defer to C++ maintainers (also whether this is appropriate now
> or should be deferred for GCC 14 stage1).
>
>         Jakub
>

Thanks, I didn't notice that there were lang specific data structures;
I'll take a look at using that instead.

From my point of view I was expecting this would be too late for GCC13
(especially since it should only affect already invalid code, and the
risk of false positives), but I'm happy either way.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-03-28 12:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-28 11:32 [PATCH 0/3] Track lifetimes in constant evaluation [PR70331,...] Nathaniel Shead
2023-03-28 11:32 ` [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Nathaniel Shead
2023-03-28 11:50   ` [PATCH 1/3] c++: Track lifetimes in constant evaluation [PR70331, PR96630, PR98675] Jakub Jelinek
2023-03-28 12:04     ` Nathaniel Shead
2023-03-28 11:32 ` [PATCH 2/3] c++: Improve constexpr error for dangling local variables Nathaniel Shead
2023-03-28 11:32 ` [PATCH 3/3] c++: Improve location information in constexpr evaluation Nathaniel Shead

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).