public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v4 0/3] c++: Track lifetimes in constant evaluation [PR70331, ...]
@ 2023-07-20  9:33 Nathaniel Shead
  2023-07-20  9:35 ` [PATCH v4 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Nathaniel Shead
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Nathaniel Shead @ 2023-07-20  9:33 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jason Merrill, Patrick Palka, Nathaniel Shead

This is an update of the patch series at
https://gcc.gnu.org/pipermail/gcc-patches/2023-July/623375.html

Changes since v3:

- Use void_node in values map to indicate out-of-lifetime instead of a separate
  hash set
- Remove tracking of temporaries for loops and calls
- Fix missed checks for uses of empty classes outside lifetime and associated
  test
- Add reference to PR c++/110619 for the second patch, and corresponding new
  test case

Bootstrapped and regtested on x86_64-pc-linux-gnu.

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

 gcc/cp/constexpr.cc                           | 178 +++++++++++++-----
 gcc/cp/semantics.cc                           |   5 +-
 gcc/cp/typeck.cc                              |   5 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C  |  10 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C  |   8 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C |   8 +-
 .../g++.dg/cpp0x/constexpr-delete2.C          |   5 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C  |   2 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C  |   1 +
 .../g++.dg/cpp0x/constexpr-recursion.C        |   6 +-
 gcc/testsuite/g++.dg/cpp0x/overflow1.C        |   2 +-
 gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C |  10 +
 gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C  |   5 +-
 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 ++
 .../g++.dg/cpp1y/constexpr-lifetime6.C        |  15 ++
 .../g++.dg/cpp1y/constexpr-tracking-const14.C |   3 +-
 .../g++.dg/cpp1y/constexpr-tracking-const16.C |   3 +-
 .../g++.dg/cpp1y/constexpr-tracking-const18.C |   4 +-
 .../g++.dg/cpp1y/constexpr-tracking-const19.C |   4 +-
 .../g++.dg/cpp1y/constexpr-tracking-const21.C |   4 +-
 .../g++.dg/cpp1y/constexpr-tracking-const22.C |   4 +-
 .../g++.dg/cpp1y/constexpr-tracking-const3.C  |   3 +-
 .../g++.dg/cpp1y/constexpr-tracking-const4.C  |   3 +-
 .../g++.dg/cpp1y/constexpr-tracking-const7.C  |   3 +-
 gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C |   4 +-
 gcc/testsuite/g++.dg/cpp1y/pr68180.C          |   4 +-
 .../g++.dg/cpp1z/constexpr-lambda6.C          |   4 +-
 .../g++.dg/cpp1z/constexpr-lambda8.C          |   5 +-
 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/cpp2a/constinit10.C      |   5 +-
 .../g++.dg/cpp2a/is-corresponding-member4.C   |   4 +-
 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          |  23 +--
 .../g++.dg/warn/Wreturn-local-addr-6.C        |   3 -
 .../25_algorithms/equal/constexpr_neg.cc      |   7 +-
 .../testsuite/26_numerics/gcd/105844.cc       |  10 +-
 .../testsuite/26_numerics/lcm/105844.cc       |  14 +-
 50 files changed, 350 insertions(+), 168 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C
 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
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C

-- 
2.41.0


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

* [PATCH v4 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675]
  2023-07-20  9:33 [PATCH v4 0/3] c++: Track lifetimes in constant evaluation [PR70331, ...] Nathaniel Shead
@ 2023-07-20  9:35 ` Nathaniel Shead
  2023-07-20 14:42   ` Jason Merrill
  2023-07-20  9:36 ` [PATCH v4 2/3] c++: Improve constexpr error for dangling local variables [PR110619] Nathaniel Shead
  2023-07-20  9:37 ` [PATCH v4 3/3] c++: Improve location information in constant evaluation Nathaniel Shead
  2 siblings, 1 reply; 12+ messages in thread
From: Nathaniel Shead @ 2023-07-20  9:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jason Merrill, Patrick Palka

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
accessing values outside their lifetime (e.g. following explicit
destructor calls).

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

gcc/cp/ChangeLog:

	* constexpr.cc (constexpr_global_ctx::is_outside_lifetime): New
	function.
	(constexpr_global_ctx::get_value): Don't return expired values.
	(constexpr_global_ctx::get_value_ptr): Likewise.
	(constexpr_global_ctx::remove_value): Mark value outside
	lifetime.
	(outside_lifetime_error): New function.
	(cxx_eval_call_expression): No longer track save_exprs.
	(cxx_eval_loop_expr): Likewise.
	(cxx_eval_constant_expression): Add checks for outside lifetime
	values. Remove local variables at end of bind exprs, and
	temporaries after cleanup points.

gcc/testsuite/ChangeLog:

	* 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.
	* g++.dg/cpp1y/constexpr-lifetime6.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
 gcc/cp/constexpr.cc                           | 132 ++++++++++++------
 .../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 ++
 .../g++.dg/cpp1y/constexpr-lifetime6.C        |  15 ++
 7 files changed, 170 insertions(+), 45 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
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 6e8f1c2b61e..cd4424bcb44 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -1148,7 +1148,8 @@ enum constexpr_switch_state {
 
 class constexpr_global_ctx {
   /* Values for any temporaries or local variables within the
-     constant-expression. */
+     constant-expression. Objects outside their lifetime have
+     value 'void_node'.  */
   hash_map<tree,tree> values;
 public:
   /* Number of cxx_eval_constant_expression calls (except skipped ones,
@@ -1170,17 +1171,28 @@ public:
     : constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr),
       heap_dealloc_count (0) {}
 
+  bool is_outside_lifetime (tree t)
+  {
+    if (tree *p = values.get(t))
+      if (*p == void_node)
+	return true;
+    return false;
+  }
  tree get_value (tree t)
   {
     if (tree *p = values.get (t))
-      return *p;
+      if (*p != void_node)
+	return *p;
     return NULL_TREE;
   }
   tree *get_value_ptr (tree t)
   {
     if (modifiable && !modifiable->contains (t))
       return nullptr;
-    return values.get (t);
+    if (tree *p = values.get (t))
+      if (*p != void_node)
+	return p;
+    return nullptr;
   }
   void put_value (tree t, tree v)
   {
@@ -1188,7 +1200,13 @@ public:
     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))
+      values.put (t, void_node);
+    else
+      values.remove (t);
+  }
 };
 
 /* Helper class for constexpr_global_ctx.  In some cases we want to avoid
@@ -3085,12 +3103,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  gcc_assert (!DECL_BY_REFERENCE (res));
 	  ctx->global->put_value (res, NULL_TREE);
 
-	  /* Track the callee's evaluated SAVE_EXPRs and TARGET_EXPRs so that
-	     we can forget their values after the call.  */
-	  constexpr_ctx ctx_with_save_exprs = *ctx;
-	  auto_vec<tree, 10> save_exprs;
-	  ctx_with_save_exprs.save_exprs = &save_exprs;
-	  ctx_with_save_exprs.call = &new_call;
+	  /* Remember the current call we're evaluating.  */
+	  constexpr_ctx call_ctx = *ctx;
+	  call_ctx.call = &new_call;
 	  unsigned save_heap_alloc_count = ctx->global->heap_vars.length ();
 	  unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count;
 
@@ -3101,7 +3116,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 				      non_constant_p, overflow_p);
 
 	  tree jump_target = NULL_TREE;
-	  cxx_eval_constant_expression (&ctx_with_save_exprs, body,
+	  cxx_eval_constant_expression (&call_ctx, body,
 					vc_discard, non_constant_p, overflow_p,
 					&jump_target);
 
@@ -3157,15 +3172,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	    cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/true,
 				      non_constant_p, overflow_p);
 
-	  /* Forget the saved values of the callee's SAVE_EXPRs and
-	     TARGET_EXPRs.  */
-	  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);
@@ -5697,6 +5704,25 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
   return r;
 }
 
+/* Complain about R, a DECL that is accessed outside its lifetime.  */
+
+static void
+outside_lifetime_error (location_t loc, tree r)
+{
+  if (DECL_NAME (r) == heap_deleted_identifier)
+    {
+      /* Provide a more accurate message for deleted variables.  */
+      error_at (loc, "use of allocated storage after deallocation "
+		"in a constant expression");
+      inform (DECL_SOURCE_LOCATION (r), "allocated here");
+    }
+  else
+    {
+      error_at (loc, "accessing object outside its lifetime");
+      inform (DECL_SOURCE_LOCATION (r), "declared here");
+    }
+}
+
 /* Complain about R, a VAR_DECL, not being usable in a constant expression.
    FUNDEF_P is true if we're checking a constexpr function body.
    Shared between potential_constant_expression and
@@ -6602,7 +6628,6 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
 		    bool *non_constant_p, bool *overflow_p,
 		    tree *jump_target)
 {
-  constexpr_ctx new_ctx = *ctx;
   tree local_target;
   if (!jump_target)
     {
@@ -6640,14 +6665,12 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
     default:
       gcc_unreachable ();
     }
-  auto_vec<tree, 10> save_exprs;
-  new_ctx.save_exprs = &save_exprs;
   do
     {
       if (count != -1)
 	{
 	  if (body)
-	    cxx_eval_constant_expression (&new_ctx, body, vc_discard,
+	    cxx_eval_constant_expression (ctx, body, vc_discard,
 					  non_constant_p, overflow_p,
 					  jump_target);
 	  if (breaks (jump_target))
@@ -6660,7 +6683,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
 	    *jump_target = NULL_TREE;
 
 	  if (expr)
-	    cxx_eval_constant_expression (&new_ctx, expr, vc_prvalue,
+	    cxx_eval_constant_expression (ctx, expr, vc_prvalue,
 					  non_constant_p, overflow_p,
 					  jump_target);
 	}
@@ -6668,7 +6691,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
       if (cond)
 	{
 	  tree res
-	    = cxx_eval_constant_expression (&new_ctx, cond, vc_prvalue,
+	    = cxx_eval_constant_expression (ctx, cond, vc_prvalue,
 					    non_constant_p, overflow_p,
 					    jump_target);
 	  if (res)
@@ -6683,11 +6706,6 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
 	    gcc_assert (*jump_target);
 	}
 
-      /* Forget saved values of SAVE_EXPRs and TARGET_EXPRs.  */
-      for (tree save_expr : save_exprs)
-	ctx->global->remove_value (save_expr);
-      save_exprs.truncate (0);
-
       if (++count >= constexpr_loop_limit)
 	{
 	  if (!ctx->quiet)
@@ -6705,10 +6723,6 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
 	 && (!switches (jump_target) || count == 0)
 	 && !*non_constant_p);
 
-  /* Forget saved values of SAVE_EXPRs and TARGET_EXPRs.  */
-  for (tree save_expr : save_exprs)
-    ctx->global->remove_value (save_expr);
-
   return NULL_TREE;
 }
 
@@ -7051,10 +7065,17 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	return ctx->ctor;
       if (VAR_P (t))
 	if (tree v = ctx->global->get_value (t))
-	    {
-	      r = v;
-	      break;
-	    }
+	  {
+	    r = v;
+	    break;
+	  }
+      if (ctx->global->is_outside_lifetime (t))
+	{
+	  if (!ctx->quiet)
+	    outside_lifetime_error (loc, t);
+	  *non_constant_p = true;
+	  break;
+	}
       if (ctx->manifestly_const_eval == mce_true)
 	maybe_warn_about_constant_value (loc, t);
       if (COMPLETE_TYPE_P (TREE_TYPE (t))
@@ -7099,6 +7120,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	r = v;
       else if (lval)
 	/* Defer in case this is only used for its type.  */;
+      else if (ctx->global->is_outside_lifetime (t))
+	{
+	  if (!ctx->quiet)
+	    outside_lifetime_error (loc, t);
+	  *non_constant_p = true;
+	  break;
+	}
       else if (COMPLETE_TYPE_P (TREE_TYPE (t))
 	       && is_really_empty_class (TREE_TYPE (t), /*ignore_vptr*/false))
 	{
@@ -7338,17 +7366,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;
 
@@ -7864,10 +7903,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/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/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
new file mode 100644
index 00000000000..f358aff4490
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
@@ -0,0 +1,15 @@
+// { dg-do compile { target c++14 } }
+// { dg-options "-Wno-return-local-addr" }
+
+struct Empty {};
+
+constexpr const Empty& empty() {
+  return Empty{};
+}
+
+constexpr const Empty& empty_parm(Empty e) {
+  return e;
+}
+
+constexpr Empty a = empty();  // { dg-error "" }
+constexpr Empty b = empty_parm({});  // { dg-error "" }
-- 
2.41.0


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

* [PATCH v4 2/3] c++: Improve constexpr error for dangling local variables [PR110619]
  2023-07-20  9:33 [PATCH v4 0/3] c++: Track lifetimes in constant evaluation [PR70331, ...] Nathaniel Shead
  2023-07-20  9:35 ` [PATCH v4 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Nathaniel Shead
@ 2023-07-20  9:36 ` Nathaniel Shead
  2023-07-20 15:46   ` Jason Merrill
  2023-07-20  9:37 ` [PATCH v4 3/3] c++: Improve location information in constant evaluation Nathaniel Shead
  2 siblings, 1 reply; 12+ messages in thread
From: Nathaniel Shead @ 2023-07-20  9:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jason Merrill, Patrick Palka

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.

	PR c++/110619

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/cpp1y/constexpr-lifetime6.C: Likewise.
	* g++.dg/cpp1y/constexpr-110619.C: New test.
	* 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-110619.C    | 10 ++++++++++
 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C |  4 ++--
 gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C |  8 ++++----
 gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C |  3 ---
 6 files changed, 23 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C

diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 8fb47fd179e..107407de513 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -1260,7 +1260,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 859b133a18d..47233b3b717 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -11273,8 +11273,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;
     }
 
   /* A naive attempt to reduce the number of -Wdangling-reference false
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C
new file mode 100644
index 00000000000..cca13302238
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++14 } }
+// { dg-options "-Wno-return-local-addr" }
+// PR c++/110619
+
+constexpr auto f() {
+    int i = 0;
+    return &i;
+};
+
+static_assert( f() != nullptr );
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/cpp1y/constexpr-lifetime6.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
index f358aff4490..b81e89af79c 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
@@ -4,12 +4,12 @@
 struct Empty {};
 
 constexpr const Empty& empty() {
-  return Empty{};
+  return Empty{};  // { dg-message "note: declared here" }
 }
 
-constexpr const Empty& empty_parm(Empty e) {
+constexpr const Empty& empty_parm(Empty e) {  // { dg-message "note: declared here" }
   return e;
 }
 
-constexpr Empty a = empty();  // { dg-error "" }
-constexpr Empty b = empty_parm({});  // { dg-error "" }
+constexpr Empty a = empty();  // { dg-error "accessing object outside its lifetime" }
+constexpr Empty b = empty_parm({});  // { 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.41.0


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

* [PATCH v4 3/3] c++: Improve location information in constant evaluation
  2023-07-20  9:33 [PATCH v4 0/3] c++: Track lifetimes in constant evaluation [PR70331, ...] Nathaniel Shead
  2023-07-20  9:35 ` [PATCH v4 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Nathaniel Shead
  2023-07-20  9:36 ` [PATCH v4 2/3] c++: Improve constexpr error for dangling local variables [PR110619] Nathaniel Shead
@ 2023-07-20  9:37 ` Nathaniel Shead
  2023-07-20 17:00   ` Jason Merrill
  2 siblings, 1 reply; 12+ messages in thread
From: Nathaniel Shead @ 2023-07-20  9:37 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jason Merrill, Patrick Palka

This patch updates 'input_location' during constant evaluation to ensure
that errors in subexpressions that lack location information still
provide accurate diagnostics.

By itself this change causes some small regressions in diagnostic
quality for circumstances where errors used 'input_location' but the
location of the parent subexpression doesn't make sense, so this patch
also includes a couple of other small diagnostic improvements to improve
the most egregious cases.

gcc/cp/ChangeLog:

	* constexpr.cc (modifying_const_object_error): Find the source
	location of the const object's declaration.
	(cxx_eval_store_expression): Fall back to the location of the
	target object when evaluating initialiser.
	(cxx_eval_constant_expression): Update input_location to the location
	of the currently evaluated expression, if possible.

libstdc++-v3/ChangeLog:

	* testsuite/25_algorithms/equal/constexpr_neg.cc: Update diagnostic
	locations.
	* testsuite/26_numerics/gcd/105844.cc: Likewise.
	* testsuite/26_numerics/lcm/105844.cc: Likewise.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/constexpr-48089.C: Update diagnostic locations.
	* g++.dg/cpp0x/constexpr-70323.C: Likewise.
	* g++.dg/cpp0x/constexpr-70323a.C: Likewise.
	* g++.dg/cpp0x/constexpr-delete2.C: Likewise.
	* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
	* g++.dg/cpp0x/constexpr-ice20.C: Likewise.
	* g++.dg/cpp0x/constexpr-recursion.C: Likewise.
	* g++.dg/cpp0x/overflow1.C: Likewise.
	* g++.dg/cpp1y/constexpr-89285.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-tracking-const14.C: Likewise.
	* g++.dg/cpp1y/constexpr-tracking-const16.C: Likewise.
	* g++.dg/cpp1y/constexpr-tracking-const18.C: Likewise.
	* g++.dg/cpp1y/constexpr-tracking-const19.C: Likewise.
	* g++.dg/cpp1y/constexpr-tracking-const21.C: Likewise.
	* g++.dg/cpp1y/constexpr-tracking-const22.C: Likewise.
	* g++.dg/cpp1y/constexpr-tracking-const3.C: Likewise.
	* g++.dg/cpp1y/constexpr-tracking-const4.C: Likewise.
	* g++.dg/cpp1y/constexpr-tracking-const7.C: Likewise.
	* g++.dg/cpp1y/constexpr-union5.C: Likewise.
	* g++.dg/cpp1y/pr68180.C: Likewise.
	* g++.dg/cpp1z/constexpr-lambda6.C: Likewise.
	* g++.dg/cpp1z/constexpr-lambda8.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/cpp2a/constinit10.C: Likewise.
	* g++.dg/cpp2a/is-corresponding-member4.C: Likewise.
	* g++.dg/ext/constexpr-vla2.C: Likewise.
	* g++.dg/ext/constexpr-vla3.C: Likewise.
	* g++.dg/ubsan/pr63956.C: Likewise.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
 gcc/cp/constexpr.cc                           | 46 ++++++++++++++++++-
 gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C  | 10 ++--
 gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C  |  8 ++--
 gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C |  8 ++--
 .../g++.dg/cpp0x/constexpr-delete2.C          |  5 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C  |  2 +-
 gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C  |  1 +
 .../g++.dg/cpp0x/constexpr-recursion.C        |  6 +--
 gcc/testsuite/g++.dg/cpp0x/overflow1.C        |  2 +-
 gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C  |  5 +-
 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 +-
 .../g++.dg/cpp1y/constexpr-tracking-const14.C |  3 +-
 .../g++.dg/cpp1y/constexpr-tracking-const16.C |  3 +-
 .../g++.dg/cpp1y/constexpr-tracking-const18.C |  4 +-
 .../g++.dg/cpp1y/constexpr-tracking-const19.C |  4 +-
 .../g++.dg/cpp1y/constexpr-tracking-const21.C |  4 +-
 .../g++.dg/cpp1y/constexpr-tracking-const22.C |  4 +-
 .../g++.dg/cpp1y/constexpr-tracking-const3.C  |  3 +-
 .../g++.dg/cpp1y/constexpr-tracking-const4.C  |  3 +-
 .../g++.dg/cpp1y/constexpr-tracking-const7.C  |  3 +-
 gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C |  4 +-
 gcc/testsuite/g++.dg/cpp1y/pr68180.C          |  4 +-
 .../g++.dg/cpp1z/constexpr-lambda6.C          |  4 +-
 .../g++.dg/cpp1z/constexpr-lambda8.C          |  5 +-
 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/cpp2a/constinit10.C      |  5 +-
 .../g++.dg/cpp2a/is-corresponding-member4.C   |  4 +-
 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          | 23 +++++-----
 .../25_algorithms/equal/constexpr_neg.cc      |  7 +--
 .../testsuite/26_numerics/gcd/105844.cc       | 10 ++--
 .../testsuite/26_numerics/lcm/105844.cc       | 14 +++---
 45 files changed, 170 insertions(+), 124 deletions(-)

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index cd4424bcb44..63e8274e0dc 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -2178,7 +2178,33 @@ modifying_const_object_error (tree expr, tree obj)
   auto_diagnostic_group d;
   error_at (loc, "modifying a const object %qE is not allowed in "
 	    "a constant expression", TREE_OPERAND (expr, 0));
-  inform (location_of (obj), "originally declared %<const%> here");
+
+  /* Find the underlying object that was declared as const.  */
+  location_t decl_loc = UNKNOWN_LOCATION;
+  for (tree probe = obj; decl_loc == UNKNOWN_LOCATION; )
+    switch (TREE_CODE (probe))
+      {
+      case BIT_FIELD_REF:
+      case COMPONENT_REF:
+	{
+	  tree elt = TREE_OPERAND (probe, 1);
+	  if (CP_TYPE_CONST_P (TREE_TYPE (elt)))
+	    decl_loc = DECL_SOURCE_LOCATION (elt);
+	  probe = TREE_OPERAND (probe, 0);
+	}
+	break;
+
+      case ARRAY_REF:
+      case REALPART_EXPR:
+      case IMAGPART_EXPR:
+	probe = TREE_OPERAND (probe, 0);
+	break;
+
+      default:
+	decl_loc = location_of (probe);
+	break;
+      }
+  inform (decl_loc, "originally declared %<const%> here");
 }
 
 /* Return true if FNDECL is a replaceable global allocation function that
@@ -6276,6 +6302,21 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
       if (TREE_CODE (init) == TARGET_EXPR)
 	if (tree tinit = TARGET_EXPR_INITIAL (init))
 	  init = tinit;
+
+      /* Improve error messages for initialisers when the initialising
+	 expression has no location information by pointing to the decl
+	 that is getting initialised.  */
+      location_t target_loc = cp_expr_loc_or_input_loc (init);
+      if (!EXPR_HAS_LOCATION (init))
+	{
+	  if (DECL_P (target))
+	    target_loc = DECL_SOURCE_LOCATION (target);
+	  else if (TREE_CODE (target) == COMPONENT_REF
+		   || TREE_CODE (target) == BIT_FIELD_REF)
+	    target_loc = DECL_SOURCE_LOCATION (TREE_OPERAND (target, 1));
+	}
+      iloc_sentinel sentinel = target_loc;
+
       init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue,
 					   non_constant_p, overflow_p);
       /* The hash table might have moved since the get earlier, and the
@@ -6964,7 +7005,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       return t;
     }
 
+  /* Change the input location to the currently processed expression for
+     better error messages when a subexpression has no location.  */
   location_t loc = cp_expr_loc_or_input_loc (t);
+  iloc_sentinel sentinel (loc);
 
   STRIP_ANY_LOCATION_WRAPPER (t);
 
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-70323.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C
index 272a225d967..bfb185f2fb5 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C
@@ -1,10 +1,10 @@
 // PR c++/70323
 // { dg-do compile { target c++11 } }
 
-constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }
-constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }
+constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }  // { dg-error "overflow in constant expression" }
+constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }   // { dg-error "overflow in constant expression" }
 
-constexpr bool i0_0 = overflow_if_0 (0);   // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
+constexpr bool i0_0 = overflow_if_0 (0);   // { dg-message "in .constexpr. expansion of " }
 constexpr bool i0_1 = overflow_if_0 (1);
 constexpr bool i1_0 = overflow_if_1 (0);
-constexpr bool i1_1 = overflow_if_1 (1);   // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
+constexpr bool i1_1 = overflow_if_1 (1);   // { dg-message "in .constexpr. expansion of " }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C
index 1990ab6be2d..b5ed581e1c8 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C
@@ -2,10 +2,10 @@
 // { dg-do compile { target c++11 } }
 // { dg-options "-Wall" }
 
-constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }
-constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }
+constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }  // { dg-error "overflow in constant expression" }
+constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }   // { dg-error "overflow in constant expression" }
 
-constexpr bool i0_0 = overflow_if_0 (0);   // { dg-error "overflow in constant expression|in .constexpr. expansion of" }
+constexpr bool i0_0 = overflow_if_0 (0);   // { dg-message "in .constexpr. expansion of" }
 constexpr bool i0_1 = overflow_if_0 (1);
 constexpr bool i1_0 = overflow_if_1 (0);
-constexpr bool i1_1 = overflow_if_1 (1);   // { dg-error "overflow in constant expression|in .constexpr. expansion of" }
+constexpr bool i1_1 = overflow_if_1 (1);   // { dg-message "in .constexpr. expansion of" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C
index 999f9b7851e..f2b6df2509c 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C
@@ -6,8 +6,9 @@ constexpr int f(int i) { return i; }
 constexpr int g(A* ap)
 {
   return f((delete[] ap, 42)); // { dg-message "" "" { target c++17_down } }
+                               // { dg-error "" "" { target c++2a } .-1 }
 }
 
 A a;
-constexpr int i = g(&a);	// { dg-error "" }
-				// { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 }
+constexpr int i = g(&a);  // { dg-error "" "" { target c++17_down } }
+			  // { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 }
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 e2d4853a284..43fa9a03c14 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
@@ -3,5 +3,6 @@
 
 typedef bool (*Function)(int);
 constexpr bool check(int x, Function p) { return p(x); }  // { dg-message "in .constexpr. expansion of" }
+// { dg-error "not a constant expression" "" { target *-*-* } .-1 }
 
 static_assert(check(2, check), "");  // { dg-error "conversion|constant|in .constexpr. expansion of" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C
index 8c4201e1ec2..b00f8794d4c 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C
@@ -1,6 +1,6 @@
 // Test that we catch excessive recursion.
 // { dg-do compile { target c++11 } }
 // { dg-options "-fconstexpr-depth=5" }
-// { dg-prune-output "in constexpr expansion" }
-constexpr int f (int i) { return f (i-1); } // { dg-message "in .constexpr. expansion of " }
-constexpr int i = f(42);	// { dg-error ".constexpr. evaluation depth|in .constexpr. expansion of " }
+// { dg-prune-output "in .constexpr. expansion" }
+constexpr int f (int i) { return f (i-1); } // { dg-error ".constexpr. evaluation depth" }
+constexpr int i = f(42);	
diff --git a/gcc/testsuite/g++.dg/cpp0x/overflow1.C b/gcc/testsuite/g++.dg/cpp0x/overflow1.C
index b8591b4af41..e295355c88f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/overflow1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/overflow1.C
@@ -4,7 +4,7 @@ template <long long i>
 struct Fib
 {
     static const long long value // { dg-error "overflow" }
-    = Fib<i-1>::value + Fib<i-2>::value;
+    = Fib<i-1>::value + Fib<i-2>::value; // { dg-error "overflow" }
 };
 
 template <>
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
index fe0b8570ca2..d09df091fdb 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
@@ -14,7 +14,8 @@ struct B {
   }
 };
 struct C : A {
-  B bar {this};
+  B bar {this};  // { dg-error "" "" { target c++14_down } }
 };
 
-constexpr C foo {};		// { dg-message "" }
+// error path changes in C++17 due to `C` becoming an aggregate
+constexpr C foo {};  // { dg-error "" "" { target c++17 } }
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-tracking-const14.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const14.C
index 45c4fcf50be..0c77dd4934d 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const14.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const14.C
@@ -14,7 +14,7 @@ struct C {
 
 struct A {
   int r;
-  const C c;
+  const C c; // { dg-message "originally declared" }
   constexpr A() : r(11) { r = 14; const_cast<C &>(c).n = 42; } // { dg-error "modifying a const object" }
 };
 
@@ -34,5 +34,4 @@ struct B {
 };
 
 constexpr B b(false); // { dg-message "in .constexpr. expansion of" }
-// { dg-message "originally declared" "" { target *-*-* } .-1 }
 static_assert(b.e.d.a.c.n == 2, ""); // { dg-error "non-constant condition" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C
index 5a5b92bc8cc..b5ccf3a4ea5 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C
@@ -7,7 +7,7 @@ constexpr int& impl(const int (&array)[10], int index) {
 
 struct A {
   constexpr int& operator[](int i) { return impl(elems, i); }
-  const int elems[10];
+  const int elems[10]; // { dg-message "originally declared" }
 };
 
 constexpr bool
@@ -19,4 +19,3 @@ f()
 }
 
 constexpr bool b = f(); // { dg-message "in .constexpr. expansion of " }
-// { dg-message "originally declared" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
index 11a680468c2..278628c121b 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
@@ -13,11 +13,11 @@ struct array
 template <typename T>
 struct S {
   using U = array<T, 4>;
-  const U m;
+  const U m; // { dg-message "originally declared" }
   constexpr S(int) : m{}
   {
     const_cast<int &>(const_cast<const U &>(m)[0]) = 42; // { dg-error "modifying a const object" }
   }
 };
 
-constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
+constexpr S<int> p = { 10 };
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
index c31222ffcdd..2d18c94537b 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
@@ -7,7 +7,7 @@ template <typename E, size_t N>
 struct array
 {
   constexpr const E &operator[](size_t n) const noexcept { return elems[n]; }
-  const E elems[N];
+  const E elems[N]; // { dg-message "originally declared" }
 };
 
 template <typename T>
@@ -20,4 +20,4 @@ struct S {
   }
 };
 
-constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
+constexpr S<int> p = { 10 };
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
index 0b16193398e..d3bbcb116a6 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
@@ -18,11 +18,11 @@ struct array2 {
 template <typename T>
 struct S {
   using U = array2<T, 4>;
-  const U m;
+  const U m; // { dg-message "originally declared" }
   constexpr S(int) : m{}
   {
     const_cast<int &>(m.a[0]) = 42; // { dg-error "modifying a const object" }
   }
 };
 
-constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
+constexpr S<int> p = { 10 };
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
index 216cf1607a4..27522f86dbd 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
@@ -7,11 +7,11 @@ struct X {
 
 template <typename T>
 struct S {
-  const X x;
+  const X x; // { dg-message "originally declared" }
   constexpr S(int) : x{}
   {
     const_cast<X&>(x).i = 19; // { dg-error "modifying a const object" }
   }
 };
 
-constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
+constexpr S<int> p = { 10 };
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C
index 6853775c1e2..fc88dd05eef 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C
@@ -7,7 +7,7 @@ struct A {
 };
 
 struct B {
-  const A a;
+  const A a; // { dg-message "originally declared" }
   constexpr B(bool b) {
     if (b)
       const_cast<A &>(a).n = 3; // { dg-error "modifying a const object" }
@@ -18,5 +18,4 @@ constexpr B b(false);
 static_assert(b.a.n == 2, "");
 
 constexpr B b2(true); // { dg-message "in .constexpr. expansion of " }
-// { dg-message "originally declared" "" { target *-*-* } .-1 } 
 static_assert((b2.a.n, 1), "");
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C
index 8263a7cc505..27fede152c7 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C
@@ -2,7 +2,7 @@
 // { dg-do compile { target c++14 } }
 
 struct A {
-  const int n;
+  const int n; // { dg-message "originally declared" }
   constexpr A() : n(1) { }
 };
 struct B {
@@ -13,5 +13,4 @@ struct B {
   }
 };
 constexpr B b; // { dg-message "in .constexpr. expansion of " }
-// { dg-message "originally declared" "" { target *-*-* } .-1 }
 static_assert((b.a.n, 1), "");
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C
index 922e8ff126f..bea14b05602 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C
@@ -3,7 +3,7 @@
 
 struct D { int n; };
 
-struct C { const D d; };
+struct C { const D d; }; // { dg-message "originally declared" }
 
 struct A {
   C c;
@@ -19,5 +19,4 @@ struct B {
 };
 
 constexpr B b{}; // { dg-message "in .constexpr. expansion of " }
-// { dg-message "originally declared" "" { target *-*-* } .-1 }
 static_assert((b.a.c.d.n, 1), "");
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/cpp1z/constexpr-lambda8.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
index 84be68ab7f0..82351ff0c54 100644
--- a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
@@ -4,11 +4,14 @@
 auto Fwd = [](int (*fp)(int), auto a) { return fp(a); };
 auto C = [](auto a) { return a; };
 static_assert( Fwd(C ,3) == 3); // OK
+
 // No specialization of the function call operator template can be constexpr
 // (because of the local static).
 auto NC = [](auto a) { static int s; return a; }; // { dg-error "static" }
-// { dg-message "operator int" "" { target *-*-* } .+1 }
+// { dg-error "called in a constant expression" "" { target *-*-* } .-1 }
+
 static_assert( Fwd(NC ,3) == 3); // { dg-error "" }
+// { dg-message "operator int" "" { target *-*-* } .-1 }
 
 // We look for the string "operator int" to check that we aren't trying to do
 // template pretty-printing in an expression; that gets incredibly unwieldy
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/cpp2a/constinit10.C b/gcc/testsuite/g++.dg/cpp2a/constinit10.C
index b678788541e..d2c49c41f91 100644
--- a/gcc/testsuite/g++.dg/cpp2a/constinit10.C
+++ b/gcc/testsuite/g++.dg/cpp2a/constinit10.C
@@ -11,15 +11,14 @@ struct S1
 struct alignas(64) S2
 {
     constexpr S2 ()
-    : m_tabS1()
+    : m_tabS1() // { dg-error "used before its definition" }
     {}
 
     S1 m_tabS1[7];
 };
 
 constinit S2 objX; // { dg-error ".constinit. variable .objX. does not have a constant initializer" }
-// { dg-error "used before its definition" "" { target *-*-* } .-1 }
-// // { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-2 }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
 
 constexpr S1::S1 ()
 : m_i(14)
diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C
index 6b74090306b..241e9976e8c 100644
--- a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C
+++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C
@@ -14,8 +14,8 @@ is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
 struct A { int a; };
 struct B;
 constexpr int B::*n = nullptr;
-constexpr auto a = std::is_corresponding_member (&A::a, n);	// { dg-error "invalid use of incomplete type 'struct B'" }
-constexpr auto b = std::is_corresponding_member (n, &A::a);	// { dg-error "invalid use of incomplete type 'struct B'" }
+constexpr auto a = std::is_corresponding_member (&A::a, n);	// { dg-message "in .constexpr. expansion of" }
+constexpr auto b = std::is_corresponding_member (n, &A::a);	// { dg-message "in .constexpr. expansion of" }
 
 void
 foo (int B::*m)
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..6fd0b4f893e 100644
--- a/gcc/testsuite/g++.dg/ubsan/pr63956.C
+++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C
@@ -56,12 +56,13 @@ fn3 (int a, int b)
 {
   if (b != 2)
     a = a / b; // { dg-error "..7 / 0.. is not a constant expression" }
+               // { dg-error "overflow in constant expression" "" { target *-*-* } .-1 }
   return a;
 }
 
 constexpr int k1 = fn3 (8, 4);
 constexpr int k2 = fn3 (7, 0); // { dg-message "in .constexpr. expansion" }
-constexpr int k3 = fn3 (INT_MIN, -1); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
+constexpr int k3 = fn3 (INT_MIN, -1); // { dg-message "in .constexpr. expansion of " }
 
 SA (k1 == 2);
 
@@ -100,13 +101,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)
@@ -122,15 +123,15 @@ constexpr int
 fn9 (int a, int b)
 {
   if (b != 0)
-    return a + b;
+    return a + b; // { dg-error "overflow in constant expression" }
   return a;
 }
 
 constexpr int p1 = fn9 (42, 7);
-constexpr int p2 = fn9 (__INT_MAX__, 1); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
+constexpr int p2 = fn9 (__INT_MAX__, 1); // { dg-message "in .constexpr. expansion of " }
 constexpr int p3 = fn9 (__INT_MAX__, -1);
 constexpr int p4 = fn9 (INT_MIN, 1);
-constexpr int p5 = fn9 (INT_MIN, -1); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
+constexpr int p5 = fn9 (INT_MIN, -1); // { dg-message "in .constexpr. expansion of " }
 
 SA (p1 == 49);
 SA (p3 == __INT_MAX__ - 1);
@@ -140,13 +141,13 @@ constexpr int
 fn10 (int a, int b)
 {
   if (b != 0)
-    return a * b;
+    return a * b; // { dg-error "overflow in constant expression" }
   return a;
 }
 
 constexpr int q1 = fn10 (10, 10);
-constexpr int q2 = fn10 (__INT_MAX__, 2); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
-constexpr int q3 = fn10 (INT_MIN, 2); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
+constexpr int q2 = fn10 (__INT_MAX__, 2); // { dg-message "in .constexpr. expansion of " }
+constexpr int q3 = fn10 (INT_MIN, 2); // { dg-message "in .constexpr. expansion of " }
 constexpr int q4 = fn10 (-1, -1);
 
 SA (q1 == 100);
@@ -155,14 +156,14 @@ SA (q4 == 1);
 constexpr int
 fn11 (double d)
 {
-  int i = d;
+  int i = d; // { dg-error "overflow in constant expression" }
   if (i != 0)
     return i;
   return i * 2;
 }
 
 constexpr int r1 = fn11 (3.4);
-constexpr int r2 = fn11 (__builtin_inf ()); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
+constexpr int r2 = fn11 (__builtin_inf ()); // { dg-message "in .constexpr. expansion of " }
 
 constexpr int
 fn12 (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'" }
diff --git a/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc b/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc
index 5b6fea7b560..bc9b29bc39d 100644
--- a/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc
+++ b/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc
@@ -13,9 +13,11 @@ static_assert( std::gcd(LLONG_MIN, 2ull) == 2 );
 static_assert( std::gcd(2ull, LLONG_MIN) == 2 );
 
 // But |INT_MIN| cannot be represented in common_type<int, int> i.e. int.
-constexpr int a = std::gcd(INT_MIN, 1); // { dg-error "overflow" }
-constexpr int b = std::gcd(1, INT_MIN); // { dg-error "overflow" }
+constexpr int a = std::gcd(INT_MIN, 1); // { dg-error "in .constexpr." }
+constexpr int b = std::gcd(1, INT_MIN); // { dg-error "in .constexpr." }
 
 // And |LLONG_MIN| cannot be represented in long.
-constexpr long long c = std::gcd(LLONG_MIN, 1); // { dg-error "overflow" }
-constexpr long long d = std::gcd(1, LLONG_MIN); // { dg-error "overflow" }
+constexpr long long c = std::gcd(LLONG_MIN, 1); // { dg-error "in .constexpr." }
+constexpr long long d = std::gcd(1, LLONG_MIN); // { dg-error "in .constexpr." }
+
+// { dg-error "overflow" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc b/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc
index d0e032e03e0..d853974f77e 100644
--- a/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc
+++ b/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc
@@ -9,14 +9,16 @@ static_assert( std::lcm(INT_MIN, 1u) == INT_MAX+1u );
 static_assert( std::lcm(1u, INT_MIN) == INT_MAX+1u );
 
 // But |INT_MIN| cannot be represented in common_type<int, int> i.e. int.
-constexpr int a = std::lcm(INT_MIN, 1); // { dg-error "overflow" }
-constexpr int b = std::lcm(1, INT_MIN); // { dg-error "overflow" }
+constexpr int a = std::lcm(INT_MIN, 1); // { dg-error "in .constexpr." }
+constexpr int b = std::lcm(1, INT_MIN); // { dg-error "in .constexpr." }
 
 // And the LCM of 50000 and 49999 cannot be represented in int.
-constexpr int c = std::lcm(50000, 49999); // { dg-error "overflow" }
-constexpr int d = std::lcm(49999, 50000); // { dg-error "overflow" }
+constexpr int c = std::lcm(50000, 49999); // { dg-error "in .constexpr." }
+constexpr int d = std::lcm(49999, 50000); // { dg-error "in .constexpr." }
 
 // Similarly for unsigned, but the diagnostic is a failed assertion instead.
-constexpr int e = std::lcm(500000u, 499999); // { dg-error "in 'constexpr'" }
-constexpr int f = std::lcm(499999u, 500000); // { dg-error "in 'constexpr'" }
+constexpr int e = std::lcm(500000u, 499999); // { dg-error "in .constexpr." }
+constexpr int f = std::lcm(499999u, 500000); // { dg-error "in .constexpr." }
+
+// { dg-error "overflow" "" { target *-*-* } 0 }
 // { dg-error "unreachable" "" { target *-*-* } 0 }
-- 
2.41.0


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

* Re: [PATCH v4 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675]
  2023-07-20  9:35 ` [PATCH v4 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Nathaniel Shead
@ 2023-07-20 14:42   ` Jason Merrill
  2023-07-22  5:28     ` Nathaniel Shead
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Merrill @ 2023-07-20 14:42 UTC (permalink / raw)
  To: Nathaniel Shead, gcc-patches; +Cc: Patrick Palka

On 7/20/23 05:35, Nathaniel Shead wrote:
> 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
> accessing values outside their lifetime (e.g. following explicit
> destructor calls).

Incidentally, much of that should be straightforward to handle by no 
longer ignoring clobbers here:

>     case MODIFY_EXPR:
>       if (cxx_dialect < cxx14)
>         goto fail;
>       if (!RECUR (TREE_OPERAND (t, 0), any))
>         return false;
>       /* Just ignore clobbers.  */
>       if (TREE_CLOBBER_P (TREE_OPERAND (t, 1)))
>         return true;

Assignment from a clobber represents end of lifetime to the middle-end. 
This can be a follow-up patch.

> @@ -7051,10 +7065,17 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
>   	return ctx->ctor;
>         if (VAR_P (t))
>   	if (tree v = ctx->global->get_value (t))
> -	    {
> -	      r = v;
> -	      break;
> -	    }
> +	  {
> +	    r = v;
> +	    break;
> +	  }
> +      if (ctx->global->is_outside_lifetime (t))
> +	{
> +	  if (!ctx->quiet)
> +	    outside_lifetime_error (loc, t);
> +	  *non_constant_p = true;
> +	  break;
> +	}

Shouldn't this new check also be under the if (VAR_P (t))?  A CONST_DECL 
can't go out of scope.

Jason


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

* Re: [PATCH v4 2/3] c++: Improve constexpr error for dangling local variables [PR110619]
  2023-07-20  9:36 ` [PATCH v4 2/3] c++: Improve constexpr error for dangling local variables [PR110619] Nathaniel Shead
@ 2023-07-20 15:46   ` Jason Merrill
  2023-07-21  5:39     ` Nathaniel Shead
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Merrill @ 2023-07-20 15:46 UTC (permalink / raw)
  To: Nathaniel Shead, gcc-patches; +Cc: Patrick Palka

On 7/20/23 05:36, Nathaniel Shead wrote:
> 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".

This isn't an optimization, it's for safety, removing a way for an 
attacker to get a handle on other data on the stack (CWE-562).

But I agree that we need to preserve some element of UB for constexpr 
evaluation to see.

Perhaps we want to move this transformation to 
cp_maybe_instrument_return, so it happens after maybe_save_constexpr_fundef?

> 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.
> 
> 	PR c++/110619
> 
> 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/cpp1y/constexpr-lifetime6.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-110619.C: New test.
> 	* 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-110619.C    | 10 ++++++++++
>   gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C |  4 ++--
>   gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C |  8 ++++----
>   gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C |  3 ---
>   6 files changed, 23 insertions(+), 12 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C
> 
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 8fb47fd179e..107407de513 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -1260,7 +1260,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 859b133a18d..47233b3b717 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -11273,8 +11273,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;
>       }
>   
>     /* A naive attempt to reduce the number of -Wdangling-reference false
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C
> new file mode 100644
> index 00000000000..cca13302238
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C
> @@ -0,0 +1,10 @@
> +// { dg-do compile { target c++14 } }
> +// { dg-options "-Wno-return-local-addr" }
> +// PR c++/110619
> +
> +constexpr auto f() {
> +    int i = 0;
> +    return &i;
> +};
> +
> +static_assert( f() != nullptr );
> 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/cpp1y/constexpr-lifetime6.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
> index f358aff4490..b81e89af79c 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
> @@ -4,12 +4,12 @@
>   struct Empty {};
>   
>   constexpr const Empty& empty() {
> -  return Empty{};
> +  return Empty{};  // { dg-message "note: declared here" }
>   }
>   
> -constexpr const Empty& empty_parm(Empty e) {
> +constexpr const Empty& empty_parm(Empty e) {  // { dg-message "note: declared here" }
>     return e;
>   }
>   
> -constexpr Empty a = empty();  // { dg-error "" }
> -constexpr Empty b = empty_parm({});  // { dg-error "" }
> +constexpr Empty a = empty();  // { dg-error "accessing object outside its lifetime" }
> +constexpr Empty b = empty_parm({});  // { 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" } } */


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

* Re: [PATCH v4 3/3] c++: Improve location information in constant evaluation
  2023-07-20  9:37 ` [PATCH v4 3/3] c++: Improve location information in constant evaluation Nathaniel Shead
@ 2023-07-20 17:00   ` Jason Merrill
  2023-07-22  5:26     ` Nathaniel Shead
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Merrill @ 2023-07-20 17:00 UTC (permalink / raw)
  To: Nathaniel Shead, gcc-patches; +Cc: Patrick Palka

On 7/20/23 05:37, Nathaniel Shead wrote:
> This patch updates 'input_location' during constant evaluation to ensure
> that errors in subexpressions that lack location information still
> provide accurate diagnostics.
> 
> By itself this change causes some small regressions in diagnostic
> quality for circumstances where errors used 'input_location' but the
> location of the parent subexpression doesn't make sense, so this patch
> also includes a couple of other small diagnostic improvements to improve
> the most egregious cases.
> 
> gcc/cp/ChangeLog:
> 
> 	* constexpr.cc (modifying_const_object_error): Find the source
> 	location of the const object's declaration.
> 	(cxx_eval_store_expression): Fall back to the location of the
> 	target object when evaluating initialiser.

I'm skeptical about this workaround being an improvement in general. 
Reverting it, there only seems to be a difference for constexpr-89285.C, 
which seems fine; we see the location as the first line of the class, as 
usual for implicitly declared constructors.

Showing the DMI location might be an improvement, but I think it would 
be better to make that change in perform_member_init so it applies to 
runtime as well.

> 	(cxx_eval_constant_expression): Update input_location to the location
> 	of the currently evaluated expression, if possible.
> 
> libstdc++-v3/ChangeLog:
> 
> 	* testsuite/25_algorithms/equal/constexpr_neg.cc: Update diagnostic
> 	locations.
> 	* testsuite/26_numerics/gcd/105844.cc: Likewise.
> 	* testsuite/26_numerics/lcm/105844.cc: Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp0x/constexpr-48089.C: Update diagnostic locations.
> 	* g++.dg/cpp0x/constexpr-70323.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-70323a.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-delete2.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-diag3.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-ice20.C: Likewise.
> 	* g++.dg/cpp0x/constexpr-recursion.C: Likewise.
> 	* g++.dg/cpp0x/overflow1.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-89285.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-tracking-const14.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-tracking-const16.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-tracking-const18.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-tracking-const19.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-tracking-const21.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-tracking-const22.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-tracking-const3.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-tracking-const4.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-tracking-const7.C: Likewise.
> 	* g++.dg/cpp1y/constexpr-union5.C: Likewise.
> 	* g++.dg/cpp1y/pr68180.C: Likewise.
> 	* g++.dg/cpp1z/constexpr-lambda6.C: Likewise.
> 	* g++.dg/cpp1z/constexpr-lambda8.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/cpp2a/constinit10.C: Likewise.
> 	* g++.dg/cpp2a/is-corresponding-member4.C: Likewise.
> 	* g++.dg/ext/constexpr-vla2.C: Likewise.
> 	* g++.dg/ext/constexpr-vla3.C: Likewise.
> 	* g++.dg/ubsan/pr63956.C: Likewise.
> 
> Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
> ---
>   gcc/cp/constexpr.cc                           | 46 ++++++++++++++++++-
>   gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C  | 10 ++--
>   gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C  |  8 ++--
>   gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C |  8 ++--
>   .../g++.dg/cpp0x/constexpr-delete2.C          |  5 +-
>   gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C  |  2 +-
>   gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C  |  1 +
>   .../g++.dg/cpp0x/constexpr-recursion.C        |  6 +--
>   gcc/testsuite/g++.dg/cpp0x/overflow1.C        |  2 +-
>   gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C  |  5 +-
>   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 +-
>   .../g++.dg/cpp1y/constexpr-tracking-const14.C |  3 +-
>   .../g++.dg/cpp1y/constexpr-tracking-const16.C |  3 +-
>   .../g++.dg/cpp1y/constexpr-tracking-const18.C |  4 +-
>   .../g++.dg/cpp1y/constexpr-tracking-const19.C |  4 +-
>   .../g++.dg/cpp1y/constexpr-tracking-const21.C |  4 +-
>   .../g++.dg/cpp1y/constexpr-tracking-const22.C |  4 +-
>   .../g++.dg/cpp1y/constexpr-tracking-const3.C  |  3 +-
>   .../g++.dg/cpp1y/constexpr-tracking-const4.C  |  3 +-
>   .../g++.dg/cpp1y/constexpr-tracking-const7.C  |  3 +-
>   gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C |  4 +-
>   gcc/testsuite/g++.dg/cpp1y/pr68180.C          |  4 +-
>   .../g++.dg/cpp1z/constexpr-lambda6.C          |  4 +-
>   .../g++.dg/cpp1z/constexpr-lambda8.C          |  5 +-
>   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/cpp2a/constinit10.C      |  5 +-
>   .../g++.dg/cpp2a/is-corresponding-member4.C   |  4 +-
>   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          | 23 +++++-----
>   .../25_algorithms/equal/constexpr_neg.cc      |  7 +--
>   .../testsuite/26_numerics/gcd/105844.cc       | 10 ++--
>   .../testsuite/26_numerics/lcm/105844.cc       | 14 +++---
>   45 files changed, 170 insertions(+), 124 deletions(-)
> 
> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> index cd4424bcb44..63e8274e0dc 100644
> --- a/gcc/cp/constexpr.cc
> +++ b/gcc/cp/constexpr.cc
> @@ -2178,7 +2178,33 @@ modifying_const_object_error (tree expr, tree obj)
>     auto_diagnostic_group d;
>     error_at (loc, "modifying a const object %qE is not allowed in "
>   	    "a constant expression", TREE_OPERAND (expr, 0));
> -  inform (location_of (obj), "originally declared %<const%> here");
> +
> +  /* Find the underlying object that was declared as const.  */
> +  location_t decl_loc = UNKNOWN_LOCATION;
> +  for (tree probe = obj; decl_loc == UNKNOWN_LOCATION; )
> +    switch (TREE_CODE (probe))
> +      {
> +      case BIT_FIELD_REF:
> +      case COMPONENT_REF:
> +	{
> +	  tree elt = TREE_OPERAND (probe, 1);
> +	  if (CP_TYPE_CONST_P (TREE_TYPE (elt)))
> +	    decl_loc = DECL_SOURCE_LOCATION (elt);
> +	  probe = TREE_OPERAND (probe, 0);
> +	}
> +	break;
> +
> +      case ARRAY_REF:
> +      case REALPART_EXPR:
> +      case IMAGPART_EXPR:
> +	probe = TREE_OPERAND (probe, 0);
> +	break;
> +
> +      default:
> +	decl_loc = location_of (probe);
> +	break;
> +      }
> +  inform (decl_loc, "originally declared %<const%> here");
>   }
>   
>   /* Return true if FNDECL is a replaceable global allocation function that
> @@ -6276,6 +6302,21 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
>         if (TREE_CODE (init) == TARGET_EXPR)
>   	if (tree tinit = TARGET_EXPR_INITIAL (init))
>   	  init = tinit;
> +
> +      /* Improve error messages for initialisers when the initialising
> +	 expression has no location information by pointing to the decl
> +	 that is getting initialised.  */
> +      location_t target_loc = cp_expr_loc_or_input_loc (init);
> +      if (!EXPR_HAS_LOCATION (init))
> +	{
> +	  if (DECL_P (target))
> +	    target_loc = DECL_SOURCE_LOCATION (target);
> +	  else if (TREE_CODE (target) == COMPONENT_REF
> +		   || TREE_CODE (target) == BIT_FIELD_REF)
> +	    target_loc = DECL_SOURCE_LOCATION (TREE_OPERAND (target, 1));
> +	}
> +      iloc_sentinel sentinel = target_loc;
> +
>         init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue,
>   					   non_constant_p, overflow_p);
>         /* The hash table might have moved since the get earlier, and the
> @@ -6964,7 +7005,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
>         return t;
>       }
>   
> +  /* Change the input location to the currently processed expression for
> +     better error messages when a subexpression has no location.  */
>     location_t loc = cp_expr_loc_or_input_loc (t);
> +  iloc_sentinel sentinel (loc);
>   
>     STRIP_ANY_LOCATION_WRAPPER (t);
>   
> 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-70323.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C
> index 272a225d967..bfb185f2fb5 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C
> @@ -1,10 +1,10 @@
>   // PR c++/70323
>   // { dg-do compile { target c++11 } }
>   
> -constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }
> -constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }
> +constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }  // { dg-error "overflow in constant expression" }
> +constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }   // { dg-error "overflow in constant expression" }
>   
> -constexpr bool i0_0 = overflow_if_0 (0);   // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> +constexpr bool i0_0 = overflow_if_0 (0);   // { dg-message "in .constexpr. expansion of " }
>   constexpr bool i0_1 = overflow_if_0 (1);
>   constexpr bool i1_0 = overflow_if_1 (0);
> -constexpr bool i1_1 = overflow_if_1 (1);   // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> +constexpr bool i1_1 = overflow_if_1 (1);   // { dg-message "in .constexpr. expansion of " }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C
> index 1990ab6be2d..b5ed581e1c8 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C
> @@ -2,10 +2,10 @@
>   // { dg-do compile { target c++11 } }
>   // { dg-options "-Wall" }
>   
> -constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }
> -constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }
> +constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }  // { dg-error "overflow in constant expression" }
> +constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }   // { dg-error "overflow in constant expression" }
>   
> -constexpr bool i0_0 = overflow_if_0 (0);   // { dg-error "overflow in constant expression|in .constexpr. expansion of" }
> +constexpr bool i0_0 = overflow_if_0 (0);   // { dg-message "in .constexpr. expansion of" }
>   constexpr bool i0_1 = overflow_if_0 (1);
>   constexpr bool i1_0 = overflow_if_1 (0);
> -constexpr bool i1_1 = overflow_if_1 (1);   // { dg-error "overflow in constant expression|in .constexpr. expansion of" }
> +constexpr bool i1_1 = overflow_if_1 (1);   // { dg-message "in .constexpr. expansion of" }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C
> index 999f9b7851e..f2b6df2509c 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C
> @@ -6,8 +6,9 @@ constexpr int f(int i) { return i; }
>   constexpr int g(A* ap)
>   {
>     return f((delete[] ap, 42)); // { dg-message "" "" { target c++17_down } }
> +                               // { dg-error "" "" { target c++2a } .-1 }
>   }
>   
>   A a;
> -constexpr int i = g(&a);	// { dg-error "" }
> -				// { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 }
> +constexpr int i = g(&a);  // { dg-error "" "" { target c++17_down } }
> +			  // { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 }
> 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 e2d4853a284..43fa9a03c14 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
> @@ -3,5 +3,6 @@
>   
>   typedef bool (*Function)(int);
>   constexpr bool check(int x, Function p) { return p(x); }  // { dg-message "in .constexpr. expansion of" }
> +// { dg-error "not a constant expression" "" { target *-*-* } .-1 }
>   
>   static_assert(check(2, check), "");  // { dg-error "conversion|constant|in .constexpr. expansion of" }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C
> index 8c4201e1ec2..b00f8794d4c 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C
> @@ -1,6 +1,6 @@
>   // Test that we catch excessive recursion.
>   // { dg-do compile { target c++11 } }
>   // { dg-options "-fconstexpr-depth=5" }
> -// { dg-prune-output "in constexpr expansion" }
> -constexpr int f (int i) { return f (i-1); } // { dg-message "in .constexpr. expansion of " }
> -constexpr int i = f(42);	// { dg-error ".constexpr. evaluation depth|in .constexpr. expansion of " }
> +// { dg-prune-output "in .constexpr. expansion" }
> +constexpr int f (int i) { return f (i-1); } // { dg-error ".constexpr. evaluation depth" }
> +constexpr int i = f(42);	
> diff --git a/gcc/testsuite/g++.dg/cpp0x/overflow1.C b/gcc/testsuite/g++.dg/cpp0x/overflow1.C
> index b8591b4af41..e295355c88f 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/overflow1.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/overflow1.C
> @@ -4,7 +4,7 @@ template <long long i>
>   struct Fib
>   {
>       static const long long value // { dg-error "overflow" }
> -    = Fib<i-1>::value + Fib<i-2>::value;
> +    = Fib<i-1>::value + Fib<i-2>::value; // { dg-error "overflow" }
>   };
>   
>   template <>
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
> index fe0b8570ca2..d09df091fdb 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
> @@ -14,7 +14,8 @@ struct B {
>     }
>   };
>   struct C : A {
> -  B bar {this};
> +  B bar {this};  // { dg-error "" "" { target c++14_down } }
>   };
>   
> -constexpr C foo {};		// { dg-message "" }
> +// error path changes in C++17 due to `C` becoming an aggregate
> +constexpr C foo {};  // { dg-error "" "" { target c++17 } }
> 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-tracking-const14.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const14.C
> index 45c4fcf50be..0c77dd4934d 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const14.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const14.C
> @@ -14,7 +14,7 @@ struct C {
>   
>   struct A {
>     int r;
> -  const C c;
> +  const C c; // { dg-message "originally declared" }
>     constexpr A() : r(11) { r = 14; const_cast<C &>(c).n = 42; } // { dg-error "modifying a const object" }
>   };
>   
> @@ -34,5 +34,4 @@ struct B {
>   };
>   
>   constexpr B b(false); // { dg-message "in .constexpr. expansion of" }
> -// { dg-message "originally declared" "" { target *-*-* } .-1 }
>   static_assert(b.e.d.a.c.n == 2, ""); // { dg-error "non-constant condition" }
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C
> index 5a5b92bc8cc..b5ccf3a4ea5 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C
> @@ -7,7 +7,7 @@ constexpr int& impl(const int (&array)[10], int index) {
>   
>   struct A {
>     constexpr int& operator[](int i) { return impl(elems, i); }
> -  const int elems[10];
> +  const int elems[10]; // { dg-message "originally declared" }
>   };
>   
>   constexpr bool
> @@ -19,4 +19,3 @@ f()
>   }
>   
>   constexpr bool b = f(); // { dg-message "in .constexpr. expansion of " }
> -// { dg-message "originally declared" "" { target *-*-* } .-1 }
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
> index 11a680468c2..278628c121b 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
> @@ -13,11 +13,11 @@ struct array
>   template <typename T>
>   struct S {
>     using U = array<T, 4>;
> -  const U m;
> +  const U m; // { dg-message "originally declared" }
>     constexpr S(int) : m{}
>     {
>       const_cast<int &>(const_cast<const U &>(m)[0]) = 42; // { dg-error "modifying a const object" }
>     }
>   };
>   
> -constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
> +constexpr S<int> p = { 10 };
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
> index c31222ffcdd..2d18c94537b 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
> @@ -7,7 +7,7 @@ template <typename E, size_t N>
>   struct array
>   {
>     constexpr const E &operator[](size_t n) const noexcept { return elems[n]; }
> -  const E elems[N];
> +  const E elems[N]; // { dg-message "originally declared" }
>   };
>   
>   template <typename T>
> @@ -20,4 +20,4 @@ struct S {
>     }
>   };
>   
> -constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
> +constexpr S<int> p = { 10 };
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
> index 0b16193398e..d3bbcb116a6 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
> @@ -18,11 +18,11 @@ struct array2 {
>   template <typename T>
>   struct S {
>     using U = array2<T, 4>;
> -  const U m;
> +  const U m; // { dg-message "originally declared" }
>     constexpr S(int) : m{}
>     {
>       const_cast<int &>(m.a[0]) = 42; // { dg-error "modifying a const object" }
>     }
>   };
>   
> -constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
> +constexpr S<int> p = { 10 };
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
> index 216cf1607a4..27522f86dbd 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
> @@ -7,11 +7,11 @@ struct X {
>   
>   template <typename T>
>   struct S {
> -  const X x;
> +  const X x; // { dg-message "originally declared" }
>     constexpr S(int) : x{}
>     {
>       const_cast<X&>(x).i = 19; // { dg-error "modifying a const object" }
>     }
>   };
>   
> -constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
> +constexpr S<int> p = { 10 };
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C
> index 6853775c1e2..fc88dd05eef 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C
> @@ -7,7 +7,7 @@ struct A {
>   };
>   
>   struct B {
> -  const A a;
> +  const A a; // { dg-message "originally declared" }
>     constexpr B(bool b) {
>       if (b)
>         const_cast<A &>(a).n = 3; // { dg-error "modifying a const object" }
> @@ -18,5 +18,4 @@ constexpr B b(false);
>   static_assert(b.a.n == 2, "");
>   
>   constexpr B b2(true); // { dg-message "in .constexpr. expansion of " }
> -// { dg-message "originally declared" "" { target *-*-* } .-1 }
>   static_assert((b2.a.n, 1), "");
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C
> index 8263a7cc505..27fede152c7 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C
> @@ -2,7 +2,7 @@
>   // { dg-do compile { target c++14 } }
>   
>   struct A {
> -  const int n;
> +  const int n; // { dg-message "originally declared" }
>     constexpr A() : n(1) { }
>   };
>   struct B {
> @@ -13,5 +13,4 @@ struct B {
>     }
>   };
>   constexpr B b; // { dg-message "in .constexpr. expansion of " }
> -// { dg-message "originally declared" "" { target *-*-* } .-1 }
>   static_assert((b.a.n, 1), "");
> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C
> index 922e8ff126f..bea14b05602 100644
> --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C
> +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C
> @@ -3,7 +3,7 @@
>   
>   struct D { int n; };
>   
> -struct C { const D d; };
> +struct C { const D d; }; // { dg-message "originally declared" }
>   
>   struct A {
>     C c;
> @@ -19,5 +19,4 @@ struct B {
>   };
>   
>   constexpr B b{}; // { dg-message "in .constexpr. expansion of " }
> -// { dg-message "originally declared" "" { target *-*-* } .-1 }
>   static_assert((b.a.c.d.n, 1), "");
> 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/cpp1z/constexpr-lambda8.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
> index 84be68ab7f0..82351ff0c54 100644
> --- a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
> +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
> @@ -4,11 +4,14 @@
>   auto Fwd = [](int (*fp)(int), auto a) { return fp(a); };
>   auto C = [](auto a) { return a; };
>   static_assert( Fwd(C ,3) == 3); // OK
> +
>   // No specialization of the function call operator template can be constexpr
>   // (because of the local static).
>   auto NC = [](auto a) { static int s; return a; }; // { dg-error "static" }
> -// { dg-message "operator int" "" { target *-*-* } .+1 }
> +// { dg-error "called in a constant expression" "" { target *-*-* } .-1 }
> +
>   static_assert( Fwd(NC ,3) == 3); // { dg-error "" }
> +// { dg-message "operator int" "" { target *-*-* } .-1 }
>   
>   // We look for the string "operator int" to check that we aren't trying to do
>   // template pretty-printing in an expression; that gets incredibly unwieldy
> 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/cpp2a/constinit10.C b/gcc/testsuite/g++.dg/cpp2a/constinit10.C
> index b678788541e..d2c49c41f91 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/constinit10.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/constinit10.C
> @@ -11,15 +11,14 @@ struct S1
>   struct alignas(64) S2
>   {
>       constexpr S2 ()
> -    : m_tabS1()
> +    : m_tabS1() // { dg-error "used before its definition" }
>       {}
>   
>       S1 m_tabS1[7];
>   };
>   
>   constinit S2 objX; // { dg-error ".constinit. variable .objX. does not have a constant initializer" }
> -// { dg-error "used before its definition" "" { target *-*-* } .-1 }
> -// // { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-2 }
> +// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
>   
>   constexpr S1::S1 ()
>   : m_i(14)
> diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C
> index 6b74090306b..241e9976e8c 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C
> @@ -14,8 +14,8 @@ is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
>   struct A { int a; };
>   struct B;
>   constexpr int B::*n = nullptr;
> -constexpr auto a = std::is_corresponding_member (&A::a, n);	// { dg-error "invalid use of incomplete type 'struct B'" }
> -constexpr auto b = std::is_corresponding_member (n, &A::a);	// { dg-error "invalid use of incomplete type 'struct B'" }
> +constexpr auto a = std::is_corresponding_member (&A::a, n);	// { dg-message "in .constexpr. expansion of" }
> +constexpr auto b = std::is_corresponding_member (n, &A::a);	// { dg-message "in .constexpr. expansion of" }
>   
>   void
>   foo (int B::*m)
> 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..6fd0b4f893e 100644
> --- a/gcc/testsuite/g++.dg/ubsan/pr63956.C
> +++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C
> @@ -56,12 +56,13 @@ fn3 (int a, int b)
>   {
>     if (b != 2)
>       a = a / b; // { dg-error "..7 / 0.. is not a constant expression" }
> +               // { dg-error "overflow in constant expression" "" { target *-*-* } .-1 }
>     return a;
>   }
>   
>   constexpr int k1 = fn3 (8, 4);
>   constexpr int k2 = fn3 (7, 0); // { dg-message "in .constexpr. expansion" }
> -constexpr int k3 = fn3 (INT_MIN, -1); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> +constexpr int k3 = fn3 (INT_MIN, -1); // { dg-message "in .constexpr. expansion of " }
>   
>   SA (k1 == 2);
>   
> @@ -100,13 +101,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)
> @@ -122,15 +123,15 @@ constexpr int
>   fn9 (int a, int b)
>   {
>     if (b != 0)
> -    return a + b;
> +    return a + b; // { dg-error "overflow in constant expression" }
>     return a;
>   }
>   
>   constexpr int p1 = fn9 (42, 7);
> -constexpr int p2 = fn9 (__INT_MAX__, 1); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> +constexpr int p2 = fn9 (__INT_MAX__, 1); // { dg-message "in .constexpr. expansion of " }
>   constexpr int p3 = fn9 (__INT_MAX__, -1);
>   constexpr int p4 = fn9 (INT_MIN, 1);
> -constexpr int p5 = fn9 (INT_MIN, -1); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> +constexpr int p5 = fn9 (INT_MIN, -1); // { dg-message "in .constexpr. expansion of " }
>   
>   SA (p1 == 49);
>   SA (p3 == __INT_MAX__ - 1);
> @@ -140,13 +141,13 @@ constexpr int
>   fn10 (int a, int b)
>   {
>     if (b != 0)
> -    return a * b;
> +    return a * b; // { dg-error "overflow in constant expression" }
>     return a;
>   }
>   
>   constexpr int q1 = fn10 (10, 10);
> -constexpr int q2 = fn10 (__INT_MAX__, 2); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> -constexpr int q3 = fn10 (INT_MIN, 2); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> +constexpr int q2 = fn10 (__INT_MAX__, 2); // { dg-message "in .constexpr. expansion of " }
> +constexpr int q3 = fn10 (INT_MIN, 2); // { dg-message "in .constexpr. expansion of " }
>   constexpr int q4 = fn10 (-1, -1);
>   
>   SA (q1 == 100);
> @@ -155,14 +156,14 @@ SA (q4 == 1);
>   constexpr int
>   fn11 (double d)
>   {
> -  int i = d;
> +  int i = d; // { dg-error "overflow in constant expression" }
>     if (i != 0)
>       return i;
>     return i * 2;
>   }
>   
>   constexpr int r1 = fn11 (3.4);
> -constexpr int r2 = fn11 (__builtin_inf ()); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> +constexpr int r2 = fn11 (__builtin_inf ()); // { dg-message "in .constexpr. expansion of " }
>   
>   constexpr int
>   fn12 (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'" }
> diff --git a/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc b/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc
> index 5b6fea7b560..bc9b29bc39d 100644
> --- a/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc
> +++ b/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc
> @@ -13,9 +13,11 @@ static_assert( std::gcd(LLONG_MIN, 2ull) == 2 );
>   static_assert( std::gcd(2ull, LLONG_MIN) == 2 );
>   
>   // But |INT_MIN| cannot be represented in common_type<int, int> i.e. int.
> -constexpr int a = std::gcd(INT_MIN, 1); // { dg-error "overflow" }
> -constexpr int b = std::gcd(1, INT_MIN); // { dg-error "overflow" }
> +constexpr int a = std::gcd(INT_MIN, 1); // { dg-error "in .constexpr." }
> +constexpr int b = std::gcd(1, INT_MIN); // { dg-error "in .constexpr." }
>   
>   // And |LLONG_MIN| cannot be represented in long.
> -constexpr long long c = std::gcd(LLONG_MIN, 1); // { dg-error "overflow" }
> -constexpr long long d = std::gcd(1, LLONG_MIN); // { dg-error "overflow" }
> +constexpr long long c = std::gcd(LLONG_MIN, 1); // { dg-error "in .constexpr." }
> +constexpr long long d = std::gcd(1, LLONG_MIN); // { dg-error "in .constexpr." }
> +
> +// { dg-error "overflow" "" { target *-*-* } 0 }
> diff --git a/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc b/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc
> index d0e032e03e0..d853974f77e 100644
> --- a/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc
> +++ b/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc
> @@ -9,14 +9,16 @@ static_assert( std::lcm(INT_MIN, 1u) == INT_MAX+1u );
>   static_assert( std::lcm(1u, INT_MIN) == INT_MAX+1u );
>   
>   // But |INT_MIN| cannot be represented in common_type<int, int> i.e. int.
> -constexpr int a = std::lcm(INT_MIN, 1); // { dg-error "overflow" }
> -constexpr int b = std::lcm(1, INT_MIN); // { dg-error "overflow" }
> +constexpr int a = std::lcm(INT_MIN, 1); // { dg-error "in .constexpr." }
> +constexpr int b = std::lcm(1, INT_MIN); // { dg-error "in .constexpr." }
>   
>   // And the LCM of 50000 and 49999 cannot be represented in int.
> -constexpr int c = std::lcm(50000, 49999); // { dg-error "overflow" }
> -constexpr int d = std::lcm(49999, 50000); // { dg-error "overflow" }
> +constexpr int c = std::lcm(50000, 49999); // { dg-error "in .constexpr." }
> +constexpr int d = std::lcm(49999, 50000); // { dg-error "in .constexpr." }
>   
>   // Similarly for unsigned, but the diagnostic is a failed assertion instead.
> -constexpr int e = std::lcm(500000u, 499999); // { dg-error "in 'constexpr'" }
> -constexpr int f = std::lcm(499999u, 500000); // { dg-error "in 'constexpr'" }
> +constexpr int e = std::lcm(500000u, 499999); // { dg-error "in .constexpr." }
> +constexpr int f = std::lcm(499999u, 500000); // { dg-error "in .constexpr." }
> +
> +// { dg-error "overflow" "" { target *-*-* } 0 }
>   // { dg-error "unreachable" "" { target *-*-* } 0 }


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

* Re: [PATCH v4 2/3] c++: Improve constexpr error for dangling local variables [PR110619]
  2023-07-20 15:46   ` Jason Merrill
@ 2023-07-21  5:39     ` Nathaniel Shead
  2023-07-21 21:44       ` Jason Merrill
  0 siblings, 1 reply; 12+ messages in thread
From: Nathaniel Shead @ 2023-07-21  5:39 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Patrick Palka

On Thu, Jul 20, 2023 at 11:46:47AM -0400, Jason Merrill wrote:
> On 7/20/23 05:36, Nathaniel Shead wrote:
> > 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".
> 
> This isn't an optimization, it's for safety, removing a way for an attacker
> to get a handle on other data on the stack (CWE-562).
> 
> But I agree that we need to preserve some element of UB for constexpr
> evaluation to see.
> 
> Perhaps we want to move this transformation to cp_maybe_instrument_return,
> so it happens after maybe_save_constexpr_fundef?

Hm, OK. I can try giving this a go. I guess I should move the entire
maybe_warn_about_returning_address_of_local function to cp-gimplify.cc
to be able to detect this? Or is there a better way of marking that a
return expression will return a reference to a local for this
transformation? (I guess I can't use whether the warning has been
surpressed or not because the warning might not be enabled at all.)

It looks like this warning is raised also by diag_return_locals in
gimple-ssa-isolate-paths, should the transformation also be made here?

I note that the otherwise very similar -Wdangling-pointer warning
doesn't do this transformation either, should that also be something I
look into fixing here?

> > 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.
> > 
> > 	PR c++/110619
> > 
> > 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/cpp1y/constexpr-lifetime6.C: Likewise.
> > 	* g++.dg/cpp1y/constexpr-110619.C: New test.
> > 	* 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-110619.C    | 10 ++++++++++
> >   gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime5.C |  4 ++--
> >   gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C |  8 ++++----
> >   gcc/testsuite/g++.dg/warn/Wreturn-local-addr-6.C |  3 ---
> >   6 files changed, 23 insertions(+), 12 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C
> > 
> > diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> > index 8fb47fd179e..107407de513 100644
> > --- a/gcc/cp/semantics.cc
> > +++ b/gcc/cp/semantics.cc
> > @@ -1260,7 +1260,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 859b133a18d..47233b3b717 100644
> > --- a/gcc/cp/typeck.cc
> > +++ b/gcc/cp/typeck.cc
> > @@ -11273,8 +11273,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;
> >       }
> >     /* A naive attempt to reduce the number of -Wdangling-reference false
> > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C
> > new file mode 100644
> > index 00000000000..cca13302238
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-110619.C
> > @@ -0,0 +1,10 @@
> > +// { dg-do compile { target c++14 } }
> > +// { dg-options "-Wno-return-local-addr" }
> > +// PR c++/110619
> > +
> > +constexpr auto f() {
> > +    int i = 0;
> > +    return &i;
> > +};
> > +
> > +static_assert( f() != nullptr );
> > 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/cpp1y/constexpr-lifetime6.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
> > index f358aff4490..b81e89af79c 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-lifetime6.C
> > @@ -4,12 +4,12 @@
> >   struct Empty {};
> >   constexpr const Empty& empty() {
> > -  return Empty{};
> > +  return Empty{};  // { dg-message "note: declared here" }
> >   }
> > -constexpr const Empty& empty_parm(Empty e) {
> > +constexpr const Empty& empty_parm(Empty e) {  // { dg-message "note: declared here" }
> >     return e;
> >   }
> > -constexpr Empty a = empty();  // { dg-error "" }
> > -constexpr Empty b = empty_parm({});  // { dg-error "" }
> > +constexpr Empty a = empty();  // { dg-error "accessing object outside its lifetime" }
> > +constexpr Empty b = empty_parm({});  // { 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" } } */
> 

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

* Re: [PATCH v4 2/3] c++: Improve constexpr error for dangling local variables [PR110619]
  2023-07-21  5:39     ` Nathaniel Shead
@ 2023-07-21 21:44       ` Jason Merrill
  2023-07-22  5:20         ` Nathaniel Shead
  0 siblings, 1 reply; 12+ messages in thread
From: Jason Merrill @ 2023-07-21 21:44 UTC (permalink / raw)
  To: Nathaniel Shead; +Cc: gcc-patches, Patrick Palka

On 7/21/23 01:39, Nathaniel Shead wrote:
> On Thu, Jul 20, 2023 at 11:46:47AM -0400, Jason Merrill wrote:
>> On 7/20/23 05:36, Nathaniel Shead wrote:
>>> 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".
>>
>> This isn't an optimization, it's for safety, removing a way for an attacker
>> to get a handle on other data on the stack (CWE-562).
>>
>> But I agree that we need to preserve some element of UB for constexpr
>> evaluation to see.
>>
>> Perhaps we want to move this transformation to cp_maybe_instrument_return,
>> so it happens after maybe_save_constexpr_fundef?
> 
> Hm, OK. I can try giving this a go. I guess I should move the entire
> maybe_warn_about_returning_address_of_local function to cp-gimplify.cc
> to be able to detect this? Or is there a better way of marking that a
> return expression will return a reference to a local for this
> transformation? (I guess I can't use whether the warning has been
> surpressed or not because the warning might not be enabled at all.)

You could use a TREE_LANG_FLAG, looks like none of them are used on 
RETURN_EXPR.

> It looks like this warning is raised also by diag_return_locals in
> gimple-ssa-isolate-paths, should the transformation also be made here?

Looks like it already is, in warn_return_addr_local:

>       tree zero = build_zero_cst (TREE_TYPE (val));
>       gimple_return_set_retval (return_stmt, zero);
>       update_stmt (return_stmt);

...but, weirdly, only with -fisolate-erroneous-paths-*, even though it 
isn't isolating anything.  Perhaps there should be another flag for this.

> I note that the otherwise very similar -Wdangling-pointer warning
> doesn't do this transformation either, should that also be something I
> look into fixing here?

With that same flag, perhaps.  I wonder if it would make sense to remove 
the isolate-paths handling of locals in favor of the dangling-pointer 
handling?  I don't know either file much at all.

Jason


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

* Re: [PATCH v4 2/3] c++: Improve constexpr error for dangling local variables [PR110619]
  2023-07-21 21:44       ` Jason Merrill
@ 2023-07-22  5:20         ` Nathaniel Shead
  0 siblings, 0 replies; 12+ messages in thread
From: Nathaniel Shead @ 2023-07-22  5:20 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Patrick Palka

On Fri, Jul 21, 2023 at 05:44:51PM -0400, Jason Merrill wrote:
> On 7/21/23 01:39, Nathaniel Shead wrote:
> > On Thu, Jul 20, 2023 at 11:46:47AM -0400, Jason Merrill wrote:
> > > On 7/20/23 05:36, Nathaniel Shead wrote:
> > > > 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".
> > > 
> > > This isn't an optimization, it's for safety, removing a way for an attacker
> > > to get a handle on other data on the stack (CWE-562).
> > > 
> > > But I agree that we need to preserve some element of UB for constexpr
> > > evaluation to see.
> > > 
> > > Perhaps we want to move this transformation to cp_maybe_instrument_return,
> > > so it happens after maybe_save_constexpr_fundef?
> > 
> > Hm, OK. I can try giving this a go. I guess I should move the entire
> > maybe_warn_about_returning_address_of_local function to cp-gimplify.cc
> > to be able to detect this? Or is there a better way of marking that a
> > return expression will return a reference to a local for this
> > transformation? (I guess I can't use whether the warning has been
> > surpressed or not because the warning might not be enabled at all.)
> 
> You could use a TREE_LANG_FLAG, looks like none of them are used on
> RETURN_EXPR.
> 
> > It looks like this warning is raised also by diag_return_locals in
> > gimple-ssa-isolate-paths, should the transformation also be made here?
> 
> Looks like it already is, in warn_return_addr_local:
> 
> >       tree zero = build_zero_cst (TREE_TYPE (val));
> >       gimple_return_set_retval (return_stmt, zero);
> >       update_stmt (return_stmt);
> 
> ...but, weirdly, only with -fisolate-erroneous-paths-*, even though it isn't
> isolating anything.  Perhaps there should be another flag for this.
> 

I see, thanks. From this I've found that my above patch isn't sufficient
anyway, as compiling with -O2 causes the warning to appear twice as the
suppression I did wasn't sufficient. As such I'll exclude this patch
from the next revision since it's not actually necessary for the problem
I was trying to solve, and I'll work on trying to solve this properly
a bit later.

> > I note that the otherwise very similar -Wdangling-pointer warning
> > doesn't do this transformation either, should that also be something I
> > look into fixing here?
> 
> With that same flag, perhaps.  I wonder if it would make sense to remove the
> isolate-paths handling of locals in favor of the dangling-pointer handling?
> I don't know either file much at all.
> 
> Jason
> 

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

* Re: [PATCH v4 3/3] c++: Improve location information in constant evaluation
  2023-07-20 17:00   ` Jason Merrill
@ 2023-07-22  5:26     ` Nathaniel Shead
  0 siblings, 0 replies; 12+ messages in thread
From: Nathaniel Shead @ 2023-07-22  5:26 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Patrick Palka

On Fri, Jul 21, 2023 at 3:00 AM Jason Merrill <jason@redhat.com> wrote:
>
> On 7/20/23 05:37, Nathaniel Shead wrote:
> > This patch updates 'input_location' during constant evaluation to ensure
> > that errors in subexpressions that lack location information still
> > provide accurate diagnostics.
> >
> > By itself this change causes some small regressions in diagnostic
> > quality for circumstances where errors used 'input_location' but the
> > location of the parent subexpression doesn't make sense, so this patch
> > also includes a couple of other small diagnostic improvements to improve
> > the most egregious cases.
> >
> > gcc/cp/ChangeLog:
> >
> >       * constexpr.cc (modifying_const_object_error): Find the source
> >       location of the const object's declaration.
> >       (cxx_eval_store_expression): Fall back to the location of the
> >       target object when evaluating initialiser.
>
> I'm skeptical about this workaround being an improvement in general.
> Reverting it, there only seems to be a difference for constexpr-89285.C,
> which seems fine; we see the location as the first line of the class, as
> usual for implicitly declared constructors.
>
> Showing the DMI location might be an improvement, but I think it would
> be better to make that change in perform_member_init so it applies to
> runtime as well.

Makes sense, I'll get rid of it.

> >       (cxx_eval_constant_expression): Update input_location to the location
> >       of the currently evaluated expression, if possible.
> >
> > libstdc++-v3/ChangeLog:
> >
> >       * testsuite/25_algorithms/equal/constexpr_neg.cc: Update diagnostic
> >       locations.
> >       * testsuite/26_numerics/gcd/105844.cc: Likewise.
> >       * testsuite/26_numerics/lcm/105844.cc: Likewise.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * g++.dg/cpp0x/constexpr-48089.C: Update diagnostic locations.
> >       * g++.dg/cpp0x/constexpr-70323.C: Likewise.
> >       * g++.dg/cpp0x/constexpr-70323a.C: Likewise.
> >       * g++.dg/cpp0x/constexpr-delete2.C: Likewise.
> >       * g++.dg/cpp0x/constexpr-diag3.C: Likewise.
> >       * g++.dg/cpp0x/constexpr-ice20.C: Likewise.
> >       * g++.dg/cpp0x/constexpr-recursion.C: Likewise.
> >       * g++.dg/cpp0x/overflow1.C: Likewise.
> >       * g++.dg/cpp1y/constexpr-89285.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-tracking-const14.C: Likewise.
> >       * g++.dg/cpp1y/constexpr-tracking-const16.C: Likewise.
> >       * g++.dg/cpp1y/constexpr-tracking-const18.C: Likewise.
> >       * g++.dg/cpp1y/constexpr-tracking-const19.C: Likewise.
> >       * g++.dg/cpp1y/constexpr-tracking-const21.C: Likewise.
> >       * g++.dg/cpp1y/constexpr-tracking-const22.C: Likewise.
> >       * g++.dg/cpp1y/constexpr-tracking-const3.C: Likewise.
> >       * g++.dg/cpp1y/constexpr-tracking-const4.C: Likewise.
> >       * g++.dg/cpp1y/constexpr-tracking-const7.C: Likewise.
> >       * g++.dg/cpp1y/constexpr-union5.C: Likewise.
> >       * g++.dg/cpp1y/pr68180.C: Likewise.
> >       * g++.dg/cpp1z/constexpr-lambda6.C: Likewise.
> >       * g++.dg/cpp1z/constexpr-lambda8.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/cpp2a/constinit10.C: Likewise.
> >       * g++.dg/cpp2a/is-corresponding-member4.C: Likewise.
> >       * g++.dg/ext/constexpr-vla2.C: Likewise.
> >       * g++.dg/ext/constexpr-vla3.C: Likewise.
> >       * g++.dg/ubsan/pr63956.C: Likewise.
> >
> > Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
> > ---
> >   gcc/cp/constexpr.cc                           | 46 ++++++++++++++++++-
> >   gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C  | 10 ++--
> >   gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C  |  8 ++--
> >   gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C |  8 ++--
> >   .../g++.dg/cpp0x/constexpr-delete2.C          |  5 +-
> >   gcc/testsuite/g++.dg/cpp0x/constexpr-diag3.C  |  2 +-
> >   gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C  |  1 +
> >   .../g++.dg/cpp0x/constexpr-recursion.C        |  6 +--
> >   gcc/testsuite/g++.dg/cpp0x/overflow1.C        |  2 +-
> >   gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C  |  5 +-
> >   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 +-
> >   .../g++.dg/cpp1y/constexpr-tracking-const14.C |  3 +-
> >   .../g++.dg/cpp1y/constexpr-tracking-const16.C |  3 +-
> >   .../g++.dg/cpp1y/constexpr-tracking-const18.C |  4 +-
> >   .../g++.dg/cpp1y/constexpr-tracking-const19.C |  4 +-
> >   .../g++.dg/cpp1y/constexpr-tracking-const21.C |  4 +-
> >   .../g++.dg/cpp1y/constexpr-tracking-const22.C |  4 +-
> >   .../g++.dg/cpp1y/constexpr-tracking-const3.C  |  3 +-
> >   .../g++.dg/cpp1y/constexpr-tracking-const4.C  |  3 +-
> >   .../g++.dg/cpp1y/constexpr-tracking-const7.C  |  3 +-
> >   gcc/testsuite/g++.dg/cpp1y/constexpr-union5.C |  4 +-
> >   gcc/testsuite/g++.dg/cpp1y/pr68180.C          |  4 +-
> >   .../g++.dg/cpp1z/constexpr-lambda6.C          |  4 +-
> >   .../g++.dg/cpp1z/constexpr-lambda8.C          |  5 +-
> >   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/cpp2a/constinit10.C      |  5 +-
> >   .../g++.dg/cpp2a/is-corresponding-member4.C   |  4 +-
> >   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          | 23 +++++-----
> >   .../25_algorithms/equal/constexpr_neg.cc      |  7 +--
> >   .../testsuite/26_numerics/gcd/105844.cc       | 10 ++--
> >   .../testsuite/26_numerics/lcm/105844.cc       | 14 +++---
> >   45 files changed, 170 insertions(+), 124 deletions(-)
> >
> > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> > index cd4424bcb44..63e8274e0dc 100644
> > --- a/gcc/cp/constexpr.cc
> > +++ b/gcc/cp/constexpr.cc
> > @@ -2178,7 +2178,33 @@ modifying_const_object_error (tree expr, tree obj)
> >     auto_diagnostic_group d;
> >     error_at (loc, "modifying a const object %qE is not allowed in "
> >           "a constant expression", TREE_OPERAND (expr, 0));
> > -  inform (location_of (obj), "originally declared %<const%> here");
> > +
> > +  /* Find the underlying object that was declared as const.  */
> > +  location_t decl_loc = UNKNOWN_LOCATION;
> > +  for (tree probe = obj; decl_loc == UNKNOWN_LOCATION; )
> > +    switch (TREE_CODE (probe))
> > +      {
> > +      case BIT_FIELD_REF:
> > +      case COMPONENT_REF:
> > +     {
> > +       tree elt = TREE_OPERAND (probe, 1);
> > +       if (CP_TYPE_CONST_P (TREE_TYPE (elt)))
> > +         decl_loc = DECL_SOURCE_LOCATION (elt);
> > +       probe = TREE_OPERAND (probe, 0);
> > +     }
> > +     break;
> > +
> > +      case ARRAY_REF:
> > +      case REALPART_EXPR:
> > +      case IMAGPART_EXPR:
> > +     probe = TREE_OPERAND (probe, 0);
> > +     break;
> > +
> > +      default:
> > +     decl_loc = location_of (probe);
> > +     break;
> > +      }
> > +  inform (decl_loc, "originally declared %<const%> here");
> >   }
> >
> >   /* Return true if FNDECL is a replaceable global allocation function that
> > @@ -6276,6 +6302,21 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
> >         if (TREE_CODE (init) == TARGET_EXPR)
> >       if (tree tinit = TARGET_EXPR_INITIAL (init))
> >         init = tinit;
> > +
> > +      /* Improve error messages for initialisers when the initialising
> > +      expression has no location information by pointing to the decl
> > +      that is getting initialised.  */
> > +      location_t target_loc = cp_expr_loc_or_input_loc (init);
> > +      if (!EXPR_HAS_LOCATION (init))
> > +     {
> > +       if (DECL_P (target))
> > +         target_loc = DECL_SOURCE_LOCATION (target);
> > +       else if (TREE_CODE (target) == COMPONENT_REF
> > +                || TREE_CODE (target) == BIT_FIELD_REF)
> > +         target_loc = DECL_SOURCE_LOCATION (TREE_OPERAND (target, 1));
> > +     }
> > +      iloc_sentinel sentinel = target_loc;
> > +
> >         init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue,
> >                                          non_constant_p, overflow_p);
> >         /* The hash table might have moved since the get earlier, and the
> > @@ -6964,7 +7005,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
> >         return t;
> >       }
> >
> > +  /* Change the input location to the currently processed expression for
> > +     better error messages when a subexpression has no location.  */
> >     location_t loc = cp_expr_loc_or_input_loc (t);
> > +  iloc_sentinel sentinel (loc);
> >
> >     STRIP_ANY_LOCATION_WRAPPER (t);
> >
> > 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-70323.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C
> > index 272a225d967..bfb185f2fb5 100644
> > --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C
> > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323.C
> > @@ -1,10 +1,10 @@
> >   // PR c++/70323
> >   // { dg-do compile { target c++11 } }
> >
> > -constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }
> > -constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }
> > +constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }  // { dg-error "overflow in constant expression" }
> > +constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }   // { dg-error "overflow in constant expression" }
> >
> > -constexpr bool i0_0 = overflow_if_0 (0);   // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> > +constexpr bool i0_0 = overflow_if_0 (0);   // { dg-message "in .constexpr. expansion of " }
> >   constexpr bool i0_1 = overflow_if_0 (1);
> >   constexpr bool i1_0 = overflow_if_1 (0);
> > -constexpr bool i1_1 = overflow_if_1 (1);   // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> > +constexpr bool i1_1 = overflow_if_1 (1);   // { dg-message "in .constexpr. expansion of " }
> > diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C
> > index 1990ab6be2d..b5ed581e1c8 100644
> > --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C
> > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-70323a.C
> > @@ -2,10 +2,10 @@
> >   // { dg-do compile { target c++11 } }
> >   // { dg-options "-Wall" }
> >
> > -constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }
> > -constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }
> > +constexpr int overflow_if_0 (int i) { return __INT_MAX__ + !i; }  // { dg-error "overflow in constant expression" }
> > +constexpr int overflow_if_1 (int i) { return __INT_MAX__ + i; }   // { dg-error "overflow in constant expression" }
> >
> > -constexpr bool i0_0 = overflow_if_0 (0);   // { dg-error "overflow in constant expression|in .constexpr. expansion of" }
> > +constexpr bool i0_0 = overflow_if_0 (0);   // { dg-message "in .constexpr. expansion of" }
> >   constexpr bool i0_1 = overflow_if_0 (1);
> >   constexpr bool i1_0 = overflow_if_1 (0);
> > -constexpr bool i1_1 = overflow_if_1 (1);   // { dg-error "overflow in constant expression|in .constexpr. expansion of" }
> > +constexpr bool i1_1 = overflow_if_1 (1);   // { dg-message "in .constexpr. expansion of" }
> > diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C
> > index 999f9b7851e..f2b6df2509c 100644
> > --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C
> > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-delete2.C
> > @@ -6,8 +6,9 @@ constexpr int f(int i) { return i; }
> >   constexpr int g(A* ap)
> >   {
> >     return f((delete[] ap, 42)); // { dg-message "" "" { target c++17_down } }
> > +                               // { dg-error "" "" { target c++2a } .-1 }
> >   }
> >
> >   A a;
> > -constexpr int i = g(&a);     // { dg-error "" }
> > -                             // { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 }
> > +constexpr int i = g(&a);  // { dg-error "" "" { target c++17_down } }
> > +                       // { dg-message "in 'constexpr' expansion of" "" { target c++2a } .-1 }
> > 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 e2d4853a284..43fa9a03c14 100644
> > --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
> > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice20.C
> > @@ -3,5 +3,6 @@
> >
> >   typedef bool (*Function)(int);
> >   constexpr bool check(int x, Function p) { return p(x); }  // { dg-message "in .constexpr. expansion of" }
> > +// { dg-error "not a constant expression" "" { target *-*-* } .-1 }
> >
> >   static_assert(check(2, check), "");  // { dg-error "conversion|constant|in .constexpr. expansion of" }
> > diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C
> > index 8c4201e1ec2..b00f8794d4c 100644
> > --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C
> > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-recursion.C
> > @@ -1,6 +1,6 @@
> >   // Test that we catch excessive recursion.
> >   // { dg-do compile { target c++11 } }
> >   // { dg-options "-fconstexpr-depth=5" }
> > -// { dg-prune-output "in constexpr expansion" }
> > -constexpr int f (int i) { return f (i-1); } // { dg-message "in .constexpr. expansion of " }
> > -constexpr int i = f(42);     // { dg-error ".constexpr. evaluation depth|in .constexpr. expansion of " }
> > +// { dg-prune-output "in .constexpr. expansion" }
> > +constexpr int f (int i) { return f (i-1); } // { dg-error ".constexpr. evaluation depth" }
> > +constexpr int i = f(42);
> > diff --git a/gcc/testsuite/g++.dg/cpp0x/overflow1.C b/gcc/testsuite/g++.dg/cpp0x/overflow1.C
> > index b8591b4af41..e295355c88f 100644
> > --- a/gcc/testsuite/g++.dg/cpp0x/overflow1.C
> > +++ b/gcc/testsuite/g++.dg/cpp0x/overflow1.C
> > @@ -4,7 +4,7 @@ template <long long i>
> >   struct Fib
> >   {
> >       static const long long value // { dg-error "overflow" }
> > -    = Fib<i-1>::value + Fib<i-2>::value;
> > +    = Fib<i-1>::value + Fib<i-2>::value; // { dg-error "overflow" }
> >   };
> >
> >   template <>
> > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
> > index fe0b8570ca2..d09df091fdb 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C
> > @@ -14,7 +14,8 @@ struct B {
> >     }
> >   };
> >   struct C : A {
> > -  B bar {this};
> > +  B bar {this};  // { dg-error "" "" { target c++14_down } }
> >   };
> >
> > -constexpr C foo {};          // { dg-message "" }
> > +// error path changes in C++17 due to `C` becoming an aggregate
> > +constexpr C foo {};  // { dg-error "" "" { target c++17 } }
> > 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-tracking-const14.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const14.C
> > index 45c4fcf50be..0c77dd4934d 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const14.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const14.C
> > @@ -14,7 +14,7 @@ struct C {
> >
> >   struct A {
> >     int r;
> > -  const C c;
> > +  const C c; // { dg-message "originally declared" }
> >     constexpr A() : r(11) { r = 14; const_cast<C &>(c).n = 42; } // { dg-error "modifying a const object" }
> >   };
> >
> > @@ -34,5 +34,4 @@ struct B {
> >   };
> >
> >   constexpr B b(false); // { dg-message "in .constexpr. expansion of" }
> > -// { dg-message "originally declared" "" { target *-*-* } .-1 }
> >   static_assert(b.e.d.a.c.n == 2, ""); // { dg-error "non-constant condition" }
> > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C
> > index 5a5b92bc8cc..b5ccf3a4ea5 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const16.C
> > @@ -7,7 +7,7 @@ constexpr int& impl(const int (&array)[10], int index) {
> >
> >   struct A {
> >     constexpr int& operator[](int i) { return impl(elems, i); }
> > -  const int elems[10];
> > +  const int elems[10]; // { dg-message "originally declared" }
> >   };
> >
> >   constexpr bool
> > @@ -19,4 +19,3 @@ f()
> >   }
> >
> >   constexpr bool b = f(); // { dg-message "in .constexpr. expansion of " }
> > -// { dg-message "originally declared" "" { target *-*-* } .-1 }
> > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
> > index 11a680468c2..278628c121b 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const18.C
> > @@ -13,11 +13,11 @@ struct array
> >   template <typename T>
> >   struct S {
> >     using U = array<T, 4>;
> > -  const U m;
> > +  const U m; // { dg-message "originally declared" }
> >     constexpr S(int) : m{}
> >     {
> >       const_cast<int &>(const_cast<const U &>(m)[0]) = 42; // { dg-error "modifying a const object" }
> >     }
> >   };
> >
> > -constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
> > +constexpr S<int> p = { 10 };
> > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
> > index c31222ffcdd..2d18c94537b 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const19.C
> > @@ -7,7 +7,7 @@ template <typename E, size_t N>
> >   struct array
> >   {
> >     constexpr const E &operator[](size_t n) const noexcept { return elems[n]; }
> > -  const E elems[N];
> > +  const E elems[N]; // { dg-message "originally declared" }
> >   };
> >
> >   template <typename T>
> > @@ -20,4 +20,4 @@ struct S {
> >     }
> >   };
> >
> > -constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
> > +constexpr S<int> p = { 10 };
> > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
> > index 0b16193398e..d3bbcb116a6 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const21.C
> > @@ -18,11 +18,11 @@ struct array2 {
> >   template <typename T>
> >   struct S {
> >     using U = array2<T, 4>;
> > -  const U m;
> > +  const U m; // { dg-message "originally declared" }
> >     constexpr S(int) : m{}
> >     {
> >       const_cast<int &>(m.a[0]) = 42; // { dg-error "modifying a const object" }
> >     }
> >   };
> >
> > -constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
> > +constexpr S<int> p = { 10 };
> > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
> > index 216cf1607a4..27522f86dbd 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const22.C
> > @@ -7,11 +7,11 @@ struct X {
> >
> >   template <typename T>
> >   struct S {
> > -  const X x;
> > +  const X x; // { dg-message "originally declared" }
> >     constexpr S(int) : x{}
> >     {
> >       const_cast<X&>(x).i = 19; // { dg-error "modifying a const object" }
> >     }
> >   };
> >
> > -constexpr S<int> p = { 10 }; // { dg-message "originally declared" }
> > +constexpr S<int> p = { 10 };
> > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C
> > index 6853775c1e2..fc88dd05eef 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const3.C
> > @@ -7,7 +7,7 @@ struct A {
> >   };
> >
> >   struct B {
> > -  const A a;
> > +  const A a; // { dg-message "originally declared" }
> >     constexpr B(bool b) {
> >       if (b)
> >         const_cast<A &>(a).n = 3; // { dg-error "modifying a const object" }
> > @@ -18,5 +18,4 @@ constexpr B b(false);
> >   static_assert(b.a.n == 2, "");
> >
> >   constexpr B b2(true); // { dg-message "in .constexpr. expansion of " }
> > -// { dg-message "originally declared" "" { target *-*-* } .-1 }
> >   static_assert((b2.a.n, 1), "");
> > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C
> > index 8263a7cc505..27fede152c7 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const4.C
> > @@ -2,7 +2,7 @@
> >   // { dg-do compile { target c++14 } }
> >
> >   struct A {
> > -  const int n;
> > +  const int n; // { dg-message "originally declared" }
> >     constexpr A() : n(1) { }
> >   };
> >   struct B {
> > @@ -13,5 +13,4 @@ struct B {
> >     }
> >   };
> >   constexpr B b; // { dg-message "in .constexpr. expansion of " }
> > -// { dg-message "originally declared" "" { target *-*-* } .-1 }
> >   static_assert((b.a.n, 1), "");
> > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C
> > index 922e8ff126f..bea14b05602 100644
> > --- a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C
> > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const7.C
> > @@ -3,7 +3,7 @@
> >
> >   struct D { int n; };
> >
> > -struct C { const D d; };
> > +struct C { const D d; }; // { dg-message "originally declared" }
> >
> >   struct A {
> >     C c;
> > @@ -19,5 +19,4 @@ struct B {
> >   };
> >
> >   constexpr B b{}; // { dg-message "in .constexpr. expansion of " }
> > -// { dg-message "originally declared" "" { target *-*-* } .-1 }
> >   static_assert((b.a.c.d.n, 1), "");
> > 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/cpp1z/constexpr-lambda8.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
> > index 84be68ab7f0..82351ff0c54 100644
> > --- a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
> > +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda8.C
> > @@ -4,11 +4,14 @@
> >   auto Fwd = [](int (*fp)(int), auto a) { return fp(a); };
> >   auto C = [](auto a) { return a; };
> >   static_assert( Fwd(C ,3) == 3); // OK
> > +
> >   // No specialization of the function call operator template can be constexpr
> >   // (because of the local static).
> >   auto NC = [](auto a) { static int s; return a; }; // { dg-error "static" }
> > -// { dg-message "operator int" "" { target *-*-* } .+1 }
> > +// { dg-error "called in a constant expression" "" { target *-*-* } .-1 }
> > +
> >   static_assert( Fwd(NC ,3) == 3); // { dg-error "" }
> > +// { dg-message "operator int" "" { target *-*-* } .-1 }
> >
> >   // We look for the string "operator int" to check that we aren't trying to do
> >   // template pretty-printing in an expression; that gets incredibly unwieldy
> > 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/cpp2a/constinit10.C b/gcc/testsuite/g++.dg/cpp2a/constinit10.C
> > index b678788541e..d2c49c41f91 100644
> > --- a/gcc/testsuite/g++.dg/cpp2a/constinit10.C
> > +++ b/gcc/testsuite/g++.dg/cpp2a/constinit10.C
> > @@ -11,15 +11,14 @@ struct S1
> >   struct alignas(64) S2
> >   {
> >       constexpr S2 ()
> > -    : m_tabS1()
> > +    : m_tabS1() // { dg-error "used before its definition" }
> >       {}
> >
> >       S1 m_tabS1[7];
> >   };
> >
> >   constinit S2 objX; // { dg-error ".constinit. variable .objX. does not have a constant initializer" }
> > -// { dg-error "used before its definition" "" { target *-*-* } .-1 }
> > -// // { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-2 }
> > +// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
> >
> >   constexpr S1::S1 ()
> >   : m_i(14)
> > diff --git a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C
> > index 6b74090306b..241e9976e8c 100644
> > --- a/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C
> > +++ b/gcc/testsuite/g++.dg/cpp2a/is-corresponding-member4.C
> > @@ -14,8 +14,8 @@ is_corresponding_member (M1 S1::*m1, M2 S2::*m2) noexcept
> >   struct A { int a; };
> >   struct B;
> >   constexpr int B::*n = nullptr;
> > -constexpr auto a = std::is_corresponding_member (&A::a, n);  // { dg-error "invalid use of incomplete type 'struct B'" }
> > -constexpr auto b = std::is_corresponding_member (n, &A::a);  // { dg-error "invalid use of incomplete type 'struct B'" }
> > +constexpr auto a = std::is_corresponding_member (&A::a, n);  // { dg-message "in .constexpr. expansion of" }
> > +constexpr auto b = std::is_corresponding_member (n, &A::a);  // { dg-message "in .constexpr. expansion of" }
> >
> >   void
> >   foo (int B::*m)
> > 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..6fd0b4f893e 100644
> > --- a/gcc/testsuite/g++.dg/ubsan/pr63956.C
> > +++ b/gcc/testsuite/g++.dg/ubsan/pr63956.C
> > @@ -56,12 +56,13 @@ fn3 (int a, int b)
> >   {
> >     if (b != 2)
> >       a = a / b; // { dg-error "..7 / 0.. is not a constant expression" }
> > +               // { dg-error "overflow in constant expression" "" { target *-*-* } .-1 }
> >     return a;
> >   }
> >
> >   constexpr int k1 = fn3 (8, 4);
> >   constexpr int k2 = fn3 (7, 0); // { dg-message "in .constexpr. expansion" }
> > -constexpr int k3 = fn3 (INT_MIN, -1); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> > +constexpr int k3 = fn3 (INT_MIN, -1); // { dg-message "in .constexpr. expansion of " }
> >
> >   SA (k1 == 2);
> >
> > @@ -100,13 +101,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)
> > @@ -122,15 +123,15 @@ constexpr int
> >   fn9 (int a, int b)
> >   {
> >     if (b != 0)
> > -    return a + b;
> > +    return a + b; // { dg-error "overflow in constant expression" }
> >     return a;
> >   }
> >
> >   constexpr int p1 = fn9 (42, 7);
> > -constexpr int p2 = fn9 (__INT_MAX__, 1); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> > +constexpr int p2 = fn9 (__INT_MAX__, 1); // { dg-message "in .constexpr. expansion of " }
> >   constexpr int p3 = fn9 (__INT_MAX__, -1);
> >   constexpr int p4 = fn9 (INT_MIN, 1);
> > -constexpr int p5 = fn9 (INT_MIN, -1); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> > +constexpr int p5 = fn9 (INT_MIN, -1); // { dg-message "in .constexpr. expansion of " }
> >
> >   SA (p1 == 49);
> >   SA (p3 == __INT_MAX__ - 1);
> > @@ -140,13 +141,13 @@ constexpr int
> >   fn10 (int a, int b)
> >   {
> >     if (b != 0)
> > -    return a * b;
> > +    return a * b; // { dg-error "overflow in constant expression" }
> >     return a;
> >   }
> >
> >   constexpr int q1 = fn10 (10, 10);
> > -constexpr int q2 = fn10 (__INT_MAX__, 2); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> > -constexpr int q3 = fn10 (INT_MIN, 2); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> > +constexpr int q2 = fn10 (__INT_MAX__, 2); // { dg-message "in .constexpr. expansion of " }
> > +constexpr int q3 = fn10 (INT_MIN, 2); // { dg-message "in .constexpr. expansion of " }
> >   constexpr int q4 = fn10 (-1, -1);
> >
> >   SA (q1 == 100);
> > @@ -155,14 +156,14 @@ SA (q4 == 1);
> >   constexpr int
> >   fn11 (double d)
> >   {
> > -  int i = d;
> > +  int i = d; // { dg-error "overflow in constant expression" }
> >     if (i != 0)
> >       return i;
> >     return i * 2;
> >   }
> >
> >   constexpr int r1 = fn11 (3.4);
> > -constexpr int r2 = fn11 (__builtin_inf ()); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }
> > +constexpr int r2 = fn11 (__builtin_inf ()); // { dg-message "in .constexpr. expansion of " }
> >
> >   constexpr int
> >   fn12 (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'" }
> > diff --git a/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc b/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc
> > index 5b6fea7b560..bc9b29bc39d 100644
> > --- a/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc
> > +++ b/libstdc++-v3/testsuite/26_numerics/gcd/105844.cc
> > @@ -13,9 +13,11 @@ static_assert( std::gcd(LLONG_MIN, 2ull) == 2 );
> >   static_assert( std::gcd(2ull, LLONG_MIN) == 2 );
> >
> >   // But |INT_MIN| cannot be represented in common_type<int, int> i.e. int.
> > -constexpr int a = std::gcd(INT_MIN, 1); // { dg-error "overflow" }
> > -constexpr int b = std::gcd(1, INT_MIN); // { dg-error "overflow" }
> > +constexpr int a = std::gcd(INT_MIN, 1); // { dg-error "in .constexpr." }
> > +constexpr int b = std::gcd(1, INT_MIN); // { dg-error "in .constexpr." }
> >
> >   // And |LLONG_MIN| cannot be represented in long.
> > -constexpr long long c = std::gcd(LLONG_MIN, 1); // { dg-error "overflow" }
> > -constexpr long long d = std::gcd(1, LLONG_MIN); // { dg-error "overflow" }
> > +constexpr long long c = std::gcd(LLONG_MIN, 1); // { dg-error "in .constexpr." }
> > +constexpr long long d = std::gcd(1, LLONG_MIN); // { dg-error "in .constexpr." }
> > +
> > +// { dg-error "overflow" "" { target *-*-* } 0 }
> > diff --git a/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc b/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc
> > index d0e032e03e0..d853974f77e 100644
> > --- a/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc
> > +++ b/libstdc++-v3/testsuite/26_numerics/lcm/105844.cc
> > @@ -9,14 +9,16 @@ static_assert( std::lcm(INT_MIN, 1u) == INT_MAX+1u );
> >   static_assert( std::lcm(1u, INT_MIN) == INT_MAX+1u );
> >
> >   // But |INT_MIN| cannot be represented in common_type<int, int> i.e. int.
> > -constexpr int a = std::lcm(INT_MIN, 1); // { dg-error "overflow" }
> > -constexpr int b = std::lcm(1, INT_MIN); // { dg-error "overflow" }
> > +constexpr int a = std::lcm(INT_MIN, 1); // { dg-error "in .constexpr." }
> > +constexpr int b = std::lcm(1, INT_MIN); // { dg-error "in .constexpr." }
> >
> >   // And the LCM of 50000 and 49999 cannot be represented in int.
> > -constexpr int c = std::lcm(50000, 49999); // { dg-error "overflow" }
> > -constexpr int d = std::lcm(49999, 50000); // { dg-error "overflow" }
> > +constexpr int c = std::lcm(50000, 49999); // { dg-error "in .constexpr." }
> > +constexpr int d = std::lcm(49999, 50000); // { dg-error "in .constexpr." }
> >
> >   // Similarly for unsigned, but the diagnostic is a failed assertion instead.
> > -constexpr int e = std::lcm(500000u, 499999); // { dg-error "in 'constexpr'" }
> > -constexpr int f = std::lcm(499999u, 500000); // { dg-error "in 'constexpr'" }
> > +constexpr int e = std::lcm(500000u, 499999); // { dg-error "in .constexpr." }
> > +constexpr int f = std::lcm(499999u, 500000); // { dg-error "in .constexpr." }
> > +
> > +// { dg-error "overflow" "" { target *-*-* } 0 }
> >   // { dg-error "unreachable" "" { target *-*-* } 0 }
>

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

* Re: [PATCH v4 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675]
  2023-07-20 14:42   ` Jason Merrill
@ 2023-07-22  5:28     ` Nathaniel Shead
  0 siblings, 0 replies; 12+ messages in thread
From: Nathaniel Shead @ 2023-07-22  5:28 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Patrick Palka

On Thu, Jul 20, 2023 at 10:42:29AM -0400, Jason Merrill wrote:
> On 7/20/23 05:35, Nathaniel Shead wrote:
> > 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
> > accessing values outside their lifetime (e.g. following explicit
> > destructor calls).
> 
> Incidentally, much of that should be straightforward to handle by no longer
> ignoring clobbers here:
> 
> >     case MODIFY_EXPR:
> >       if (cxx_dialect < cxx14)
> >         goto fail;
> >       if (!RECUR (TREE_OPERAND (t, 0), any))
> >         return false;
> >       /* Just ignore clobbers.  */
> >       if (TREE_CLOBBER_P (TREE_OPERAND (t, 1)))
> >         return true;
> 
> Assignment from a clobber represents end of lifetime to the middle-end. This
> can be a follow-up patch.

Thanks, this is very helpful to know. I'll keep this in mind.

> > @@ -7051,10 +7065,17 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
> >   	return ctx->ctor;
> >         if (VAR_P (t))
> >   	if (tree v = ctx->global->get_value (t))
> > -	    {
> > -	      r = v;
> > -	      break;
> > -	    }
> > +	  {
> > +	    r = v;
> > +	    break;
> > +	  }
> > +      if (ctx->global->is_outside_lifetime (t))
> > +	{
> > +	  if (!ctx->quiet)
> > +	    outside_lifetime_error (loc, t);
> > +	  *non_constant_p = true;
> > +	  break;
> > +	}
> 
> Shouldn't this new check also be under the if (VAR_P (t))?  A CONST_DECL
> can't go out of scope.
> 
> Jason
> 

Yup you're right; I didn't properly read the documentation on what a
CONST_DECL was and misunderstood. I'll fix this up for the next version.

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

end of thread, other threads:[~2023-07-22  5:28 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-20  9:33 [PATCH v4 0/3] c++: Track lifetimes in constant evaluation [PR70331, ...] Nathaniel Shead
2023-07-20  9:35 ` [PATCH v4 1/3] c++: Track lifetimes in constant evaluation [PR70331,PR96630,PR98675] Nathaniel Shead
2023-07-20 14:42   ` Jason Merrill
2023-07-22  5:28     ` Nathaniel Shead
2023-07-20  9:36 ` [PATCH v4 2/3] c++: Improve constexpr error for dangling local variables [PR110619] Nathaniel Shead
2023-07-20 15:46   ` Jason Merrill
2023-07-21  5:39     ` Nathaniel Shead
2023-07-21 21:44       ` Jason Merrill
2023-07-22  5:20         ` Nathaniel Shead
2023-07-20  9:37 ` [PATCH v4 3/3] c++: Improve location information in constant evaluation Nathaniel Shead
2023-07-20 17:00   ` Jason Merrill
2023-07-22  5:26     ` 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).