From: Douglas Gregor Date: Wed, 15 Feb 2012 19:33:52 +0000 (+0000) Subject: When overload resolution picks an implicitly-deleted special member X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e4e68d45f89ff4899d30cbd196603d09b7fbc150;p=clang When overload resolution picks an implicitly-deleted special member function, provide a specialized diagnostic that indicates the kind of special member function (default constructor, copy assignment operator, etc.) and that it was implicitly deleted. Add a hook where we can provide more detailed information later. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150611 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d9dadce1d3..8e502ed77a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1157,8 +1157,8 @@ def err_temp_copy_ambiguous : Error< def err_temp_copy_deleted : Error< "%select{copying variable|copying parameter|returning object|throwing " "object|copying member subobject|copying array element|allocating object|" - "copying temporary|initializing base subobject|initializing vector element}0 " - "of type %1 invokes deleted constructor|capturing value">; + "copying temporary|initializing base subobject|initializing vector element|" + "capturing value}0 of type %1 invokes deleted constructor">; def err_temp_copy_incomplete : Error< "copying a temporary object of incomplete type %0">; def warn_cxx98_compat_temp_copy : Warning< @@ -2104,6 +2104,10 @@ def err_ref_init_ambiguous : Error< "reference initialization of type %0 with initializer of type %1 is ambiguous">; def err_ovl_deleted_init : Error< "call to %select{unavailable|deleted}0 constructor of %1">; +def err_ovl_deleted_special_init : Error< + "call to implicitly-deleted %select{default constructor|copy constructor|" + "move constructor|copy assignment operator|move assignment operator|" + "destructor|function}0 of %1">; def err_ovl_ambiguous_oper_unary : Error< "use of overloaded operator '%0' is ambiguous (operand type %1)">; def err_ovl_ambiguous_oper_binary : Error< @@ -2111,6 +2115,10 @@ def err_ovl_ambiguous_oper_binary : Error< def err_ovl_no_viable_oper : Error<"no viable overloaded '%0'">; def err_ovl_deleted_oper : Error< "overload resolution selected %select{unavailable|deleted}0 operator '%1'%2">; +def err_ovl_deleted_special_oper : Error< + "overload resolution selected implicitly-deleted %select{default constructor|" + "copy constructor|move constructor|copy assignment operator|move assignment " + "operator|destructor|'%1'}0%2">; def err_ovl_no_viable_subscript : Error<"no viable overloaded operator[] for type %0">; def err_ovl_no_oper : @@ -4307,6 +4315,8 @@ def err_typecheck_call_too_many_args_at_most : Error< "expected at most %1, have %2">; def note_callee_decl : Note< "%0 declared here">; +def note_defined_here : Note<"%0 defined here">; + def warn_call_wrong_number_of_arguments : Warning< "too %select{few|many}0 arguments in call to %1">; def err_atomic_builtin_must_be_pointer : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 5ac2cd560b..0fd4c9f8b1 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3068,6 +3068,10 @@ public: /// class. void ForceDeclarationOfImplicitMembers(CXXRecordDecl *Class); + /// \brief Determine whether the given function is an implicitly-deleted + /// special member function. + bool isImplicitlyDeleted(FunctionDecl *FD); + /// MaybeBindToTemporary - If the passed in expression has a record type with /// a non-trivial destructor, this will return CXXBindTemporaryExpr. Otherwise /// it simply returns the passed in expression. diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 7d30b7ff8b..664a658160 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -672,8 +672,14 @@ void TypePrinter::printTag(TagDecl *D, std::string &InnerString) { // Make an unambiguous representation for anonymous types, e.g. // llvm::raw_string_ostream OS(Buffer); - OS << "(D) && cast(D)->isLambda()) { + OS << "isDeleted() && + (FD->isDefaulted() || FD->isImplicit()) && + isa(FD); +} + ExprResult Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType, CXXConstructorDecl *Constructor, diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 1638693a2c..da6892e230 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -5273,6 +5273,26 @@ InitializationSequence::Perform(Sema &S, return move(CurInit); } +/// \brief Provide some notes that detail why a function was implicitly +/// deleted. +static void diagnoseImplicitlyDeletedFunction(Sema &S, CXXMethodDecl *Method) { + // FIXME: This is a work in progress. It should dig deeper to figure out + // why the function was deleted (e.g., because one of its members doesn't + // have a copy constructor, for the copy-constructor case). + if (!Method->isImplicit()) { + S.Diag(Method->getLocation(), diag::note_callee_decl) + << Method->getDeclName(); + } + + if (Method->getParent()->isLambda()) { + S.Diag(Method->getParent()->getLocation(), diag::note_lambda_decl); + return; + } + + S.Diag(Method->getParent()->getLocation(), diag::note_defined_here) + << Method->getParent(); +} + //===----------------------------------------------------------------------===// // Diagnose initialization failures //===----------------------------------------------------------------------===// @@ -5536,17 +5556,33 @@ bool InitializationSequence::Diagnose(Sema &S, break; case OR_Deleted: { - S.Diag(Kind.getLocation(), diag::err_ovl_deleted_init) - << true << DestType << ArgsRange; OverloadCandidateSet::iterator Best; OverloadingResult Ovl = FailedCandidateSet.BestViableFunction(S, Kind.getLocation(), Best); - if (Ovl == OR_Deleted) { - S.Diag(Best->Function->getLocation(), diag::note_unavailable_here) - << 1 << Best->Function->isDeleted(); - } else { + if (Ovl != OR_Deleted) { + S.Diag(Kind.getLocation(), diag::err_ovl_deleted_init) + << true << DestType << ArgsRange; llvm_unreachable("Inconsistent overload resolution?"); + break; } + + // If this is a defaulted or implicitly-declared function, then + // it was implicitly deleted. Make it clear that the deletion was + // implicit. + if (S.isImplicitlyDeleted(Best->Function)) { + S.Diag(Kind.getLocation(), diag::err_ovl_deleted_special_init) + << S.getSpecialMember(cast(Best->Function)) + << DestType << ArgsRange; + + diagnoseImplicitlyDeletedFunction(S, + cast(Best->Function)); + break; + } + + S.Diag(Kind.getLocation(), diag::err_ovl_deleted_init) + << true << DestType << ArgsRange; + S.Diag(Best->Function->getLocation(), diag::note_unavailable_here) + << 1 << Best->Function->isDeleted(); break; } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index b20dc955c1..2b025da088 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -9984,11 +9984,24 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, return ExprError(); case OR_Deleted: - Diag(OpLoc, diag::err_ovl_deleted_oper) - << Best->Function->isDeleted() - << BinaryOperator::getOpcodeStr(Opc) - << getDeletedOrUnavailableSuffix(Best->Function) - << Args[0]->getSourceRange() << Args[1]->getSourceRange(); + if (isImplicitlyDeleted(Best->Function)) { + CXXMethodDecl *Method = cast(Best->Function); + Diag(OpLoc, diag::err_ovl_deleted_special_oper) + << getSpecialMember(Method) + << BinaryOperator::getOpcodeStr(Opc) + << getDeletedOrUnavailableSuffix(Best->Function); + + if (Method->getParent()->isLambda()) { + Diag(Method->getParent()->getLocation(), diag::note_lambda_decl); + return ExprError(); + } + } else { + Diag(OpLoc, diag::err_ovl_deleted_oper) + << Best->Function->isDeleted() + << BinaryOperator::getOpcodeStr(Opc) + << getDeletedOrUnavailableSuffix(Best->Function) + << Args[0]->getSourceRange() << Args[1]->getSourceRange(); + } CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, 2, BinaryOperator::getOpcodeStr(Opc), OpLoc); return ExprError(); diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp index 03f25ba1de..47efde2db0 100644 --- a/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.aggr/p1-0x.cpp @@ -54,9 +54,9 @@ struct NonAggr5 : Aggr { // expected-note 3 {{candidate constructor}} }; NonAggr5 na5 = { b }; // expected-error {{no matching constructor for initialization of 'NonAggr5'}} template -struct MaybeAggr5a : BaseList... {}; // expected-note {{explicitly marked deleted}} +struct MaybeAggr5a : BaseList... {}; // expected-note {{defined here}} MaybeAggr5a<> ma5a0 = {}; // ok -MaybeAggr5a ma5a1 = {}; // expected-error {{call to deleted constructor of 'MaybeAggr5a'}} +MaybeAggr5a ma5a1 = {}; // expected-error {{call to implicitly-deleted default constructor of 'MaybeAggr5a'}} // and no virtual functions. struct NonAggr6 { // expected-note 3 {{candidate constructor}} diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp index b5a445cd9e..6fe3b25259 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/p19.cpp @@ -7,16 +7,13 @@ struct MoveOnly { template T &&move(T&); void test_special_member_functions(MoveOnly mo, int i) { - // FIXME: terrible note - auto lambda1 = [i]() { }; // expected-note{{function has been explicitly marked deleted here}} \ - // expected-note{{the implicit copy assignment operator}} \ - // expected-note{{the implicit move assignment operator}} \ + auto lambda1 = [i]() { }; // expected-note 2 {{lambda expression begins here}} // Default constructor - decltype(lambda1) lambda2; // expected-error{{call to deleted constructor}} + decltype(lambda1) lambda2; // expected-error{{call to implicitly-deleted default constructor of 'decltype(lambda1)' (aka '