Keep track of whether a C++ class is an aggregate. Don't allow initialization of non-aggregates with initializer lists.

llvm-svn: 58757
This commit is contained in:
Douglas Gregor 2008-11-05 16:20:31 +00:00
parent 6f5431543a
commit cfd8ddc6de
6 changed files with 85 additions and 9 deletions

View File

@ -215,6 +215,9 @@ class CXXRecordDecl : public RecordDecl, public DeclContext {
/// user-defined copy constructor. /// user-defined copy constructor.
bool UserDeclaredCopyConstructor : 1; bool UserDeclaredCopyConstructor : 1;
/// Aggregate - True when this class is an aggregate.
bool Aggregate : 1;
/// Bases - Base classes of this class. /// Bases - Base classes of this class.
/// FIXME: This is wasted space for a union. /// FIXME: This is wasted space for a union.
CXXBaseSpecifier *Bases; CXXBaseSpecifier *Bases;
@ -231,7 +234,7 @@ class CXXRecordDecl : public RecordDecl, public DeclContext {
SourceLocation L, IdentifierInfo *Id) SourceLocation L, IdentifierInfo *Id)
: RecordDecl(CXXRecord, TK, DC, L, Id), DeclContext(CXXRecord), : RecordDecl(CXXRecord, TK, DC, L, Id), DeclContext(CXXRecord),
UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false), UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false),
Bases(0), NumBases(0), Constructors(DC, Id) { } Aggregate(true), Bases(0), NumBases(0), Constructors(DC, Id) { }
~CXXRecordDecl(); ~CXXRecordDecl();
@ -300,6 +303,16 @@ public:
return UserDeclaredCopyConstructor; return UserDeclaredCopyConstructor;
} }
/// isAggregate - Whether this class is an aggregate (C++
/// [dcl.init.aggr]), which is a class with no user-declared
/// constructors, no private or protected non-static data members,
/// no base classes, and no virtual functions (C++ [dcl.init.aggr]p1).
bool isAggregate() const { return Aggregate; }
/// setAggregate - Set whether this class is an aggregate (C++
/// [dcl.init.aggr]).
void setAggregate(bool Agg) { Aggregate = Agg; }
/// viewInheritance - Renders and displays an inheritance diagram /// viewInheritance - Renders and displays an inheritance diagram
/// for this C++ class and all of its base classes (transitively) using /// for this C++ class and all of its base classes (transitively) using
/// GraphViz. /// GraphViz.

View File

