[C++] Avoid constexpr garbage for implicit conversion to void.

Message ID 20190607210809.28909-1-jason@redhat.com
State New
Headers show
Series
  • [C++] Avoid constexpr garbage for implicit conversion to void.
Related show

Commit Message

Jason Merrill June 7, 2019, 9:08 p.m.
All expression statements and some other places express implicit conversion to
void with a CONVERT_EXPR.  There's no reason to build up a new one as part of
constexpr evaluation.

The ADDR_EXPR change also avoids a bit of garbage by discarding an ADDR_EXPR we
just built but didn't end up using.

The location wrapper change doesn't affect garbage, it's just a minor
optimization.

Tested x86_64-pc-linux-gnu, applying to trunk.

	* constexpr.c (cxx_eval_constant_expression): Call
	STRIP_ANY_LOCATION_WRAPPER early.
	[CONVERT_EXPR]: Don't build anything for conversion to void.
	[ADDR_EXPR]: ggc_free unused ADDR_EXPR.
---
 gcc/cp/constexpr.c | 19 +++++++++++++------
 gcc/cp/ChangeLog   |  7 +++++++
 2 files changed, 20 insertions(+), 6 deletions(-)


base-commit: 92964728f22989296756c1ece2dbbed323007c8e
-- 
2.20.1

Comments

Jason Merrill June 11, 2019, 4:28 a.m. | #1
On Fri, Jun 7, 2019 at 5:08 PM Jason Merrill <jason@redhat.com> wrote:
>

> All expression statements and some other places express implicit conversion to

> void with a CONVERT_EXPR.  There's no reason to build up a new one as part of

> constexpr evaluation.

>

> The ADDR_EXPR change also avoids a bit of garbage by discarding an ADDR_EXPR we

> just built but didn't end up using.

>

> The location wrapper change doesn't affect garbage, it's just a minor

> optimization.


More constexpr garbage reduction:

1) Using a TREE_VEC instead of TREE_LIST to store bindings saves a
bunch of space when dealing with multiple arguments.
2) We don't need to unshare any expressions containing a CONSTRUCTOR,
or expressions within a CONSTRUCTOR; we're only interested in an
initializer which is itself a CONSTRUCTOR, as that's what can get
modified.  A CONSTRUCTOR appearing as a subexpression won't be used to
directly initialize a variable, so we don't need to unshare it.  We
also don't need to unshare the return value of the function, as it
will be unshared if it is used as an initializer.
3) When we free the TREE_VEC for a call we end up not using, we can
also free any CONSTRUCTORs we unshared for it.  This unsharing isn't
necessary, but oddly it significantly reduces the max resident memory
on the testcase I've been looking at, so I'm going to leave it in.  We
also free CONSTRUCTORs for initialized parameters when we're done
evaluating the function.

Tested x86_64-pc-linux-gnu, applied to trunk.
commit 20c4756f8e94a0278b3b74d1e674e3163b1e6a33
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Jun 10 00:16:05 2019 -0400

            Reduce constexpr_call memory consumption.
    
            * constexpr.c (cxx_bind_parameters_in_call): Use TREE_VEC rather
            than TREE_LIST.
            (constexpr_call_hasher::equal, cxx_bind_parameters_in_call)
            (cxx_eval_call_expression): Adjust.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 10afc419a33..74752bc72dd 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -974,12 +974,7 @@ explain_invalid_constexpr_fn (tree fun)
 struct GTY((for_user)) constexpr_call {
   /* Description of the constexpr function definition.  */
   constexpr_fundef *fundef;
-  /* Parameter bindings environment.  A TREE_LIST where each TREE_PURPOSE
-     is a parameter _DECL and the TREE_VALUE is the value of the parameter.
-     Note: This arrangement is made to accommodate the use of
-     iterative_hash_template_arg (see pt.c).  If you change this
-     representation, also change the hash calculation in
-     cxx_eval_call_expression.  */
+  /* Parameter bindings environment.  A TREE_VEC of arguments.  */
   tree bindings;
   /* Result of the call.
        NULL means the call is being evaluated.
@@ -1069,8 +1064,6 @@ constexpr_call_hasher::hash (constexpr_call *info)
 bool
 constexpr_call_hasher::equal (constexpr_call *lhs, constexpr_call *rhs)
 {
-  tree lhs_bindings;
-  tree rhs_bindings;
   if (lhs == rhs)
     return true;
   if (lhs->hash != rhs->hash)
@@ -1079,19 +1072,7 @@ constexpr_call_hasher::equal (constexpr_call *lhs, constexpr_call *rhs)
     return false;
   if (!constexpr_fundef_hasher::equal (lhs->fundef, rhs->fundef))
     return false;
-  lhs_bindings = lhs->bindings;
-  rhs_bindings = rhs->bindings;
-  while (lhs_bindings != NULL && rhs_bindings != NULL)
-    {
-      tree lhs_arg = TREE_VALUE (lhs_bindings);
-      tree rhs_arg = TREE_VALUE (rhs_bindings);
-      gcc_assert (same_type_p (TREE_TYPE (lhs_arg), TREE_TYPE (rhs_arg)));
-      if (!cp_tree_equal (lhs_arg, rhs_arg))
-        return false;
-      lhs_bindings = TREE_CHAIN (lhs_bindings);
-      rhs_bindings = TREE_CHAIN (rhs_bindings);
-    }
-  return lhs_bindings == rhs_bindings;
+  return cp_tree_equal (lhs->bindings, rhs->bindings);
 }
 
 /* Initialize the constexpr call table, if needed.  */
@@ -1380,7 +1361,10 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
   tree fun = new_call->fundef->decl;
   tree parms = new_call->fundef->parms;
   int i;
-  tree *p = &new_call->bindings;
+  /* We don't record ellipsis args below.  */
+  int nparms = list_length (parms);
+  int nbinds = nargs < nparms ? nargs : nparms;
+  tree binds = new_call->bindings = make_tree_vec (nbinds);
   for (i = 0; i < nargs; ++i)
     {
       tree x, arg;
@@ -1417,8 +1401,7 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
 	    arg = adjust_temp_type (type, arg);
 	  if (!TREE_CONSTANT (arg))
 	    *non_constant_args = true;
-	  *p = build_tree_list (parms, arg);
-	  p = &TREE_CHAIN (*p);
+	  TREE_VEC_ELT (binds, i) = arg;
 	}
       parms = TREE_CHAIN (parms);
     }
