]> granicus.if.org Git - clang/commitdiff
Keep track of whether a C++ class is an aggregate. Don't allow initialization of...
authorDouglas Gregor <dgregor@apple.com>
Wed, 5 Nov 2008 16:20:31 +0000 (16:20 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 5 Nov 2008 16:20:31 +0000 (16:20 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58757 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclCXX.h
include/clang/Basic/DiagnosticKinds.def
lib/AST/DeclCXX.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
test/SemaCXX/aggregate-initialization.cpp [new file with mode: 0644]

index 3d0c805a3a7aba03b84ce582c5c002b135d52a8d..9d0065e4fbb6cdf535a058ee0f4489dfa123fb2c 100644 (file)
@@ -215,6 +215,9 @@ class CXXRecordDecl : public RecordDecl, public DeclContext {
   /// user-defined copy constructor.
   bool UserDeclaredCopyConstructor : 1;
 
+  /// Aggregate - True when this class is an aggregate.
+  bool Aggregate : 1;
+
   /// Bases - Base classes of this class.
   /// FIXME: This is wasted space for a union.
   CXXBaseSpecifier *Bases;
@@ -230,8 +233,8 @@ class CXXRecordDecl : public RecordDecl, public DeclContext {
   CXXRecordDecl(TagKind TK, DeclContext *DC,
                 SourceLocation L, IdentifierInfo *Id) 
     : RecordDecl(CXXRecord, TK, DC, L, Id), DeclContext(CXXRecord),
-      UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false), 
-      Bases(0), NumBases(0), Constructors(DC, Id) { }
+      UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false),
+      Aggregate(true), Bases(0), NumBases(0), Constructors(DC, Id) { }
 
   ~CXXRecordDecl();
 
@@ -300,6 +303,16 @@ public:
     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
   /// for this C++ class and all of its base classes (transitively) using
   /// GraphViz.
index 30238f6d6f9895ef94fc0b3d01a708051d47ef01..c4c3d0c986845ddbca1f62e99b60b2aea1d88091 100644 (file)
@@ -697,6 +697,8 @@ DIAG(err_reference_var_requires_init, ERROR,
      "declaration of reference variable '%0' requires an initializer")
 DIAG(err_const_var_requires_init, ERROR,
      "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++
 DIAG(err_objc_decls_may_only_appear_in_global_scope, ERROR,
index f49b207b0c0dd3cf2120dfbf9a2ea7bc70cdd74c..cf59d1af3608d90dba2954bc1eabc6639cf60f56 100644 (file)
@@ -50,6 +50,11 @@ void CXXRecordDecl::Destroy(ASTContext &C) {
 void 
 CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, 
                         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)
     delete [] this->Bases;
 
@@ -78,6 +83,11 @@ CXXRecordDecl::addConstructor(ASTContext &Context,
     // Note that we have a user-declared constructor.
     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
     // suppress the implicit declaration of a copy constructor.
     if (ConDecl->isCopyConstructor(Context))
@@ -154,9 +164,8 @@ CXXConstructorDecl::Create(ASTContext &C, CXXRecordDecl *RD,
 
 bool CXXConstructorDecl::isDefaultConstructor() const {
   // C++ [class.ctor]p5:
-  //
-  //     A default constructor for a class X is a constructor of class
-  //     X that can be called without an argument.
+  //   A default constructor for a class X is a constructor of class
+  //   X that can be called without an argument.
   return (getNumParams() == 0) ||
          (getNumParams() > 0 && getParamDecl(0)->getDefaultArg() != 0);
 }
@@ -165,10 +174,10 @@ bool
 CXXConstructorDecl::isCopyConstructor(ASTContext &Context, 
                                       unsigned &TypeQuals) const {
   // C++ [class.copy]p2:
-  //     A non-template constructor for class X is a copy constructor
-  //     if its first parameter is of type X&, const X&, volatile X& or
-  //     const volatile X&, and either there are no other parameters
-  //     or else all other parameters have default arguments (8.3.6).
+  //   A non-template constructor for class X is a copy constructor
+  //   if its first parameter is of type X&, const X&, volatile X& or
+  //   const volatile X&, and either there are no other parameters
+  //   or else all other parameters have default arguments (8.3.6).
   if ((getNumParams() < 1) ||
       (getNumParams() > 1 && getParamDecl(1)->getDefaultArg() == 0))
     return false;
index 5dc571c0ee54ec68fa014627897f7834f539caed..c8c253a3fba56185623de6fd66b7541a6ddad117 100644 (file)
@@ -701,6 +701,23 @@ bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType,
                   Init->getSourceRange());
 
     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);
index b95ebf0bad8387a1a0d7dcff6d64141001208abc..53051ff57fb894c182b7658a16c77c213c55c127 100644 (file)
@@ -461,6 +461,15 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
   // member decls.
   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) {
     // C++ 9.6p2: Only when declaring an unnamed bit-field may the
     // constant-expression be a value equal to zero.
diff --git a/test/SemaCXX/aggregate-initialization.cpp b/test/SemaCXX/aggregate-initialization.cpp
new file mode 100644 (file)
index 0000000..855bc27
--- /dev/null
@@ -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}}