Skip to content

Commit b1368f6

Browse files
committed
start working on lifetime capture
1 parent 09284e7 commit b1368f6

11 files changed

+283
-1
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,6 +1869,41 @@ def LifetimeBound : DeclOrTypeAttr {
18691869
let SimpleHandler = 1;
18701870
}
18711871

1872+
def LifetimeCaptureBy : DeclOrTypeAttr {
1873+
let Spellings = [Clang<"lifetime_capture_by", 0>];
1874+
let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>;
1875+
let Args = [VariadicParamOrParamIdxArgument<"Params">];
1876+
let Documentation = [LifetimeBoundDocs];
1877+
let LangOpts = [CPlusPlus];
1878+
1879+
// let SimpleHandler = 1;
1880+
// let LateParsed = LateAttrParseStandard;
1881+
// let HasCustomParsing = 1;
1882+
// let ParseArgumentsAsUnevaluated = 1;
1883+
1884+
let AdditionalMembers = [{
1885+
private:
1886+
SmallVector<IdentifierInfo*, 1> ArgIdents;
1887+
SmallVector<SourceLocation, 1> ArgLocs;
1888+
1889+
public:
1890+
void setArgs(SmallVector<IdentifierInfo*, 1> Idents,
1891+
SmallVector<SourceLocation, 1> Locs) {
1892+
assert(Idents.size() == Locs.size());
1893+
assert(Idents.size() == params_Size);
1894+
ArgIdents = std::move(Idents);
1895+
ArgLocs = std::move(Locs);
1896+
}
1897+
1898+
const SmallVector<IdentifierInfo*, 1>& getArgIdents() const { return ArgIdents; }
1899+
const SmallVector<SourceLocation, 1>& getArgLocs() const { return ArgLocs; }
1900+
void setParamIdx(size_t Idx, int Val) {
1901+
assert(Idx < params_Size);
1902+
params_[Idx] = Val;
1903+
}
1904+
}];
1905+
}
1906+
18721907
def TrivialABI : InheritableAttr {
18731908
// This attribute does not have a C [[]] spelling because it requires the
18741909
// CPlusPlus language option.

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3382,6 +3382,17 @@ def err_callback_callee_is_variadic : Error<
33823382
"'callback' attribute callee may not be variadic">;
33833383
def err_callback_implicit_this_not_available : Error<
33843384
"'callback' argument at position %0 references unavailable implicit 'this'">;
3385+
3386+
def err_capture_by_attribute_multiple : Error<
3387+
"multiple 'lifetime_capture' attributes specified">;
3388+
def err_capture_by_attribute_no_entity : Error<
3389+
"'lifetime_capture_by' attribute specifies no capturing entity">;
3390+
def err_capture_by_implicit_this_not_available : Error<
3391+
"'lifetime_capture_by' argument references unavailable implicit 'this'">;
3392+
def err_capture_by_attribute_argument_unknown : Error<
3393+
"'lifetime_capture_by' attribute argument %0 is not a known function parameter"
3394+
". Must be a function parameter of one of 'this', 'global' or 'unknown'">;
3395+
33853396
def err_init_method_bad_return_type : Error<
33863397
"init methods must return an object pointer type, not %0">;
33873398
def err_attribute_invalid_size : Error<
@@ -10185,6 +10196,10 @@ def warn_dangling_pointer_assignment : Warning<
1018510196
"object backing the pointer %0 "
1018610197
"will be destroyed at the end of the full-expression">,
1018710198
InGroup<DanglingAssignment>;
10199+
def warn_dangling_reference_captured : Warning<
10200+
"object captured by the '%0' "
10201+
"will be destroyed at the end of the full-expression">,
10202+
InGroup<DanglingAssignment>;
1018810203

1018910204
// For non-floating point, expressions of the form x == x or x != x
1019010205
// should result in a warning, since these always evaluate to a constant.

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,8 @@ class Sema final : public SemaBase {
18301830
/// Add [[gsl::Pointer]] attributes for std:: types.
18311831
void inferGslPointerAttribute(TypedefNameDecl *TD);
18321832

1833+
void lazyProcessLifetimeCaptureByParams(FunctionDecl *FD);
1834+
18331835
/// Add _Nullable attributes for std:: types.
18341836
void inferNullableClassAttribute(CXXRecordDecl *CRD);
18351837

@@ -2384,6 +2386,9 @@ class Sema final : public SemaBase {
23842386
bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res);
23852387
bool BuiltinVectorToScalarMath(CallExpr *TheCall);
23862388

2389+
void checkLifetimeCaptureBy(FunctionDecl *FDecl, bool IsMemberFunction,
2390+
const Expr *ThisArg, ArrayRef<const Expr *> Args);
2391+
23872392
/// Handles the checks for format strings, non-POD arguments to vararg
23882393
/// functions, NULL arguments passed to non-NULL parameters, diagnose_if
23892394
/// attributes and AArch64 SME attributes.

clang/lib/AST/TypePrinter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,6 +1966,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
19661966
case attr::SizedBy:
19671967
case attr::SizedByOrNull:
19681968
case attr::LifetimeBound:
1969+
case attr::LifetimeCaptureBy:
19691970
case attr::TypeNonNull:
19701971
case attr::TypeNullable:
19711972
case attr::TypeNullableResult:

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ enum LifetimeKind {
4545
/// because the entity is a pointer and we assign the address of a temporary
4646
/// object to it.
4747
LK_Assignment,
48+
49+
LK_Capture,
4850
};
4951
using LifetimeResult =
5052
llvm::PointerIntPair<const InitializedEntity *, 3, LifetimeKind>;
@@ -189,6 +191,7 @@ struct IndirectLocalPathEntry {
189191
VarInit,
190192
LValToRVal,
191193
LifetimeBoundCall,
194+
LifetimeCapture,
192195
TemporaryCopy,
193196
LambdaCaptureInit,
194197
GslReferenceInit,
@@ -898,6 +901,7 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
898901
case IndirectLocalPathEntry::AddressOf:
899902
case IndirectLocalPathEntry::LValToRVal:
900903
case IndirectLocalPathEntry::LifetimeBoundCall:
904+
case IndirectLocalPathEntry::LifetimeCapture:
901905
case IndirectLocalPathEntry::TemporaryCopy:
902906
case IndirectLocalPathEntry::GslReferenceInit:
903907
case IndirectLocalPathEntry::GslPointerInit:
@@ -928,6 +932,7 @@ static bool pathOnlyHandlesGslPointer(IndirectLocalPath &Path) {
928932
case IndirectLocalPathEntry::VarInit:
929933
case IndirectLocalPathEntry::AddressOf:
930934
case IndirectLocalPathEntry::LifetimeBoundCall:
935+
case IndirectLocalPathEntry::LifetimeCapture:
931936
continue;
932937
case IndirectLocalPathEntry::GslPointerInit:
933938
case IndirectLocalPathEntry::GslReferenceInit:
@@ -961,7 +966,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
961966
const InitializedEntity *ExtendingEntity,
962967
LifetimeKind LK,
963968
const AssignedEntity *AEntity, Expr *Init) {
964-
assert((AEntity && LK == LK_Assignment) ||
969+
assert((AEntity && (LK == LK_Assignment || LK == LK_Capture)) ||
965970
(InitEntity && LK != LK_Assignment));
966971
// If this entity doesn't have an interesting lifetime, don't bother looking
967972
// for temporaries within its initializer.
@@ -1046,6 +1051,17 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
10461051
break;
10471052
}
10481053

1054+
case LK_Capture: {
1055+
if (!MTE)
1056+
return false;
1057+
assert(shouldLifetimeExtendThroughPath(Path) ==
1058+
PathLifetimeKind::NoExtend &&
1059+
"No lifetime extension for in function calls");
1060+
SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured)
1061+
<< AEntity->LHS << DiagRange;
1062+
return false;
1063+
}
1064+
10491065
case LK_Assignment: {
10501066
if (!MTE || pathContainsInit(Path))
10511067
return false;
@@ -1199,6 +1215,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
11991215
break;
12001216

12011217
case IndirectLocalPathEntry::LifetimeBoundCall:
1218+
case IndirectLocalPathEntry::LifetimeCapture:
12021219
case IndirectLocalPathEntry::TemporaryCopy:
12031220
case IndirectLocalPathEntry::GslPointerInit:
12041221
case IndirectLocalPathEntry::GslReferenceInit:
@@ -1245,6 +1262,8 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
12451262
llvm::SmallVector<IndirectLocalPathEntry, 8> Path;
12461263
if (LK == LK_Assignment && shouldRunGSLAssignmentAnalysis(SemaRef, *AEntity))
12471264
Path.push_back({IndirectLocalPathEntry::GslPointerAssignment, Init});
1265+
else if (LK == LK_Capture)
1266+
Path.push_back({IndirectLocalPathEntry::LifetimeCapture, Init});
12481267

12491268
if (Init->isGLValue())
12501269
visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding,
@@ -1281,4 +1300,9 @@ void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity,
12811300
Init);
12821301
}
12831302

1303+
void checkCaptureLifetime(Sema &SemaRef, const AssignedEntity &Entity,
1304+
Expr *Init) {
1305+
checkExprLifetimeImpl(SemaRef, /*InitEntity=*/nullptr,
1306+
/*ExtendingEntity=*/nullptr, LK_Capture, &Entity, Init);
1307+
}
12841308
} // namespace clang::sema

clang/lib/Sema/CheckExprLifetime.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ void checkExprLifetime(Sema &SemaRef, const InitializedEntity &Entity,
3535
/// sufficient for assigning to the entity.
3636
void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity, Expr *Init);
3737

38+
void checkCaptureLifetime(Sema &SemaRef, const AssignedEntity &Entity,
39+
Expr *Init);
40+
3841
} // namespace clang::sema
3942