@@ -1745,14 +1728,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
     void preserve () { do_free = false; }
     ~free_bindings () {
       if (do_free)
-	{
-	  while (bindings)
-	    {
-	      tree b = bindings;
-	      bindings = TREE_CHAIN (bindings);
-	      ggc_free (b);
-	    }
-	}
+	ggc_free (bindings);
     }
   } fb (new_call.bindings);
 
@@ -1833,15 +1809,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  /* Associate the bindings with the remapped parms.  */
 	  tree bound = new_call.bindings;
 	  tree remapped = parms;
-	  while (bound)
+	  for (int i = 0; i < TREE_VEC_LENGTH (bound); ++i)
 	    {
-	      tree oparm = TREE_PURPOSE (bound);
-	      tree arg = TREE_VALUE (bound);
-	      gcc_assert (DECL_NAME (remapped) == DECL_NAME (oparm));
+	      tree arg = TREE_VEC_ELT (bound, i);
 	      /* Don't share a CONSTRUCTOR that might be changed.  */
 	      arg = unshare_constructor (arg);
 	      ctx->values->put (remapped, arg);
-	      bound = TREE_CHAIN (bound);
 	      remapped = DECL_CHAIN (remapped);
 	    }
 	  /* Add the RESULT_DECL to the values map, too.  */
commit b624a048c602f46be2f15c84e55f31ce869d8e08
Author: Jason Merrill <jason@redhat.com>
Date:   Sat Jun 8 09:30:43 2019 -0400

    Reduce unsharing in constexpr call evaluation.
    
            * constexpr.c (unshare_constructor): Only unshare if T is itself a
            CONSTRUCTOR.
            (cxx_eval_call_expression): Don't call it on the result here.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 74752bc72dd..d7adb469b45 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1321,27 +1321,32 @@ adjust_temp_type (tree type, tree temp)
   return cp_fold_convert (type, temp);
 }
 
-/* Callback for walk_tree used by unshare_constructor.  */
+/* If T is a CONSTRUCTOR, return an unshared copy of T and any
+   sub-CONSTRUCTORs.  Otherwise return T.
 
-static tree
-find_constructor (tree *tp, int *walk_subtrees, void *)
-{
-  if (TYPE_P (*tp))
-    *walk_subtrees = 0;
-  if (TREE_CODE (*tp) == CONSTRUCTOR)
-    return *tp;
-  return NULL_TREE;
-}
-
-/* If T is a CONSTRUCTOR or an expression that has a CONSTRUCTOR node as a
-   subexpression, return an unshared copy of T.  Otherwise return T.  */
+   We use this whenever we initialize an object as a whole, whether it's a
+   parameter, a local variable, or a subobject, so that subsequent
+   modifications don't affect other places where it was used.  */
 
 tree
 unshare_constructor (tree t)
 {
-  tree ctor = walk_tree (&t, find_constructor, NULL, NULL);
-  if (ctor != NULL_TREE)
-    return unshare_expr (t);
+  if (!t || TREE_CODE (t) != CONSTRUCTOR)
+    return t;
+  auto_vec <tree*, 4> ptrs;
+  ptrs.safe_push (&t);
+  while (!ptrs.is_empty ())
+    {
+      tree *p = ptrs.pop ();
+      tree n = copy_node (*p);
+      CONSTRUCTOR_ELTS (n) = vec_safe_copy (CONSTRUCTOR_ELTS (*p));
+      *p = n;
+      vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (n);
+      constructor_elt *ce;
+      for (HOST_WIDE_INT i = 0; vec_safe_iterate (v, i, &ce); ++i)
+	if (TREE_CODE (ce->value) == CONSTRUCTOR)
+	  ptrs.safe_push (&ce->value);
+    }
   return t;
 }
 
@@ -1898,7 +1903,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
     clear_no_implicit_zero (result);
 
   pop_cx_call_context ();
-  return unshare_constructor (result);
+  return result;
 }
 
 /* FIXME speed this up, it's taking 16% of compile time on sieve testcase.  */
commit 3faf66f3270c9f07ebd73376e66751105419322e
Author: Jason Merrill <jason@redhat.com>
Date:   Sat Jun 8 10:56:21 2019 -0400

            * constexpr.c (free_constructor): New.
    
            (cxx_eval_call_expression): Free parameter value CONSTRUCTORs.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index d7adb469b45..a2f29694462 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1350,6 +1350,30 @@ unshare_constructor (tree t)
   return t;
 }
 
