This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCH to implement C++14 aggregate NSDMI (N3653)


This patch implements one of the remaining missing features in our C++14 support. To support references to the object being initialized, which has also been a hole in our support of C++11 constexpr constructors, I now pass down both the object being initialized and the initializer we're building through the constexpr machinery. I also track the values of temporaries created within the constant-expression, which should extend easily to tracking local variables in full C++14 constexpr support.

I'm using PLACEHOLDER_EXPR for references to the object being initialized in NSDMI, since its semantics described in tree.def match very well what we need here. Unfortunately, contains_placeholder_p and substitute_placeholder_in_expr don't work so well for me; in particular, I need to be able to start from the most complete object and build up subobject references as we work inward, while s_p_i_e expects to be passed a reference to an innermost subobject, so I needed to roll my own replacement.

Currently I'm not tracking whether or not a particular class uses placeholders, and the changes for tracking the initializer as we build it required me to drop the memory optimization I had previously where we wouldn't build a new CONSTRUCTOR unless one of the elements changed. Hopefully these won't have too much impact on time/space usage.

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

commit c7c58e6d9ee52a501cc002c67c8972798a98814e
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Apr 22 13:17:05 2013 -0400

    	Implement N3653 (Member initializers and aggregates) and fix
    	references to 'this' in constexpr constructors.
    	* class.c (check_field_decls): In C++14 an NSDMI does not make the
    	class non-aggregate.
    	* constexpr.c (struct constexpr_ctx): New.
    	(cxx_bind_parameters_in_call): Handle 'this'.
    	(cxx_eval_call_expression): Create new constexpr_ctx.
    	(cxx_eval_component_reference): Check CONSTRUCTOR_NO_IMPLICIT_ZERO.
    	(initialized_type, init_subob_ctx, verify_ctor_sanity): New.
    	(cxx_eval_bare_aggregate): Use them.  Build CONSTRUCTOR early.
    	(cxx_eval_vec_init_1): Likewise.
    	(cxx_eval_constant_expression) [PARM_DECL]: Allow 'this'.
    	[TARGET_EXPR]: Build new constexpr_ctx.
    	[PLACEHOLDER_EXPR]: New.
    	(cxx_eval_outermost_constant_expr): Build new constexpr_ctx.  Add
    	object parameter.
    	(is_sub_constant_expr): Build new constexpr_ctx.
    	(potential_constant_expression_1): Handle PLACEHOLDER_EXPR.
    	Allow 'this'.
    	* cp-gimplify.c (cp_gimplify_init_expr): Call replace_placeholders.
    	* cp-tree.h (CONSTRUCTOR_NO_IMPLICIT_ZERO): New.
    	* error.c (dump_expr): Handle PLACEHOLDER_EXPR.
    	* init.c (get_nsdmi): Generate PLACEHOLDER_EXPR.
    	* tree.c (lvalue_kind): Handle PLACEHOLDER_EXPR.
    	(build_ctor_subob_ref, replace_placeholders): New.
    	* typeck2.c (store_init_value): Use replace_placeholders.
    	(process_init_constructor_record): Make zero-init before NSDMI
    	explicit.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 99bfa95..5f50aff 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -3659,8 +3659,8 @@ check_field_decls (tree t, tree *access_decls,
 
       /* Now that we've removed bit-field widths from DECL_INITIAL,
 	 anything left in DECL_INITIAL is an NSDMI that makes the class
-	 non-aggregate.  */
-      if (DECL_INITIAL (x))
+	 non-aggregate in C++11.  */
+      if (DECL_INITIAL (x) && cxx_dialect < cxx14)
 	CLASSTYPE_NON_AGGREGATE (t) = true;
 
       /* If any field is const, the structure type is pseudo-const.  */
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 30a5e08..cd10766 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -846,14 +846,26 @@ struct constexpr_call_hasher : ggc_hasher<constexpr_call *>
 {
   static hashval_t hash (constexpr_call *);
   static bool equal (constexpr_call *, constexpr_call *);
-		     };
+};
+
+/* The constexpr expansion context.  CALL is the current function
+   expansion, CTOR is the current aggregate initializer, OBJECT is the
+   object being initialized by CTOR, either a VAR_DECL or a _REF.  VALUES
+   is a map of values of variables initialized within the expression.  */
+
+struct constexpr_ctx {
+  constexpr_call *call;
+  hash_map<tree,tree> *values;
+  tree ctor;
+  tree object;
+};
 
 /* A table of all constexpr calls that have been evaluated by the
    compiler in this translation unit.  */
 
 static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
 
-static tree cxx_eval_constant_expression (const constexpr_call *, tree,
+static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
 					  bool, bool, bool *, bool *);
 
 /* Compute a hash value for a constexpr call representation.  */
@@ -964,7 +976,7 @@ lookup_parameter_binding (const constexpr_call *call, tree t)
    represented by _CST nodes.  */
 
 static tree