4043
#endif // LLVM_CLANG_SEMA_CHECK_EXPR_LIFETIME_H

clang/lib/Sema/SemaChecking.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
//
1212
//===----------------------------------------------------------------------===//
1313

14+
#include "CheckExprLifetime.h"
1415
#include "clang/AST/APValue.h"
1516
#include "clang/AST/ASTContext.h"
1617
#include "clang/AST/Attr.h"
1718
#include "clang/AST/AttrIterator.h"
19+
#include "clang/AST/Attrs.inc"
1820
#include "clang/AST/CharUnits.h"
1921
#include "clang/AST/Decl.h"
2022
#include "clang/AST/DeclBase.h"
@@ -3203,6 +3205,28 @@ void Sema::CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl,
32033205
<< ParamName << (FDecl != nullptr) << FDecl;
32043206
}
32053207

3208+
void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction,
3209+
const Expr *ThisArg,
3210+
ArrayRef<const Expr *> Args) {
3211+
auto GetArgAt = [&](int Idx) {
3212+
if (IsMemberFunction && Idx == 0)
3213+
return const_cast<Expr *>(ThisArg);
3214+
return const_cast<Expr *>(Args[Idx - int(IsMemberFunction)]);
3215+
};
3216+
for (unsigned I = 0; I < FD->getNumParams(); ++I) {
3217+
auto *CapturedByAttr =
3218+
FD->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>();
3219+
if (!CapturedByAttr)
3220+
continue;
3221+
for (int CapturingParamIdx : CapturedByAttr->params()) {
3222+
Expr *LHS = GetArgAt(CapturingParamIdx);
3223+
Expr *RHS = GetArgAt(I + IsMemberFunction);
3224+
AssignedEntity AE{LHS};
3225+
checkCaptureLifetime(*this, AE, RHS);
3226+
}
3227+
}
3228+
}
3229+
32063230
void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
32073231
const Expr *ThisArg, ArrayRef<const Expr *> Args,
32083232
bool IsMemberFunction, SourceLocation Loc,
@@ -3244,6 +3268,8 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
32443268
}
32453269
}
32463270