+/* If T is a CONSTRUCTOR, ggc_free T and any sub-CONSTRUCTORs.  */
+
+static void
+free_constructor (tree t)
+{
+  if (!t || TREE_CODE (t) != CONSTRUCTOR)
+    return;
+  releasing_vec ctors;
+  vec_safe_push (ctors, t);
+  while (!ctors->is_empty ())
+    {
+      tree c = ctors->pop ();
+      if (vec<constructor_elt, va_gc> *elts = CONSTRUCTOR_ELTS (c))
+	{
+	  constructor_elt *ce;
+	  for (HOST_WIDE_INT i = 0; vec_safe_iterate (elts, i, &ce); ++i)
+	    if (TREE_CODE (ce->value) == CONSTRUCTOR)
+	      vec_safe_push (ctors, ce->value);
+	  ggc_free (elts);
+	}
+      ggc_free (c);
+    }
+}
+
 /* Subroutine of cxx_eval_call_expression.
    We are processing a call expression (either CALL_EXPR or
    AGGR_INIT_EXPR) in the context of CTX.  Evaluate
@@ -1398,7 +1422,8 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
 
       if (!*non_constant_p)
 	{
-	  /* Don't share a CONSTRUCTOR that might be changed.  */
+	  /* Unsharing here isn't necessary for correctness, but it
+	     significantly improves memory performance for some reason.  */
 	  arg = unshare_constructor (arg);
 	  /* Make sure the binding has the same type as the parm.  But
 	     only for constant args.  */
@@ -1733,7 +1758,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
     void preserve () { do_free = false; }
     ~free_bindings () {
       if (do_free)
-	ggc_free (bindings);
+	{
+	  for (int i = 0; i < TREE_VEC_LENGTH (bindings); ++i)
+	    free_constructor (TREE_VEC_ELT (bindings, i));
+	  ggc_free (bindings);
+	}
     }
   } fb (new_call.bindings);
 
@@ -1804,6 +1833,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
       else
 	{
 	  tree body, parms, res;
+	  releasing_vec ctors;
 
 	  /* Reuse or create a new unshared copy of this function's body.  */
 	  tree copy = get_fundef_copy (new_call.fundef);
@@ -1819,6 +1849,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	      tree arg = TREE_VEC_ELT (bound, i);
 	      /* Don't share a CONSTRUCTOR that might be changed.  */
 	      arg = unshare_constructor (arg);
+	      if (TREE_CODE (arg) == CONSTRUCTOR)
+		vec_safe_push (ctors, arg);
 	      ctx->values->put (remapped, arg);
 	      remapped = DECL_CHAIN (remapped);
 	    }
@@ -1884,6 +1916,14 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
 	    ctx->values->remove (parm);
 
+	  /* Free any parameter CONSTRUCTORs we aren't returning directly.  */
+	  while (!ctors->is_empty ())
+	    {
+	      tree c = ctors->pop ();
+	      if (c != result)
+		free_constructor (c);
+	    }
+
 	  /* Make the unshared function copy we used available for re-use.  */
 	  save_fundef_copy (fun, copy);
 	}
Jason Merrill June 18, 2019, 4:07 p.m. | #2
On Tue, Jun 11, 2019 at 12:28 AM Jason Merrill <jason@redhat.com> wrote:
>

> On Fri, Jun 7, 2019 at 5:08 PM Jason Merrill <jason@redhat.com> wrote:

> >

> > All expression statements and some other places express implicit conversion to

> > void with a CONVERT_EXPR.  There's no reason to build up a new one as part of

> > constexpr evaluation.

> >

> > The ADDR_EXPR change also avoids a bit of garbage by discarding an ADDR_EXPR we

> > just built but didn't end up using.

> >

> > The location wrapper change doesn't affect garbage, it's just a minor

> > optimization.

>

> More constexpr garbage reduction:


And more:

1) Avoid building anything when converting to reference and back to pointer.
2) Avoid building up new COMPONENT_REFs for store evaluation.
3) Share array index checking between ARRAY_REF and store evaluation.
4) Track where we're calling unshare_constructor from.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 325be200c3c9a2fefed41f2adf874ccd3ca5b4cf
Author: Jason Merrill <jason@redhat.com>
Date:   Sat Jun 15 07:45:01 2019 -0400

    Handle constexpr conversion from and then to the same type.
    
            * constexpr.c (cxx_eval_constant_expression): Handle conversion from
            and then to the same type.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 22901f811f1..0f68a0c9fca 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -5034,6 +5034,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	if (*non_constant_p)
 	  return t;
 	tree type = TREE_TYPE (t);
+
+	if (VOID_TYPE_P (type))
+	  return void_node;
+
 	if (TREE_CODE (op) == PTRMEM_CST
 	    && !TYPE_PTRMEM_P (type))
 	  op = cplus_expand_constant (op);
@@ -5094,14 +5098,18 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	     conversion.  */
 	  return fold (t);
 
+	tree sop;
+
 	/* Handle an array's bounds having been deduced after we built
 	   the wrapping expression.  */
 	if (same_type_ignoring_tlq_and_bounds_p (type, TREE_TYPE (op)))
 	  r = op;
+	else if (sop = tree_strip_nop_conversions (op),
+		 sop != op && (same_type_ignoring_tlq_and_bounds_p
+			       (type, TREE_TYPE (sop))))
+	  r = sop;
 	else if (tcode == UNARY_PLUS_EXPR)
 	  r = fold_convert (TREE_TYPE (t), op);
-	else if (VOID_TYPE_P (type))
-	  r = void_node;
 	else
 	  r = fold_build1 (tcode, type, op);
commit 30360c70d9d5bc5b680b6e274fa6aca6f2ee8137
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Jun 14 07:45:01 2019 -0400

            * constexpr.c (cxx_eval_store_expression): Delay target evaluation.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 7c733d78b5b..22f4fa0d351 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -3747,22 +3747,18 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
       if (*non_constant_p)
 	return t;
     }
-  target = cxx_eval_constant_expression (ctx, target,
-					 true,
-					 non_constant_p, overflow_p);
-  if (*non_constant_p)
-    return t;
 
