diff options
author | Sebastian Redl <sebastian.redl@getdesigned.at> | 2011-02-05 19:23:19 +0000 |
---|---|---|
committer | Sebastian Redl <sebastian.redl@getdesigned.at> | 2011-02-05 19:23:19 +0000 |
commit | f677ea3cc9598d9952ad7ffab5fb322ba4c5be31 (patch) | |
tree | 6fd84ac5d0d6b1128c4f53e7a36f08b89996f47b | |
parent | 57ca32bbf03d30b8867f6c07f1a3e42484bbfec7 (diff) |
Basic implementation of inherited constructors. Only generates declarations, and probably only works for very basic use cases.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124970 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/DeclCXX.h | 24 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 44 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 8 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 16 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 232 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 33 | ||||
-rw-r--r-- | lib/Serialization/ASTReader.cpp | 5 | ||||
-rw-r--r-- | lib/Serialization/ASTWriter.cpp | 1 | ||||
-rw-r--r-- | test/CXX/special/class.inhctor/elsewhere.cpp | 31 | ||||
-rw-r--r-- | test/CXX/special/class.inhctor/p3.cpp | 30 | ||||
-rw-r--r-- | test/CXX/special/class.inhctor/p7.cpp | 18 |
11 files changed, 421 insertions, 21 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 1d1b749feb..d11ee8f7fd 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -187,6 +187,10 @@ class CXXBaseSpecifier { /// VC++ bug. unsigned Access : 2; + /// InheritConstructors - Whether the class contains a using declaration + /// to inherit the named class's constructors. + bool InheritConstructors : 1; + /// BaseTypeInfo - The type of the base class. This will be a class or struct /// (or a typedef of such). The source code range does not include the /// "virtual" or access specifier. @@ -198,7 +202,7 @@ public: CXXBaseSpecifier(SourceRange R, bool V, bool BC, AccessSpecifier A, TypeSourceInfo *TInfo, SourceLocation EllipsisLoc) : Range(R), EllipsisLoc(EllipsisLoc), Virtual(V), BaseOfClass(BC), - Access(A), BaseTypeInfo(TInfo) { } + Access(A), InheritConstructors(false), BaseTypeInfo(TInfo) { } /// getSourceRange - Retrieves the source range that contains the /// entire base specifier. @@ -214,12 +218,20 @@ public: /// \brief Determine whether this base specifier is a pack expansion. bool isPackExpansion() const { return EllipsisLoc.isValid(); } - + + /// \brief Determine whether this base class's constructors get inherited. + bool getInheritConstructors() const { return InheritConstructors; } + + /// \brief Set that this base class's constructors should be inherited. + void setInheritConstructors(bool Inherit = true) { + InheritConstructors = Inherit; + } + /// \brief For a pack expansion, determine the location of the ellipsis. SourceLocation getEllipsisLoc() const { return EllipsisLoc; } - + /// getAccessSpecifier - Returns the access specifier for this base /// specifier. This is the actual base specifier as used for /// semantic analysis, so the result can never be AS_none. To @@ -1519,6 +1531,12 @@ public: /// would copy the object to itself. Such constructors are never used to copy /// an object. bool isSpecializationCopyingObject() const; + + /// \brief Get the constructor that this inheriting constructor is based on. + const CXXConstructorDecl *getInheritedConstructor() const; + + /// \brief Set the constructor that this inheriting constructor is based on. + void setInheritedConstructor(const CXXConstructorDecl *BaseCtor); // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 4c5db08802..9839359688 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -162,6 +162,17 @@ def err_using_decl_nested_name_specifier_is_current_class : Error< "using declaration refers to its own class">; def err_using_decl_nested_name_specifier_is_not_base_class : Error< "using declaration refers into '%0', which is not a base class of %1">; +def err_using_decl_constructor_not_in_direct_base : Error< + "%0 is not a direct base of %1, can not inherit constructors">; +def err_using_decl_constructor_conflict : Error< + "can not inherit constructor, already inherited constructor with " + "the same signature">; +def note_using_decl_constructor_conflict_current_ctor : Note< + "conflicting constructor">; +def note_using_decl_constructor_conflict_previous_ctor : Note< + "previous constructor">; +def note_using_decl_constructor_conflict_previous_using : Note< + "previously inherited here">; def err_using_decl_can_not_refer_to_class_member : Error< "using declaration can not refer to class member">; def err_using_decl_can_not_refer_to_namespace : Error< @@ -1282,12 +1293,14 @@ def note_ovl_candidate : Note<"candidate " "function |function |constructor |" "is the implicit default constructor|" "is the implicit copy constructor|" - "is the implicit copy assignment operator}0%1">; + "is the implicit copy assignment operator|" + "is an inherited constructor}0%1">; def warn_init_pointer_from_false : Warning< "initialization of pointer of type %0 from literal 'false'">, InGroup<BoolConversions>; +def note_ovl_candidate_inherited_constructor : Note<"inherited from here">; def note_ovl_candidate_bad_deduction : Note< "candidate template ignored: failed template argument deduction">; def note_ovl_candidate_incomplete_deduction : Note<"candidate template ignored: " @@ -1315,14 +1328,15 @@ def note_ovl_candidate_arity : Note<"candidate " "%select{function|function|constructor|function|function|constructor|" "constructor (the implicit default constructor)|" "constructor (the implicit copy constructor)|" - "function (the implicit copy assignment operator)}0 %select{|template }1" + "function (the implicit copy assignment operator)|" + "constructor (inherited)}0 %select{|template }1" "not viable: requires%select{ at least| at most|}2 %3 argument%s3, but %4 " "%plural{1:was|:were}4 provided">; def note_ovl_candidate_deleted : Note< - "candidate %select{function|function|constructor|" - "function |function |constructor |||}0%1 " - "has been explicitly %select{made unavailable|deleted}2">; + "candidate %select{function|function|constructor|" + "function |function |constructor ||||constructor (inherited)}0%1 " + "has been explicitly %select{made unavailable|deleted}2">; // Giving the index of the bad argument really clutters this message, and // it's relatively unimportant because 1) it's generally obvious which @@ -1334,21 +1348,24 @@ def note_ovl_candidate_bad_conv_incomplete : Note<"candidate " "function |function |constructor |" "constructor (the implicit default constructor)|" "constructor (the implicit copy constructor)|" - "function (the implicit copy assignment operator)}0%1 " + "function (the implicit copy assignment operator)|" + "constructor (inherited)}0%1 " "not viable: cannot convert argument of incomplete type %2 to %3">; def note_ovl_candidate_bad_overload : Note<"candidate " "%select{function|function|constructor|" "function |function |constructor |" "constructor (the implicit default constructor)|" "constructor (the implicit copy constructor)|" - "function (the implicit copy assignment operator)}0%1" + "function (the implicit copy assignment operator)|" + "constructor (inherited)}0%1" " not viable: no overload of %3 matching %2 for %ordinal4 argument">; def note_ovl_candidate_bad_conv : Note<"candidate " "%select{function|function|constructor|" "function |function |constructor |" "constructor (the implicit default constructor)|" "constructor (the implicit copy constructor)|" - "function (the implicit copy assignment operator)}0%1" + "function (the implicit copy assignment operator)|" + "constructor (inherited)}0%1" " not viable: no known conversion from %2 to %3 for " "%select{%ordinal5 argument|object argument}4">; def note_ovl_candidate_bad_addrspace : Note<"candidate " @@ -1356,12 +1373,13 @@ def note_ovl_candidate_bad_addrspace : Note<"candidate " "function |function |constructor |" "constructor (the implicit default constructor)|" "constructor (the implicit copy constructor)|" - "function (the implicit copy assignment operator)}0%1 not viable: " + "function (the implicit copy assignment operator)|" + "constructor (inherited)}0%1 not viable: " "%select{%ordinal6|'this'}5 argument (%2) is in " "address space %3, but parameter must be in address space %4">; def note_ovl_candidate_bad_cvr_this : Note<"candidate " "%select{|function|||function||||" - "function (the implicit copy assignment operator)}0 not viable: " + "function (the implicit copy assignment operator)|}0 not viable: " "'this' argument has type %2, but method is not marked " "%select{const|restrict|const or restrict|volatile|const or volatile|" "volatile or restrict|const, volatile, or restrict}3">; @@ -1370,7 +1388,8 @@ def note_ovl_candidate_bad_cvr : Note<"candidate " "function |function |constructor |" "constructor (the implicit default constructor)|" "constructor (the implicit copy constructor)|" - "function (the implicit copy assignment operator)}0%1 not viable: " + "function (the implicit copy assignment operator)|" + "constructor (inherited)}0%1 not viable: " "%ordinal4 argument (%2) would lose " "%select{const|restrict|const and restrict|volatile|const and volatile|" "volatile and restrict|const, volatile, and restrict}3 qualifier" @@ -1380,7 +1399,8 @@ def note_ovl_candidate_bad_base_to_derived_conv : Note<"candidate " "function |function |constructor |" "constructor (the implicit default constructor)|" "constructor (the implicit copy constructor)|" - "function (the implicit copy assignment operator)}0%1" + "function (the implicit copy assignment operator)|" + "constructor (inherited)}0%1" " not viable: cannot %select{convert from|convert from|bind}2 " "%select{base class pointer|superclass|base class object of type}2 %3 to " "%select{derived class pointer|subclass|derived class reference}2 %4 for " diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 026930dabf..65b6c860f4 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2091,6 +2091,8 @@ public: bool IsTypeName, SourceLocation TypenameLoc); + bool CheckInheritedConstructorUsingDecl(UsingDecl *UD); + Decl *ActOnUsingDeclaration(Scope *CurScope, AccessSpecifier AS, bool HasUsingKeyword, @@ -2171,6 +2173,12 @@ public: void DefineImplicitDestructor(SourceLocation CurrentLocation, CXXDestructorDecl *Destructor); + /// \brief Declare all inherited constructors for the given class. + /// + /// \param ClassDecl The class declaration into which the inherited + /// constructors will be added. + void DeclareInheritedConstructors(CXXRecordDecl *ClassDecl); + /// \brief Declare the implicit copy constructor for the given class. /// /// \param S The scope of the class, which may be NULL if this is a diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index bce8b92c48..fba73f59d5 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1204,6 +1204,22 @@ bool CXXConstructorDecl::isSpecializationCopyingObject() const { return true; } +const CXXConstructorDecl *CXXConstructorDecl::getInheritedConstructor() const { + // Hack: we store the inherited constructor in the overridden method table + method_iterator It = begin_overridden_methods(); + if (It == end_overridden_methods()) + return 0; + + return cast<CXXConstructorDecl>(*It); +} + +void +CXXConstructorDecl::setInheritedConstructor(const CXXConstructorDecl *BaseCtor){ + // Hack: we store the inherited constructor in the overridden method table + assert(size_overridden_methods() == 0 && "Base ctor already set."); + addOverriddenMethod(BaseCtor); +} + CXXDestructorDecl * CXXDestructorDecl::Create(ASTContext &C, EmptyShell Empty) { return new (C) CXXDestructorDecl(0, DeclarationNameInfo(), diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 49be40c5e4..0f8f87143e 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -2787,6 +2787,15 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { DiagnoseHiddenVirtualMethods(Record, *M); } } + + // Declare inherited constructors. We do this eagerly here because: + // - The standard requires an eager diagnostic for conflicting inherited + // constructors from different classes. + // - The lazy declaration of the other implicit constructors is so as to not + // waste space and performance on classes that are not meant to be + // instantiated (e.g. meta-functions). This doesn't apply to classes that + // have inherited constructors. + DeclareInheritedConstructors(Record); } /// \brief Data used with FindHiddenVirtualMethod @@ -4122,8 +4131,7 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS, LookupQualifiedName(Previous, CurContext); } - NestedNameSpecifier *NNS = - static_cast<NestedNameSpecifier *>(SS.getScopeRep()); + NestedNameSpecifier *NNS = SS.getScopeRep(); // Check for invalid redeclarations. if (CheckUsingDeclRedeclaration(UsingLoc, IsTypeName, SS, IdentLoc, Previous)) @@ -4163,7 +4171,14 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS, return UD; } - // Look up the target name. + // Constructor inheriting using decls get special treatment. + if (NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName) { + if (CheckInheritedConstructorUsingDecl(UD)) + UD->setInvalidDecl(); + return UD; + } + + // Otherwise, look up the target name. LookupResult R(*this, NameInfo, LookupOrdinaryName); @@ -4227,6 +4242,42 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS, return UD; } +/// Additional checks for a using declaration referring to a constructor name. +bool Sema::CheckInheritedConstructorUsingDecl(UsingDecl *UD) { + if (UD->isTypeName()) { + // FIXME: Cannot specify typename when specifying constructor + return true; + } + + const Type *SourceType = UD->getTargetNestedNameDecl()->getAsType(); + assert(SourceType && + "Using decl naming constructor doesn't have type in scope spec."); + CXXRecordDecl *TargetClass = cast<CXXRecordDecl>(CurContext); + + // Check whether the named type is a direct base class. + CanQualType CanonicalSourceType = SourceType->getCanonicalTypeUnqualified(); + CXXRecordDecl::base_class_iterator BaseIt, BaseE; + for (BaseIt = TargetClass->bases_begin(), BaseE = TargetClass->bases_end(); + BaseIt != BaseE; ++BaseIt) { + CanQualType BaseType = BaseIt->getType()->getCanonicalTypeUnqualified(); + if (CanonicalSourceType == BaseType) + break; + } + + if (BaseIt == BaseE) { + // Did not find SourceType in the bases. + Diag(UD->getUsingLocation(), + diag::err_using_decl_constructor_not_in_direct_base) + << UD->getNameInfo().getSourceRange() + << QualType(SourceType, 0) << TargetClass; + return true; + } + + BaseIt->setInheritConstructors(); + + return false; +} + /// Checks that the given using declaration is not an invalid /// redeclaration. Note that this is checking only for the using decl /// itself, not for any ill-formedness among the UsingShadowDecls. @@ -4670,6 +4721,180 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation, MarkVTableUsed(CurrentLocation, ClassDecl); } +void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { + // We start with an initial pass over the base classes to collect those that + // inherit constructors from. If there are none, we can forgo all further + // processing. + typedef llvm::SmallVector<const RecordType *, 4> BasesVector; + BasesVector BasesToInheritFrom; + for (CXXRecordDecl::base_class_iterator BaseIt = ClassDecl->bases_begin(), + BaseE = ClassDecl->bases_end(); + BaseIt != BaseE; ++BaseIt) { + if (BaseIt->getInheritConstructors()) { + QualType Base = BaseIt->getType(); + if (Base->isDependentType()) { + // If we inherit constructors from anything that is dependent, just + // abort processing altogether. We'll get another chance for the + // instantiations. + return; + } + BasesToInheritFrom.push_back(Base->castAs<RecordType>()); + } + } + if (BasesToInheritFrom.empty()) + return; + + // Now collect the constructors that we already have in the current class. + // Those take precedence over inherited constructors. + // C++0x [class.inhctor]p3: [...] a constructor is implicitly declared [...] + // unless there is a user-declared constructor with the same signature in + // the class where the using-declaration appears. + llvm::SmallSet<const Type *, 8> ExistingConstructors; + for (CXXRecordDecl::ctor_iterator CtorIt = ClassDecl->ctor_begin(), + CtorE = ClassDecl->ctor_end(); + CtorIt != CtorE; ++CtorIt) { + ExistingConstructors.insert( + Context.getCanonicalType(CtorIt->getType()).getTypePtr()); + } + + Scope *S = getScopeForContext(ClassDecl); + DeclarationName CreatedCtorName = + Context.DeclarationNames.getCXXConstructorName( + ClassDecl->getTypeForDecl()->getCanonicalTypeUnqualified()); + + // Now comes the true work. + // First, we keep a map from constructor types to the base that introduced + // them. Needed for finding conflicting constructors. We also keep the + // actually inserted declarations in there, for pretty diagnostics. + typedef std::pair<CanQualType, CXXConstructorDecl *> ConstructorInfo; + typedef llvm::DenseMap<const Type *, ConstructorInfo> ConstructorToSourceMap; + ConstructorToSourceMap InheritedConstructors; + for (BasesVector::iterator BaseIt = BasesToInheritFrom.begin(), + BaseE = BasesToInheritFrom.end(); + BaseIt != BaseE; ++BaseIt) { + const RecordType *Base = *BaseIt; + CanQualType CanonicalBase = Base->getCanonicalTypeUnqualified(); + CXXRecordDecl *BaseDecl = cast<CXXRecordDecl>(Base->getDecl()); + for (CXXRecordDecl::ctor_iterator CtorIt = BaseDecl->ctor_begin(), + CtorE = BaseDecl->ctor_end(); + CtorIt != CtorE; ++CtorIt) { + // Find the using declaration for inheriting this base's constructors. + DeclarationName Name = + Context.DeclarationNames.getCXXConstructorName(CanonicalBase); + UsingDecl *UD = dyn_cast_or_null<UsingDecl>( + LookupSingleName(S, Name,SourceLocation(), LookupUsingDeclName)); + SourceLocation UsingLoc = UD ? UD->getLocation() : + ClassDecl->getLocation(); + + // C++0x [class.inhctor]p1: The candidate set of inherited constructors + // from the class X named in the using-declaration consists of actual + // constructors and notional constructors that result from the + // transformation of defaulted parameters as follows: + // - all non-template default constructors of X, and + // - for each non-template constructor of X that has at least one + // parameter with a default argument, the set of constructors that + // results from omitting any ellipsis parameter specification and + // successively omitting parameters with a default argument from the + // end of the parameter-type-list. + CXXConstructorDecl *BaseCtor = *CtorIt; + bool CanBeCopyOrMove = BaseCtor->isCopyOrMoveConstructor(); + const FunctionProtoType *BaseCtorType = + BaseCtor->getType()->getAs<FunctionProtoType>(); + + for (unsigned params = BaseCtor->getMinRequiredArguments(), + maxParams = BaseCtor->getNumParams(); + params <= maxParams; ++params) { + // Skip default constructors. They're never inherited. + if (params == 0) + continue; + // Skip copy and move constructors for the same reason. + if (CanBeCopyOrMove && params == 1) + continue; + + // Build up a function type for this particular constructor. + // FIXME: The working paper does not consider that the exception spec + // for the inheriting constructor might be larger than that of the + // source. This code doesn't yet, either. + const Type *NewCtorType; + if (params == maxParams) + NewCtorType = BaseCtorType; + else { + llvm::SmallVector<QualType, 16> Args; + for (unsigned i = 0; i < params; ++i) { + Args.push_back(BaseCtorType->getArgType(i)); + } + FunctionProtoType::ExtProtoInfo ExtInfo = + BaseCtorType->getExtProtoInfo(); + ExtInfo.Variadic = false; + NewCtorType = Context.getFunctionType(BaseCtorType->getResultType(), + Args.data(), params, ExtInfo) + .getTypePtr(); + } + const Type *CanonicalNewCtorType = + Context.getCanonicalType(NewCtorType); + + // Now that we have the type, first check if the class already has a + // constructor with this signature. + if (ExistingConstructors.count(CanonicalNewCtorType)) + continue; + + // Then we check if we have already declared an inherited constructor + // with this signature. + std::pair<ConstructorToSourceMap::iterator, bool> result = + InheritedConstructors.insert(std::make_pair( + CanonicalNewCtorType, + std::make_pair(CanonicalBase, (CXXConstructorDecl*)0))); + if (!result.second) { + // Already in the map. If it came from a different class, that's an + // error. Not if it's from the same. + CanQualType PreviousBase = result.first->second.first; + if (CanonicalBase != PreviousBase) { + const CXXConstructorDecl *PrevCtor = result.first->second.second; + const CXXConstructorDecl *PrevBaseCtor = + PrevCtor->getInheritedConstructor(); + assert(PrevBaseCtor && "Conflicting constructor was not inherited"); + + Diag(UsingLoc, diag::err_using_decl_constructor_conflict); + Diag(BaseCtor->getLocation(), + diag::note_using_decl_constructor_conflict_current_ctor); + Diag(PrevBaseCtor->getLocation(), + diag::note_using_decl_constructor_conflict_previous_ctor); + Diag(PrevCtor->getLocation(), + diag::note_using_decl_constructor_conflict_previous_using); + } + continue; + } + + // OK, we're there, now add the constructor. + // C++0x [class.inhctor]p8: [...] that would be performed by a + // user-writtern inline constructor [...] + DeclarationNameInfo DNI(CreatedCtorName, UsingLoc); + CXXConstructorDecl *NewCtor = CXXConstructorDecl::Create( + Context, ClassDecl, DNI, QualType(NewCtorType, 0), /*TInfo=*/0, + BaseCtor->isExplicit(), /*Inline=*/true, + /*ImplicitlyDeclared=*/true); + NewCtor->setAccess(BaseCtor->getAccess()); + + // Build up the parameter decls and add them. + llvm::SmallVector<ParmVarDecl *, 16> ParamDecls; + for (unsigned i = 0; i < params; ++i) { + ParamDecls.push_back(ParmVarDecl::Create(Context, NewCtor, UsingLoc, + /*IdentifierInfo=*/0, + BaseCtorType->getArgType(i), + /*TInfo=*/0, SC_None, + SC_None, /*DefaultArg=*/0)); + } + NewCtor->setParams(ParamDecls.data(), ParamDecls.size()); + NewCtor->setInheritedConstructor(BaseCtor); + + PushOnScopeChains(NewCtor, S, false); + ClassDecl->addDecl(NewCtor); + result.first->second.second = NewCtor; + } + } + } +} + CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { // C++ [class.dtor]p2: // If a class has no user-declared destructor, a destructor is @@ -5590,7 +5815,6 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( /*isInline=*/true, /*isImplicitlyDeclared=*/true); CopyConstructor->setAccess(AS_public); - CopyConstructor->setImplicit(); CopyConstructor->setTrivial(ClassDecl->hasTrivialCopyConstructor()); // Note that we have declared this constructor. diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 00cdca3bcf..916c5a198f 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -6201,7 +6201,8 @@ enum OverloadCandidateKind { oc_constructor_template, oc_implicit_default_constructor, oc_implicit_copy_constructor, - oc_implicit_copy_assignment + oc_implicit_copy_assignment, + oc_implicit_inherited_constructor }; OverloadCandidateKind ClassifyOverloadCandidate(Sema &S, @@ -6219,6 +6220,9 @@ OverloadCandidateKind ClassifyOverloadCandidate(Sema &S, if (!Ctor->isImplicit()) return isTemplate ? oc_constructor_template : oc_constructor; + if (Ctor->getInheritedConstructor()) + return oc_implicit_inherited_constructor; + return Ctor->isCopyConstructor() ? oc_implicit_copy_constructor : oc_implicit_default_constructor; } @@ -6237,6 +6241,16 @@ OverloadCandidateKind ClassifyOverloadCandidate(Sema &S, return isTemplate ? oc_function_template : oc_function; } +void MaybeEmitInheritedConstructorNote(Sema &S, FunctionDecl *Fn) { + const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(Fn); + if (!Ctor) return; + + Ctor = Ctor->getInheritedConstructor(); + if (!Ctor) return; + + S.Diag(Ctor->getLocation(), diag::note_ovl_candidate_inherited_constructor); +} + } // end anonymous namespace // Notes the location of an overload candidate. @@ -6245,6 +6259,7 @@ void Sema::NoteOverloadCandidate(FunctionDecl *Fn) { OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Fn, FnDesc); Diag(Fn->getLocation(), diag::note_ovl_candidate) << (unsigned) K << FnDesc; + MaybeEmitInheritedConstructorNote(*this, Fn); } /// Diagnoses an ambiguous conversion. The partial diagnostic is the @@ -6299,6 +6314,7 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) { << (unsigned) FnKind << FnDesc << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << ToTy << Name << I+1; + MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -6333,6 +6349,7 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) { << FromTy << FromQs.getAddressSpace() << ToQs.getAddressSpace() << (unsigned) isObjectArgument << I+1; + MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -6350,6 +6367,7 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) { << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy << (CVR - 1) << I+1; } + MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -6364,6 +6382,7 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) { << (unsigned) FnKind << FnDesc << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy << ToTy << (unsigned) isObjectArgument << I+1; + MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -6404,6 +6423,7 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) { << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << (BaseToDerivedConversion - 1) << FromTy << ToTy << I+1; + MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -6412,6 +6432,7 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) { << (unsigned) FnKind << FnDesc << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) << FromTy << ToTy << (unsigned) isObjectArgument << I+1; + MaybeEmitInheritedConstructorNote(S, Fn); } void DiagnoseArityMismatch(Sema &S, OverloadCandidate *Cand, @@ -6452,6 +6473,7 @@ void DiagnoseArityMismatch(Sema &S, OverloadCandidate *Cand, S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity) << (unsigned) FnKind << (Fn->getDescribedFunctionTemplate() != 0) << mode << modeCount << NumFormalArgs; + MaybeEmitInheritedConstructorNote(S, Fn); } /// Diagnose a failed template-argument deduction. @@ -6472,6 +6494,7 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, assert(ParamD && "no parameter found for incomplete deduction result"); S.Diag(Fn->getLocation(), diag::note_ovl_candidate_incomplete_deduction) << ParamD->getDeclName(); + MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -6496,6 +6519,7 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, S.Diag(Fn->getLocation(), diag::note_ovl_candidate_underqualified) << ParamD->getDeclName() << Arg << NonCanonParam; + MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -6514,6 +6538,7 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, << which << ParamD->getDeclName() << *Cand->DeductionFailure.getFirstArg() << *Cand->DeductionFailure.getSecondArg(); + MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -6536,6 +6561,7 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, diag::note_ovl_candidate_explicit_arg_mismatch_unnamed) << (index + 1); } + MaybeEmitInheritedConstructorNote(S, Fn); return; case Sema::TDK_TooManyArguments: @@ -6545,6 +6571,7 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, case Sema::TDK_InstantiationDepth: S.Diag(Fn->getLocation(), diag::note_ovl_candidate_instantiation_depth); + MaybeEmitInheritedConstructorNote(S, Fn); return; case Sema::TDK_SubstitutionFailure: { @@ -6556,6 +6583,7 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, *Args); S.Diag(Fn->getLocation(), diag::note_ovl_candidate_substitution_failure) << ArgString; + MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -6564,6 +6592,7 @@ void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, case Sema::TDK_NonDeducedMismatch: case Sema::TDK_FailedOverloadResolution: S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_deduction); + MaybeEmitInheritedConstructorNote(S, Fn); return; } } @@ -6592,6 +6621,7 @@ void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted) << FnKind << FnDesc << Fn->isDeleted(); + MaybeEmitInheritedConstructorNote(S, Fn); return; } @@ -6658,6 +6688,7 @@ void NoteSurrogateCandidate(Sema &S, OverloadCandidate *Cand) { S.Diag(Cand->Surrogate->getLocation(), diag::note_ovl_surrogate_cand) << FnType; + MaybeEmitInheritedConstructorNote(S, Cand->Surrogate); } void NoteBuiltinOperatorCandidate(Sema &S, diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index f8f281d999..68e2e79dde 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -4381,11 +4381,14 @@ ASTReader::ReadCXXBaseSpecifier(PerFileData &F, bool isVirtual = static_cast<bool>(Record[Idx++]); bool isBaseOfClass = static_cast<bool>(Record[Idx++]); AccessSpecifier AS = static_cast<AccessSpecifier>(Record[Idx++]); + bool inheritConstructors = static_cast<bool>(Record[Idx++]); TypeSourceInfo *TInfo = GetTypeSourceInfo(F, Record, Idx); SourceRange Range = ReadSourceRange(F, Record, Idx); SourceLocation EllipsisLoc = ReadSourceLocation(F, Record, Idx); - return CXXBaseSpecifier(Range, isVirtual, isBaseOfClass, AS, TInfo, + CXXBaseSpecifier Result(Range, isVirtual, isBaseOfClass, AS, TInfo, EllipsisLoc); + Result.setInheritConstructors(inheritConstructors); + return Result; } std::pair<CXXCtorInitializer **, unsigned> diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index b85aac1cc9..9ffcc1d9d3 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -3304,6 +3304,7 @@ void ASTWriter::AddCXXBaseSpecifier(const CXXBaseSpecifier &Base, Record.push_back(Base.isVirtual()); Record.push_back(Base.isBaseOfClass()); Record.push_back(Base.getAccessSpecifierAsWritten()); + Record.push_back(Base.getInheritConstructors()); AddTypeSourceInfo(Base.getTypeSourceInfo(), Record); AddSourceRange(Base.getSourceRange(), Record); AddSourceLocation(Base.isPackExpansion()? Base.getEllipsisLoc() diff --git a/test/CXX/special/class.inhctor/elsewhere.cpp b/test/CXX/special/class.inhctor/elsewhere.cpp new file mode 100644 index 0000000000..82944d65df --- /dev/null +++ b/test/CXX/special/class.inhctor/elsewhere.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +// Tests related to constructor inheriting, but not specified in [class.inhctor] + +// [namespace.udecl]p8: +// A using-declaration for a class member shall be a member-declaration. + +struct B1 { + B1(int); +}; + +using B1::B1; // expected-error {{using declaration can not refer to class member}} + +// C++0x [namespace.udecl]p10: +// A using-declaration is a declaration and can therefore be used repeatedly +// where (and only where) multiple declarations are allowed. + +struct I1 : B1 { + using B1::B1; // expected-note {{previous using declaration}} + using B1::B1; // expected-error {{redeclaration of using decl}} +}; + +// C++0x [namespace.udecl]p3: +// In a using declaration used as a member-declaration, the nested-name- +// specifier shall name a base class of the class being defined. +// If such a using-declaration names a constructor, the nested-name-specifier +// shall name a direct base class of the class being defined. + +struct D1 : I1 { + using B1::B1; // expected-error {{'B1' is not a direct base of 'D1', can not inherit constructors}} +}; diff --git a/test/CXX/special/class.inhctor/p3.cpp b/test/CXX/special/class.inhctor/p3.cpp new file mode 100644 index 0000000000..021f701ab4 --- /dev/null +++ b/test/CXX/special/class.inhctor/p3.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +struct B1 { + B1(int); + B1(int, int); +}; +struct D1 : B1 { + using B1::B1; +}; +D1 d1a(1), d1b(1, 1); + +D1 fd1() { return 1; } + +struct B2 { + explicit B2(int, int = 0, int = 0); +}; +struct D2 : B2 { // expected-note {{candidate constructor}} + using B2::B2; +}; +D2 d2a(1), d2b(1, 1), d2c(1, 1, 1); + +D2 fd2() { return 1; } // expected-error {{no viable conversion}} + +struct B3 { + B3(void*); // expected-note {{inherited from here}} +}; +struct D3 : B3 { // expected-note {{candidate constructor}} + using B3::B3; // expected-note {{candidate constructor (inherited)}} +}; +D3 fd3() { return 1; } // expected-error {{no viable conversion}} diff --git a/test/CXX/special/class.inhctor/p7.cpp b/test/CXX/special/class.inhctor/p7.cpp new file mode 100644 index 0000000000..3ad761f08b --- /dev/null +++ b/test/CXX/special/class.inhctor/p7.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +// Straight from the standard +struct B1 { + B1(int); // expected-note {{previous constructor}} +}; +struct B2 { + B2(int); // expected-note {{conflicting constructor}} +}; +struct D1 : B1, B2 { + using B1::B1; // expected-note {{inherited here}} + using B2::B2; // expected-error {{already inherited constructor with the same signature}} +}; +struct D2 : B1, B2 { + using B1::B1; + using B2::B2; + D2(int); +}; |