3271+
if (FD)
3272+
checkLifetimeCaptureBy(FD, IsMemberFunction, ThisArg, Args);
32473273
if (FDecl || Proto) {
32483274
CheckNonNullArguments(*this, FDecl, Proto, Args, Loc);
32493275

clang/lib/Sema/SemaDecl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16615,6 +16615,7 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) {
1661516615
}
1661616616

1661716617
inferLifetimeBoundAttribute(FD);
16618+
lazyProcessLifetimeCaptureByParams(FD);
1661816619
AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction(FD);
1661916620

1662016621
// If C++ exceptions are enabled but we are told extern "C" functions cannot

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "clang/AST/ASTContext.h"
1515
#include "clang/AST/ASTMutationListener.h"
1616
#include "clang/AST/CXXInheritance.h"
17+
#include "clang/AST/Decl.h"
1718
#include "clang/AST/DeclCXX.h"
1819
#include "clang/AST/DeclObjC.h"
1920
#include "clang/AST/DeclTemplate.h"
@@ -3834,6 +3835,81 @@ static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
38343835
S.Context, AL, EncodingIndices.data(), EncodingIndices.size()));
38353836
}
38363837

3838+
static void HandleLifetimeCaptureByAttr(Sema &S, Decl *D,
3839+
const ParsedAttr &AL) {
3840+
// Atleast one capture by is required. TODO()
3841+
if (AL.getNumArgs() == 0) {
3842+
S.Diag(AL.getLoc(), diag::err_capture_by_attribute_no_entity)
3843+
<< AL.getRange();
3844+
return;
3845+
}
3846+
3847+
ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(D);
3848+
if (!PVD) {
3849+
llvm::errs() << "Should be attached only to a function parameter;\n";
3850+
return;
3851+
}
3852+
SmallVector<IdentifierInfo *, 1> ParamIdents;
3853+
SmallVector<SourceLocation, 1> ParamLocs;
3854+
for (unsigned I = 0; I < AL.getNumArgs(); ++I) {
3855+
if (AL.isArgIdent(I)) {
3856+
IdentifierLoc *IdLoc = AL.getArgAsIdent(I);
3857+
ParamIdents.push_back(IdLoc->Ident);
3858+
ParamLocs.push_back(IdLoc->Loc);
3859+
} else if (AL.isArgExpr(I)) {
3860+
Expr *E = AL.getArgAsExpr(I);
3861+
S.Diag(E->getExprLoc(), diag::err_capture_by_attribute_argument_unknown)
3862+
<< E << E->getExprLoc();
3863+
}
3864+
}
3865+
// Do not allow multiple attributes.
3866+
if (D->hasAttr<LifetimeCaptureByAttr>()) {
3867+
S.Diag(AL.getLoc(), diag::err_capture_by_attribute_multiple)
3868+
<< AL.getRange();
3869+
return;
3870+
}
3871+
SmallVector<int, 1> FakeParamIndices(ParamIdents.size(), -1);
3872+
LifetimeCaptureByAttr *CapturedBy = ::new (S.Context) LifetimeCaptureByAttr(
3873+
S.Context, AL, FakeParamIndices.data(), FakeParamIndices.size());
3874+
CapturedBy->setArgs(std::move(ParamIdents), std::move(ParamLocs));
3875+
D->addAttr(CapturedBy);
3876+
}
3877+
3878+
void Sema::lazyProcessLifetimeCaptureByParams(FunctionDecl *FD) {
3879+
bool HasImplicitThisParam = isInstanceMethod(FD);
3880+
3881+
llvm::StringMap<std::pair<int, QualType>> NameIdxMapping;
3882+
NameIdxMapping["global"] = {-1, {}};
3883+
NameIdxMapping["unknown"] = {-1, {}};
3884+
int Idx = 0;
3885+
if (HasImplicitThisParam) {
3886+
NameIdxMapping["this"] = {0, dyn_cast<CXXMethodDecl>(FD)->getThisType()};
3887+
Idx++;
3888+
}
3889+
for (const ParmVarDecl *PVD : FD->parameters())
3890+
NameIdxMapping[PVD->getName()] = {Idx++, PVD->getType()};
3891+
for (ParmVarDecl *PVD : FD->parameters()) {
3892+
auto *CapturedBy = PVD->getAttr<LifetimeCaptureByAttr>();
3893+
if (!CapturedBy)
3894+
continue;
3895+
const auto &Entities = CapturedBy->getArgIdents();
3896+
for (size_t I = 0; I < Entities.size(); ++I) {
3897+
StringRef Name = Entities[I]->getName();
3898+
auto It = NameIdxMapping.find(Name);
3899+
if (It == NameIdxMapping.end()) {
3900+
auto Loc = CapturedBy->getArgLocs()[I];
3901+
if (!HasImplicitThisParam && Name == "this")
3902+
Diag(Loc, diag::err_capture_by_implicit_this_not_available) << Loc;
3903+
else
3904+
Diag(Loc, diag::err_capture_by_attribute_argument_unknown)
3905+
<< Entities[I] << Loc;
3906+
continue;
3907+
}
3908+
CapturedBy->setParamIdx(I, It->second.first);
3909+
}
3910+
}
3911+
}
3912+
38373913
static bool isFunctionLike(const Type &T) {
38383914
// Check for explicit function types.
38393915
// '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,
66186694
case ParsedAttr::AT_Callback:
66196695
handleCallbackAttr(S, D, AL);
66206696
break;
6697+
case ParsedAttr::AT_LifetimeCaptureBy:
6698+
HandleLifetimeCaptureByAttr(S, D, AL);
6699+
break;
66216700
case ParsedAttr::AT_CalledOnce:
66226701
handleCalledOnceAttr(S, D, AL);
66236702
break;

clang/lib/Sema/SemaType.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8548,6 +8548,15 @@ static void HandleLifetimeBoundAttr(TypeProcessingState &State,
85488548
}
85498549
}
85508550

8551+
static void HandleLifetimeCaptureByAttr(TypeProcessingState &State,
8552+
QualType &CurType, ParsedAttr &Attr) {
8553+
// if (State.getDeclarator().isDeclarationOfFunction()) {
8554+
// CurType = State.getAttributedType(
8555+
// createSimpleAttr<LifetimeCaptureByAttr>(State.getSema().Context,
8556+
// Attr), CurType, CurType);
8557+
// }
8558+
}
8559+
85518560
static void HandleHLSLParamModifierAttr(TypeProcessingState &State,
85528561
QualType &CurType,
85538562
const ParsedAttr &Attr, Sema &S) {
@@ -8709,6 +8718,10 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
87098718
if (TAL == TAL_DeclChunk)
87108719
HandleLifetimeBoundAttr(state, type, attr);
87118720
break;
8721+
case ParsedAttr::AT_LifetimeCaptureBy:
8722+
if (TAL == TAL_DeclChunk)
8723+
HandleLifetimeCaptureByAttr(state, type, attr);
8724+
break;
87128725

87138726
case ParsedAttr::AT_NoDeref: {
87148727
// FIXME: `noderef` currently doesn't work correctly in [[]] syntax.

0 commit comments

Comments
 (0)