-  if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (target), type))
+  bool evaluated = false;
+  if (lval)
     {
-      /* For initialization of an empty base, the original target will be
-         *(base*)this, which the above evaluation resolves to the object
-	 argument, which has the derived type rather than the base type.  In
-	 this situation, just evaluate the initializer and return, since
-	 there's no actual data to store.  */
-      gcc_assert (is_empty_class (type));
-      return cxx_eval_constant_expression (ctx, init, false,
-					   non_constant_p, overflow_p);
+      /* If we want to return a reference to the target, we need to evaluate it
+	 as a whole; otherwise, only evaluate the innermost piece to avoid
+	 building up unnecessary *_REFs.  */
+      target = cxx_eval_constant_expression (ctx, target, true,
+					     non_constant_p, overflow_p);
+      evaluated = true;
+      if (*non_constant_p)
+	return t;
     }
 
   /* Find the underlying variable.  */
@@ -3792,7 +3788,17 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 	  break;
 
 	default:
-	  object = probe;
+	  if (evaluated)
+	    object = probe;
+	  else
+	    {
+	      probe = cxx_eval_constant_expression (ctx, probe, true,
+						    non_constant_p, overflow_p);
+	      evaluated = true;
+	      if (*non_constant_p)
+		return t;
+	    }
+	  break;
 	}
     }
 
@@ -3948,7 +3954,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
       new_ctx.object = target;
       init = cxx_eval_constant_expression (&new_ctx, init, false,
 					   non_constant_p, overflow_p);
-      if (target == object)
+      if (ctors->is_empty())
 	/* The hash table might have moved since the get earlier.  */
 	valp = ctx->values->get (object);
     }
@@ -3961,6 +3967,17 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
     {
       /* An outer ctx->ctor might be pointing to *valp, so replace
 	 its contents.  */
+      if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init),
+						      TREE_TYPE (*valp)))
+	{
+	  /* For initialization of an empty base, the original target will be
+	   *(base*)this, evaluation of which resolves to the object
+	   argument, which has the derived type rather than the base type.  In
+	   this situation, just evaluate the initializer and return, since
+	   there's no actual data to store.  */
+	  gcc_assert (is_empty_class (TREE_TYPE (init)) && !lval);
+	  return init;
+	}
       CONSTRUCTOR_ELTS (*valp) = CONSTRUCTOR_ELTS (init);
       TREE_CONSTANT (*valp) = TREE_CONSTANT (init);
       TREE_SIDE_EFFECTS (*valp) = TREE_SIDE_EFFECTS (init);
commit 8f67898b9bd5924f3dd5218164df62ada10ea428
Author: Jason Merrill <jason@redhat.com>
Date:   Sat Jun 15 23:59:55 2019 -0400

    Consolidate constexpr array handling.
    
            * constexpr.c (eval_and_check_array_index): Split out from...
            (cxx_eval_array_reference): ...here.
            (cxx_eval_store_expression): Use it here, too.
            (diag_array_subscript): Take location.  Strip location wrapper.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 0f68a0c9fca..7c733d78b5b 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2488,7 +2488,7 @@ find_array_ctor_elt (tree ary, tree dindex, bool insert)
    an out-of-bounds subscript INDEX into the expression ARRAY.  */
 
 static void
-diag_array_subscript (const constexpr_ctx *ctx, tree array, tree index)
+diag_array_subscript (location_t loc, const constexpr_ctx *ctx, tree array, tree index)
 {
   if (!ctx->quiet)
     {
@@ -2497,22 +2497,23 @@ diag_array_subscript (const constexpr_ctx *ctx, tree array, tree index)
       /* Convert the unsigned array subscript to a signed integer to avoid
 	 printing huge numbers for small negative values.  */
       tree sidx = fold_convert (ssizetype, index);
+      STRIP_ANY_LOCATION_WRAPPER (array);
       if (DECL_P (array))
 	{
 	  if (TYPE_DOMAIN (arraytype))
-	    error ("array subscript value %qE is outside the bounds "
-	           "of array %qD of type %qT", sidx, array, arraytype);
+	    error_at (loc, "array subscript value %qE is outside the bounds "
+		      "of array %qD of type %qT", sidx, array, arraytype);
 	  else
-	    error ("nonzero array subscript %qE is used with array %qD of "
-		   "type %qT with unknown bounds", sidx, array, arraytype);
+	    error_at (loc, "nonzero array subscript %qE is used with array %qD of "
+		      "type %qT with unknown bounds", sidx, array, arraytype);
 	  inform (DECL_SOURCE_LOCATION (array), "declared here");
 	}
       else if (TYPE_DOMAIN (arraytype))
-	error ("array subscript value %qE is outside the bounds "
-	       "of array type %qT", sidx, arraytype);
+	error_at (loc, "array subscript value %qE is outside the bounds "
+		  "of array type %qT", sidx, arraytype);
       else
-	error ("nonzero array subscript %qE is used with array of type %qT "
-	       "with unknown bounds", sidx, arraytype);
+	error_at (loc, "nonzero array subscript %qE is used with array of type %qT "
+		  "with unknown bounds", sidx, arraytype);
     }
 }
 
@@ -2563,6 +2564,44 @@ extract_string_elt (tree string, unsigned chars_per_elt, unsigned index)
   return r;
 }
 
