]> granicus.if.org Git - clang/commitdiff
Migrate the mish-mash of declaration checks in
authorDouglas Gregor <dgregor@apple.com>
Tue, 9 Feb 2010 07:26:29 +0000 (07:26 +0000)
committerDouglas Gregor <dgregor@apple.com>
Tue, 9 Feb 2010 07:26:29 +0000 (07:26 +0000)
Sema::ActOnUninitializedDecl over to InitializationSequence (with
default initialization), eliminating redundancy. More importantly, we
now check that a const definition in C++ has an initilizer, which was
an #if 0'd code for many, many months. A few other tweaks were needed
to get everything working again:

  - Fix all of the places in the testsuite where we defined const
    objects without initializers (now that we diagnose this issue)
  - Teach instantiation of static data members to find the previous
    declaration, so that we build proper redeclaration
    chains. Previously, we had the redeclaration chain but built it
    too late to be useful, because...
  - Teach instantiation of static data member definitions not to try
    to check an initializer if a previous declaration already had an
    initializer. This makes sure that we don't complain about static
    const data members with in-class initializers and out-of-line
    definitions.
  - Move all of the incomplete-type checking logic out of
    Sema::FinalizeDeclaratorGroup; it makes more sense in
    ActOnUnitializedDecl.

There may still be a few places where we can improve these
diagnostics. I'll address that as a separate commit.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95657 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaDecl.cpp
lib/Sema/SemaInit.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/CXX/dcl.decl/dcl.init/p6.cpp
test/CXX/expr/p8.cpp
test/CXX/temp/temp.decls/temp.mem/p5.cpp
test/Sema/incomplete-decl.c
test/SemaCXX/cast-conversion.cpp
test/SemaCXX/dcl_ambig_res.cpp
test/SemaTemplate/instantiate-declref-ice.cpp
test/SemaTemplate/instantiate-expr-4.cpp

