[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 8 01:25:16 PDT 2024
https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/111499
This implements the RFC https://discourse.llvm.org/t/rfc-introduce-clang-lifetime-capture-by-x/81371
>From b1368f676ac5f55741df021c2697d3b46fd2c92d Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Tue, 8 Oct 2024 08:19:56 +0000
Subject: [PATCH] start working on lifetime capture
---
clang/include/clang/Basic/Attr.td | 35 ++++++++
.../clang/Basic/DiagnosticSemaKinds.td | 15 ++++
clang/include/clang/Sema/Sema.h | 5 ++
clang/lib/AST/TypePrinter.cpp | 1 +
clang/lib/Sema/CheckExprLifetime.cpp | 26 +++++-
clang/lib/Sema/CheckExprLifetime.h | 3 +
clang/lib/Sema/SemaChecking.cpp | 26 ++++++
clang/lib/Sema/SemaDecl.cpp | 1 +
clang/lib/Sema/SemaDeclAttr.cpp | 79 ++++++++++++++++++
clang/lib/Sema/SemaType.cpp | 13 +++
clang/test/SemaCXX/attr-lifetimebound.cpp | 80 +++++++++++++++++++
11 files changed, 283 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 35b9716e13ff21..8307d8d19c7fec 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1869,6 +1869,41 @@ def LifetimeBound : DeclOrTypeAttr {
let SimpleHandler = 1;
}
+def LifetimeCaptureBy : DeclOrTypeAttr {
+ let Spellings = [Clang<"lifetime_capture_by", 0>];
+ let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>;
+ let Args = [VariadicParamOrParamIdxArgument<"Params">];
+ let Documentation = [LifetimeBoundDocs];
+ let LangOpts = [CPlusPlus];
+
+ // let SimpleHandler = 1;
+ // let LateParsed = LateAttrParseStandard;
+ // let HasCustomParsing = 1;
+ // let ParseArgumentsAsUnevaluated = 1;
+
+ let AdditionalMembers = [{
+private:
+ SmallVector<IdentifierInfo*, 1> ArgIdents;
+ SmallVector<SourceLocation, 1> ArgLocs;
+
+public:
+ void setArgs(SmallVector<IdentifierInfo*, 1> Idents,
+ SmallVector<SourceLocation, 1> Locs) {
+ assert(Idents.size() == Locs.size());
+ assert(Idents.size() == params_Size);
+ ArgIdents = std::move(Idents);
+ ArgLocs = std::move(Locs);
+ }
+
+ const SmallVector<IdentifierInfo*, 1>& getArgIdents() const { return ArgIdents; }
+ const SmallVector<SourceLocation, 1>& getArgLocs() const { return ArgLocs; }
+ void setParamIdx(size_t Idx, int Val) {
+ assert(Idx < params_Size);
+ params_[Idx] = Val;
+ }
+}];
+}
+
def TrivialABI : InheritableAttr {
// This attribute does not have a C [[]] spelling because it requires the
// CPlusPlus language option.
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e8b64f3c5a0187..94b2b9252ff834 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3382,6 +3382,17 @@ def err_callback_callee_is_variadic : Error<
"'callback' attribute callee may not be variadic">;
def err_callback_implicit_this_not_available : Error<
"'callback' argument at position %0 references unavailable implicit 'this'">;
+
+def err_capture_by_attribute_multiple : Error<
+ "multiple 'lifetime_capture' attributes specified">;
+def err_capture_by_attribute_no_entity : Error<
+ "'lifetime_capture_by' attribute specifies no capturing entity">;
+def err_capture_by_implicit_this_not_available : Error<
+ "'lifetime_capture_by' argument references unavailable implicit 'this'">;
+def err_capture_by_attribute_argument_unknown : Error<
+ "'lifetime_capture_by' attribute argument %0 is not a known function parameter"
+ ". Must be a function parameter of one of 'this', 'global' or 'unknown'">;
+
def err_init_method_bad_return_type : Error<
"init methods must return an object pointer type, not %0">;
def err_attribute_invalid_size : Error<
@@ -10185,6 +10196,10 @@ def warn_dangling_pointer_assignment : Warning<
"object backing the pointer %0 "
"will be destroyed at the end of the full-expression">,
InGroup<DanglingAssignment>;
+def warn_dangling_reference_captured : Warning<
+ "object captured by the '%0' "
+ "will be destroyed at the end of the full-expression">,
+ InGroup<DanglingAssignment>;
// For non-floating point, expressions of the form x == x or x != x
// should result in a warning, since these always evaluate to a constant.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 0809ac1b144ef6..26b648868d7eb4 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1830,6 +1830,8 @@ class Sema final : public SemaBase {
/// Add [[gsl::Pointer]] attributes for std:: types.
void inferGslPointerAttribute(TypedefNameDecl *TD);
+ void lazyProcessLifetimeCaptureByParams(FunctionDecl *FD);
+
/// Add _Nullable attributes for std:: types.
void inferNullableClassAttribute(CXXRecordDecl *CRD);
@@ -2384,6 +2386,9 @@ class Sema final : public SemaBase {
bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res);
bool BuiltinVectorToScalarMath(CallExpr *TheCall);
+ void checkLifetimeCaptureBy(FunctionDecl *FDecl, bool IsMemberFunction,
+ const Expr *ThisArg, ArrayRef<const Expr *> Args);
+
/// Handles the checks for format strings, non-POD arguments to vararg
/// functions, NULL arguments passed to non-NULL parameters, diagnose_if
/// attributes and AArch64 SME attributes.
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index ca75bb97c158e1..d7f447ebf0f9ed 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1966,6 +1966,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::SizedBy:
case attr::SizedByOrNull:
case attr::LifetimeBound:
+ case attr::LifetimeCaptureBy:
case attr::TypeNonNull:
case attr::TypeNullable:
case attr::TypeNullableResult:
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index c98fbca849faba..55129c0c04ebb5 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -45,6 +45,8 @@ enum LifetimeKind {
/// because the entity is a pointer and we assign the address of a temporary
/// object to it.
LK_Assignment,
+
+ LK_Capture,
};
using LifetimeResult =
llvm::PointerIntPair<const InitializedEntity *, 3, LifetimeKind>;
@@ -189,6 +191,7 @@ struct IndirectLocalPathEntry {
VarInit,
LValToRVal,
LifetimeBoundCall,
+ LifetimeCapture,
TemporaryCopy,
LambdaCaptureInit,
GslReferenceInit,
@@ -898,6 +901,7 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
case IndirectLocalPathEntry::AddressOf:
case IndirectLocalPathEntry::LValToRVal:
case IndirectLocalPathEntry::LifetimeBoundCall:
+ case IndirectLocalPathEntry::LifetimeCapture:
case IndirectLocalPathEntry::TemporaryCopy:
case IndirectLocalPathEntry::GslReferenceInit:
case IndirectLocalPathEntry::GslPointerInit:
@@ -928,6 +932,7 @@ static bool pathOnlyHandlesGslPointer(IndirectLocalPath &Path) {
case IndirectLocalPathEntry::VarInit:
case IndirectLocalPathEntry::AddressOf:
case IndirectLocalPathEntry::LifetimeBoundCall:
+ case IndirectLocalPathEntry::LifetimeCapture:
continue;
case IndirectLocalPathEntry::GslPointerInit:
case IndirectLocalPathEntry::GslReferenceInit:
@@ -961,7 +966,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
const InitializedEntity *ExtendingEntity,
LifetimeKind LK,
const AssignedEntity *AEntity, Expr *Init) {
- assert((AEntity && LK == LK_Assignment) ||
+ assert((AEntity && (LK == LK_Assignment || LK == LK_Capture)) ||
(InitEntity && LK != LK_Assignment));
// If this entity doesn't have an interesting lifetime, don't bother looking
// for temporaries within its initializer.
@@ -1046,6 +1051,17 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
break;
}
+ case LK_Capture: {
+ if (!MTE)
+ return false;
+ assert(shouldLifetimeExtendThroughPath(Path) ==
+ PathLifetimeKind::NoExtend &&
+ "No lifetime extension for in function calls");
+ SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured)
+ << AEntity->LHS << DiagRange;
+ return false;
+ }
+
case LK_Assignment: {
if (!MTE || pathContainsInit(Path))
return false;
@@ -1199,6 +1215,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
break;
case IndirectLocalPathEntry::LifetimeBoundCall:
+ case IndirectLocalPathEntry::LifetimeCapture:
case IndirectLocalPathEntry::TemporaryCopy:
case IndirectLocalPathEntry::GslPointerInit:
case IndirectLocalPathEntry::GslReferenceInit:
@@ -1245,6 +1262,8 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
llvm::SmallVector<IndirectLocalPathEntry, 8> Path;
if (LK == LK_Assignment && shouldRunGSLAssignmentAnalysis(SemaRef, *AEntity))
Path.push_back({IndirectLocalPathEntry::GslPointerAssignment, Init});
+ else if (LK == LK_Capture)
+ Path.push_back({IndirectLocalPathEntry::LifetimeCapture, Init});
if (Init->isGLValue())
visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding,
@@ -1281,4 +1300,9 @@ void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity,
Init);
}
+void checkCaptureLifetime(Sema &SemaRef, const AssignedEntity &Entity,
+ Expr *Init) {
+ checkExprLifetimeImpl(SemaRef, /*InitEntity=*/nullptr,
+ /*ExtendingEntity=*/nullptr, LK_Capture, &Entity, Init);
+}
} // namespace clang::sema
diff --git a/clang/lib/Sema/CheckExprLifetime.h b/clang/lib/Sema/CheckExprLifetime.h
index 8c8d0806dee0a3..174d86da86ddff 100644
--- a/clang/lib/Sema/CheckExprLifetime.h
+++ b/clang/lib/Sema/CheckExprLifetime.h
@@ -35,6 +35,9 @@ void checkExprLifetime(Sema &SemaRef, const InitializedEntity &Entity,
/// sufficient for assigning to the entity.
void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity, Expr *Init);
+void checkCaptureLifetime(Sema &SemaRef, const AssignedEntity &Entity,
+ Expr *Init);
+
} // namespace clang::sema
#endif // LLVM_CLANG_SEMA_CHECK_EXPR_LIFETIME_H
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 99500daca295c9..3388ff5fb7310c 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -11,10 +11,12 @@
//
//===----------------------------------------------------------------------===//
+#include "CheckExprLifetime.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/AttrIterator.h"
+#include "clang/AST/Attrs.inc"
#include "clang/AST/CharUnits.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
@@ -3203,6 +3205,28 @@ void Sema::CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl,
<< ParamName << (FDecl != nullptr) << FDecl;
}
+void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction,
+ const Expr *ThisArg,
+ ArrayRef<const Expr *> Args) {
+ auto GetArgAt = [&](int Idx) {
+ if (IsMemberFunction && Idx == 0)
+ return const_cast<Expr *>(ThisArg);
+ return const_cast<Expr *>(Args[Idx - int(IsMemberFunction)]);
+ };
+ for (unsigned I = 0; I < FD->getNumParams(); ++I) {
+ auto *CapturedByAttr =
+ FD->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>();
+ if (!CapturedByAttr)
+ continue;
+ for (int CapturingParamIdx : CapturedByAttr->params()) {
+ Expr *LHS = GetArgAt(CapturingParamIdx);
+ Expr *RHS = GetArgAt(I + IsMemberFunction);
+ AssignedEntity AE{LHS};
+ checkCaptureLifetime(*this, AE, RHS);
+ }
+ }
+}
+
void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
const Expr *ThisArg, ArrayRef<const Expr *> Args,
bool IsMemberFunction, SourceLocation Loc,
@@ -3244,6 +3268,8 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
}
}
+ if (FD)
+ checkLifetimeCaptureBy(FD, IsMemberFunction, ThisArg, Args);
if (FDecl || Proto) {
CheckNonNullArguments(*this, FDecl, Proto, Args, Loc);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 8557c25b93a8da..4e49cca7f09265 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -16615,6 +16615,7 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) {
}
inferLifetimeBoundAttribute(FD);
+ lazyProcessLifetimeCaptureByParams(FD);
AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction(FD);
// If C++ exceptions are enabled but we are told extern "C" functions cannot
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 14cc51cf89665a..67b27c6bbd86d6 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -14,6 +14,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
@@ -3834,6 +3835,81 @@ static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
S.Context, AL, EncodingIndices.data(), EncodingIndices.size()));
}
+static void HandleLifetimeCaptureByAttr(Sema &S, Decl *D,
+ const ParsedAttr &AL) {
+ // Atleast one capture by is required. TODO()
+ if (AL.getNumArgs() == 0) {
+ S.Diag(AL.getLoc(), diag::err_capture_by_attribute_no_entity)
+ << AL.getRange();
+ return;
+ }
+
+ ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(D);
+ if (!PVD) {
+ llvm::errs() << "Should be attached only to a function parameter;\n";
+ return;
+ }
+ SmallVector<IdentifierInfo *, 1> ParamIdents;
+ SmallVector<SourceLocation, 1> ParamLocs;
+ for (unsigned I = 0; I < AL.getNumArgs(); ++I) {
+ if (AL.isArgIdent(I)) {
+ IdentifierLoc *IdLoc = AL.getArgAsIdent(I);
+ ParamIdents.push_back(IdLoc->Ident);
+ ParamLocs.push_back(IdLoc->Loc);
+ } else if (AL.isArgExpr(I)) {
+ Expr *E = AL.getArgAsExpr(I);
+ S.Diag(E->getExprLoc(), diag::err_capture_by_attribute_argument_unknown)
+ << E << E->getExprLoc();
+ }
+ }
+ // Do not allow multiple attributes.
+ if (D->hasAttr<LifetimeCaptureByAttr>()) {
+ S.Diag(AL.getLoc(), diag::err_capture_by_attribute_multiple)
+ << AL.getRange();
+ return;
+ }
+ SmallVector<int, 1> FakeParamIndices(ParamIdents.size(), -1);
+ LifetimeCaptureByAttr *CapturedBy = ::new (S.Context) LifetimeCaptureByAttr(
+ S.Context, AL, FakeParamIndices.data(), FakeParamIndices.size());
+ CapturedBy->setArgs(std::move(ParamIdents), std::move(ParamLocs));
+ D->addAttr(CapturedBy);
+}
+
+void Sema::lazyProcessLifetimeCaptureByParams(FunctionDecl *FD) {
+ bool HasImplicitThisParam = isInstanceMethod(FD);
+
+ llvm::StringMap<std::pair<int, QualType>> NameIdxMapping;
+ NameIdxMapping["global"] = {-1, {}};
+ NameIdxMapping["unknown"] = {-1, {}};
+ int Idx = 0;
+ if (HasImplicitThisParam) {
+ NameIdxMapping["this"] = {0, dyn_cast<CXXMethodDecl>(FD)->getThisType()};
+ Idx++;
+ }
+ for (const ParmVarDecl *PVD : FD->parameters())
+ NameIdxMapping[PVD->getName()] = {Idx++, PVD->getType()};
+ for (ParmVarDecl *PVD : FD->parameters()) {
+ auto *CapturedBy = PVD->getAttr<LifetimeCaptureByAttr>();
+ if (!CapturedBy)
+ continue;
+ const auto &Entities = CapturedBy->getArgIdents();
+ for (size_t I = 0; I < Entities.size(); ++I) {
+ StringRef Name = Entities[I]->getName();
+ auto It = NameIdxMapping.find(Name);
+ if (It == NameIdxMapping.end()) {
+ auto Loc = CapturedBy->getArgLocs()[I];
+ if (!HasImplicitThisParam && Name == "this")
+ Diag(Loc, diag::err_capture_by_implicit_this_not_available) << Loc;
+ else
+ Diag(Loc, diag::err_capture_by_attribute_argument_unknown)
+ << Entities[I] << Loc;
+ continue;
+ }
+ CapturedBy->setParamIdx(I, It->second.first);
+ }
+ }
+}
+
static bool isFunctionLike(const Type &T) {
// Check for explicit function types.
// 'called_once' is only supported in Objective-C and it has
@@ -6618,6 +6694,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_Callback:
handleCallbackAttr(S, D, AL);
break;
+ case ParsedAttr::AT_LifetimeCaptureBy:
+ HandleLifetimeCaptureByAttr(S, D, AL);
+ break;
case ParsedAttr::AT_CalledOnce:
handleCalledOnceAttr(S, D, AL);
break;
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 950bd6db0359d1..8305b24b469558 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8548,6 +8548,15 @@ static void HandleLifetimeBoundAttr(TypeProcessingState &State,
}
}
+static void HandleLifetimeCaptureByAttr(TypeProcessingState &State,
+ QualType &CurType, ParsedAttr &Attr) {
+ // if (State.getDeclarator().isDeclarationOfFunction()) {
+ // CurType = State.getAttributedType(
+ // createSimpleAttr<LifetimeCaptureByAttr>(State.getSema().Context,
+ // Attr), CurType, CurType);
+ // }
+}
+
static void HandleHLSLParamModifierAttr(TypeProcessingState &State,
QualType &CurType,
const ParsedAttr &Attr, Sema &S) {
@@ -8709,6 +8718,10 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
if (TAL == TAL_DeclChunk)
HandleLifetimeBoundAttr(state, type, attr);
break;
+ case ParsedAttr::AT_LifetimeCaptureBy:
+ if (TAL == TAL_DeclChunk)
+ HandleLifetimeCaptureByAttr(state, type, attr);
+ break;
case ParsedAttr::AT_NoDeref: {
// FIXME: `noderef` currently doesn't work correctly in [[]] syntax.
diff --git a/clang/test/SemaCXX/attr-lifetimebound.cpp b/clang/test/SemaCXX/attr-lifetimebound.cpp
index 0fb997a5671085..5de4f47ac176dd 100644
--- a/clang/test/SemaCXX/attr-lifetimebound.cpp
+++ b/clang/test/SemaCXX/attr-lifetimebound.cpp
@@ -307,3 +307,83 @@ void test(StatusOr<FooView> foo1, StatusOr<NonAnnotatedFooView> foo2) {
foo2 = NonAnnotatedFoo();
}
} // namespace GH106372
+
+namespace lifetime_capture_by {
+
+struct S {
+ const int *x;
+ void setX(const int *x) { this->x = x; }
+};
+
+///////////////////////////
+// Test for valid usages.
+///////////////////////////
+[[clang::lifetime_capture_by(unknown)]] // expected-error {{'lifetime_capture_by' attribute only applies to parameters and implicit object parameters}}
+void nonMember(
+ const int &x1 [[clang::lifetime_capture_by(s, t)]],
+ S &s,
+ S &t,
+ const int &x2 [[clang::lifetime_capture_by(12345 + 12)]], // expected-error {{'lifetime_capture_by' attribute argument 12345 + 12 is not a known function parameter. Must be a function parameter of one of 'this', 'global' or 'unknown'}}
+ const int &x3 [[clang::lifetime_capture_by(abcdefgh)]], // expected-error {{'lifetime_capture_by' attribute argument 'abcdefgh' is not a known function parameter. Must be a function parameter of one of 'this', 'global' or 'unknown'}}
+ const int &x4 [[clang::lifetime_capture_by("abcdefgh")]], // expected-error {{'lifetime_capture_by' attribute argument "abcdefgh" is not a known function parameter. Must be a function parameter of one of 'this', 'global' or 'unknown'}}
+ const int &x5 [[clang::lifetime_capture_by(this)]], // expected-error {{'lifetime_capture_by' argument references unavailable implicit 'this'}}
+ const int &x6 [[clang::lifetime_capture_by()]], // expected-error {{'lifetime_capture_by' attribute specifies no capturing entity}}
+
+ const int& x7 [[clang::lifetime_capture_by(u)]],
+ const S& u
+ )
+{
+ s.setX(&x1);
+}
+
+struct T {
+ void member(
+ const int &x [[clang::lifetime_capture_by(s)]],
+ S &s,
+ S &t,
+ const int &y [[clang::lifetime_capture_by(s)]],
+ const int &z [[clang::lifetime_capture_by(this, x, y)]],
+ const int &u [[clang::lifetime_capture_by(global, x, s)]])
+ {
+ s.setX(&x);
+ }
+};
+
+///////////////////////////
+// Detect dangling warning.
+///////////////////////////
+void captureInt(const int&x [[clang::lifetime_capture_by(s)]], S&s);
+void noCaptureInt(int x [[clang::lifetime_capture_by(s)]], S&s);
+
+std::string_view substr(const std::string& s [[clang::lifetimebound]]);
+std::string_view strcopy(const std::string& s);
+
+void captureSV(std::string_view x [[clang::lifetime_capture_by(s)]], S&s);
+void noCaptureSV(std::string_view x, S&s);
+
+void use() {
+ S s;
+ int local;
+ captureInt(1, // expected-warning {{object captured by the 's' will be destroyed at the end of the full-expression}}
+ s);
+ captureInt(local, s);
+
+ noCaptureInt(1, s);
+ noCaptureInt(local, s);
+
+ std::string_view local_sv;
+ captureSV(local_sv, s);
+ captureSV(std::string(), // expected-warning {{object captured by the 's'}}
+ s);
+ captureSV(substr(
+ std::string() // expected-warning {{object captured by the 's'}}
+ ), s);
+ captureSV(strcopy(std::string()), s);
+
+ noCaptureSV(local_sv, s);
+ noCaptureSV(std::string(), s);
+ noCaptureSV(substr(std::string()), s);
+}
+} // namespace lifetime_capture_by_usage
+
+// Test for templated code.
\ No newline at end of file
More information about the cfe-commits
mailing list