+/* Subroutine of cxx_eval_array_reference.  T is an ARRAY_REF; evaluate the
+   subscript, diagnose any problems with it, and return the result.  */
+
+static tree
+eval_and_check_array_index (const constexpr_ctx *ctx,
+			    tree t, bool allow_one_past,
+			    bool *non_constant_p, bool *overflow_p)
+{
+  location_t loc = cp_expr_loc_or_loc (t, input_location);
+  tree ary = TREE_OPERAND (t, 0);
+  t = TREE_OPERAND (t, 1);
+  tree index = cxx_eval_constant_expression (ctx, t, false,
+					     non_constant_p, overflow_p);
+  VERIFY_CONSTANT (index);
+
+  if (!tree_fits_shwi_p (index)
+      || tree_int_cst_sgn (index) < 0)
+    {
+      diag_array_subscript (loc, ctx, ary, index);
+      *non_constant_p = true;
+      return t;
+    }
+
+  tree nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary), non_constant_p,
+					  overflow_p);
+  VERIFY_CONSTANT (nelts);
+  if (allow_one_past
+      ? !tree_int_cst_le (index, nelts)
+      : !tree_int_cst_lt (index, nelts))
+    {
+      diag_array_subscript (loc, ctx, ary, index);
+      *non_constant_p = true;
+      return t;
+    }
+
+  return index;
+}
+
 /* Subroutine of cxx_eval_constant_expression.
    Attempt to reduce a reference to an array slot.  */
 
@@ -2575,71 +2614,47 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
   tree ary = cxx_eval_constant_expression (ctx, oldary,
 					   lval,
 					   non_constant_p, overflow_p);
-  tree index, oldidx;
-  HOST_WIDE_INT i = 0;
-  tree elem_type = NULL_TREE;
-  unsigned len = 0, elem_nchars = 1;
   if (*non_constant_p)
     return t;
-  oldidx = TREE_OPERAND (t, 1);
-  index = cxx_eval_constant_expression (ctx, oldidx,
-					false,
-					non_constant_p, overflow_p);
-  VERIFY_CONSTANT (index);
-  if (!lval)
-    {
-      elem_type = TREE_TYPE (TREE_TYPE (ary));
-      if (TREE_CODE (ary) == VIEW_CONVERT_EXPR
-	  && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0)))
-	  && TREE_TYPE (t) == TREE_TYPE (TREE_TYPE (TREE_OPERAND (ary, 0))))
-	ary = TREE_OPERAND (ary, 0);
-      if (TREE_CODE (ary) == CONSTRUCTOR)
-	len = CONSTRUCTOR_NELTS (ary);
-      else if (TREE_CODE (ary) == STRING_CST)
-	{
-	  elem_nchars = (TYPE_PRECISION (elem_type)
-			 / TYPE_PRECISION (char_type_node));
-	  len = (unsigned) TREE_STRING_LENGTH (ary) / elem_nchars;
-	}
-      else if (TREE_CODE (ary) == VECTOR_CST)
-	/* We don't create variable-length VECTOR_CSTs.  */
-	len = VECTOR_CST_NELTS (ary).to_constant ();
-      else
-	{
-	  /* We can't do anything with other tree codes, so use
-	     VERIFY_CONSTANT to complain and fail.  */
-	  VERIFY_CONSTANT (ary);
-	  gcc_unreachable ();
-	}
+  if (TREE_CODE (ary) == VIEW_CONVERT_EXPR
+      && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0)))
+      && TREE_TYPE (t) == TREE_TYPE (TREE_TYPE (TREE_OPERAND (ary, 0))))
+    ary = TREE_OPERAND (ary, 0);
 
-      if (!tree_fits_shwi_p (index)
-	  || (i = tree_to_shwi (index)) < 0)
-	{
-	  diag_array_subscript (ctx, ary, index);
-	  *non_constant_p = true;
-	  return t;
-	}
-    }
-
-  tree nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary), non_constant_p,
-					  overflow_p);
-  VERIFY_CONSTANT (nelts);
-  if ((lval
-       ? !tree_int_cst_le (index, nelts)
-       : !tree_int_cst_lt (index, nelts))
-      || tree_int_cst_sgn (index) < 0)
-    {
-      diag_array_subscript (ctx, ary, index);
-      *non_constant_p = true;
-      return t;
-    }
+  tree oldidx = TREE_OPERAND (t, 1);
+  tree index = eval_and_check_array_index (ctx, t, lval,
+					   non_constant_p, overflow_p);
+  if (*non_constant_p)
+    return t;
 
   if (lval && ary == oldary && index == oldidx)
     return t;
   else if (lval)
     return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL);
 
+  unsigned len = 0, elem_nchars = 1;
+  tree elem_type = TREE_TYPE (TREE_TYPE (ary));
+  if (TREE_CODE (ary) == CONSTRUCTOR)
+    len = CONSTRUCTOR_NELTS (ary);
+  else if (TREE_CODE (ary) == STRING_CST)
+    {
+      elem_nchars = (TYPE_PRECISION (elem_type)
+		     / TYPE_PRECISION (char_type_node));
+      len = (unsigned) TREE_STRING_LENGTH (ary) / elem_nchars;
+    }
+  else if (TREE_CODE (ary) == VECTOR_CST)
+    /* We don't create variable-length VECTOR_CSTs.  */
+    len = VECTOR_CST_NELTS (ary).to_constant ();
+  else
+    {
+      /* We can't do anything with other tree codes, so use
+	 VERIFY_CONSTANT to complain and fail.  */
+      VERIFY_CONSTANT (ary);
+      gcc_unreachable ();
+    }
+
   bool found;
+  HOST_WIDE_INT i = 0;
   if (TREE_CODE (ary) == CONSTRUCTOR)
     {
       HOST_WIDE_INT ix = find_array_ctor_elt (ary, index);
@@ -2648,7 +2663,10 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 	i = ix;
     }
   else
