diff options
Diffstat (limited to 'lib/Sema/SemaInit.cpp')
-rw-r--r-- | lib/Sema/SemaInit.cpp | 1210 |
1 files changed, 792 insertions, 418 deletions
diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index bc1069609336..10cb7acad567 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -16,6 +16,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" #include "clang/AST/TypeLoc.h" +#include "clang/Basic/CharInfo.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/Designator.h" #include "clang/Sema/Initialization.h" @@ -197,7 +198,7 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT, llvm::APInt ConstVal(32, StrLength); // Return a new array type (C99 6.7.8p22). DeclT = S.Context.getConstantArrayType(IAT->getElementType(), - ConstVal, + ConstVal, nullptr, ArrayType::Normal, 0); updateStringLiteralType(Str, DeclT); return; @@ -271,13 +272,24 @@ namespace { /// point. CheckDesignatedInitializer() recursively steps into the /// designated subobject and manages backing out the recursion to /// initialize the subobjects after the one designated. +/// +/// If an initializer list contains any designators, we build a placeholder +/// structured list even in 'verify only' mode, so that we can track which +/// elements need 'empty' initializtion. class InitListChecker { Sema &SemaRef; - bool hadError; - bool VerifyOnly; // no diagnostics, no structure building + bool hadError = false; + bool VerifyOnly; // No diagnostics. bool TreatUnavailableAsInvalid; // Used only in VerifyOnly mode. - llvm::DenseMap<InitListExpr *, InitListExpr *> SyntacticToSemantic; - InitListExpr *FullyStructuredList; + bool InOverloadResolution; + InitListExpr *FullyStructuredList = nullptr; + NoInitExpr *DummyExpr = nullptr; + + NoInitExpr *getDummyInit() { + if (!DummyExpr) + DummyExpr = new (SemaRef.Context) NoInitExpr(SemaRef.Context.VoidTy); + return DummyExpr; + } void CheckImplicitInitList(const InitializedEntity &Entity, InitListExpr *ParentIList, QualType T, @@ -352,14 +364,71 @@ class InitListChecker { void UpdateStructuredListElement(InitListExpr *StructuredList, unsigned &StructuredIndex, Expr *expr); + InitListExpr *createInitListExpr(QualType CurrentObjectType, + SourceRange InitRange, + unsigned ExpectedNumInits); int numArrayElements(QualType DeclType); int numStructUnionElements(QualType DeclType); - static ExprResult PerformEmptyInit(Sema &SemaRef, - SourceLocation Loc, - const InitializedEntity &Entity, - bool VerifyOnly, - bool TreatUnavailableAsInvalid); + ExprResult PerformEmptyInit(SourceLocation Loc, + const InitializedEntity &Entity); + + /// Diagnose that OldInit (or part thereof) has been overridden by NewInit. + void diagnoseInitOverride(Expr *OldInit, SourceRange NewInitRange, + bool FullyOverwritten = true) { + // Overriding an initializer via a designator is valid with C99 designated + // initializers, but ill-formed with C++20 designated initializers. + unsigned DiagID = SemaRef.getLangOpts().CPlusPlus + ? diag::ext_initializer_overrides + : diag::warn_initializer_overrides; + + if (InOverloadResolution && SemaRef.getLangOpts().CPlusPlus) { + // In overload resolution, we have to strictly enforce the rules, and so + // don't allow any overriding of prior initializers. This matters for a + // case such as: + // + // union U { int a, b; }; + // struct S { int a, b; }; + // void f(U), f(S); + // + // Here, f({.a = 1, .b = 2}) is required to call the struct overload. For + // consistency, we disallow all overriding of prior initializers in + // overload resolution, not only overriding of union members. + hadError = true; + } else if (OldInit->getType().isDestructedType() && !FullyOverwritten) { + // If we'll be keeping around the old initializer but overwriting part of + // the object it initialized, and that object is not trivially + // destructible, this can leak. Don't allow that, not even as an + // extension. + // + // FIXME: It might be reasonable to allow this in cases where the part of + // the initializer that we're overriding has trivial destruction. + DiagID = diag::err_initializer_overrides_destructed; + } else if (!OldInit->getSourceRange().isValid()) { + // We need to check on source range validity because the previous + // initializer does not have to be an explicit initializer. e.g., + // + // struct P { int a, b; }; + // struct PP { struct P p } l = { { .a = 2 }, .p.b = 3 }; + // + // There is an overwrite taking place because the first braced initializer + // list "{ .a = 2 }" already provides value for .p.b (which is zero). + // + // Such overwrites are harmless, so we don't diagnose them. (Note that in + // C++, this cannot be reached unless we've already seen and diagnosed a + // different conformance issue, such as a mixture of designated and + // non-designated initializers or a multi-level designator.) + return; + } + + if (!VerifyOnly) { + SemaRef.Diag(NewInitRange.getBegin(), DiagID) + << NewInitRange << FullyOverwritten << OldInit->getType(); + SemaRef.Diag(OldInit->getBeginLoc(), diag::note_previous_initializer) + << (OldInit->HasSideEffects(SemaRef.Context) && FullyOverwritten) + << OldInit->getSourceRange(); + } + } // Explanation on the "FillWithNoInit" mode: // @@ -399,9 +468,9 @@ class InitListChecker { SourceLocation Loc); public: - InitListChecker(Sema &S, const InitializedEntity &Entity, - InitListExpr *IL, QualType &T, bool VerifyOnly, - bool TreatUnavailableAsInvalid); + InitListChecker(Sema &S, const InitializedEntity &Entity, InitListExpr *IL, + QualType &T, bool VerifyOnly, bool TreatUnavailableAsInvalid, + bool InOverloadResolution = false); bool HadError() { return hadError; } // Retrieves the fully-structured initializer list used for @@ -411,11 +480,8 @@ public: } // end anonymous namespace -ExprResult InitListChecker::PerformEmptyInit(Sema &SemaRef, - SourceLocation Loc, - const InitializedEntity &Entity, - bool VerifyOnly, - bool TreatUnavailableAsInvalid) { +ExprResult InitListChecker::PerformEmptyInit(SourceLocation Loc, + const InitializedEntity &Entity) { InitializationKind Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true); MultiExprArg SubInit; @@ -517,43 +583,44 @@ ExprResult InitListChecker::PerformEmptyInit(Sema &SemaRef, << Entity.getElementIndex(); } } + hadError = true; return ExprError(); } - return VerifyOnly ? ExprResult(static_cast<Expr *>(nullptr)) + return VerifyOnly ? ExprResult() : InitSeq.Perform(SemaRef, Entity, Kind, SubInit); } void InitListChecker::CheckEmptyInitializable(const InitializedEntity &Entity, SourceLocation Loc) { - assert(VerifyOnly && - "CheckEmptyInitializable is only inteded for verification mode."); - if (PerformEmptyInit(SemaRef, Loc, Entity, /*VerifyOnly*/true, - TreatUnavailableAsInvalid).isInvalid()) - hadError = true; + // If we're building a fully-structured list, we'll check this at the end + // once we know which elements are actually initialized. Otherwise, we know + // that there are no designators so we can just check now. + if (FullyStructuredList) + return; + PerformEmptyInit(Loc, Entity); } void InitListChecker::FillInEmptyInitForBase( unsigned Init, const CXXBaseSpecifier &Base, const InitializedEntity &ParentEntity, InitListExpr *ILE, bool &RequiresSecondPass, bool FillWithNoInit) { - assert(Init < ILE->getNumInits() && "should have been expanded"); - InitializedEntity BaseEntity = InitializedEntity::InitializeBase( SemaRef.Context, &Base, false, &ParentEntity); - if (!ILE->getInit(Init)) { - ExprResult BaseInit = - FillWithNoInit - ? new (SemaRef.Context) NoInitExpr(Base.getType()) - : PerformEmptyInit(SemaRef, ILE->getEndLoc(), BaseEntity, - /*VerifyOnly*/ false, TreatUnavailableAsInvalid); + if (Init >= ILE->getNumInits() || !ILE->getInit(Init)) { + ExprResult BaseInit = FillWithNoInit + ? new (SemaRef.Context) NoInitExpr(Base.getType()) + : PerformEmptyInit(ILE->getEndLoc(), BaseEntity); if (BaseInit.isInvalid()) { hadError = true; return; } - ILE->setInit(Init, BaseInit.getAs<Expr>()); + if (!VerifyOnly) { + assert(Init < ILE->getNumInits() && "should have been expanded"); + ILE->setInit(Init, BaseInit.getAs<Expr>()); + } } else if (InitListExpr *InnerILE = dyn_cast<InitListExpr>(ILE->getInit(Init))) { FillInEmptyInitializations(BaseEntity, InnerILE, RequiresSecondPass, @@ -576,12 +643,14 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, InitializedEntity MemberEntity = InitializedEntity::InitializeMember(Field, &ParentEntity); - if (const RecordType *RType = ILE->getType()->getAs<RecordType>()) - if (!RType->getDecl()->isUnion()) - assert(Init < NumInits && "This ILE should have been expanded"); - if (Init >= NumInits || !ILE->getInit(Init)) { + if (const RecordType *RType = ILE->getType()->getAs<RecordType>()) + if (!RType->getDecl()->isUnion()) + assert((Init < NumInits || VerifyOnly) && + "This ILE should have been expanded"); + if (FillWithNoInit) { + assert(!VerifyOnly && "should not fill with no-init in verify-only mode"); Expr *Filler = new (SemaRef.Context) NoInitExpr(Field->getType()); if (Init < NumInits) ILE->setInit(Init, Filler); @@ -594,6 +663,9 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, // members in the aggregate, then each member not explicitly initialized // shall be initialized from its brace-or-equal-initializer [...] if (Field->hasInClassInitializer()) { + if (VerifyOnly) + return; + ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); if (DIE.isInvalid()) { hadError = true; @@ -610,28 +682,28 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, } 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(Loc, diag::err_init_reference_member_uninitialized) - << Field->getType() - << ILE->getSyntacticForm()->getSourceRange(); - SemaRef.Diag(Field->getLocation(), - diag::note_uninit_reference_member); + if (!VerifyOnly) { + // 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(Loc, diag::err_init_reference_member_uninitialized) + << Field->getType() + << ILE->getSyntacticForm()->getSourceRange(); + SemaRef.Diag(Field->getLocation(), + diag::note_uninit_reference_member); + } hadError = true; return; } - ExprResult MemberInit = PerformEmptyInit(SemaRef, Loc, MemberEntity, - /*VerifyOnly*/false, - TreatUnavailableAsInvalid); + ExprResult MemberInit = PerformEmptyInit(Loc, MemberEntity); if (MemberInit.isInvalid()) { hadError = true; return; } - if (hadError) { + if (hadError || VerifyOnly) { // Do nothing } else if (Init < NumInits) { ILE->setInit(Init, MemberInit.getAs<Expr>()); @@ -644,14 +716,15 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, RequiresSecondPass = true; } } else if (InitListExpr *InnerILE - = dyn_cast<InitListExpr>(ILE->getInit(Init))) + = dyn_cast<InitListExpr>(ILE->getInit(Init))) { FillInEmptyInitializations(MemberEntity, InnerILE, RequiresSecondPass, ILE, Init, FillWithNoInit); - else if (DesignatedInitUpdateExpr *InnerDIUE - = dyn_cast<DesignatedInitUpdateExpr>(ILE->getInit(Init))) + } else if (DesignatedInitUpdateExpr *InnerDIUE = + dyn_cast<DesignatedInitUpdateExpr>(ILE->getInit(Init))) { FillInEmptyInitializations(MemberEntity, InnerDIUE->getUpdater(), RequiresSecondPass, ILE, Init, /*FillWithNoInit =*/true); + } } /// Recursively replaces NULL values within the given initializer list @@ -667,6 +740,11 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, assert((ILE->getType() != SemaRef.Context.VoidTy) && "Should not have void type"); + // We don't need to do any checks when just filling NoInitExprs; that can't + // fail. + if (FillWithNoInit && VerifyOnly) + return; + // If this is a nested initializer list, we might have changed its contents // (and therefore some of its properties, such as instantiation-dependence) // while filling it in. Inform the outer initializer list so that its state @@ -709,7 +787,7 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, unsigned NumElems = numStructUnionElements(ILE->getType()); if (RDecl->hasFlexibleArrayMember()) ++NumElems; - if (ILE->getNumInits() < NumElems) + if (!VerifyOnly && ILE->getNumInits() < NumElems) ILE->resizeInits(SemaRef.Context, NumElems); unsigned Init = 0; @@ -771,6 +849,7 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, } else ElementType = ILE->getType(); + bool SkipEmptyInitChecks = false; for (unsigned Init = 0; Init != NumElements; ++Init) { if (hadError) return; @@ -779,21 +858,25 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, ElementEntity.getKind() == InitializedEntity::EK_VectorElement) ElementEntity.setElementIndex(Init); - if (Init >= NumInits && ILE->hasArrayFiller()) + if (Init >= NumInits && (ILE->hasArrayFiller() || SkipEmptyInitChecks)) return; Expr *InitExpr = (Init < NumInits ? ILE->getInit(Init) : nullptr); if (!InitExpr && Init < NumInits && ILE->hasArrayFiller()) ILE->setInit(Init, ILE->getArrayFiller()); else if (!InitExpr && !ILE->hasArrayFiller()) { + // In VerifyOnly mode, there's no point performing empty initialization + // more than once. + if (SkipEmptyInitChecks) + continue; + Expr *Filler = nullptr; if (FillWithNoInit) Filler = new (SemaRef.Context) NoInitExpr(ElementType); else { ExprResult ElementInit = - PerformEmptyInit(SemaRef, ILE->getEndLoc(), ElementEntity, - /*VerifyOnly*/ false, TreatUnavailableAsInvalid); + PerformEmptyInit(ILE->getEndLoc(), ElementEntity); if (ElementInit.isInvalid()) { hadError = true; return; @@ -804,6 +887,8 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, if (hadError) { // Do nothing + } else if (VerifyOnly) { + SkipEmptyInitChecks = true; } else if (Init < NumInits) { // For arrays, just set the expression used for value-initialization // of the "holes" in the array. @@ -829,34 +914,46 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, } } } else if (InitListExpr *InnerILE - = dyn_cast_or_null<InitListExpr>(InitExpr)) + = dyn_cast_or_null<InitListExpr>(InitExpr)) { FillInEmptyInitializations(ElementEntity, InnerILE, RequiresSecondPass, ILE, Init, FillWithNoInit); - else if (DesignatedInitUpdateExpr *InnerDIUE - = dyn_cast_or_null<DesignatedInitUpdateExpr>(InitExpr)) + } else if (DesignatedInitUpdateExpr *InnerDIUE = + dyn_cast_or_null<DesignatedInitUpdateExpr>(InitExpr)) { FillInEmptyInitializations(ElementEntity, InnerDIUE->getUpdater(), RequiresSecondPass, ILE, Init, /*FillWithNoInit =*/true); + } } } +static bool hasAnyDesignatedInits(const InitListExpr *IL) { + for (const Stmt *Init : *IL) + if (Init && isa<DesignatedInitExpr>(Init)) + return true; + return false; +} + InitListChecker::InitListChecker(Sema &S, const InitializedEntity &Entity, - InitListExpr *IL, QualType &T, - bool VerifyOnly, - bool TreatUnavailableAsInvalid) - : SemaRef(S), VerifyOnly(VerifyOnly), - TreatUnavailableAsInvalid(TreatUnavailableAsInvalid) { - // FIXME: Check that IL isn't already the semantic form of some other - // InitListExpr. If it is, we'd create a broken AST. - - hadError = false; - - FullyStructuredList = - getStructuredSubobjectInit(IL, 0, T, nullptr, 0, IL->getSourceRange()); + InitListExpr *IL, QualType &T, bool VerifyOnly, + bool TreatUnavailableAsInvalid, + bool InOverloadResolution) + : SemaRef(S), VerifyOnly(VerifyOnly), + TreatUnavailableAsInvalid(TreatUnavailableAsInvalid), + InOverloadResolution(InOverloadResolution) { + if (!VerifyOnly || hasAnyDesignatedInits(IL)) { + FullyStructuredList = + createInitListExpr(T, IL->getSourceRange(), IL->getNumInits()); + + // FIXME: Check that IL isn't already the semantic form of some other + // InitListExpr. If it is, we'd create a broken AST. + if (!VerifyOnly) + FullyStructuredList->setSyntacticForm(IL); + } + CheckExplicitInitList(Entity, IL, T, FullyStructuredList, /*TopLevelObject=*/true); - if (!hadError && !VerifyOnly) { + if (!hadError && FullyStructuredList) { bool RequiresSecondPass = false; FillInEmptyInitializations(Entity, FullyStructuredList, RequiresSecondPass, /*OuterILE=*/nullptr, /*OuterIndex=*/0); @@ -877,7 +974,7 @@ int InitListChecker::numArrayElements(QualType DeclType) { } int InitListChecker::numStructUnionElements(QualType DeclType) { - RecordDecl *structDecl = DeclType->getAs<RecordType>()->getDecl(); + RecordDecl *structDecl = DeclType->castAs<RecordType>()->getDecl(); int InitializableMembers = 0; if (auto *CXXRD = dyn_cast<CXXRecordDecl>(structDecl)) InitializableMembers += CXXRD->getNumBases(); @@ -936,7 +1033,7 @@ void InitListChecker::CheckImplicitInitList(const InitializedEntity &Entity, else if (T->isRecordType()) maxElements = numStructUnionElements(T); else if (T->isVectorType()) - maxElements = T->getAs<VectorType>()->getNumElements(); + maxElements = T->castAs<VectorType>()->getNumElements(); else llvm_unreachable("CheckImplicitInitList(): Illegal type"); @@ -963,7 +1060,7 @@ void InitListChecker::CheckImplicitInitList(const InitializedEntity &Entity, StructuredSubobjectInitList, StructuredSubobjectInitIndex); - if (!VerifyOnly) { + if (StructuredSubobjectInitList) { StructuredSubobjectInitList->setType(T); unsigned EndIndex = (Index == StartIndex? StartIndex : Index - 1); @@ -977,7 +1074,7 @@ void InitListChecker::CheckImplicitInitList(const InitializedEntity &Entity, } // Complain about missing braces. - if ((T->isArrayType() || T->isRecordType()) && + if (!VerifyOnly && (T->isArrayType() || T->isRecordType()) && !ParentIList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()) && !isIdiomaticBraceElisionEntity(Entity)) { SemaRef.Diag(StructuredSubobjectInitList->getBeginLoc(), @@ -993,7 +1090,7 @@ void InitListChecker::CheckImplicitInitList(const InitializedEntity &Entity, // Warn if this type won't be an aggregate in future versions of C++. auto *CXXRD = T->getAsCXXRecordDecl(); - if (CXXRD && CXXRD->hasUserDeclaredConstructor()) { + if (!VerifyOnly && CXXRD && CXXRD->hasUserDeclaredConstructor()) { SemaRef.Diag(StructuredSubobjectInitList->getBeginLoc(), diag::warn_cxx2a_compat_aggregate_init_with_ctors) << StructuredSubobjectInitList->getSourceRange() << T; @@ -1074,67 +1171,46 @@ void InitListChecker::CheckExplicitInitList(const InitializedEntity &Entity, InitListExpr *IList, QualType &T, InitListExpr *StructuredList, bool TopLevelObject) { - if (!VerifyOnly) { - SyntacticToSemantic[IList] = StructuredList; - StructuredList->setSyntacticForm(IList); - } - unsigned Index = 0, StructuredIndex = 0; CheckListElementTypes(Entity, IList, T, /*SubobjectIsDesignatorContext=*/true, Index, StructuredList, StructuredIndex, TopLevelObject); - if (!VerifyOnly) { + if (StructuredList) { QualType ExprTy = T; if (!ExprTy->isArrayType()) ExprTy = ExprTy.getNonLValueExprType(SemaRef.Context); - IList->setType(ExprTy); + if (!VerifyOnly) + IList->setType(ExprTy); StructuredList->setType(ExprTy); } if (hadError) return; - if (Index < IList->getNumInits()) { + // Don't complain for incomplete types, since we'll get an error elsewhere. + if (Index < IList->getNumInits() && !T->isIncompleteType()) { // We have leftover initializers + bool ExtraInitsIsError = SemaRef.getLangOpts().CPlusPlus || + (SemaRef.getLangOpts().OpenCL && T->isVectorType()); + hadError = ExtraInitsIsError; if (VerifyOnly) { - if (SemaRef.getLangOpts().CPlusPlus || - (SemaRef.getLangOpts().OpenCL && - IList->getType()->isVectorType())) { - hadError = true; - } return; - } - - if (StructuredIndex == 1 && - IsStringInit(StructuredList->getInit(0), T, SemaRef.Context) == - SIF_None) { - unsigned DK = diag::ext_excess_initializers_in_char_array_initializer; - if (SemaRef.getLangOpts().CPlusPlus) { - DK = diag::err_excess_initializers_in_char_array_initializer; - hadError = true; - } - // Special-case + } else if (StructuredIndex == 1 && + IsStringInit(StructuredList->getInit(0), T, SemaRef.Context) == + SIF_None) { + unsigned DK = + ExtraInitsIsError + ? diag::err_excess_initializers_in_char_array_initializer + : diag::ext_excess_initializers_in_char_array_initializer; SemaRef.Diag(IList->getInit(Index)->getBeginLoc(), DK) << IList->getInit(Index)->getSourceRange(); - } else if (!T->isIncompleteType()) { - // Don't complain for incomplete types, since we'll get an error - // elsewhere - QualType CurrentObjectType = StructuredList->getType(); - int initKind = - CurrentObjectType->isArrayType()? 0 : - CurrentObjectType->isVectorType()? 1 : - CurrentObjectType->isScalarType()? 2 : - CurrentObjectType->isUnionType()? 3 : - 4; - - unsigned DK = diag::ext_excess_initializers; - if (SemaRef.getLangOpts().CPlusPlus) { - DK = diag::err_excess_initializers; - hadError = true; - } - if (SemaRef.getLangOpts().OpenCL && initKind == 1) { - DK = diag::err_excess_initializers; - hadError = true; - } - + } else { + int initKind = T->isArrayType() ? 0 : + T->isVectorType() ? 1 : + T->isScalarType() ? 2 : + T->isUnionType() ? 3 : + 4; + + unsigned DK = ExtraInitsIsError ? diag::err_excess_initializers + : diag::ext_excess_initializers; SemaRef.Diag(IList->getInit(Index)->getBeginLoc(), DK) << initKind << IList->getInit(Index)->getSourceRange(); } @@ -1188,7 +1264,7 @@ void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity, } else if (DeclType->isRecordType()) { assert(DeclType->isAggregateType() && "non-aggregate records should be handed in CheckSubElementType"); - RecordDecl *RD = DeclType->getAs<RecordType>()->getDecl(); + RecordDecl *RD = DeclType->castAs<RecordType>()->getDecl(); auto Bases = CXXRecordDecl::base_class_range(CXXRecordDecl::base_class_iterator(), CXXRecordDecl::base_class_iterator()); @@ -1246,42 +1322,22 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, if (SubInitList->getNumInits() == 1 && IsStringInit(SubInitList->getInit(0), ElemType, SemaRef.Context) == SIF_None) { + // FIXME: It would be more faithful and no less correct to include an + // InitListExpr in the semantic form of the initializer list in this case. expr = SubInitList->getInit(0); - } else if (!SemaRef.getLangOpts().CPlusPlus) { - InitListExpr *InnerStructuredList - = getStructuredSubobjectInit(IList, Index, ElemType, - StructuredList, StructuredIndex, - SubInitList->getSourceRange(), true); - CheckExplicitInitList(Entity, SubInitList, ElemType, - InnerStructuredList); - - if (!hadError && !VerifyOnly) { - bool RequiresSecondPass = false; - FillInEmptyInitializations(Entity, InnerStructuredList, - RequiresSecondPass, StructuredList, - StructuredIndex); - if (RequiresSecondPass && !hadError) - FillInEmptyInitializations(Entity, InnerStructuredList, - RequiresSecondPass, StructuredList, - StructuredIndex); - } - ++StructuredIndex; - ++Index; - return; } - // C++ initialization is handled later. + // Nested aggregate initialization and C++ initialization are handled later. } else if (isa<ImplicitValueInitExpr>(expr)) { // This happens during template instantiation when we see an InitListExpr // that we've already checked once. assert(SemaRef.Context.hasSameType(expr->getType(), ElemType) && "found implicit initialization for the wrong type"); - if (!VerifyOnly) - UpdateStructuredListElement(StructuredList, StructuredIndex, expr); + UpdateStructuredListElement(StructuredList, StructuredIndex, expr); ++Index; return; } - if (SemaRef.getLangOpts().CPlusPlus) { + if (SemaRef.getLangOpts().CPlusPlus || isa<InitListExpr>(expr)) { // C++ [dcl.init.aggr]p2: // Each member is copy-initialized from the corresponding // initializer-clause. @@ -1289,7 +1345,16 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, // FIXME: Better EqualLoc? InitializationKind Kind = InitializationKind::CreateCopy(expr->getBeginLoc(), SourceLocation()); - InitializationSequence Seq(SemaRef, Entity, Kind, expr, + + // Vector elements can be initialized from other vectors in which case + // we need initialization entity with a type of a vector (and not a vector + // element!) initializing multiple vector elements. + auto TmpEntity = + (ElemType->isExtVectorType() && !Entity.getType()->isExtVectorType()) + ? InitializedEntity::InitializeTemporary(ElemType) + : Entity; + + InitializationSequence Seq(SemaRef, TmpEntity, Kind, expr, /*TopLevelOfInitList*/ true); // C++14 [dcl.init.aggr]p13: @@ -1300,15 +1365,18 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, // assignment-expression. if (Seq || isa<InitListExpr>(expr)) { if (!VerifyOnly) { - ExprResult Result = - Seq.Perform(SemaRef, Entity, Kind, expr); + ExprResult Result = Seq.Perform(SemaRef, TmpEntity, Kind, expr); if (Result.isInvalid()) hadError = true; UpdateStructuredListElement(StructuredList, StructuredIndex, Result.getAs<Expr>()); - } else if (!Seq) + } else if (!Seq) { hadError = true; + } else if (StructuredList) { + UpdateStructuredListElement(StructuredList, StructuredIndex, + getDummyInit()); + } ++Index; return; } @@ -1325,10 +1393,11 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, // type here, though. if (IsStringInit(expr, arrayType, SemaRef.Context) == SIF_None) { - if (!VerifyOnly) { + // FIXME: Should we do this checking in verify-only mode? + if (!VerifyOnly) CheckStringInit(expr, ElemType, arrayType, SemaRef); + if (StructuredList) UpdateStructuredListElement(StructuredList, StructuredIndex, expr); - } ++Index; return; } @@ -1354,8 +1423,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, hadError = true; else { ExprRes = SemaRef.DefaultFunctionArrayLvalueConversion(ExprRes.get()); - if (ExprRes.isInvalid()) - hadError = true; + if (ExprRes.isInvalid()) + hadError = true; } UpdateStructuredListElement(StructuredList, StructuredIndex, ExprRes.getAs<Expr>()); @@ -1380,10 +1449,15 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, ++StructuredIndex; } else { if (!VerifyOnly) { - // We cannot initialize this element, so let - // PerformCopyInitialization produce the appropriate diagnostic. - SemaRef.PerformCopyInitialization(Entity, SourceLocation(), expr, - /*TopLevelOfInitList=*/true); + // We cannot initialize this element, so let PerformCopyInitialization + // produce the appropriate diagnostic. We already checked that this + // initialization will fail. + ExprResult Copy = + SemaRef.PerformCopyInitialization(Entity, SourceLocation(), expr, + /*TopLevelOfInitList=*/true); + (void)Copy; + assert(Copy.isInvalid() && + "expected non-aggregate initialization to fail"); } hadError = true; ++Index; @@ -1416,7 +1490,7 @@ void InitListChecker::CheckComplexType(const InitializedEntity &Entity, << IList->getSourceRange(); // Initialize the complex number. - QualType elementType = DeclType->getAs<ComplexType>()->getElementType(); + QualType elementType = DeclType->castAs<ComplexType>()->getElementType(); InitializedEntity ElementEntity = InitializedEntity::InitializeElement(SemaRef.Context, 0, Entity); @@ -1467,17 +1541,18 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity, return; } + ExprResult Result; if (VerifyOnly) { - if (!SemaRef.CanPerformCopyInitialization(Entity,expr)) - hadError = true; - ++Index; - return; + if (SemaRef.CanPerformCopyInitialization(Entity, expr)) + Result = getDummyInit(); + else + Result = ExprError(); + } else { + Result = + SemaRef.PerformCopyInitialization(Entity, expr->getBeginLoc(), expr, + /*TopLevelOfInitList=*/true); } - ExprResult Result = - SemaRef.PerformCopyInitialization(Entity, expr->getBeginLoc(), expr, - /*TopLevelOfInitList=*/true); - Expr *ResultExpr = nullptr; if (Result.isInvalid()) @@ -1485,8 +1560,9 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity, else { ResultExpr = Result.getAs<Expr>(); - if (ResultExpr != expr) { + if (ResultExpr != expr && !VerifyOnly) { // The type was promoted, update initializer list. + // FIXME: Why are we updating the syntactic init list? IList->setInit(Index, ResultExpr); } } @@ -1528,22 +1604,25 @@ void InitListChecker::CheckReferenceType(const InitializedEntity &Entity, return; } + ExprResult Result; if (VerifyOnly) { - if (!SemaRef.CanPerformCopyInitialization(Entity,expr)) - hadError = true; - ++Index; - return; + if (SemaRef.CanPerformCopyInitialization(Entity,expr)) + Result = getDummyInit(); + else + Result = ExprError(); + } else { + Result = + SemaRef.PerformCopyInitialization(Entity, expr->getBeginLoc(), expr, + /*TopLevelOfInitList=*/true); } - ExprResult Result = - SemaRef.PerformCopyInitialization(Entity, expr->getBeginLoc(), expr, - /*TopLevelOfInitList=*/true); - if (Result.isInvalid()) hadError = true; expr = Result.getAs<Expr>(); - IList->setInit(Index, expr); + // FIXME: Why are we updating the syntactic init list? + if (!VerifyOnly) + IList->setInit(Index, expr); if (hadError) ++StructuredIndex; @@ -1557,17 +1636,16 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity, unsigned &Index, InitListExpr *StructuredList, unsigned &StructuredIndex) { - const VectorType *VT = DeclType->getAs<VectorType>(); + const VectorType *VT = DeclType->castAs<VectorType>(); unsigned maxElements = VT->getNumElements(); unsigned numEltsInit = 0; QualType elementType = VT->getElementType(); if (Index >= IList->getNumInits()) { // Make sure the element type can be value-initialized. - if (VerifyOnly) - CheckEmptyInitializable( - InitializedEntity::InitializeElement(SemaRef.Context, 0, Entity), - IList->getEndLoc()); + CheckEmptyInitializable( + InitializedEntity::InitializeElement(SemaRef.Context, 0, Entity), + IList->getEndLoc()); return; } @@ -1576,25 +1654,27 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity, // instead of breaking it apart (which is doomed to failure anyway). Expr *Init = IList->getInit(Index); if (!isa<InitListExpr>(Init) && Init->getType()->isVectorType()) { + ExprResult Result; if (VerifyOnly) { - if (!SemaRef.CanPerformCopyInitialization(Entity, Init)) - hadError = true; - ++Index; - return; + if (SemaRef.CanPerformCopyInitialization(Entity, Init)) + Result = getDummyInit(); + else + Result = ExprError(); + } else { + Result = + SemaRef.PerformCopyInitialization(Entity, Init->getBeginLoc(), Init, + /*TopLevelOfInitList=*/true); } - ExprResult Result = - SemaRef.PerformCopyInitialization(Entity, Init->getBeginLoc(), Init, - /*TopLevelOfInitList=*/true); - Expr *ResultExpr = nullptr; if (Result.isInvalid()) hadError = true; // types weren't compatible. else { ResultExpr = Result.getAs<Expr>(); - if (ResultExpr != Init) { + if (ResultExpr != Init && !VerifyOnly) { // The type was promoted, update initializer list. + // FIXME: Why are we updating the syntactic init list? IList->setInit(Index, ResultExpr); } } @@ -1613,8 +1693,7 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity, for (unsigned i = 0; i < maxElements; ++i, ++numEltsInit) { // Don't attempt to go past the end of the init list if (Index >= IList->getNumInits()) { - if (VerifyOnly) - CheckEmptyInitializable(ElementEntity, IList->getEndLoc()); + CheckEmptyInitializable(ElementEntity, IList->getEndLoc()); break; } @@ -1627,7 +1706,7 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity, return; bool isBigEndian = SemaRef.Context.getTargetInfo().isBigEndian(); - const VectorType *T = Entity.getType()->getAs<VectorType>(); + const VectorType *T = Entity.getType()->castAs<VectorType>(); if (isBigEndian && (T->getVectorKind() == VectorType::NeonVector || T->getVectorKind() == VectorType::NeonPolyVector)) { // The ability to use vector initializer lists is a GNU vector extension @@ -1683,7 +1762,7 @@ void InitListChecker::CheckVectorType(const InitializedEntity &Entity, ++numEltsInit; } else { QualType VecType; - const VectorType *IVT = IType->getAs<VectorType>(); + const VectorType *IVT = IType->castAs<VectorType>(); unsigned numIElts = IVT->getNumElements(); if (IType->isExtVectorType()) @@ -1757,8 +1836,10 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity, // of the structured initializer list doesn't match exactly, // because doing so would involve allocating one character // constant for each string. - if (!VerifyOnly) { + // FIXME: Should we do these checks in verify-only mode too? + if (!VerifyOnly) CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef); + if (StructuredList) { UpdateStructuredListElement(StructuredList, StructuredIndex, IList->getInit(Index)); StructuredList->resizeInits(SemaRef.Context, StructuredIndex); @@ -1854,15 +1935,14 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity, SemaRef.Diag(IList->getBeginLoc(), diag::ext_typecheck_zero_array_size); } - DeclType = SemaRef.Context.getConstantArrayType(elementType, maxElements, - ArrayType::Normal, 0); + DeclType = SemaRef.Context.getConstantArrayType( + elementType, maxElements, nullptr, ArrayType::Normal, 0); } - if (!hadError && VerifyOnly) { + if (!hadError) { // If there are any members of the array that get value-initialized, check // that is possible. That happens if we know the bound and don't have // enough elements, or if we're performing an array new with an unknown // bound. - // FIXME: This needs to detect holes left by designated initializers too. if ((maxElementsKnown && elementIndex < maxElements) || Entity.isVariableLengthArrayNew()) CheckEmptyInitializable( @@ -1915,7 +1995,7 @@ void InitListChecker::CheckStructUnionTypes( bool SubobjectIsDesignatorContext, unsigned &Index, InitListExpr *StructuredList, unsigned &StructuredIndex, bool TopLevelObject) { - RecordDecl *structDecl = DeclType->getAs<RecordType>()->getDecl(); + RecordDecl *structDecl = DeclType->castAs<RecordType>()->getDecl(); // If the record is invalid, some of it's members are invalid. To avoid // confusion, we forgo checking the intializer for the entire record. @@ -1927,7 +2007,7 @@ void InitListChecker::CheckStructUnionTypes( } if (DeclType->isUnionType() && IList->getNumInits() == 0) { - RecordDecl *RD = DeclType->getAs<RecordType>()->getDecl(); + RecordDecl *RD = DeclType->castAs<RecordType>()->getDecl(); if (!VerifyOnly) for (FieldDecl *FD : RD->fields()) { @@ -1939,8 +2019,9 @@ void InitListChecker::CheckStructUnionTypes( } // If there's a default initializer, use it. - if (isa<CXXRecordDecl>(RD) && cast<CXXRecordDecl>(RD)->hasInClassInitializer()) { - if (VerifyOnly) + if (isa<CXXRecordDecl>(RD) && + cast<CXXRecordDecl>(RD)->hasInClassInitializer()) { + if (!StructuredList) return; for (RecordDecl::field_iterator FieldEnd = RD->field_end(); Field != FieldEnd; ++Field) { @@ -1957,11 +2038,10 @@ void InitListChecker::CheckStructUnionTypes( for (RecordDecl::field_iterator FieldEnd = RD->field_end(); Field != FieldEnd; ++Field) { if (!Field->isUnnamedBitfield()) { - if (VerifyOnly) - CheckEmptyInitializable( - InitializedEntity::InitializeMember(*Field, &Entity), - IList->getEndLoc()); - else + CheckEmptyInitializable( + InitializedEntity::InitializeMember(*Field, &Entity), + IList->getEndLoc()); + if (StructuredList) StructuredList->setInitializedFieldInUnion(*Field); break; } @@ -1987,7 +2067,7 @@ void InitListChecker::CheckStructUnionTypes( CheckSubElementType(BaseEntity, IList, Base.getType(), Index, StructuredList, StructuredIndex); InitializedSomething = true; - } else if (VerifyOnly) { + } else { CheckEmptyInitializable(BaseEntity, InitLoc); } @@ -2002,7 +2082,7 @@ void InitListChecker::CheckStructUnionTypes( // anything except look at designated initializers; That's okay, // because an error should get printed out elsewhere. It might be // worthwhile to skip over the rest of the initializer, though. - RecordDecl *RD = DeclType->getAs<RecordType>()->getDecl(); + RecordDecl *RD = DeclType->castAs<RecordType>()->getDecl(); RecordDecl::field_iterator FieldEnd = RD->field_end(); bool CheckForMissingFields = !IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()); @@ -2095,7 +2175,7 @@ void InitListChecker::CheckStructUnionTypes( StructuredList, StructuredIndex); InitializedSomething = true; - if (DeclType->isUnionType() && !VerifyOnly) { + if (DeclType->isUnionType() && StructuredList) { // Initialize the first field within the union. StructuredList->setInitializedFieldInUnion(*Field); } @@ -2119,10 +2199,10 @@ void InitListChecker::CheckStructUnionTypes( } } - // Check that any remaining fields can be value-initialized. - if (VerifyOnly && Field != FieldEnd && !DeclType->isUnionType() && + // Check that any remaining fields can be value-initialized if we're not + // building a structured list. (If we are, we'll check this later.) + if (!StructuredList && Field != FieldEnd && !DeclType->isUnionType() && !Field->getType()->isIncompleteArrayType()) { - // FIXME: Should check for holes left by designated initializers too. for (; Field != FieldEnd && !hadError; ++Field) { if (!Field->isUnnamedBitfield() && !Field->hasInClassInitializer()) CheckEmptyInitializable( @@ -2227,7 +2307,7 @@ class FieldInitializerValidatorCCC final : public CorrectionCandidateCallback { } std::unique_ptr<CorrectionCandidateCallback> clone() override { - return llvm::make_unique<FieldInitializerValidatorCCC>(*this); + return std::make_unique<FieldInitializerValidatorCCC>(*this); } private: @@ -2257,7 +2337,9 @@ class FieldInitializerValidatorCCC final : public CorrectionCandidateCallback { /// /// @param NextField If non-NULL and the first designator in @p DIE is /// a field, this will be set to the field declaration corresponding -/// to the field named by the designator. +/// to the field named by the designator. On input, this is expected to be +/// the next field that would be initialized in the absence of designation, +/// if the complete object being initialized is a struct. /// /// @param NextElementIndex If non-NULL and the first designator in @p /// DIE is an array designator or GNU array-range designator, this @@ -2285,6 +2367,29 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, bool FinishSubobjectInit, bool TopLevelObject) { if (DesigIdx == DIE->size()) { + // C++20 designated initialization can result in direct-list-initialization + // of the designated subobject. This is the only way that we can end up + // performing direct initialization as part of aggregate initialization, so + // it needs special handling. + if (DIE->isDirectInit()) { + Expr *Init = DIE->getInit(); + assert(isa<InitListExpr>(Init) && + "designator result in direct non-list initialization?"); + InitializationKind Kind = InitializationKind::CreateDirectList( + DIE->getBeginLoc(), Init->getBeginLoc(), Init->getEndLoc()); + InitializationSequence Seq(SemaRef, Entity, Kind, Init, + /*TopLevelOfInitList*/ true); + if (StructuredList) { + ExprResult Result = VerifyOnly + ? getDummyInit() + : Seq.Perform(SemaRef, Entity, Kind, Init); + UpdateStructuredListElement(StructuredList, StructuredIndex, + Result.get()); + } + ++Index; + return !Seq; + } + // Check the actual initialization for the designated object type. bool prevHadError = hadError; @@ -2308,14 +2413,11 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, DesignatedInitExpr::Designator *D = DIE->getDesignator(DesigIdx); bool IsFirstDesignator = (DesigIdx == 0); - if (!VerifyOnly) { - assert((IsFirstDesignator || StructuredList) && - "Need a non-designated initializer list to start from"); - + if (IsFirstDesignator ? FullyStructuredList : StructuredList) { // Determine the structural initializer list that corresponds to the // current subobject. if (IsFirstDesignator) - StructuredList = SyntacticToSemantic.lookup(IList); + StructuredList = FullyStructuredList; else { Expr *ExistingInit = StructuredIndex < StructuredList->getNumInits() ? StructuredList->getInit(StructuredIndex) : nullptr; @@ -2329,48 +2431,42 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, else if (InitListExpr *Result = dyn_cast<InitListExpr>(ExistingInit)) StructuredList = Result; else { - if (DesignatedInitUpdateExpr *E = - dyn_cast<DesignatedInitUpdateExpr>(ExistingInit)) - StructuredList = E->getUpdater(); - else { - DesignatedInitUpdateExpr *DIUE = new (SemaRef.Context) - DesignatedInitUpdateExpr(SemaRef.Context, D->getBeginLoc(), - ExistingInit, DIE->getEndLoc()); - StructuredList->updateInit(SemaRef.Context, StructuredIndex, DIUE); - StructuredList = DIUE->getUpdater(); - } - - // We need to check on source range validity because the previous - // initializer does not have to be an explicit initializer. e.g., + // We are creating an initializer list that initializes the + // subobjects of the current object, but there was already an + // initialization that completely initialized the current + // subobject, e.g., by a compound literal: // - // struct P { int a, b; }; - // struct PP { struct P p } l = { { .a = 2 }, .p.b = 3 }; + // struct X { int a, b; }; + // struct X xs[] = { [0] = (struct X) { 1, 2 }, [0].b = 3 }; // - // There is an overwrite taking place because the first braced initializer - // list "{ .a = 2 }" already provides value for .p.b (which is zero). - if (ExistingInit->getSourceRange().isValid()) { - // We are creating an initializer list that initializes the - // subobjects of the current object, but there was already an - // initialization that completely initialized the current - // subobject, e.g., by a compound literal: - // - // struct X { int a, b; }; - // struct X xs[] = { [0] = (struct X) { 1, 2 }, [0].b = 3 }; - // - // Here, xs[0].a == 0 and xs[0].b == 3, since the second, - // designated initializer re-initializes the whole - // subobject [0], overwriting previous initializers. - SemaRef.Diag(D->getBeginLoc(), - diag::warn_subobject_initializer_overrides) - << SourceRange(D->getBeginLoc(), DIE->getEndLoc()); - - SemaRef.Diag(ExistingInit->getBeginLoc(), - diag::note_previous_initializer) - << /*FIXME:has side effects=*/0 << ExistingInit->getSourceRange(); + // Here, xs[0].a == 1 and xs[0].b == 3, since the second, + // designated initializer re-initializes only its current object + // subobject [0].b. + diagnoseInitOverride(ExistingInit, + SourceRange(D->getBeginLoc(), DIE->getEndLoc()), + /*FullyOverwritten=*/false); + + if (!VerifyOnly) { + if (DesignatedInitUpdateExpr *E = + dyn_cast<DesignatedInitUpdateExpr>(ExistingInit)) + StructuredList = E->getUpdater(); + else { + DesignatedInitUpdateExpr *DIUE = new (SemaRef.Context) + DesignatedInitUpdateExpr(SemaRef.Context, D->getBeginLoc(), + ExistingInit, DIE->getEndLoc()); + StructuredList->updateInit(SemaRef.Context, StructuredIndex, DIUE); + StructuredList = DIUE->getUpdater(); + } + } else { + // We don't need to track the structured representation of a + // designated init update of an already-fully-initialized object in + // verify-only mode. The only reason we would need the structure is + // to determine where the uninitialized "holes" are, and in this + // case, we know there aren't any and we can't introduce any. + StructuredList = nullptr; } } } - assert(StructuredList && "Expected a structured initializer list"); } if (D->isFieldDesignator()) { @@ -2453,10 +2549,11 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, } } - unsigned FieldIndex = 0; - + unsigned NumBases = 0; if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RT->getDecl())) - FieldIndex = CXXRD->getNumBases(); + NumBases = CXXRD->getNumBases(); + + unsigned FieldIndex = NumBases; for (auto *FI : RT->getDecl()->fields()) { if (FI->isUnnamedBitfield()) @@ -2475,7 +2572,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, // the initializer list. if (RT->getDecl()->isUnion()) { FieldIndex = 0; - if (!VerifyOnly) { + if (StructuredList) { FieldDecl *CurrentField = StructuredList->getInitializedFieldInUnion(); if (CurrentField && !declaresSameEntity(CurrentField, *Field)) { assert(StructuredList->getNumInits() == 1 @@ -2484,13 +2581,8 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, Expr *ExistingInit = StructuredList->getInit(0); if (ExistingInit) { // We're about to throw away an initializer, emit warning. - SemaRef.Diag(D->getFieldLoc(), - diag::warn_initializer_overrides) - << D->getSourceRange(); - SemaRef.Diag(ExistingInit->getBeginLoc(), - diag::note_previous_initializer) - << /*FIXME:has side effects=*/0 - << ExistingInit->getSourceRange(); + diagnoseInitOverride( + ExistingInit, SourceRange(D->getBeginLoc(), DIE->getEndLoc())); } // remove existing initializer @@ -2513,16 +2605,63 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, return true; } - if (!VerifyOnly) { - // Update the designator with the field declaration. - D->setField(*Field); + // C++20 [dcl.init.list]p3: + // The ordered identifiers in the designators of the designated- + // initializer-list shall form a subsequence of the ordered identifiers + // in the direct non-static data members of T. + // + // Note that this is not a condition on forming the aggregate + // initialization, only on actually performing initialization, + // so it is not checked in VerifyOnly mode. + // + // FIXME: This is the only reordering diagnostic we produce, and it only + // catches cases where we have a top-level field designator that jumps + // backwards. This is the only such case that is reachable in an + // otherwise-valid C++20 program, so is the only case that's required for + // conformance, but for consistency, we should diagnose all the other + // cases where a designator takes us backwards too. + if (IsFirstDesignator && !VerifyOnly && SemaRef.getLangOpts().CPlusPlus && + NextField && + (*NextField == RT->getDecl()->field_end() || + (*NextField)->getFieldIndex() > Field->getFieldIndex() + 1)) { + // Find the field that we just initialized. + FieldDecl *PrevField = nullptr; + for (auto FI = RT->getDecl()->field_begin(); + FI != RT->getDecl()->field_end(); ++FI) { + if (FI->isUnnamedBitfield()) + continue; + if (*NextField != RT->getDecl()->field_end() && + declaresSameEntity(*FI, **NextField)) + break; + PrevField = *FI; + } - // Make sure that our non-designated initializer list has space - // for a subobject corresponding to this field. - if (FieldIndex >= StructuredList->getNumInits()) - StructuredList->resizeInits(SemaRef.Context, FieldIndex + 1); + if (PrevField && + PrevField->getFieldIndex() > KnownField->getFieldIndex()) { + SemaRef.Diag(DIE->getBeginLoc(), diag::ext_designated_init_reordered) + << KnownField << PrevField << DIE->getSourceRange(); + + unsigned OldIndex = NumBases + PrevField->getFieldIndex(); + if (StructuredList && OldIndex <= StructuredList->getNumInits()) { + if (Expr *PrevInit = StructuredList->getInit(OldIndex)) { + SemaRef.Diag(PrevInit->getBeginLoc(), + diag::note_previous_field_init) + << PrevField << PrevInit->getSourceRange(); + } + } + } } + + // Update the designator with the field declaration. + if (!VerifyOnly) + D->setField(*Field); + + // Make sure that our non-designated initializer list has space + // for a subobject corresponding to this field. + if (StructuredList && FieldIndex >= StructuredList->getNumInits()) + StructuredList->resizeInits(SemaRef.Context, FieldIndex + 1); + // This designator names a flexible array member. if (Field->getType()->isIncompleteArrayType()) { bool Invalid = false; @@ -2707,7 +2846,13 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, DesignatedEndIndex.setIsUnsigned(true); } - if (!VerifyOnly && StructuredList->isStringLiteralInit()) { + bool IsStringLiteralInitUpdate = + StructuredList && StructuredList->isStringLiteralInit(); + if (IsStringLiteralInitUpdate && VerifyOnly) { + // We're just verifying an update to a string literal init. We don't need + // to split the string up into individual characters to do that. + StructuredList = nullptr; + } else if (IsStringLiteralInitUpdate) { // We're modifying a string literal init; we have to decompose the string // so we can modify the individual characters. ASTContext &Context = SemaRef.Context; @@ -2767,7 +2912,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, // Make sure that our non-designated initializer list has space // for a subobject corresponding to this array element. - if (!VerifyOnly && + if (StructuredList && DesignatedEndIndex.getZExtValue() >= StructuredList->getNumInits()) StructuredList->resizeInits(SemaRef.Context, DesignatedEndIndex.getZExtValue() + 1); @@ -2829,12 +2974,11 @@ InitListChecker::getStructuredSubobjectInit(InitListExpr *IList, unsigned Index, unsigned StructuredIndex, SourceRange InitRange, bool IsFullyOverwritten) { - if (VerifyOnly) - return nullptr; // No structured list in verification-only mode. - Expr *ExistingInit = nullptr; if (!StructuredList) - ExistingInit = SyntacticToSemantic.lookup(IList); - else if (StructuredIndex < StructuredList->getNumInits()) + return nullptr; + + Expr *ExistingInit = nullptr; + if (StructuredIndex < StructuredList->getNumInits()) ExistingInit = StructuredList->getInit(StructuredIndex); if (InitListExpr *Result = dyn_cast_or_null<InitListExpr>(ExistingInit)) @@ -2853,21 +2997,46 @@ InitListChecker::getStructuredSubobjectInit(InitListExpr *IList, unsigned Index, // We are creating an initializer list that initializes the // subobjects of the current object, but there was already an // initialization that completely initialized the current - // subobject, e.g., by a compound literal: + // subobject: // // struct X { int a, b; }; + // struct X xs[] = { [0] = { 1, 2 }, [0].b = 3 }; + // + // Here, xs[0].a == 1 and xs[0].b == 3, since the second, + // designated initializer overwrites the [0].b initializer + // from the prior initialization. + // + // When the existing initializer is an expression rather than an + // initializer list, we cannot decompose and update it in this way. + // For example: + // // struct X xs[] = { [0] = (struct X) { 1, 2 }, [0].b = 3 }; // - // Here, xs[0].a == 0 and xs[0].b == 3, since the second, - // designated initializer re-initializes the whole - // subobject [0], overwriting previous initializers. - SemaRef.Diag(InitRange.getBegin(), - diag::warn_subobject_initializer_overrides) - << InitRange; - SemaRef.Diag(ExistingInit->getBeginLoc(), diag::note_previous_initializer) - << /*FIXME:has side effects=*/0 << ExistingInit->getSourceRange(); + // This case is handled by CheckDesignatedInitializer. + diagnoseInitOverride(ExistingInit, InitRange); } + unsigned ExpectedNumInits = 0; + if (Index < IList->getNumInits()) { + if (auto *Init = dyn_cast_or_null<InitListExpr>(IList->getInit(Index))) + ExpectedNumInits = Init->getNumInits(); + else + ExpectedNumInits = IList->getNumInits() - Index; + } + + InitListExpr *Result = + createInitListExpr(CurrentObjectType, InitRange, ExpectedNumInits); + + // Link this new initializer list into the structured initializer + // lists. + StructuredList->updateInit(SemaRef.Context, StructuredIndex, Result); + return Result; +} + +InitListExpr * +InitListChecker::createInitListExpr(QualType CurrentObjectType, + SourceRange InitRange, + unsigned ExpectedNumInits) { InitListExpr *Result = new (SemaRef.Context) InitListExpr(SemaRef.Context, InitRange.getBegin(), None, @@ -2880,17 +3049,6 @@ InitListChecker::getStructuredSubobjectInit(InitListExpr *IList, unsigned Index, // Pre-allocate storage for the structured initializer list. unsigned NumElements = 0; - unsigned NumInits = 0; - bool GotNumInits = false; - if (!StructuredList) { - NumInits = IList->getNumInits(); - GotNumInits = true; - } else if (Index < IList->getNumInits()) { - if (InitListExpr *SubList = dyn_cast<InitListExpr>(IList->getInit(Index))) { - NumInits = SubList->getNumInits(); - GotNumInits = true; - } - } if (const ArrayType *AType = SemaRef.Context.getAsArrayType(CurrentObjectType)) { @@ -2898,30 +3056,17 @@ InitListChecker::getStructuredSubobjectInit(InitListExpr *IList, unsigned Index, NumElements = CAType->getSize().getZExtValue(); // Simple heuristic so that we don't allocate a very large // initializer with many empty entries at the end. - if (GotNumInits && NumElements > NumInits) + if (NumElements > ExpectedNumInits) NumElements = 0; } - } else if (const VectorType *VType = CurrentObjectType->getAs<VectorType>()) + } else if (const VectorType *VType = CurrentObjectType->getAs<VectorType>()) { NumElements = VType->getNumElements(); - else if (const RecordType *RType = CurrentObjectType->getAs<RecordType>()) { - RecordDecl *RDecl = RType->getDecl(); - if (RDecl->isUnion()) - NumElements = 1; - else - NumElements = std::distance(RDecl->field_begin(), RDecl->field_end()); + } else if (CurrentObjectType->isRecordType()) { + NumElements = numStructUnionElements(CurrentObjectType); } Result->reserveInits(SemaRef.Context, NumElements); - // Link this new initializer list into the structured initializer - // lists. - if (StructuredList) - StructuredList->updateInit(SemaRef.Context, StructuredIndex, Result); - else { - Result->setSyntacticForm(IList); - SyntacticToSemantic[IList] = Result; - } - return Result; } @@ -2937,24 +3082,23 @@ void InitListChecker::UpdateStructuredListElement(InitListExpr *StructuredList, if (Expr *PrevInit = StructuredList->updateInit(SemaRef.Context, StructuredIndex, expr)) { // This initializer overwrites a previous initializer. Warn. - // We need to check on source range validity because the previous - // initializer does not have to be an explicit initializer. - // struct P { int a, b; }; - // struct PP { struct P p } l = { { .a = 2 }, .p.b = 3 }; - // There is an overwrite taking place because the first braced initializer - // list "{ .a = 2 }' already provides value for .p.b (which is zero). - if (PrevInit->getSourceRange().isValid()) { - SemaRef.Diag(expr->getBeginLoc(), diag::warn_initializer_overrides) - << expr->getSourceRange(); - - SemaRef.Diag(PrevInit->getBeginLoc(), diag::note_previous_initializer) - << /*FIXME:has side effects=*/0 << PrevInit->getSourceRange(); - } + diagnoseInitOverride(PrevInit, expr->getSourceRange()); } ++StructuredIndex; } +/// Determine whether we can perform aggregate initialization for the purposes +/// of overload resolution. +bool Sema::CanPerformAggregateInitializationForOverloadResolution( + const InitializedEntity &Entity, InitListExpr *From) { + QualType Type = Entity.getType(); + InitListChecker Check(*this, Entity, From, Type, /*VerifyOnly=*/true, + /*TreatUnavailableAsInvalid=*/false, + /*InOverloadResolution=*/true); + return !Check.HadError(); +} + /// Check that the given Index expression is a valid array designator /// value. This is essentially just a wrapper around /// VerifyIntegerConstantExpression that also checks for negative values @@ -2980,7 +3124,7 @@ CheckArrayDesignatorExpr(Sema &S, Expr *Index, llvm::APSInt &Value) { } ExprResult Sema::ActOnDesignatedInitializer(Designation &Desig, - SourceLocation Loc, + SourceLocation EqualOrColonLoc, bool GNUSyntax, ExprResult Init) { typedef DesignatedInitExpr::Designator ASTDesignator; @@ -3065,17 +3209,9 @@ ExprResult Sema::ActOnDesignatedInitializer(Designation &Desig, // Clear out the expressions within the designation. Desig.ClearExprs(*this); - DesignatedInitExpr *DIE - = DesignatedInitExpr::Create(Context, - Designators, - InitExpressions, Loc, GNUSyntax, - Init.getAs<Expr>()); - - if (!getLangOpts().C99) - Diag(DIE->getBeginLoc(), diag::ext_designated_init) - << DIE->getSourceRange(); - - return DIE; + return DesignatedInitExpr::Create(Context, Designators, InitExpressions, + EqualOrColonLoc, GNUSyntax, + Init.getAs<Expr>()); } //===----------------------------------------------------------------------===// @@ -3691,9 +3827,10 @@ static bool TryInitializerListConstruction(Sema &S, // Try initializing a temporary array from the init list. QualType ArrayType = S.Context.getConstantArrayType( - E.withConst(), llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()), - List->getNumInits()), - clang::ArrayType::Normal, 0); + E.withConst(), + llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()), + List->getNumInits()), + nullptr, clang::ArrayType::Normal, 0); InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary(ArrayType); InitializationKind Kind = InitializationKind::CreateDirectList( @@ -4070,7 +4207,7 @@ static void TryReferenceListInitialization(Sema &S, } QualType DestType = Entity.getType(); - QualType cv1T1 = DestType->getAs<ReferenceType>()->getPointeeType(); + QualType cv1T1 = DestType->castAs<ReferenceType>()->getPointeeType(); Qualifiers T1Quals; QualType T1 = S.Context.getUnqualifiedArrayType(cv1T1, T1Quals); @@ -4092,10 +4229,10 @@ static void TryReferenceListInitialization(Sema &S, return; SourceLocation DeclLoc = Initializer->getBeginLoc(); - bool dummy1, dummy2, dummy3; + bool dummy1, dummy2, dummy3, dummy4; Sema::ReferenceCompareResult RefRelationship = S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, dummy1, - dummy2, dummy3); + dummy2, dummy3, dummy4); if (RefRelationship >= Sema::Ref_Related) { // Try to bind the reference here. TryReferenceInitializationCore(S, Entity, Kind, Initializer, cv1T1, T1, @@ -4327,7 +4464,7 @@ static OverloadingResult TryRefInitWithConversionFunction( Expr *Initializer, bool AllowRValues, bool IsLValueRef, InitializationSequence &Sequence) { QualType DestType = Entity.getType(); - QualType cv1T1 = DestType->getAs<ReferenceType>()->getPointeeType(); + QualType cv1T1 = DestType->castAs<ReferenceType>()->getPointeeType(); QualType T1 = cv1T1.getUnqualifiedType(); QualType cv2T2 = Initializer->getType(); QualType T2 = cv2T2.getUnqualifiedType(); @@ -4335,13 +4472,15 @@ static OverloadingResult TryRefInitWithConversionFunction( bool DerivedToBase; bool ObjCConversion; bool ObjCLifetimeConversion; - assert(!S.CompareReferenceRelationship(Initializer->getBeginLoc(), T1, T2, - DerivedToBase, ObjCConversion, - ObjCLifetimeConversion) && + bool FunctionConversion; + assert(!S.CompareReferenceRelationship( + Initializer->getBeginLoc(), T1, T2, DerivedToBase, ObjCConversion, + ObjCLifetimeConversion, FunctionConversion) && "Must have incompatible references when binding via conversion"); (void)DerivedToBase; (void)ObjCConversion; (void)ObjCLifetimeConversion; + (void)FunctionConversion; // Build the candidate set directly in the initialization sequence // structure, so that it will persist if we fail. @@ -4468,10 +4607,11 @@ static OverloadingResult TryRefInitWithConversionFunction( bool NewDerivedToBase = false; bool NewObjCConversion = false; bool NewObjCLifetimeConversion = false; - Sema::ReferenceCompareResult NewRefRelationship - = S.CompareReferenceRelationship(DeclLoc, T1, cv3T3, - NewDerivedToBase, NewObjCConversion, - NewObjCLifetimeConversion); + bool NewFunctionConversion = false; + Sema::ReferenceCompareResult NewRefRelationship = + S.CompareReferenceRelationship( + DeclLoc, T1, cv3T3, NewDerivedToBase, NewObjCConversion, + NewObjCLifetimeConversion, NewFunctionConversion); // Add the final conversion sequence, if necessary. if (NewRefRelationship == Sema::Ref_Incompatible) { @@ -4505,6 +4645,8 @@ static OverloadingResult TryRefInitWithConversionFunction( Sequence.AddDerivedToBaseCastStep(cv1T1, VK); else if (NewObjCConversion) Sequence.AddObjCObjectConversionStep(cv1T1); + else if (NewFunctionConversion) + Sequence.AddQualificationConversionStep(cv1T1, VK); return OR_Success; } @@ -4520,7 +4662,7 @@ static void TryReferenceInitialization(Sema &S, Expr *Initializer, InitializationSequence &Sequence) { QualType DestType = Entity.getType(); - QualType cv1T1 = DestType->getAs<ReferenceType>()->getPointeeType(); + QualType cv1T1 = DestType->castAs<ReferenceType>()->getPointeeType(); Qualifiers T1Quals; QualType T1 = S.Context.getUnqualifiedArrayType(cv1T1, T1Quals); QualType cv2T2 = Initializer->getType(); @@ -4564,10 +4706,11 @@ static void TryReferenceInitializationCore(Sema &S, bool DerivedToBase = false; bool ObjCConversion = false; bool ObjCLifetimeConversion = false; + bool FunctionConversion = false; Expr::Classification InitCategory = Initializer->Classify(S.Context); - Sema::ReferenceCompareResult RefRelationship - = S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, DerivedToBase, - ObjCConversion, ObjCLifetimeConversion); + Sema::ReferenceCompareResult RefRelationship = S.CompareReferenceRelationship( + DeclLoc, cv1T1, cv2T2, DerivedToBase, ObjCConversion, + ObjCLifetimeConversion, FunctionConversion); // C++0x [dcl.init.ref]p5: // A reference to type "cv1 T1" is initialized by an expression of type @@ -4598,6 +4741,8 @@ static void TryReferenceInitializationCore(Sema &S, Sequence.AddDerivedToBaseCastStep(cv1T1, VK_LValue); else if (ObjCConversion) Sequence.AddObjCObjectConversionStep(cv1T1); + else if (FunctionConversion) + Sequence.AddQualificationConversionStep(cv1T1, VK_LValue); // We only create a temporary here when binding a reference to a // bit-field or vector element. Those cases are't supposed to be @@ -6233,8 +6378,11 @@ PerformConstructorInitialization(Sema &S, // the definition for completely trivial constructors. assert(Constructor->getParent() && "No parent class for constructor."); if (Constructor->isDefaulted() && Constructor->isDefaultConstructor() && - Constructor->isTrivial() && !Constructor->isUsed(false)) - S.DefineImplicitDefaultConstructor(Loc, Constructor); + Constructor->isTrivial() && !Constructor->isUsed(false)) { + S.runWithSufficientStackSpace(Loc, [&] { + S.DefineImplicitDefaultConstructor(Loc, Constructor); + }); + } } ExprResult CurInit((Expr *)nullptr); @@ -6505,6 +6653,7 @@ struct IndirectLocalPathEntry { VarInit, LValToRVal, LifetimeBoundCall, + GslPointerInit } Kind; Expr *E; const Decl *D = nullptr; @@ -6543,11 +6692,138 @@ static bool pathContainsInit(IndirectLocalPath &Path) { static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, Expr *Init, LocalVisitor Visit, - bool RevisitSubinits); + bool RevisitSubinits, + bool EnableLifetimeWarnings); static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, Expr *Init, ReferenceKind RK, - LocalVisitor Visit); + LocalVisitor Visit, + bool EnableLifetimeWarnings); + +template <typename T> static bool isRecordWithAttr(QualType Type) { + if (auto *RD = Type->getAsCXXRecordDecl()) + return RD->hasAttr<T>(); + return false; +} + +// Decl::isInStdNamespace will return false for iterators in some STL +// implementations due to them being defined in a namespace outside of the std +// namespace. +static bool isInStlNamespace(const Decl *D) { + const DeclContext *DC = D->getDeclContext(); + if (!DC) + return false; + if (const auto *ND = dyn_cast<NamespaceDecl>(DC)) + if (const IdentifierInfo *II = ND->getIdentifier()) { + StringRef Name = II->getName(); + if (Name.size() >= 2 && Name.front() == '_' && + (Name[1] == '_' || isUppercase(Name[1]))) + return true; + } + + return DC->isStdNamespace(); +} + +static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) { + if (auto *Conv = dyn_cast_or_null<CXXConversionDecl>(Callee)) + if (isRecordWithAttr<PointerAttr>(Conv->getConversionType())) + return true; + if (!isInStlNamespace(Callee->getParent())) + return false; + if (!isRecordWithAttr<PointerAttr>(Callee->getThisObjectType()) && + !isRecordWithAttr<OwnerAttr>(Callee->getThisObjectType())) + return false; + if (Callee->getReturnType()->isPointerType() || + isRecordWithAttr<PointerAttr>(Callee->getReturnType())) { + if (!Callee->getIdentifier()) + return false; + return llvm::StringSwitch<bool>(Callee->getName()) + .Cases("begin", "rbegin", "cbegin", "crbegin", true) + .Cases("end", "rend", "cend", "crend", true) + .Cases("c_str", "data", "get", true) + // Map and set types. + .Cases("find", "equal_range", "lower_bound", "upper_bound", true) + .Default(false); + } else if (Callee->getReturnType()->isReferenceType()) { + if (!Callee->getIdentifier()) { + auto OO = Callee->getOverloadedOperator(); + return OO == OverloadedOperatorKind::OO_Subscript || + OO == OverloadedOperatorKind::OO_Star; + } + return llvm::StringSwitch<bool>(Callee->getName()) + .Cases("front", "back", "at", "top", "value", true) + .Default(false); + } + return false; +} + +static bool shouldTrackFirstArgument(const FunctionDecl *FD) { + if (!FD->getIdentifier() || FD->getNumParams() != 1) + return false; + const auto *RD = FD->getParamDecl(0)->getType()->getPointeeCXXRecordDecl(); + if (!FD->isInStdNamespace() || !RD || !RD->isInStdNamespace()) + return false; + if (!isRecordWithAttr<PointerAttr>(QualType(RD->getTypeForDecl(), 0)) && + !isRecordWithAttr<OwnerAttr>(QualType(RD->getTypeForDecl(), 0))) + return false; + if (FD->getReturnType()->isPointerType() || + isRecordWithAttr<PointerAttr>(FD->getReturnType())) { + return llvm::StringSwitch<bool>(FD->getName()) + .Cases("begin", "rbegin", "cbegin", "crbegin", true) + .Cases("end", "rend", "cend", "crend", true) + .Case("data", true) + .Default(false); + } else if (FD->getReturnType()->isReferenceType()) { + return llvm::StringSwitch<bool>(FD->getName()) + .Cases("get", "any_cast", true) + .Default(false); + } + return false; +} + +static void handleGslAnnotatedTypes(IndirectLocalPath &Path, Expr *Call, + LocalVisitor Visit) { + auto VisitPointerArg = [&](const Decl *D, Expr *Arg) { + // We are not interested in the temporary base objects of gsl Pointers: + // Temp().ptr; // Here ptr might not dangle. + if (isa<MemberExpr>(Arg->IgnoreImpCasts())) + return; + Path.push_back({IndirectLocalPathEntry::GslPointerInit, Arg, D}); + if (Arg->isGLValue()) + visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding, + Visit, + /*EnableLifetimeWarnings=*/true); + else + visitLocalsRetainedByInitializer(Path, Arg, Visit, true, + /*EnableLifetimeWarnings=*/true); + Path.pop_back(); + }; + + if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) { + const auto *MD = cast_or_null<CXXMethodDecl>(MCE->getDirectCallee()); + if (MD && shouldTrackImplicitObjectArg(MD)) + VisitPointerArg(MD, MCE->getImplicitObjectArgument()); + return; + } else if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(Call)) { + FunctionDecl *Callee = OCE->getDirectCallee(); + if (Callee && Callee->isCXXInstanceMember() && + shouldTrackImplicitObjectArg(cast<CXXMethodDecl>(Callee))) + VisitPointerArg(Callee, OCE->getArg(0)); + return; + } else if (auto *CE = dyn_cast<CallExpr>(Call)) { + FunctionDecl *Callee = CE->getDirectCallee(); + if (Callee && shouldTrackFirstArgument(Callee)) + VisitPointerArg(Callee, CE->getArg(0)); + return; + } + + if (auto *CCE = dyn_cast<CXXConstructExpr>(Call)) { + const auto *Ctor = CCE->getConstructor(); + const CXXRecordDecl *RD = Ctor->getParent(); + if (CCE->getNumArgs() > 0 && RD->hasAttr<PointerAttr>()) + VisitPointerArg(Ctor->getParamDecl(0), CCE->getArgs()[0]); + } +} static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { const TypeSourceInfo *TSI = FD->getTypeSourceInfo(); @@ -6594,9 +6870,11 @@ static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call, Path.push_back({IndirectLocalPathEntry::LifetimeBoundCall, Arg, D}); if (Arg->isGLValue()) visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding, - Visit); + Visit, + /*EnableLifetimeWarnings=*/false); else - visitLocalsRetainedByInitializer(Path, Arg, Visit, true); + visitLocalsRetainedByInitializer(Path, Arg, Visit, true, + /*EnableLifetimeWarnings=*/false); Path.pop_back(); }; @@ -6615,7 +6893,8 @@ static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call, /// glvalue expression \c Init. static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, Expr *Init, ReferenceKind RK, - LocalVisitor Visit) { + LocalVisitor Visit, + bool EnableLifetimeWarnings) { RevertToOldSizeRAII RAII(Path); // Walk past any constructs which we can lifetime-extend across. @@ -6652,7 +6931,8 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, else // We can't lifetime extend through this but we might still find some // retained temporaries. - return visitLocalsRetainedByInitializer(Path, Init, Visit, true); + return visitLocalsRetainedByInitializer(Path, Init, Visit, true, + EnableLifetimeWarnings); } // Step into CXXDefaultInitExprs so we can diagnose cases where a @@ -6667,11 +6947,14 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) { if (Visit(Path, Local(MTE), RK)) visitLocalsRetainedByInitializer(Path, MTE->GetTemporaryExpr(), Visit, - true); + true, EnableLifetimeWarnings); } - if (isa<CallExpr>(Init)) + if (isa<CallExpr>(Init)) { + if (EnableLifetimeWarnings) + handleGslAnnotatedTypes(Path, Init, Visit); return visitLifetimeBoundArguments(Path, Init, Visit); + } switch (Init->getStmtClass()) { case Stmt::DeclRefExprClass: { @@ -6690,7 +6973,8 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, } else if (VD->getInit() && !isVarOnPath(Path, VD)) { Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD}); visitLocalsRetainedByReferenceBinding(Path, VD->getInit(), - RK_ReferenceBinding, Visit); + RK_ReferenceBinding, Visit, + EnableLifetimeWarnings); } } break; @@ -6702,13 +6986,15 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, // handling all sorts of rvalues passed to a unary operator. const UnaryOperator *U = cast<UnaryOperator>(Init); if (U->getOpcode() == UO_Deref) - visitLocalsRetainedByInitializer(Path, U->getSubExpr(), Visit, true); + visitLocalsRetainedByInitializer(Path, U->getSubExpr(), Visit, true, + EnableLifetimeWarnings); break; } case Stmt::OMPArraySectionExprClass: { - visitLocalsRetainedByInitializer( - Path, cast<OMPArraySectionExpr>(Init)->getBase(), Visit, true); + visitLocalsRetainedByInitializer(Path, + cast<OMPArraySectionExpr>(Init)->getBase(), + Visit, true, EnableLifetimeWarnings); break; } @@ -6716,9 +7002,11 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, case Stmt::BinaryConditionalOperatorClass: { auto *C = cast<AbstractConditionalOperator>(Init); if (!C->getTrueExpr()->getType()->isVoidType()) - visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit); + visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit, + EnableLifetimeWarnings); if (!C->getFalseExpr()->getType()->isVoidType()) - visitLocalsRetainedByReferenceBinding(Path, C->getFalseExpr(), RK, Visit); + visitLocalsRetainedByReferenceBinding(Path, C->getFalseExpr(), RK, Visit, + EnableLifetimeWarnings); break; } @@ -6733,7 +7021,8 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, /// the prvalue expression \c Init. static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, Expr *Init, LocalVisitor Visit, - bool RevisitSubinits) { + bool RevisitSubinits, + bool EnableLifetimeWarnings) { RevertToOldSizeRAII RAII(Path); Expr *Old; @@ -6773,15 +7062,17 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, if (VD && VD->getType().isConstQualified() && VD->getInit() && !isVarOnPath(Path, VD)) { Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD}); - visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, true); + visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, true, + EnableLifetimeWarnings); } } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) { if (MTE->getType().isConstQualified()) visitLocalsRetainedByInitializer(Path, MTE->GetTemporaryExpr(), - Visit, true); + Visit, true, + EnableLifetimeWarnings); } return false; - }); + }, EnableLifetimeWarnings); // We assume that objects can be retained by pointers cast to integers, // but not if the integer is cast to floating-point type or to _Complex. @@ -6811,7 +7102,8 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, // lvalue. Path.push_back({IndirectLocalPathEntry::AddressOf, CE}); return visitLocalsRetainedByReferenceBinding(Path, CE->getSubExpr(), - RK_ReferenceBinding, Visit); + RK_ReferenceBinding, Visit, + EnableLifetimeWarnings); default: return; @@ -6826,7 +7118,8 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, // lifetime of the array exactly like binding a reference to a temporary. if (auto *ILE = dyn_cast<CXXStdInitializerListExpr>(Init)) return visitLocalsRetainedByReferenceBinding(Path, ILE->getSubExpr(), - RK_StdInitializerList, Visit); + RK_StdInitializerList, Visit, + EnableLifetimeWarnings); if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { // We already visited the elements of this initializer list while @@ -6837,12 +7130,14 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, if (ILE->isTransparent()) return visitLocalsRetainedByInitializer(Path, ILE->getInit(0), Visit, - RevisitSubinits); + RevisitSubinits, + EnableLifetimeWarnings); if (ILE->getType()->isArrayType()) { for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I) visitLocalsRetainedByInitializer(Path, ILE->getInit(I), Visit, - RevisitSubinits); + RevisitSubinits, + EnableLifetimeWarnings); return; } @@ -6855,12 +7150,14 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, if (RD->isUnion() && ILE->getInitializedFieldInUnion() && ILE->getInitializedFieldInUnion()->getType()->isReferenceType()) visitLocalsRetainedByReferenceBinding(Path, ILE->getInit(0), - RK_ReferenceBinding, Visit); + RK_ReferenceBinding, Visit, + EnableLifetimeWarnings); else { unsigned Index = 0; for (; Index < RD->getNumBases() && Index < ILE->getNumInits(); ++Index) visitLocalsRetainedByInitializer(Path, ILE->getInit(Index), Visit, - RevisitSubinits); + RevisitSubinits, + EnableLifetimeWarnings); for (const auto *I : RD->fields()) { if (Index >= ILE->getNumInits()) break; @@ -6869,13 +7166,15 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, Expr *SubInit = ILE->getInit(Index); if (I->getType()->isReferenceType()) visitLocalsRetainedByReferenceBinding(Path, SubInit, - RK_ReferenceBinding, Visit); + RK_ReferenceBinding, Visit, + EnableLifetimeWarnings); else // This might be either aggregate-initialization of a member or // initialization of a std::initializer_list object. Regardless, // we should recursively lifetime-extend that initializer. visitLocalsRetainedByInitializer(Path, SubInit, Visit, - RevisitSubinits); + RevisitSubinits, + EnableLifetimeWarnings); ++Index; } } @@ -6891,14 +7190,18 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, continue; if (E->isGLValue()) visitLocalsRetainedByReferenceBinding(Path, E, RK_ReferenceBinding, - Visit); + Visit, EnableLifetimeWarnings); else - visitLocalsRetainedByInitializer(Path, E, Visit, true); + visitLocalsRetainedByInitializer(Path, E, Visit, true, + EnableLifetimeWarnings); } } - if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init)) + if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init)) { + if (EnableLifetimeWarnings) + handleGslAnnotatedTypes(Path, Init, Visit); return visitLifetimeBoundArguments(Path, Init, Visit); + } switch (Init->getStmtClass()) { case Stmt::UnaryOperatorClass: { @@ -6914,7 +7217,8 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, Path.push_back({IndirectLocalPathEntry::AddressOf, UO}); visitLocalsRetainedByReferenceBinding(Path, UO->getSubExpr(), - RK_ReferenceBinding, Visit); + RK_ReferenceBinding, Visit, + EnableLifetimeWarnings); } break; } @@ -6927,9 +7231,11 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, break; if (BO->getLHS()->getType()->isPointerType()) - visitLocalsRetainedByInitializer(Path, BO->getLHS(), Visit, true); + visitLocalsRetainedByInitializer(Path, BO->getLHS(), Visit, true, + EnableLifetimeWarnings); else if (BO->getRHS()->getType()->isPointerType()) - visitLocalsRetainedByInitializer(Path, BO->getRHS(), Visit, true); + visitLocalsRetainedByInitializer(Path, BO->getRHS(), Visit, true, + EnableLifetimeWarnings); break; } @@ -6939,9 +7245,11 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, // In C++, we can have a throw-expression operand, which has 'void' type // and isn't interesting from a lifetime perspective. if (!C->getTrueExpr()->getType()->isVoidType()) - visitLocalsRetainedByInitializer(Path, C->getTrueExpr(), Visit, true); + visitLocalsRetainedByInitializer(Path, C->getTrueExpr(), Visit, true, + EnableLifetimeWarnings); if (!C->getFalseExpr()->getType()->isVoidType()) - visitLocalsRetainedByInitializer(Path, C->getFalseExpr(), Visit, true); + visitLocalsRetainedByInitializer(Path, C->getFalseExpr(), Visit, true, + EnableLifetimeWarnings); break; } @@ -6980,18 +7288,33 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I, case IndirectLocalPathEntry::AddressOf: case IndirectLocalPathEntry::LValToRVal: case IndirectLocalPathEntry::LifetimeBoundCall: + case IndirectLocalPathEntry::GslPointerInit: // These exist primarily to mark the path as not permitting or // supporting lifetime extension. break; - case IndirectLocalPathEntry::DefaultInit: case IndirectLocalPathEntry::VarInit: + if (cast<VarDecl>(Path[I].D)->isImplicit()) + return SourceRange(); + LLVM_FALLTHROUGH; + case IndirectLocalPathEntry::DefaultInit: return Path[I].E->getSourceRange(); } } return E->getSourceRange(); } +static bool pathOnlyInitializesGslPointer(IndirectLocalPath &Path) { + for (auto It = Path.rbegin(), End = Path.rend(); It != End; ++It) { + if (It->Kind == IndirectLocalPathEntry::VarInit) + continue; + if (It->Kind == IndirectLocalPathEntry::AddressOf) + continue; + return It->Kind == IndirectLocalPathEntry::GslPointerInit; + } + return false; +} + void Sema::checkInitializerLifetime(const InitializedEntity &Entity, Expr *Init) { LifetimeResult LR = getEntityLifetime(&Entity); @@ -7008,12 +7331,36 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, SourceRange DiagRange = nextPathEntryRange(Path, 0, L); SourceLocation DiagLoc = DiagRange.getBegin(); + auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L); + + bool IsGslPtrInitWithGslTempOwner = false; + bool IsLocalGslOwner = false; + if (pathOnlyInitializesGslPointer(Path)) { + if (isa<DeclRefExpr>(L)) { + // We do not want to follow the references when returning a pointer originating + // from a local owner to avoid the following false positive: + // int &p = *localUniquePtr; + // someContainer.add(std::move(localUniquePtr)); + // return p; + IsLocalGslOwner = isRecordWithAttr<OwnerAttr>(L->getType()); + if (pathContainsInit(Path) || !IsLocalGslOwner) + return false; + } else { + IsGslPtrInitWithGslTempOwner = MTE && !MTE->getExtendingDecl() && + isRecordWithAttr<OwnerAttr>(MTE->getType()); + // Skipping a chain of initializing gsl::Pointer annotated objects. + // We are looking only for the final source to find out if it was + // a local or temporary owner or the address of a local variable/param. + if (!IsGslPtrInitWithGslTempOwner) + return true; + } + } + switch (LK) { case LK_FullExpression: llvm_unreachable("already handled this"); case LK_Extended: { - auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L); if (!MTE) { // The initialized entity has lifetime beyond the full-expression, // and the local entity does too, so don't warn. @@ -7023,6 +7370,11 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, return false; } + if (IsGslPtrInitWithGslTempOwner && DiagLoc.isValid()) { + Diag(DiagLoc, diag::warn_dangling_lifetime_pointer) << DiagRange; + return false; + } + // Lifetime-extend the temporary. if (Path.empty()) { // Update the storage duration of the materialized temporary. @@ -7064,6 +7416,14 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, // temporary, the program is ill-formed. if (auto *ExtendingDecl = ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) { + if (IsGslPtrInitWithGslTempOwner) { + Diag(DiagLoc, diag::warn_dangling_lifetime_pointer_member) + << ExtendingDecl << DiagRange; + Diag(ExtendingDecl->getLocation(), + diag::note_ref_or_ptr_member_declared_here) + << true; + return false; + } bool IsSubobjectMember = ExtendingEntity != &Entity; Diag(DiagLoc, shouldLifetimeExtendThroughPath(Path) ? diag::err_dangling_member @@ -7094,6 +7454,11 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, if (pathContainsInit(Path)) return false; + // Suppress false positives for code like the one below: + // Ctor(unique_ptr<T> up) : member(*up), member2(move(up)) {} + if (IsLocalGslOwner && pathOnlyInitializesGslPointer(Path)) + return false; + auto *DRE = dyn_cast<DeclRefExpr>(L); auto *VD = DRE ? dyn_cast<VarDecl>(DRE->getDecl()) : nullptr; if (!VD) { @@ -7104,7 +7469,7 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, if (auto *Member = ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) { - bool IsPointer = Member->getType()->isAnyPointerType(); + bool IsPointer = !Member->getType()->isReferenceType(); Diag(DiagLoc, IsPointer ? diag::warn_init_ptr_member_to_parameter_addr : diag::warn_bind_ref_member_to_parameter) << Member << VD << isa<ParmVarDecl>(VD) << DiagRange; @@ -7118,10 +7483,13 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, case LK_New: if (isa<MaterializeTemporaryExpr>(L)) { - Diag(DiagLoc, RK == RK_ReferenceBinding - ? diag::warn_new_dangling_reference - : diag::warn_new_dangling_initializer_list) - << !Entity.getParent() << DiagRange; + if (IsGslPtrInitWithGslTempOwner) + Diag(DiagLoc, diag::warn_dangling_lifetime_pointer) << DiagRange; + else + Diag(DiagLoc, RK == RK_ReferenceBinding + ? diag::warn_new_dangling_reference + : diag::warn_new_dangling_initializer_list) + << !Entity.getParent() << DiagRange; } else { // We can't determine if the allocation outlives the local declaration. return false; @@ -7164,7 +7532,8 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, break; case IndirectLocalPathEntry::LifetimeBoundCall: - // FIXME: Consider adding a note for this. + case IndirectLocalPathEntry::GslPointerInit: + // FIXME: Consider adding a note for these. break; case IndirectLocalPathEntry::DefaultInit: { @@ -7189,12 +7558,16 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, return false; }; + bool EnableLifetimeWarnings = !getDiagnostics().isIgnored( + diag::warn_dangling_lifetime_pointer, SourceLocation()); llvm::SmallVector<IndirectLocalPathEntry, 8> Path; if (Init->isGLValue()) visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding, - TemporaryVisitor); + TemporaryVisitor, + EnableLifetimeWarnings); else - visitLocalsRetainedByInitializer(Path, Init, TemporaryVisitor, false); + visitLocalsRetainedByInitializer(Path, Init, TemporaryVisitor, false, + EnableLifetimeWarnings); } static void DiagnoseNarrowingInInitList(Sema &S, @@ -7837,7 +8210,7 @@ ExprResult InitializationSequence::Perform(Sema &S, Ty = S.Context.getRValueReferenceType(Ty); else if ((*ResultType)->isLValueReferenceType()) Ty = S.Context.getLValueReferenceType(Ty, - (*ResultType)->getAs<LValueReferenceType>()->isSpelledAsLValue()); + (*ResultType)->castAs<LValueReferenceType>()->isSpelledAsLValue()); *ResultType = Ty; } @@ -8043,6 +8416,7 @@ ExprResult InitializationSequence::Perform(Sema &S, *ResultType = S.Context.getConstantArrayType( IncompleteDest->getElementType(), ConstantSource->getSize(), + ConstantSource->getSizeExpr(), ArrayType::Normal, 0); } } @@ -8108,7 +8482,7 @@ ExprResult InitializationSequence::Perform(Sema &S, // argument passing. assert(Step->Type->isSamplerT() && "Sampler initialization on non-sampler type."); - Expr *Init = CurInit.get(); + Expr *Init = CurInit.get()->IgnoreParens(); QualType SourceType = Init->getType(); // Case 1 if (Entity.isParameterKind()) { @@ -8285,7 +8659,7 @@ static void diagnoseListInit(Sema &S, const InitializedEntity &Entity, E.withConst(), llvm::APInt(S.Context.getTypeSize(S.Context.getSizeType()), InitList->getNumInits()), - clang::ArrayType::Normal, 0); + nullptr, clang::ArrayType::Normal, 0); InitializedEntity HiddenArray = InitializedEntity::InitializeTemporary(ArrayType); return diagnoseListInit(S, HiddenArray, InitList); @@ -8295,7 +8669,7 @@ static void diagnoseListInit(Sema &S, const InitializedEntity &Entity, // A list-initialization failure for a reference means that we tried to // create a temporary of the inner type (per [dcl.init.list]p3.6) and the // inner initialization failed. - QualType T = DestType->getAs<ReferenceType>()->getPointeeType(); + QualType T = DestType->castAs<ReferenceType>()->getPointeeType(); diagnoseListInit(S, InitializedEntity::InitializeTemporary(T), InitList); SourceLocation Loc = InitList->getBeginLoc(); if (auto *D = Entity.getDecl()) @@ -8652,7 +9026,7 @@ bool InitializationSequence::Diagnose(Sema &S, << InheritedFrom; RecordDecl *BaseDecl - = Entity.getBaseSpecifier()->getType()->getAs<RecordType>() + = Entity.getBaseSpecifier()->getType()->castAs<RecordType>() ->getDecl(); S.Diag(BaseDecl->getLocation(), diag::note_previous_decl) << S.Context.getTagDeclType(BaseDecl); |