diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Sema/SemaInit.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Sema/SemaInit.cpp | 1813 |
1 files changed, 460 insertions, 1353 deletions
diff --git a/contrib/llvm-project/clang/lib/Sema/SemaInit.cpp b/contrib/llvm-project/clang/lib/Sema/SemaInit.cpp index 457fa377355a..eea4bdfa68b5 100644 --- a/contrib/llvm-project/clang/lib/Sema/SemaInit.cpp +++ b/contrib/llvm-project/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" @@ -28,9 +29,11 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/Ownership.h" #include "clang/Sema/SemaInternal.h" +#include "clang/Sema/SemaObjC.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -190,12 +193,34 @@ static void updateGNUCompoundLiteralRValue(Expr *E) { } } +static bool initializingConstexprVariable(const InitializedEntity &Entity) { + Decl *D = Entity.getDecl(); + const InitializedEntity *Parent = &Entity; + + while (Parent) { + D = Parent->getDecl(); + Parent = Parent->getParent(); + } + + if (const auto *VD = dyn_cast_if_present<VarDecl>(D); VD && VD->isConstexpr()) + return true; + + return false; +} + +static void CheckC23ConstexprInitStringLiteral(const StringLiteral *SE, + Sema &SemaRef, QualType &TT); + static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT, - Sema &S) { + Sema &S, bool CheckC23ConstexprInit = false) { // Get the length of the string as parsed. auto *ConstantArrayTy = cast<ConstantArrayType>(Str->getType()->getAsArrayTypeUnsafe()); - uint64_t StrLength = ConstantArrayTy->getSize().getZExtValue(); + uint64_t StrLength = ConstantArrayTy->getZExtSize(); + + if (CheckC23ConstexprInit) + if (const StringLiteral *SL = dyn_cast<StringLiteral>(Str->IgnoreParens())) + CheckC23ConstexprInitStringLiteral(SL, S, DeclT); if (const IncompleteArrayType *IAT = dyn_cast<IncompleteArrayType>(AT)) { // C99 6.7.8p14. We have an array of character type with unknown size @@ -224,14 +249,13 @@ static void CheckStringInit(Expr *Str, QualType &DeclT, const ArrayType *AT, } // [dcl.init.string]p2 - if (StrLength > CAT->getSize().getZExtValue()) + if (StrLength > CAT->getZExtSize()) S.Diag(Str->getBeginLoc(), diag::err_initializer_string_for_char_array_too_long) - << CAT->getSize().getZExtValue() << StrLength - << Str->getSourceRange(); + << CAT->getZExtSize() << StrLength << Str->getSourceRange(); } else { // C99 6.7.8p14. - if (StrLength-1 > CAT->getSize().getZExtValue()) + if (StrLength - 1 > CAT->getZExtSize()) S.Diag(Str->getBeginLoc(), diag::ext_initializer_string_for_char_array_too_long) << Str->getSourceRange(); @@ -290,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) @@ -478,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) { + auto *AType = + SemaRef.Context.getAsArrayType(Entity.getParent()->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, @@ -490,7 +552,7 @@ public: : InitListChecker(S, Entity, IL, T, /*VerifyOnly=*/true, /*TreatUnavailableAsInvalid=*/false, /*InOverloadResolution=*/false, - &AggrDeductionCandidateParamTypes){}; + &AggrDeductionCandidateParamTypes) {} bool HadError() { return hadError; } @@ -791,19 +853,13 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, if (const RecordType *RType = ILE->getType()->getAs<RecordType>()) { const RecordDecl *RDecl = RType->getDecl(); - if (RDecl->isUnion() && ILE->getInitializedFieldInUnion()) - FillInEmptyInitForField(0, ILE->getInitializedFieldInUnion(), - Entity, ILE, RequiresSecondPass, FillWithNoInit); - else if (RDecl->isUnion() && isa<CXXRecordDecl>(RDecl) && - cast<CXXRecordDecl>(RDecl)->hasInClassInitializer()) { - for (auto *Field : RDecl->fields()) { - if (Field->hasInClassInitializer()) { - FillInEmptyInitForField(0, Field, Entity, ILE, RequiresSecondPass, - FillWithNoInit); - break; - } - } + if (RDecl->isUnion() && ILE->getInitializedFieldInUnion()) { + FillInEmptyInitForField(0, ILE->getInitializedFieldInUnion(), Entity, ILE, + RequiresSecondPass, FillWithNoInit); } else { + assert((!RDecl->isUnion() || !isa<CXXRecordDecl>(RDecl) || + !cast<CXXRecordDecl>(RDecl)->hasInClassInitializer()) && + "We should have computed initialized fields already"); // The fields beyond ILE->getNumInits() are default initialized, so in // order to leave them uninitialized, the ILE is expanded and the extra // fields are then filled with NoInitExpr. @@ -827,7 +883,7 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, } for (auto *Field : RDecl->fields()) { - if (Field->isUnnamedBitfield()) + if (Field->isUnnamedBitField()) continue; if (hadError) @@ -853,11 +909,11 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, InitializedEntity ElementEntity = Entity; unsigned NumInits = ILE->getNumInits(); - unsigned NumElements = NumInits; + uint64_t NumElements = NumInits; if (const ArrayType *AType = SemaRef.Context.getAsArrayType(ILE->getType())) { ElementType = AType->getElementType(); if (const auto *CAType = dyn_cast<ConstantArrayType>(AType)) - NumElements = CAType->getSize().getZExtValue(); + NumElements = CAType->getZExtSize(); // For an array new with an unknown bound, ask for one additional element // in order to populate the array filler. if (Entity.isVariableLengthArrayNew()) @@ -873,7 +929,7 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, ElementType = ILE->getType(); bool SkipEmptyInitChecks = false; - for (unsigned Init = 0; Init != NumElements; ++Init) { + for (uint64_t Init = 0; Init != NumElements; ++Init) { if (hadError) return; @@ -994,7 +1050,7 @@ int InitListChecker::numArrayElements(QualType DeclType) { int maxElements = 0x7FFFFFFF; if (const ConstantArrayType *CAT = SemaRef.Context.getAsConstantArrayType(DeclType)) { - maxElements = static_cast<int>(CAT->getSize().getZExtValue()); + maxElements = static_cast<int>(CAT->getZExtSize()); } return maxElements; } @@ -1005,7 +1061,7 @@ int InitListChecker::numStructUnionElements(QualType DeclType) { if (auto *CXXRD = dyn_cast<CXXRecordDecl>(structDecl)) InitializableMembers += CXXRD->getNumBases(); for (const auto *Field : structDecl->fields()) - if (!Field->isUnnamedBitfield()) + if (!Field->isUnnamedBitField()) ++InitializableMembers; if (structDecl->isUnion()) @@ -1426,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); @@ -1442,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()) @@ -1455,7 +1528,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, UpdateStructuredListElement(StructuredList, StructuredIndex, getDummyInit()); } - ++Index; + if (!CurEmbed) + ++Index; if (AggrDeductionCandidateParamTypes) AggrDeductionCandidateParamTypes->push_back(ElemType); return; @@ -1476,7 +1550,9 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, if (IsStringInit(expr, arrayType, SemaRef.Context) == SIF_None) { // FIXME: Should we do this checking in verify-only mode? if (!VerifyOnly) - CheckStringInit(expr, ElemType, arrayType, SemaRef); + CheckStringInit(expr, ElemType, arrayType, SemaRef, + SemaRef.getLangOpts().C23 && + initializingConstexprVariable(Entity)); if (StructuredList) UpdateStructuredListElement(StructuredList, StructuredIndex, expr); ++Index; @@ -1646,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; @@ -1667,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); } @@ -1913,6 +1993,39 @@ static bool checkDestructorReference(QualType ElementType, SourceLocation Loc, return SemaRef.DiagnoseUseOfDecl(Destructor, Loc); } +static bool +canInitializeArrayWithEmbedDataString(ArrayRef<Expr *> ExprList, + const InitializedEntity &Entity, + ASTContext &Context) { + QualType InitType = Entity.getType(); + const InitializedEntity *Parent = &Entity; + + while (Parent) { + InitType = Parent->getType(); + Parent = Parent->getParent(); + } + + // 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->getDataStringLiteral()->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, @@ -1930,6 +2043,12 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity, } } + if (canInitializeArrayWithEmbedDataString(IList->inits(), Entity, + 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) == @@ -1941,7 +2060,9 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity, // constant for each string. // FIXME: Should we do these checks in verify-only mode too? if (!VerifyOnly) - CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef); + CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef, + SemaRef.getLangOpts().C23 && + initializingConstexprVariable(Entity)); if (StructuredList) { UpdateStructuredListElement(StructuredList, StructuredIndex, IList->getInit(Index)); @@ -2030,13 +2151,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. @@ -2137,19 +2269,22 @@ void InitListChecker::CheckStructUnionTypes( return; for (RecordDecl::field_iterator FieldEnd = RD->field_end(); Field != FieldEnd; ++Field) { - if (Field->hasInClassInitializer()) { + if (Field->hasInClassInitializer() || + (Field->isAnonymousStructOrUnion() && + Field->getType()->getAsCXXRecordDecl()->hasInClassInitializer())) { StructuredList->setInitializedFieldInUnion(*Field); // FIXME: Actually build a CXXDefaultInitExpr? return; } } + llvm_unreachable("Couldn't find in-class initializer"); } // Value-initialize the first member of the union that isn't an unnamed // bitfield. for (RecordDecl::field_iterator FieldEnd = RD->field_end(); Field != FieldEnd; ++Field) { - if (!Field->isUnnamedBitfield()) { + if (!Field->isUnnamedBitField()) { CheckEmptyInitializable( InitializedEntity::InitializeMember(*Field, &Entity), IList->getEndLoc()); @@ -2170,7 +2305,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: @@ -2227,8 +2362,6 @@ void InitListChecker::CheckStructUnionTypes( size_t NumRecordDecls = llvm::count_if(RD->decls(), [&](const Decl *D) { return isa<FieldDecl>(D) || isa<RecordDecl>(D); }); - bool CheckForMissingFields = - !IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()); bool HasDesignatedInit = false; llvm::SmallPtrSet<FieldDecl *, 4> InitializedFields; @@ -2269,11 +2402,6 @@ void InitListChecker::CheckStructUnionTypes( } InitializedSomething = true; - - // Disable check for missing fields when designators are used. - // This matches gcc behaviour. - if (!SemaRef.getLangOpts().CPlusPlus) - CheckForMissingFields = false; continue; } @@ -2285,7 +2413,7 @@ void InitListChecker::CheckStructUnionTypes( // These are okay for randomized structures. [C99 6.7.8p19] // // Also, if there is only one element in the structure, we allow something - // like this, because it's really not randomized in the tranditional sense. + // like this, because it's really not randomized in the traditional sense. // // struct foo h = {bar}; auto IsZeroInitializer = [&](const Expr *I) { @@ -2311,15 +2439,15 @@ void InitListChecker::CheckStructUnionTypes( break; } - // We've already initialized a member of a union. We're done. + // We've already initialized a member of a union. We can stop entirely. if (InitializedSomething && RD->isUnion()) - break; + return; - // If we've hit the flexible array member at the end, we're done. + // Stop if we've hit a flexible array member. if (Field->getType()->isIncompleteArrayType()) break; - if (Field->isUnnamedBitfield()) { + if (Field->isUnnamedBitField()) { // Don't initialize unnamed bitfields, e.g. "int : 20;" ++Field; continue; @@ -2363,8 +2491,13 @@ void InitListChecker::CheckStructUnionTypes( } // Emit warnings for missing struct field initializers. - if (!VerifyOnly && InitializedSomething && CheckForMissingFields && - !RD->isUnion()) { + // This check is disabled for designated initializers in C. + // This matches gcc behaviour. + bool IsCDesignatedInitializer = + HasDesignatedInit && !SemaRef.getLangOpts().CPlusPlus; + if (!VerifyOnly && InitializedSomething && !RD->isUnion() && + !IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()) && + !IsCDesignatedInitializer) { // It is possible we have one or more unnamed bitfields remaining. // Find first (if any) named field and emit warning. for (RecordDecl::field_iterator it = HasDesignatedInit ? RD->field_begin() @@ -2374,11 +2507,12 @@ void InitListChecker::CheckStructUnionTypes( if (HasDesignatedInit && InitializedFields.count(*it)) continue; - if (!it->isUnnamedBitfield() && !it->hasInClassInitializer() && + if (!it->isUnnamedBitField() && !it->hasInClassInitializer() && !it->getType()->isIncompleteArrayType()) { - SemaRef.Diag(IList->getSourceRange().getEnd(), - diag::warn_missing_field_initializers) - << *it; + auto Diag = HasDesignatedInit + ? diag::warn_missing_designated_field_initializers + : diag::warn_missing_field_initializers; + SemaRef.Diag(IList->getSourceRange().getEnd(), Diag) << *it; break; } } @@ -2389,7 +2523,7 @@ void InitListChecker::CheckStructUnionTypes( if (!StructuredList && Field != FieldEnd && !RD->isUnion() && !Field->getType()->isIncompleteArrayType()) { for (; Field != FieldEnd && !hadError; ++Field) { - if (!Field->isUnnamedBitfield() && !Field->hasInClassInitializer()) + if (!Field->isUnnamedBitField() && !Field->hasInClassInitializer()) CheckEmptyInitializable( InitializedEntity::InitializeMember(*Field, &Entity), IList->getEndLoc()); @@ -2432,6 +2566,11 @@ void InitListChecker::CheckStructUnionTypes( else CheckImplicitInitList(MemberEntity, IList, Field->getType(), Index, StructuredList, StructuredIndex); + + if (RD->isUnion() && StructuredList) { + // Initialize the first field within the union. + StructuredList->setInitializedFieldInUnion(*Field); + } } /// Expand a field designator that refers to a member of an @@ -2754,7 +2893,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, unsigned FieldIndex = NumBases; for (auto *FI : RD->fields()) { - if (FI->isUnnamedBitfield()) + if (FI->isUnnamedBitField()) continue; if (declaresSameEntity(KnownField, FI)) { KnownField = FI; @@ -2828,7 +2967,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, // Find the field that we just initialized. FieldDecl *PrevField = nullptr; for (auto FI = RD->field_begin(); FI != RD->field_end(); ++FI) { - if (FI->isUnnamedBitfield()) + if (FI->isUnnamedBitField()) continue; if (*NextField != RD->field_end() && declaresSameEntity(*FI, **NextField)) @@ -2946,7 +3085,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, // If this the first designator, our caller will continue checking // the rest of this struct/class/union subobject. if (IsFirstDesignator) { - if (Field != RD->field_end() && Field->isUnnamedBitfield()) + if (Field != RD->field_end() && Field->isUnnamedBitField()) ++Field; if (NextField) @@ -3076,7 +3215,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, // Get the length of the string. uint64_t StrLen = SL->getLength(); if (cast<ConstantArrayType>(AT)->getSize().ult(StrLen)) - StrLen = cast<ConstantArrayType>(AT)->getSize().getZExtValue(); + StrLen = cast<ConstantArrayType>(AT)->getZExtSize(); StructuredList->resizeInits(Context, StrLen); // Build a literal for each character in the string, and put them into @@ -3099,7 +3238,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, // Get the length of the string. uint64_t StrLen = Str.size(); if (cast<ConstantArrayType>(AT)->getSize().ult(StrLen)) - StrLen = cast<ConstantArrayType>(AT)->getSize().getZExtValue(); + StrLen = cast<ConstantArrayType>(AT)->getZExtSize(); StructuredList->resizeInits(Context, StrLen); // Build a literal for each character in the string, and put them into @@ -3258,7 +3397,7 @@ InitListChecker::createInitListExpr(QualType CurrentObjectType, if (const ArrayType *AType = SemaRef.Context.getAsArrayType(CurrentObjectType)) { if (const ConstantArrayType *CAType = dyn_cast<ConstantArrayType>(AType)) { - NumElements = CAType->getSize().getZExtValue(); + NumElements = CAType->getZExtSize(); // Simple heuristic so that we don't allocate a very large // initializer with many empty entries at the end. if (NumElements > ExpectedNumInits) @@ -3299,8 +3438,6 @@ void InitListChecker::UpdateStructuredListElement(InitListExpr *StructuredList, ++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(); @@ -5448,6 +5585,10 @@ static void TryOrBuildParenListInitialization( ExprResult ER; ER = IS.Perform(S, SubEntity, SubKind, Arg ? MultiExprArg(Arg) : std::nullopt); + + if (ER.isInvalid()) + return false; + if (InitExpr) *InitExpr = ER.get(); else @@ -5467,7 +5608,7 @@ static void TryOrBuildParenListInitialization( // having k elements. if (const ConstantArrayType *CAT = S.getASTContext().getAsConstantArrayType(Entity.getType())) { - ArrayLength = CAT->getSize().getZExtValue(); + ArrayLength = CAT->getZExtSize(); ResultType = Entity.getType(); } else if (const VariableArrayType *VAT = S.getASTContext().getAsVariableArrayType(Entity.getType())) { @@ -5480,7 +5621,7 @@ static void TryOrBuildParenListInitialization( << SE->getSourceRange(); return; } else { - assert(isa<IncompleteArrayType>(Entity.getType())); + assert(Entity.getType()->isIncompleteArrayType()); ArrayLength = Args.size(); } EntityIndexToProcess = ArrayLength; @@ -5555,7 +5696,7 @@ static void TryOrBuildParenListInitialization( for (FieldDecl *FD : RD->fields()) { // Unnamed bitfields should not be initialized at all, either with an arg // or by default. - if (FD->isUnnamedBitfield()) + if (FD->isUnnamedBitField()) continue; InitializedEntity SubEntity = @@ -5988,8 +6129,8 @@ static bool tryObjCWritebackConversion(Sema &S, // Handle write-back conversion. QualType ConvertedArgType; - if (!S.isObjCWritebackConversion(ArgType, Entity.getType(), - ConvertedArgType)) + if (!S.ObjC().isObjCWritebackConversion(ArgType, Entity.getType(), + ConvertedArgType)) return false; // We should copy unless we're passing to an argument explicitly @@ -6181,10 +6322,10 @@ void InitializationSequence::InitializeFrom(Sema &S, if (Args.size() == 1) { Initializer = Args[0]; if (S.getLangOpts().ObjC) { - if (S.CheckObjCBridgeRelatedConversions(Initializer->getBeginLoc(), - DestType, Initializer->getType(), - Initializer) || - S.CheckConversionToObjCLiteral(DestType, Initializer)) + if (S.ObjC().CheckObjCBridgeRelatedConversions( + Initializer->getBeginLoc(), DestType, Initializer->getType(), + Initializer) || + S.ObjC().CheckConversionToObjCLiteral(DestType, Initializer)) Args[0] = Initializer; } if (!isa<InitListExpr>(Initializer)) @@ -6240,7 +6381,10 @@ void InitializationSequence::InitializeFrom(Sema &S, // initializer is a string literal, see 8.5.2. // - Otherwise, if the destination type is an array, the program is // ill-formed. - if (const ArrayType *DestAT = Context.getAsArrayType(DestType)) { + // - Except in HLSL, where non-decaying array parameters behave like + // non-array types for initialization. + if (DestType->isArrayType() && !DestType->isArrayParameterType()) { + const ArrayType *DestAT = Context.getAsArrayType(DestType); if (Initializer && isa<VariableArrayType>(DestAT)) { SetFailed(FK_VariableLengthArrayHasInitializer); return; @@ -6319,7 +6463,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); @@ -6432,7 +6576,7 @@ void InitializationSequence::InitializeFrom(Sema &S, // For HLSL ext vector types we allow list initialization behavior for C++ // constructor syntax. This is accomplished by converting initialization // arguments an InitListExpr late. - if (S.getLangOpts().HLSL && DestType->isExtVectorType() && + if (S.getLangOpts().HLSL && Args.size() > 1 && DestType->isExtVectorType() && (SourceType.isNull() || !Context.hasSameUnqualifiedType(SourceType, DestType))) { @@ -6543,12 +6687,12 @@ void InitializationSequence::InitializeFrom(Sema &S, AddPassByIndirectCopyRestoreStep(DestType, ShouldCopy); } else if (ICS.isBad()) { - DeclAccessPair dap; - if (isLibstdcxxPointerReturnFalseHack(S, Entity, Initializer)) { + if (isLibstdcxxPointerReturnFalseHack(S, Entity, Initializer)) AddZeroInitializationStep(Entity.getType()); - } else if (Initializer->getType() == Context.OverloadTy && - !S.ResolveAddressOfOverloadedFunction(Initializer, DestType, - false, dap)) + else if (DeclAccessPair Found; + Initializer->getType() == Context.OverloadTy && + !S.ResolveAddressOfOverloadedFunction(Initializer, DestType, + /*Complain=*/false, Found)) SetFailed(InitializationSequence::FK_AddressOfOverloadFailed); else if (Initializer->getType()->isFunctionType() && isExprAnUnaddressableFunction(S, Initializer)) @@ -7050,6 +7194,11 @@ PerformConstructorInitialization(Sema &S, hasCopyOrMoveCtorParam(S.Context, getConstructorInfo(Step.Function.FoundDecl)); + // A smart pointer constructed from a nullable pointer is nullable. + if (NumArgs == 1 && !Kind.isExplicitCast()) + S.diagnoseNullableToNonnullConversion( + Entity.getType(), Args.front()->getType(), Kind.getLocation()); + // Determine the arguments required to actually perform the constructor // call. if (S.CompleteConstructorCall(Constructor, Step.Type, Args, Loc, @@ -7147,1217 +7296,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::OMPArraySectionExprClass: { - visitLocalsRetainedByInitializer(Path, - cast<OMPArraySectionExpr>(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; - } - - // 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 { - 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, @@ -8366,6 +7307,9 @@ static void DiagnoseNarrowingInInitList(Sema &S, QualType EntityType, const Expr *PostInit); +static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType, + QualType ToType, Expr *Init); + /// Provide warnings when std::move is used on construction. static void CheckMoveOnConstruction(Sema &S, const Expr *InitExpr, bool IsReturnStmt) { @@ -8491,6 +7435,10 @@ Sema::CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary, // are done in both CreateMaterializeTemporaryExpr and MaybeBindToTemporary, // but there may be a chance to merge them. Cleanup.setExprNeedsCleanups(false); + if (isInLifetimeExtendingContext()) { + auto &Record = ExprEvalContexts.back(); + Record.ForRangeLifetimeExtendTemps.push_back(MTE); + } return MTE; } @@ -8729,7 +7677,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 @@ -8999,19 +7947,18 @@ ExprResult InitializationSequence::Perform(Sema &S, } } } - - Sema::CheckedConversionKind CCK - = Kind.isCStyleCast()? Sema::CCK_CStyleCast - : Kind.isFunctionalCast()? Sema::CCK_FunctionalCast - : Kind.isExplicitCast()? Sema::CCK_OtherCast - : Sema::CCK_ImplicitConversion; - ExprResult CurInitExprRes = - S.PerformImplicitConversion(CurInit.get(), Step->Type, *Step->ICS, - getAssignmentAction(Entity), CCK); + Expr *Init = CurInit.get(); + CheckedConversionKind CCK = + Kind.isCStyleCast() ? CheckedConversionKind::CStyleCast + : Kind.isFunctionalCast() ? CheckedConversionKind::FunctionalCast + : Kind.isExplicitCast() ? CheckedConversionKind::OtherCast + : CheckedConversionKind::Implicit; + 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; @@ -9166,10 +8113,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); @@ -9188,6 +8136,23 @@ ExprResult InitializationSequence::Perform(Sema &S, return ExprError(); CurInit = CurInitExprRes; + if (S.getLangOpts().C23 && initializingConstexprVariable(Entity)) { + CheckC23ConstexprInitConversion(S, SourceType, Entity.getType(), + CurInit.get()); + + // C23 6.7.1p6: If an object or subobject declared with storage-class + // specifier constexpr has pointer, integer, or arithmetic type, any + // explicit initializer value for it shall be null, an integer + // constant expression, or an arithmetic constant expression, + // respectively. + Expr::EvalResult ER; + if (Entity.getType()->getAs<PointerType>() && + CurInit.get()->EvaluateAsRValue(ER, S.Context) && + !ER.Val.isNullPointer()) { + S.Diag(Kind.getLocation(), diag::err_c23_constexpr_pointer_not_null); + } + } + bool Complained; if (S.DiagnoseAssignmentResult(ConvTy, Kind.getLocation(), Step->Type, SourceType, @@ -9205,7 +8170,9 @@ ExprResult InitializationSequence::Perform(Sema &S, QualType Ty = Step->Type; bool UpdateType = ResultType && Entity.getType()->isIncompleteArrayType(); CheckStringInit(CurInit.get(), UpdateType ? *ResultType : Ty, - S.Context.getAsArrayType(Ty), S); + S.Context.getAsArrayType(Ty), S, + S.getLangOpts().C23 && + initializingConstexprVariable(Entity)); break; } @@ -9295,6 +8262,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)) @@ -9456,7 +8474,7 @@ static bool DiagnoseUninitializedReference(Sema &S, SourceLocation Loc, return false; for (const auto *FI : RD->fields()) { - if (FI->isUnnamedBitfield()) + if (FI->isUnnamedBitField()) continue; if (DiagnoseUninitializedReference(S, FI->getLocation(), FI->getType())) { @@ -9490,12 +8508,12 @@ static void emitBadConversionNotes(Sema &S, const InitializedEntity &entity, // Emit a possible note about the conversion failing because the // operand is a message send with a related result type. - S.EmitRelatedResultTypeNote(op); + S.ObjC().EmitRelatedResultTypeNote(op); // Emit a possible note about a return failing because we're // expecting a related result type. if (entity.getKind() == InitializedEntity::EK_Result) - S.EmitRelatedResultTypeNoteForReturn(destType); + S.ObjC().EmitRelatedResultTypeNoteForReturn(destType); } QualType fromType = op->getType(); QualType fromPointeeType = fromType.getCanonicalType()->getPointeeType(); @@ -9556,6 +8574,8 @@ bool InitializationSequence::Diagnose(Sema &S, if (!Failed()) return false; + QualType DestType = Entity.getType(); + // When we want to diagnose only one element of a braced-init-list, // we need to factor it out. Expr *OnlyArg; @@ -9565,11 +8585,21 @@ bool InitializationSequence::Diagnose(Sema &S, OnlyArg = List->getInit(0); else OnlyArg = Args[0]; + + if (OnlyArg->getType() == S.Context.OverloadTy) { + DeclAccessPair Found; + if (FunctionDecl *FD = S.ResolveAddressOfOverloadedFunction( + OnlyArg, DestType.getNonReferenceType(), /*Complain=*/false, + Found)) { + if (Expr *Resolved = + S.FixOverloadedFunctionReference(OnlyArg, Found, FD).get()) + OnlyArg = Resolved; + } + } } else OnlyArg = nullptr; - QualType DestType = Entity.getType(); switch (Failure) { case FK_TooManyInitsForReference: // FIXME: Customize for the initialized entity? @@ -9687,12 +8717,15 @@ bool InitializationSequence::Diagnose(Sema &S, break; } case OR_Deleted: { - S.Diag(Kind.getLocation(), diag::err_typecheck_deleted_function) - << OnlyArg->getType() << DestType.getNonReferenceType() - << Args[0]->getSourceRange(); OverloadCandidateSet::iterator Best; OverloadingResult Ovl = FailedCandidateSet.BestViableFunction(S, Kind.getLocation(), Best); + + StringLiteral *Msg = Best->Function->getDeletedMessage(); + S.Diag(Kind.getLocation(), diag::err_typecheck_deleted_function) + << OnlyArg->getType() << DestType.getNonReferenceType() + << (Msg != nullptr) << (Msg ? Msg->getString() : StringRef()) + << Args[0]->getSourceRange(); if (Ovl == OR_Deleted) { S.NoteDeletedFunction(Best->Function); } else { @@ -9948,11 +8981,15 @@ bool InitializationSequence::Diagnose(Sema &S, // implicit. if (S.isImplicitlyDeleted(Best->Function)) S.Diag(Kind.getLocation(), diag::err_ovl_deleted_special_init) - << S.getSpecialMember(cast<CXXMethodDecl>(Best->Function)) - << DestType << ArgsRange; - else - S.Diag(Kind.getLocation(), diag::err_ovl_deleted_init) + << llvm::to_underlying( + S.getSpecialMember(cast<CXXMethodDecl>(Best->Function))) << DestType << ArgsRange; + else { + StringLiteral *Msg = Best->Function->getDeletedMessage(); + S.Diag(Kind.getLocation(), diag::err_ovl_deleted_init) + << DestType << (Msg != nullptr) + << (Msg ? Msg->getString() : StringRef()) << ArgsRange; + } S.NoteDeletedFunction(Best->Function); break; @@ -10494,6 +9531,69 @@ static void DiagnoseNarrowingInInitList(Sema &S, S.getLocForEndOfToken(PostInit->getEndLoc()), ")"); } +static void CheckC23ConstexprInitConversion(Sema &S, QualType FromType, + QualType ToType, Expr *Init) { + assert(S.getLangOpts().C23); + ImplicitConversionSequence ICS = S.TryImplicitConversion( + Init->IgnoreParenImpCasts(), ToType, /*SuppressUserConversions*/ false, + Sema::AllowedExplicit::None, + /*InOverloadResolution*/ false, + /*CStyle*/ false, + /*AllowObjCWritebackConversion=*/false); + + if (!ICS.isStandard()) + return; + + APValue Value; + QualType PreNarrowingType; + // Reuse C++ narrowing check. + switch (ICS.Standard.getNarrowingKind( + S.Context, Init, Value, PreNarrowingType, + /*IgnoreFloatToIntegralConversion*/ false)) { + // The value doesn't fit. + case NK_Constant_Narrowing: + S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_not_representable) + << Value.getAsString(S.Context, PreNarrowingType) << ToType; + return; + + // Conversion to a narrower type. + case NK_Type_Narrowing: + S.Diag(Init->getBeginLoc(), diag::err_c23_constexpr_init_type_mismatch) + << ToType << FromType; + return; + + // Since we only reuse narrowing check for C23 constexpr variables here, we're + // not really interested in these cases. + case NK_Dependent_Narrowing: + case NK_Variable_Narrowing: + case NK_Not_Narrowing: + return; + } + llvm_unreachable("unhandled case in switch"); +} + +static void CheckC23ConstexprInitStringLiteral(const StringLiteral *SE, + Sema &SemaRef, QualType &TT) { + assert(SemaRef.getLangOpts().C23); + // character that string literal contains fits into TT - target type. + const ArrayType *AT = SemaRef.Context.getAsArrayType(TT); + QualType CharType = AT->getElementType(); + uint32_t BitWidth = SemaRef.Context.getTypeSize(CharType); + bool isUnsigned = CharType->isUnsignedIntegerType(); + llvm::APSInt Value(BitWidth, isUnsigned); + for (unsigned I = 0, N = SE->getLength(); I != N; ++I) { + int64_t C = SE->getCodeUnitS(I, SemaRef.Context.getCharWidth()); + Value = C; + if (Value != C) { + SemaRef.Diag(SemaRef.getLocationOfStringLiteralByte(SE, I), + diag::err_c23_constexpr_init_not_representable) + << C << CharType; + return; + } + } + return; +} + //===----------------------------------------------------------------------===// // Initialization helper functions //===----------------------------------------------------------------------===// @@ -10594,13 +9694,40 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( if (TemplateName.isDependent()) return SubstAutoTypeDependent(TSInfo->getType()); - // We can only perform deduction for class templates. + // We can only perform deduction for class templates or alias templates. auto *Template = dyn_cast_or_null<ClassTemplateDecl>(TemplateName.getAsTemplateDecl()); + TemplateDecl *LookupTemplateDecl = Template; + if (!Template) { + if (auto *AliasTemplate = dyn_cast_or_null<TypeAliasTemplateDecl>( + TemplateName.getAsTemplateDecl())) { + Diag(Kind.getLocation(), + diag::warn_cxx17_compat_ctad_for_alias_templates); + LookupTemplateDecl = AliasTemplate; + auto UnderlyingType = AliasTemplate->getTemplatedDecl() + ->getUnderlyingType() + .getCanonicalType(); + // C++ [over.match.class.deduct#3]: ..., the defining-type-id of A must be + // of the form + // [typename] [nested-name-specifier] [template] simple-template-id + if (const auto *TST = + UnderlyingType->getAs<TemplateSpecializationType>()) { + Template = dyn_cast_or_null<ClassTemplateDecl>( + TST->getTemplateName().getAsTemplateDecl()); + } else if (const auto *RT = UnderlyingType->getAs<RecordType>()) { + // Cases where template arguments in the RHS of the alias are not + // dependent. e.g. + // using AliasFoo = Foo<bool>; + if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>( + RT->getAsCXXRecordDecl())) + Template = CTSD->getSpecializedTemplate(); + } + } + } if (!Template) { Diag(Kind.getLocation(), - diag::err_deduced_non_class_template_specialization_type) - << (int)getTemplateNameKindForDiagnostics(TemplateName) << TemplateName; + diag::err_deduced_non_class_or_alias_template_specialization_type) + << (int)getTemplateNameKindForDiagnostics(TemplateName) << TemplateName; if (auto *TD = TemplateName.getAsTemplateDecl()) NoteTemplateLocation(*TD); return QualType(); @@ -10617,8 +9744,6 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // FIXME: Perform "exact type" matching first, per CWG discussion? // Or implement this via an implied 'T(T) -> T' deduction guide? - // FIXME: Do we need/want a std::initializer_list<T> special case? - // Look up deduction guides, including those synthesized from constructors. // // C++1z [over.match.class.deduct]p1: @@ -10627,10 +9752,10 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // template-name, a function template [...] // - For each deduction-guide, a function or function template [...] DeclarationNameInfo NameInfo( - Context.DeclarationNames.getCXXDeductionGuideName(Template), + Context.DeclarationNames.getCXXDeductionGuideName(LookupTemplateDecl), TSInfo->getTypeLoc().getEndLoc()); LookupResult Guides(*this, NameInfo, LookupOrdinaryName); - LookupQualifiedName(Guides, Template->getDeclContext()); + LookupQualifiedName(Guides, LookupTemplateDecl->getDeclContext()); // FIXME: Do not diagnose inaccessible deduction guides. The standard isn't // clear on this, but they're not found by name so access does not apply. @@ -10699,11 +9824,6 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( // C++ [over.best.ics]p4: // When [...] the constructor [...] is a candidate by // - [over.match.copy] (in all cases) - // 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) @@ -10713,12 +9833,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); } }; @@ -10750,14 +9870,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())) @@ -10765,32 +9885,16 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( Context.getLValueReferenceType(ElementTypes[I].withConst()); } - llvm::FoldingSetNodeID ID; - ID.AddPointer(Template); - for (auto &T : ElementTypes) - T.getCanonicalType().Profile(ID); - unsigned Hash = ID.ComputeHash(); - if (AggregateDeductionCandidates.count(Hash) == 0) { - if (FunctionTemplateDecl *TD = - DeclareImplicitDeductionGuideFromInitList( - Template, ElementTypes, - TSInfo->getTypeLoc().getEndLoc())) { - auto *GD = cast<CXXDeductionGuideDecl>(TD->getTemplatedDecl()); - GD->setDeductionCandidateKind(DeductionCandidate::Aggregate); - AggregateDeductionCandidates[Hash] = GD; - addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public), - OnlyListConstructors, - /*AllowAggregateDeductionCandidate=*/true); - } - } else { - CXXDeductionGuideDecl *GD = AggregateDeductionCandidates[Hash]; - FunctionTemplateDecl *TD = GD->getDescribedFunctionTemplate(); - assert(TD && "aggregate deduction candidate is function template"); + if (FunctionTemplateDecl *TD = + DeclareAggregateDeductionGuideFromInitList( + LookupTemplateDecl, ElementTypes, + TSInfo->getTypeLoc().getEndLoc())) { + auto *GD = cast<CXXDeductionGuideDecl>(TD->getTemplatedDecl()); addDeductionCandidate(TD, GD, DeclAccessPair::make(TD, AS_public), OnlyListConstructors, /*AllowAggregateDeductionCandidate=*/true); + HasAnyDeductionGuide = true; } - HasAnyDeductionGuide = true; } }; @@ -10910,6 +10014,9 @@ QualType Sema::DeduceTemplateSpecializationFromInitializer( } case OR_Deleted: { + // FIXME: There are no tests for this diagnostic, and it doesn't seem + // like we ever get here; attempts to trigger this seem to yield a + // generic c'all to deleted function' diagnostic instead. Diag(Kind.getLocation(), diag::err_deduced_class_template_deleted) << TemplateName; NoteDeletedFunction(Best->Function); |