-    found = (i < len);
+    {
+      i = tree_to_shwi (index);
+      found = (i < len);
+    }
 
   if (found)
     {
@@ -3735,41 +3753,6 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
   if (*non_constant_p)
     return t;
 
-  /* cxx_eval_array_reference for lval = true allows references one past
-     end of array, because it does not know if it is just taking address
-     (which is valid), or actual dereference.  Here we know it is
-     a dereference, so diagnose it here.  */
-  for (tree probe = target; probe; )
-    {
-      switch (TREE_CODE (probe))
-	{
-	case ARRAY_REF:
-	  tree nelts, ary;
-	  ary = TREE_OPERAND (probe, 0);
-	  nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary),
-					     non_constant_p, overflow_p);
-	  VERIFY_CONSTANT (nelts);
-	  gcc_assert (TREE_CODE (nelts) == INTEGER_CST
-		      && TREE_CODE (TREE_OPERAND (probe, 1)) == INTEGER_CST);
-	  if (wi::to_widest (TREE_OPERAND (probe, 1)) == wi::to_widest (nelts))
-	    {
-	      diag_array_subscript (ctx, ary, TREE_OPERAND (probe, 1));
-	      *non_constant_p = true;
-	      return t;
-	    }
-	  /* FALLTHRU */
-
-	case BIT_FIELD_REF:
-	case COMPONENT_REF:
-	  probe = TREE_OPERAND (probe, 0);
-	  continue;
-
-	default:
-	  probe = NULL_TREE;
-	  continue;
-	}
-    }
-
   if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (target), type))
     {
       /* For initialization of an empty base, the original target will be
@@ -3782,7 +3765,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 					   non_constant_p, overflow_p);
     }
 
-  /* And then find the underlying variable.  */
+  /* Find the underlying variable.  */
   releasing_vec refs;
   tree object = NULL_TREE;
   for (tree probe = target; object == NULL_TREE; )
@@ -3792,9 +3775,20 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 	case BIT_FIELD_REF:
 	case COMPONENT_REF:
 	case ARRAY_REF:
-	  vec_safe_push (refs, TREE_OPERAND (probe, 1));
-	  vec_safe_push (refs, TREE_TYPE (probe));
-	  probe = TREE_OPERAND (probe, 0);
+	  {
+	    tree ob = TREE_OPERAND (probe, 0);
+	    tree elt = TREE_OPERAND (probe, 1);
+	    if (TREE_CODE (probe) == ARRAY_REF)
+	      {
+		elt = eval_and_check_array_index (ctx, probe, false,
+						  non_constant_p, overflow_p);
+		if (*non_constant_p)
+		  return t;
+	      }
+	    vec_safe_push (refs, elt);
+	    vec_safe_push (refs, TREE_TYPE (probe));
+	    probe = ob;
+	  }
 	  break;
 
 	default:
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-79655.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-79655.C
index cc9ce6c505e..063d556871d 100644
--- a/gcc/testsuite/g++.dg/cpp1y/constexpr-79655.C
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-79655.C
@@ -5,14 +5,14 @@ constexpr int
 foo (int x, int y)
 {
   int a[6] = { 1, 2, 3, 4, 5, 6 };
-  a[x] = 0;
-  return a[y];
+  a[x] = 0;			   // { dg-error "is outside the bounds" }
+  return a[y];			   // { dg-error "is outside the bounds" }
 }
 
-constexpr int b = foo (0, -1);	// { dg-error "is outside the bounds|in .constexpr. expansion of " }
-constexpr int c = foo (0, 6);	// { dg-error "is outside the bounds|in .constexpr. expansion of " }
-constexpr int d = foo (6, 0);	// { dg-error "is outside the bounds|in .constexpr. expansion of " }
-constexpr int e = foo (-1, 0);	// { dg-error "is outside the bounds|in .constexpr. expansion of " }
+constexpr int b = foo (0, -1);	// { dg-message "in .constexpr. expansion of " }
+constexpr int c = foo (0, 6);	// { dg-message "in .constexpr. expansion of " }
+constexpr int d = foo (6, 0);	// { dg-message "in .constexpr. expansion of " }
+constexpr int e = foo (-1, 0);	// { dg-message "in .constexpr. expansion of " }
 static_assert (foo (5, 5) == 0, "");
 static_assert (foo (4, 5) == 6, "");
 static_assert (foo (5, 4) == 5, "");
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr77830.C b/gcc/testsuite/g++.dg/cpp1y/pr77830.C
index 6fcb1ba8847..21eeda3c940 100644
--- a/gcc/testsuite/g++.dg/cpp1y/pr77830.C
+++ b/gcc/testsuite/g++.dg/cpp1y/pr77830.C
@@ -13,7 +13,7 @@ constexpr void
 P<N>::foo (const char *, int i)
 {
   for (auto j = 0; j < 2; ++j)
-    arr[i][j] = true;
+    arr[i][j] = true;		// { dg-error "outside the bounds of array type" }
 }
 
 template <typename... T>
@@ -30,5 +30,5 @@ bar (T... a)
 int
 main ()
 {
-  constexpr auto a = bar ("", "");	// { dg-error "outside the bounds of array type|in .constexpr. expansion of " }
+  constexpr auto a = bar ("", "");	// { dg-message "in .constexpr. expansion of " }
 }
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-vla1.C b/gcc/testsuite/g++.dg/ext/constexpr-vla1.C
index aff1d21a4b7..62832725a59 100644
--- a/gcc/testsuite/g++.dg/ext/constexpr-vla1.C
+++ b/gcc/testsuite/g++.dg/ext/constexpr-vla1.C
@@ -21,10 +21,10 @@ fn_not_ok (int n)
     int z = 0;
 
     for (unsigned i = 0; i < sizeof (a); ++i)
-      z += a[i];
+      z += a[i];		// { dg-error "array subscript" }
 
     return z;
 }
 
 constexpr int n1 = fn_ok (3);
