2020-04-04 Patrick Palka <ppalka@redhat.com>
+ PR c++/94205
+ PR c++/79937
+ * constexpr.c (struct constexpr_ctx): New field 'parent'.
+ (cxx_eval_bare_aggregate): Propagate CONSTRUCTOR_PLACEHOLDER_BOUNDARY
+ flag from the original constructor to the reduced constructor.
+ (lookup_placeholder): Prefer to return the outermost matching object
+ by recursively calling lookup_placeholder on the 'parent' context,
+ but don't cross CONSTRUCTOR_PLACEHOLDER_BOUNDARY constructors.
+ (cxx_eval_constant_expression): Link the 'ctx' context to the 'new_ctx'
+ context via 'new_ctx.parent' when being expanded without an explicit
+ target. Don't call replace_placeholders.
+ (cxx_eval_outermost_constant_expr): Initialize 'ctx.parent' to NULL.
+
PR c++/94219
PR c++/94205
* constexpr.c (get_or_insert_ctor_field): Split out (while adding
tree object;
/* If inside SWITCH_EXPR. */
constexpr_switch_state *css_state;
+ /* The aggregate initialization context inside which this one is nested. This
+ is used by lookup_placeholder to resolve PLACEHOLDER_EXPRs. */
+ const constexpr_ctx *parent;
/* Whether we should error on a non-constant expression or fail quietly.
This flag needs to be here, but some of the others could move to global
vec<constructor_elt, va_gc> **p = &CONSTRUCTOR_ELTS (ctx->ctor);
vec_alloc (*p, vec_safe_length (v));
+ if (CONSTRUCTOR_PLACEHOLDER_BOUNDARY (t))
+ CONSTRUCTOR_PLACEHOLDER_BOUNDARY (ctx->ctor) = 1;
+
unsigned i;
tree index, value;
bool constant_p = true;
if (!ctx)
return NULL_TREE;
+ /* Prefer the outermost matching object, but don't cross
+ CONSTRUCTOR_PLACEHOLDER_BOUNDARY constructors. */
+ if (ctx->ctor && !CONSTRUCTOR_PLACEHOLDER_BOUNDARY (ctx->ctor))
+ if (tree outer_ob = lookup_placeholder (ctx->parent, lval, type))
+ return outer_ob;
+
/* We could use ctx->object unconditionally, but using ctx->ctor when we
can is a minor optimization. */
if (!lval && ctx->ctor && same_type_p (TREE_TYPE (ctx->ctor), type))
r = *p;
break;
}
- tree init = TARGET_EXPR_INITIAL (t);
if ((AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type)))
{
- if (ctx->object)
- /* If the initializer contains any PLACEHOLDER_EXPR, we need to
- resolve them before we create a new CONSTRUCTOR for the
- temporary. */
- init = replace_placeholders (init, ctx->object);
-
/* 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;
+ /* Link CTX to NEW_CTX so that lookup_placeholder can resolve
+ any PLACEHOLDER_EXPR within the initializer that refers to the
+ former object under construction. */
+ new_ctx.parent = ctx;
new_ctx.ctor = build_constructor (type, NULL);
CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true;
new_ctx.object = slot;
bool overflow_p = false;
constexpr_global_ctx global_ctx;
- constexpr_ctx ctx = { &global_ctx, NULL, NULL, NULL, NULL, NULL,
+ constexpr_ctx ctx = { &global_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
allow_non_constant, strict,
manifestly_const_eval || !allow_non_constant,
uid_sensitive };
--- /dev/null
+// PR c++/79937
+// This is a constexpr adaptation of pr79937-3.C and pr79937-4.C.
+// { dg-do compile { target c++14 } }
+
+struct X {
+ unsigned i;
+ unsigned n = i;
+};
+
+constexpr X bar(X x) {
+ return x;
+}
+
+struct Y
+{
+ static constexpr Y bar(Y y) { return y; }
+ unsigned i;
+ unsigned n = bar(Y{2,i}).n;
+};
+
+constexpr X x { 1, bar(X{2}).n };
+static_assert(x.n == 2, "");
+
+constexpr Y y { 1 };
+static_assert(y.n == 1, "");
+
+struct Z {
+ unsigned i;
+ unsigned n = i;
+ unsigned m = i;
+};
+
+constexpr Z
+baz (Z z)
+{
+ if (z.i != 1 || z.n != 2 || z.m != 1)
+ __builtin_abort ();
+ return z;
+}
+
+constexpr Z z = baz (Z {1, Z {2}.n});
+static_assert(z.i == 1 && z.n == 2 && z.m == 1, "");