Index: clang/lib/Sema/SemaInit.cpp =================================================================== --- clang/lib/Sema/SemaInit.cpp +++ clang/lib/Sema/SemaInit.cpp @@ -6551,6 +6551,15 @@ }); } +static bool pathOnlyInitializesGslPointer(IndirectLocalPath &Path) { + for (auto It = Path.rbegin(), End = Path.rend(); It != End; ++It) { + if (It->Kind == IndirectLocalPathEntry::VarInit) + continue; + return It->Kind == IndirectLocalPathEntry::GslPointerInit; + } + return false; +} + static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, Expr *Init, LocalVisitor Visit, bool RevisitSubinits); @@ -6619,17 +6628,14 @@ 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(QualType(RD->getTypeForDecl(), 0)) && - !isRecordWithAttr(QualType(RD->getTypeForDecl(), 0))) + if (!FD->isInStdNamespace()) return false; if (FD->getReturnType()->isPointerType() || isRecordWithAttr(FD->getReturnType())) { return llvm::StringSwitch(FD->getName()) .Cases("begin", "rbegin", "cbegin", "crbegin", true) .Cases("end", "rend", "cend", "crend", true) + .Cases("cref", "ref", true) .Case("data", true) .Default(false); } else if (FD->getReturnType()->isReferenceType()) { @@ -6763,7 +6769,10 @@ // Step over any subobject adjustments; we may have a materialized // temporary inside them. - Init = const_cast(Init->skipRValueSubobjectAdjustments()); + // We are not interested in the temporary base objects of gsl::Pointers: + // Temp().ptr; // Here ptr might not dangle. + if (!pathOnlyInitializesGslPointer(Path)) + Init = const_cast(Init->skipRValueSubobjectAdjustments()); // Per current approach for DR1376, look through casts to reference type // when performing lifetime extension. @@ -6882,7 +6891,10 @@ Init = FE->getSubExpr(); // Dig out the expression which constructs the extended temporary. - Init = const_cast(Init->skipRValueSubobjectAdjustments()); + // We are not interested in the temporary base objects of gsl::Pointers: + // Temp().ptr; // Here ptr might not dangle. + if (!pathOnlyInitializesGslPointer(Path)) + Init = const_cast(Init->skipRValueSubobjectAdjustments()); if (CXXBindTemporaryExpr *BTE = dyn_cast(Init)) Init = BTE->getSubExpr(); @@ -7129,15 +7141,6 @@ return E->getSourceRange(); } -static bool pathOnlyInitializesGslPointer(IndirectLocalPath &Path) { - for (auto It = Path.rbegin(), End = Path.rend(); It != End; ++It) { - if (It->Kind == IndirectLocalPathEntry::VarInit) - continue; - return It->Kind == IndirectLocalPathEntry::GslPointerInit; - } - return false; -} - void Sema::checkInitializerLifetime(const InitializedEntity &Entity, Expr *Init) { LifetimeResult LR = getEntityLifetime(&Entity); @@ -7166,7 +7169,9 @@ // someContainer.add(std::move(localUniquePtr)); // return p; IsLocalGslOwner = isRecordWithAttr(L->getType()); - if (pathContainsInit(Path) || !IsLocalGslOwner) + if (pathContainsInit(Path) && IsLocalGslOwner) + return false; + if (isRecordWithAttr(L->getType())) return false; } else { IsGslPtrInitWithGslTempOwner = MTE && !MTE->getExtendingDecl() && Index: clang/test/Sema/warn-lifetime-analysis-nocfg.cpp =================================================================== --- clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -141,6 +141,9 @@ template auto data(const C &c) -> decltype(c.data()); +template +T *begin(T (&array)[N]); + template struct vector { typedef __gnu_cxx::basic_iterator iterator; @@ -188,6 +191,14 @@ template T any_cast(const any& operand); + +template +struct reference_wrapper { + reference_wrapper(T &&); +}; + +template +reference_wrapper ref(T& t) noexcept; } void modelIterators() { @@ -298,3 +309,45 @@ const std::vector &v = getVec(); const int *val = v.data(); // Ok, it is lifetime extended. } + +std::reference_wrapper danglingPtrFromNonOwnerLocal() { + int i = 5; + return i; // expected-warning {{address of stack memory associated with local variable 'i' returned}} +} + +std::reference_wrapper danglingPtrFromNonOwnerLocal2() { + int i = 5; + return std::ref(i); // expected-warning {{address of stack memory associated with local variable 'i' returned}} +} + +int *returnPtrToLocalArray() { + int a[5]; + return std::begin(a); // expected-warning {{address of stack memory associated with local variable 'a' returned}} +} + +struct ptr_wrapper { + std::vector::iterator member; +}; + +ptr_wrapper getPtrWrapper(); + +std::vector::iterator returnPtrFromWrapper() { + ptr_wrapper local = getPtrWrapper(); + return local.member; // OK. +} + +std::vector::iterator returnPtrFromWrapperThroughRef() { + ptr_wrapper local = getPtrWrapper(); + ptr_wrapper &local2 = local; + return local2.member; // OK. +} + +std::vector::iterator returnPtrFromWrapperThroughRef2() { + ptr_wrapper local = getPtrWrapper(); + std::vector::iterator &local2 = local.member; + return local2; // OK. +} + +void checkPtrMemberFromAggregate() { + std::vector::iterator local = getPtrWrapper().member; // OK. +}