-cxx_eval_builtin_function_call (const constexpr_call *call, tree t,
+cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t,
 				bool allow_non_constant, bool addr,
 				bool *non_constant_p, bool *overflow_p)
 {
@@ -974,7 +986,7 @@ cxx_eval_builtin_function_call (const constexpr_call *call, tree t,
   int i;
   for (i = 0; i < nargs; ++i)
     {
-      args[i] = cxx_eval_constant_expression (call, CALL_EXPR_ARG (t, i),
+      args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, i),
 					      allow_non_constant, addr,
 					      non_constant_p, overflow_p);
       if (allow_non_constant && *non_constant_p)
@@ -1006,12 +1018,12 @@ adjust_temp_type (tree type, tree temp)
 
 /* Subroutine of cxx_eval_call_expression.
    We are processing a call expression (either CALL_EXPR or
-   AGGR_INIT_EXPR) in the call context of OLD_CALL.  Evaluate
+   AGGR_INIT_EXPR) in the context of CTX.  Evaluate
    all arguments and bind their values to correspondings
    parameters, making up the NEW_CALL context.  */
 
 static void
-cxx_bind_parameters_in_call (const constexpr_call *old_call, tree t,
+cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
                              constexpr_call *new_call,
 			     bool allow_non_constant,
 			     bool *non_constant_p, bool *overflow_p)
@@ -1024,12 +1036,18 @@ cxx_bind_parameters_in_call (const constexpr_call *old_call, tree t,
     {
       tree x, arg;
       tree type = parms ? TREE_TYPE (parms) : void_type_node;
-      /* For member function, the first argument is a pointer to the implied
-         object.  And for an object construction, don't bind `this' before
-         it is fully constructed.  */
-      if (i == 0 && DECL_CONSTRUCTOR_P (fun))
-        goto next;
       x = get_nth_callarg (t, i);
+      /* For member function, the first argument is a pointer to the implied
+         object.  For a constructor, it might still be a dummy object, in
+         which case we get the real argument from ctx or the AGGR_INIT_EXPR. */
+      if (i == 0 && DECL_CONSTRUCTOR_P (fun)
+	  && is_dummy_object (x))
+	{
+	  x = ctx->object;
+	  if (!x)
+	    x = AGGR_INIT_EXPR_SLOT (t);
+	  x = cp_build_addr_expr (x, tf_warning_or_error);
+	}
       if (parms && DECL_BY_REFERENCE (parms))
 	{
 	  /* cp_genericize made this a reference for argument passing, but
@@ -1039,7 +1057,7 @@ cxx_bind_parameters_in_call (const constexpr_call *old_call, tree t,
 	  type = TREE_TYPE (type);
 	  x = convert_from_reference (x);
 	}
-      arg = cxx_eval_constant_expression (old_call, x, allow_non_constant,
+      arg = cxx_eval_constant_expression (ctx, x, allow_non_constant,
 					  TREE_CODE (type) == REFERENCE_TYPE,
 					  non_constant_p, overflow_p);
       /* Don't VERIFY_CONSTANT here.  */
@@ -1104,7 +1122,7 @@ cx_error_context (void)
    evaluation.  */
 
 static tree
-cxx_eval_call_expression (const constexpr_call *old_call, tree t,
+cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 			  bool allow_non_constant, bool addr,
 			  bool *non_constant_p, bool *overflow_p)
 {
@@ -1119,7 +1137,7 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t,
   if (TREE_CODE (fun) != FUNCTION_DECL)
     {
       /* Might be a constexpr function pointer.  */
-      fun = cxx_eval_constant_expression (old_call, fun, allow_non_constant,
+      fun = cxx_eval_constant_expression (ctx, fun, allow_non_constant,
 					  /*addr*/false, non_constant_p,
 					  overflow_p);
       STRIP_NOPS (fun);
@@ -1137,7 +1155,7 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t,
   if (DECL_CLONED_FUNCTION_P (fun))
     fun = DECL_CLONED_FUNCTION (fun);
   if (is_builtin_fn (fun))
-    return cxx_eval_builtin_function_call (old_call, t, allow_non_constant,
+    return cxx_eval_builtin_function_call (ctx, t, allow_non_constant,
 					   addr, non_constant_p, overflow_p);
   if (!DECL_DECLARED_CONSTEXPR_P (fun))
     {
@@ -1156,7 +1174,7 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t,
       if (call_expr_nargs (t) == 2)
 	{
 	  tree arg = convert_from_reference (get_nth_callarg (t, 1));
-	  return cxx_eval_constant_expression (old_call, arg, allow_non_constant,
+	  return cxx_eval_constant_expression (ctx, arg, allow_non_constant,
 					       addr, non_constant_p, overflow_p);
 	}
       else if (TREE_CODE (t) == AGGR_INIT_EXPR
@@ -1165,8 +1183,8 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t,
     }
 
   /* If in direct recursive call, optimize definition search.  */
-  if (old_call != NULL && old_call->fundef->decl == fun)
-    new_call.fundef = old_call->fundef;
+  if (ctx && ctx->call && ctx->call->fundef->decl == fun)
+    new_call.fundef = ctx->call->fundef;
   else
     {
       new_call.fundef = retrieve_constexpr_fundef (fun);
@@ -1187,7 +1205,7 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t,
           return t;
         }
     }
-  cxx_bind_parameters_in_call (old_call, t, &new_call,
+  cxx_bind_parameters_in_call (ctx, t, &new_call,
 			       allow_non_constant, non_constant_p, overflow_p);
   if (*non_constant_p)
     return t;
@@ -1232,10 +1250,14 @@ cxx_eval_call_expression (const constexpr_call *old_call, tree t,
     {
       result = entry->result;
       if (!result || result == error_mark_node)
-	result = (cxx_eval_constant_expression
-		  (&new_call, new_call.fundef->body,
-		   allow_non_constant, addr,
-		   non_constant_p, overflow_p));
+	{
+	  constexpr_ctx new_ctx = *ctx;
+	  new_ctx.call = &new_call;
+	  result = (cxx_eval_constant_expression
+		    (&new_ctx, new_call.fundef->body,
+		     allow_non_constant, addr,
+		     non_constant_p, overflow_p));
+	}
       if (result == error_mark_node)
 	*non_constant_p = true;
       if (*non_constant_p)
@@ -1326,13 +1348,13 @@ verify_constant (tree t, bool allow_non_constant, bool *non_constant_p,
    and return error_mark_node.  */
 
 static tree
-cxx_eval_unary_expression (const constexpr_call *call, tree t,
+cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
 			   bool allow_non_constant, bool addr,
 			   bool *non_constant_p, bool *overflow_p)
 {
   tree r;
   tree orig_arg = TREE_OPERAND (t, 0);
-  tree arg = cxx_eval_constant_expression (call, orig_arg, allow_non_constant,
+  tree arg = cxx_eval_constant_expression (ctx, orig_arg, allow_non_constant,
 					   addr, non_constant_p, overflow_p);
   VERIFY_CONSTANT (arg);
   if (arg == orig_arg)
@@ -1346,7 +1368,7 @@ cxx_eval_unary_expression (const constexpr_call *call, tree t,
    Like cxx_eval_unary_expression, except for binary expressions.  */
 
 static tree
-cxx_eval_binary_expression (const constexpr_call *call, tree t,
+cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
 			    bool allow_non_constant, bool addr,
 			    bool *non_constant_p, bool *overflow_p)
 {
@@ -1354,11 +1376,11 @@ cxx_eval_binary_expression (const constexpr_call *call, tree t,
   tree orig_lhs = TREE_OPERAND (t, 0);
   tree orig_rhs = TREE_OPERAND (t, 1);
   tree lhs, rhs;
-  lhs = cxx_eval_constant_expression (call, orig_lhs,
+  lhs = cxx_eval_constant_expression (ctx, orig_lhs,
 				      allow_non_constant, addr,
 				      non_constant_p, overflow_p);
   VERIFY_CONSTANT (lhs);
-  rhs = cxx_eval_constant_expression (call, orig_rhs,
+  rhs = cxx_eval_constant_expression (ctx, orig_rhs,
 				      allow_non_constant, addr,
 				      non_constant_p, overflow_p);
   VERIFY_CONSTANT (rhs);
@@ -1374,20 +1396,20 @@ cxx_eval_binary_expression (const constexpr_call *call, tree t,
    looked into.  */
 
 static tree
-cxx_eval_conditional_expression (const constexpr_call *call, tree t,
+cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
 				 bool allow_non_constant, bool addr,
 				 bool *non_constant_p, bool *overflow_p)
 {
-  tree val = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0),
+  tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					   allow_non_constant, addr,
 					   non_constant_p, overflow_p);
   VERIFY_CONSTANT (val);
   /* Don't VERIFY_CONSTANT the other operands.  */
   if (integer_zerop (val))
-    return cxx_eval_constant_expression (call, TREE_OPERAND (t, 2),
+    return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
 					 allow_non_constant, addr,
 					 non_constant_p, overflow_p);
-  return cxx_eval_constant_expression (call, TREE_OPERAND (t, 1),
+  return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 				       allow_non_constant, addr,
 				       non_constant_p, overflow_p);
 }
@@ -1396,12 +1418,12 @@ cxx_eval_conditional_expression (const constexpr_call *call, tree t,
    Attempt to reduce a reference to an array slot.  */
 
 static tree
-cxx_eval_array_reference (const constexpr_call *call, tree t,
+cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
 			  bool allow_non_constant, bool addr,
 			  bool *non_constant_p, bool *overflow_p)
 {
   tree oldary = TREE_OPERAND (t, 0);
-  tree ary = cxx_eval_constant_expression (call, oldary,
+  tree ary = cxx_eval_constant_expression (ctx, oldary,
 					   allow_non_constant, addr,
 					   non_constant_p, overflow_p);
   tree index, oldidx;
@@ -1411,7 +1433,7 @@ cxx_eval_array_reference (const constexpr_call *call, tree t,
   if (*non_constant_p)
     return t;
   oldidx = TREE_OPERAND (t, 1);
-  index = cxx_eval_constant_expression (call, oldidx,
+  index = cxx_eval_constant_expression (ctx, oldidx,
 					allow_non_constant, false,
 					non_constant_p, overflow_p);
   VERIFY_CONSTANT (index);
@@ -1442,7 +1464,7 @@ cxx_eval_array_reference (const constexpr_call *call, tree t,
 	  /* If it's within the array bounds but doesn't have an explicit
 	     initializer, it's value-initialized.  */
 	  tree val = build_value_init (elem_type, tf_warning_or_error);
-	  return cxx_eval_constant_expression (call, val,
+	  return cxx_eval_constant_expression (ctx, val,
 					       allow_non_constant, addr,
 					       non_constant_p, overflow_p);
 	}
@@ -1479,7 +1501,7 @@ cxx_eval_array_reference (const constexpr_call *call, tree t,
    Attempt to reduce a field access of a value of class type.  */
 
 static tree
-cxx_eval_component_reference (const constexpr_call *call, tree t,
+cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
 			      bool allow_non_constant, bool addr,
 			      bool *non_constant_p, bool *overflow_p)
 {
@@ -1488,7 +1510,7 @@ cxx_eval_component_reference (const constexpr_call *call, tree t,
   tree value;
   tree part = TREE_OPERAND (t, 1);
   tree orig_whole = TREE_OPERAND (t, 0);
-  tree whole = cxx_eval_constant_expression (call, orig_whole,
+  tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     allow_non_constant, addr,
 					     non_constant_p, overflow_p);
   if (whole == orig_whole)
@@ -1528,9 +1550,20 @@ cxx_eval_component_reference (const constexpr_call *call, tree t,
       return t;
     }
 
+  if (CONSTRUCTOR_NO_IMPLICIT_ZERO (whole))
+    {
+      /* 'whole' is part of the aggregate initializer we're currently
+	 building; if there's no initializer for this member yet, that's an
+	 error. */
+      if (!allow_non_constant)
+	error ("accessing uninitialized member %qD", part);
+      *non_constant_p = true;
+      return t;
+    }
+
   /* If there's no explicit init for this field, it's value-initialized.  */
   value = build_value_init (TREE_TYPE (t), tf_warning_or_error);
-  return cxx_eval_constant_expression (call, value,
+  return cxx_eval_constant_expression (ctx, value,
 				       allow_non_constant, addr,
 				       non_constant_p, overflow_p);
 }
@@ -1540,7 +1573,7 @@ cxx_eval_component_reference (const constexpr_call *call, tree t,
    expressed as a BIT_FIELD_REF.  */
 
 static tree
-cxx_eval_bit_field_ref (const constexpr_call *call, tree t,
+cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
 			bool allow_non_constant, bool addr,
 			bool *non_constant_p, bool *overflow_p)
 {
@@ -1548,7 +1581,7 @@ cxx_eval_bit_field_ref (const constexpr_call *call, tree t,
   tree retval, fldval, utype, mask;
   bool fld_seen = false;
   HOST_WIDE_INT istart, isize;
-  tree whole = cxx_eval_constant_expression (call, orig_whole,
+  tree whole = cxx_eval_constant_expression (ctx, orig_whole,
 					     allow_non_constant, addr,
 					     non_constant_p, overflow_p);
   tree start, field, value;
@@ -1625,20 +1658,20 @@ cxx_eval_bit_field_ref (const constexpr_call *call, tree t,
    sanity check purposes.  */
 
 static tree
-cxx_eval_logical_expression (const constexpr_call *call, tree t,
+cxx_eval_logical_expression (const constexpr_ctx *ctx, tree t,
                              tree bailout_value, tree continue_value,
 			     bool allow_non_constant, bool addr,
 			     bool *non_constant_p, bool *overflow_p)
 {
   tree r;
-  tree lhs = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0),
+  tree lhs = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					   allow_non_constant, addr,
 					   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 (call, TREE_OPERAND (t, 1),
+  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 				    allow_non_constant, addr, non_constant_p, overflow_p);
   VERIFY_CONSTANT (r);
   return r;
@@ -1674,59 +1707,153 @@ base_field_constructor_elt (vec<constructor_elt, va_gc> *v, tree ref)
   return NULL;
 }
 
+/* Some of the expressions fed to the constexpr mechanism are calls to
+   constructors, which have type void.  In that case, return the type being
+   initialized by the constructor.  */
+
+static tree
+initialized_type (tree t)
+{
+  if (TYPE_P (t))
+    return t;
+  tree type = cv_unqualified (TREE_TYPE (t));
+  if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)
+    {
+      /* A constructor call has void type, so we need to look deeper.  */
+      tree fn = get_function_named_in_call (t);
+      if (fn && TREE_CODE (fn) == FUNCTION_DECL
+	  && DECL_CXX_CONSTRUCTOR_P (fn))
+	type = DECL_CONTEXT (fn);
+    }
+  return type;
+}
+
+/* We're about to initialize element INDEX of an array or class from VALUE.
+   Set up NEW_CTX appropriately by adjusting .object to refer to the
+   subobject and creating a new CONSTRUCTOR if the element is itself
+   a class or array.  */
+
+static void
+init_subob_ctx (const constexpr_ctx *ctx, constexpr_ctx &new_ctx,
+	       tree index, tree &value)
+{
+  new_ctx = *ctx;
+
+  if (index && TREE_CODE (index) != INTEGER_CST
+      && TREE_CODE (index) != FIELD_DECL)
+    /* This won't have an element in the new CONSTRUCTOR.  */
+    return;
+
+  tree type = initialized_type (value);
+  if (!AGGREGATE_TYPE_P (type) && !VECTOR_TYPE_P (type))
+    /* A non-aggregate member doesn't get its own CONSTRUCTOR.  */
+    return;
+
+  /* The sub-aggregate initializer might contain a placeholder;
+     update object to refer to the subobject and ctor to refer to
+     the (newly created) sub-initializer.  */
+  if (ctx->object)
+    new_ctx.object = build_ctor_subob_ref (index, type, ctx->object);
+  tree elt = build_constructor (type, NULL);
+  CONSTRUCTOR_NO_IMPLICIT_ZERO (elt) = true;
+  new_ctx.ctor = elt;
+
+  if (TREE_CODE (value) == TARGET_EXPR)
+    /* Avoid creating another CONSTRUCTOR when we expand the TARGET_EXPR.  */
+    value = TARGET_EXPR_INITIAL (value);
+}
+
+/* We're about to process an initializer for a class or array TYPE.  Make
+   sure that CTX is set up appropriately.  */
+
+static void
+verify_ctor_sanity (const constexpr_ctx *ctx, tree type)
+{
+  /* We don't bother building a ctor for an empty base subobject.  */
+  if (is_empty_class (type))
+    return;
+
+  /* We're in the middle of an initializer that might involve placeholders;
+     our caller should have created a CONSTRUCTOR for us to put the
+     initializer into.  We will either return that constructor or T.  */
+  gcc_assert (ctx->ctor);
+  gcc_assert (same_type_ignoring_top_level_qualifiers_p
+	      (type, TREE_TYPE (ctx->ctor)));
+  gcc_assert (CONSTRUCTOR_NELTS (ctx->ctor) == 0);
+  if (ctx->object)
+    gcc_assert (same_type_ignoring_top_level_qualifiers_p
+		(type, TREE_TYPE (ctx->object)));
+  gcc_assert (!ctx->object || !DECL_P (ctx->object)
+	      || *(ctx->values->get (ctx->object)) == ctx->ctor);
+}
+
 /* Subroutine of cxx_eval_constant_expression.
    The expression tree T denotes a C-style array or a C-style
    aggregate.  Reduce it to a constant expression.  */
 
 static tree
-cxx_eval_bare_aggregate (const constexpr_call *call, tree t,
+cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
 			 bool allow_non_constant, bool addr,
 			 bool *non_constant_p, bool *overflow_p)
 {
   vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t);
-  vec<constructor_elt, va_gc> *n;
-  vec_alloc (n, vec_safe_length (v));
-  constructor_elt *ce;
-  HOST_WIDE_INT i;
   bool changed = false;
   gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P (t));
-  for (i = 0; vec_safe_iterate (v, i, &ce); ++i)
+
+  verify_ctor_sanity (ctx, TREE_TYPE (t));
+  vec<constructor_elt, va_gc> **p = &CONSTRUCTOR_ELTS (ctx->ctor);
+  vec_alloc (*p, vec_safe_length (v));
+
+  unsigned i; tree index, value;
+  FOR_EACH_CONSTRUCTOR_ELT (v, i, index, value)
     {
-      tree elt = cxx_eval_constant_expression (call, ce->value,
+      constexpr_ctx new_ctx;
+      init_subob_ctx (ctx, new_ctx, index, value);
+      if (new_ctx.ctor != ctx->ctor)
+	/* If we built a new CONSTRUCTOR, attach it now so that other
+	   initializers can refer to it.  */
+	CONSTRUCTOR_APPEND_ELT (*p, index, new_ctx.ctor);
+      tree elt = cxx_eval_constant_expression (&new_ctx, value,
 					       allow_non_constant, addr,
 					       non_constant_p, overflow_p);
       /* Don't VERIFY_CONSTANT here.  */
       if (allow_non_constant && *non_constant_p)
-	goto fail;
-      if (elt != ce->value)
+	break;
+      if (elt != value)
 	changed = true;
-      if (ce->index && TREE_CODE (ce->index) == COMPONENT_REF)
+      if (index && TREE_CODE (index) == COMPONENT_REF)
 	{
 	  /* This is an initialization of a vfield inside a base
 	     subaggregate that we already initialized; push this
 	     initialization into the previous initialization.  */
-	  constructor_elt *inner = base_field_constructor_elt (n, ce->index);
+	  constructor_elt *inner = base_field_constructor_elt (*p, index);
 	  inner->value = elt;
+	  changed = true;
 	}
-      else if (ce->index
-	       && (TREE_CODE (ce->index) == NOP_EXPR
-		   || TREE_CODE (ce->index) == POINTER_PLUS_EXPR))
+      else if (index
+	       && (TREE_CODE (index) == NOP_EXPR
+		   || TREE_CODE (index) == POINTER_PLUS_EXPR))
 	{
 	  /* This is an initializer for an empty base; now that we've
 	     checked that it's constant, we can ignore it.  */
-	  gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (ce->index))));
+	  gcc_assert (is_empty_class (TREE_TYPE (TREE_TYPE (index))));
+	  changed = true;
+	}
+      else if (new_ctx.ctor != ctx->ctor)
+	{
+	  /* We appended this element above; update the value.  */
+	  gcc_assert ((*p)->last().index == index);
+	  (*p)->last().value = elt;
 	}
       else
-	CONSTRUCTOR_APPEND_ELT (n, ce->index, elt);
+	CONSTRUCTOR_APPEND_ELT (*p, index, elt);
     }
   if (*non_constant_p || !changed)
-    {
-    fail:
-      vec_free (n);
-      return t;
-    }
-  t = build_constructor (TREE_TYPE (t), n);
-  TREE_CONSTANT (t) = true;
+    return t;
+  t = ctx->ctor;
+  /* We're done building this CONSTRUCTOR, so now we can interpret an
+     element without an explicit initializer as value-initialized.  */
+  CONSTRUCTOR_NO_IMPLICIT_ZERO (t) = false;
   if (TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
     t = fold (t);
   return t;
@@ -1745,14 +1872,15 @@ cxx_eval_bare_aggregate (const constexpr_call *call, tree t,
    for the copy/move constructor.  */
 
 static tree
-cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init,
+cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
 		     bool value_init, bool allow_non_constant, bool addr,
 		     bool *non_constant_p, bool *overflow_p)
 {
   tree elttype = TREE_TYPE (atype);
   int max = tree_to_shwi (array_type_nelts (atype));
-  vec<constructor_elt, va_gc> *n;
-  vec_alloc (n, max + 1);
+  verify_ctor_sanity (ctx, atype);
+  vec<constructor_elt, va_gc> **p = &CONSTRUCTOR_ELTS (ctx->ctor);
+  vec_alloc (*p, max + 1);
   bool pre_init = false;
   int i;
 
@@ -1766,8 +1894,6 @@ cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init,
   else if (value_init)
     {
       init = build_value_init (elttype, tf_warning_or_error);
-      init = cxx_eval_constant_expression
-	    (call, init, allow_non_constant, addr, non_constant_p, overflow_p);
       pre_init = true;
     }
   else if (!init)
@@ -1777,18 +1903,17 @@ cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init,
 					&argvec, elttype, LOOKUP_NORMAL,
 					tf_warning_or_error);
       release_tree_vector (argvec);
-      init = cxx_eval_constant_expression (call, init, allow_non_constant,
-					   addr, non_constant_p, overflow_p);
       pre_init = true;
     }
 
-  if (*non_constant_p && !allow_non_constant)
-    goto fail;
-
   for (i = 0; i <= max; ++i)
     {
       tree idx = build_int_cst (size_type_node, i);
       tree eltinit;
+      constexpr_ctx new_ctx;
+      init_subob_ctx (ctx, new_ctx, idx, pre_init ? init : elttype);
+      if (new_ctx.ctor != ctx->ctor)
+	CONSTRUCTOR_APPEND_ELT (*p, idx, new_ctx.ctor);
       if (TREE_CODE (elttype) == ARRAY_TYPE)
 	{
 	  /* A multidimensional array; recurse.  */
@@ -1797,7 +1922,7 @@ cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init,
 	  else
 	    eltinit = cp_build_array_ref (input_location, init, idx,
 					  tf_warning_or_error);
-	  eltinit = cxx_eval_vec_init_1 (call, elttype, eltinit, value_init,
+	  eltinit = cxx_eval_vec_init_1 (&new_ctx, elttype, eltinit, value_init,
 					 allow_non_constant, addr,
 					 non_constant_p, overflow_p);
 	}
@@ -1805,10 +1930,9 @@ cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init,
 	{
 	  /* Initializing an element using value or default initialization
 	     we just pre-built above.  */
-	  if (i == 0)
-	    eltinit = init;
-	  else
-	    eltinit = unshare_expr (init);
+	  eltinit = (cxx_eval_constant_expression
+		     (&new_ctx, init, allow_non_constant,
+		      addr, non_constant_p, overflow_p));
 	}
       else
 	{
@@ -1820,34 +1944,38 @@ cxx_eval_vec_init_1 (const constexpr_call *call, tree atype, tree init,
 	  if (!real_lvalue_p (init))
 	    eltinit = move (eltinit);
 	  eltinit = force_rvalue (eltinit, tf_warning_or_error);
-	  eltinit = cxx_eval_constant_expression
-	    (call, eltinit, allow_non_constant, addr, non_constant_p, overflow_p);
+	  eltinit = (cxx_eval_constant_expression
+		     (&new_ctx, eltinit, allow_non_constant, addr,
+		      non_constant_p, overflow_p));
 	}
       if (*non_constant_p && !allow_non_constant)
-	goto fail;
-      CONSTRUCTOR_APPEND_ELT (n, idx, eltinit);
+	break;
+      if (new_ctx.ctor != ctx->ctor)
+	{
+	  /* We appended this element above; update the value.  */
+	  gcc_assert ((*p)->last().index == idx);
+	  (*p)->last().value = eltinit;
+	}
+      else
+	CONSTRUCTOR_APPEND_ELT (*p, idx, eltinit);
     }
 
   if (!*non_constant_p)
     {
-      init = build_constructor (atype, n);
-      TREE_CONSTANT (init) = true;
-      return init;
+      init = ctx->ctor;
+      CONSTRUCTOR_NO_IMPLICIT_ZERO (init) = false;
     }
-
- fail:
-  vec_free (n);
   return init;
 }
 
 static tree
-cxx_eval_vec_init (const constexpr_call *call, tree t,
+cxx_eval_vec_init (const constexpr_ctx *ctx, tree t,
 		   bool allow_non_constant, bool addr,
 		   bool *non_constant_p, bool *overflow_p)
 {
   tree atype = TREE_TYPE (t);
   tree init = VEC_INIT_EXPR_INIT (t);
-  tree r = cxx_eval_vec_init_1 (call, atype, init,
+  tree r = cxx_eval_vec_init_1 (ctx, atype, init,
 				VEC_INIT_EXPR_VALUE_INIT (t),
 				allow_non_constant, addr, non_constant_p, overflow_p);
   if (*non_constant_p)
@@ -2048,12 +2176,12 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base)
 }
 
 static tree
-cxx_eval_indirect_ref (const constexpr_call *call, tree t,
+cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 		       bool allow_non_constant, bool addr,
 		       bool *non_constant_p, bool *overflow_p)
 {
   tree orig_op0 = TREE_OPERAND (t, 0);
-  tree op0 = cxx_eval_constant_expression (call, orig_op0, allow_non_constant,
+  tree op0 = cxx_eval_constant_expression (ctx, orig_op0, allow_non_constant,
 					   /*addr*/false, non_constant_p, overflow_p);
   bool empty_base = false;
   tree r;
@@ -2066,7 +2194,7 @@ cxx_eval_indirect_ref (const constexpr_call *call, tree t,
 			     &empty_base);
 
   if (r)
-    r = cxx_eval_constant_expression (call, r, allow_non_constant,
+    r = cxx_eval_constant_expression (ctx, r, allow_non_constant,
 				      addr, non_constant_p, overflow_p);
   else
     {
@@ -2157,7 +2285,7 @@ non_const_var_error (tree r)
    Like cxx_eval_unary_expression, except for trinary expressions.  */
 
 static tree
-cxx_eval_trinary_expression (const constexpr_call *call, tree t,
+cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
 			     bool allow_non_constant, bool addr,
 			     bool *non_constant_p, bool *overflow_p)
 {
@@ -2167,7 +2295,7 @@ cxx_eval_trinary_expression (const constexpr_call *call, tree t,
 
   for (i = 0; i < 3; i++)
     {
-      args[i] = cxx_eval_constant_expression (call, TREE_OPERAND (t, i),
+      args[i] = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, i),
 					      allow_non_constant, addr,
 					      non_constant_p, overflow_p);
       VERIFY_CONSTANT (args[i]);
@@ -2194,10 +2322,11 @@ var_in_constexpr_fn (tree t)
 /* FIXME unify with c_fully_fold */
 
 static tree
-cxx_eval_constant_expression (const constexpr_call *call, tree t,
+cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 			      bool allow_non_constant, bool addr,
 			      bool *non_constant_p, bool *overflow_p)
 {
+  constexpr_ctx new_ctx;
   tree r = t;
 
   if (t == error_mark_node)
@@ -2228,11 +2357,14 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
       if (TREE_CODE (r) == TARGET_EXPR
 	  && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR)
 	r = TARGET_EXPR_INITIAL (r);
-      if (DECL_P (r) && var_in_constexpr_fn (r)
+      if (TREE_CODE (r) == VAR_DECL && var_in_constexpr_fn (r)
 	  && DECL_INITIAL (r))
-	r = cxx_eval_constant_expression (call, DECL_INITIAL (r),
+	r = cxx_eval_constant_expression (ctx, DECL_INITIAL (r),
 					  allow_non_constant, false,
 					  non_constant_p, overflow_p);
+      if (TREE_CODE (r) == VAR_DECL)
+	if (tree *p = ctx->values->get (r))
+	  r = *p;
       if (DECL_P (r))
 	{
 	  if (!allow_non_constant)
@@ -2247,18 +2379,8 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
       return t;
 
     case PARM_DECL:
-      if (call && DECL_CONTEXT (t) == call->fundef->decl)
-	{
-	  if (DECL_ARTIFICIAL (t) && DECL_CONSTRUCTOR_P (DECL_CONTEXT (t)))
-	    {
-	      if (!allow_non_constant)
-		sorry ("use of the value of the object being constructed "
-		       "in a constant expression");
-	      *non_constant_p = true;
-	    }
-	  else
-	    r = lookup_parameter_binding (call, t);
-	}
+      if (ctx && ctx->call && DECL_CONTEXT (t) == ctx->call->fundef->decl)
+	r = lookup_parameter_binding (ctx->call, t);
       else if (addr)
 	/* Defer in case this is only used for its type.  */;
       else
@@ -2271,7 +2393,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
 
     case CALL_EXPR:
     case AGGR_INIT_EXPR:
-      r = cxx_eval_call_expression (call, t, allow_non_constant, addr,
+      r = cxx_eval_call_expression (ctx, t, allow_non_constant, addr,
 				    non_constant_p, overflow_p);
       break;
 
@@ -2287,11 +2409,23 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
 	  *non_constant_p = true;
 	  break;
 	}
+      if ((AGGREGATE_TYPE_P (TREE_TYPE (t)) || VECTOR_TYPE_P (TREE_TYPE (t))))
+	{
+	  /* We're being expanded without an explicit target, so start
+	     initializing a new object; expansion with an explicit target
+	     strips the TARGET_EXPR before we get here.  */
+	  new_ctx = *ctx;
+	  new_ctx.ctor = build_constructor (TREE_TYPE (t), NULL);
+	  CONSTRUCTOR_NO_IMPLICIT_ZERO (new_ctx.ctor) = true;
+	  new_ctx.object = TARGET_EXPR_SLOT (t);
+	  ctx->values->put (new_ctx.object, new_ctx.ctor);
+	  ctx = &new_ctx;
+	}
       /* else fall through.  */
     case INIT_EXPR:
       /* Pass false for 'addr' because these codes indicate
 	 initialization of a temporary.  */
-      r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1),
+      r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 					allow_non_constant, false,
 					non_constant_p, overflow_p);
       if (!*non_constant_p)
@@ -2300,7 +2434,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
       break;
 
     case SCOPE_REF:
-      r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 1),
+      r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
 					allow_non_constant, addr,
 					non_constant_p, overflow_p);
       break;
@@ -2311,7 +2445,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
     case CLEANUP_POINT_EXPR:
     case MUST_NOT_THROW_EXPR:
     case SAVE_EXPR:
-      r = cxx_eval_constant_expression (call, TREE_OPERAND (t, 0),
+      r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					allow_non_constant, addr,
 					non_constant_p, overflow_p);
       break;
@@ -2320,14 +2454,14 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
 	 check for a constant operand or result; an address can be
 	 constant without its operand being, and vice versa.  */
     case INDIRECT_REF:
-      r = cxx_eval_indirect_ref (call, t, allow_non_constant, addr,
+      r = cxx_eval_indirect_ref (ctx, t, allow_non_constant, addr,
 				 non_constant_p, overflow_p);
       break;
 
     case ADDR_EXPR:
       {
 	tree oldop = TREE_OPERAND (t, 0);
-	tree op = cxx_eval_constant_expression (call, oldop,
+	tree op = cxx_eval_constant_expression (ctx, oldop,
 						allow_non_constant,
 						/*addr*/true,
 						non_constant_p, overflow_p);
@@ -2351,7 +2485,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
     case BIT_NOT_EXPR:
     case TRUTH_NOT_EXPR:
     case FIXED_CONVERT_EXPR:
-      r = cxx_eval_unary_expression (call, t, allow_non_constant, addr,
+      r = cxx_eval_unary_expression (ctx, t, allow_non_constant, addr,
 				     non_constant_p, overflow_p);
       break;
 
@@ -2380,15 +2514,15 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
 	STRIP_NOPS (op1);
 	if ((TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0))
 	    || TREE_CODE (op1) == EMPTY_CLASS_EXPR)
-	  r = cxx_eval_constant_expression (call, op0, allow_non_constant,
+	  r = cxx_eval_constant_expression (ctx, op0, allow_non_constant,
 					    addr, non_constant_p, overflow_p);
 	else
 	  {
 	    /* Check that the LHS is constant and then discard it.  */
-	    cxx_eval_constant_expression (call, op0, allow_non_constant,
+	    cxx_eval_constant_expression (ctx, op0, allow_non_constant,
 					  false, non_constant_p, overflow_p);
 	    op1 = TREE_OPERAND (t, 1);
-	    r = cxx_eval_constant_expression (call, op1, allow_non_constant,
+	    r = cxx_eval_constant_expression (ctx, op1, allow_non_constant,
 					      addr, non_constant_p, overflow_p);
 	  }
       }
@@ -2433,7 +2567,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
     case LTGT_EXPR:
     case RANGE_EXPR:
     case COMPLEX_EXPR:
-      r = cxx_eval_binary_expression (call, t, allow_non_constant, addr,
+      r = cxx_eval_binary_expression (ctx, t, allow_non_constant, addr,
 				      non_constant_p, overflow_p);
       break;
 
@@ -2441,7 +2575,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
 	 short-circuiting.  */
     case TRUTH_AND_EXPR:
     case TRUTH_ANDIF_EXPR:
-      r = cxx_eval_logical_expression (call, t, boolean_false_node,
+      r = cxx_eval_logical_expression (ctx, t, boolean_false_node,
 				       boolean_true_node,
 				       allow_non_constant, addr,
 				       non_constant_p, overflow_p);
@@ -2449,14 +2583,14 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
 
     case TRUTH_OR_EXPR:
     case TRUTH_ORIF_EXPR:
-      r = cxx_eval_logical_expression (call, t, boolean_true_node,
+      r = cxx_eval_logical_expression (ctx, t, boolean_true_node,
 				       boolean_false_node,
 				       allow_non_constant, addr,
 				       non_constant_p, overflow_p);
       break;
 
     case ARRAY_REF:
-      r = cxx_eval_array_reference (call, t, allow_non_constant, addr,
+      r = cxx_eval_array_reference (ctx, t, allow_non_constant, addr,
 				    non_constant_p, overflow_p);
       break;
 
@@ -2471,23 +2605,23 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
 	  *non_constant_p = true;
 	  return t;
 	}
-      r = cxx_eval_component_reference (call, t, allow_non_constant, addr,
+      r = cxx_eval_component_reference (ctx, t, allow_non_constant, addr,
 					non_constant_p, overflow_p);
       break;
 
     case BIT_FIELD_REF:
-      r = cxx_eval_bit_field_ref (call, t, allow_non_constant, addr,
+      r = cxx_eval_bit_field_ref (ctx, t, allow_non_constant, addr,
 				  non_constant_p, overflow_p);
       break;
 
     case COND_EXPR:
     case VEC_COND_EXPR:
-      r = cxx_eval_conditional_expression (call, t, allow_non_constant, addr,
+      r = cxx_eval_conditional_expression (ctx, t, allow_non_constant, addr,
 					   non_constant_p, overflow_p);
       break;
 
     case CONSTRUCTOR:
-      r = cxx_eval_bare_aggregate (call, t, allow_non_constant, addr,
+      r = cxx_eval_bare_aggregate (ctx, t, allow_non_constant, addr,
 				   non_constant_p, overflow_p);
       break;
 
@@ -2497,13 +2631,13 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
 	 be NULL, meaning default-initialization, or it will be an lvalue
 	 or xvalue of the same type, meaning direct-initialization from the
 	 corresponding member.  */
-      r = cxx_eval_vec_init (call, t, allow_non_constant, addr,
+      r = cxx_eval_vec_init (ctx, t, allow_non_constant, addr,
 			     non_constant_p, overflow_p);
       break;
 
     case FMA_EXPR:
     case VEC_PERM_EXPR:
-      r = cxx_eval_trinary_expression (call, t, allow_non_constant, addr,
+      r = cxx_eval_trinary_expression (ctx, t, allow_non_constant, addr,
 				       non_constant_p, overflow_p);
       break;
 
@@ -2512,7 +2646,7 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
     case NOP_EXPR:
       {
 	tree oldop = TREE_OPERAND (t, 0);
-	tree op = cxx_eval_constant_expression (call, oldop,
+	tree op = cxx_eval_constant_expression (ctx, oldop,
 						allow_non_constant, addr,
 						non_constant_p, overflow_p);
 	if (*non_constant_p)
@@ -2573,6 +2707,29 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
       *non_constant_p = true;
       break;
 
+    case PLACEHOLDER_EXPR:
+      if (!ctx || !ctx->ctor || (addr && !ctx->object))
+	{
+	  /* A placeholder without a referent.  We can get here when
+	     checking whether NSDMIs are noexcept, or in massage_init_elt;
+	     just say it's non-constant for now.  */
+	  gcc_assert (allow_non_constant);
+	  *non_constant_p = true;
+	  break;
+	}
+      else
+	{
+	  /* Use of the value or address of the current object.  We could
+	     use ctx->object unconditionally, but using ctx->ctor when we
+	     can is a minor optimization.  */
+	  tree ctor = addr ? ctx->object : ctx->ctor;
+	  gcc_assert (same_type_ignoring_top_level_qualifiers_p
+		      (TREE_TYPE (t), TREE_TYPE (ctor)));
+	  return cxx_eval_constant_expression
+	    (ctx, ctor, allow_non_constant, addr, non_constant_p, overflow_p);
+	}
+      break;
+
     default:
       internal_error ("unexpected expression %qE of kind %s", t,
 		      get_tree_code_name (TREE_CODE (t)));
@@ -2590,12 +2747,43 @@ cxx_eval_constant_expression (const constexpr_call *call, tree t,
 }
 
 static tree
-cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant)
+cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
+				  tree object = NULL_TREE)
 {
   bool non_constant_p = false;
   bool overflow_p = false;
-  tree r = cxx_eval_constant_expression (NULL, t, allow_non_constant,
-					 false, &non_constant_p, &overflow_p);
+  constexpr_ctx ctx = { NULL, NULL, NULL, NULL };
+  hash_map<tree,tree> map;
+  ctx.values = &map;
+  tree type = initialized_type (t);
+  if (!object && TREE_CODE (t) == TARGET_EXPR)
+    object = TARGET_EXPR_SLOT (t);
+  tree r = t;
+  if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
+    {
+      /* In C++14 an NSDMI can participate in aggregate initialization,
+	 and can refer to the address of the object being initialized, so
+	 we need to pass in the relevant VAR_DECL if we want to do the
+	 evaluation in a single pass.  The evaluation will dynamically
+	 update ctx.values for the VAR_DECL.  We use the same strategy
+	 for C++11 constexpr constructors that refer to the object being
+	 initialized.  */
+      ctx.ctor = build_constructor (type, NULL);
+      CONSTRUCTOR_NO_IMPLICIT_ZERO (ctx.ctor) = true;
+      ctx.object = object;
+      if (object)
+	gcc_assert (same_type_ignoring_top_level_qualifiers_p
+		    (type, TREE_TYPE (object)));
+      if (object && DECL_P (object))
+	map.put (object, ctx.ctor);
+      if (TREE_CODE (r) == TARGET_EXPR)
+	/* Avoid creating another CONSTRUCTOR when we expand the
+	   TARGET_EXPR.  */
+	r = TARGET_EXPR_INITIAL (r);
+    }
+
+  r = cxx_eval_constant_expression (&ctx, r, allow_non_constant,
+				    false, &non_constant_p, &overflow_p);
 
   verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
 
@@ -2607,7 +2795,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant)
 	 constexpr variables.  */
       if (!allow_non_constant)
 	error ("%qT cannot be the type of a complete constant expression "
-	       "because it has mutable sub-objects", TREE_TYPE (t));
+	       "because it has mutable sub-objects", type);
       non_constant_p = true;
     }
 
@@ -2668,7 +2856,17 @@ is_sub_constant_expr (tree t)
 {
   bool non_constant_p = false;
   bool overflow_p = false;
-  cxx_eval_constant_expression (NULL, t, true, false, &non_constant_p,
+  constexpr_ctx ctx = { NULL, NULL, NULL, NULL };
+  hash_map <tree, tree> map;
+  ctx.values = &map;
+  tree type = initialized_type (t);
+  if ((AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
+      && TREE_CODE (t) != TARGET_EXPR)
+    {
+      ctx.ctor = build_constructor (type, NULL);
+      CONSTRUCTOR_NO_IMPLICIT_ZERO (ctx.ctor) = true;
+    }
+  cxx_eval_constant_expression (&ctx, t, true, false, &non_constant_p,
 				&overflow_p);
   return !non_constant_p && !overflow_p;
 }
@@ -2678,9 +2876,9 @@ is_sub_constant_expr (tree t)
    return NULL.  */
 
 tree
-cxx_constant_value (tree t)
+cxx_constant_value (tree t, tree decl)
 {
-  return cxx_eval_outermost_constant_expr (t, false);
+  return cxx_eval_outermost_constant_expr (t, false, decl);
 }
 
 /* If T is a constant expression, returns its reduced value.
@@ -2688,7 +2886,7 @@ cxx_constant_value (tree t)
    Otherwise, returns a version of T without TREE_CONSTANT.  */
 
 tree
-maybe_constant_value (tree t)
+maybe_constant_value (tree t, tree decl)
 {
   tree r;
 
@@ -2705,7 +2903,7 @@ maybe_constant_value (tree t)
       return t;
     }
 
-  r = cxx_eval_outermost_constant_expr (t, true);
+  r = cxx_eval_outermost_constant_expr (t, true, decl);
 #ifdef ENABLE_CHECKING
   /* cp_tree_equal looks through NOPs, so allow them.  */
   gcc_assert (r == t
@@ -2720,14 +2918,14 @@ maybe_constant_value (tree t)
    than wrapped in a TARGET_EXPR.  */
 
 tree
-maybe_constant_init (tree t)
+maybe_constant_init (tree t, tree decl)
 {
   if (TREE_CODE (t) == EXPR_STMT)
     t = TREE_OPERAND (t, 0);
   if (TREE_CODE (t) == CONVERT_EXPR
       && VOID_TYPE_P (TREE_TYPE (t)))
     t = TREE_OPERAND (t, 0);
-  t = maybe_constant_value (t);
+  t = maybe_constant_value (t, decl);
   if (TREE_CODE (t) == TARGET_EXPR)
     {
       tree init = TARGET_EXPR_INITIAL (t);
@@ -2822,6 +3020,7 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
     case FIELD_DECL:
     case PARM_DECL:
     case USING_DECL:
+    case PLACEHOLDER_EXPR:
       return true;
 
     case AGGR_INIT_EXPR:
@@ -2861,18 +3060,7 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
 		  {
 		    tree x = get_nth_callarg (t, 0);
 		    if (is_this_parameter (x))
-		      {
-			if (DECL_CONTEXT (x) == NULL_TREE
-			    || DECL_CONSTRUCTOR_P (DECL_CONTEXT (x)))
-			  {
-			    if (flags & tf_error)
-			      sorry ("calling a member function of the "
-				     "object being constructed in a constant "
-				     "expression");
-			    return false;
-			  }
-			/* Otherwise OK.  */;
-		      }
+		      return true;
 		    else if (!potential_constant_expression_1 (x, rval, flags))
 		      return false;
 		    i = 1;
@@ -3002,14 +3190,6 @@ potential_constant_expression_1 (tree t, bool want_rval, tsubst_flags_t flags)
 		  error ("use of %<this%> in a constant expression");
 		return false;
 	      }
-	    if (want_rval && DECL_CONTEXT (x)
-		&& DECL_CONSTRUCTOR_P (DECL_CONTEXT (x)))
-	      {
-		if (flags & tf_error)
-		  sorry ("use of the value of the object being constructed "
-			 "in a constant expression");
-		return false;
-	      }
 	    return true;
 	  }
 	return potential_constant_expression_1 (x, rval, flags);
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 22703b7..556e1e6 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -488,6 +488,10 @@ cp_gimplify_init_expr (tree *expr_p)
 	    TREE_TYPE (from) = void_type_node;
 	}
 
+      if (cxx_dialect >= cxx14 && TREE_CODE (sub) == CONSTRUCTOR)
+	/* Handle aggregate NSDMI.  */
+	replace_placeholders (sub, to);
+
       if (t == sub)
 	break;
       else
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0923d9f..abc3d6f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -98,6 +98,7 @@ c-common.h, not after.
       DECL_FINAL_P (in FUNCTION_DECL)
       QUALIFIED_NAME_IS_TEMPLATE (in SCOPE_REF)
       DECLTYPE_FOR_INIT_CAPTURE (in DECLTYPE_TYPE)
+      CONSTRUCTOR_NO_IMPLICIT_ZERO (in CONSTRUCTOR)
    2: IDENTIFIER_OPNAME_P (in IDENTIFIER_NODE)
       ICS_THIS_FLAG (in _CONV)
       DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL)
@@ -3479,6 +3480,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
    B b{1,2}, not B b({1,2}) or B b = {1,2}.  */
 #define CONSTRUCTOR_IS_DIRECT_INIT(NODE) (TREE_LANG_FLAG_0 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if an uninitialized element in NODE should not be treated as
+   implicitly value-initialized.  Only used in constexpr evaluation.  */
+#define CONSTRUCTOR_NO_IMPLICIT_ZERO(NODE) \
+  (TREE_LANG_FLAG_1 (CONSTRUCTOR_CHECK (NODE)))
+
 #define DIRECT_LIST_INIT_P(NODE) \
    (BRACE_ENCLOSED_INITIALIZER_P (NODE) && CONSTRUCTOR_IS_DIRECT_INIT (NODE))
 
@@ -6033,6 +6039,8 @@ extern tree bind_template_template_parm		(tree, tree);
 extern tree array_type_nelts_total		(tree);
 extern tree array_type_nelts_top		(tree);
 extern tree break_out_target_exprs		(tree);
+extern tree build_ctor_subob_ref		(tree, tree, tree);
+extern tree replace_placeholders		(tree, tree);
 extern tree get_type_decl			(tree);
 extern tree decl_namespace_context		(tree);
 extern bool decl_anon_ns_mem_p			(const_tree);
@@ -6320,9 +6328,9 @@ extern bool potential_constant_expression       (tree);
 extern bool potential_rvalue_constant_expression (tree);
 extern bool require_potential_constant_expression (tree);
 extern bool require_potential_rvalue_constant_expression (tree);
-extern tree cxx_constant_value                  (tree);
-extern tree maybe_constant_value                (tree);
-extern tree maybe_constant_init                 (tree);
+extern tree cxx_constant_value			(tree, tree = NULL_TREE);
+extern tree maybe_constant_value		(tree, tree = NULL_TREE);
+extern tree maybe_constant_init			(tree, tree = NULL_TREE);
 extern bool is_sub_constant_expr                (tree);
 extern bool reduced_constant_expression_p       (tree);
 extern bool is_instantiation_of_constexpr       (tree);
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 57f3c31..4c85ddb 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -2647,6 +2647,10 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
       pp_cxx_right_paren (pp);
       break;
 
+    case PLACEHOLDER_EXPR:
+      pp_string (pp, M_("*this"));
+      break;
+
       /*  This list is incomplete, but should suffice for now.
 	  It is very important that `sorry' does not call
 	  `report_error_function'.  That could cause an infinite loop.  */
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 6851fe9..5c61107 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -540,7 +540,12 @@ get_nsdmi (tree member, bool in_ctor)
   tree save_ccp = current_class_ptr;
   tree save_ccr = current_class_ref;
   if (!in_ctor)
-    inject_this_parameter (DECL_CONTEXT (member), TYPE_UNQUALIFIED);
+    {
+      /* Use a PLACEHOLDER_EXPR when we don't have a 'this' parameter to
+	 refer to; constexpr evaluation knows what to do with it.  */
+      current_class_ref = build0 (PLACEHOLDER_EXPR, DECL_CONTEXT (member));
+      current_class_ptr = build_address (current_class_ref);
+    }
   if (DECL_LANG_SPECIFIC (member) && DECL_TEMPLATE_INFO (member))
     {
       /* Do deferred instantiation of the NSDMI.  */
@@ -560,7 +565,7 @@ get_nsdmi (tree member, bool in_ctor)
 	  error ("constructor required before non-static data member "
 		 "for %qD has been parsed", member);
 	  DECL_INITIAL (member) = error_mark_node;
-	  init = NULL_TREE;
+	  init = error_mark_node;
 	}
       /* Strip redundant TARGET_EXPR so we don't need to remap it, and
 	 so the aggregate init code below will see a CONSTRUCTOR.  */
@@ -1723,7 +1728,7 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
       tree fn = get_callee_fndecl (rval);
       if (fn && DECL_DECLARED_CONSTEXPR_P (fn))
 	{
-	  tree e = maybe_constant_init (rval);
+	  tree e = maybe_constant_init (rval, exp);
 	  if (TREE_CONSTANT (e))
 	    rval = build2 (INIT_EXPR, type, exp, e);
 	}
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 9008cdb..ef7f675 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -158,6 +158,7 @@ lvalue_kind (const_tree ref)
     case ARRAY_NOTATION_REF:
     case PARM_DECL:
     case RESULT_DECL:
+    case PLACEHOLDER_EXPR:
       return clk_ordinary;
 
       /* A scope ref in a template, left as SCOPE_REF to support later
@@ -2450,6 +2451,103 @@ break_out_target_exprs (tree t)
   return t;
 }
 
+/* Build an expression for the subobject of OBJ at CONSTRUCTOR index INDEX,
+   which we expect to have type TYPE.  */
+
+tree
+build_ctor_subob_ref (tree index, tree type, tree obj)
+{
+  if (index == NULL_TREE)
+    /* Can't refer to a particular member of a vector.  */
+    obj = NULL_TREE;
+  else if (TREE_CODE (index) == INTEGER_CST)
+    obj = cp_build_array_ref (input_location, obj, index, tf_none);
+  else
+    obj = build_class_member_access_expr (obj, index, NULL_TREE,
+					  /*reference*/false, tf_none);
+  if (obj)
+    gcc_assert (same_type_ignoring_top_level_qualifiers_p (type,
+							   TREE_TYPE (obj)));
+  return obj;
+}
+
+/* Like substitute_placeholder_in_expr, but handle C++ tree codes and
+   build up subexpressions as we go deeper.  */
+
+struct replace_placeholders_t
+{
+  tree obj;
+  hash_set<tree> *pset;
+};
+
+static tree
+replace_placeholders_r (tree* t, int* walk_subtrees, void* data_)
+{
+  tree obj = static_cast<tree>(data_);
+
+  if (TREE_CONSTANT (*t))
+    {
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
+  switch (TREE_CODE (*t))
+    {
+    case PLACEHOLDER_EXPR:
+      gcc_assert (same_type_ignoring_top_level_qualifiers_p
+		  (TREE_TYPE (*t), TREE_TYPE (obj)));
+      *t = obj;
+      *walk_subtrees = false;
+      break;
+
+    case TARGET_EXPR:
+      /* Don't mess with placeholders in an unrelated object.  */
+      *walk_subtrees = false;
+      break;
+
+    case CONSTRUCTOR:
+      {
+	constructor_elt *ce;
+	vec<constructor_elt,va_gc> *v = CONSTRUCTOR_ELTS (*t);
+	for (unsigned i = 0; vec_safe_iterate (v, i, &ce); ++i)
+	  {
+	    tree *valp = &ce->value;
+	    tree type = TREE_TYPE (*valp);
+	    tree subob = obj;
+
+	    if (TREE_CODE (*valp) == CONSTRUCTOR
+		&& AGGREGATE_TYPE_P (type))
+	      {
+		subob = build_ctor_subob_ref (ce->index, type, obj);
+		if (TREE_CODE (*valp) == TARGET_EXPR)
+		  valp = &TARGET_EXPR_INITIAL (*valp);
+	      }
+
+	    cp_walk_tree (valp, replace_placeholders_r,
+			  subob, NULL);
+	  }
+	*walk_subtrees = false;
+	break;
+      }
+
+    default:
+      break;
+    }
+
+  return NULL_TREE;
+}
+
+tree
+replace_placeholders (tree exp, tree obj)
+{
+  hash_set<tree> pset;
+  tree *tp = &exp;
+  if (TREE_CODE (exp) == TARGET_EXPR)
+    tp = &TARGET_EXPR_INITIAL (exp);
+  cp_walk_tree (tp, replace_placeholders_r, obj, NULL);
+  return exp;
+}
+
 /* Similar to `build_nt', but for template definitions of dependent
    expressions  */
 
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index ad69668..d57f75d 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -806,15 +806,19 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
 	      && !require_potential_constant_expression (value))
 	    value = error_mark_node;
 	  else
-	    value = cxx_constant_value (value);
+	    value = cxx_constant_value (value, decl);
 	}
-      value = maybe_constant_init (value);
+      value = maybe_constant_init (value, decl);
       const_init = (reduced_constant_expression_p (value)
 		    || error_operand_p (value));
       DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = const_init;
       TREE_CONSTANT (decl) = const_init && decl_maybe_constant_var_p (decl);
     }
 
+  if (cxx_dialect >= cxx14)
+    /* Handle aggregate NSDMI in non-constant initializers, too.  */
+    value = replace_placeholders (value, decl);
+
   /* If the initializer is not a constant, fill in DECL_INITIAL with
      the bits that are constant, and then return an expression that
      will perform the dynamic initialization.  */
@@ -1292,9 +1296,8 @@ process_init_constructor_record (tree type, tree init,
 				 tsubst_flags_t complain)
 {
   vec<constructor_elt, va_gc> *v = NULL;
-  int flags = 0;
   tree field;
-  unsigned HOST_WIDE_INT idx = 0;
+  int skipped = 0;
 
   gcc_assert (TREE_CODE (type) == RECORD_TYPE);
   gcc_assert (!CLASSTYPE_VBASECLASSES (type));
@@ -1302,6 +1305,9 @@ process_init_constructor_record (tree type, tree init,
 	      || !BINFO_N_BASE_BINFOS (TYPE_BINFO (type)));
   gcc_assert (!TYPE_POLYMORPHIC_P (type));
 
+ restart:
+  int flags = 0;
+  unsigned HOST_WIDE_INT idx = 0;
   /* Generally, we will always have an index for each initializer (which is
      a FIELD_DECL, put by reshape_init), but compound literals don't go trough
      reshape_init. So we need to handle both cases.  */
@@ -1345,6 +1351,19 @@ process_init_constructor_record (tree type, tree init,
 	  next = massage_init_elt (type, ce->value, complain);
 	  ++idx;
 	}
+      else if (DECL_INITIAL (field))
+	{
+	  if (skipped > 0)
+	    {
+	      /* We're using an NSDMI past a field with implicit
+	         zero-init.  Go back and make it explicit.  */
+	      skipped = -1;
+	      vec_safe_truncate (v, 0);
+	      goto restart;
+	    }
+	  /* C++14 aggregate NSDMI.  */
+	  next = get_nsdmi (field, /*ctor*/false);
+	}
       else if (type_build_ctor_call (TREE_TYPE (field)))
 	{
 	  /* If this type needs constructors run for
@@ -1387,13 +1406,17 @@ process_init_constructor_record (tree type, tree init,
 	    warning (OPT_Wmissing_field_initializers,
 		     "missing initializer for member %qD", field);
 
-	  if (!zero_init_p (TREE_TYPE (field)))
+	  if (!zero_init_p (TREE_TYPE (field))
+	      || skipped < 0)
 	    next = build_zero_init (TREE_TYPE (field), /*nelts=*/NULL_TREE,
 				    /*static_storage_p=*/false);
 	  else
-	    /* The default zero-initialization is fine for us; don't
-	    add anything to the CONSTRUCTOR.  */
-	    continue;
+	    {
+	      /* The default zero-initialization is fine for us; don't
+		 add anything to the CONSTRUCTOR.  */
+	      skipped = 1;
+	      continue;
+	    }
 	}
 
       /* If this is a bitfield, now convert to the lowered type.  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C
index 88d4426..31010ed 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C
@@ -10,18 +10,18 @@
 // R() is well-formed because i is initialized before j.
 
 struct s {
-  constexpr s() : v(v) { }	// { dg-message "" }
+  constexpr s() : v(v) { }
   int v;
 };
 
-constexpr s bang;		// { dg-message "" }
+constexpr s bang;		// { dg-error "" }
 
 struct R {
   int i,j;
-  constexpr R() : i(42),j(i) { } // { dg-bogus "" "" { xfail *-*-* } }
+  constexpr R() : i(42),j(i) { } // { dg-bogus "" }
 };
 
-constexpr R r;			// { dg-bogus "" "" { xfail *-*-* } }
+constexpr R r;			// { dg-bogus "" }
 
 // Ill-formed (no diagnostic required)
 struct T {
@@ -41,10 +41,10 @@ struct U {
   constexpr int f(int _i) { return _i; }
   constexpr int g() { return i; }
   constexpr U(): i(0), j(0) { }
-  constexpr U(const U& t) : i(f(t.i)),j(0) { } // { dg-bogus "" "" { xfail *-*-* } }
-  constexpr U(int _i) : i(_i),j(g()) { } // { dg-bogus "" "" { xfail *-*-* } }
+  constexpr U(const U& t) : i(f(t.i)),j(0) { } // { dg-bogus "" }
+  constexpr U(int _i) : i(_i),j(g()) { } // { dg-bogus "" }
 };
 
 constexpr U u1;
-constexpr U u2(u1);		// { dg-bogus "" "" { xfail *-*-* } }
-constexpr U u3(1);		// { dg-bogus "" "" { xfail *-*-* } }
+constexpr U u2(u1);		// { dg-bogus "" }
+constexpr U u3(1);		// { dg-bogus "" }
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14.C
new file mode 100644
index 0000000..1730995
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14.C
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++11 } }
+
+struct A
+{
+  void *p;
+  constexpr A(): p(this) {}
+};
+
+constexpr A a;
+constexpr A b = A();
+
+#define SA(X) static_assert ((X), #X)
+SA(a.p == &a);
+SA(b.p == &b);
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C
new file mode 100644
index 0000000..644ae63
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-fno-elide-constructors" }
+
+struct A
+{
+  void *p;
+  constexpr A(): p(this) {}
+};
+
+constexpr A a;
+constexpr A b = A();		// { dg-error "" }
+
+#define SA(X) static_assert ((X), #X)
+SA(a.p == &a);
diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi7.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi7.C
index 4b08474..e378360 100644
--- a/gcc/testsuite/g++.dg/cpp0x/nsdmi7.C
+++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi7.C
@@ -11,6 +11,7 @@ struct A
 
 struct B
 {
+  virtual void g();
   const int d;		// { dg-warning "non-static const member" }
   int &e;		// { dg-warning "non-static reference" }
   int f = 7;
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr1.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr1.C
new file mode 100644
index 0000000..97bcc05
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr1.C
@@ -0,0 +1,41 @@
+// { dg-do run { target c++14 } }
+
+struct S { int a; const char* b; int c; int d = b[a]; void *p = this+1; };
+constexpr S ss = S(S{ 1, "asdf" });
+
+#define SA(X) static_assert ((X),#X)
+
+SA(ss.a==1);
+SA(ss.b[0] == 'a' && ss.b[1] == 's' && ss.b[2] == 'd' && ss.b[3] == 'f');
+SA(ss.d == 's');
+SA(ss.p == &ss+1);
+
+struct A
+{
+  struct B {
+    int i;
+    int j = i+1;
+  } b;
+  int a = b.j+1;
+};
+
+extern constexpr A a = { };
+SA(a.b.i == 0 && a.b.j == 1 && a.a == 2);
+
+int f(const A& ar) { return ar.a; }
+
+int main()
+{
+  S ss2 = { 1, "asdf" };
+  if (ss2.a != 1
+      || __builtin_strcmp(ss2.b,"asdf") != 0
+      || ss2.c != int()
+      || ss2.d != 's'
+      || ss2.p != &ss2+1)
+    __builtin_abort();
+
+  A a = {};
+  int i = f(A{});
+  if (a.a != 2 || i != 2)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr2.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr2.C
new file mode 100644
index 0000000..83cb6c2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr2.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++14 } }
+
+struct S { int a; const char* b; int c; int d = b[a]; };
+
+constexpr int f(const S& s) { return s.a; }
+
+int main()
+{
+  constexpr int i = f(S{ 1, "asdf" });
+}
diff --git a/gcc/testsuite/lib/prune.exp b/gcc/testsuite/lib/prune.exp
index 679d894..859d25e 100644
--- a/gcc/testsuite/lib/prune.exp
+++ b/gcc/testsuite/lib/prune.exp
@@ -28,6 +28,7 @@ proc prune_gcc_output { text } {
     regsub -all "(^|\n)\[^\n\]*(: )?At (top level|global scope):\[^\n\]*" $text "" text
     regsub -all "(^|\n)\[^\n\]*:   (recursively )?required \[^\n\]*" $text "" text
     regsub -all "(^|\n)\[^\n\]*:   . skipping \[0-9\]* instantiation contexts \[^\n\]*" $text "" text
+    regsub -all "(^|\n)\[^\n\]*:   in constexpr expansion \[^\n\]*" $text "" text
     regsub -all "(^|\n)    inlined from \[^\n\]*" $text "" text
     regsub -all "(^|\n)collect2: error: ld returned \[^\n\]*" $text "" text
     regsub -all "(^|\n)collect: re(compiling|linking)\[^\n\]*" $text "" text

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]