summaryrefslogtreecommitdiff
path: root/clang/lib/Sema/SemaInit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema/SemaInit.cpp')
-rw-r--r--clang/lib/Sema/SemaInit.cpp1430
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()))