-constexpr int n2 = fn_not_ok (3); // { dg-error "array subscript|in .constexpr. expansion of " }
+constexpr int n2 = fn_not_ok (3); // { dg-message "in .constexpr. expansion of " }
commit 23b95612d61379b93dcae6e44d19cfc4bd986104
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Jun 14 00:22:07 2019 -0400

            * tree.c (build_constructor): Add MEM_STAT_DECL.
    
    gcc/cp/
            * constexpr.c (unshare_constructor): Add MEM_STAT_DECL.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1f4e1e15554..98f7a0c0cd0 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7725,7 +7725,7 @@ extern void explain_invalid_constexpr_fn        (tree);
 extern vec<tree> cx_error_context               (void);
 extern tree fold_sizeof_expr			(tree);
 extern void clear_cv_and_fold_caches		(void);
-extern tree unshare_constructor			(tree);
+extern tree unshare_constructor			(tree CXX_MEM_STAT_INFO);
 
 /* In cp-ubsan.c */
 extern void cp_ubsan_maybe_instrument_member_call (tree);
diff --git a/gcc/tree.h b/gcc/tree.h
index d45a391428e..23ac9b1ff5e 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -4247,7 +4247,7 @@ extern tree build_vec_series (tree, tree, tree);
 extern tree build_index_vector (tree, poly_uint64, poly_uint64);
 extern void recompute_constructor_flags (tree);
 extern void verify_constructor_flags (tree);
-extern tree build_constructor (tree, vec<constructor_elt, va_gc> *);
+extern tree build_constructor (tree, vec<constructor_elt, va_gc> * CXX_MEM_STAT_INFO);
 extern tree build_constructor_single (tree, tree, tree);
 extern tree build_constructor_from_list (tree, tree);
 extern tree build_constructor_va (tree, int, ...);
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 8bbabd8ab44..22901f811f1 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1331,7 +1331,7 @@ adjust_temp_type (tree type, tree temp)
    modifications don't affect other places where it was used.  */
 
 tree