@ -697,6 +697,8 @@ DIAG(err_reference_var_requires_init, ERROR,
"declaration of reference variable '%0' requires an initializer") "declaration of reference variable '%0' requires an initializer")
DIAG(err_const_var_requires_init, ERROR, DIAG(err_const_var_requires_init, ERROR,
"declaration of const variable '%0' requires an initializer") "declaration of const variable '%0' requires an initializer")
DIAG(err_init_non_aggr_init_list, ERROR,
"initialization of non-aggregate type '%0' with an initializer list")
// Objective-C++ // Objective-C++
DIAG(err_objc_decls_may_only_appear_in_global_scope, ERROR, DIAG(err_objc_decls_may_only_appear_in_global_scope, ERROR,

View File

@ -50,6 +50,11 @@ void CXXRecordDecl::Destroy(ASTContext &C) {
void void
CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
unsigned NumBases) { unsigned NumBases) {
// C++ [dcl.init.aggr]p1:
// An aggregate is an array or a class (clause 9) with [...]
// no base classes [...].
Aggregate = false;
if (this->Bases) if (this->Bases)
delete [] this->Bases; delete [] this->Bases;
@ -78,6 +83,11 @@ CXXRecordDecl::addConstructor(ASTContext &Context,
// Note that we have a user-declared constructor. // Note that we have a user-declared constructor.
UserDeclaredConstructor = true; UserDeclaredConstructor = true;
// C++ [dcl.init.aggr]p1:
// An aggregate is an array or a class (clause 9) with no
// user-declared constructors (12.1) [...].
Aggregate = false;
// Note when we have a user-declared copy constructor, which will // Note when we have a user-declared copy constructor, which will
// suppress the implicit declaration of a copy constructor. // suppress the implicit declaration of a copy constructor.
if (ConDecl->isCopyConstructor(Context)) if (ConDecl->isCopyConstructor(Context))
@ -154,9 +164,8 @@ CXXConstructorDecl::Create(ASTContext &C, CXXRecordDecl *RD,
bool CXXConstructorDecl::isDefaultConstructor() const { bool CXXConstructorDecl::isDefaultConstructor() const {
// C++ [class.ctor]p5: // C++ [class.ctor]p5:
// // A default constructor for a class X is a constructor of class
// A default constructor for a class X is a constructor of class // X that can be called without an argument.
// X that can be called without an argument.
return (getNumParams() == 0) || return (getNumParams() == 0) ||
(getNumParams() > 0 && getParamDecl(0)->getDefaultArg() != 0); (getNumParams() > 0 && getParamDecl(0)->getDefaultArg() != 0);
} }
@ -165,10 +174,10 @@ bool
CXXConstructorDecl::isCopyConstructor(ASTContext &Context, CXXConstructorDecl::isCopyConstructor(ASTContext &Context,
unsigned &TypeQuals) const { unsigned &TypeQuals) const {
// C++ [class.copy]p2: // C++ [class.copy]p2:
// A non-template constructor for class X is a copy constructor // A non-template constructor for class X is a copy constructor
// if its first parameter is of type X&, const X&, volatile X& or // if its first parameter is of type X&, const X&, volatile X& or
// const volatile X&, and either there are no other parameters // const volatile X&, and either there are no other parameters
// or else all other parameters have default arguments (8.3.6). // or else all other parameters have default arguments (8.3.6).
if ((getNumParams() < 1) || if ((getNumParams() < 1) ||
(getNumParams() > 1 && getParamDecl(1)->getDefaultArg() == 0)) (getNumParams() > 1 && getParamDecl(1)->getDefaultArg() == 0))
return false; return false;

View File

@ -701,6 +701,23 @@ bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType,
Init->getSourceRange()); Init->getSourceRange());
return CheckSingleInitializer(Init, DeclType); return CheckSingleInitializer(Init, DeclType);
} else if (getLangOptions().CPlusPlus) {
// C++ [dcl.init]p14:
// [...] If the class is an aggregate (8.5.1), and the initializer
// is a brace-enclosed list, see 8.5.1.
//
// Note: 8.5.1 is handled below; here, we diagnose the case where
// we have an initializer list and a destination type that is not
// an aggregate.
// FIXME: In C++0x, this is yet another form of initialization.
if (const RecordType *ClassRec = DeclType->getAsRecordType()) {
const CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(ClassRec->getDecl());
if (!ClassDecl->isAggregate())
return Diag(InitLoc,
diag::err_init_non_aggr_init_list,
DeclType.getAsString(),
Init->getSourceRange());
}
} }
InitListChecker CheckInitList(this, InitList, DeclType); InitListChecker CheckInitList(this, InitList, DeclType);

View File

@ -461,6 +461,15 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
// member decls. // member decls.
CXXClassMemberWrapper(Member).setAccess(AS); CXXClassMemberWrapper(Member).setAccess(AS);
// C++ [dcl.init.aggr]p1:
// An aggregate is an array or a class (clause 9) with [...] no
// private or protected non-static data members (clause 11).
if (isInstField && (AS == AS_private || AS == AS_protected))
cast<CXXRecordDecl>(CurContext)->setAggregate(false);
// FIXME: If the member is a virtual function, mark it its class as
// a non-aggregate.
if (BitWidth) { if (BitWidth) {
// C++ 9.6p2: Only when declaring an unnamed bit-field may the // C++ 9.6p2: Only when declaring an unnamed bit-field may the
// constant-expression be a value equal to zero. // constant-expression be a value equal to zero.

View File

@ -0,0 +1,26 @@
// RUN: clang -fsyntax-only -verify -std=c++98 %s
// Verify that we can't initialize non-aggregates with an initializer
// list.
struct NonAggr1 {
NonAggr1(int) { }
int m;
};
struct Base { };
struct NonAggr2 : public Base {
int m;
};
class NonAggr3 {
int m;
};
// FIXME: virtual functions
struct NonAggr4 {
};
NonAggr1 na1 = { 17 }; // expected-error{{initialization of non-aggregate type 'struct NonAggr1' with an initializer list}}
NonAggr2 na2 = { 17 }; // expected-error{{initialization of non-aggregate type 'struct NonAggr2' with an initializer list}}
NonAggr3 na3 = { 17 }; // expected-error{{initialization of non-aggregate type 'class NonAggr3' with an initializer list}}