Summary: | [c++2a] Deleted move constructor is not selected when returning an automatic variable | ||
---|---|---|---|
Product: | gcc | Reporter: | Yunrui Wang <ph3rin> |
Component: | c++ | Assignee: | Not yet assigned to anyone <unassigned> |
Status: | RESOLVED FIXED | ||
Severity: | normal | CC: | arthur.j.odwyer, dev, jakub, jason, webrown.cpp |
Priority: | P3 | Keywords: | accepts-invalid |
Version: | 9.2.0 | ||
Target Milestone: | --- | ||
See Also: | https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91427 | ||
Host: | Target: | ||
Build: | Known to work: | ||
Known to fail: | Last reconfirmed: | 2021-05-13 00:00:00 |
Description
Yunrui Wang
2019-12-30 14:48:28 UTC
What you cite doesn't say anything that would make the testcase invalid. The standard says that for an implicitly movable entity seen in return statement shall first perform overload resolution for the copy constructor as if it was an rvalue, which in this case fails because the move ctor is deleted. And in that case, another overload resolution is performed, this time considering the implicitly movable entity as lvalue, and that succeeds. So, if copy-ellision wouldn't be performed, not_movable::not_movable(not_movable const&) would be invoked, but in this case copy-ellision is invoked and thus it is default constructed into the return value object in the caller. (In reply to Jakub Jelinek from comment #1) > What you cite doesn't say anything that would make the testcase invalid. > The standard says that for an implicitly movable entity seen in return > statement shall first perform overload resolution for the copy constructor > as if it was an rvalue, which in this case fails because the move ctor is > deleted. And in that case, another overload resolution is performed, this > time considering the implicitly movable entity as lvalue, and that succeeds. > So, if copy-ellision wouldn't be performed, > not_movable::not_movable(not_movable const&) would be invoked, but in this > case copy-ellision is invoked and thus it is default constructed into the > return value object in the caller. The first overload resolution should succeed by selecting the deleted move constructors. Deleted functions are defined and are considered for overload resolution (unless otherwise specified - there are some edge cases, but not in this example). Confirmed. check_return_expr can't use convert_for_initialization to test whether to treat the returned lvalue as an rvalue. (In reply to Jason Merrill from comment #3) > Confirmed. check_return_expr can't use convert_for_initialization to test > whether to treat the returned lvalue as an rvalue. Indeed: convert_for_initialization -> perform_implicit_conversion_flags -> convert_like_real will build_temp for the rvalue version of OBJ, so it calls build_special_member_call -> build_new_method_call_1. Here we have three candidates: X::X(X&&) constexpr X::X(const X&) constexpr X::X() the last one is not viable so splice_viable kills it. And tourney selects X::X(X&&) as expected. But we pass it to build_over_call and that will not complain and just return error_mark_node for a DECL_DELETED_FN function. Then the second stage of the two-stage overload resolution succeeds. I guess we need to stop and issue an error when we found a move ctor, but it's deleted (but not if we don't find a move ctor at all). (Came here in the context of PR91212 where this convert_for_initialization selects the wrong overload.) *** Bug 93929 has been marked as a duplicate of this bug. *** *** Bug 108594 has been marked as a duplicate of this bug. *** According to godbolt.org, Bug 93929, Bug 108594, and this Bug 93106 all appear to be fixed now (failed in GCC 12, correct behavior in GCC 13.1). Issue has been resolved in gcc 12.1. |