From cdee3fee8ca4df7fb9179f29cc3ba96ac4fd0f95 Mon Sep 17 00:00:00 2001 From: Sean Hunt Date: Wed, 11 May 2011 22:34:38 +0000 Subject: [PATCH] Implement implicit deletion of default constructors. Yes, I'm aware that the diagnostics are awful. Tests to follow. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@131203 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 19 ++- include/clang/Sema/Sema.h | 4 + lib/AST/DeclCXX.cpp | 7 +- lib/Sema/SemaDecl.cpp | 5 +- lib/Sema/SemaDeclCXX.cpp | 186 +++++++++++++++++++++++++++- lib/Sema/SemaLookup.cpp | 6 +- lib/Serialization/ASTReaderDecl.cpp | 2 +- lib/Serialization/ASTWriter.cpp | 2 +- 8 files changed, 210 insertions(+), 21 deletions(-) diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 72340b357b..47308b4ccd 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -436,9 +436,9 @@ class CXXRecordDecl : public RecordDecl { /// already computed and are available. bool ComputedVisibleConversions : 1; - /// \brief Whether we have already declared the default constructor or - /// do not need to have one declared. - bool NeedsImplicitDefaultConstructor : 1; + /// \brief Whether we have a C++0x user-provided default constructor (not + /// explicitly deleted or defaulted. + bool UserProvidedDefaultConstructor : 1; /// \brief Whether we have already declared the default constructor. bool DeclaredDefaultConstructor : 1; @@ -675,12 +675,13 @@ public: return data().FirstFriend != 0; } - /// \brief Determine whether this class has had its default constructor - /// declared implicitly or does not need one declared implicitly. + /// \brief Determine if we need to declare a default constructor for + /// this class. /// /// This value is used for lazy creation of default constructors. bool needsImplicitDefaultConstructor() const { - return data().NeedsImplicitDefaultConstructor; + return !data().UserDeclaredConstructor && + !data().DeclaredDefaultConstructor; } /// hasConstCopyConstructor - Determines whether this class has a @@ -710,6 +711,12 @@ public: return data().UserDeclaredConstructor; } + /// hasUserProvidedDefaultconstructor - Whether this class has a + /// user-provided default constructor per C++0x. + bool hasUserProvidedDefaultConstructor() const { + return data().UserProvidedDefaultConstructor; + } + /// hasUserDeclaredCopyConstructor - Whether this class has a /// user-declared copy constructor. When false, a copy constructor /// will be implicitly declared. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 29117f6f91..3abc970de2 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2582,6 +2582,10 @@ public: ImplicitExceptionSpecification ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl); + /// \brief Determine if a defaulted default constructor ought to be + /// deleted. + bool ShouldDeleteDefaultConstructor(CXXConstructorDecl *RD); + /// \brief Declare the implicit default constructor for the given class. /// /// \param ClassDecl The class declaration into which the implicit diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index a18aeffe57..f32c85629c 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -38,7 +38,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) HasTrivialMoveConstructor(true), HasTrivialCopyAssignment(true), HasTrivialMoveAssignment(true), HasTrivialDestructor(true), HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false), - NeedsImplicitDefaultConstructor(false), DeclaredDefaultConstructor(false), + UserProvidedDefaultConstructor(false), DeclaredDefaultConstructor(false), DeclaredCopyConstructor(false), DeclaredCopyAssignment(false), DeclaredDestructor(false), NumBases(0), NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend(0) { @@ -460,7 +460,6 @@ void CXXRecordDecl::addedMember(Decl *D) { // declared it. if (Constructor->isDefaultConstructor()) { data().DeclaredDefaultConstructor = true; - data().NeedsImplicitDefaultConstructor = true; } // If this is the implicit copy constructor, note that we have now // declared it. @@ -491,9 +490,6 @@ void CXXRecordDecl::addedMember(Decl *D) { // Note that we have a user-declared constructor. data().UserDeclaredConstructor = true; - // Note that we have no need of an implicitly-declared default constructor. - data().NeedsImplicitDefaultConstructor = true; - // FIXME: Under C++0x, /only/ special member functions may be user-provided. // This is probably a defect. bool UserProvided = false; @@ -504,6 +500,7 @@ void CXXRecordDecl::addedMember(Decl *D) { data().DeclaredDefaultConstructor = true; if (Constructor->isUserProvided()) { data().HasTrivialDefaultConstructor = false; + data().UserProvidedDefaultConstructor = true; UserProvided = true; } } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 29343886f7..44e47ea8d8 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5642,7 +5642,7 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl, const RecordType *Record = Context.getBaseElementType(Type)->getAs(); - if (Record && getLangOptions().CPlusPlus && + if (Record && getLangOptions().CPlusPlus && !getLangOptions().CPlusPlus0x && cast(Record->getDecl())->isPOD()) { // C++03 [dcl.init]p9: // If no initializer is specified for an object, and the @@ -5655,6 +5655,9 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl, // any, have an indeterminate initial value); if the object // or any of its subobjects are of const-qualified type, the // program is ill-formed. + // C++0x [dcl.init]p11: + // If no initializer is specified for an object, the object is + // default-intiialized; [...]. } else { // Check for jumps past the implicit initializer. C++0x // clarifies that this applies to a "variable with automatic diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 47e4ca129e..bd16cda9f1 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3057,7 +3057,180 @@ void Sema::CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *CD) { // We know there are no parameters. CD->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI)); } +} + +bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { + CXXRecordDecl *RD = CD->getParent(); + assert(!RD->isDependentType() && "do deletion after instantiation"); + if (!LangOpts.CPlusPlus0x) + return false; + + // Do access control from the constructor + ContextRAII CtorContext(*this, CD); + + bool Union = RD->isUnion(); + bool AllConst = true; + + DiagnosticErrorTrap Trap(Diags); + + // We do this because we should never actually use an anonymous + // union's constructor. + if (Union && RD->isAnonymousStructOrUnion()) + return false; + + // 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: + + for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(), + BE = RD->bases_end(); + BI != BE; ++BI) { + CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl(); + 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 + CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); + if (BaseDtor->isDeleted()) + return true; + if (CheckDestructorAccess(SourceLocation(), BaseDtor, PDiag()) != + AR_accessible) + return true; + + // We'll handle this one later + if (BI->isVirtual()) + continue; + + // -- any [direct base class either] has no default constructor or + // 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(SourceLocation(), SourceLocation(), + SourceLocation()); + + InitializationSequence InitSeq(*this, BaseEntity, Kind, 0, 0); + + if (InitSeq.getKind() == InitializationSequence::FailedSequence) + return true; + } + + for (CXXRecordDecl::base_class_iterator BI = RD->vbases_begin(), + BE = RD->vbases_end(); + BI != BE; ++BI) { + CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl(); + assert(BaseDecl && "base isn't a CXXRecordDecl"); + + // -- any [virtual base class] has a type with a destructor that is + // delete or inaccessible from the defaulted default constructor + CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl); + if (BaseDtor->isDeleted()) + return true; + if (CheckDestructorAccess(SourceLocation(), BaseDtor, PDiag()) != + AR_accessible) + return true; + + // -- any [virtual base class either] has no default constructor or + // 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(SourceLocation(), SourceLocation(), + SourceLocation()); + + InitializationSequence InitSeq(*this, BaseEntity, Kind, 0, 0); + + if (InitSeq.getKind() == InitializationSequence::FailedSequence) + return true; + } + + for (CXXRecordDecl::field_iterator FI = RD->field_begin(), + FE = RD->field_end(); + FI != FE; ++FI) { + QualType FieldType = Context.getBaseElementType(FI->getType()); + CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl(); + + // -- any non-static data member with no brace-or-equal-initializer is of + // reference type + if (FieldType->isReferenceType()) + return true; + + // -- X is a union and all its variant members are of const-qualified type + // (or array thereof) + if (Union && !FieldType.isConstQualified()) + AllConst = false; + + if (FieldRecord) { + // -- X is a union-like class that has a variant member with a non-trivial + // default constructor + if (Union && !FieldRecord->hasTrivialDefaultConstructor()) + return true; + + CXXDestructorDecl *FieldDtor = LookupDestructor(FieldRecord); + if (FieldDtor->isDeleted()) + return true; + if (CheckDestructorAccess(SourceLocation(), FieldDtor, PDiag()) != + AR_accessible) + return true; + + // -- 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 + if (FieldType.isConstQualified() && + !FieldRecord->hasUserProvidedDefaultConstructor()) + return true; + + if (!Union && FieldRecord->isUnion() && + FieldRecord->isAnonymousStructOrUnion()) { + // We're okay to reuse AllConst here since we only care about the + // value otherwise if we're in a union. + AllConst = true; + + for (CXXRecordDecl::field_iterator UI = FieldRecord->field_begin(), + UE = FieldRecord->field_end(); + UI != UE; ++UI) { + QualType UnionFieldType = Context.getBaseElementType(UI->getType()); + CXXRecordDecl *UnionFieldRecord = + UnionFieldType->getAsCXXRecordDecl(); + + if (!UnionFieldType.isConstQualified()) + AllConst = false; + + if (UnionFieldRecord && + !UnionFieldRecord->hasTrivialDefaultConstructor()) + return true; + } + + if (AllConst) + return true; + + // Don't try to initialize the anonymous union + // This is technically non-conformant, but sanity deamands it. + continue; + } + } + + InitializedEntity MemberEntity = + InitializedEntity::InitializeMember(*FI, 0); + InitializationKind Kind = + InitializationKind::CreateDirect(SourceLocation(), SourceLocation(), + SourceLocation()); + + InitializationSequence InitSeq(*this, MemberEntity, Kind, 0, 0); + + if (InitSeq.getKind() == InitializationSequence::FailedSequence) + return true; + } + if (Union && AllConst) + return true; + + return false; } /// \brief Data used with FindHiddenVirtualMethod @@ -5010,7 +5183,7 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { if (const RecordType *BaseType = B->getType()->getAs()) { CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); - if (!BaseClassDecl->needsImplicitDefaultConstructor()) + if (BaseClassDecl->needsImplicitDefaultConstructor()) ExceptSpec.CalledDecl(DeclareImplicitDefaultConstructor(BaseClassDecl)); else if (CXXConstructorDecl *Constructor = getDefaultConstructorUnsafe(*this, BaseClassDecl)) @@ -5024,7 +5197,7 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { B != BEnd; ++B) { if (const RecordType *BaseType = B->getType()->getAs()) { CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); - if (!BaseClassDecl->needsImplicitDefaultConstructor()) + if (BaseClassDecl->needsImplicitDefaultConstructor()) ExceptSpec.CalledDecl(DeclareImplicitDefaultConstructor(BaseClassDecl)); else if (CXXConstructorDecl *Constructor = getDefaultConstructorUnsafe(*this, BaseClassDecl)) @@ -5039,7 +5212,7 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { if (const RecordType *RecordTy = Context.getBaseElementType(F->getType())->getAs()) { CXXRecordDecl *FieldClassDecl = cast(RecordTy->getDecl()); - if (!FieldClassDecl->needsImplicitDefaultConstructor()) + if (FieldClassDecl->needsImplicitDefaultConstructor()) ExceptSpec.CalledDecl( DeclareImplicitDefaultConstructor(FieldClassDecl)); else if (CXXConstructorDecl *Constructor @@ -5087,6 +5260,11 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( // Note that we have declared this constructor. ++ASTContext::NumImplicitDefaultConstructorsDeclared; + + // Do not delete this yet if we're in a template + if (!ClassDecl->isDependentType() && + ShouldDeleteDefaultConstructor(DefaultCon)) + DefaultCon->setDeletedAsWritten(); if (Scope *S = getScopeForContext(ClassDecl)) PushOnScopeChains(DefaultCon, S, false); @@ -5098,7 +5276,7 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation, CXXConstructorDecl *Constructor) { assert((Constructor->isImplicit() && Constructor->isDefaultConstructor() && - !Constructor->isUsed(false)) && + !Constructor->isUsed(false) && !Constructor->isDeleted()) && "DefineImplicitDefaultConstructor - call it for implicit default ctor"); CXXRecordDecl *ClassDecl = Constructor->getParent(); diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index db8d29c1bb..2bfa86cb55 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -534,7 +534,7 @@ void Sema::ForceDeclarationOfImplicitMembers(CXXRecordDecl *Class) { return; // If the default constructor has not yet been declared, do so now. - if (!Class->needsImplicitDefaultConstructor()) + if (Class->needsImplicitDefaultConstructor()) DeclareImplicitDefaultConstructor(Class); // If the copy constructor has not yet been declared, do so now. @@ -581,7 +581,7 @@ static void DeclareImplicitMemberFunctionsWithName(Sema &S, if (const CXXRecordDecl *Record = dyn_cast(DC)) if (Record->getDefinition() && CanDeclareSpecialMemberFunction(S.Context, Record)) { - if (!Record->needsImplicitDefaultConstructor()) + if (Record->needsImplicitDefaultConstructor()) S.DeclareImplicitDefaultConstructor( const_cast(Record)); if (!Record->hasDeclaredCopyConstructor()) @@ -2140,7 +2140,7 @@ void Sema::LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S, DeclContext::lookup_result Sema::LookupConstructors(CXXRecordDecl *Class) { // If the copy constructor has not yet been declared, do so now. if (CanDeclareSpecialMemberFunction(Context, Class)) { - if (!Class->needsImplicitDefaultConstructor()) + if (Class->needsImplicitDefaultConstructor()) DeclareImplicitDefaultConstructor(Class); if (!Class->hasDeclaredCopyConstructor()) DeclareImplicitCopyConstructor(Class); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 29ab396f2c..6c6a65a570 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -867,7 +867,7 @@ void ASTDeclReader::ReadCXXDefinitionData( Data.HasTrivialDestructor = Record[Idx++]; Data.HasNonLiteralTypeFieldsOrBases = Record[Idx++]; Data.ComputedVisibleConversions = Record[Idx++]; - Data.NeedsImplicitDefaultConstructor = Record[Idx++]; + Data.UserProvidedDefaultConstructor = Record[Idx++]; Data.DeclaredDefaultConstructor = Record[Idx++]; Data.DeclaredCopyConstructor = Record[Idx++]; Data.DeclaredCopyAssignment = Record[Idx++]; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 21c452edf6..aae62a8ff8 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -3827,7 +3827,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec Record.push_back(Data.HasTrivialDestructor); Record.push_back(Data.HasNonLiteralTypeFieldsOrBases); Record.push_back(Data.ComputedVisibleConversions); - Record.push_back(Data.NeedsImplicitDefaultConstructor); + Record.push_back(Data.UserProvidedDefaultConstructor); Record.push_back(Data.DeclaredDefaultConstructor); Record.push_back(Data.DeclaredCopyConstructor); Record.push_back(Data.DeclaredCopyAssignment); -- 2.40.0