index 521b420e56797ea7ea19624a7cbd99108cd41579..e12902bff6bdd2f3fe381e61fa2e4b2b96061609 100644 (file)
@@ -3658,24 +3658,6 @@ void Sema::ActOnUninitializedDecl(DeclPtrTy dcl,
   if (VarDecl *Var = dyn_cast<VarDecl>(RealDecl)) {
     QualType Type = Var->getType();
 
-    // Record tentative definitions.
-    if (Var->isTentativeDefinitionNow())
-      TentativeDefinitions.push_back(Var);
-
-    // C++ [dcl.init.ref]p3:
-    //   The initializer can be omitted for a reference only in a
-    //   parameter declaration (8.3.5), in the declaration of a
-    //   function return type, in the declaration of a class member
-    //   within its class declaration (9.2), and where the extern
-    //   specifier is explicitly used.
-    if (Type->isReferenceType() && !Var->hasExternalStorage()) {
-      Diag(Var->getLocation(), diag::err_reference_var_requires_init)
-        << Var->getDeclName()
-        << SourceRange(Var->getLocation(), Var->getLocation());
-      Var->setInvalidDecl();
-      return;
-    }
-
     // C++0x [dcl.spec.auto]p3
     if (TypeContainsUndeducedAuto) {
       Diag(Var->getLocation(), diag::err_auto_var_requires_init)
@@ -3684,99 +3666,133 @@ void Sema::ActOnUninitializedDecl(DeclPtrTy dcl,
       return;
     }
 
-    // An array without size is an incomplete type, and there are no special
-    // rules in C++ to make such a definition acceptable.
-    if (getLangOptions().CPlusPlus && Type->isIncompleteArrayType() &&
-        !Var->hasExternalStorage()) {
+    switch (Var->isThisDeclarationADefinition()) {
+    case VarDecl::Definition:
+      if (!Var->isStaticDataMember() || !Var->getAnyInitializer())
+        break;
+
+      // We have an out-of-line definition of a static data member
+      // that has an in-class initializer, so we type-check this like
+      // a declaration. 
+      //
+      // Fall through
+      
+    case VarDecl::DeclarationOnly:
+      // It's only a declaration. 
+
+      // Block scope. C99 6.7p7: If an identifier for an object is
+      // declared with no linkage (C99 6.2.2p6), the type for the
+      // object shall be complete.
+      if (!Type->isDependentType() && Var->isBlockVarDecl() && 
+          !Var->getLinkage() && !Var->isInvalidDecl() &&
+          RequireCompleteType(Var->getLocation(), Type,
+                              diag::err_typecheck_decl_incomplete_type))
+        Var->setInvalidDecl();
+
+      // Make sure that the type is not abstract.
+      if (!Type->isDependentType() && !Var->isInvalidDecl() &&
+          RequireNonAbstractType(Var->getLocation(), Type,
+                                 diag::err_abstract_type_in_decl,
+                                 AbstractVariableType))
+        Var->setInvalidDecl();
+      return;
+
+    case VarDecl::TentativeDefinition:
+      // File scope. C99 6.9.2p2: A declaration of an identifier for an
+      // object that has file scope without an initializer, and without a
+      // storage-class specifier or with the storage-class specifier "static",
+      // constitutes a tentative definition. Note: A tentative definition with
+      // external linkage is valid (C99 6.2.2p5).
+      if (!Var->isInvalidDecl()) {
+        if (const IncompleteArrayType *ArrayT
+                                    = Context.getAsIncompleteArrayType(Type)) {
+          if (RequireCompleteType(Var->getLocation(),
+                                  ArrayT->getElementType(),
+                                  diag::err_illegal_decl_array_incomplete_type))
+            Var->setInvalidDecl();
+        } else if (Var->getStorageClass() == VarDecl::Static) {
+          // C99 6.9.2p3: If the declaration of an identifier for an object is
+          // a tentative definition and has internal linkage (C99 6.2.2p3), the
+          // declared type shall not be an incomplete type.
+          // NOTE: code such as the following
+          //     static struct s;
+          //     struct s { int a; };
+          // is accepted by gcc. Hence here we issue a warning instead of
+          // an error and we do not invalidate the static declaration.
+          // NOTE: to avoid multiple warnings, only check the first declaration.
+          if (Var->getPreviousDeclaration() == 0)
+            RequireCompleteType(Var->getLocation(), Type,
+                                diag::ext_typecheck_decl_incomplete_type);
+        }
+      }
+
+      // Record the tentative definition; we're done.
+      if (!Var->isInvalidDecl())
+        TentativeDefinitions.push_back(Var);
+      return;
+    }
+
+    // Provide a specific diagnostic for uninitialized variable
+    // definitions with incomplete array type.
+    if (Type->isIncompleteArrayType()) {
       Diag(Var->getLocation(),
            diag::err_typecheck_incomplete_array_needs_initializer);
       Var->setInvalidDecl();
       return;
     }
 
-    // C++ [temp.expl.spec]p15:
-    //   An explicit specialization of a static data member of a template is a
-    //   definition if the declaration includes an initializer; otherwise, it 
-    //   is a declaration.
-    if (Var->isStaticDataMember() &&
-        Var->getInstantiatedFromStaticDataMember() &&
-        Var->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+   // Provide a specific diagnostic for uninitialized variable
+   // definitions with reference type.
+   if (Type->isReferenceType()) {
+     Diag(Var->getLocation(), diag::err_reference_var_requires_init)
+       << Var->getDeclName()
+       << SourceRange(Var->getLocation(), Var->getLocation());
+     Var->setInvalidDecl();
+     return;
+   }
+
+    // Do not attempt to type-check the default initializer for a
+    // variable with dependent type.
+    if (Type->isDependentType())
       return;
-    
-    // C++ [dcl.init]p9:
-    //   If no initializer is specified for an object, and the object
-    //   is of (possibly cv-qualified) non-POD class type (or array
-    //   thereof), the object shall be default-initialized; if the
-    //   object is of const-qualified type, the underlying class type
-    //   shall have a user-declared default constructor.
-    //
-    // FIXME: Diagnose the "user-declared default constructor" bit.
-    if (getLangOptions().CPlusPlus) {
-      QualType InitType = Type;
-      if (const ArrayType *Array = Context.getAsArrayType(Type))
-        InitType = Context.getBaseElementType(Array);
-      if ((!Var->hasExternalStorage() && !Var->isExternC()) &&
-          InitType->isRecordType() && !InitType->isDependentType()) {
-        if (!RequireCompleteType(Var->getLocation(), InitType,
-                                 diag::err_invalid_incomplete_type_use)) {
-          InitializedEntity Entity
-            = InitializedEntity::InitializeVariable(Var);
-          InitializationKind Kind
-            = InitializationKind::CreateDefault(Var->getLocation());
-
-          InitializationSequence InitSeq(*this, Entity, Kind, 0, 0);
-          OwningExprResult Init = InitSeq.Perform(*this, Entity, Kind,
-                                                  MultiExprArg(*this, 0, 0));
-          if (Init.isInvalid())
-            Var->setInvalidDecl();
-          else {
-            if (Init.get())
-              Var->setInit(Context, 
-                       MaybeCreateCXXExprWithTemporaries(Init.takeAs<Expr>()));
-            FinalizeVarWithDestructor(Var, InitType->getAs<RecordType>());
-          }
-        } else {
-          Var->setInvalidDecl();
-        }
-      }
 
-      // The variable can not have an abstract class type.
-      if (RequireNonAbstractType(Var->getLocation(), Type,
-                                 diag::err_abstract_type_in_decl,
-                                 AbstractVariableType))
-        Var->setInvalidDecl();
+    if (Var->isInvalidDecl())
+      return;
+
+    if (RequireCompleteType(Var->getLocation(), 
+                            Context.getBaseElementType(Type),
+                            diag::err_typecheck_decl_incomplete_type)) {
+      Var->setInvalidDecl();
+      return;
     }
 
-#if 0
-    // FIXME: Temporarily disabled because we are not properly parsing
-    // linkage specifications on declarations, e.g.,
-    //
-    //   extern "C" const CGPoint CGPointerZero;
-    //
-    // C++ [dcl.init]p9:
-    //
-    //     If no initializer is specified for an object, and the
-    //     object is of (possibly cv-qualified) non-POD class type (or
-    //     array thereof), the object shall be default-initialized; if
-    //     the object is of const-qualified type, the underlying class
-    //     type shall have a user-declared default
-    //     constructor. Otherwise, if no initializer is specified for
-    //     an object, the object and its subobjects, if any, have an
-    //     indeterminate initial value; if the object or any of its
-    //     subobjects are of const-qualified type, the program is
-    //     ill-formed.
-    //
-    // This isn't technically an error in C, so we don't diagnose it.
-    //
-    // FIXME: Actually perform the POD/user-defined default
-    // constructor check.
-    if (getLangOptions().CPlusPlus &&
-        Context.getCanonicalType(Type).isConstQualified() &&
-        !Var->hasExternalStorage())
-      Diag(Var->getLocation(),  diag::err_const_var_requires_init)
-        << Var->getName()
-        << SourceRange(Var->getLocation(), Var->getLocation());
-#endif
+    // The variable can not have an abstract class type.
+    if (RequireNonAbstractType(Var->getLocation(), Type,
+                               diag::err_abstract_type_in_decl,
+                               AbstractVariableType)) {
+      Var->setInvalidDecl();
+      return;
+    }
+
+    InitializedEntity Entity = InitializedEntity::InitializeVariable(Var);
+    InitializationKind Kind
+      = InitializationKind::CreateDefault(Var->getLocation());
+    
+    InitializationSequence InitSeq(*this, Entity, Kind, 0, 0);
+    OwningExprResult Init = InitSeq.Perform(*this, Entity, Kind,
+                                            MultiExprArg(*this, 0, 0));
+    if (Init.isInvalid())
+      Var->setInvalidDecl();
+    else {
+      if (Init.get())
+        Var->setInit(Context, 
+                     MaybeCreateCXXExprWithTemporaries(Init.takeAs<Expr>()));
+
+      if (getLangOptions().CPlusPlus)
+        if (const RecordType *Record
+                        = Context.getBaseElementType(Type)->getAs<RecordType>())
+          FinalizeVarWithDestructor(Var, Record);
+    }
   }
 }
 
@@ -3792,78 +3808,6 @@ Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS,
     if (Decl *D = Group[i].getAs<Decl>())
       Decls.push_back(D);
 
-  // Perform semantic analysis that depends on having fully processed both
-  // the declarator and initializer.
-  for (unsigned i = 0, e = Decls.size(); i != e; ++i) {
-    VarDecl *IDecl = dyn_cast<VarDecl>(Decls[i]);
-    if (!IDecl)
-      continue;
-    QualType T = IDecl->getType();
-
-    // Block scope. C99 6.7p7: If an identifier for an object is declared with
-    // no linkage (C99 6.2.2p6), the type for the object shall be complete...
-    if (IDecl->isBlockVarDecl() && !IDecl->hasExternalStorage()) {
-      if (T->isDependentType()) {
-        // If T is dependent, we should not require a complete type.
-        // (RequireCompleteType shouldn't be called with dependent types.)
-        // But we still can at least check if we've got an array of unspecified
-        // size without an initializer.
-        if (!IDecl->isInvalidDecl() && T->isIncompleteArrayType() &&
-            !IDecl->getInit()) {
-          Diag(IDecl->getLocation(), diag::err_typecheck_decl_incomplete_type)
-            << T;
-          IDecl->setInvalidDecl();
-        }
-      } else if (!IDecl->isInvalidDecl()) {
-        // If T is an incomplete array type with an initializer list that is
-        // dependent on something, its size has not been fixed. We could attempt
-        // to fix the size for such arrays, but we would still have to check
-        // here for initializers containing a C++0x vararg expansion, e.g.
-        // template <typename... Args> void f(Args... args) {
-        //   int vals[] = { args };
-        // }
-        const IncompleteArrayType *IAT = Context.getAsIncompleteArrayType(T);
-        Expr *Init = IDecl->getInit();
-        if (IAT && Init &&
-            (Init->isTypeDependent() || Init->isValueDependent())) {
-          // Check that the member type of the array is complete, at least.
-          if (RequireCompleteType(IDecl->getLocation(), IAT->getElementType(),
-                                  diag::err_typecheck_decl_incomplete_type))
-            IDecl->setInvalidDecl();
-        } else if (RequireCompleteType(IDecl->getLocation(), T,
-                                      diag::err_typecheck_decl_incomplete_type))
-          IDecl->setInvalidDecl();
-      }
-    }
-    // File scope. C99 6.9.2p2: A declaration of an identifier for an
-    // object that has file scope without an initializer, and without a
-    // storage-class specifier or with the storage-class specifier "static",
-    // constitutes a tentative definition. Note: A tentative definition with
-    // external linkage is valid (C99 6.2.2p5).
-    if (IDecl->isThisDeclarationADefinition() == VarDecl::TentativeDefinition &&
-        !IDecl->isInvalidDecl()) {
-      if (const IncompleteArrayType *ArrayT
-          = Context.getAsIncompleteArrayType(T)) {
-        if (RequireCompleteType(IDecl->getLocation(),
-                                ArrayT->getElementType(),
-                                diag::err_illegal_decl_array_incomplete_type))
-          IDecl->setInvalidDecl();
-      } else if (IDecl->getStorageClass() == VarDecl::Static) {
-        // C99 6.9.2p3: If the declaration of an identifier for an object is
-        // a tentative definition and has internal linkage (C99 6.2.2p3), the
-        // declared type shall not be an incomplete type.
-        // NOTE: code such as the following
-        //     static struct s;
-        //     struct s { int a; };
-        // is accepted by gcc. Hence here we issue a warning instead of
-        // an error and we do not invalidate the static declaration.
-        // NOTE: to avoid multiple warnings, only check the first declaration.
-        if (IDecl->getPreviousDeclaration() == 0)
-          RequireCompleteType(IDecl->getLocation(), T,
-                              diag::ext_typecheck_decl_incomplete_type);
-      }
-    }
-  }
   return DeclGroupPtrTy::make(DeclGroupRef::Create(Context,
                                                    Decls.data(), Decls.size()));
 }
index 3e710cfd5a3b0ce7f046d95a25ed7d8e923fc56c..d47b8caec0e39bcbfdff9123f00bb45d07d4139e 100644 (file)
@@ -2650,7 +2650,7 @@ static void TryDefaultInitialization(Sema &S,
   //     - if T is a (possibly cv-qualified) class type (Clause 9), the default
   //       constructor for T is called (and the initialization is ill-formed if
   //       T has no accessible default constructor);
-  if (DestType->isRecordType()) {
+  if (DestType->isRecordType() && S.getLangOptions().CPlusPlus) {
     return TryConstructorInitialization(S, Entity, Kind, 0, 0, DestType,
                                         Sequence);
   }
@@ -2661,7 +2661,7 @@ static void TryDefaultInitialization(Sema &S,
   //   If a program calls for the default initialization of an object of
   //   a const-qualified type T, T shall be a class type with a user-provided 
   //   default constructor.
-  if (DestType.isConstQualified())
+  if (DestType.isConstQualified() && S.getLangOptions().CPlusPlus)
     Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst);
 }
 
index d7820bbc2e66ddf17a1500e0c1a5cacd11ef1317..08cb681960054b9ec63c806e7b160d2d67560e4b 100644 (file)
@@ -217,6 +217,8 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
   // FIXME: having to fake up a LookupResult is dumb.
   LookupResult Previous(SemaRef, Var->getDeclName(), Var->getLocation(),
                         Sema::LookupOrdinaryName);
+  if (D->isStaticDataMember())
+    SemaRef.LookupQualifiedName(Previous, Owner, false);
   SemaRef.CheckVariableDeclaration(Var, Previous, Redeclaration);
 
   if (D->isOutOfLine()) {
@@ -232,7 +234,9 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) {
     SemaRef.Context.setInstantiatedFromStaticDataMember(Var, D, 
                                                      TSK_ImplicitInstantiation);
   
-  if (D->getInit()) {
+  if (Var->getAnyInitializer()) {
+    // We already have an initializer in the class.
+  } else if (D->getInit()) {
     if (Var->isStaticDataMember() && !D->isOutOfLine())
       SemaRef.PushExpressionEvaluationContext(Sema::Unevaluated);
     else
@@ -1821,7 +1825,6 @@ void Sema::InstantiateStaticDataMemberDefinition(
   CurContext = PreviousContext;
 
   if (Var) {
-    Var->setPreviousDeclaration(OldVar);
     MemberSpecializationInfo *MSInfo = OldVar->getMemberSpecializationInfo();
     assert(MSInfo && "Missing member specialization information?");
     Var->setTemplateSpecializationKind(MSInfo->getTemplateSpecializationKind(),
index 370bafcfbd5bb28b63aaa2394cd2b9283981da6a..f627a199eca672f369854a142d2dff38578f2daf 100644 (file)
@@ -11,5 +11,5 @@ struct HasUserDefault { HasUserDefault(); };
 void test_const_default_init() {
   const NoUserDefault x1; // expected-error{{default initialization of an object of const type 'struct NoUserDefault const' requires a user-provided default constructor}}
   const HasUserDefault x2;
-  const int x3; // FIXME: xpected-error{{default initialization of an object of const type 'struct NoUserDefault const' requires a user-provided default constructor}}
+  const int x3; // expected-error{{default initialization of an object of const type 'int const'}}
 }
index cc834d9dc8d8275d79098516565e7e514b7524a6..2f6c094301ee2d2063c31b3284123fb995d57806 100644 (file)
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 
 int a0;
-const volatile int a1;
+const volatile int a1 = 2;
 int a2[16];
 int a3();
 
index 098ffa4dec752464cd4c11a05fea6f511da43e68..b0078d4bdb6d6cc1fa74b93fe477473afd5b80d1 100644 (file)
@@ -55,7 +55,7 @@ template double Foo::As2();
 // Partial ordering with conversion function templates.
 struct X0 {
   template<typename T> operator T*() {
-    T x;
+    T x = 1;
     x = 17; // expected-error{{read-only variable is not assignable}}
   }
   
index 753d9c0a3c1a0590736501c5888e5c1922eb4f54..e5b6d5f0da6df110ae7065ae8d4ce150c129e280 100644 (file)
@@ -16,7 +16,7 @@ int ary[]; // expected-warning {{tentative array definition assumed to have one
 struct foo bary[]; // expected-error {{array has incomplete element type 'struct foo'}}
 
 void func() {
-  int ary[]; // expected-error{{variable has incomplete type 'int []'}}
+  int ary[]; // expected-error{{definition of variable with array type needs an explicit size or an initializer}}
   void b; // expected-error {{variable has incomplete type 'void'}}
   struct foo f; // expected-error {{variable has incomplete type 'struct foo'}}
 }
index 77f4a528906d623fa456f9625f1365476f00ad4b..074e1330403de68e5320d8efcf7d1bccb507fc60 100644 (file)
@@ -30,7 +30,7 @@ X0<T> make_X0(const T &Val) {
 }
 
 void test_X0() {
-  const char array[2];
+  const char array[2] = { 'a', 'b' };
   make_X0(array);
 }
 
index 859d2045da0991e7509fa7a69f224d73d1a79c38..f0ba2978e8d73ab5fe535b3756db69bdb98a9d18 100644 (file)
@@ -12,8 +12,8 @@ void foo(double a)
 { 
   S w(int(a)); // expected-warning{{disambiguated}}
   w(17);
-  S x(int()); // expected-warning{{disambiguated}}
-  x(&returns_an_int);
+  S x1(int()); // expected-warning{{disambiguated}}
+  x1(&returns_an_int);
   S y((int)a); 
   y.bar();
   S z = int(a);
index e4e071dd20d7a24f8695ff921a324205475e27de..e88b49447cb90bdba5ed3939307387074696f633 100644 (file)
@@ -1,5 +1,4 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
-
 template<int i> struct x {
   static const int j = i;
   x<j>* y;
@@ -10,7 +9,6 @@ const int x<i>::j;
 
 int array0[x<2>::j];
 
-
 template<typename T>
 struct X0 {
   static const unsigned value = sizeof(T);
index b5ffa4b39b2a1621cbde7058b51a6e4c00e8ef22..c5eb3cc53e7a26a17da14b0670adee0be15e6a3a 100644 (file)
@@ -173,8 +173,8 @@ struct is_pod {
   static const bool value = __is_pod(T);
 };
 
-static const int is_pod0[is_pod<X>::value? -1 : 1];
-static const int is_pod1[is_pod<Y>::value? 1 : -1];
+static int is_pod0[is_pod<X>::value? -1 : 1];
+static int is_pod1[is_pod<Y>::value? 1 : -1];
 
 // ---------------------------------------------------------------------
 // initializer lists