]> granicus.if.org Git - clang/commitdiff
Check value-initializations that occur when an initializer list
authorDouglas Gregor <dgregor@apple.com>
Mon, 2 Feb 2009 17:43:21 +0000 (17:43 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 2 Feb 2009 17:43:21 +0000 (17:43 +0000)
provides too few elements.

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

include/clang/AST/Expr.h
lib/Sema/Sema.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaInit.cpp
test/SemaCXX/dcl_init_aggr.cpp

index 94beaf31e2c7618603065fc0e35baca012cc7361..c933a0c94bb57ee62f0694c3600e1382b9defd17 100644 (file)
@@ -1725,6 +1725,8 @@ public:
     return LBraceLoc.isValid() && RBraceLoc.isValid();
   }
   
+  void setRBraceLoc(SourceLocation Loc) { RBraceLoc = Loc; }
+
   /// @brief Retrieve the initializer list that describes the
   /// syntactic form of the initializer.
   ///
index 6108f9447fdb788ba899ea74c00a332f8425e7a3..60362054889d0eaa87aa9327aa3bfcc74be22b97 100644 (file)
@@ -1715,6 +1715,7 @@ public:
   
   StringLiteral *IsStringLiteralInit(Expr *Init, QualType DeclType);
   bool CheckStringLiteralInit(StringLiteral *strLiteral, QualType &DeclT);
+  bool CheckValueInitialization(QualType Type, SourceLocation Loc);
 
   // type checking C++ declaration initializers (C++ [dcl.init]).
 
index f3bb9a5dfbc8ce6f5b0dbb7a859ee61bfbe24cda..296adc1a9419d6d7a2d5f709534ba76351ad37a1 100644 (file)
@@ -1592,13 +1592,20 @@ Sema::PerformInitializationByConstructor(QualType ClassType,
     return cast<CXXConstructorDecl>(Best->Function);
     
   case OR_No_Viable_Function:
-    Diag(Loc, diag::err_ovl_no_viable_function_in_init)
-      << InitEntity << (unsigned)CandidateSet.size() << Range;
+    if (InitEntity)
+      Diag(Loc, diag::err_ovl_no_viable_function_in_init)
+        << InitEntity << (unsigned)CandidateSet.size() << Range;
+    else
+      Diag(Loc, diag::err_ovl_no_viable_function_in_init)
+        << ClassType << (unsigned)CandidateSet.size() << Range;
     PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false);
     return 0;
     
   case OR_Ambiguous:
-    Diag(Loc, diag::err_ovl_ambiguous_init) << InitEntity << Range;
+    if (InitEntity)
+      Diag(Loc, diag::err_ovl_ambiguous_init) << InitEntity << Range;
+    else
+      Diag(Loc, diag::err_ovl_ambiguous_init) << ClassType << Range;
     PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true);
     return 0;
   }
