On Thu, Feb 27, 2025 at 10:42:07AM -0500, Jason Merrill wrote: > On 2/26/25 2:16 PM, Marek Polacek wrote: > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > -- >8 -- > > Yet another problem that started with r15-6052, compile time evaluation of > > prvalues. > > > > cp_fold_r/TARGET_EXPR sees: > > > > TARGET_EXPR <D.2701, <<< Unknown tree: expr_stmt > > D.2701.__p = TARGET_EXPR <D.2684, <<< Unknown tree: aggr_init_expr > > 3 > > f1 > > D.2684 >>>> >>>> > > > > so when we call maybe_constant_init, the object we're initializing is > > D.2701, > > and the init is the expr_stmt. We unwrap the > > EXPR_STMT/INIT_EXPR/TARGET_EXPR > > in maybe_constant_init_1 and so end up evaluating the f1 call. But f1 > > returns > > c2 whereas the type of D.2701 is ._anon_0 -- the closure. > > Sounds like the problem is with the maybe_constant_init_1 unwrapping, it > probably shouldn't strip INIT_EXPR if the type doesn't match that of 'decl'.
Yes, that's where the types change. I thought that skipping the unpacking here might cause other issues but it seems not. > > So then we crash in replace_decl on: > > > > gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p > > (TREE_TYPE (decl), TREE_TYPE (replacement))); > > > > due to the mismatched types. > > > > cxx_eval_outermost_constant_expr is already ready for the types to be > > different, in which case the result isn't constant. But replace_decl > > is called before that check. > > > > I'm leaving the assert in replace_decl on purpose, maybe we'll find > > another use for it. > > > > PR c++/118986 > > > > gcc/cp/ChangeLog: > > > > * constexpr.cc (cxx_eval_call_expression): Check that the types match > > before calling replace_decl. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp2a/constexpr-prvalue1.C: New test. > > --- > > gcc/cp/constexpr.cc | 4 +++- > > .../g++.dg/cpp2a/constexpr-prvalue1.C | 23 +++++++++++++++++++ > > 2 files changed, 26 insertions(+), 1 deletion(-) > > create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-prvalue1.C > > > > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > > index 59dd0668af3..204cda2a222 100644 > > --- a/gcc/cp/constexpr.cc > > +++ b/gcc/cp/constexpr.cc > > @@ -3390,7 +3390,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, > > tree t, > > current object under construction. */ > > if (!*non_constant_p && ctx->object > > && CLASS_TYPE_P (TREE_TYPE (res)) > > - && !is_empty_class (TREE_TYPE (res))) > > + && !is_empty_class (TREE_TYPE (res)) > > + && same_type_ignoring_top_level_qualifiers_p > > + (TREE_TYPE (res), TREE_TYPE (ctx->object))) > > If this happens, rather than just skip the replace_decl, I think we want to > set *non_constant_p or I expect we'll end up with a wrong value somewhere. Good point, I agree. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- Yet another problem that started with r15-6052, compile time evaluation of prvalues. cp_fold_r/TARGET_EXPR sees: TARGET_EXPR <D.2701, <<< Unknown tree: expr_stmt D.2701.__p = TARGET_EXPR <D.2684, <<< Unknown tree: aggr_init_expr 3 f1 D.2684 >>>> >>>> so when we call maybe_constant_init, the object we're initializing is D.2701, and the init is the expr_stmt. We unwrap the EXPR_STMT/INIT_EXPR/TARGET_EXPR in maybe_constant_init_1 and so end up evaluating the f1 call. But f1 returns c2 whereas the type of D.2701 is ._anon_0 -- the closure. So then we crash in replace_decl on: gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (decl), TREE_TYPE (replacement))); due to the mismatched types. cxx_eval_outermost_constant_expr is already ready for the types to be different, in which case the result isn't constant. But replace_decl is called before that check. I'm leaving the assert in replace_decl on purpose, maybe we'll find another use for it. PR c++/118986 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_call_expression): Check that the types match before calling replace_decl, if not, set *non_constant_p. (maybe_constant_init_1): Don't strip INIT_EXPR if it would change the type of the expression. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/constexpr-prvalue1.C: New test. --- gcc/cp/constexpr.cc | 29 +++++++++++++------ .../g++.dg/cpp2a/constexpr-prvalue1.C | 23 +++++++++++++++ 2 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-prvalue1.C diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index c68666cc5dd..5439b2ea8fe 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -3388,16 +3388,22 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, /* Rewrite all occurrences of the function's RESULT_DECL with the current object under construction. */ - if (!*non_constant_p && ctx->object + if (!*non_constant_p + && ctx->object && CLASS_TYPE_P (TREE_TYPE (res)) && !is_empty_class (TREE_TYPE (res))) - if (replace_decl (&result, res, ctx->object)) - { - cacheable = false; - result = cxx_eval_constant_expression (ctx, result, lval, - non_constant_p, - overflow_p); - } + { + if (!same_type_ignoring_top_level_qualifiers_p + (TREE_TYPE (res), TREE_TYPE (ctx->object))) + *non_constant_p = true; + else if (replace_decl (&result, res, ctx->object)) + { + cacheable = false; + result = cxx_eval_constant_expression (ctx, result, lval, + non_constant_p, + overflow_p); + } + } /* Only cache a permitted result of a constant expression. */ if (cacheable && !reduced_constant_expression_p (result)) @@ -9605,7 +9611,12 @@ maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant, if (TREE_CODE (t) == CONVERT_EXPR && VOID_TYPE_P (TREE_TYPE (t))) t = TREE_OPERAND (t, 0); - if (TREE_CODE (t) == INIT_EXPR) + /* Doing the following stripping could change the type of the expression, + which then would not match DECL's type, and cause mischief later. */ + if (TREE_CODE (t) == INIT_EXPR + && (!decl + || same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (decl), + TREE_TYPE (t)))) t = TREE_OPERAND (t, 1); if (TREE_CODE (t) == TARGET_EXPR) t = TARGET_EXPR_INITIAL (t); diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-prvalue1.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-prvalue1.C new file mode 100644 index 00000000000..f4e704d9487 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-prvalue1.C @@ -0,0 +1,23 @@ +// PR c++/118986 +// { dg-do compile { target c++20 } } +// { dg-options "-O" } + +struct c1 { + constexpr c1(int *ptr) {} +}; +struct c2 { + c1 _M_t; + constexpr ~c2() {} +}; +constexpr inline +c2 f1 () +{ + return c2(new int); +} + +void +f () +{ + auto l = [p = f1()](){}; + [p = f1()](){}; +} base-commit: 9792126ac769f2962c0f305991818c64f9e51221 -- 2.48.1