diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -4119,7 +4119,11 @@ InitializationSequence::FK_ListConstructorOverloadFailed : InitializationSequence::FK_ConstructorOverloadFailed, Result); - return; + + // delete function also need the initialization sequence. + // TODO: which FailureKind? + if (Result != OR_Deleted) + return; } bool HadMultipleCandidates = (CandidateSet.size() > 1); @@ -5260,7 +5264,8 @@ Sequence.SetOverloadFailure( InitializationSequence::FK_UserConversionOverloadFailed, Result); - return; + if (Result != OR_Deleted) + return; } FunctionDecl *Function = Best->Function; diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -3106,11 +3106,14 @@ /// If move-initialization is not possible, such that we must fall back to /// treating the operand as an lvalue, we will leave Res in its original /// invalid state. -static void TryMoveInitialization(Sema& S, - const InitializedEntity &Entity, +/// +/// \returns Whether need to do the second overload resolution. If the first +/// overload resolution fails, or if the first overload resolution success but +/// the selected constructor/operator doesn't match the additional criteria, we +/// need to do the second overload resolution. +static bool TryMoveInitialization(Sema &S, const InitializedEntity &Entity, const VarDecl *NRVOCandidate, - QualType ResultType, - Expr *&Value, + QualType ResultType, Expr *&Value, bool ConvertingConstructorsOnly, ExprResult &Res) { ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(), @@ -3123,8 +3126,10 @@ InitializationSequence Seq(S, Entity, Kind, InitExpr); - if (!Seq) - return; + bool NeedSecondOverload = true; + if (!Seq && Seq.getFailedOverloadResult() != OR_Deleted) { + return NeedSecondOverload; + } for (const InitializationSequence::Step &Step : Seq.steps()) { if (Step.Kind != InitializationSequence::SK_ConstructorInitialization && @@ -3167,6 +3172,7 @@ } } + NeedSecondOverload = false; // Promote "AsRvalue" to the heap, since we now need this // expression node to persist. Value = @@ -3177,6 +3183,8 @@ // using the constructor we found. Res = Seq.Perform(S, Entity, Kind, Value); } + + return NeedSecondOverload; } /// Perform the initialization of a potentially-movable value, which @@ -3201,6 +3209,7 @@ // select the constructor for the copy is first performed as if the object // were designated by an rvalue. ExprResult Res = ExprError(); + bool NeedSecondOverload = true; if (AllowNRVO) { bool AffectedByCWG1579 = false; @@ -3217,11 +3226,11 @@ } if (NRVOCandidate) { - TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, Value, - true, Res); + NeedSecondOverload = TryMoveInitialization(*this, Entity, NRVOCandidate, + ResultType, Value, true, Res); } - if (!Res.isInvalid() && AffectedByCWG1579) { + if (!NeedSecondOverload && AffectedByCWG1579) { QualType QT = NRVOCandidate->getType(); if (QT.getNonReferenceType() .getUnqualifiedType() @@ -3244,7 +3253,7 @@ Diag(Value->getExprLoc(), diag::note_add_std_move_in_cxx11) << FixItHint::CreateReplacement(Value->getSourceRange(), Str); } - } else if (Res.isInvalid() && + } else if (NeedSecondOverload && !getDiagnostics().isIgnored(diag::warn_return_std_move, Value->getExprLoc())) { const VarDecl *FakeNRVOCandidate = @@ -3285,7 +3294,7 @@ // Either we didn't meet the criteria for treating an lvalue as an rvalue, // above, or overload resolution failed. Either way, we need to try // (again) now with the return value expression as written. - if (Res.isInvalid()) + if (NeedSecondOverload) Res = PerformCopyInitialization(Entity, SourceLocation(), Value); return Res; diff --git a/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp b/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/class/class.init/class.copy.elision/p3.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=cxx20 %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=cxx11_14_17 %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=cxx11_14_17 %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=cxx11_14_17 %s + +namespace test_delete_function { +struct A1 { + A1(); + A1(const A1&); + A1(A1&&) = delete; + // cxx11_14_17-note@-1 {{'A1' has been explicitly marked deleted here}} + // cxx20-note@-2 {{'A1' has been explicitly marked deleted here}} +}; +A1 test1() { + A1 a; + return a; + // cxx11_14_17-error@-1 {{call to deleted constructor of 'test_delete_function::A1'}} + // cxx20-error@-2 {{call to deleted constructor of 'test_delete_function::A1'}} +} + +struct A2 { + A2(); + A2(const A2&); +private: + A2(A2&&); + // cxx11_14_17-note@-1 {{declared private here}} + // cxx20-note@-2 {{declared private here}} +}; +A2 test2() { + A2 a; + return a; + // cxx11_14_17-error@-1 {{calling a private constructor of class 'test_delete_function::A2'}} + // cxx20-error@-2 {{calling a private constructor of class 'test_delete_function::A2'}} +} + + +struct C {}; + +struct B1 { + B1(C&); + B1(C&&) = delete; + // cxx11_14_17-note@-1 {{'B1' has been explicitly marked deleted here}} + // cxx20-note@-2 {{'B1' has been explicitly marked deleted here}} +}; +B1 test3() { + C c; + return c; + // cxx11_14_17-error@-1 {{conversion function from 'test_delete_function::C' to 'test_delete_function::B1' invokes a deleted function}} + // cxx20-error@-2 {{conversion function from 'test_delete_function::C' to 'test_delete_function::B1' invokes a deleted function}} +} + +struct B2 { + B2(C&); +private: + B2(C&&); + // cxx11_14_17-note@-1 {{declared private here}} + // cxx20-note@-2 {{declared private here}} +}; +B2 test4() { + C c; + return c; + // cxx11_14_17-error@-1 {{calling a private constructor of class 'test_delete_function::B2'}} + // cxx20-error@-2 {{calling a private constructor of class 'test_delete_function::B2'}} +} +} // namespace test_delete_function