https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127710
--- Comment #7 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The trunk branch has been updated by Marek Polacek <[email protected]>: https://gcc.gnu.org/g:7ae49df44393ea4ce5024511a3d1b2ba81deca90 commit r17-5007-g7ae49df44393ea4ce5024511a3d1b2ba81deca90 Author: Marek Polacek <[email protected]> Date: Wed Oct 7 15:56:28 2026 -0400 c++: TARGET_EXPR in assignment [PR127710] Here we crash on auto foo = [] {} = {}; on the assert in build_over_call: gcc_assert (TREE_CODE (arg) != TARGET_EXPR || force_elide /* It's from binding the ref parm to a packed field. */ || convs[0]->need_temporary_p || ... ) which makes sure that we are initializing directly from class prvalues rather than copying them. The problem is that we created *&TARGET_EXPR <D.2704, {}> in if (is_really_empty_class (type, /*ignore_vptr*/true)) { /* Avoid copying empty classes, but ensure op= returns an lvalue even if the object argument isn't one. */ to = force_lvalue (to, complain); val = build2 (COMPOUND_EXPR, type, arg, to); suppress_warning (val, OPT_Wunused); } where we want an lvalue, but then in cp_build_addr_expr_1 (Let &* cancel out to simplify resulting code) we get &TARGET_EXPR and then build_over_call strips the & and we end up with a prvalue. The test is actually ([]{}).operator=({}) where binding the []{} object materializes a temporary, operator= returns an lvalue and foo is copy-initialized from that lvalue. My fix is to follow what cp_build_modify_expr (and other places) do: build up a COMPOUND_EXPR and use the TARGET_EXPR_SLOT so that the TARGET_EXPR can't become a prvalue. PR c++/127710 gcc/cp/ChangeLog: * cvt.cc (force_lvalue): For a TARGET_EXPR, build (TARGET_EXPR<...>, slot). gcc/testsuite/ChangeLog: * g++.dg/cpp2a/class-copy1.C: New test. * g++.dg/cpp2a/lambda-assign1.C: New test. Reviewed-by: Jason Merrill <[email protected]>