-unshare_constructor (tree t)
+unshare_constructor (tree t MEM_STAT_DECL)
 {
   if (!t || TREE_CODE (t) != CONSTRUCTOR)
     return t;
@@ -1340,8 +1340,8 @@ unshare_constructor (tree t)
   while (!ptrs.is_empty ())
     {
       tree *p = ptrs.pop ();
-      tree n = copy_node (*p);
-      CONSTRUCTOR_ELTS (n) = vec_safe_copy (CONSTRUCTOR_ELTS (*p));
+      tree n = copy_node (*p PASS_MEM_STAT);
+      CONSTRUCTOR_ELTS (n) = vec_safe_copy (CONSTRUCTOR_ELTS (*p) PASS_MEM_STAT);
       *p = n;
       vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (n);
       constructor_elt *ce;
diff --git a/gcc/tree.c b/gcc/tree.c
index e879f15a841..f65025f1089 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -2023,9 +2023,9 @@ verify_constructor_flags (tree c)
 /* Return a new CONSTRUCTOR node whose type is TYPE and whose values
    are in the vec pointed to by VALS.  */
 tree
-build_constructor (tree type, vec<constructor_elt, va_gc> *vals)
+build_constructor (tree type, vec<constructor_elt, va_gc> *vals MEM_STAT_DECL)
 {
-  tree c = make_node (CONSTRUCTOR);
+  tree c = make_node (CONSTRUCTOR PASS_MEM_STAT);
 
   TREE_TYPE (c) = type;
   CONSTRUCTOR_ELTS (c) = vals;
Jakub Jelinek June 19, 2019, 8:23 a.m. | #3
On Tue, Jun 18, 2019 at 12:07:38PM -0400, Jason Merrill wrote:
> commit 8f67898b9bd5924f3dd5218164df62ada10ea428

> Author: Jason Merrill <jason@redhat.com>

> Date:   Sat Jun 15 23:59:55 2019 -0400

> 

>     Consolidate constexpr array handling.

>     

>             * constexpr.c (eval_and_check_array_index): Split out from...

>             (cxx_eval_array_reference): ...here.

>             (cxx_eval_store_expression): Use it here, too.

>             (diag_array_subscript): Take location.  Strip location wrapper.


The r272430 change introduced:
+FAIL: g++.dg/ubsan/pr63956.C   -O0  (test for excess errors)
+FAIL: g++.dg/ubsan/pr63956.C   -O1  (test for excess errors)
+FAIL: g++.dg/ubsan/pr63956.C   -O2  (test for excess errors)
+FAIL: g++.dg/ubsan/pr63956.C   -O2 -flto  (test for excess errors)
+FAIL: g++.dg/ubsan/pr63956.C   -O2 -flto -flto-partition=none  (test for excess errors)
+FAIL: g++.dg/ubsan/pr63956.C   -O3 -g  (test for excess errors)
+FAIL: g++.dg/ubsan/pr63956.C   -Os  (test for excess errors)
From what I can see, it just changed the location of the diagnostics, like:
@@ -29,17 +29,23 @@ pr63956.C:72:11: error: ‘(7.0e+0f / 0.
    72 |     a = a / b; // { dg-error "is not a constant expression" }
       |         ~~^~~
 pr63956.C:89:24:   in ‘constexpr’ expansion of ‘fn5(((const int*)(& m1)), 4)’
-pr63956.C:89:30: error: array subscript value ‘4’ is outside the bounds of array type ‘const int [4]’
-   89 | constexpr int m3 = fn5 (m1, 4); // { dg-error "array subscript|in .constexpr. expansion of " }
-      |                              ^
+pr63956.C:83:12: error: array subscript value ‘4’ is outside the bounds of array ‘m1’ of type ‘const int [4]’
+   83 |     b = a[b];
+      |         ~~~^
+pr63956.C:87:15: note: declared here
+   87 | constexpr int m1[4] = { 1, 2, 3, 4 };
+      |               ^~
 pr63956.C:109:24:   in ‘constexpr’ expansion of ‘fn7(0, 8)’
 pr63956.C:109:43: error: dereferencing a null pointer
   109 | constexpr int n3 = fn7 ((const int *) 0, 8);  // { dg-error "null pointer|in .constexpr. expansion of " }
       |                                           ^
 pr63956.C:119:24:   in ‘constexpr’ expansion of ‘fn8(10)’
-pr63956.C:119:27: error: array subscript value ‘10’ is outside the bounds of array type ‘const int [10]’
-  119 | constexpr int o2 = fn8 (10); // { dg-error "array subscript|in .constexpr. expansion of " }
-      |                           ^
+pr63956.C:115:13: error: array subscript value ‘10’ is outside the bounds of array ‘g’ of type ‘const int [10]’
+  115 |   return g[i];
+      |          ~~~^
+pr63956.C:114:17: note: declared here
+  114 |   constexpr int g[10] = { };
+      |                 ^
 pr63956.C:130:24:   in ‘constexpr’ expansion of ‘fn9(2147483647, 1)’
 pr63956.C:130:39: error: overflow in constant expression [-fpermissive]
   130 | constexpr int p2 = fn9 (__INT_MAX__, 1); // { dg-error "overflow in constant expression|in .constexpr. expansion of " }

So, I've committed following patch as obvious after testing it on
x86_64-linux -m32/-m64.

2019-06-19  Jakub Jelinek  <jakub@redhat.com>

	* g++.dg/ubsan/pr63956.C: Adjust expected diagnostics.

--- gcc/testsuite/g++.dg/ubsan/pr63956.C.jj	2019-05-20 11:39:25.003963953 +0200
+++ gcc/testsuite/g++.dg/ubsan/pr63956.C	2019-06-19 10:20:12.390666122 +0200
@@ -80,13 +80,13 @@ constexpr int
 fn5 (const int *a, int b)
 {
   if (b != 2)
-    b = a[b];
+    b = a[b]; // { dg-error "array subscript" }
   return b;
 }
 
 constexpr int m1[4] = { 1, 2, 3, 4 };
 constexpr int m2 = fn5 (m1, 3);
-constexpr int m3 = fn5 (m1, 4); // { dg-error "array subscript|in .constexpr. expansion of " }
+constexpr int m3 = fn5 (m1, 4); // { dg-message "in .constexpr. expansion of " }
 
 constexpr int
 fn6 (const int &a, int b)
@@ -112,11 +112,11 @@ constexpr int
 fn8 (int i)
 {
   constexpr int g[10] = { };
-  return g[i];
+  return g[i]; // { dg-error "array subscript" }
 }
 
 constexpr int o1 = fn8 (9);
-constexpr int o2 = fn8 (10); // { dg-error "array subscript|in .constexpr. expansion of " }
+constexpr int o2 = fn8 (10); // { dg-message "in .constexpr. expansion of " }
 
 constexpr int
 fn9 (int a, int b)


	Jakub

Patch

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 15bfb6d8148..10afc419a33 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4382,9 +4382,6 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 			      bool *non_constant_p, bool *overflow_p,
 			      tree *jump_target /* = NULL */)
 {
-  constexpr_ctx new_ctx;
-  tree r = t;
-
   if (jump_target && *jump_target)
     {
       /* If we are jumping, ignore all statements/expressions except those
@@ -4415,6 +4412,9 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       *non_constant_p = true;
       return t;
     }
+
+  STRIP_ANY_LOCATION_WRAPPER (t);
+
   if (CONSTANT_CLASS_P (t))
     {
       if (TREE_OVERFLOW (t))
@@ -4439,8 +4439,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     }
 
   /* Avoid excessively long constexpr evaluations.  */
-  if (!location_wrapper_p (t)
-      && ++*ctx->constexpr_ops_count >= constexpr_ops_limit)
+  if (++*ctx->constexpr_ops_count >= constexpr_ops_limit)
     {
       if (!ctx->quiet)
 	error_at (cp_expr_loc_or_loc (t, input_location),
@@ -4452,6 +4451,9 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       return t;
     }
 
+  constexpr_ctx new_ctx;
+  tree r = t;
+
   tree_code tcode = TREE_CODE (t);
   switch (tcode)
     {
@@ -4752,7 +4754,10 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	/* This function does more aggressive folding than fold itself.  */
 	r = build_fold_addr_expr_with_type (op, TREE_TYPE (t));
 	if (TREE_CODE (r) == ADDR_EXPR && TREE_OPERAND (r, 0) == oldop)
-	  return t;
+	  {
+	    ggc_free (r);
+	    return t;
+	  }
 	break;
       }
 
@@ -5065,6 +5070,8 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	  r = op;
 	else if (tcode == UNARY_PLUS_EXPR)
 	  r = fold_convert (TREE_TYPE (t), op);
+	else if (VOID_TYPE_P (type))
+	  r = void_node;
 	else
 	  r = fold_build1 (tcode, type, op);
 
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 1489238b716..c0720404b75 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,10 @@ 
+2019-06-07  Jason Merrill  <jason@redhat.com>
+
+	* constexpr.c (cxx_eval_constant_expression): Call
+	STRIP_ANY_LOCATION_WRAPPER early.
+	[CONVERT_EXPR]: Don't build anything for conversion to void.
+	[ADDR_EXPR]: ggc_free unused ADDR_EXPR.
+
 2019-06-05  Martin Sebor  <msebor@redhat.com>
 
 	PR c/90737