In this test we have a user-defined conversion converting const int & to T, and we're binding a const int to const int & -- the parameter of the converting ctor. We call Func with "VIEW_CONVERT_EXPR<const int>(Val)" as an argument. I like to use a diagram for the conversion like this:
ck_rvalue <- ck_user <- ck_identity T T const int V_C_E<const int>(Val) when processing the ck_user part we call __ct_comp, whose ICS for its argument is ck_ref_bind <- ck_identity const int & <- const int V_C_E<const int>(Val) this ICS comes from reference_binding, and at that time we still had "Val" so need_temporary_p is 0. But when processing the ck_user conversion before calling build_over_call for the __ct_comp, we call 7009 expr = mark_rvalue_use (expr); for the argument which turns the V_C_E to NON_LVALUE_EXPR<42>. Binding "42" to const int & is possible, but requires a temporary, which we don't create precisely because need_temporary_p is 0. I.e. the original expr of the conversion changed behind reference_binding's back. Now, the call to mark_rvalue_use was added in r159096 to handle -Wunused-but-set-variable. Since r261121, we're actually able to turn a V_C_E into an rvalue. But it seems we shouldn't be trying to turn the argument into an rvalue in the ck_user case; mark_lvalue_use should do the job (= mark is as used) without changing an lvalue into an rvalue, breaking the ICS as above. I'm not sure if I'm describing the issue clearly enough, but I hope this will do. Bootstrapped/regtested on x86_64-linux, ok for trunk/8? 2019-02-04 Marek Polacek <pola...@redhat.com> PR c++/89158 - by-value capture of constexpr variable broken. * call.c (convert_like_real) <case ck_user>: Call mark_lvalue_use instead of mark_rvalue_use. * g++.dg/cpp0x/lambda/lambda-89158.C: New test. diff --git gcc/cp/call.c gcc/cp/call.c index c74d1b4ebdf..68395503e1e 100644 --- gcc/cp/call.c +++ gcc/cp/call.c @@ -7006,7 +7006,9 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, return expr; } - expr = mark_rvalue_use (expr); + /* Calling mark_rvalue_use could turn a decl into a constant, breaking + e.g. the need_temporary_p assumption in the ICS. */ + mark_lvalue_use (expr); /* Pass LOOKUP_NO_CONVERSION so rvalue/base handling knows not to allow any more UDCs. */ diff --git gcc/testsuite/g++.dg/cpp0x/lambda/lambda-89158.C gcc/testsuite/g++.dg/cpp0x/lambda/lambda-89158.C new file mode 100644 index 00000000000..15f15b46875 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/lambda/lambda-89158.C @@ -0,0 +1,11 @@ +// PR c++/89158 +// { dg-do compile { target c++11 } } + +struct T { T(const int&); }; +void Func(T); + +void test() +{ + constexpr int Val = 42; + [Val]() { Func(Val); }; +}