index 3440d04f07bc988cda3a4192142a33c0cbfbd195..8bc3496a19fba93b799ee031b89223ce9862b47e 100644 (file)
@@ -127,6 +127,10 @@ public:
 void InitListChecker::FillInValueInitializations(InitListExpr *ILE) {
   assert((ILE->getType() != SemaRef->Context.VoidTy) && 
          "Should not have void type");
+  SourceLocation Loc = ILE->getSourceRange().getBegin();
+  if (ILE->getSyntacticForm())
+    Loc = ILE->getSyntacticForm()->getSourceRange().getBegin();
+  
   if (const RecordType *RType = ILE->getType()->getAsRecordType()) {
     unsigned Init = 0, NumInits = ILE->getNumInits();
     for (RecordDecl::field_iterator Field = RType->getDecl()->field_begin(),
@@ -135,24 +139,32 @@ void InitListChecker::FillInValueInitializations(InitListExpr *ILE) {
       if (Field->isUnnamedBitfield())
         continue;
 
-      if (Init >= NumInits) {
+      if (Init >= NumInits || !ILE->getInit(Init)) {
         if (Field->getType()->isReferenceType()) {
           // C++ [dcl.init.aggr]p9:
           //   If an incomplete or empty initializer-list leaves a
           //   member of reference type uninitialized, the program is
           //   ill-formed. 
-          SemaRef->Diag(ILE->getSyntacticForm()->getLocStart(), 
-                        diag::err_init_reference_member_uninitialized)
+          SemaRef->Diag(Loc, diag::err_init_reference_member_uninitialized)
             << Field->getType()
             << ILE->getSyntacticForm()->getSourceRange();
           SemaRef->Diag(Field->getLocation(), 
                         diag::note_uninit_reference_member);
           hadError = true;
+          return;
+        } else if (SemaRef->CheckValueInitialization(Field->getType(), Loc)) {
+          hadError = true;
+          return;
         }
-      } else if (!ILE->getInit(Init))
-        ILE->setInit(Init, 
-                new (SemaRef->Context) ImplicitValueInitExpr(Field->getType()));
-      else if (InitListExpr *InnerILE 
+
+        // FIXME: If value-initialization involves calling a
+        // constructor, should we make that call explicit in the
+        // representation (even when it means extending the
+        // initializer list)?
+        if (Init < NumInits && !hadError)
+          ILE->setInit(Init, 
+              new (SemaRef->Context) ImplicitValueInitExpr(Field->getType()));
+      } else if (InitListExpr *InnerILE 
                  = dyn_cast<InitListExpr>(ILE->getInit(Init)))
         FillInValueInitializations(InnerILE);
       ++Init;
@@ -167,18 +179,33 @@ void InitListChecker::FillInValueInitializations(InitListExpr *ILE) {
 
   QualType ElementType;
   
-  if (const ArrayType *AType = SemaRef->Context.getAsArrayType(ILE->getType()))
+  unsigned NumInits = ILE->getNumInits();
+  unsigned NumElements = NumInits;
+  if (const ArrayType *AType = SemaRef->Context.getAsArrayType(ILE->getType())) {
     ElementType = AType->getElementType();
-  else if (const VectorType *VType = ILE->getType()->getAsVectorType())
+    if (const ConstantArrayType *CAType = dyn_cast<ConstantArrayType>(AType))
+      NumElements = CAType->getSize().getZExtValue();
+  } else if (const VectorType *VType = ILE->getType()->getAsVectorType()) {
     ElementType = VType->getElementType();
-  else 
+    NumElements = VType->getNumElements();
+  } else 
     ElementType = ILE->getType();
   
-  for (unsigned Init = 0, NumInits = ILE->getNumInits(); Init != NumInits; 
-       ++Init) {
-    if (!ILE->getInit(Init))
-      ILE->setInit(Init, 
-                   new (SemaRef->Context) ImplicitValueInitExpr(ElementType));
+  for (unsigned Init = 0; Init != NumElements; ++Init) {
+    if (Init >= NumInits || !ILE->getInit(Init)) {
+      if (SemaRef->CheckValueInitialization(ElementType, Loc)) {
+        hadError = true;
+        return;
+      }
+
+      // FIXME: If value-initialization involves calling a
+      // constructor, should we make that call explicit in the
+      // representation (even when it means extending the
+      // initializer list)?
+      if (Init < NumInits && !hadError)
+        ILE->setInit(Init, 
+                     new (SemaRef->Context) ImplicitValueInitExpr(ElementType));
+    }
     else if (InitListExpr *InnerILE =dyn_cast<InitListExpr>(ILE->getInit(Init)))
       FillInValueInitializations(InnerILE);
   }
@@ -254,9 +281,19 @@ void InitListChecker::CheckImplicitInitList(InitListExpr *ParentIList,
   unsigned StructuredSubobjectInitIndex = 0;
 
   // Check the element types and build the structural subobject.
+  unsigned StartIndex = Index;
   CheckListElementTypes(ParentIList, T, false, Index,
                         StructuredSubobjectInitList, 
                         StructuredSubobjectInitIndex);
+  unsigned EndIndex = (Index == StartIndex? StartIndex : Index - 1);
+  
+  // Update the structured sub-object initialize so that it's ending
+  // range corresponds with the end of the last initializer it used.
+  if (EndIndex < ParentIList->getNumInits()) {
+    SourceLocation EndLoc 
+      = ParentIList->getInit(EndIndex)->getSourceRange().getEnd();
+    StructuredSubobjectInitList->setRBraceLoc(EndLoc);
+  }
 }
 
 void InitListChecker::CheckExplicitInitList(InitListExpr *IList, QualType &T,
@@ -1102,9 +1139,12 @@ InitListChecker::getStructuredSubobjectInit(InitListExpr *IList, unsigned Index,
       << ExistingInit->getSourceRange();
   }
 
+  SourceLocation StartLoc;
+  if (Index < IList->getNumInits())
+    StartLoc = IList->getInit(Index)->getSourceRange().getBegin();
   InitListExpr *Result 
-    = new (SemaRef->Context) InitListExpr(SourceLocation(), 0, 0, 
-                                          SourceLocation());
+    = new (SemaRef->Context) InitListExpr(StartLoc, 0, 0, 
+                                          IList->getSourceRange().getEnd());
   Result->setType(CurrentObjectType);
 
   // Link this new initializer list into the structured initializer
@@ -1257,3 +1297,52 @@ bool Sema::CheckInitList(InitListExpr *&InitList, QualType &DeclType) {
 
   return CheckInitList.HadError();
 }
+
+/// \brief Diagnose any semantic errors with value-initialization of
+/// the given type.
+///
+/// Value-initialization effectively zero-initializes any types
+/// without user-declared constructors, and calls the default
+/// constructor for a for any type that has a user-declared
+/// constructor (C++ [dcl.init]p5). Value-initialization can fail when
+/// a type with a user-declared constructor does not have an
+/// accessible, non-deleted default constructor. In C, everything can
+/// be value-initialized, which corresponds to C's notion of
+/// initializing objects with static storage duration when no
+/// initializer is provided for that object. 
+///
+/// \returns true if there was an error, false otherwise.
+bool Sema::CheckValueInitialization(QualType Type, SourceLocation Loc) {
+  // C++ [dcl.init]p5:
+  //
+  //   To value-initialize an object of type T means:
+
+  //     -- if T is an array type, then each element is value-initialized;
+  if (const ArrayType *AT = Context.getAsArrayType(Type))
+    return CheckValueInitialization(AT->getElementType(), Loc);
+
+  if (const RecordType *RT = Type->getAsRecordType()) {
+    if (const CXXRecordType *CXXRec = dyn_cast<CXXRecordType>(RT)) {
+      // -- if T is a class type (clause 9) with a user-declared
+      //    constructor (12.1), then the default constructor for T is
+      //    called (and the initialization is ill-formed if T has no
+      //    accessible default constructor);
+      if (CXXRec->getDecl()->hasUserDeclaredConstructor())
+        // FIXME: Eventually, we'll need to put the constructor decl
+        // into the AST.
+        return PerformInitializationByConstructor(Type, 0, 0, Loc,
+                                                  SourceRange(Loc), 
+                                                  DeclarationName(),
+                                                  IK_Direct);
+    }
+  }
+
+  if (Type->isReferenceType()) {
+    // C++ [dcl.init]p5:
+    //   [...] A program that calls for default-initialization or
+    //   value-initialization of an entity of reference type is
+    //   ill-formed. [...]
+  }
+
+  return false;
+}
index c2c9d67a4d52bee96abd5921741bcf793fa3b88f..e5015fab030d190e84b99fef42f12ae96ead41cc 100644 (file)
@@ -40,6 +40,21 @@ char cv[4] = { 'a', 's', 'd', 'f', 0 }; // expected-error{{excess elements in ar
 struct TooFew { int a; char* b; int c; }; 
 TooFew too_few = { 1, "asdf" }; // okay
 
+struct NoDefaultConstructor { // expected-note{{candidate function}}
+  NoDefaultConstructor(int); // expected-note{{candidate function}}
+};
+struct TooFewError {
+  int a;
+  NoDefaultConstructor nodef;
+};
+TooFewError too_few_okay = { 1, 1 };
+TooFewError too_few_error = { 1 }; // expected-error{{no matching constructor}}
+
+TooFewError too_few_okay2[2] = { 1, 1 };
+TooFewError too_few_error2[2] = { 1 }; // expected-error{{no matching constructor}}
+
+NoDefaultConstructor too_few_error3[3] = { }; // expected-error{{no matching constructor}}
+
 // C++ [dcl.init.aggr]p8
 struct Empty { };
 struct EmptyTest {