From: Sean Hunt Date: Fri, 10 Jun 2011 03:50:41 +0000 (+0000) Subject: Implement caching of default constructors on the resolution table. This X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b320e0c322fb71d6235ebca9ec22940a97bdcdc7;p=clang Implement caching of default constructors on the resolution table. This isn't yet used for the less controlled environments of initialization. Also a few random text fixups. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@132833 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index ac449cfb30..32bd86252e 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1668,6 +1668,7 @@ public: SourceLocation GnuLabelLoc = SourceLocation()); DeclContextLookupResult LookupConstructors(CXXRecordDecl *Class); + CXXConstructorDecl *LookupDefaultConstructor(CXXRecordDecl *Class); CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class); void ArgumentDependentLookup(DeclarationName Name, bool Operator, @@ -3465,6 +3466,10 @@ public: const InitializedEntity &Entity, AccessSpecifier Access, bool IsCopyBindingRefToTemp = false); + AccessResult CheckConstructorAccess(SourceLocation Loc, + CXXConstructorDecl *D, + AccessSpecifier Access, + PartialDiagnostic PD); AccessResult CheckDestructorAccess(SourceLocation Loc, CXXDestructorDecl *Dtor, const PartialDiagnostic &PDiag); diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index add0c5d659..a26737e00e 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -1460,30 +1460,48 @@ Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, AccessTarget AccessEntity(Context, AccessTarget::Member, NamingClass, DeclAccessPair::make(Constructor, Access), QualType()); + PartialDiagnostic PD(PDiag()); switch (Entity.getKind()) { default: - AccessEntity.setDiag(IsCopyBindingRefToTemp - ? diag::ext_rvalue_to_reference_access_ctor - : diag::err_access_ctor); + PD = PDiag(IsCopyBindingRefToTemp + ? diag::ext_rvalue_to_reference_access_ctor + : diag::err_access_ctor); + break; case InitializedEntity::EK_Base: - AccessEntity.setDiag(PDiag(diag::err_access_base_ctor) - << Entity.isInheritedVirtualBase() - << Entity.getBaseSpecifier()->getType() - << getSpecialMember(Constructor)); + PD = PDiag(diag::err_access_base_ctor); + PD << Entity.isInheritedVirtualBase() + << Entity.getBaseSpecifier()->getType() << getSpecialMember(Constructor); break; case InitializedEntity::EK_Member: { const FieldDecl *Field = cast(Entity.getDecl()); - AccessEntity.setDiag(PDiag(diag::err_access_field_ctor) - << Field->getType() - << getSpecialMember(Constructor)); + PD = PDiag(diag::err_access_field_ctor); + PD << Field->getType() << getSpecialMember(Constructor); break; } } + return CheckConstructorAccess(UseLoc, Constructor, Access, PD); +} + +/// Checks access to a constructor. +Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, + CXXConstructorDecl *Constructor, + AccessSpecifier Access, + PartialDiagnostic PD) { + if (!getLangOptions().AccessControl || + Access == AS_public) + return AR_accessible; + + CXXRecordDecl *NamingClass = Constructor->getParent(); + AccessTarget AccessEntity(Context, AccessTarget::Member, NamingClass, + DeclAccessPair::make(Constructor, Access), + QualType()); + AccessEntity.setDiag(PD); + return CheckAccess(*this, UseLoc, AccessEntity); } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 18a73666b3..29e81d3f7f 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3318,7 +3318,7 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { // FIXME: We should put some diagnostic logic right into this function. // C++0x [class.ctor]/5 - // A defaulted default constructor for class X is defined as delete if: + // A defaulted default constructor for class X is defined as deleted if: for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(), BE = RD->bases_end(); @@ -3331,7 +3331,7 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { assert(BaseDecl && "base isn't a CXXRecordDecl"); // -- any [direct base class] has a type with a destructor that is - // delete or inaccessible from the defaulted default constructor + // deleted or inaccessible from the defaulted default constructor CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); if (BaseDtor->isDeleted()) return true; @@ -3343,14 +3343,12 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { // overload resolution as applied to [its] default constructor // results in an ambiguity or in a function that is deleted or // inaccessible from the defaulted default constructor - InitializedEntity BaseEntity = - InitializedEntity::InitializeBase(Context, BI, 0); - InitializationKind Kind = - InitializationKind::CreateDirect(Loc, Loc, Loc); - - InitializationSequence InitSeq(*this, BaseEntity, Kind, 0, 0); + CXXConstructorDecl *BaseDefault = LookupDefaultConstructor(BaseDecl); + if (!BaseDefault || BaseDefault->isDeleted()) + return true; - if (InitSeq.Failed()) + if (CheckConstructorAccess(Loc, BaseDefault, BaseDefault->getAccess(), + PDiag()) != AR_accessible) return true; } @@ -3373,14 +3371,12 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { // overload resolution as applied to [its] default constructor // results in an ambiguity or in a function that is deleted or // inaccessible from the defaulted default constructor - InitializedEntity BaseEntity = - InitializedEntity::InitializeBase(Context, BI, BI); - InitializationKind Kind = - InitializationKind::CreateDirect(Loc, Loc, Loc); - - InitializationSequence InitSeq(*this, BaseEntity, Kind, 0, 0); + CXXConstructorDecl *BaseDefault = LookupDefaultConstructor(BaseDecl); + if (!BaseDefault || BaseDefault->isDeleted()) + return true; - if (InitSeq.Failed()) + if (CheckConstructorAccess(Loc, BaseDefault, BaseDefault->getAccess(), + PDiag()) != AR_accessible) return true; } @@ -3448,22 +3444,24 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { // This is technically non-conformant, but sanity demands it. continue; } + + // -- any non-static data member ... has class type M (or array thereof) + // and either M has no default constructor or overload resolution as + // applied to M's default constructor results in an ambiguity or in a + // function that is deleted or inaccessible from the defaulted default + // constructor. + CXXConstructorDecl *FieldDefault = LookupDefaultConstructor(FieldRecord); + if (!FieldDefault || FieldDefault->isDeleted()) + return true; + if (CheckConstructorAccess(Loc, FieldDefault, FieldDefault->getAccess(), + PDiag()) != AR_accessible) + return true; } else if (!Union && FieldType.isConstQualified()) { // -- any non-variant non-static data member of const-qualified type (or // array thereof) with no brace-or-equal-initializer does not have a // user-provided default constructor return true; } - - InitializedEntity MemberEntity = - InitializedEntity::InitializeMember(*FI, 0); - InitializationKind Kind = - InitializationKind::CreateDirect(Loc, Loc, Loc); - - InitializationSequence InitSeq(*this, MemberEntity, Kind, 0, 0); - - if (InitSeq.Failed()) - return true; } if (Union && AllConst) @@ -3548,7 +3546,7 @@ bool Sema::ShouldDeleteCopyConstructor(CXXConstructorDecl *CD) { CXXRecordDecl *BaseDecl = BaseType->getAsCXXRecordDecl(); assert(BaseDecl && "base isn't a CXXRecordDecl"); - // -- any [direct base class] of a type with a destructor that is deleted or + // -- any [virtual base class] of a type with a destructor that is deleted or // inaccessible from the defaulted constructor CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); if (BaseDtor->isDeleted()) @@ -5895,28 +5893,6 @@ namespace { }; } -static CXXConstructorDecl *getDefaultConstructorUnsafe(Sema &Self, - CXXRecordDecl *D) { - ASTContext &Context = Self.Context; - QualType ClassType = Context.getTypeDeclType(D); - DeclarationName ConstructorName - = Context.DeclarationNames.getCXXConstructorName( - Context.getCanonicalType(ClassType.getUnqualifiedType())); - - DeclContext::lookup_const_iterator Con, ConEnd; - for (llvm::tie(Con, ConEnd) = D->lookup(ConstructorName); - Con != ConEnd; ++Con) { - // FIXME: In C++0x, a constructor template can be a default constructor. - if (isa(*Con)) - continue; - - CXXConstructorDecl *Constructor = cast(*Con); - if (Constructor->isDefaultConstructor()) - return Constructor; - } - return 0; -} - Sema::ImplicitExceptionSpecification Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { // C++ [except.spec]p14: @@ -5933,10 +5909,10 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { if (const RecordType *BaseType = B->getType()->getAs()) { CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); - if (BaseClassDecl->needsImplicitDefaultConstructor()) - ExceptSpec.CalledDecl(DeclareImplicitDefaultConstructor(BaseClassDecl)); - else if (CXXConstructorDecl *Constructor - = getDefaultConstructorUnsafe(*this, BaseClassDecl)) + CXXConstructorDecl *Constructor = LookupDefaultConstructor(BaseClassDecl); + // If this is a deleted function, add it anyway. This might be conformant + // with the standard. This might not. I'm not sure. It might not matter. + if (Constructor) ExceptSpec.CalledDecl(Constructor); } } @@ -5947,10 +5923,10 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { B != BEnd; ++B) { if (const RecordType *BaseType = B->getType()->getAs()) { CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); - if (BaseClassDecl->needsImplicitDefaultConstructor()) - ExceptSpec.CalledDecl(DeclareImplicitDefaultConstructor(BaseClassDecl)); - else if (CXXConstructorDecl *Constructor - = getDefaultConstructorUnsafe(*this, BaseClassDecl)) + CXXConstructorDecl *Constructor = LookupDefaultConstructor(BaseClassDecl); + // If this is a deleted function, add it anyway. This might be conformant + // with the standard. This might not. I'm not sure. It might not matter. + if (Constructor) ExceptSpec.CalledDecl(Constructor); } } @@ -5961,12 +5937,14 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { F != FEnd; ++F) { if (const RecordType *RecordTy = Context.getBaseElementType(F->getType())->getAs()) { - CXXRecordDecl *FieldClassDecl = cast(RecordTy->getDecl()); - if (FieldClassDecl->needsImplicitDefaultConstructor()) - ExceptSpec.CalledDecl( - DeclareImplicitDefaultConstructor(FieldClassDecl)); - else if (CXXConstructorDecl *Constructor - = getDefaultConstructorUnsafe(*this, FieldClassDecl)) + CXXRecordDecl *FieldRecDecl = cast(RecordTy->getDecl()); + CXXConstructorDecl *Constructor = LookupDefaultConstructor(FieldRecDecl); + // If this is a deleted function, add it anyway. This might be conformant + // with the standard. This might not. I'm not sure. It might not matter. + // In particular, the problem is that this function never gets called. It + // might just be ill-formed because this function attempts to refer to + // a deleted function here. + if (Constructor) ExceptSpec.CalledDecl(Constructor); } } diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index f7726d99c8..b730408b91 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -2186,12 +2186,133 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, return Result; } - llvm_unreachable("haven't implemented this for non-destructors yet"); + // Prepare for overload resolution. Here we construct a synthetic argument + // if necessary and make sure that implicit functions are declared. + CanQualType CanTy = Context.getCanonicalType(Context.getTagDeclType(D)); + DeclarationName Name; + Expr *Arg = 0; + unsigned NumArgs; + + if (SM == CXXDefaultConstructor) { + Name = Context.DeclarationNames.getCXXConstructorName(CanTy); + NumArgs = 0; + if (D->needsImplicitDefaultConstructor()) + DeclareImplicitDefaultConstructor(D); + } else { + if (SM == CXXCopyConstructor || SM == CXXMoveConstructor) { + Name = Context.DeclarationNames.getCXXConstructorName(CanTy); + if (!D->hasDeclaredCopyConstructor()) + DeclareImplicitCopyConstructor(D); + // TODO: Move constructors + } else { + Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); + if (!D->hasDeclaredCopyAssignment()) + DeclareImplicitCopyAssignment(D); + // TODO: Move assignment + } + + QualType ArgType = CanTy; + if (ConstArg) + ArgType.addConst(); + if (VolatileArg) + ArgType.addVolatile(); + + // This isn't /really/ specified by the standard, but it's implied + // we should be working from an RValue in the case of move to ensure + // that we prefer to bind to rvalue references, and an LValue in the + // case of copy to ensure we don't bind to rvalue references. + // Possibly an XValue is actually correct in the case of move, but + // there is no semantic difference for class types in this restricted + // case. + ExprValueKind VK; + if (SM == CXXCopyAssignment || SM == CXXMoveAssignment) + VK = VK_LValue; + else + VK = VK_RValue; + + NumArgs = 1; + Arg = new (Context) OpaqueValueExpr(SourceLocation(), ArgType, VK); + } + + // Create the object argument + QualType ThisTy = CanTy; + if (ConstThis) + ThisTy.addConst(); + if (VolatileThis) + ThisTy.addVolatile(); + Expr::Classification ObjectClassification = + (new (Context) OpaqueValueExpr(SourceLocation(), ThisTy, + RValueThis ? VK_RValue : VK_LValue))-> + Classify(Context); + + // Now we perform lookup on the name we computed earlier and do overload + // resolution. Lookup is only performed directly into the class since there + // will always be a (possibly implicit) declaration to shadow any others. + OverloadCandidateSet OCS((SourceLocation())); + DeclContext::lookup_iterator I, E; + Result->setConstParamMatch(false); + + llvm::tie(I, E) = D->lookup(Name); + assert((I != E) && + "lookup for a constructor or assignment operator was empty"); + for ( ; I != E; ++I) { + if ((*I)->isInvalidDecl()) + continue; + + if (CXXMethodDecl *M = dyn_cast(*I)) { + AddOverloadCandidate(M, DeclAccessPair::make(M, AS_public), &Arg, NumArgs, + OCS, true); + + // Here we're looking for a const parameter to speed up creation of + // implicit copy methods. + if ((SM == CXXCopyAssignment && M->isCopyAssignmentOperator()) || + (SM == CXXCopyConstructor && + cast(M)->isCopyConstructor())) { + QualType ArgType = M->getType()->getAs()->getArgType(0); + if (ArgType->getPointeeType().isConstQualified()) + Result->setConstParamMatch(true); + } + } else { + FunctionTemplateDecl *Tmpl = cast(*I); + AddTemplateOverloadCandidate(Tmpl, DeclAccessPair::make(Tmpl, AS_public), + 0, &Arg, NumArgs, OCS, true); + } + } + + OverloadCandidateSet::iterator Best; + switch (OCS.BestViableFunction(*this, SourceLocation(), Best)) { + case OR_Success: + Result->setMethod(cast(Best->Function)); + Result->setSuccess(true); + break; + + case OR_Deleted: + Result->setMethod(cast(Best->Function)); + Result->setSuccess(false); + break; + + case OR_Ambiguous: + case OR_No_Viable_Function: + Result->setMethod(0); + Result->setSuccess(false); + break; + } + + return Result; +} + +/// \brief Look up the default constructor for the given class. +CXXConstructorDecl *Sema::LookupDefaultConstructor(CXXRecordDecl *Class) { + SpecialMemberOverloadResult *Result = + LookupSpecialMember(Class, CXXDefaultConstructor, false, false, false, + false, false); + + return cast_or_null(Result->getMethod()); } /// \brief Look up the constructors for the given class. DeclContext::lookup_result Sema::LookupConstructors(CXXRecordDecl *Class) { - // If the copy constructor has not yet been declared, do so now. + // If the implicit constructors have not yet been declared, do so now. if (CanDeclareSpecialMemberFunction(Context, Class)) { if (Class->needsImplicitDefaultConstructor()) DeclareImplicitDefaultConstructor(Class);