[pushed] c++: discarded-value and constexpr

Message ID 20220524195059.991193-1-jason@redhat.com
State Committed
Commit 72f76540ad0e7185d4f516e781e8bead13ebc170
Headers
Series [pushed] c++: discarded-value and constexpr |

Commit Message

Jason Merrill May 24, 2022, 7:50 p.m. UTC
  I've been thinking for a while that the 'lval' parameter needed a third
value for discarded-value expressions; most importantly,
cxx_eval_store_expression does extra work for an lvalue result, and we also
don't want to do the l->r conversion.

Mostly this is pretty mechanical.  Apart from the _store_ fix, I also use
vc_discard for substatements of a STATEMENT_LIST other than a stmt-expr
result, and avoid building _REFs to be ignored in a few other places.

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

gcc/cp/ChangeLog:

	* constexpr.cc (enum value_cat): New. Change all 'lval' parameters
	from int to value_cat.  Change most false to vc_prvalue, most true
	to vc_glvalue, cases where the return value is ignored to
	vc_discard.
	(cxx_eval_statement_list): Only vc_prvalue for stmt-expr result.
	(cxx_eval_store_expression): Only build _REF for vc_glvalue.
	(cxx_eval_array_reference, cxx_eval_component_reference)
	(cxx_eval_indirect_ref, cxx_eval_constant_expression): Likewise.
---
 gcc/cp/constexpr.cc | 198 ++++++++++++++++++++++++--------------------
 1 file changed, 108 insertions(+), 90 deletions(-)


base-commit: 2540e2c604142889308857657d3510874955336a
  

Patch

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 0f1a43982d0..a015bc7c818 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -1210,9 +1210,6 @@  uid_sensitive_constexpr_evaluation_checker::evaluation_restricted_p () const
 
 static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
 
-static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
-					  bool, bool *, bool *, tree * = NULL);
-
 /* Compute a hash value for a constexpr call representation.  */
 
 inline hashval_t
@@ -1346,13 +1343,25 @@  get_nth_callarg (tree t, int n)
     }
 }
 
+/* Whether our evaluation wants a prvalue (e.g. CONSTRUCTOR or _CST),
+   a glvalue (e.g. VAR_DECL or _REF), or nothing.  */
+
+enum value_cat {
+   vc_prvalue = 0,
+   vc_glvalue = 1,
+   vc_discard = 2
+};
+
+static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
+					  value_cat, bool *, bool *, tree * = NULL);
+
 /* Attempt to evaluate T which represents a call to a builtin function.
    We assume here that all builtin functions evaluate to scalar types
    represented by _CST nodes.  */
 
 static tree
 cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
-				bool lval,
+				value_cat lval,
 				bool *non_constant_p, bool *overflow_p)
 {
   const int nargs = call_expr_nargs (t);
@@ -1458,7 +1467,7 @@  cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
 	  || potential_constant_expression (arg))
 	{
 	  bool dummy1 = false, dummy2 = false;
-	  arg = cxx_eval_constant_expression (&new_ctx, arg, false,
+	  arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
 					      &dummy1, &dummy2);
 	}
 
@@ -1703,7 +1712,7 @@  cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
       /* Normally we would strip a TARGET_EXPR in an initialization context
 	 such as this, but here we do the elision differently: we keep the
 	 TARGET_EXPR, and use its CONSTRUCTOR as the value of the parm.  */
-      arg = cxx_eval_constant_expression (ctx, x, /*lval=*/false,
+      arg = cxx_eval_constant_expression (ctx, x, vc_prvalue,
 					  non_constant_p, overflow_p);
       /* Don't VERIFY_CONSTANT here.  */
       if (*non_constant_p && ctx->quiet)
@@ -1807,7 +1816,7 @@  cx_error_context (void)
 
 static tree
 cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
-			    bool lval,
+			    value_cat lval,
 			    bool *non_constant_p, bool *overflow_p)
 {
   enum tree_code opcode = ERROR_MARK;
@@ -1832,12 +1841,13 @@  cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
 
     case IFN_LAUNDER:
       return cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
-					   false, non_constant_p, overflow_p);
+					   vc_prvalue, non_constant_p,
+					   overflow_p);
 
     case IFN_VEC_CONVERT:
       {
 	tree arg = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
-						 false, non_constant_p,
+						 vc_prvalue, non_constant_p,
 						 overflow_p);
 	if (TREE_CODE (arg) == VECTOR_CST)
 	  if (tree r = fold_const_call (CFN_VEC_CONVERT, TREE_TYPE (t), arg))
