diff options
Diffstat (limited to 'clang/lib/Sema/SemaInit.cpp')
| -rw-r--r-- | clang/lib/Sema/SemaInit.cpp | 1430 |
1 files changed, 176 insertions, 1254 deletions
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index ed8b226a6b39..26c65b34f4cc 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "CheckExprLifetime.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" @@ -313,6 +314,8 @@ class InitListChecker { InitListExpr *FullyStructuredList = nullptr; NoInitExpr *DummyExpr = nullptr; SmallVectorImpl<QualType> *AggrDeductionCandidateParamTypes = nullptr; + EmbedExpr *CurEmbed = nullptr; // Save current embed we're processing. + unsigned CurEmbedIndex = 0; NoInitExpr *getDummyInit() { if (!DummyExpr) @@ -501,6 +504,42 @@ class InitListChecker { void CheckEmptyInitializable(const InitializedEntity &Entity, SourceLocation Loc); + Expr *HandleEmbed(EmbedExpr *Embed, const InitializedEntity &Entity) { + Expr *Result = nullptr; + // Undrestand which part of embed we'd like to reference. + if (!CurEmbed) { + CurEmbed = Embed; + CurEmbedIndex = 0; + } + // Reference just one if we're initializing a single scalar. + uint64_t ElsCount = 1; + // Otherwise try to fill whole array with embed data. + if (Entity.getKind() == InitializedEntity::EK_ArrayElement) { + ValueDecl *ArrDecl = Entity.getParent()->getDecl(); + auto *AType = SemaRef.Context.getAsArrayType(ArrDecl->getType()); + assert(AType && "expected array type when initializing array"); + ElsCount = Embed->getDataElementCount(); + if (const auto *CAType = dyn_cast<ConstantArrayType>(AType)) + ElsCount = std::min(CAType->getSize().getZExtValue(), + ElsCount - CurEmbedIndex); + if (ElsCount == Embed->getDataElementCount()) { + CurEmbed = nullptr; + CurEmbedIndex = 0; + return Embed; + } + } + + Result = new (SemaRef.Context) + EmbedExpr(SemaRef.Context, Embed->getLocation(), Embed->getData(), + CurEmbedIndex, ElsCount); + CurEmbedIndex += ElsCount; + if (CurEmbedIndex >= Embed->getDataElementCount()) { + CurEmbed = nullptr; + CurEmbedIndex = 0; + } + return Result; + } + public: InitListChecker( Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T, @@ -513,7 +552,7 @@ public: : InitListChecker(S, Entity, IL, T, /*VerifyOnly=*/true, /*TreatUnavailableAsInvalid=*/false, /*InOverloadResolution=*/false, - &AggrDeductionCandidateParamTypes){}; + &AggrDeductionCandidateParamTypes) {} bool HadError() { return hadError; } @@ -1443,7 +1482,21 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, // dependent non-array type or an array type with a value-dependent // bound assert(AggrDeductionCandidateParamTypes); - if (!isa_and_nonnull<ConstantArrayType>( + + // In the presence of a braced-init-list within the initializer, we should + // not perform brace-elision, even if brace elision would otherwise be + // applicable. For example, given: + // + // template <class T> struct Foo { + // T t[2]; + // }; + // + // Foo t = {{1, 2}}; + // + // we don't want the (T, T) but rather (T [2]) in terms of the initializer + // {{1, 2}}. + if (isa<InitListExpr, DesignatedInitExpr>(expr) || + !isa_and_present<ConstantArrayType>( SemaRef.Context.getAsArrayType(ElemType))) { ++Index; AggrDeductionCandidateParamTypes->push_back(ElemType); @@ -1459,6 +1512,9 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, // Brace elision is never performed if the element is not an // assignment-expression. if (Seq || isa<InitListExpr>(expr)) { + if (auto *Embed = dyn_cast<EmbedExpr>(expr)) { + expr = HandleEmbed(Embed, Entity); + } if (!VerifyOnly) { ExprResult Result = Seq.Perform(SemaRef, TmpEntity, Kind, expr); if (Result.isInvalid()) @@ -1472,7 +1528,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, UpdateStructuredListElement(StructuredList, StructuredIndex, getDummyInit()); } - ++Index; + if (!CurEmbed) + ++Index; if (AggrDeductionCandidateParamTypes) AggrDeductionCandidateParamTypes->push_back(ElemType); return; @@ -1665,6 +1722,8 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity, ++Index; ++StructuredIndex; return; + } else if (auto *Embed = dyn_cast<EmbedExpr>(expr)) { + expr = HandleEmbed(Embed, Entity); } ExprResult Result; @@ -1686,14 +1745,16 @@ void InitListChecker::CheckScalarType(const InitializedEntity &Entity, else { ResultExpr = Result.getAs<Expr>(); - if (ResultExpr != expr && !VerifyOnly) { + if (ResultExpr != expr && !VerifyOnly && !CurEmbed) { // The type was promoted, update initializer list. // FIXME: Why are we updating the syntactic init list? IList->setInit(Index, ResultExpr); } } + UpdateStructuredListElement(StructuredList, StructuredIndex, ResultExpr); - ++Index; + if (!CurEmbed) + ++Index; if (AggrDeductionCandidateParamTypes) AggrDeductionCandidateParamTypes->push_back(DeclType); } @@ -1932,6 +1993,30 @@ static bool checkDestructorReference(QualType ElementType, SourceLocation Loc, return SemaRef.DiagnoseUseOfDecl(Destructor, Loc); } +static bool canInitializeArrayWithEmbedDataString(ArrayRef<Expr *> ExprList, + QualType InitType, + ASTContext &Context) { + // Only one initializer, it's an embed and the types match; + EmbedExpr *EE = + ExprList.size() == 1 + ? dyn_cast_if_present<EmbedExpr>(ExprList[0]->IgnoreParens()) + : nullptr; + if (!EE) + return false; + + if (InitType->isArrayType()) { + const ArrayType *InitArrayType = InitType->getAsArrayTypeUnsafe(); + QualType InitElementTy = InitArrayType->getElementType(); + QualType EmbedExprElementTy = EE->getType(); + const bool TypesMatch = + Context.typesAreCompatible(InitElementTy, EmbedExprElementTy) || + (InitElementTy->isCharType() && EmbedExprElementTy->isCharType()); + if (TypesMatch) + return true; + } + return false; +} + void InitListChecker::CheckArrayType(const InitializedEntity &Entity, InitListExpr *IList, QualType &DeclType, llvm::APSInt elementIndex, @@ -1949,6 +2034,12 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity, } } + if (canInitializeArrayWithEmbedDataString(IList->inits(), DeclType, + SemaRef.Context)) { + EmbedExpr *Embed = cast<EmbedExpr>(IList->inits()[0]); + IList->setInit(0, Embed->getDataStringLiteral()); + } + // Check for the special-case of initializing an array with a string. if (Index < IList->getNumInits()) { if (IsStringInit(IList->getInit(Index), arrayType, SemaRef.Context) == @@ -2051,13 +2142,24 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity, if (maxElementsKnown && elementIndex == maxElements) break; - InitializedEntity ElementEntity = - InitializedEntity::InitializeElement(SemaRef.Context, StructuredIndex, - Entity); + InitializedEntity ElementEntity = InitializedEntity::InitializeElement( + SemaRef.Context, StructuredIndex, Entity); + + unsigned EmbedElementIndexBeforeInit = CurEmbedIndex; // Check this element. CheckSubElementType(ElementEntity, IList, elementType, Index, StructuredList, StructuredIndex); ++elementIndex; + if ((CurEmbed || isa<EmbedExpr>(Init)) && elementType->isScalarType()) { + if (CurEmbed) { + elementIndex = + elementIndex + CurEmbedIndex - EmbedElementIndexBeforeInit - 1; + } else { + auto Embed = cast<EmbedExpr>(Init); + elementIndex = elementIndex + Embed->getDataElementCount() - + EmbedElementIndexBeforeInit - 1; + } + } // If the array is of incomplete type, keep track of the number of // elements in the initializer. @@ -2194,7 +2296,7 @@ void InitListChecker::CheckStructUnionTypes( // Designated inits always initialize fields, so if we see one, all // remaining base classes have no explicit initializer. - if (Init && isa<DesignatedInitExpr>(Init)) + if (isa_and_nonnull<DesignatedInitExpr>(Init)) Init = nullptr; // C++ [over.match.class.deduct]p1.6: @@ -6350,7 +6452,7 @@ void InitializationSequence::InitializeFrom(Sema &S, // class member of array type from a parenthesized initializer list. else if (S.getLangOpts().CPlusPlus && Entity.getKind() == InitializedEntity::EK_Member && - Initializer && isa<InitListExpr>(Initializer)) { + isa_and_nonnull<InitListExpr>(Initializer)) { TryListInitialization(S, Entity, Kind, cast<InitListExpr>(Initializer), *this, TreatUnavailableAsInvalid); AddParenthesizedArrayInitStep(DestType); @@ -7183,1238 +7285,9 @@ PerformConstructorInitialization(Sema &S, return CurInit; } -namespace { -enum LifetimeKind { - /// The lifetime of a temporary bound to this entity ends at the end of the - /// full-expression, and that's (probably) fine. - LK_FullExpression, - - /// The lifetime of a temporary bound to this entity is extended to the - /// lifeitme of the entity itself. - LK_Extended, - - /// The lifetime of a temporary bound to this entity probably ends too soon, - /// because the entity is allocated in a new-expression. - LK_New, - - /// The lifetime of a temporary bound to this entity ends too soon, because - /// the entity is a return object. - LK_Return, - - /// The lifetime of a temporary bound to this entity ends too soon, because - /// the entity is the result of a statement expression. - LK_StmtExprResult, - - /// This is a mem-initializer: if it would extend a temporary (other than via - /// a default member initializer), the program is ill-formed. - LK_MemInitializer, -}; -using LifetimeResult = - llvm::PointerIntPair<const InitializedEntity *, 3, LifetimeKind>; -} - -/// Determine the declaration which an initialized entity ultimately refers to, -/// for the purpose of lifetime-extending a temporary bound to a reference in -/// the initialization of \p Entity. -static LifetimeResult getEntityLifetime( - const InitializedEntity *Entity, - const InitializedEntity *InitField = nullptr) { - // C++11 [class.temporary]p5: - switch (Entity->getKind()) { - case InitializedEntity::EK_Variable: - // The temporary [...] persists for the lifetime of the reference - return {Entity, LK_Extended}; - - case InitializedEntity::EK_Member: - // For subobjects, we look at the complete object. - if (Entity->getParent()) - return getEntityLifetime(Entity->getParent(), Entity); - - // except: - // C++17 [class.base.init]p8: - // A temporary expression bound to a reference member in a - // mem-initializer is ill-formed. - // C++17 [class.base.init]p11: - // A temporary expression bound to a reference member from a - // default member initializer is ill-formed. - // - // The context of p11 and its example suggest that it's only the use of a - // default member initializer from a constructor that makes the program - // ill-formed, not its mere existence, and that it can even be used by - // aggregate initialization. - return {Entity, Entity->isDefaultMemberInitializer() ? LK_Extended - : LK_MemInitializer}; - - case InitializedEntity::EK_Binding: - // Per [dcl.decomp]p3, the binding is treated as a variable of reference - // type. - return {Entity, LK_Extended}; - - case InitializedEntity::EK_Parameter: - case InitializedEntity::EK_Parameter_CF_Audited: - // -- A temporary bound to a reference parameter in a function call - // persists until the completion of the full-expression containing - // the call. - return {nullptr, LK_FullExpression}; - - case InitializedEntity::EK_TemplateParameter: - // FIXME: This will always be ill-formed; should we eagerly diagnose it here? - return {nullptr, LK_FullExpression}; - - case InitializedEntity::EK_Result: - // -- The lifetime of a temporary bound to the returned value in a - // function return statement is not extended; the temporary is - // destroyed at the end of the full-expression in the return statement. - return {nullptr, LK_Return}; - - case InitializedEntity::EK_StmtExprResult: - // FIXME: Should we lifetime-extend through the result of a statement - // expression? - return {nullptr, LK_StmtExprResult}; - - case InitializedEntity::EK_New: - // -- A temporary bound to a reference in a new-initializer persists - // until the completion of the full-expression containing the - // new-initializer. - return {nullptr, LK_New}; - - case InitializedEntity::EK_Temporary: - case InitializedEntity::EK_CompoundLiteralInit: - case InitializedEntity::EK_RelatedResult: - // We don't yet know the storage duration of the surrounding temporary. - // Assume it's got full-expression duration for now, it will patch up our - // storage duration if that's not correct. - return {nullptr, LK_FullExpression}; - - case InitializedEntity::EK_ArrayElement: - // For subobjects, we look at the complete object. - return getEntityLifetime(Entity->getParent(), InitField); - - case InitializedEntity::EK_Base: - // For subobjects, we look at the complete object. - if (Entity->getParent()) - return getEntityLifetime(Entity->getParent(), InitField); - return {InitField, LK_MemInitializer}; - - case InitializedEntity::EK_Delegating: - // We can reach this case for aggregate initialization in a constructor: - // struct A { int &&r; }; - // struct B : A { B() : A{0} {} }; - // In this case, use the outermost field decl as the context. - return {InitField, LK_MemInitializer}; - - case InitializedEntity::EK_BlockElement: - case InitializedEntity::EK_LambdaToBlockConversionBlockElement: - case InitializedEntity::EK_LambdaCapture: - case InitializedEntity::EK_VectorElement: - case InitializedEntity::EK_ComplexElement: - return {nullptr, LK_FullExpression}; - - case InitializedEntity::EK_Exception: - // FIXME: Can we diagnose lifetime problems with exceptions? - return {nullptr, LK_FullExpression}; - - case InitializedEntity::EK_ParenAggInitMember: - // -- A temporary object bound to a reference element of an aggregate of - // class type initialized from a parenthesized expression-list - // [dcl.init, 9.3] persists until the completion of the full-expression - // containing the expression-list. - return {nullptr, LK_FullExpression}; - } - - llvm_unreachable("unknown entity kind"); -} - -namespace { -enum ReferenceKind { - /// Lifetime would be extended by a reference binding to a temporary. - RK_ReferenceBinding, - /// Lifetime would be extended by a std::initializer_list object binding to - /// its backing array. - RK_StdInitializerList, -}; - -/// A temporary or local variable. This will be one of: -/// * A MaterializeTemporaryExpr. -/// * A DeclRefExpr whose declaration is a local. -/// * An AddrLabelExpr. -/// * A BlockExpr for a block with captures. -using Local = Expr*; - -/// Expressions we stepped over when looking for the local state. Any steps -/// that would inhibit lifetime extension or take us out of subexpressions of -/// the initializer are included. -struct IndirectLocalPathEntry { - enum EntryKind { - DefaultInit, - AddressOf, - VarInit, - LValToRVal, - LifetimeBoundCall, - TemporaryCopy, - LambdaCaptureInit, - GslReferenceInit, - GslPointerInit - } Kind; - Expr *E; - union { - const Decl *D = nullptr; - const LambdaCapture *Capture; - }; - IndirectLocalPathEntry() {} - IndirectLocalPathEntry(EntryKind K, Expr *E) : Kind(K), E(E) {} - IndirectLocalPathEntry(EntryKind K, Expr *E, const Decl *D) - : Kind(K), E(E), D(D) {} - IndirectLocalPathEntry(EntryKind K, Expr *E, const LambdaCapture *Capture) - : Kind(K), E(E), Capture(Capture) {} -}; - -using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>; - -struct RevertToOldSizeRAII { - IndirectLocalPath &Path; - unsigned OldSize = Path.size(); - RevertToOldSizeRAII(IndirectLocalPath &Path) : Path(Path) {} - ~RevertToOldSizeRAII() { Path.resize(OldSize); } -}; - -using LocalVisitor = llvm::function_ref<bool(IndirectLocalPath &Path, Local L, - ReferenceKind RK)>; -} - -static bool isVarOnPath(IndirectLocalPath &Path, VarDecl *VD) { - for (auto E : Path) - if (E.Kind == IndirectLocalPathEntry::VarInit && E.D == VD) - return true; - return false; -} - -static bool pathContainsInit(IndirectLocalPath &Path) { - return llvm::any_of(Path, [=](IndirectLocalPathEntry E) { - return E.Kind == IndirectLocalPathEntry::DefaultInit || - E.Kind == IndirectLocalPathEntry::VarInit; - }); -} - -static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, - Expr *Init, LocalVisitor Visit, - bool RevisitSubinits, - bool EnableLifetimeWarnings); - -static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, - Expr *Init, ReferenceKind RK, - 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->getFunctionObjectParameterType()) && - !isRecordWithAttr<OwnerAttr>(Callee->getFunctionObjectParameterType())) - 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, bool Value) { - // 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; - // Once we initialized a value with a reference, it can no longer dangle. - if (!Value) { - for (const IndirectLocalPathEntry &PE : llvm::reverse(Path)) { - if (PE.Kind == IndirectLocalPathEntry::GslReferenceInit) - continue; - if (PE.Kind == IndirectLocalPathEntry::GslPointerInit) - return; - break; - } - } - Path.push_back({Value ? IndirectLocalPathEntry::GslPointerInit - : IndirectLocalPathEntry::GslReferenceInit, - 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(), - !MD->getReturnType()->isReferenceType()); - 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), - !Callee->getReturnType()->isReferenceType()); - return; - } else if (auto *CE = dyn_cast<CallExpr>(Call)) { - FunctionDecl *Callee = CE->getDirectCallee(); - if (Callee && shouldTrackFirstArgument(Callee)) - VisitPointerArg(Callee, CE->getArg(0), - !Callee->getReturnType()->isReferenceType()); - 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], true); - } -} - -static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { - const TypeSourceInfo *TSI = FD->getTypeSourceInfo(); - if (!TSI) - return false; - // Don't declare this variable in the second operand of the for-statement; - // GCC miscompiles that by ending its lifetime before evaluating the - // third operand. See gcc.gnu.org/PR86769. - AttributedTypeLoc ATL; - for (TypeLoc TL = TSI->getTypeLoc(); - (ATL = TL.getAsAdjusted<AttributedTypeLoc>()); - TL = ATL.getModifiedLoc()) { - if (ATL.getAttrAs<LifetimeBoundAttr>()) - return true; - } - - // Assume that all assignment operators with a "normal" return type return - // *this, that is, an lvalue reference that is the same type as the implicit - // object parameter (or the LHS for a non-member operator$=). - OverloadedOperatorKind OO = FD->getDeclName().getCXXOverloadedOperator(); - if (OO == OO_Equal || isCompoundAssignmentOperator(OO)) { - QualType RetT = FD->getReturnType(); - if (RetT->isLValueReferenceType()) { - ASTContext &Ctx = FD->getASTContext(); - QualType LHST; - auto *MD = dyn_cast<CXXMethodDecl>(FD); - if (MD && MD->isCXXInstanceMember()) - LHST = Ctx.getLValueReferenceType(MD->getFunctionObjectParameterType()); - else - LHST = MD->getParamDecl(0)->getType(); - if (Ctx.hasSameType(RetT, LHST)) - return true; - } - } - - return false; -} - -static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call, - LocalVisitor Visit) { - const FunctionDecl *Callee; - ArrayRef<Expr*> Args; - - if (auto *CE = dyn_cast<CallExpr>(Call)) { - Callee = CE->getDirectCallee(); - Args = llvm::ArrayRef(CE->getArgs(), CE->getNumArgs()); - } else { - auto *CCE = cast<CXXConstructExpr>(Call); - Callee = CCE->getConstructor(); - Args = llvm::ArrayRef(CCE->getArgs(), CCE->getNumArgs()); - } - if (!Callee) - return; - - Expr *ObjectArg = nullptr; - if (isa<CXXOperatorCallExpr>(Call) && Callee->isCXXInstanceMember()) { - ObjectArg = Args[0]; - Args = Args.slice(1); - } else if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) { - ObjectArg = MCE->getImplicitObjectArgument(); - } - - auto VisitLifetimeBoundArg = [&](const Decl *D, Expr *Arg) { - Path.push_back({IndirectLocalPathEntry::LifetimeBoundCall, Arg, D}); - if (Arg->isGLValue()) - visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding, - Visit, - /*EnableLifetimeWarnings=*/false); - else - visitLocalsRetainedByInitializer(Path, Arg, Visit, true, - /*EnableLifetimeWarnings=*/false); - Path.pop_back(); - }; - - bool CheckCoroCall = false; - if (const auto *RD = Callee->getReturnType()->getAsRecordDecl()) { - CheckCoroCall = RD->hasAttr<CoroLifetimeBoundAttr>() && - RD->hasAttr<CoroReturnTypeAttr>() && - !Callee->hasAttr<CoroDisableLifetimeBoundAttr>(); - } - - if (ObjectArg) { - bool CheckCoroObjArg = CheckCoroCall; - // Coroutine lambda objects with empty capture list are not lifetimebound. - if (auto *LE = dyn_cast<LambdaExpr>(ObjectArg->IgnoreImplicit()); - LE && LE->captures().empty()) - CheckCoroObjArg = false; - // Allow `get_return_object()` as the object param (__promise) is not - // lifetimebound. - if (Sema::CanBeGetReturnObject(Callee)) - CheckCoroObjArg = false; - if (implicitObjectParamIsLifetimeBound(Callee) || CheckCoroObjArg) - VisitLifetimeBoundArg(Callee, ObjectArg); - } - - for (unsigned I = 0, - N = std::min<unsigned>(Callee->getNumParams(), Args.size()); - I != N; ++I) { - if (CheckCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>()) - VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]); - } -} - -/// Visit the locals that would be reachable through a reference bound to the -/// glvalue expression \c Init. -static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, - Expr *Init, ReferenceKind RK, - LocalVisitor Visit, - bool EnableLifetimeWarnings) { - RevertToOldSizeRAII RAII(Path); - - // Walk past any constructs which we can lifetime-extend across. - Expr *Old; - do { - Old = Init; - - if (auto *FE = dyn_cast<FullExpr>(Init)) - Init = FE->getSubExpr(); - - if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { - // If this is just redundant braces around an initializer, step over it. - if (ILE->isTransparent()) - Init = ILE->getInit(0); - } - - // Step over any subobject adjustments; we may have a materialized - // temporary inside them. - Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments()); - - // Per current approach for DR1376, look through casts to reference type - // when performing lifetime extension. - if (CastExpr *CE = dyn_cast<CastExpr>(Init)) - if (CE->getSubExpr()->isGLValue()) - Init = CE->getSubExpr(); - - // Per the current approach for DR1299, look through array element access - // on array glvalues when performing lifetime extension. - if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Init)) { - Init = ASE->getBase(); - auto *ICE = dyn_cast<ImplicitCastExpr>(Init); - if (ICE && ICE->getCastKind() == CK_ArrayToPointerDecay) - Init = ICE->getSubExpr(); - else - // We can't lifetime extend through this but we might still find some - // retained temporaries. - return visitLocalsRetainedByInitializer(Path, Init, Visit, true, - EnableLifetimeWarnings); - } - - // Step into CXXDefaultInitExprs so we can diagnose cases where a - // constructor inherits one as an implicit mem-initializer. - if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) { - Path.push_back( - {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); - Init = DIE->getExpr(); - } - } while (Init != Old); - - if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) { - if (Visit(Path, Local(MTE), RK)) - visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), Visit, true, - EnableLifetimeWarnings); - } - - if (isa<CallExpr>(Init)) { - if (EnableLifetimeWarnings) - handleGslAnnotatedTypes(Path, Init, Visit); - return visitLifetimeBoundArguments(Path, Init, Visit); - } - - switch (Init->getStmtClass()) { - case Stmt::DeclRefExprClass: { - // If we find the name of a local non-reference parameter, we could have a - // lifetime problem. - auto *DRE = cast<DeclRefExpr>(Init); - auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (VD && VD->hasLocalStorage() && - !DRE->refersToEnclosingVariableOrCapture()) { - if (!VD->getType()->isReferenceType()) { - Visit(Path, Local(DRE), RK); - } else if (isa<ParmVarDecl>(DRE->getDecl())) { - // The lifetime of a reference parameter is unknown; assume it's OK - // for now. - break; - } else if (VD->getInit() && !isVarOnPath(Path, VD)) { - Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD}); - visitLocalsRetainedByReferenceBinding(Path, VD->getInit(), - RK_ReferenceBinding, Visit, - EnableLifetimeWarnings); - } - } - break; - } - - case Stmt::UnaryOperatorClass: { - // The only unary operator that make sense to handle here - // is Deref. All others don't resolve to a "name." This includes - // 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, - EnableLifetimeWarnings); - break; - } - - case Stmt::ArraySectionExprClass: { - visitLocalsRetainedByInitializer(Path, - cast<ArraySectionExpr>(Init)->getBase(), - Visit, true, EnableLifetimeWarnings); - break; - } - - case Stmt::ConditionalOperatorClass: - case Stmt::BinaryConditionalOperatorClass: { - auto *C = cast<AbstractConditionalOperator>(Init); - if (!C->getTrueExpr()->getType()->isVoidType()) - visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit, - EnableLifetimeWarnings); - if (!C->getFalseExpr()->getType()->isVoidType()) - visitLocalsRetainedByReferenceBinding(Path, C->getFalseExpr(), RK, Visit, - EnableLifetimeWarnings); - break; - } - - case Stmt::CompoundLiteralExprClass: { - if (auto *CLE = dyn_cast<CompoundLiteralExpr>(Init)) { - if (!CLE->isFileScope()) - Visit(Path, Local(CLE), RK); - } - break; - } - - // FIXME: Visit the left-hand side of an -> or ->*. - - default: - break; - } -} - -/// Visit the locals that would be reachable through an object initialized by -/// the prvalue expression \c Init. -static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, - Expr *Init, LocalVisitor Visit, - bool RevisitSubinits, - bool EnableLifetimeWarnings) { - RevertToOldSizeRAII RAII(Path); - - Expr *Old; - do { - Old = Init; - - // Step into CXXDefaultInitExprs so we can diagnose cases where a - // constructor inherits one as an implicit mem-initializer. - if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) { - Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); - Init = DIE->getExpr(); - } - - if (auto *FE = dyn_cast<FullExpr>(Init)) - Init = FE->getSubExpr(); - - // Dig out the expression which constructs the extended temporary. - Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments()); - - if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Init)) - Init = BTE->getSubExpr(); - - Init = Init->IgnoreParens(); - - // Step over value-preserving rvalue casts. - if (auto *CE = dyn_cast<CastExpr>(Init)) { - switch (CE->getCastKind()) { - case CK_LValueToRValue: - // If we can match the lvalue to a const object, we can look at its - // initializer. - Path.push_back({IndirectLocalPathEntry::LValToRVal, CE}); - return visitLocalsRetainedByReferenceBinding( - Path, Init, RK_ReferenceBinding, - [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool { - if (auto *DRE = dyn_cast<DeclRefExpr>(L)) { - auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (VD && VD->getType().isConstQualified() && VD->getInit() && - !isVarOnPath(Path, VD)) { - Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD}); - visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, true, - EnableLifetimeWarnings); - } - } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) { - if (MTE->getType().isConstQualified()) - visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), 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. - // We assume that casts to 'bool' do not preserve enough information to - // retain a local object. - case CK_NoOp: - case CK_BitCast: - case CK_BaseToDerived: - case CK_DerivedToBase: - case CK_UncheckedDerivedToBase: - case CK_Dynamic: - case CK_ToUnion: - case CK_UserDefinedConversion: - case CK_ConstructorConversion: - case CK_IntegralToPointer: - case CK_PointerToIntegral: - case CK_VectorSplat: - case CK_IntegralCast: - case CK_CPointerToObjCPointerCast: - case CK_BlockPointerToObjCPointerCast: - case CK_AnyPointerToBlockPointerCast: - case CK_AddressSpaceConversion: - break; - - case CK_ArrayToPointerDecay: - // Model array-to-pointer decay as taking the address of the array - // lvalue. - Path.push_back({IndirectLocalPathEntry::AddressOf, CE}); - return visitLocalsRetainedByReferenceBinding(Path, CE->getSubExpr(), - RK_ReferenceBinding, Visit, - EnableLifetimeWarnings); - - default: - return; - } - - Init = CE->getSubExpr(); - } - } while (Old != Init); - - // C++17 [dcl.init.list]p6: - // initializing an initializer_list object from the array extends the - // 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, - EnableLifetimeWarnings); - - if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { - // We already visited the elements of this initializer list while - // performing the initialization. Don't visit them again unless we've - // changed the lifetime of the initialized entity. - if (!RevisitSubinits) - return; - - if (ILE->isTransparent()) - return visitLocalsRetainedByInitializer(Path, ILE->getInit(0), Visit, - RevisitSubinits, - EnableLifetimeWarnings); - - if (ILE->getType()->isArrayType()) { - for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I) - visitLocalsRetainedByInitializer(Path, ILE->getInit(I), Visit, - RevisitSubinits, - EnableLifetimeWarnings); - return; - } - - if (CXXRecordDecl *RD = ILE->getType()->getAsCXXRecordDecl()) { - assert(RD->isAggregate() && "aggregate init on non-aggregate"); - - // If we lifetime-extend a braced initializer which is initializing an - // aggregate, and that aggregate contains reference members which are - // bound to temporaries, those temporaries are also lifetime-extended. - if (RD->isUnion() && ILE->getInitializedFieldInUnion() && - ILE->getInitializedFieldInUnion()->getType()->isReferenceType()) - visitLocalsRetainedByReferenceBinding(Path, ILE->getInit(0), - RK_ReferenceBinding, Visit, - EnableLifetimeWarnings); - else { - unsigned Index = 0; - for (; Index < RD->getNumBases() && Index < ILE->getNumInits(); ++Index) - visitLocalsRetainedByInitializer(Path, ILE->getInit(Index), Visit, - RevisitSubinits, - EnableLifetimeWarnings); - for (const auto *I : RD->fields()) { - if (Index >= ILE->getNumInits()) - break; - if (I->isUnnamedBitField()) - continue; - Expr *SubInit = ILE->getInit(Index); - if (I->getType()->isReferenceType()) - visitLocalsRetainedByReferenceBinding(Path, SubInit, - 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, - EnableLifetimeWarnings); - ++Index; - } - } - } - return; - } - - // The lifetime of an init-capture is that of the closure object constructed - // by a lambda-expression. - if (auto *LE = dyn_cast<LambdaExpr>(Init)) { - LambdaExpr::capture_iterator CapI = LE->capture_begin(); - for (Expr *E : LE->capture_inits()) { - assert(CapI != LE->capture_end()); - const LambdaCapture &Cap = *CapI++; - if (!E) - continue; - if (Cap.capturesVariable()) - Path.push_back({IndirectLocalPathEntry::LambdaCaptureInit, E, &Cap}); - if (E->isGLValue()) - visitLocalsRetainedByReferenceBinding(Path, E, RK_ReferenceBinding, - Visit, EnableLifetimeWarnings); - else - visitLocalsRetainedByInitializer(Path, E, Visit, true, - EnableLifetimeWarnings); - if (Cap.capturesVariable()) - Path.pop_back(); - } - } - - // Assume that a copy or move from a temporary references the same objects - // that the temporary does. - if (auto *CCE = dyn_cast<CXXConstructExpr>(Init)) { - if (CCE->getConstructor()->isCopyOrMoveConstructor()) { - if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(CCE->getArg(0))) { - Expr *Arg = MTE->getSubExpr(); - Path.push_back({IndirectLocalPathEntry::TemporaryCopy, Arg, - CCE->getConstructor()}); - visitLocalsRetainedByInitializer(Path, Arg, Visit, true, - /*EnableLifetimeWarnings*/false); - Path.pop_back(); - } - } - } - - if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init)) { - if (EnableLifetimeWarnings) - handleGslAnnotatedTypes(Path, Init, Visit); - return visitLifetimeBoundArguments(Path, Init, Visit); - } - - switch (Init->getStmtClass()) { - case Stmt::UnaryOperatorClass: { - auto *UO = cast<UnaryOperator>(Init); - // If the initializer is the address of a local, we could have a lifetime - // problem. - if (UO->getOpcode() == UO_AddrOf) { - // If this is &rvalue, then it's ill-formed and we have already diagnosed - // it. Don't produce a redundant warning about the lifetime of the - // temporary. - if (isa<MaterializeTemporaryExpr>(UO->getSubExpr())) - return; - - Path.push_back({IndirectLocalPathEntry::AddressOf, UO}); - visitLocalsRetainedByReferenceBinding(Path, UO->getSubExpr(), - RK_ReferenceBinding, Visit, - EnableLifetimeWarnings); - } - break; - } - - case Stmt::BinaryOperatorClass: { - // Handle pointer arithmetic. - auto *BO = cast<BinaryOperator>(Init); - BinaryOperatorKind BOK = BO->getOpcode(); - if (!BO->getType()->isPointerType() || (BOK != BO_Add && BOK != BO_Sub)) - break; - - if (BO->getLHS()->getType()->isPointerType()) - visitLocalsRetainedByInitializer(Path, BO->getLHS(), Visit, true, - EnableLifetimeWarnings); - else if (BO->getRHS()->getType()->isPointerType()) - visitLocalsRetainedByInitializer(Path, BO->getRHS(), Visit, true, - EnableLifetimeWarnings); - break; - } - - case Stmt::ConditionalOperatorClass: - case Stmt::BinaryConditionalOperatorClass: { - auto *C = cast<AbstractConditionalOperator>(Init); - // 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, - EnableLifetimeWarnings); - if (!C->getFalseExpr()->getType()->isVoidType()) - visitLocalsRetainedByInitializer(Path, C->getFalseExpr(), Visit, true, - EnableLifetimeWarnings); - break; - } - - case Stmt::BlockExprClass: - if (cast<BlockExpr>(Init)->getBlockDecl()->hasCaptures()) { - // This is a local block, whose lifetime is that of the function. - Visit(Path, Local(cast<BlockExpr>(Init)), RK_ReferenceBinding); - } - break; - - case Stmt::AddrLabelExprClass: - // We want to warn if the address of a label would escape the function. - Visit(Path, Local(cast<AddrLabelExpr>(Init)), RK_ReferenceBinding); - break; - - default: - break; - } -} - -/// Whether a path to an object supports lifetime extension. -enum PathLifetimeKind { - /// Lifetime-extend along this path. - Extend, - /// We should lifetime-extend, but we don't because (due to technical - /// limitations) we can't. This happens for default member initializers, - /// which we don't clone for every use, so we don't have a unique - /// MaterializeTemporaryExpr to update. - ShouldExtend, - /// Do not lifetime extend along this path. - NoExtend -}; - -/// Determine whether this is an indirect path to a temporary that we are -/// supposed to lifetime-extend along. -static PathLifetimeKind -shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) { - PathLifetimeKind Kind = PathLifetimeKind::Extend; - for (auto Elem : Path) { - if (Elem.Kind == IndirectLocalPathEntry::DefaultInit) - Kind = PathLifetimeKind::ShouldExtend; - else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit) - return PathLifetimeKind::NoExtend; - } - return Kind; -} - -/// Find the range for the first interesting entry in the path at or after I. -static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I, - Expr *E) { - for (unsigned N = Path.size(); I != N; ++I) { - switch (Path[I].Kind) { - case IndirectLocalPathEntry::AddressOf: - case IndirectLocalPathEntry::LValToRVal: - case IndirectLocalPathEntry::LifetimeBoundCall: - case IndirectLocalPathEntry::TemporaryCopy: - case IndirectLocalPathEntry::GslReferenceInit: - case IndirectLocalPathEntry::GslPointerInit: - // These exist primarily to mark the path as not permitting or - // supporting lifetime extension. - break; - - case IndirectLocalPathEntry::VarInit: - if (cast<VarDecl>(Path[I].D)->isImplicit()) - return SourceRange(); - [[fallthrough]]; - case IndirectLocalPathEntry::DefaultInit: - return Path[I].E->getSourceRange(); - - case IndirectLocalPathEntry::LambdaCaptureInit: - if (!Path[I].Capture->capturesVariable()) - continue; - return Path[I].E->getSourceRange(); - } - } - return E->getSourceRange(); -} - -static bool pathOnlyInitializesGslPointer(IndirectLocalPath &Path) { - for (const auto &It : llvm::reverse(Path)) { - if (It.Kind == IndirectLocalPathEntry::VarInit) - continue; - if (It.Kind == IndirectLocalPathEntry::AddressOf) - continue; - if (It.Kind == IndirectLocalPathEntry::LifetimeBoundCall) - continue; - return It.Kind == IndirectLocalPathEntry::GslPointerInit || - It.Kind == IndirectLocalPathEntry::GslReferenceInit; - } - return false; -} - void Sema::checkInitializerLifetime(const InitializedEntity &Entity, Expr *Init) { - LifetimeResult LR = getEntityLifetime(&Entity); - LifetimeKind LK = LR.getInt(); - const InitializedEntity *ExtendingEntity = LR.getPointer(); - - // If this entity doesn't have an interesting lifetime, don't bother looking - // for temporaries within its initializer. - if (LK == LK_FullExpression) - return; - - auto TemporaryVisitor = [&](IndirectLocalPath &Path, Local L, - ReferenceKind RK) -> bool { - 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: { - if (!MTE) { - // The initialized entity has lifetime beyond the full-expression, - // and the local entity does too, so don't warn. - // - // FIXME: We should consider warning if a static / thread storage - // duration variable retains an automatic storage duration local. - return false; - } - - if (IsGslPtrInitWithGslTempOwner && DiagLoc.isValid()) { - Diag(DiagLoc, diag::warn_dangling_lifetime_pointer) << DiagRange; - return false; - } - - switch (shouldLifetimeExtendThroughPath(Path)) { - case PathLifetimeKind::Extend: - // Update the storage duration of the materialized temporary. - // FIXME: Rebuild the expression instead of mutating it. - MTE->setExtendingDecl(ExtendingEntity->getDecl(), - ExtendingEntity->allocateManglingNumber()); - // Also visit the temporaries lifetime-extended by this initializer. - return true; - - case PathLifetimeKind::ShouldExtend: - // We're supposed to lifetime-extend the temporary along this path (per - // the resolution of DR1815), but we don't support that yet. - // - // FIXME: Properly handle this situation. Perhaps the easiest approach - // would be to clone the initializer expression on each use that would - // lifetime extend its temporaries. - Diag(DiagLoc, diag::warn_unsupported_lifetime_extension) - << RK << DiagRange; - break; - - case PathLifetimeKind::NoExtend: - // If the path goes through the initialization of a variable or field, - // it can't possibly reach a temporary created in this full-expression. - // We will have already diagnosed any problems with the initializer. - if (pathContainsInit(Path)) - return false; - - Diag(DiagLoc, diag::warn_dangling_variable) - << RK << !Entity.getParent() - << ExtendingEntity->getDecl()->isImplicit() - << ExtendingEntity->getDecl() << Init->isGLValue() << DiagRange; - break; - } - break; - } - - case LK_MemInitializer: { - if (isa<MaterializeTemporaryExpr>(L)) { - // Under C++ DR1696, if a mem-initializer (or a default member - // initializer used by the absence of one) would lifetime-extend a - // 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) != - PathLifetimeKind::NoExtend - ? diag::err_dangling_member - : diag::warn_dangling_member) - << ExtendingDecl << IsSubobjectMember << RK << DiagRange; - // Don't bother adding a note pointing to the field if we're inside - // its default member initializer; our primary diagnostic points to - // the same place in that case. - if (Path.empty() || - Path.back().Kind != IndirectLocalPathEntry::DefaultInit) { - Diag(ExtendingDecl->getLocation(), - diag::note_lifetime_extending_member_declared_here) - << RK << IsSubobjectMember; - } - } else { - // We have a mem-initializer but no particular field within it; this - // is either a base class or a delegating initializer directly - // initializing the base-class from something that doesn't live long - // enough. - // - // FIXME: Warn on this. - return false; - } - } else { - // Paths via a default initializer can only occur during error recovery - // (there's no other way that a default initializer can refer to a - // local). Don't produce a bogus warning on those cases. - 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) { - // A member was initialized to a local block. - // FIXME: Warn on this. - return false; - } - - if (auto *Member = - ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) { - 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; - Diag(Member->getLocation(), - diag::note_ref_or_ptr_member_declared_here) - << (unsigned)IsPointer; - } - } - break; - } - - case LK_New: - if (isa<MaterializeTemporaryExpr>(L)) { - 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; - } - break; - - case LK_Return: - case LK_StmtExprResult: - if (auto *DRE = dyn_cast<DeclRefExpr>(L)) { - // We can't determine if the local variable outlives the statement - // expression. - if (LK == LK_StmtExprResult) - return false; - Diag(DiagLoc, diag::warn_ret_stack_addr_ref) - << Entity.getType()->isReferenceType() << DRE->getDecl() - << isa<ParmVarDecl>(DRE->getDecl()) << DiagRange; - } else if (isa<BlockExpr>(L)) { - Diag(DiagLoc, diag::err_ret_local_block) << DiagRange; - } else if (isa<AddrLabelExpr>(L)) { - // Don't warn when returning a label from a statement expression. - // Leaving the scope doesn't end its lifetime. - if (LK == LK_StmtExprResult) - return false; - Diag(DiagLoc, diag::warn_ret_addr_label) << DiagRange; - } else if (auto *CLE = dyn_cast<CompoundLiteralExpr>(L)) { - Diag(DiagLoc, diag::warn_ret_stack_addr_ref) - << Entity.getType()->isReferenceType() << CLE->getInitializer() << 2 - << DiagRange; - } else { - // P2748R5: Disallow Binding a Returned Glvalue to a Temporary. - // [stmt.return]/p6: In a function whose return type is a reference, - // other than an invented function for std::is_convertible ([meta.rel]), - // a return statement that binds the returned reference to a temporary - // expression ([class.temporary]) is ill-formed. - if (getLangOpts().CPlusPlus26 && Entity.getType()->isReferenceType()) - Diag(DiagLoc, diag::err_ret_local_temp_ref) - << Entity.getType()->isReferenceType() << DiagRange; - else - Diag(DiagLoc, diag::warn_ret_local_temp_addr_ref) - << Entity.getType()->isReferenceType() << DiagRange; - } - break; - } - - for (unsigned I = 0; I != Path.size(); ++I) { - auto Elem = Path[I]; - - switch (Elem.Kind) { - case IndirectLocalPathEntry::AddressOf: - case IndirectLocalPathEntry::LValToRVal: - // These exist primarily to mark the path as not permitting or - // supporting lifetime extension. - break; - - case IndirectLocalPathEntry::LifetimeBoundCall: - case IndirectLocalPathEntry::TemporaryCopy: - case IndirectLocalPathEntry::GslPointerInit: - case IndirectLocalPathEntry::GslReferenceInit: - // FIXME: Consider adding a note for these. - break; - - case IndirectLocalPathEntry::DefaultInit: { - auto *FD = cast<FieldDecl>(Elem.D); - Diag(FD->getLocation(), diag::note_init_with_default_member_initializer) - << FD << nextPathEntryRange(Path, I + 1, L); - break; - } - - case IndirectLocalPathEntry::VarInit: { - const VarDecl *VD = cast<VarDecl>(Elem.D); - Diag(VD->getLocation(), diag::note_local_var_initializer) - << VD->getType()->isReferenceType() - << VD->isImplicit() << VD->getDeclName() - << nextPathEntryRange(Path, I + 1, L); - break; - } - - case IndirectLocalPathEntry::LambdaCaptureInit: - if (!Elem.Capture->capturesVariable()) - break; - // FIXME: We can't easily tell apart an init-capture from a nested - // capture of an init-capture. - const ValueDecl *VD = Elem.Capture->getCapturedVar(); - Diag(Elem.Capture->getLocation(), diag::note_lambda_capture_initializer) - << VD << VD->isInitCapture() << Elem.Capture->isExplicit() - << (Elem.Capture->getCaptureKind() == LCK_ByRef) << VD - << nextPathEntryRange(Path, I + 1, L); - break; - } - } - - // We didn't lifetime-extend, so don't go any further; we don't need more - // warnings or errors on inner temporaries within this one's initializer. - 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, - EnableLifetimeWarnings); - else - visitLocalsRetainedByInitializer(Path, Init, TemporaryVisitor, false, - EnableLifetimeWarnings); + return sema::checkExprLifetime(*this, Entity, Init); } static void DiagnoseNarrowingInInitList(Sema &S, @@ -8793,7 +7666,7 @@ ExprResult InitializationSequence::Perform(Sema &S, // constant expressions here in order to perform narrowing checks =( EnterExpressionEvaluationContext Evaluated( S, EnterExpressionEvaluationContext::InitList, - CurInit.get() && isa<InitListExpr>(CurInit.get())); + isa_and_nonnull<InitListExpr>(CurInit.get())); // C++ [class.abstract]p2: // no objects of an abstract class can be created except as subobjects @@ -9063,19 +7936,18 @@ ExprResult InitializationSequence::Perform(Sema &S, } } } - + Expr *Init = CurInit.get(); CheckedConversionKind CCK = Kind.isCStyleCast() ? CheckedConversionKind::CStyleCast : Kind.isFunctionalCast() ? CheckedConversionKind::FunctionalCast : Kind.isExplicitCast() ? CheckedConversionKind::OtherCast : CheckedConversionKind::Implicit; - ExprResult CurInitExprRes = - S.PerformImplicitConversion(CurInit.get(), Step->Type, *Step->ICS, - getAssignmentAction(Entity), CCK); + ExprResult CurInitExprRes = S.PerformImplicitConversion( + Init, Step->Type, *Step->ICS, getAssignmentAction(Entity), CCK); if (CurInitExprRes.isInvalid()) return ExprError(); - S.DiscardMisalignedMemberAddress(Step->Type.getTypePtr(), CurInit.get()); + S.DiscardMisalignedMemberAddress(Step->Type.getTypePtr(), Init); CurInit = CurInitExprRes; @@ -9230,10 +8102,11 @@ ExprResult InitializationSequence::Perform(Sema &S, case SK_CAssignment: { QualType SourceType = CurInit.get()->getType(); + Expr *Init = CurInit.get(); // Save off the initial CurInit in case we need to emit a diagnostic - ExprResult InitialCurInit = CurInit; - ExprResult Result = CurInit; + ExprResult InitialCurInit = Init; + ExprResult Result = Init; Sema::AssignConvertType ConvTy = S.CheckSingleAssignmentConstraints(Step->Type, Result, true, Entity.getKind() == InitializedEntity::EK_Parameter_CF_Audited); @@ -9378,6 +8251,57 @@ ExprResult InitializationSequence::Perform(Sema &S, // Wrap it in a construction of a std::initializer_list<T>. CurInit = new (S.Context) CXXStdInitializerListExpr(Step->Type, MTE); + if (!Step->Type->isDependentType()) { + QualType ElementType; + [[maybe_unused]] bool IsStdInitializerList = + S.isStdInitializerList(Step->Type, &ElementType); + assert(IsStdInitializerList && + "StdInitializerList step to non-std::initializer_list"); + const CXXRecordDecl *Record = + Step->Type->getAsCXXRecordDecl()->getDefinition(); + assert(Record && Record->isCompleteDefinition() && + "std::initializer_list should have already be " + "complete/instantiated by this point"); + + auto InvalidType = [&] { + S.Diag(Record->getLocation(), + diag::err_std_initializer_list_malformed) + << Step->Type.getUnqualifiedType(); + return ExprError(); + }; + + if (Record->isUnion() || Record->getNumBases() != 0 || + Record->isPolymorphic()) + return InvalidType(); + + RecordDecl::field_iterator Field = Record->field_begin(); + if (Field == Record->field_end()) + return InvalidType(); + + // Start pointer + if (!Field->getType()->isPointerType() || + !S.Context.hasSameType(Field->getType()->getPointeeType(), + ElementType.withConst())) + return InvalidType(); + + if (++Field == Record->field_end()) + return InvalidType(); + + // Size or end pointer + if (const auto *PT = Field->getType()->getAs<PointerType>()) { + if (!S.Context.hasSameType(PT->getPointeeType(), + ElementType.withConst())) + return InvalidType(); + } else { + if (Field->isBitField() || + !S.Context.hasSameType(Field->getType(), S.Context.getSizeType())) + return InvalidType(); + } + + if (++Field != Record->field_end()) + return InvalidType(); + } + // Bind the result, in case the library has given initializer_list a // non-trivial destructor. if (shouldBindAsTemporary(Entity)) @@ -10892,8 +9816,6 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // FIXME: The "second phase of [over.match.list] case can also // theoretically happen here, but it's not clear whether we can // ever have a parameter of the right type. - bool SuppressUserConversions = Kind.isCopyInit(); - if (TD) { SmallVector<Expr *, 8> TmpInits; for (Expr *E : Inits) @@ -10903,12 +9825,12 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( TmpInits.push_back(E); AddTemplateOverloadCandidate( TD, FoundDecl, /*ExplicitArgs=*/nullptr, TmpInits, Candidates, - SuppressUserConversions, + /*SuppressUserConversions=*/false, /*PartialOverloading=*/false, AllowExplicit, ADLCallKind::NotADL, /*PO=*/{}, AllowAggregateDeductionCandidate); } else { AddOverloadCandidate(GD, FoundDecl, Inits, Candidates, - SuppressUserConversions, + /*SuppressUserConversions=*/false, /*PartialOverloading=*/false, AllowExplicit); } }; @@ -10940,14 +9862,14 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // if e_i is of array type and x_i is a braced-init-list, T_i is an // rvalue reference to the declared type of e_i and // C++ [over.match.class.deduct]p1.9: - // if e_i is of array type and x_i is a bstring-literal, T_i is an + // if e_i is of array type and x_i is a string-literal, T_i is an // lvalue reference to the const-qualified declared type of e_i and // C++ [over.match.class.deduct]p1.10: // otherwise, T_i is the declared type of e_i for (int I = 0, E = ListInit->getNumInits(); I < E && !isa<PackExpansionType>(ElementTypes[I]); ++I) if (ElementTypes[I]->isArrayType()) { - if (isa<InitListExpr>(ListInit->getInit(I))) + if (isa<InitListExpr, DesignatedInitExpr>(ListInit->getInit(I))) ElementTypes[I] = Context.getRValueReferenceType(ElementTypes[I]); else if (isa<StringLiteral>( ListInit->getInit(I)->IgnoreParenImpCasts())) |
