From 49a86fce1a879a206fb4b27f097910005d968fda Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Thu, 2 Apr 2020 16:03:18 -0400 Subject: [PATCH] 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. --- gcc/cp/ChangeLog | 13 ++++++++++ gcc/cp/constexpr.c | 25 ++++++++++++------ gcc/testsuite/ChangeLog | 5 ++++ gcc/testsuite/g++.dg/cpp1y/pr79937-5.C | 42 +++++++++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp1z/lambda-this6.C | 12 +++++++++ 5 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1y/pr79937-5.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/lambda-this6.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 011da37..686128a 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,18 @@ 2020-04-04 Patrick Palka + 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 diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 8fa1f53..96497ab 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1076,6 +1076,9 @@ struct constexpr_ctx { 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 @@ -3841,6 +3844,9 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, vec **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; @@ -5303,6 +5309,12 @@ lookup_placeholder (const constexpr_ctx *ctx, bool lval, tree type) 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)) @@ -5606,19 +5618,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, 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; @@ -6472,7 +6481,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, 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 }; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index dedde9f..7b2d3c6 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,10 @@ 2020-04-04 Patrick Palka + 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++/94205 * g++.dg/cpp1y/constexpr-nsdmi3.C: New test. diff --git a/gcc/testsuite/g++.dg/cpp1y/pr79937-5.C b/gcc/testsuite/g++.dg/cpp1y/pr79937-5.C new file mode 100644 index 0000000..cbf0115 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/pr79937-5.C @@ -0,0 +1,42 @@ +// 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, ""); diff --git a/gcc/testsuite/g++.dg/cpp1z/lambda-this6.C b/gcc/testsuite/g++.dg/cpp1z/lambda-this6.C new file mode 100644 index 0000000..76f067d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/lambda-this6.C @@ -0,0 +1,12 @@ +// { dg-do compile { target c++17 } } + +struct S +{ + int a = [this] { return 6; } (); +}; + +S +foo() +{ + return {}; +} -- 2.7.4