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,9 @@ InitializationSequence::FK_ListConstructorOverloadFailed : InitializationSequence::FK_ConstructorOverloadFailed, Result); - return; + + if (Result != OR_Deleted) + return; } bool HadMultipleCandidates = (CandidateSet.size() > 1); @@ -4140,31 +4142,45 @@ return; } - // C++11 [dcl.init]p6: - // If a program calls for the default initialization of an object - // of a const-qualified type T, T shall be a class type with a - // user-provided default constructor. - // C++ core issue 253 proposal: - // If the implicit default constructor initializes all subobjects, no - // initializer should be required. - // The 253 proposal is for example needed to process libstdc++ headers in 5.x. CXXConstructorDecl *CtorDecl = cast(Best->Function); - if (Kind.getKind() == InitializationKind::IK_Default && - Entity.getType().isConstQualified()) { - if (!CtorDecl->getParent()->allowConstDefaultInit()) { - if (!maybeRecoverWithZeroInitialization(S, Sequence, Entity)) - Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst); + if (Result != OR_Deleted) { // TODO: Support for more than one failure. + // C++11 [dcl.init]p6: + // If a program calls for the default initialization of an object + // of a const-qualified type T, T shall be a class type with a + // user-provided default constructor. + // C++ core issue 253 proposal: + // If the implicit default constructor initializes all subobjects, no + // initializer should be required. + // The 253 proposal is for example needed to process libstdc++ headers + // in 5.x. + if (Kind.getKind() == InitializationKind::IK_Default && + Entity.getType().isConstQualified()) { + if (!CtorDecl->getParent()->allowConstDefaultInit()) { + if (!maybeRecoverWithZeroInitialization(S, Sequence, Entity)) + Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst); + return; + } + } + + // C++11 [over.match.list]p1: + // In copy-list-initialization, if an explicit constructor is chosen, the + // initializer is ill-formed. + if (IsListInit && !Kind.AllowExplicit() && CtorDecl->isExplicit()) { + Sequence.SetFailed(InitializationSequence::FK_ExplicitConstructor); return; } } - // C++11 [over.match.list]p1: - // In copy-list-initialization, if an explicit constructor is chosen, the - // initializer is ill-formed. - if (IsListInit && !Kind.AllowExplicit() && CtorDecl->isExplicit()) { - Sequence.SetFailed(InitializationSequence::FK_ExplicitConstructor); + // [class.copy.elision]p3: + // In some copy-initialization contexts, a two-stage overload resolution + // is performed. + // If the first overload resolution selects a deleted function, we also + // need the initialization sequence to decide whether to perform the second + // overload resolution. + // For deleted functions in other contexts, there is no need to get the + // initialization sequence. + if (Result == OR_Deleted && Kind.getKind() != InitializationKind::IK_Copy) return; - } // Add the constructor initialization step. Any cv-qualification conversion is // subsumed by the initialization. @@ -5260,7 +5276,16 @@ Sequence.SetOverloadFailure( InitializationSequence::FK_UserConversionOverloadFailed, Result); - return; + + // [class.copy.elision]p3: + // In some copy-initialization contexts, a two-stage overload resolution + // is performed. + // If the first overload resolution selects a deleted function, we also + // need the initialization sequence to decide whether to perform the second + // overload resolution. + if (!(Result == OR_Deleted && + Kind.getKind() == InitializationKind::IK_Copy)) + 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 we need to do the second overload resolution. If the first +/// overload resolution fails, or if the first overload resolution succeeds 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 NeedSecondOverloadResolution = true; + if (!Seq && Seq.getFailedOverloadResult() != OR_Deleted) { + return NeedSecondOverloadResolution; + } for (const InitializationSequence::Step &Step : Seq.steps()) { if (Step.Kind != InitializationSequence::SK_ConstructorInitialization && @@ -3167,6 +3172,7 @@ } } + NeedSecondOverloadResolution = 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 NeedSecondOverloadResolution; } /// 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 NeedSecondOverloadResolution = true; if (AllowNRVO) { bool AffectedByCWG1579 = false; @@ -3217,11 +3226,11 @@ } if (NRVOCandidate) { - TryMoveInitialization(*this, Entity, NRVOCandidate, ResultType, Value, - true, Res); + NeedSecondOverloadResolution = TryMoveInitialization( + *this, Entity, NRVOCandidate, ResultType, Value, true, Res); } - if (!Res.isInvalid() && AffectedByCWG1579) { + if (!NeedSecondOverloadResolution && 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 (NeedSecondOverloadResolution && !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 (NeedSecondOverloadResolution) 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,49 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected %s +// RUN: %clang_cc1 -std=c++17 -fsyntax-only -fcxx-exceptions -verify=expected %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -fcxx-exceptions -verify=expected %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fcxx-exceptions -verify=expected %s + +namespace test_delete_function { +struct A1 { + A1(); + A1(const A1 &); + A1(A1 &&) = delete; // expected-note {{'A1' has been explicitly marked deleted here}} +}; +A1 test1() { + A1 a; + return a; // expected-error {{call to deleted constructor of 'test_delete_function::A1'}} +} + +struct A2 { + A2(); + A2(const A2 &); +private: + A2(A2 &&); // expected-note {{declared private here}} +}; +A2 test2() { + A2 a; + return a; // expected-error {{calling a private constructor of class 'test_delete_function::A2'}} +} + +struct C {}; + +struct B1 { + B1(C &); + B1(C &&) = delete; // expected-note {{'B1' has been explicitly marked deleted here}} +}; +B1 test3() { + C c; + return c; // expected-error {{conversion function from 'test_delete_function::C' to 'test_delete_function::B1' invokes a deleted function}} +} + +struct B2 { + B2(C &); + +private: + B2(C &&); // expected-note {{declared private here}} +}; +B2 test4() { + C c; + return c; // expected-error {{calling a private constructor of class 'test_delete_function::B2'}} +} +} // namespace test_delete_function