Begin work consolidating ShouldDelete* functions.

Begin with just default constructors. One note is that as a side effect
of this, a conformance test was removed on the basis that this is almost
certainly a defect as with most of union initialization. As it is, clang
does not implement union initialization close to the standard as it's
quite broken as written. I hope to write a paper addressing the issues
eventually.

llvm-svn: 141528
This commit is contained in:
Alexis Hunt 2011-10-10 06:18:57 +00:00
parent a14c5723eb
commit d6da876bac
3 changed files with 134 additions and 96 deletions

View File

@ -2830,9 +2830,9 @@ public:
ImplicitExceptionSpecification
ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl);
/// \brief Determine if a defaulted default constructor ought to be
/// deleted.
bool ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD);
/// \brief Determine if a special member function should have a deleted
/// definition when it is defaulted.
bool ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM);
/// \brief Determine if a defaulted copy constructor ought to be
/// deleted.

View File

@ -3742,7 +3742,7 @@ void Sema::CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *CD) {
return;
}
if (ShouldDeleteDefaultConstructor(CD)) {
if (ShouldDeleteSpecialMember(CD, CXXDefaultConstructor)) {
if (First) {
CD->setDeletedAsWritten();
} else {
@ -4096,30 +4096,44 @@ void Sema::CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *DD) {
}
}
bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) {
CXXRecordDecl *RD = CD->getParent();
/// This function implements the following C++0x paragraphs:
/// - [class.ctor]/5
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 = CD->getLocation();
bool IsUnion = RD->isUnion();
bool IsConstructor = false;
bool IsAssignment = false;
bool IsMove = false;
bool ConstArg = false;
switch (CSM) {
case CXXDefaultConstructor:
IsConstructor = true;
break;
default:
llvm_unreachable("function only currently implemented for default ctors");
}
SourceLocation Loc = MD->getLocation();
// Do access control from the constructor
ContextRAII CtorContext(*this, CD);
ContextRAII MethodContext(*this, MD);
bool Union = RD->isUnion();
bool AllConst = true;
// We do this because we should never actually use an anonymous
// union's constructor.
if (Union && RD->isAnonymousStructOrUnion())
if (IsUnion && 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 deleted if:
for (CXXRecordDecl::base_class_iterator BI = RD->bases_begin(),
BE = RD->bases_end();
BI != BE; ++BI) {
@ -4130,26 +4144,33 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) {
CXXRecordDecl *BaseDecl = BI->getType()->getAsCXXRecordDecl();
assert(BaseDecl && "base isn't a CXXRecordDecl");
// -- any [direct base class] has a type with a destructor that is
// deleted or inaccessible from the defaulted default constructor
CXXDestructorDecl *BaseDtor = LookupDestructor(BaseDecl);
if (BaseDtor->isDeleted())
return true;
if (CheckDestructorAccess(Loc, BaseDtor, PDiag()) !=
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())
return true;
if (CheckDestructorAccess(Loc, BaseDtor, PDiag()) !=
AR_accessible)
return true;
}
// -- 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
CXXConstructorDecl *BaseDefault = LookupDefaultConstructor(BaseDecl);
if (!BaseDefault || BaseDefault->isDeleted())
return true;
if (CheckConstructorAccess(Loc, BaseDefault, BaseDefault->getAccess(),
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, IsMove, 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 (CXXRecordDecl::base_class_iterator BI = RD->vbases_begin(),
@ -4158,26 +4179,33 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) {
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(Loc, BaseDtor, PDiag()) !=
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())
return true;
if (CheckDestructorAccess(Loc, 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
CXXConstructorDecl *BaseDefault = LookupDefaultConstructor(BaseDecl);
if (!BaseDefault || BaseDefault->isDeleted())
return true;
if (CheckConstructorAccess(Loc, BaseDefault, BaseDefault->getAccess(),
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, IsMove, 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 (CXXRecordDecl::field_iterator FI = RD->field_begin(),
@ -4189,38 +4217,38 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) {
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() && !FI->hasInClassInitializer())
return true;
// 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;
// -- X is a union and all its variant members are of const-qualified type
// (or array thereof)
if (Union && !FieldType.isConstQualified())
AllConst = false;
if (IsUnion && !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;
// 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;
}
CXXDestructorDecl *FieldDtor = LookupDestructor(FieldRecord);
if (FieldDtor->isDeleted())
return true;
if (CheckDestructorAccess(Loc, 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() &&
// 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;
if (!Union && FieldRecord->isUnion() &&
// For a default constructor, additional restrictions exist on the
// variant members.
if (CSM == CXXDefaultConstructor && !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.
@ -4249,29 +4277,43 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) {
continue;
}
// -- any non-static data member with no brace-or-equal-initializer 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.
if (!FI->hasInClassInitializer()) {
CXXConstructorDecl *FieldDefault = LookupDefaultConstructor(FieldRecord);
if (!FieldDefault || FieldDefault->isDeleted())
// 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, IsMove, false,
false);
if (!SMOR->hasSuccess())
return true;
if (CheckConstructorAccess(Loc, FieldDefault, FieldDefault->getAccess(),
PDiag()) != AR_accessible)
CXXMethodDecl *FieldMember = SMOR->getMethod();
if (IsConstructor) {
CXXConstructorDecl *FieldCtor = cast<CXXConstructorDecl>(FieldMember);
if (CheckConstructorAccess(Loc, FieldCtor, FieldCtor->getAccess(),
PDiag()) != AR_accessible)
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 (!Union && FieldType.isConstQualified() &&
!FI->hasInClassInitializer()) {
// -- 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
} 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;
}
}
if (Union && AllConst)
// 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;
@ -6979,7 +7021,7 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
PushOnScopeChains(DefaultCon, S, false);
ClassDecl->addDecl(DefaultCon);
if (ShouldDeleteDefaultConstructor(DefaultCon))
if (ShouldDeleteSpecialMember(DefaultCon, CXXDefaultConstructor))
DefaultCon->setDeletedAsWritten();
return DefaultCon;

View File

@ -23,10 +23,6 @@ int n;
// default constructor,
union Deleted1a { UserProvidedDefCtor u; }; // expected-note {{deleted here}}
Deleted1a d1a; // expected-error {{deleted constructor}}
// FIXME: treating this as having a deleted default constructor is probably a
// bug in the standard.
union Deleted1b { UserProvidedDefCtor u = UserProvidedDefCtor(); }; // expected-note {{deleted here}}
Deleted1b d1b; // expected-error {{deleted constructor}}
union NotDeleted1a { DefaultedDefCtor1 nu; };
NotDeleted1a nd1a;
// FIXME: clang implements the pre-FDIS rule, under which DefaultedDefCtor2's