Hi! The following testcase is rejected, because we determine B as void & rather than the expected void when -flifetime-dse.
I'd say the main problem is premature folding, that we lower the trivial destructor already at parsing time, rather than cp_fold, I'd say that goes against the intentions of the late folding. That said, these patch attempts to address this differently (in a way that would also be backportable). With -fno-lifetime-dse, when the test passes, the trivial destructor is folded into (void) &instance and lvalue_kind returns clk_none for that, so finish_decltype_type returns just void for it. With -flifetime-dse, the trivial destructor is folded into a void mode MODIFY_EXPR instance = {CLOBBER} and lvalue_kind returns clk_ordinary in that case, so finish_decltype_type then applies the rules and makes void & out of it. The first patch fixes this by forcibly wrapping the MODIFY_EXPR into a void type NOP_EXPR, so that lvalue_kind still considers it clk_none. The second patch fixes that by special casing void type MODIFY_EXPR, I believe if we have void type MODIFY_EXPR, then it can't be an lvalue. Another option would be to tweak finish_decltype_type to special case VOID_TYPE_P and never try to create a reference for it (which is not valid in C++). Both patches successfully bootstrapped/regtested on x86_64-linux and i686-linux. 2019-05-24 Jakub Jelinek <ja...@redhat.com> PR c++/90598 * call.c (build_trivial_dtor_call): Wrap MODIFY_EXPR for -flifetime-dse into void_type_node NOP_EXPR. * g++.dg/cpp0x/pr90598.C: New test. --- gcc/cp/call.c.jj 2019-05-23 12:57:16.658493722 +0200 +++ gcc/cp/call.c 2019-05-23 18:37:09.570874611 +0200 @@ -7995,8 +7995,8 @@ build_trivial_dtor_call (tree instance) /* A trivial destructor should still clobber the object. */ tree clobber = build_clobber (TREE_TYPE (instance)); - return build2 (MODIFY_EXPR, void_type_node, - instance, clobber); + return build1 (NOP_EXPR, void_type_node, + build2 (MODIFY_EXPR, void_type_node, instance, clobber)); } /* Subroutine of the various build_*_call functions. Overload resolution --- gcc/testsuite/g++.dg/cpp0x/pr90598.C.jj 2019-05-23 18:39:37.034464509 +0200 +++ gcc/testsuite/g++.dg/cpp0x/pr90598.C 2019-05-23 18:39:05.471980348 +0200 @@ -0,0 +1,8 @@ +// PR c++/90598 +// { dg-do compile { target c++11 } } + +struct A {}; +using B = decltype(A ().~A ()); +template <typename T> struct C; +template <> struct C<void> {}; +C<B> t; Jakub
2019-05-24 Jakub Jelinek <ja...@redhat.com> PR c++/90598 * tree.c (lvalue_kind): Return clk_none for MODIFY_EXPR with VOID_TYPE_P. * g++.dg/cpp0x/pr90598.C: New test. --- gcc/cp/tree.c.jj 2019-05-20 21:59:19.886720194 +0200 +++ gcc/cp/tree.c 2019-05-23 23:37:42.335181291 +0200 @@ -245,6 +245,10 @@ lvalue_kind (const_tree ref) return clk_ordinary; case MODIFY_EXPR: + if (TREE_TYPE (ref) && VOID_TYPE_P (TREE_TYPE (ref))) + return clk_none; + return clk_ordinary; + case TYPEID_EXPR: return clk_ordinary; --- gcc/testsuite/g++.dg/cpp0x/pr90598.C.jj 2019-05-23 18:39:37.034464509 +0200 +++ gcc/testsuite/g++.dg/cpp0x/pr90598.C 2019-05-23 18:39:05.471980348 +0200 @@ -0,0 +1,8 @@ +// PR c++/90598 +// { dg-do compile { target c++11 } } + +struct A {}; +using B = decltype(A ().~A ()); +template <typename T> struct C; +template <> struct C<void> {}; +C<B> t;