]> granicus.if.org Git - clang/commitdiff
Implement implicit deletion of default constructors.
authorSean Hunt <scshunt@csclub.uwaterloo.ca>
Wed, 11 May 2011 22:34:38 +0000 (22:34 +0000)
committerSean Hunt <scshunt@csclub.uwaterloo.ca>
Wed, 11 May 2011 22:34:38 +0000 (22:34 +0000)
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
include/clang/Sema/Sema.h
lib/AST/DeclCXX.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaLookup.cpp
lib/Serialization/ASTReaderDecl.cpp
lib/Serialization/ASTWriter.cpp

index 72340b357bd7e9471eca5a2df9c3ea4d903e520f..47308b4ccdea5ce3943f8faec10fd0c2378a9034 100644 (file)
@@ -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.
index 29117f6f91f38b897e355d91ab95b7d72b775452..3abc970de2ef85ce49c6bb2a9a40fee21b726dce 100644 (file)
@@ -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 
index a18aeffe574f8fa884002f0da22f06d01229a23e..f32c85629c5a4f0f78d1ed0b7f050015038d965e 100644 (file)
@@ -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;
       }
     }
index 29343886f75deacbfd86e61e10e5fbe59b79f305..44e47ea8d86c7272cf3b90928b31daea26096ef0 100644 (file)
@@ -5642,7 +5642,7 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl,
 
     const RecordType *Record
       = Context.getBaseElementType(Type)->getAs<RecordType>();
-    if (Record && getLangOptions().CPlusPlus &&
+    if (Record && getLangOptions().CPlusPlus && !getLangOptions().CPlusPlus0x &&
         cast<CXXRecordDecl>(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
index 47e4ca129e2cccbe2175563cd25212e89066c163..bd16cda9f12c92a26bf7662ff7e21887b97a7597 100644 (file)
@@ -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<RecordType>()) {
       CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(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<RecordType>()) {
       CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(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<RecordType>()) {
       CXXRecordDecl *FieldClassDecl = cast<CXXRecordDecl>(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();
index db8d29c1bbf09779aea788494e48a2a642256682..2bfa86cb55a79eb939a8d295ad1ed0ebd858654e 100644 (file)
@@ -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<CXXRecordDecl>(DC))
       if (Record->getDefinition() &&
           CanDeclareSpecialMemberFunction(S.Context, Record)) {
-        if (!Record->needsImplicitDefaultConstructor())
+        if (Record->needsImplicitDefaultConstructor())
           S.DeclareImplicitDefaultConstructor(
                                            const_cast<CXXRecordDecl *>(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);
index 29ab396f2c967bec3ee99393b6f820d797852ccd..6c6a65a57078123036f02fc4b90e5802f7824b98 100644 (file)
@@ -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++];
index 21c452edf69915eee0e2c1553dc51876fd6ce747..aae62a8ff869ed0d9d154632e98a73dc91d15218 100644 (file)
@@ -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);