Commit 49a86fce by Patrick Palka

c++: Refrain from using replace_placeholders in constexpr evaluation [PR94205]

This removes the use of replace_placeholders in cxx_eval_constant_expression
(which is causing the new test lambda-this6.C to ICE due to replace_placeholders
mutating the shared TARGET_EXPR_INITIAL tree which then trips up the
gimplifier).

In its place, this patch adds a 'parent' field to constexpr_ctx which is used to
store a pointer to an outer constexpr_ctx that refers to another object under
construction.  With this new field, we can beef up lookup_placeholder to resolve
PLACEHOLDER_EXPRs which refer to former objects under construction, which fixes
PR94205 without needing to do replace_placeholders.  Also we can now respect the
CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag when resolving PLACEHOLDER_EXPRs, and
doing so fixes the constexpr analogue of PR79937.

gcc/cp/ChangeLog:

	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.

gcc/testsuite/ChangeLog:

	PR c++/94205
	PR c++/79937
	* g++.dg/cpp1y/pr79937-5.C: New test.
	* g++.dg/cpp1z/lambda-this6.C: New test.
parent 37244b21
2020-04-04 Patrick Palka <ppalka@redhat.com> 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++/94219
PR c++/94205 PR c++/94205
* constexpr.c (get_or_insert_ctor_field): Split out (while adding * constexpr.c (get_or_insert_ctor_field): Split out (while adding
......
...@@ -1076,6 +1076,9 @@ struct constexpr_ctx { ...@@ -1076,6 +1076,9 @@ struct constexpr_ctx {
tree object; tree object;
/* If inside SWITCH_EXPR. */ /* If inside SWITCH_EXPR. */
constexpr_switch_state *css_state; 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. /* 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 This flag needs to be here, but some of the others could move to global
...@@ -3841,6 +3844,9 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, ...@@ -3841,6 +3844,9 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
vec<constructor_elt, va_gc> **p = &CONSTRUCTOR_ELTS (ctx->ctor); vec<constructor_elt, va_gc> **p = &CONSTRUCTOR_ELTS (ctx->ctor);
vec_alloc (*p, vec_safe_length (v)); vec_alloc (*p, vec_safe_length (v));
if (CONSTRUCTOR_PLACEHOLDER_BOUNDARY (t))
CONSTRUCTOR_PLACEHOLDER_BOUNDARY (ctx->ctor) = 1;
unsigned i; unsigned i;
tree index, value; tree index, value;
bool constant_p = true; bool constant_p = true;
...@@ -5303,6 +5309,12 @@ lookup_placeholder (const constexpr_ctx *ctx, bool lval, tree type) ...@@ -5303,6 +5309,12 @@ lookup_placeholder (const constexpr_ctx *ctx, bool lval, tree type)
if (!ctx) if (!ctx)
return NULL_TREE; 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 /* We could use ctx->object unconditionally, but using ctx->ctor when we
can is a minor optimization. */ can is a minor optimization. */
if (!lval && ctx->ctor && same_type_p (TREE_TYPE (ctx->ctor), type)) if (!lval && ctx->ctor && same_type_p (TREE_TYPE (ctx->ctor), type))
...@@ -5606,19 +5618,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, ...@@ -5606,19 +5618,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
r = *p; r = *p;
break; break;
} }
tree init = TARGET_EXPR_INITIAL (t);
if ((AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))) 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 /* We're being expanded without an explicit target, so start
initializing a new object; expansion with an explicit target initializing a new object; expansion with an explicit target
strips the TARGET_EXPR before we get here. */ strips the TARGET_EXPR before we get here. */
new_ctx = *ctx; 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); new_ctx.ctor = build_constructor (type, NULL);
CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true; CONSTRUCTOR_NO_CLEARING (new_ctx.ctor) = true;
new_ctx.object = slot; new_ctx.object = slot;
...@@ -6472,7 +6481,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, ...@@ -6472,7 +6481,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
bool overflow_p = false; bool overflow_p = false;
constexpr_global_ctx global_ctx; 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, allow_non_constant, strict,
manifestly_const_eval || !allow_non_constant, manifestly_const_eval || !allow_non_constant,
uid_sensitive }; uid_sensitive };
......
2020-04-04 Patrick Palka <ppalka@redhat.com> 2020-04-04 Patrick Palka <ppalka@redhat.com>
PR c++/94205
PR c++/79937
* g++.dg/cpp1y/pr79937-5.C: New test.
* g++.dg/cpp1z/lambda-this6.C: New test.
PR c++/94219 PR c++/94219
PR c++/94205 PR c++/94205
* g++.dg/cpp1y/constexpr-nsdmi3.C: New test. * g++.dg/cpp1y/constexpr-nsdmi3.C: New test.
......
// 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, "");
// { dg-do compile { target c++17 } }
struct S
{
int a = [this] { return 6; } ();
};
S
foo()
{
return {};
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment