Implement N3653 (Member initializers and aggregates) and fix references to 'this...
authorJason Merrill <jason@redhat.com>
Mon, 27 Oct 2014 17:42:12 +0000 (13:42 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Mon, 27 Oct 2014 17:42:12 +0000 (13:42 -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.

From-SVN: r216750

16 files changed:
gcc/cp/ChangeLog
gcc/cp/class.c
gcc/cp/constexpr.c
gcc/cp/cp-gimplify.c
gcc/cp/cp-tree.h
gcc/cp/error.c
gcc/cp/init.c
gcc/cp/tree.c
gcc/cp/typeck2.c
gcc/testsuite/g++.dg/cpp0x/constexpr-48089.C
gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/nsdmi7.C
gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr2.C [new file with mode: 0644]
gcc/testsuite/lib/prune.exp

index 39c03d67e54a9a1ababc650aac9f05b504a9fe2b..90e69a36bc47d536b00c8c468ec2a946c3b4fc20 100644 (file)
@@ -1,3 +1,34 @@
+2014-10-24  Jason Merrill  <jason@redhat.com>
+
+       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.
+
 2014-10-27  Andrew MacLeod  <amacleod@redhat.com>
 
        * cp-gimplify.c: Adjust include files.
index 99bfa957f6374e8ad8d557b1ff9c39271b877805..5f50aff2417fc2556e481327cf565a4f13e1b7b9 100644 (file)
@@ -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.  */
index 30a5e08b55b89dd645e1acabd2d57b77bb119926..cd10766aebd861fa96bf7cebbdcfd28720af30c2 100644 (file)
@@ -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);
index 52750727dc30bcbb4793e8ec36976e6453b67b3f..e5436bbc747fbdf7a4899ded8b9f4725ae46b048 100644 (file)
@@ -495,6 +495,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
index 0923d9f0f7cfc2ebc41efb8c92b9e9b929401729..abc3d6fd19ff2631c5344f3fb1ceeec197cfff2c 100644 (file)
@@ -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);
index 755bb00f71bdaecd818117f49043cf5109529f57..76f86cb0d74bc43d3e2b842ec1a170b0eb939eb0 100644 (file)
@@ -2673,6 +2673,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.  */
index 6851fe9dc7e7d1da79b05c68bfe16e9a6eb2e0ad..5c61107b7f4fa1c25c7b88af73b7d18da63ee3c9 100644 (file)
@@ -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);
        }
index 9008cdb4da9cf27122dec435787b82650072774a..ef7f6756ef6bf44836878242922ecc6b4cb64e15 100644 (file)
@@ -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  */
 
index ad69668765a60d5b74da880933e7a66e65ff902c..d57f75dadc59d529d8ac544987fd87396c4833d5 100644 (file)
@@ -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.  */
index 88d442691c6a413ad5079cddd26740e43c48708d..31010ed2355f5a5f686d25c4dbe1d4531803d990 100644 (file)
 // 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 (file)
index 0000000..1730995
--- /dev/null
@@ -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 (file)
index 0000000..644ae63
--- /dev/null
@@ -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);
index 4b08474aaf954b2eb772d9c36b3bd7d9115ad200..e378360a75912df44802254b096ae6a8c57ef0dc 100644 (file)
@@ -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 (file)
index 0000000..97bcc05
--- /dev/null
@@ -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 (file)
index 0000000..83cb6c2
--- /dev/null
@@ -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" });
+}
index 679d894e267e44f0c51997385379eedbef095564..859d25ef8015c4fca74be60d79e4dadab81b57e4 100644 (file)
@@ -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