@@ -2103,7 +2113,7 @@  cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
     }
 
   /* Evaluate the object so that we know its dynamic type.  */
-  obj = cxx_eval_constant_expression (ctx, obj, /*lval*/false, non_constant_p,
+  obj = cxx_eval_constant_expression (ctx, obj, vc_prvalue, non_constant_p,
 				      overflow_p);
   if (*non_constant_p)
     return call;
@@ -2138,7 +2148,7 @@  cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
      considered to be a most derived object that has the type of the
      constructor or destructor's class.  */
   tree vtable = build_vfield_ref (obj, objtype);
-  vtable = cxx_eval_constant_expression (ctx, vtable, /*lval*/false,
+  vtable = cxx_eval_constant_expression (ctx, vtable, vc_prvalue,
 					 non_constant_p, overflow_p);
   if (*non_constant_p)
     return call;
@@ -2301,7 +2311,7 @@  replace_decl (tree *tp, tree decl, tree replacement)
 
 static tree
 cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl,
-		     bool lval,
+		     value_cat lval,
 		     bool *non_constant_p, bool *overflow_p)
 {
   tree function = THUNK_TARGET (thunk_fndecl);
@@ -2362,7 +2372,7 @@  cxx_set_object_constness (const constexpr_ctx *ctx, tree object,
     {
       /* Subobjects might not be stored in ctx->global->values but we
 	 can get its CONSTRUCTOR by evaluating *this.  */
-      tree e = cxx_eval_constant_expression (ctx, object, /*lval*/false,
+      tree e = cxx_eval_constant_expression (ctx, object, vc_prvalue,
 					     non_constant_p, overflow_p);
       if (TREE_CODE (e) == CONSTRUCTOR && !*non_constant_p)
 	TREE_READONLY (e) = readonly_p;
@@ -2375,7 +2385,7 @@  cxx_set_object_constness (const constexpr_ctx *ctx, tree object,
 
 static tree
 cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
-			  bool lval,
+			  value_cat lval,
 			  bool *non_constant_p, bool *overflow_p)
 {
   /* Handle concept checks separately.  */
@@ -2395,9 +2405,8 @@  cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
   if (TREE_CODE (fun) != FUNCTION_DECL)
     {
       /* Might be a constexpr function pointer.  */
-      fun = cxx_eval_constant_expression (ctx, fun,
-					  /*lval*/false, non_constant_p,
-					  overflow_p);
+      fun = cxx_eval_constant_expression (ctx, fun, vc_prvalue,
+					  non_constant_p, overflow_p);
       STRIP_NOPS (fun);
       if (TREE_CODE (fun) == ADDR_EXPR)
 	fun = TREE_OPERAND (fun, 0);
@@ -2463,7 +2472,7 @@  cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  for (int i = 0; i < nargs; ++i)
 	    {
 	      tree arg = CALL_EXPR_ARG (t, i);
-	      arg = cxx_eval_constant_expression (ctx, arg, false,
+	      arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
 						  non_constant_p, overflow_p);
 	      VERIFY_CONSTANT (arg);
 	      if (i == 0)
@@ -2571,7 +2580,7 @@  cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 	  for (int i = 0; i < nargs; ++i)
 	    {
 	      tree arg = CALL_EXPR_ARG (t, i);
-	      arg = cxx_eval_constant_expression (ctx, arg, false,
+	      arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
 						  non_constant_p, overflow_p);
 	      if (i == 1)
 		arg1 = arg;
@@ -2852,7 +2861,7 @@  cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 
 	  tree jump_target = NULL_TREE;
 	  cxx_eval_constant_expression (&ctx_with_save_exprs, body,
-					lval, non_constant_p, overflow_p,
+					vc_discard, non_constant_p, overflow_p,
 					&jump_target);
 
 	  if (DECL_CONSTRUCTOR_P (fun))
@@ -3213,7 +3222,7 @@  cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
 {
   tree r;
   tree orig_arg = TREE_OPERAND (t, 0);
-  tree arg = cxx_eval_constant_expression (ctx, orig_arg, /*lval*/false,
+  tree arg = cxx_eval_constant_expression (ctx, orig_arg, vc_prvalue,
 					   non_constant_p, overflow_p);
   VERIFY_CONSTANT (arg);
   location_t loc = EXPR_LOCATION (t);
@@ -3259,8 +3268,8 @@  cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
 
       t = fold_convert_loc (loc, ssizetype, TREE_OPERAND (lhs, 1));
       tree nelts = array_type_nelts_top (TREE_TYPE (TREE_OPERAND (lhs, 0)));
-      nelts = cxx_eval_constant_expression (ctx, nelts, false, non_constant_p,
-					    overflow_p);
+      nelts = cxx_eval_constant_expression (ctx, nelts, vc_prvalue,
+					    non_constant_p, overflow_p);
       if (*non_constant_p)
 	return NULL_TREE;
       /* Don't fold an out-of-bound access.  */
@@ -3281,7 +3290,7 @@  cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t,
 		      t, NULL_TREE, NULL_TREE);
       t = cp_build_addr_expr (t, tf_warning_or_error);
       t = cp_fold_convert (orig_type, t);
-      return cxx_eval_constant_expression (ctx, t, /*lval*/false,
+      return cxx_eval_constant_expression (ctx, t, vc_prvalue,
 					   non_constant_p, overflow_p);
     }
 
@@ -3325,20 +3334,20 @@  cxx_maybe_fold_addr_pointer_plus (tree t)
 
 static tree
 cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
-			    bool lval,
+			    value_cat lval,
 			    bool *non_constant_p, bool *overflow_p)
 {
   tree r = NULL_TREE;
   tree orig_lhs = TREE_OPERAND (t, 0);
   tree orig_rhs = TREE_OPERAND (t, 1);
   tree lhs, rhs;
-  lhs = cxx_eval_constant_expression (ctx, orig_lhs, /*lval*/false,
+  lhs = cxx_eval_constant_expression (ctx, orig_lhs, vc_prvalue,
 				      non_constant_p, overflow_p);
   /* Don't VERIFY_CONSTANT here, it's unnecessary and will break pointer
      subtraction.  */
   if (*non_constant_p)
     return t;
-  rhs = cxx_eval_constant_expression (ctx, orig_rhs, /*lval*/false,
+  rhs = cxx_eval_constant_expression (ctx, orig_rhs, vc_prvalue,
 				      non_constant_p, overflow_p);
   if (*non_constant_p)
     return t;
@@ -3457,12 +3466,12 @@  cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
 
 static tree
 cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
-				 bool lval,
+				 value_cat lval,
 				 bool *non_constant_p, bool *overflow_p,
 				 tree *jump_target)
 {
   tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
-					   /*lval*/false,
+					   vc_prvalue,
 					   non_constant_p, overflow_p);
   VERIFY_CONSTANT (val);
   if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t))
@@ -3504,15 +3513,15 @@  cxx_eval_vector_conditional_expression (const constexpr_ctx *ctx, tree t,
 					bool *non_constant_p, bool *overflow_p)
 {
   tree arg1 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
-					    /*lval*/false,
+					    vc_prvalue,
 					    non_constant_p, overflow_p);
   VERIFY_CONSTANT (arg1);
   tree arg2 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-					    /*lval*/false,
+					    vc_prvalue,
 					    non_constant_p, overflow_p);
   VERIFY_CONSTANT (arg2);
   tree arg3 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
-					    /*lval*/false,
+					    vc_prvalue,
 					    non_constant_p, overflow_p);
   VERIFY_CONSTANT (arg3);
   location_t loc = EXPR_LOCATION (t);
@@ -3844,7 +3853,7 @@  get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type,
     gcc_unreachable ();
 
   /* For VLAs, the number of elements won't be an integer constant.  */
-  nelts = cxx_eval_constant_expression (ctx, nelts, false,
+  nelts = cxx_eval_constant_expression (ctx, nelts, vc_prvalue,
 					non_constant_p, overflow_p);
   return nelts;
 }
@@ -3881,7 +3890,7 @@  eval_and_check_array_index (const constexpr_ctx *ctx,
   location_t loc = cp_expr_loc_or_input_loc (t);
   tree ary = TREE_OPERAND (t, 0);
   t = TREE_OPERAND (t, 1);
-  tree index = cxx_eval_constant_expression (ctx, t, false,
+  tree index = cxx_eval_constant_expression (ctx, t, vc_prvalue,
 					     non_constant_p, overflow_p);
   VERIFY_CONSTANT (index);
 
@@ -3913,7 +3922,7 @@  eval_and_check_array_index (const constexpr_ctx *ctx,
 
 static tree
 cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
-			  bool lval,
+			  value_cat lval,
 			  bool *non_constant_p, bool *overflow_p)
 {
   tree oldary = TREE_OPERAND (t, 0);
@@ -3936,6 +3945,8 @@  cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 
   if (lval && ary == oldary && index == oldidx)
     return t;
+  else if (lval == vc_discard)
+    return t;
   else if (lval)
     return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL);
 
@@ -4043,7 +4054,7 @@  cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 
 static tree
 cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
-			      bool lval,
+			      value_cat lval,
 			      bool *non_constant_p, bool *overflow_p)
 {
   unsigned HOST_WIDE_INT i;
@@ -4067,6 +4078,8 @@  cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
     whole = cplus_expand_constant (whole);
   if (whole == orig_whole)
     return t;
+  if (lval == vc_discard)
+    return t;
   if (lval)
     return fold_build3 (COMPONENT_REF, TREE_TYPE (t),
 			whole, part, NULL_TREE);
@@ -4152,7 +4165,7 @@  cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
 
 static tree
 cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
-			bool lval,
+			value_cat lval,
 			bool *non_constant_p, bool *overflow_p)
 {
   tree orig_whole = TREE_OPERAND (t, 0);
@@ -4449,7 +4462,7 @@  cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
       return t;
     }
 
-  tree op = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false,
+  tree op = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue,
 					  non_constant_p, overflow_p);
   if (*non_constant_p)
     return t;
@@ -4569,14 +4582,14 @@  cxx_eval_logical_expression (const constexpr_ctx *ctx, tree t,
 {
   tree r;
   tree lhs = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
-					   /*lval*/false, non_constant_p,
+					   vc_prvalue, non_constant_p,
 					   overflow_p);
   VERIFY_CONSTANT (lhs);
   if (tree_int_cst_equal (lhs, bailout_value))
     return lhs;
   gcc_assert (tree_int_cst_equal (lhs, continue_value));
   r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-				    /*lval*/false, non_constant_p,
+				    vc_prvalue, non_constant_p,
 				    overflow_p);
   VERIFY_CONSTANT (r);
   return r;
@@ -4725,7 +4738,7 @@  verify_ctor_sanity (const constexpr_ctx *ctx, tree type)
 
 static tree
 cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
-			 bool lval,
+			 value_cat lval,
 			 bool *non_constant_p, bool *overflow_p)
 {
   vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t);
@@ -4858,7 +4871,7 @@  cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
 
 static tree
 cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
-		     bool value_init, bool lval,
+		     bool value_init, value_cat lval,
 		     bool *non_constant_p, bool *overflow_p)
 {
   tree elttype = TREE_TYPE (atype);
@@ -5000,7 +5013,7 @@  cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
 
 static tree
 cxx_eval_vec_init (const constexpr_ctx *ctx, tree t,
-		   bool lval,
+		   value_cat lval,
 		   bool *non_constant_p, bool *overflow_p)
 {
   tree atype = TREE_TYPE (t);
@@ -5070,7 +5083,7 @@  cxx_union_active_member (const constexpr_ctx *ctx, tree t)
   constexpr_ctx new_ctx = *ctx;
   new_ctx.quiet = true;
   bool non_constant_p = false, overflow_p = false;
-  tree ctor = cxx_eval_constant_expression (&new_ctx, t, false,
+  tree ctor = cxx_eval_constant_expression (&new_ctx, t, vc_prvalue,
 					    &non_constant_p,
 					    &overflow_p);
   if (TREE_CODE (ctor) == CONSTRUCTOR
@@ -5306,7 +5319,7 @@  cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type,
 
 static tree
 cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
-		       bool lval,
+		       value_cat lval,
 		       bool *non_constant_p, bool *overflow_p)
 {
   tree orig_op0 = TREE_OPERAND (t, 0);
@@ -5330,7 +5343,7 @@  cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
     {
       /* If that didn't work, evaluate the operand first.  */
       tree op0 = cxx_eval_constant_expression (ctx, orig_op0,
-					       /*lval*/false, non_constant_p,
+					       vc_prvalue, non_constant_p,
 					       overflow_p);
       /* Don't VERIFY_CONSTANT here.  */
       if (*non_constant_p)
@@ -5366,7 +5379,7 @@  cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 	      return t;
 	    }
 
-	  if (lval && op0 != orig_op0)
+	  if (lval == vc_glvalue && op0 != orig_op0)
 	    return build1 (INDIRECT_REF, TREE_TYPE (t), op0);
 	  if (!lval)
 	    VERIFY_CONSTANT (t);
@@ -5462,7 +5475,7 @@  non_const_var_error (location_t loc, tree r)
 
 static tree
 cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
-			     bool lval,
+			     value_cat lval,
 			     bool *non_constant_p, bool *overflow_p)
 {
   int i;
@@ -5594,7 +5607,7 @@  modifying_const_object_p (tree_code code, tree obj, bool mutable_p)
 
 static tree
 cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
-			   bool lval,
+			   value_cat lval,
 			   bool *non_constant_p, bool *overflow_p)
 {
   constexpr_ctx new_ctx = *ctx;
@@ -5617,19 +5630,19 @@  cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 	 stored in, so that any side-effects happen first.  */
       if (!SCALAR_TYPE_P (type))
 	new_ctx.ctor = new_ctx.object = NULL_TREE;
-      init = cxx_eval_constant_expression (&new_ctx, init, false,
+      init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue,
 					   non_constant_p, overflow_p);
       if (*non_constant_p)
 	return t;
     }
 
   bool evaluated = false;
-  if (lval)
+  if (lval == vc_glvalue)
     {
       /* 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,
+      target = cxx_eval_constant_expression (ctx, target, lval,
 					     non_constant_p, overflow_p);
       evaluated = true;
       if (*non_constant_p)
@@ -5681,7 +5694,7 @@  cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 	    object = probe;
 	  else
 	    {
-	      probe = cxx_eval_constant_expression (ctx, probe, true,
+	      probe = cxx_eval_constant_expression (ctx, probe, vc_glvalue,
 						    non_constant_p, overflow_p);
 	      evaluated = true;
 	      if (*non_constant_p)
@@ -5893,7 +5906,7 @@  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;
-      init = cxx_eval_constant_expression (&new_ctx, init, false,
+      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
 	 initializer might have mutated the underlying CONSTRUCTORs, so we must
@@ -5992,7 +6005,7 @@  cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 
 static tree
 cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
-			      bool lval,
+			      value_cat lval,
 			      bool *non_constant_p, bool *overflow_p)
 {
   enum tree_code code = TREE_CODE (t);
@@ -6006,12 +6019,12 @@  cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
   offset = fold_simple (offset);
 
   /* The operand as an lvalue.  */
-  op = cxx_eval_constant_expression (ctx, op, true,
+  op = cxx_eval_constant_expression (ctx, op, vc_glvalue,
 				     non_constant_p, overflow_p);
 
   /* The operand as an rvalue.  */
   tree val
-    = cxx_eval_constant_expression (ctx, op, false,
+    = cxx_eval_constant_expression (ctx, op, vc_prvalue,
 				    non_constant_p, overflow_p);
   /* Don't VERIFY_CONSTANT if this might be dealing with a pointer to
      a local array in a constexpr function.  */
@@ -6160,8 +6173,10 @@  cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
       local_target = NULL_TREE;
       jump_target = &local_target;
     }
-  for (tree stmt : tsi_range (t))
+  for (tree_stmt_iterator i = tsi_start (t); !tsi_end_p (i); ++i)
     {
+      tree stmt = *i;
+
       /* We've found a continue, so skip everything until we reach
 	 the label its jumping to.  */
       if (continues (jump_target))
@@ -6174,7 +6189,13 @@  cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
 	}
       if (TREE_CODE (stmt) == DEBUG_BEGIN_STMT)
 	continue;
-      r = cxx_eval_constant_expression (ctx, stmt, false,
+
+      value_cat lval = vc_discard;
+      /* The result of a statement-expression is not wrapped in EXPR_STMT.  */
+      if (tsi_one_before_end_p (i) && TREE_CODE (stmt) != EXPR_STMT)
+	lval = vc_prvalue;
+
+      r = cxx_eval_constant_expression (ctx, stmt, lval,
 					non_constant_p, overflow_p,
 					jump_target);
       if (*non_constant_p)
@@ -6228,7 +6249,7 @@  cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
       break;
     case FOR_STMT:
       if (FOR_INIT_STMT (t))
-	cxx_eval_constant_expression (ctx, FOR_INIT_STMT (t), /*lval*/false,
+	cxx_eval_constant_expression (ctx, FOR_INIT_STMT (t), vc_discard,
 				      non_constant_p, overflow_p, jump_target);
       if (*non_constant_p)
 	return NULL_TREE;
@@ -6247,7 +6268,7 @@  cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
       if (count != -1)
 	{
 	  if (body)
-	    cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
+	    cxx_eval_constant_expression (&new_ctx, body, vc_discard,
 					  non_constant_p, overflow_p,
 					  jump_target);
 	  if (breaks (jump_target))
@@ -6260,7 +6281,7 @@  cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
 	    *jump_target = NULL_TREE;
 
 	  if (expr)
-	    cxx_eval_constant_expression (&new_ctx, expr, /*lval*/false,
+	    cxx_eval_constant_expression (&new_ctx, expr, vc_prvalue,
 					  non_constant_p, overflow_p,
 					  jump_target);
 	}
@@ -6268,7 +6289,7 @@  cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
       if (cond)
 	{
 	  tree res
-	    = cxx_eval_constant_expression (&new_ctx, cond, /*lval*/false,
+	    = cxx_eval_constant_expression (&new_ctx, cond, vc_prvalue,
 					    non_constant_p, overflow_p,
 					    jump_target);
 	  if (res)
@@ -6322,7 +6343,7 @@  cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t,
 {
   tree cond
     = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t);
-  cond = cxx_eval_constant_expression (ctx, cond, false,
+  cond = cxx_eval_constant_expression (ctx, cond, vc_prvalue,
 				       non_constant_p, overflow_p);
   VERIFY_CONSTANT (cond);
   *jump_target = cond;
@@ -6332,7 +6353,7 @@  cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t,
   constexpr_ctx new_ctx = *ctx;
   constexpr_switch_state css = css_default_not_seen;
   new_ctx.css_state = &css;
-  cxx_eval_constant_expression (&new_ctx, body, false,
+  cxx_eval_constant_expression (&new_ctx, body, vc_discard,
 				non_constant_p, overflow_p, jump_target);
   if (switches (jump_target) && css == css_default_seen)
     {
@@ -6340,7 +6361,7 @@  cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t,
 	 this time instructing label_matches to return true for default:
 	 label on switches (jump_target).  */
       css = css_default_processing;
-      cxx_eval_constant_expression (&new_ctx, body, false,
+      cxx_eval_constant_expression (&new_ctx, body, vc_discard,
 				    non_constant_p, overflow_p, jump_target);
     }
   if (breaks (jump_target) || switches (jump_target))
@@ -6351,7 +6372,7 @@  cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t,
 /* Find the object of TYPE under initialization in CTX.  */
 
 static tree
-lookup_placeholder (const constexpr_ctx *ctx, bool lval, tree type)
+lookup_placeholder (const constexpr_ctx *ctx, value_cat lval, tree type)
 {
   if (!ctx)
     return NULL_TREE;
@@ -6478,12 +6499,12 @@  build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type,
 	  tree op1 = TREE_OPERAND (arg_size, 1);
 	  if (integer_zerop (op0))
 	    arg_size
-	      = cxx_eval_constant_expression (ctx, op1, false, non_constant_p,
-					      overflow_p);
+	      = cxx_eval_constant_expression (ctx, op1, vc_prvalue,
+					      non_constant_p, overflow_p);
 	  else if (integer_zerop (op1))
 	    arg_size
-	      = cxx_eval_constant_expression (ctx, op0, false, non_constant_p,
-					      overflow_p);
+	      = cxx_eval_constant_expression (ctx, op0, vc_prvalue,
+					      non_constant_p, overflow_p);
 	  else
 	    arg_size = NULL_TREE;
 	}
@@ -6513,7 +6534,7 @@  build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type,
 
 static tree
 cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
-			      bool lval,
+			      value_cat lval,
 			      bool *non_constant_p, bool *overflow_p,
 			      tree *jump_target /* = NULL */)
 {
@@ -6760,8 +6781,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 
 	if (tree init = DECL_INITIAL (r))
 	  {
-	    init = cxx_eval_constant_expression (ctx, init,
-						 false,
+	    init = cxx_eval_constant_expression (ctx, init, vc_prvalue,
 						 non_constant_p, overflow_p);
 	    /* Don't share a CONSTRUCTOR that might be changed.  */
 	    init = unshare_constructor (init);
@@ -6821,10 +6841,9 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	    ctx->global->values.put (new_ctx.object, new_ctx.ctor);
 	    ctx = &new_ctx;
 	  }
-	/* Pass false for 'lval' because this indicates
+	/* Pass vc_prvalue because this indicates
 	   initialization of a temporary.  */
-	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-					  false,
+	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_prvalue,
 					  non_constant_p, overflow_p);
 	if (*non_constant_p)
 	  break;
@@ -6880,7 +6899,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	r = *p;
       else
 	{
-	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false,
+	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue,
 					    non_constant_p, overflow_p);
 	  if (*non_constant_p)
 	    break;
@@ -6922,7 +6941,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	tree cleanup;
 	/* Evaluate the cleanups.  */
 	FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
-	  cxx_eval_constant_expression (ctx, cleanup, false,
+	  cxx_eval_constant_expression (ctx, cleanup, vc_discard,
 					non_constant_p, overflow_p);
       }
       break;
@@ -6933,7 +6952,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 					jump_target);
       if (!*non_constant_p)
 	/* Also evaluate the cleanup.  */
-	cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), true,
+	cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard,
 				      non_constant_p, overflow_p);
       break;
 
@@ -6945,7 +6964,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	{
 	  iloc_sentinel ils (loc);
 	  /* Also evaluate the cleanup.  */
-	  cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
+	  cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), vc_discard,
 					non_constant_p, overflow_p);
 	}
       break;
@@ -6962,8 +6981,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case ADDR_EXPR:
       {
 	tree oldop = TREE_OPERAND (t, 0);
-	tree op = cxx_eval_constant_expression (ctx, oldop,
-						/*lval*/true,
+	tree op = cxx_eval_constant_expression (ctx, oldop, vc_glvalue,
 						non_constant_p, overflow_p);
 	/* Don't VERIFY_CONSTANT here.  */
 	if (*non_constant_p)
@@ -6987,7 +7005,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 					    non_constant_p, overflow_p);
 	  if (r == error_mark_node)
 	    ;
-	  else if (r == TREE_OPERAND (t, 0))
+	  else if (r == TREE_OPERAND (t, 0) || lval == vc_discard)
 	    r = t;
 	  else
 	    r = fold_build1 (TREE_CODE (t), TREE_TYPE (t), r);
@@ -7039,8 +7057,8 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	else
 	  {
 	    /* Check that the LHS is constant and then discard it.  */
-	    cxx_eval_constant_expression (ctx, op0,
-					  true, non_constant_p, overflow_p,
+	    cxx_eval_constant_expression (ctx, op0, vc_discard,
+					  non_constant_p, overflow_p,
 					  jump_target);
 	    if (*non_constant_p)
 	      return t;
@@ -7461,7 +7479,7 @@  cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case EXIT_EXPR:
       {
 	tree cond = TREE_OPERAND (t, 0);
-	cond = cxx_eval_constant_expression (ctx, cond, /*lval*/false,
+	cond = cxx_eval_constant_expression (ctx, cond, vc_prvalue,
 					     non_constant_p, overflow_p);
 	VERIFY_CONSTANT (cond);
 	if (integer_nonzerop (cond))
@@ -7820,8 +7838,8 @@  cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 
   if (manifestly_const_eval)
     instantiate_constexpr_fns (r);
-  r = cxx_eval_constant_expression (&ctx, r,
-				    false, &non_constant_p, &overflow_p);
+  r = cxx_eval_constant_expression (&ctx, r, vc_prvalue,
+				    &non_constant_p, &overflow_p);
 
   if (!constexpr_dtor)
     verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
@@ -7832,7 +7850,7 @@  cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
   tree cleanup;
   /* Evaluate the cleanups.  */
   FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup)
-    cxx_eval_constant_expression (&ctx, cleanup, false,
+    cxx_eval_constant_expression (&ctx, cleanup, vc_discard,
 				  &non_constant_p, &overflow_p);
 
   /* Mutable logic is a bit tricky: we want to allow initialization of