From 87fd703e097c27d63479cb83b687d4000a22bbb1 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 2 Feb 2009 17:43:21 +0000 Subject: [PATCH] Check value-initializations that occur when an initializer list 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 | 2 + lib/Sema/Sema.h | 1 + lib/Sema/SemaDeclCXX.cpp | 13 +++- lib/Sema/SemaInit.cpp | 123 ++++++++++++++++++++++++++++----- test/SemaCXX/dcl_init_aggr.cpp | 15 ++++ 5 files changed, 134 insertions(+), 20 deletions(-) diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 94beaf31e2..c933a0c94b 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -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. /// diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 6108f9447f..6036205488 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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]). diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index f3bb9a5dfb..296adc1a94 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1592,13 +1592,20 @@ Sema::PerformInitializationByConstructor(QualType ClassType, return cast(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; } diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 3440d04f07..8bc3496a19 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -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(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(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(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(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; +} diff --git a/test/SemaCXX/dcl_init_aggr.cpp b/test/SemaCXX/dcl_init_aggr.cpp index c2c9d67a4d..e5015fab03 100644 --- a/test/SemaCXX/dcl_init_aggr.cpp +++ b/test/SemaCXX/dcl_init_aggr.cpp @@ -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 { -- 2.50.1