From c2bc61b00656b573e146ceaff46853819ab2c4f9 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 18 Mar 2013 21:12:30 +0000 Subject: [PATCH] Bring inheriting constructor implementation up-to-date with current defect reports, and implement implicit definition of inheriting constructors. Remaining missing features: inheriting constructor templates, implicit exception specifications for inheriting constructors, inheriting constructors from dependent bases. llvm-svn: 177320 --- clang/include/clang/AST/Type.h | 3 + clang/include/clang/Basic/DiagnosticSemaKinds.td | 25 ++- clang/include/clang/Sema/Sema.h | 15 +- clang/lib/Sema/SemaDeclCXX.cpp | 247 ++++++++++++++------- clang/lib/Sema/SemaExpr.cpp | 3 + clang/lib/Sema/SemaInit.cpp | 9 +- clang/test/CXX/special/class.inhctor/elsewhere.cpp | 20 +- clang/test/CXX/special/class.inhctor/p1.cpp | 31 +++ clang/test/CXX/special/class.inhctor/p2.cpp | 87 ++++++++ clang/test/CXX/special/class.inhctor/p3.cpp | 12 +- clang/test/CXX/special/class.inhctor/p4.cpp | 70 ++++++ clang/test/CXX/special/class.inhctor/p7.cpp | 12 +- clang/test/CXX/special/class.inhctor/p8.cpp | 21 ++ clang/test/CodeGenCXX/inheriting-constructor.cpp | 11 +- 14 files changed, 446 insertions(+), 120 deletions(-) create mode 100644 clang/test/CXX/special/class.inhctor/p1.cpp create mode 100644 clang/test/CXX/special/class.inhctor/p2.cpp create mode 100644 clang/test/CXX/special/class.inhctor/p4.cpp create mode 100644 clang/test/CXX/special/class.inhctor/p8.cpp diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 24c3459..a700720 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2863,6 +2863,9 @@ public: assert(i < NumArgs && "Invalid argument number!"); return arg_type_begin()[i]; } + ArrayRef getArgTypes() const { + return ArrayRef(arg_type_begin(), arg_type_end()); + } ExtProtoInfo getExtProtoInfo() const { ExtProtoInfo EPI; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index bd3d1b4..6556c12 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -254,19 +254,20 @@ def note_using_decl_constructor_conflict_previous_ctor : Note< "previous constructor">; def note_using_decl_constructor_conflict_previous_using : Note< "previously inherited here">; +def warn_using_decl_constructor_ellipsis : Warning< + "inheriting constructor does not inherit ellipsis">, + InGroup>; +def note_using_decl_constructor_ellipsis : Note< + "constructor declared with ellipsis 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< "using declaration can not refer to namespace">; def err_using_decl_constructor : Error< "using declaration can not refer to a constructor">; -def err_using_decl_constructor_unsupported : Error< - "inheriting constructors are not supported">; -// FIXME: Replace the above error with this warning if support for -// inheriting constructors is implemented. -//def warn_cxx98_compat_using_decl_constructor : Warning< -// "inheriting constructors are incompatible with C++98">, -// InGroup, DefaultIgnore; +def warn_cxx98_compat_using_decl_constructor : Warning< + "inheriting constructors are incompatible with C++98">, + InGroup, DefaultIgnore; def err_using_decl_destructor : Error< "using declaration can not refer to a destructor">; def err_using_decl_template_id : Error< @@ -1104,9 +1105,11 @@ def note_member_synthesized_at : Note< "implicit default %select{constructor|copy constructor|move constructor|copy " "assignment operator|move assignment operator|destructor}0 for %1 first " "required here">; +def note_inhctor_synthesized_at : Note< + "inheriting constructor for %0 first required here">; def err_missing_default_ctor : Error< - "%select{|implicit default }0constructor for %1 must explicitly initialize " - "the %select{base class|member}2 %3 which does not have a default " + "%select{|implicit default |inheriting }0constructor for %1 must explicitly " + "initialize the %select{base class|member}2 %3 which does not have a default " "constructor">; def err_illegal_union_or_anon_struct_member : Error< @@ -2274,8 +2277,8 @@ def err_uninitialized_member_for_assign : Error< "non-static %select{reference|const}1 member %2 can't use default " "assignment operator">; def err_uninitialized_member_in_ctor : Error< - "%select{|implicit default }0constructor for %1 must explicitly initialize " - "the %select{reference|const}2 member %3">; + "%select{|implicit default |inheriting }0constructor for %1 must explicitly " + "initialize the %select{reference|const}2 member %3">; def err_default_arg_makes_ctor_special : Error< "addition of default argument on redeclaration makes this constructor a " "%select{default|copy|move}0 constructor">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0f51939..e8b4f55 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3597,6 +3597,11 @@ public: ImplicitExceptionSpecification ComputeDefaultedDtorExceptionSpec(CXXMethodDecl *MD); + /// \brief Determine what sort of exception specification an inheriting + /// constructor of a class will have. + ImplicitExceptionSpecification + ComputeInheritingCtorExceptionSpec(CXXMethodDecl *MD); + /// \brief Evaluate the implicit exception specification for a defaulted /// special member function. void EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD); @@ -3649,11 +3654,15 @@ public: void AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl, CXXDestructorDecl *Destructor); - /// \brief Declare all inherited constructors for the given class. + /// \brief Declare all inheriting constructors for the given class. /// - /// \param ClassDecl The class declaration into which the inherited + /// \param ClassDecl The class declaration into which the inheriting /// constructors will be added. - void DeclareInheritedConstructors(CXXRecordDecl *ClassDecl); + void DeclareInheritingConstructors(CXXRecordDecl *ClassDecl); + + /// \brief Define the specified inheriting constructor. + void DefineInheritingConstructor(SourceLocation UseLoc, + CXXConstructorDecl *Constructor); /// \brief Declare the implicit copy constructor for the given class. /// diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index f66509d..33b701a 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2642,9 +2642,10 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, } // Create a static_cast\(expr). -static Expr *CastForMoving(Sema &SemaRef, Expr *E) { - QualType ExprType = E->getType(); - QualType TargetType = SemaRef.Context.getRValueReferenceType(ExprType); +static Expr *CastForMoving(Sema &SemaRef, Expr *E, QualType T = QualType()) { + if (T.isNull()) T = E->getType(); + QualType TargetType = SemaRef.BuildReferenceType( + T, /*SpelledAsLValue*/false, SourceLocation(), DeclarationName()); SourceLocation ExprLoc = E->getLocStart(); TypeSourceInfo *TargetLoc = SemaRef.Context.getTrivialTypeSourceInfo( TargetType, ExprLoc); @@ -2659,7 +2660,8 @@ static Expr *CastForMoving(Sema &SemaRef, Expr *E) { enum ImplicitInitializerKind { IIK_Default, IIK_Copy, - IIK_Move + IIK_Move, + IIK_Inherit }; static bool @@ -2675,6 +2677,35 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, ExprResult BaseInit; switch (ImplicitInitKind) { + case IIK_Inherit: { + const CXXRecordDecl *Inherited = + Constructor->getInheritedConstructor()->getParent(); + const CXXRecordDecl *Base = BaseSpec->getType()->getAsCXXRecordDecl(); + if (Base && Inherited->getCanonicalDecl() == Base->getCanonicalDecl()) { + // C++11 [class.inhctor]p8: + // Each expression in the expression-list is of the form + // static_cast(p), where p is the name of the corresponding + // constructor parameter and T is the declared type of p. + SmallVector Args; + for (unsigned I = 0, E = Constructor->getNumParams(); I != E; ++I) { + ParmVarDecl *PD = Constructor->getParamDecl(I); + ExprResult ArgExpr = + SemaRef.BuildDeclRefExpr(PD, PD->getType().getNonReferenceType(), + VK_LValue, SourceLocation()); + if (ArgExpr.isInvalid()) + return true; + Args.push_back(CastForMoving(SemaRef, ArgExpr.take(), PD->getType())); + } + + InitializationKind InitKind = InitializationKind::CreateDirect( + Constructor->getLocation(), SourceLocation(), SourceLocation()); + InitializationSequence InitSeq(SemaRef, InitEntity, InitKind, + Args.data(), Args.size()); + BaseInit = InitSeq.Perform(SemaRef, InitEntity, InitKind, Args); + break; + } + } + // Fall through. case IIK_Default: { InitializationKind InitKind = InitializationKind::CreateDefault(Constructor->getLocation()); @@ -2897,7 +2928,8 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, return false; } - assert(ImplicitInitKind == IIK_Default && "Unhandled implicit init kind!"); + assert((ImplicitInitKind == IIK_Default || ImplicitInitKind == IIK_Inherit) && + "Unhandled implicit init kind!"); QualType FieldBaseElementType = SemaRef.Context.getBaseElementType(Field->getType()); @@ -2988,6 +3020,8 @@ struct BaseAndFieldInfo { IIK = IIK_Copy; else if (Generated && Ctor->isMoveConstructor()) IIK = IIK_Move; + else if (Ctor->getInheritedConstructor()) + IIK = IIK_Inherit; else IIK = IIK_Default; } @@ -2999,6 +3033,7 @@ struct BaseAndFieldInfo { return true; case IIK_Default: + case IIK_Inherit: return false; } @@ -3226,7 +3261,7 @@ bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors, // If we're not generating the implicit copy/move constructor, then we'll // handle anonymous struct/union fields based on their individual // indirect fields. - if (F->isAnonymousStructOrUnion() && Info.IIK == IIK_Default) + if (F->isAnonymousStructOrUnion() && !Info.isImplicitCopyOrMove()) continue; if (CollectFieldInitializer(*this, Info, F)) @@ -3235,7 +3270,7 @@ bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors, } // Beyond this point, we only consider default initialization. - if (Info.IIK != IIK_Default) + if (Info.isImplicitCopyOrMove()) continue; if (IndirectFieldDecl *F = dyn_cast(*Mem)) { @@ -4062,14 +4097,14 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { } } - // Declare inherited constructors. We do this eagerly here because: - // - The standard requires an eager diagnostic for conflicting inherited + // Declare inheriting constructors. We do this eagerly here because: + // - The standard requires an eager diagnostic for conflicting inheriting // 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); + // have inheriting constructors. + DeclareInheritingConstructors(Record); } /// Is the special member function which would be selected to perform the @@ -4185,7 +4220,9 @@ computeImplicitExceptionSpec(Sema &S, SourceLocation Loc, CXXMethodDecl *MD) { case Sema::CXXInvalid: break; } - llvm_unreachable("only special members have implicit exception specs"); + assert(cast(MD)->getInheritedConstructor() && + "only special members have implicit exception specs"); + return S.ComputeInheritingCtorExceptionSpec(cast(MD)); } static void @@ -4194,10 +4231,7 @@ updateExceptionSpec(Sema &S, FunctionDecl *FD, const FunctionProtoType *FPT, FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); ExceptSpec.getEPI(EPI); const FunctionProtoType *NewFPT = cast( - S.Context.getFunctionType(FPT->getResultType(), - ArrayRef(FPT->arg_type_begin(), - FPT->getNumArgs()), - EPI)); + S.Context.getFunctionType(FPT->getResultType(), FPT->getArgTypes(), EPI)); FD->setType(QualType(NewFPT, 0)); } @@ -5644,10 +5678,7 @@ QualType Sema::CheckConstructorDeclarator(Declarator &D, QualType R, EPI.TypeQuals = 0; EPI.RefQualifier = RQ_None; - return Context.getFunctionType(Context.VoidTy, - ArrayRef(Proto->arg_type_begin(), - Proto->getNumArgs()), - EPI); + return Context.getFunctionType(Context.VoidTy, Proto->getArgTypes(), EPI); } /// CheckConstructor - Checks a fully-formed constructor for @@ -6519,9 +6550,7 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S, // C++11 inheriting constructors. Diag(Name.getLocStart(), getLangOpts().CPlusPlus11 ? - // FIXME: Produce warn_cxx98_compat_using_decl_constructor - // instead once inheriting constructors work. - diag::err_using_decl_constructor_unsupported : + diag::warn_cxx98_compat_using_decl_constructor : diag::err_using_decl_constructor) << SS.getRange(); @@ -6545,7 +6574,7 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S, if (!TargetName) return 0; - // Warn about using declarations. + // Warn about access declarations. // TODO: store that the declaration was written without 'using' and // talk about access decls instead of using decls in the // diagnostics. @@ -7471,6 +7500,13 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(SourceLocation Loc, return ExceptSpec; } +Sema::ImplicitExceptionSpecification +Sema::ComputeInheritingCtorExceptionSpec(CXXMethodDecl *MD) { + ImplicitExceptionSpecification ExceptSpec(*this); + // FIXME: Compute the exception spec. + return ExceptSpec; +} + namespace { /// RAII object to register a special member as being currently declared. struct DeclaringSpecialMember { @@ -7597,7 +7633,7 @@ void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) { CheckDelayedExplicitlyDefaultedMemberExceptionSpecs(); } -void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { +void Sema::DeclareInheritingConstructors(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. @@ -7612,6 +7648,8 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { // If we inherit constructors from anything that is dependent, just // abort processing altogether. We'll get another chance for the // instantiations. + // FIXME: We need to ensure that any call to a constructor of this class + // is considered instantiation-dependent in this case. return; } BasesToInheritFrom.push_back(Base->castAs()); @@ -7620,18 +7658,19 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { if (BasesToInheritFrom.empty()) return; + // FIXME: Constructor templates. + // 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 [...] + // C++11 [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 ExistingConstructors; for (CXXRecordDecl::ctor_iterator CtorIt = ClassDecl->ctor_begin(), CtorE = ClassDecl->ctor_end(); - CtorIt != CtorE; ++CtorIt) { + CtorIt != CtorE; ++CtorIt) ExistingConstructors.insert( Context.getCanonicalType(CtorIt->getType()).getTypePtr()); - } DeclarationName CreatedCtorName = Context.DeclarationNames.getCXXConstructorName( @@ -7663,62 +7702,71 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { 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 + // C++11 [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 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. + // end of the parameter-type-list, and + // FIXME: ...also constructor templates. CXXConstructorDecl *BaseCtor = *CtorIt; bool CanBeCopyOrMove = BaseCtor->isCopyOrMoveConstructor(); const FunctionProtoType *BaseCtorType = BaseCtor->getType()->getAs(); - for (unsigned params = BaseCtor->getMinRequiredArguments(), - maxParams = BaseCtor->getNumParams(); - params <= maxParams; ++params) { + // Determine whether this would be a copy or move constructor for the + // derived class. + if (BaseCtorType->getNumArgs() >= 1 && + BaseCtorType->getArgType(0)->isReferenceType() && + Context.hasSameUnqualifiedType( + BaseCtorType->getArgType(0)->getPointeeType(), + Context.getTagDeclType(ClassDecl))) + CanBeCopyOrMove = true; + + ArrayRef ArgTypes(BaseCtorType->getArgTypes()); + FunctionProtoType::ExtProtoInfo EPI = BaseCtorType->getExtProtoInfo(); + // Core issue (no number yet): the ellipsis is always discarded. + if (EPI.Variadic) { + Diag(UsingLoc, diag::warn_using_decl_constructor_ellipsis); + Diag(BaseCtor->getLocation(), + diag::note_using_decl_constructor_ellipsis); + EPI.Variadic = false; + } + + for (unsigned Params = BaseCtor->getMinRequiredArguments(), + MaxParams = BaseCtor->getNumParams(); + Params <= MaxParams; ++Params) { // Skip default constructors. They're never inherited. - if (params == 0) + if (Params == 0) continue; - // Skip copy and move constructors for the same reason. - if (CanBeCopyOrMove && params == 1) + + // Skip copy and move constructors for both base and derived class + // 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. When it does, this code will - // need to be delayed until after exception specifications and in-class - // member initializers are attached. - const Type *NewCtorType; - if (params == maxParams) - NewCtorType = BaseCtorType; - else { - SmallVector 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, ExtInfo) - .getTypePtr(); - } + QualType NewCtorType = + Context.getFunctionType(Context.VoidTy, ArgTypes.slice(0, Params), + EPI); const Type *CanonicalNewCtorType = - Context.getCanonicalType(NewCtorType); + Context.getCanonicalType(NewCtorType).getTypePtr(); - // Now that we have the type, first check if the class already has a - // constructor with this signature. + // C++11 [class.inhctor]p3: + // ... a constructor is implicitly declared with the same constructor + // characteristics unless there is a user-declared constructor with + // the same signature in the class where the using-declaration appears if (ExistingConstructors.count(CanonicalNewCtorType)) continue; - // Then we check if we have already declared an inherited constructor - // with this signature. + // C++11 [class.inhctor]p7: + // If two using-declarations declare inheriting constructors with the + // same signature, the program is ill-formed std::pair result = InheritedConstructors.insert(std::make_pair( CanonicalNewCtorType, @@ -7740,35 +7788,47 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { diag::note_using_decl_constructor_conflict_previous_ctor); Diag(PrevCtor->getLocation(), diag::note_using_decl_constructor_conflict_previous_using); + } else { + // Core issue (no number): if the same inheriting constructor is + // produced by multiple base class constructors from the same base + // class, the inheriting constructor is defined as deleted. + result.first->second.second->setDeletedAsWritten(); } continue; } // OK, we're there, now add the constructor. - // C++0x [class.inhctor]p8: [...] that would be performed by a - // user-written inline constructor [...] DeclarationNameInfo DNI(CreatedCtorName, UsingLoc); CXXConstructorDecl *NewCtor = CXXConstructorDecl::Create( - Context, ClassDecl, UsingLoc, DNI, QualType(NewCtorType, 0), + Context, ClassDecl, UsingLoc, DNI, NewCtorType, /*TInfo=*/0, BaseCtor->isExplicit(), /*Inline=*/true, - /*ImplicitlyDeclared=*/true, - // FIXME: Due to a defect in the standard, we treat inherited - // constructors as constexpr even if that makes them ill-formed. - /*Constexpr=*/BaseCtor->isConstexpr()); + /*ImplicitlyDeclared=*/true, /*Constexpr=*/BaseCtor->isConstexpr()); NewCtor->setAccess(BaseCtor->getAccess()); + // Build an unevaluated exception specification for this constructor. + EPI.ExceptionSpecType = EST_Unevaluated; + EPI.ExceptionSpecDecl = NewCtor; + NewCtor->setType(Context.getFunctionType(Context.VoidTy, + ArgTypes.slice(0, Params), + EPI)); + // Build up the parameter decls and add them. SmallVector ParamDecls; - for (unsigned i = 0; i < params; ++i) { - ParamDecls.push_back(ParmVarDecl::Create(Context, NewCtor, - UsingLoc, UsingLoc, - /*IdentifierInfo=*/0, - BaseCtorType->getArgType(i), - /*TInfo=*/0, SC_None, - SC_None, /*DefaultArg=*/0)); + for (unsigned i = 0; i < Params; ++i) { + ParmVarDecl *PD = ParmVarDecl::Create(Context, NewCtor, + UsingLoc, UsingLoc, + /*IdentifierInfo=*/0, + BaseCtorType->getArgType(i), + /*TInfo=*/0, SC_None, + SC_None, /*DefaultArg=*/0); + PD->setScopeInfo(0, i); + PD->setImplicit(); + ParamDecls.push_back(PD); } NewCtor->setParams(ParamDecls); NewCtor->setInheritedConstructor(BaseCtor); + if (BaseCtor->isDeleted()) + NewCtor->setDeletedAsWritten(); ClassDecl->addDecl(NewCtor); result.first->second.second = NewCtor; @@ -7777,6 +7837,35 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) { } } +void Sema::DefineInheritingConstructor(SourceLocation CurrentLocation, + CXXConstructorDecl *Constructor) { + CXXRecordDecl *ClassDecl = Constructor->getParent(); + assert(Constructor->getInheritedConstructor() && + !Constructor->doesThisDeclarationHaveABody() && + !Constructor->isDeleted()); + + SynthesizedFunctionScope Scope(*this, Constructor); + DiagnosticErrorTrap Trap(Diags); + if (SetCtorInitializers(Constructor, /*AnyErrors=*/false) || + Trap.hasErrorOccurred()) { + Diag(CurrentLocation, diag::note_inhctor_synthesized_at) + << Context.getTagDeclType(ClassDecl); + Constructor->setInvalidDecl(); + return; + } + + SourceLocation Loc = Constructor->getLocation(); + Constructor->setBody(new (Context) CompoundStmt(Loc)); + + Constructor->setUsed(); + MarkVTableUsed(CurrentLocation, ClassDecl); + + if (ASTMutationListener *L = getASTMutationListener()) { + L->CompletedImplicitDefinition(Constructor); + } +} + + Sema::ImplicitExceptionSpecification Sema::ComputeDefaultedDtorExceptionSpec(CXXMethodDecl *MD) { CXXRecordDecl *ClassDecl = MD->getParent(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 3af64bb..f52621a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -10501,6 +10501,9 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) { if (!Constructor->isUsed(false)) DefineImplicitMoveConstructor(Loc, Constructor); } + } else if (Constructor->getInheritedConstructor()) { + if (!Constructor->isUsed(false)) + DefineInheritingConstructor(Loc, Constructor); } MarkVTableUsed(Loc, Constructor->getParent()); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index baa5243..2ddabbd 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -5825,7 +5825,8 @@ bool InitializationSequence::Diagnose(Sema &S, = cast(S.CurContext); if (Entity.getKind() == InitializedEntity::EK_Base) { S.Diag(Kind.getLocation(), diag::err_missing_default_ctor) - << Constructor->isImplicit() + << (Constructor->getInheritedConstructor() ? 2 : + Constructor->isImplicit() ? 1 : 0) << S.Context.getTypeDeclType(Constructor->getParent()) << /*base=*/0 << Entity.getType(); @@ -5837,7 +5838,8 @@ bool InitializationSequence::Diagnose(Sema &S, << S.Context.getTagDeclType(BaseDecl); } else { S.Diag(Kind.getLocation(), diag::err_missing_default_ctor) - << Constructor->isImplicit() + << (Constructor->getInheritedConstructor() ? 2 : + Constructor->isImplicit() ? 1 : 0) << S.Context.getTypeDeclType(Constructor->getParent()) << /*member=*/1 << Entity.getName(); @@ -5898,7 +5900,8 @@ bool InitializationSequence::Diagnose(Sema &S, // initialized. CXXConstructorDecl *Constructor = cast(S.CurContext); S.Diag(Kind.getLocation(), diag::err_uninitialized_member_in_ctor) - << Constructor->isImplicit() + << (Constructor->getInheritedConstructor() ? 2 : + Constructor->isImplicit() ? 1 : 0) << S.Context.getTypeDeclType(Constructor->getParent()) << /*const=*/1 << Entity.getName(); diff --git a/clang/test/CXX/special/class.inhctor/elsewhere.cpp b/clang/test/CXX/special/class.inhctor/elsewhere.cpp index 09fd3d5..184e902 100644 --- a/clang/test/CXX/special/class.inhctor/elsewhere.cpp +++ b/clang/test/CXX/special/class.inhctor/elsewhere.cpp @@ -9,49 +9,49 @@ struct B1 { B1(int); }; -using B1::B1; // expected-error {{using declaration can not refer to class member}} expected-error {{not supported}} +using B1::B1; // expected-error {{using declaration can not refer to class member}} -// C++0x [namespace.udecl]p10: +// C++11 [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}} expected-error {{not supported}} - using B1::B1; // expected-error {{redeclaration of using decl}} expected-error {{not supported}} + using B1::B1; // expected-note {{previous using declaration}} + using B1::B1; // expected-error {{redeclaration of using decl}} }; -// C++0x [namespace.udecl]p3: +// C++11 [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}} expected-error {{not supported}} + using B1::B1; // expected-error {{'B1' is not a direct base of 'D1', can not inherit constructors}} }; template struct A {}; template struct B : A, A { - using A::A; // expected-error {{'A::', which is not a base class of 'B'}} expected-error {{not supported}} + using A::A; // expected-error {{'A::', which is not a base class of 'B'}} }; B bb; B bc; B bd; // expected-note {{here}} template struct C : A { - using A::A; // expected-error {{'A::', which is not a base class of 'C'}} expected-error {{not supported}} + using A::A; // expected-error {{'A::', which is not a base class of 'C'}} }; C cb; C cc; // expected-note {{here}} template struct D : A {}; template struct E : D { - using A::A; // expected-error {{'A' is not a direct base of 'E', can not inherit}} expected-error {{not supported}} + using A::A; // expected-error {{'A' is not a direct base of 'E', can not inherit}} }; E eb; // expected-note {{here}} template struct F : D { - using A::A; // expected-error {{'A' is not a direct base of 'F'}} expected-error {{not supported}} + using A::A; // expected-error {{'A' is not a direct base of 'F'}} }; F fb; // expected-note {{here}} diff --git a/clang/test/CXX/special/class.inhctor/p1.cpp b/clang/test/CXX/special/class.inhctor/p1.cpp new file mode 100644 index 0000000..57e9150 --- /dev/null +++ b/clang/test/CXX/special/class.inhctor/p1.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s +// Per a core issue (no number yet), an ellipsis is always dropped. +struct A { + A(...); // expected-note {{here}} + A(int = 0, int = 0, int = 0, int = 0, ...); // expected-note 5{{here}} + A(int = 0, int = 0, ...); // expected-note {{here}} +}; + +struct B : A { // expected-note 3{{candidate}} + using A::A; // expected-warning 3{{inheriting constructor does not inherit ellipsis}} expected-note 4{{candidate}} expected-note 2{{deleted}} +}; + +B b0{}; +// expected-error@-1 {{call to implicitly-deleted default constructor}} +// expected-note@9 {{default constructor of 'B' is implicitly deleted because base class 'A' has multiple default constructors}} + +B b1{1}; +// FIXME: explain why the inheriting constructor was deleted +// expected-error@-2 {{call to implicitly-deleted function of 'B'}} + +B b2{1,2}; +// expected-error@-1 {{call to implicitly-deleted function of 'B'}} + +B b3{1,2,3}; +// ok + +B b4{1,2,3,4}; +// ok + +B b5{1,2,3,4,5}; +// expected-error@-1 {{no matching constructor for initialization of 'B'}} diff --git a/clang/test/CXX/special/class.inhctor/p2.cpp b/clang/test/CXX/special/class.inhctor/p2.cpp new file mode 100644 index 0000000..e426738 --- /dev/null +++ b/clang/test/CXX/special/class.inhctor/p2.cpp @@ -0,0 +1,87 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +template struct X {}; + +// Constructor characteristics are: +// - the template parameter list [FIXME] +// - the parameter-type-list +// - absence or presence of explicit +// - absence or presence of constexpr +struct A { + A(X<0>) {} // expected-note 2{{here}} + constexpr A(X<1>) {} + explicit A(X<2>) {} // expected-note 3{{here}} + explicit constexpr A(X<3>) {} // expected-note 2{{here}} +}; + +A a0 { X<0>{} }; +A a0i = { X<0>{} }; +constexpr A a0c { X<0>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}} +constexpr A a0ic = { X<0>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}} + +A a1 { X<1>{} }; +A a1i = { X<1>{} }; +constexpr A a1c { X<1>{} }; +constexpr A a1ic = { X<1>{} }; + +A a2 { X<2>{} }; +A a2i = { X<2>{} }; // expected-error {{constructor is explicit}} +constexpr A a2c { X<2>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}} +constexpr A a2ic = { X<2>{} }; // expected-error {{constructor is explicit}} + +A a3 { X<3>{} }; +A a3i = { X<3>{} }; // expected-error {{constructor is explicit}} +constexpr A a3c { X<3>{} }; +constexpr A a3ic = { X<3>{} }; // expected-error {{constructor is explicit}} + + +struct B : A { + using A::A; // expected-note 7{{here}} +}; + +B b0 { X<0>{} }; +B b0i = { X<0>{} }; +constexpr B b0c { X<0>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}} +constexpr B b0ic = { X<0>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}} + +B b1 { X<1>{} }; +B b1i = { X<1>{} }; +constexpr B b1c { X<1>{} }; +constexpr B b1ic = { X<1>{} }; + +B b2 { X<2>{} }; +B b2i = { X<2>{} }; // expected-error {{constructor is explicit}} +constexpr B b2c { X<2>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}} +constexpr B b2ic = { X<2>{} }; // expected-error {{constructor is explicit}} + +B b3 { X<3>{} }; +B b3i = { X<3>{} }; // expected-error {{constructor is explicit}} +constexpr B b3c { X<3>{} }; +constexpr B b3ic = { X<3>{} }; // expected-error {{constructor is explicit}} + + +// 'constexpr' is OK even if the constructor doesn't obey the constraints. +struct NonLiteral { NonLiteral(); }; +struct NonConstexpr { NonConstexpr(); constexpr NonConstexpr(int); }; // expected-note {{here}} +struct Constexpr { constexpr Constexpr(int) {} }; + +struct BothNonLiteral : NonLiteral, Constexpr { using Constexpr::Constexpr; }; // expected-note {{base class 'NonLiteral' of non-literal type}} +constexpr BothNonLiteral bothNL{42}; // expected-error {{constexpr variable cannot have non-literal type 'const BothNonLiteral'}} + +struct BothNonConstexpr : NonConstexpr, Constexpr { using Constexpr::Constexpr; }; // expected-note {{non-constexpr constructor 'NonConstexpr}} +constexpr BothNonConstexpr bothNC{42}; // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'BothNonConstexpr(42)'}} + + +struct ConstexprEval { + constexpr ConstexprEval(int a, const char *p) : k(p[a]) {} + char k; +}; +struct ConstexprEval2 { + char k2 = 'x'; +}; +struct ConstexprEval3 : ConstexprEval, ConstexprEval2 { + using ConstexprEval::ConstexprEval; +}; +constexpr ConstexprEval3 ce{4, "foobar"}; +static_assert(ce.k == 'a', ""); +static_assert(ce.k2 == 'x', ""); diff --git a/clang/test/CXX/special/class.inhctor/p3.cpp b/clang/test/CXX/special/class.inhctor/p3.cpp index d7093fb..f71ab16 100644 --- a/clang/test/CXX/special/class.inhctor/p3.cpp +++ b/clang/test/CXX/special/class.inhctor/p3.cpp @@ -5,7 +5,7 @@ struct B1 { B1(int, int); }; struct D1 : B1 { - using B1::B1; // expected-error {{not supported}} + using B1::B1; }; D1 d1a(1), d1b(1, 1); @@ -15,7 +15,7 @@ struct B2 { explicit B2(int, int = 0, int = 0); }; struct D2 : B2 { // expected-note 2 {{candidate constructor}} - using B2::B2; // expected-error {{not supported}} + using B2::B2; }; D2 d2a(1), d2b(1, 1), d2c(1, 1, 1); @@ -25,18 +25,18 @@ struct B3 { B3(void*); // expected-note {{inherited from here}} }; struct D3 : B3 { // expected-note 2 {{candidate constructor}} - using B3::B3; // expected-note {{candidate constructor (inherited)}} expected-error {{not supported}} + using B3::B3; // expected-note {{candidate constructor (inherited)}} }; D3 fd3() { return 1; } // expected-error {{no viable conversion}} template struct T1 : B1 { - using B1::B1; // expected-error {{not supported}} + using B1::B1; }; template struct T2 : T1 { - using T1::T1; // expected-error {{not supported}} + using T1::T1; }; template struct T3 : T1 { - using T1::T1; // expected-error {{not supported}} + using T1::T1; }; struct U { friend T1::T1(int); diff --git a/clang/test/CXX/special/class.inhctor/p4.cpp b/clang/test/CXX/special/class.inhctor/p4.cpp new file mode 100644 index 0000000..eea3bf2 --- /dev/null +++ b/clang/test/CXX/special/class.inhctor/p4.cpp @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +template struct X {}; + +// A[n inheriting] constructor [...] has the same access as the corresponding +// constructor [in the base class]. +struct A { +public: + A(X<0>) {} +protected: + A(X<1>) {} +private: + A(X<2>) {} // expected-note {{declared private here}} + friend class FA; +}; + +struct B : A { + using A::A; // expected-error {{private constructor}} expected-note {{implicitly declared protected here}} + friend class FB; +}; + +B b0{X<0>{}}; +B b1{X<1>{}}; // expected-error {{calling a protected constructor}} +B b2{X<2>{}}; // expected-note {{first required here}} + +struct C : B { + C(X<0> x) : B(x) {} + C(X<1> x) : B(x) {} +}; + +struct FB { + B b0{X<0>{}}; + B b1{X<1>{}}; +}; + +struct FA : A { + using A::A; // expected-note 2{{here}} +}; +FA fa0{X<0>{}}; +FA fa1{X<1>{}}; // expected-error {{calling a protected constructor}} +FA fa2{X<2>{}}; // expected-error {{calling a private constructor}} + + +// It is deleted if the corresponding constructor [...] is deleted. +struct G { + G(int) = delete; +}; +struct H : G { + using G::G; // expected-note {{marked deleted here}} +}; +H h(5); // expected-error {{call to implicitly-deleted function of 'H'}} + + +// Core defect: It is also deleted if multiple base constructors generate the +// same signature. +namespace DRnnnn { + struct A { + constexpr A(int, float = 0) {} + explicit A(int, int = 0) {} + + A(int, int, int = 0) = delete; + }; + struct B : A { + // FIXME: produce notes indicating why it was deleted + using A::A; // expected-note {{here}} + }; + + constexpr B b0(0, 0.0f); // ok, constexpr + B b1(0, 1); // expected-error {{call to implicitly-deleted}} +} diff --git a/clang/test/CXX/special/class.inhctor/p7.cpp b/clang/test/CXX/special/class.inhctor/p7.cpp index bfaa3ac..9ae160f 100644 --- a/clang/test/CXX/special/class.inhctor/p7.cpp +++ b/clang/test/CXX/special/class.inhctor/p7.cpp @@ -8,12 +8,12 @@ struct B2 { B2(int); // expected-note {{conflicting constructor}} }; struct D1 : B1, B2 { - using B1::B1; // expected-note {{inherited here}} expected-error {{not supported}} - using B2::B2; // expected-error {{already inherited constructor with the same signature}} expected-error {{not supported}} + 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; // expected-error {{not supported}} - using B2::B2; // expected-error {{not supported}} + using B1::B1; + using B2::B2; D2(int); }; @@ -22,8 +22,8 @@ template struct B3 { }; template struct B4 : B3, B1 { B4(); - using B3::B3; // expected-note {{inherited here}} expected-error {{not supported}} - using B1::B1; // expected-error {{already inherited}} expected-error {{not supported}} + using B3::B3; // expected-note {{inherited here}} + using B1::B1; // expected-error {{already inherited}} }; B4 b4c; B4 b4i; // expected-note {{here}} diff --git a/clang/test/CXX/special/class.inhctor/p8.cpp b/clang/test/CXX/special/class.inhctor/p8.cpp new file mode 100644 index 0000000..e2b07df --- /dev/null +++ b/clang/test/CXX/special/class.inhctor/p8.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +// expected-no-diagnostics +struct A { + constexpr A(const int&) : rval(false) {} + constexpr A(const int&&) : rval(true) {} + bool rval; +}; +struct B : A { + using A::A; +}; + +constexpr int k = 0; +constexpr A a0{0}; +constexpr A a1{k}; +constexpr B b0{0}; +// This performs static_cast<(const int&)&&>(k), so calls the A(const int&) +// constructor. +constexpr B b1{k}; + +static_assert(a0.rval && !a1.rval && b0.rval && !b1.rval, ""); diff --git a/clang/test/CodeGenCXX/inheriting-constructor.cpp b/clang/test/CodeGenCXX/inheriting-constructor.cpp index a998402..adb9f6d 100644 --- a/clang/test/CodeGenCXX/inheriting-constructor.cpp +++ b/clang/test/CodeGenCXX/inheriting-constructor.cpp @@ -1,11 +1,18 @@ // RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s -// XFAIL: * - // PR12219 struct A { A(int); virtual ~A(); }; struct B : A { using A::A; ~B(); }; B::~B() {} + +B b(123); + // CHECK: define void @_ZN1BD0Ev // CHECK: define void @_ZN1BD1Ev // CHECK: define void @_ZN1BD2Ev + +// CHECK: define linkonce_odr void @_ZN1BC1Ei( +// CHECK: call void @_ZN1BC2Ei( + +// CHECK: define linkonce_odr void @_ZN1BC2Ei( +// CHECK: call void @_ZN1AC2Ei( -- 2.7.4