aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2012-02-18 02:02:13 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2012-02-18 02:02:13 +0000
commit7d5088aa52d6e32b309ad0af32ea520ddbc5f953 (patch)
treed94a8ab89a214972208214c4d11ac57acf0050bf
parent7acebfb71f16a8bb15add402ab915d23c64d912f (diff)
Initial refactoring of 'ShouldDeleteSpecialMember', in preparation for providing
decent diagnostics. Finish the work of combining all the 'ShouldDelete' functions into one. In unifying the code, fix a minor bug where an anonymous union with a deleted default constructor as a member of a union wasn't being considered as making the outer union's default constructor deleted. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150862 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Sema/Sema.h17
-rw-r--r--lib/Sema/SemaDeclCXX.cpp917
-rw-r--r--lib/Sema/SemaLookup.cpp2
-rw-r--r--test/SemaCXX/cxx0x-deleted-default-ctor.cpp5
4 files changed, 312 insertions, 629 deletions
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 1ef4ac67dd..6293be3088 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -1702,9 +1702,6 @@ public:
ForRedeclaration
};
-private:
- bool CppLookupName(LookupResult &R, Scope *S);
-
SpecialMemberOverloadResult *LookupSpecialMember(CXXRecordDecl *D,
CXXSpecialMember SM,
bool ConstArg,
@@ -1713,6 +1710,9 @@ private:
bool ConstThis,
bool VolatileThis);
+private:
+ bool CppLookupName(LookupResult &R, Scope *S);
+
// \brief The set of known/encountered (unique, canonicalized) NamespaceDecls.
//
// The boolean value will be true to indicate that the namespace was loaded
@@ -2961,17 +2961,6 @@ public:
/// definition when it is defaulted.
bool ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM);
- /// \brief Determine if a defaulted copy assignment operator ought to be
- /// deleted.
- bool ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD);
-
- /// \brief Determine if a defaulted move assignment operator ought to be
- /// deleted.
- bool ShouldDeleteMoveAssignmentOperator(CXXMethodDecl *MD);
-
- /// \brief Determine if a defaulted destructor ought to be deleted.
- bool ShouldDeleteDestructor(CXXDestructorDecl *DD);
-
/// \brief Declare the implicit default constructor for the given class.
///
/// \param ClassDecl The class declaration into which the implicit
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 82e59ddafe..5a9b7a7508 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -875,12 +875,6 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) {
break;
ReturnStmts.push_back((*BodyIt)->getLocStart());
- // FIXME
- // - every constructor call and implicit conversion used in initializing
- // the return value shall be one of those allowed in a constant
- // expression.
- // Deal with this as part of a general check that the function can produce
- // a constant expression (for [dcl.constexpr]p5).
continue;
default:
@@ -944,20 +938,6 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) {
return false;
}
}
-
- // FIXME
- // - every constructor involved in initializing non-static data members
- // and base class sub-objects shall be a constexpr constructor;
- // - every assignment-expression that is an initializer-clause appearing
- // directly or indirectly within a brace-or-equal-initializer for
- // a non-static data member that is not named by a mem-initializer-id
- // shall be a constant expression; and
- // - every implicit conversion used in converting a constructor argument
- // to the corresponding parameter type and converting
- // a full-expression to the corresponding member type shall be one of
- // those allowed in a constant expression.
- // Deal with these as part of a general check that the function can produce
- // a constant expression (for [dcl.constexpr]p5).
} else {
if (ReturnStmts.empty()) {
Diag(Dcl->getLocation(), diag::err_constexpr_body_no_return);
@@ -4070,7 +4050,7 @@ void Sema::CheckExplicitlyDefaultedCopyAssignment(CXXMethodDecl *MD) {
return;
}
- if (ShouldDeleteCopyAssignmentOperator(MD)) {
+ if (ShouldDeleteSpecialMember(MD, CXXCopyAssignment)) {
if (First) {
MD->setDeletedAsWritten();
} else {
@@ -4248,7 +4228,7 @@ void Sema::CheckExplicitlyDefaultedMoveAssignment(CXXMethodDecl *MD) {
return;
}
- if (ShouldDeleteMoveAssignmentOperator(MD)) {
+ if (ShouldDeleteSpecialMember(MD, CXXMoveAssignment)) {
if (First) {
MD->setDeletedAsWritten();
} else {
@@ -4290,7 +4270,7 @@ void Sema::CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *DD) {
DD->setType(Context.getFunctionType(Context.VoidTy, 0, 0, EPI));
}
- if (ShouldDeleteDestructor(DD)) {
+ if (ShouldDeleteSpecialMember(DD, CXXDestructor)) {
if (First) {
DD->setDeletedAsWritten();
} else {
@@ -4301,665 +4281,380 @@ void Sema::CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *DD) {
}
}
-/// This function implements the following C++0x paragraphs:
-/// - [class.ctor]/5
-/// - [class.copy]/11
-bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM) {
- assert(!MD->isInvalidDecl());
- CXXRecordDecl *RD = MD->getParent();
- assert(!RD->isDependentType() && "do deletion after instantiation");
- if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl())
- return false;
-
- bool IsUnion = RD->isUnion();
- bool IsConstructor = false;
- bool IsAssignment = false;
- bool IsMove = false;
-
- bool ConstArg = false;
-
- switch (CSM) {
- case CXXDefaultConstructor:
- IsConstructor = true;
-
- // C++11 [expr.lambda.prim]p19:
- // The closure type associated with a lambda-expression has a
- // deleted (8.4.3) default constructor.
- if (RD->isLambda())
- return true;
+namespace {
+struct SpecialMemberDeletionInfo {
+ Sema &S;
+ CXXMethodDecl *MD;
+ Sema::CXXSpecialMember CSM;
+
+ // Properties of the special member, computed for convenience.
+ bool IsConstructor, IsAssignment, IsMove, ConstArg, VolatileArg;
+ SourceLocation Loc;
+
+ bool AllFieldsAreConst;
+
+ SpecialMemberDeletionInfo(Sema &S, CXXMethodDecl *MD,
+ Sema::CXXSpecialMember CSM)
+ : S(S), MD(MD), CSM(CSM),
+ IsConstructor(false), IsAssignment(false), IsMove(false),
+ ConstArg(false), VolatileArg(false), Loc(MD->getLocation()),
+ AllFieldsAreConst(true) {
+ switch (CSM) {
+ case Sema::CXXDefaultConstructor:
+ case Sema::CXXCopyConstructor:
+ IsConstructor = true;
+ break;
+ case Sema::CXXMoveConstructor:
+ IsConstructor = true;
+ IsMove = true;
+ break;
+ case Sema::CXXCopyAssignment:
+ IsAssignment = true;
+ break;
+ case Sema::CXXMoveAssignment:
+ IsAssignment = true;
+ IsMove = true;
+ break;
+ case Sema::CXXDestructor:
+ break;
+ case Sema::CXXInvalid:
+ llvm_unreachable("invalid special member kind");
+ }
- break;
- case CXXCopyConstructor:
- IsConstructor = true;
- ConstArg = MD->getParamDecl(0)->getType().isConstQualified();
- break;
- case CXXMoveConstructor:
- IsConstructor = true;
- IsMove = true;
- break;
- default:
- llvm_unreachable("function only currently implemented for default ctors");
+ if (MD->getNumParams()) {
+ ConstArg = MD->getParamDecl(0)->getType().isConstQualified();
+ VolatileArg = MD->getParamDecl(0)->getType().isVolatileQualified();
+ }
}
- SourceLocation Loc = MD->getLocation();
+ bool inUnion() const { return MD->getParent()->isUnion(); }
- // Do access control from the special member function
- ContextRAII MethodContext(*this, MD);
+ /// Look up the corresponding special member in the given class.
+ Sema::SpecialMemberOverloadResult *lookupIn(CXXRecordDecl *Class) {
+ unsigned TQ = MD->getTypeQualifiers();
+ return S.LookupSpecialMember(Class, CSM, ConstArg, VolatileArg,
+ MD->getRefQualifier() == RQ_RValue,
+ TQ & Qualifiers::Const,
+ TQ & Qualifiers::Volatile);
+ }
- bool AllConst = true;
+ bool shouldDeleteForBase(CXXRecordDecl *BaseDecl, bool IsVirtualBase);
+ bool shouldDeleteForField(FieldDecl *FD);
+ bool shouldDeleteForAllConstMembers();
+};
+}
- // We do this because we should never actually use an anonymous
- // union's constructor.
- if (IsUnion && RD->isAnonymousStructOrUnion())
- return false;
+/// Check whether we should delete a special member function due to the class
+/// having a particular direct or virtual base class.
+bool SpecialMemberDeletionInfo::shouldDeleteForBase(CXXRecordDecl *BaseDecl,
+ bool IsVirtualBase) {
+ // C++11 [class.copy]p23:
+ // -- for the move assignment operator, any direct or indirect virtual
+ // base class.
+ if (CSM == Sema::CXXMoveAssignment && IsVirtualBase)
+ return true;
- // FIXME: We should put some diagnostic logic right into this function.
+ // C++11 [class.ctor]p5, C++11 [class.copy]p11, C++11 [class.dtor]p5:
+ // -- any direct or virtual base class [...] has a type with a destructor
+ // that is deleted or inaccessible
+ if (!IsAssignment) {
+ CXXDestructorDecl *BaseDtor = S.LookupDestructor(BaseDecl);
+ if (BaseDtor->isDeleted())
+ return true;
+ if (S.CheckDestructorAccess(Loc, BaseDtor, S.PDiag())
+ != Sema::AR_accessible)
+ return true;
+ }
- for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(),
- BE = RD->bases_end();
- BI != BE; ++BI) {
- // We'll handle this one later
- if (BI->isVirtual())
- continue;
+ // C++11 [class.ctor]p5:
+ // -- any direct or virtual base class [...] has class type M [...] 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
+ // C++11 [class.copy]p11, C++11 [class.copy]p23:
+ // -- a direct or virtual base class B that cannot be copied/moved because
+ // overload resolution, as applied to B's corresponding special member,
+ // results in an ambiguity or a function that is deleted or inaccessible
+ // from the defaulted special member
+ if (CSM != Sema::CXXDestructor) {
+ Sema::SpecialMemberOverloadResult *SMOR = lookupIn(BaseDecl);
+ if (!SMOR->hasSuccess())
+ return true;
- CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl();
- assert(BaseDecl && "base isn't a CXXRecordDecl");
+ CXXMethodDecl *BaseMember = SMOR->getMethod();
+ if (IsConstructor) {
+ CXXConstructorDecl *BaseCtor = cast<CXXConstructorDecl>(BaseMember);
+ if (S.CheckConstructorAccess(Loc, BaseCtor, BaseCtor->getAccess(),
+ S.PDiag()) != Sema::AR_accessible)
+ return true;
- // Unless we have an assignment operator, the base's destructor must
- // be accessible and not deleted.
- if (!IsAssignment) {
- CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl);
- if (BaseDtor->isDeleted())
+ // -- for the move constructor, a [...] direct or virtual base class with
+ // a type that does not have a move constructor and is not trivially
+ // copyable.
+ if (IsMove && !BaseCtor->isMoveConstructor() &&
+ !BaseDecl->isTriviallyCopyable())
return true;
- if (CheckDestructorAccess(Loc, BaseDtor, PDiag()) !=
- AR_accessible)
+ } else {
+ assert(IsAssignment && "unexpected kind of special member");
+ if (S.CheckDirectMemberAccess(Loc, BaseMember, S.PDiag())
+ != Sema::AR_accessible)
return true;
- }
- // Finding the corresponding member in the base should lead to a
- // unique, accessible, non-deleted function. If we are doing
- // a destructor, we have already checked this case.
- if (CSM != CXXDestructor) {
- SpecialMemberOverloadResult *SMOR =
- LookupSpecialMember(BaseDecl, CSM, ConstArg, false, false, false,
- false);
- if (!SMOR->hasSuccess())
+ // -- for the move assignment operator, a direct base class with a type
+ // that does not have a move assignment operator and is not trivially
+ // copyable.
+ if (IsMove && !BaseMember->isMoveAssignmentOperator() &&
+ !BaseDecl->isTriviallyCopyable())
return true;
- CXXMethodDecl *BaseMember = SMOR->getMethod();
- if (IsConstructor) {
- CXXConstructorDecl *BaseCtor = cast<CXXConstructorDecl>(BaseMember);
- if (CheckConstructorAccess(Loc, BaseCtor, BaseCtor->getAccess(),
- PDiag()) != AR_accessible)
- return true;
-
- // For a move operation, the corresponding operation must actually
- // be a move operation (and not a copy selected by overload
- // resolution) unless we are working on a trivially copyable class.
- if (IsMove && !BaseCtor->isMoveConstructor() &&
- !BaseDecl->isTriviallyCopyable())
- 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");
-
- // Unless we have an assignment operator, the base's destructor must
- // be accessible and not deleted.
- if (!IsAssignment) {
- CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl);
- if (BaseDtor->isDeleted())
- return true;
- if (CheckDestructorAccess(Loc, BaseDtor, PDiag()) !=
- AR_accessible)
- return true;
- }
-
- // Finding the corresponding member in the base should lead to a
- // unique, accessible, non-deleted function.
- if (CSM != CXXDestructor) {
- SpecialMemberOverloadResult *SMOR =
- LookupSpecialMember(BaseDecl, CSM, ConstArg, false, false, false,
- false);
- if (!SMOR->hasSuccess())
- return true;
- CXXMethodDecl *BaseMember = SMOR->getMethod();
- if (IsConstructor) {
- CXXConstructorDecl *BaseCtor = cast<CXXConstructorDecl>(BaseMember);
- if (CheckConstructorAccess(Loc, BaseCtor, BaseCtor->getAccess(),
- PDiag()) != AR_accessible)
- return true;
-
- // For a move operation, the corresponding operation must actually
- // be a move operation (and not a copy selected by overload
- // resolution) unless we are working on a trivially copyable class.
- if (IsMove && !BaseCtor->isMoveConstructor() &&
- !BaseDecl->isTriviallyCopyable())
- return true;
- }
- }
+ // C++11 [class.dtor]p5:
+ // -- for a virtual destructor, lookup of the non-array deallocation function
+ // results in an ambiguity or in a function that is deleted or inaccessible
+ if (CSM == Sema::CXXDestructor && MD->isVirtual()) {
+ FunctionDecl *OperatorDelete = 0;
+ DeclarationName Name =
+ S.Context.DeclarationNames.getCXXOperatorName(OO_Delete);
+ if (S.FindDeallocationFunction(Loc, MD->getParent(), Name,
+ OperatorDelete, false))
+ return true;
}
- for (CXXRecordDecl::field_iterator FI = RD->field_begin(),
- FE = RD->field_end();
- FI != FE; ++FI) {
- if (FI->isInvalidDecl() || FI->isUnnamedBitfield())
- continue;
-
- QualType FieldType = Context.getBaseElementType(FI->getType());
- CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl();
+ return false;
+}
+
+/// Check whether we should delete a special member function due to the class
+/// having a particular non-static data member.
+bool SpecialMemberDeletionInfo::shouldDeleteForField(FieldDecl *FD) {
+ QualType FieldType = S.Context.getBaseElementType(FD->getType());
+ CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl();
+ if (CSM == Sema::CXXDefaultConstructor) {
// For a default constructor, all references must be initialized in-class
// and, if a union, it must have a non-const member.
- if (CSM == CXXDefaultConstructor) {
- if (FieldType->isReferenceType() && !FI->hasInClassInitializer())
- return true;
+ if (FieldType->isReferenceType() && !FD->hasInClassInitializer())
+ return true;
- if (IsUnion && !FieldType.isConstQualified())
- AllConst = false;
+ if (inUnion() && !FieldType.isConstQualified())
+ AllFieldsAreConst = false;
+ } else if (CSM == Sema::CXXCopyConstructor) {
// For a copy constructor, data members must not be of rvalue reference
// type.
- } else if (CSM == CXXCopyConstructor) {
- if (FieldType->isRValueReferenceType())
- return true;
- }
+ if (FieldType->isRValueReferenceType())
+ return true;
+ } else if (IsAssignment) {
+ // For an assignment operator, data members must not be of reference type.
+ if (FieldType->isReferenceType())
+ return true;
+ }
- if (FieldRecord) {
- // For a default constructor, a const member must have a user-provided
- // default constructor or else be explicitly initialized.
- if (CSM == CXXDefaultConstructor && FieldType.isConstQualified() &&
- !FI->hasInClassInitializer() &&
- !FieldRecord->hasUserProvidedDefaultConstructor())
- return true;
-
- // Some additional restrictions exist on the variant members.
- if (!IsUnion && 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) {
+ if (FieldRecord) {
+ // For a default constructor, a const member must have a user-provided
+ // default constructor or else be explicitly initialized.
+ if (CSM == Sema::CXXDefaultConstructor && FieldType.isConstQualified() &&
+ !FD->hasInClassInitializer() &&
+ !FieldRecord->hasUserProvidedDefaultConstructor())
+ return true;
+
+ // Some additional restrictions exist on the variant members.
+ if (!inUnion() && FieldRecord->isUnion() &&
+ FieldRecord->isAnonymousStructOrUnion()) {
+ bool AllVariantFieldsAreConst = true;
+
+ for (CXXRecordDecl::field_iterator UI = FieldRecord->field_begin(),
+ UE = FieldRecord->field_end();
+ UI != UE; ++UI) {
+ QualType UnionFieldType = S.Context.getBaseElementType(UI->getType());
+ CXXRecordDecl *UnionFieldRecord =
+ UnionFieldType->getAsCXXRecordDecl();
+
+ if (!UnionFieldType.isConstQualified())
+ AllVariantFieldsAreConst = false;
+
+ if (UnionFieldRecord) {
+ // FIXME: Checking for accessibility and validity of this
+ // destructor is technically going beyond the
+ // standard, but this is believed to be a defect.
+ if (!IsAssignment) {
+ CXXDestructorDecl *FieldDtor = S.LookupDestructor(UnionFieldRecord);
+ if (FieldDtor->isDeleted())
+ return true;
+ if (S.CheckDestructorAccess(Loc, FieldDtor, S.PDiag()) !=
+ Sema::AR_accessible)
+ return true;
+ if (!FieldDtor->isTrivial())
+ return true;
+ }
+
+ // FIXME: in-class initializers should be handled here
+ if (CSM != Sema::CXXDestructor) {
+ Sema::SpecialMemberOverloadResult *SMOR =
+ lookupIn(UnionFieldRecord);
// FIXME: Checking for accessibility and validity of this
- // destructor is technically going beyond the
+ // corresponding member is technically going beyond the
// standard, but this is believed to be a defect.
- if (!IsAssignment) {
- CXXDestructorDecl *FieldDtor = LookupDestructor(UnionFieldRecord);
- if (FieldDtor->isDeleted())
- return true;
- if (CheckDestructorAccess(Loc, FieldDtor, PDiag()) !=
- AR_accessible)
- return true;
- if (!FieldDtor->isTrivial())
- return true;
- }
-
- if (CSM != CXXDestructor) {
- SpecialMemberOverloadResult *SMOR =
- LookupSpecialMember(UnionFieldRecord, CSM, ConstArg, false,
- false, false, false);
- // FIXME: Checking for accessibility and validity of this
- // corresponding member is technically going beyond the
- // standard, but this is believed to be a defect.
- if (!SMOR->hasSuccess())
- return true;
+ if (!SMOR->hasSuccess())
+ return true;
- CXXMethodDecl *FieldMember = SMOR->getMethod();
- // A member of a union must have a trivial corresponding
- // constructor.
- if (!FieldMember->isTrivial())
- return true;
+ CXXMethodDecl *FieldMember = SMOR->getMethod();
+ // A member of a union must have a trivial corresponding
+ // special member.
+ if (!FieldMember->isTrivial())
+ return true;
- if (IsConstructor) {
- CXXConstructorDecl *FieldCtor = cast<CXXConstructorDecl>(FieldMember);
- if (CheckConstructorAccess(Loc, FieldCtor, FieldCtor->getAccess(),
- PDiag()) != AR_accessible)
+ if (IsConstructor) {
+ CXXConstructorDecl *FieldCtor =
+ cast<CXXConstructorDecl>(FieldMember);
+ if (S.CheckConstructorAccess(Loc, FieldCtor,
+ FieldCtor->getAccess(),
+ S.PDiag()) != Sema::AR_accessible)
+ return true;
+ } else {
+ assert(IsAssignment && "unexpected kind of special member");
+ if (S.CheckDirectMemberAccess(Loc, FieldMember, S.PDiag())
+ != Sema::AR_accessible)
return true;
- }
}
}
}
-
- // At least one member in each anonymous union must be non-const
- if (CSM == CXXDefaultConstructor && AllConst)
- return true;
-
- // Don't try to initialize the anonymous union
- // This is technically non-conformant, but sanity demands it.
- continue;
}
- // Unless we're doing assignment, the field's destructor must be
- // accessible and not deleted.
- if (!IsAssignment) {
- CXXDestructorDecl *FieldDtor = LookupDestructor(FieldRecord);
- if (FieldDtor->isDeleted())
- return true;
- if (CheckDestructorAccess(Loc, FieldDtor, PDiag()) !=
- AR_accessible)
- return true;
- }
-
- // Check that the corresponding member of the field is accessible,
- // unique, and non-deleted. We don't do this if it has an explicit
- // initialization when default-constructing.
- if (CSM != CXXDestructor &&
- (CSM != CXXDefaultConstructor || !FI->hasInClassInitializer())) {
- SpecialMemberOverloadResult *SMOR =
- LookupSpecialMember(FieldRecord, CSM, ConstArg, false, false, false,
- false);
- if (!SMOR->hasSuccess())
- return true;
-
- CXXMethodDecl *FieldMember = SMOR->getMethod();
- if (IsConstructor) {
- CXXConstructorDecl *FieldCtor = cast<CXXConstructorDecl>(FieldMember);
- if (CheckConstructorAccess(Loc, FieldCtor, FieldCtor->getAccess(),
- PDiag()) != AR_accessible)
- return true;
-
- // For a move operation, the corresponding operation must actually
- // be a move operation (and not a copy selected by overload
- // resolution) unless we are working on a trivially copyable class.
- if (IsMove && !FieldCtor->isMoveConstructor() &&
- !FieldRecord->isTriviallyCopyable())
- return true;
- }
+ // At least one member in each anonymous union must be non-const
+ if (CSM == Sema::CXXDefaultConstructor && AllVariantFieldsAreConst)
+ return true;
- // We need the corresponding member of a union to be trivial so that
- // we can safely copy them all simultaneously.
- // FIXME: Note that performing the check here (where we rely on the lack
- // of an in-class initializer) is technically ill-formed. However, this
- // seems most obviously to be a bug in the standard.
- if (IsUnion && !FieldMember->isTrivial())
- return true;
- }
- } else if (CSM == CXXDefaultConstructor && !IsUnion &&
- FieldType.isConstQualified() && !FI->hasInClassInitializer()) {
- // We can't initialize a const member of non-class type to any value.
- return true;
+ // Don't try to initialize the anonymous union
+ // This is technically non-conformant, but sanity demands it.
+ return false;
}
- }
-
- // We can't have all const members in a union when default-constructing,
- // or else they're all nonsensical garbage values that can't be changed.
- if (CSM == CXXDefaultConstructor && IsUnion && AllConst)
- return true;
-
- return false;
-}
-bool Sema::ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD) {
- CXXRecordDecl *RD = MD->getParent();
- assert(!RD->isDependentType() && "do deletion after instantiation");
- if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl())
- return false;
-
- // C++11 [expr.lambda.prim]p19:
- // The closure type associated with a lambda-expression has a
- // [...] deleted copy assignment operator.
- if (RD->isLambda())
- return true;
-
- SourceLocation Loc = MD->getLocation();
-
- // Do access control from the constructor
- ContextRAII MethodContext(*this, MD);
-
- bool Union = RD->isUnion();
-
- unsigned ArgQuals =
- MD->getParamDecl(0)->getType()->getPointeeType().isConstQualified() ?
- Qualifiers::Const : 0;
-
- // 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.copy]/20
- // A defaulted [copy] assignment operator for class X is defined as deleted
- // if X has:
-
- for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(),
- BE = RD->bases_end();
- BI != BE; ++BI) {
- // We'll handle this one later
- if (BI->isVirtual())
- continue;
-
- QualType BaseType = BI->getType();
- CXXRecordDecl *BaseDecl = BaseType->getAsCXXRecordDecl();
- assert(BaseDecl && "base isn't a CXXRecordDecl");
-
- // -- a [direct base class] B that cannot be [copied] because overload
- // resolution, as applied to B's [copy] assignment operator, results in
- // an ambiguity or a function that is deleted or inaccessible from the
- // assignment operator
- CXXMethodDecl *CopyOper = LookupCopyingAssignment(BaseDecl, ArgQuals, false,
- 0);
- if (!CopyOper || CopyOper->isDeleted())
- return true;
- if (CheckDirectMemberAccess(Loc, CopyOper, PDiag()) != AR_accessible)
- return true;
- }
+ // Unless we're doing assignment, the field's destructor must be
+ // accessible and not deleted.
+ if (!IsAssignment) {
+ CXXDestructorDecl *FieldDtor = S.LookupDestructor(FieldRecord);
+ if (FieldDtor->isDeleted())
+ return true;
+ if (S.CheckDestructorAccess(Loc, FieldDtor, S.PDiag()) !=
+ Sema::AR_accessible)
+ return true;
+ }
- for (CXXRecordDecl::base_class_iterator BI = RD->vbases_begin(),
- BE = RD->vbases_end();
- BI != BE; ++BI) {
- QualType BaseType = BI->getType();
- CXXRecordDecl *BaseDecl = BaseType->getAsCXXRecordDecl();
- assert(BaseDecl && "base isn't a CXXRecordDecl");
-
- // -- a [virtual base class] B that cannot be [copied] because overload
- // resolution, as applied to B's [copy] assignment operator, results in
- // an ambiguity or a function that is deleted or inaccessible from the
- // assignment operator
- CXXMethodDecl *CopyOper = LookupCopyingAssignment(BaseDecl, ArgQuals, false,
- 0);
- if (!CopyOper || CopyOper->isDeleted())
- return true;
- if (CheckDirectMemberAccess(Loc, CopyOper, PDiag()) != AR_accessible)
- return true;
- }
+ // Check that the corresponding member of the field is accessible,
+ // unique, and non-deleted. We don't do this if it has an explicit
+ // initialization when default-constructing.
+ if (CSM != Sema::CXXDestructor &&
+ !(CSM == Sema::CXXDefaultConstructor && FD->hasInClassInitializer())) {
+ Sema::SpecialMemberOverloadResult *SMOR = lookupIn(FieldRecord);
+ if (!SMOR->hasSuccess())
+ return true;
- for (CXXRecordDecl::field_iterator FI = RD->field_begin(),
- FE = RD->field_end();
- FI != FE; ++FI) {
- if (FI->isUnnamedBitfield())
- continue;
-
- QualType FieldType = Context.getBaseElementType(FI->getType());
-
- // -- a non-static data member of reference type
- if (FieldType->isReferenceType())
- return true;
+ CXXMethodDecl *FieldMember = SMOR->getMethod();
+ if (IsConstructor) {
+ CXXConstructorDecl *FieldCtor = cast<CXXConstructorDecl>(FieldMember);
+ if (S.CheckConstructorAccess(Loc, FieldCtor, FieldCtor->getAccess(),
+ S.PDiag()) != Sema::AR_accessible)
+ return true;
- // -- a non-static data member of const non-class type (or array thereof)
- if (FieldType.isConstQualified() && !FieldType->isRecordType())
- return true;
-
- CXXRecordDecl *FieldRecord = FieldType->getAsCXXRecordDecl();
-
- if (FieldRecord) {
- // This is an anonymous union
- if (FieldRecord->isUnion() && FieldRecord->isAnonymousStructOrUnion()) {
- // Anonymous unions inside unions do not variant members create
- if (!Union) {
- 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();
-
- // -- a variant member with a non-trivial [copy] assignment operator
- // and X is a union-like class
- if (UnionFieldRecord &&
- !UnionFieldRecord->hasTrivialCopyAssignment())
- return true;
- }
- }
+ // For a move operation, the corresponding operation must actually
+ // be a move operation (and not a copy selected by overload
+ // resolution) unless we are working on a trivially copyable class.
+ if (IsMove && !FieldCtor->isMoveConstructor() &&
+ !FieldRecord->isTriviallyCopyable())
+ return true;
+ } else {
+ assert(IsAssignment && "unexpected kind of special member");
+ if (S.CheckDirectMemberAccess(Loc, FieldMember, S.PDiag())
+ != Sema::AR_accessible)
+ return true;
- // Don't try to initalize an anonymous union
- continue;
- // -- a variant member with a non-trivial [copy] assignment operator
- // and X is a union-like class
- } else if (Union && !FieldRecord->hasTrivialCopyAssignment()) {
+ // -- for the move assignment operator, a non-static data member with a
+ // type that does not have a move assignment operator and is not
+ // trivially copyable.
+ if (IsMove && !FieldMember->isMoveAssignmentOperator() &&
+ !FieldRecord->isTriviallyCopyable())
return true;
}
- CXXMethodDecl *CopyOper = LookupCopyingAssignment(FieldRecord, ArgQuals,
- false, 0);
- if (!CopyOper || CopyOper->isDeleted())
- return true;
- if (CheckDirectMemberAccess(Loc, CopyOper, PDiag()) != AR_accessible)
+ // We need the corresponding member of a union to be trivial so that
+ // we can safely copy them all simultaneously.
+ // FIXME: Note that performing the check here (where we rely on the lack
+ // of an in-class initializer) is technically ill-formed. However, this
+ // seems most obviously to be a bug in the standard.
+ if (inUnion() && !FieldMember->isTrivial())
return true;
}
+ } else if (CSM == Sema::CXXDefaultConstructor && !inUnion() &&
+ FieldType.isConstQualified() && !FD->hasInClassInitializer()) {
+ // We can't initialize a const member of non-class type to any value.
+ return true;
+ } else if (IsAssignment && FieldType.isConstQualified()) {
+ // C++11 [class.copy]p23:
+ // -- a non-static data member of const non-class type (or array thereof)
+ return true;
}
return false;
}
-bool Sema::ShouldDeleteMoveAssignmentOperator(CXXMethodDecl *MD) {
+/// C++11 [class.ctor] p5:
+/// A defaulted default constructor for a class X is defined as deleted if
+/// X is a union and all of its variant members are of const-qualified type.
+bool SpecialMemberDeletionInfo::shouldDeleteForAllConstMembers() {
+ return CSM == Sema::CXXDefaultConstructor && inUnion() && AllFieldsAreConst;
+}
+
+/// Determine whether a defaulted special member function should be defined as
+/// deleted, as specified in C++11 [class.ctor]p5, C++11 [class.copy]p11,
+/// C++11 [class.copy]p23, and C++11 [class.dtor]p5.
+bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM) {
+ assert(!MD->isInvalidDecl());
CXXRecordDecl *RD = MD->getParent();
assert(!RD->isDependentType() && "do deletion after instantiation");
if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl())
return false;
- SourceLocation Loc = MD->getLocation();
-
- // Do access control from the constructor
- ContextRAII MethodContext(*this, MD);
-
- bool Union = RD->isUnion();
-
- // We do this because we should never actually use an anonymous
- // union's constructor.
- if (Union && RD->isAnonymousStructOrUnion())
- return false;
-
- // C++0x [class.copy]/20
- // A defaulted [move] assignment operator for class X is defined as deleted
- // if X has:
-
- // -- for the move constructor, [...] any direct or indirect virtual base
- // class.
- if (RD->getNumVBases() != 0)
+ // C++11 [expr.lambda.prim]p19:
+ // The closure type associated with a lambda-expression has a
+ // deleted (8.4.3) default constructor and a deleted copy
+ // assignment operator.
+ if (RD->isLambda() &&
+ (CSM == CXXDefaultConstructor || CSM == CXXCopyAssignment))
return true;
- for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(),
- BE = RD->bases_end();
- BI != BE; ++BI) {
-
- QualType BaseType = BI->getType();
- CXXRecordDecl *BaseDecl = BaseType->getAsCXXRecordDecl();
- assert(BaseDecl && "base isn't a CXXRecordDecl");
-
- // -- a [direct base class] B that cannot be [moved] because overload
- // resolution, as applied to B's [move] assignment operator, results in
- // an ambiguity or a function that is deleted or inaccessible from the
- // assignment operator
- CXXMethodDecl *MoveOper = LookupMovingAssignment(BaseDecl, false, 0);
- if (!MoveOper || MoveOper->isDeleted())
- return true;
- if (CheckDirectMemberAccess(Loc, MoveOper, PDiag()) != AR_accessible)
- return true;
-
- // -- for the move assignment operator, a [direct base class] with a type
- // that does not have a move assignment operator and is not trivially
- // copyable.
- if (!MoveOper->isMoveAssignmentOperator() &&
- !BaseDecl->isTriviallyCopyable())
- return true;
- }
-
- for (CXXRecordDecl::field_itera