Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -3472,6 +3472,14 @@ def DiagnoseIf : InheritableAttr {
let Documentation = [DiagnoseIfDocs];
}

def LifetimeBoundIf : Attr {
let Spellings = [Clang<"lifetimebound_if">];
let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>;
let Args = [ExprArgument<"Cond">];
// FIXME: add documentation
let Documentation = [InternalOnly];
}

def NoSpecializations : InheritableAttr {
let Spellings = [Clang<"no_specializations", /*AllowInC*/0>];
let Args = [StringArgument<"Message", 1>];
Expand Down
21 changes: 19 additions & 2 deletions clang/lib/Sema/CheckExprLifetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,23 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
return isNormalAssignmentOperator(FD);
}

static bool hasLifetimeBoundAttribute(const Decl *D) {
if (D->hasAttr<LifetimeBoundAttr>())
return true;
if (const auto *AI = D->getAttr<LifetimeBoundIfAttr>()) {
bool Result;
// FIXME: we pay the cost of evaluating the binary condition everytime we
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to cache the evaluation or would it make sense to replace LifetimeBoundIfAttr with LifetimeBoundAttr once it evaluated to true so clients who already check for LifetimeBoundAttr does not need to be updated? Admittedly this would be a departure from what Clang AST is today, that is a really close representation of what the user wrote.

// check the existence of the attribute. Cache the result.
if (AI->getCond()->EvaluateAsBooleanCondition(Result, D->getASTContext())) {
if (Result)
return true;
} else {
// FIXME: emits an error.
}
}
return false;
}

// Visit lifetimebound or gsl-pointer arguments.
static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
LocalVisitor Visit) {
Expand Down Expand Up @@ -659,7 +676,7 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
Arg = DAE->getExpr();
}
if (CheckCoroCall ||
CanonCallee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
hasLifetimeBoundAttribute(CanonCallee->getParamDecl(I)))
VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg);
else if (const auto *CaptureAttr =
CanonCallee->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>();
Expand Down Expand Up @@ -1278,7 +1295,7 @@ static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,
static bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD) {
CMD = getDeclWithMergedLifetimeBoundAttrs(CMD);
return CMD && isNormalAssignmentOperator(CMD) && CMD->param_size() == 1 &&
CMD->getParamDecl(0)->hasAttr<LifetimeBoundAttr>();
hasLifetimeBoundAttribute(CMD->getParamDecl(0));
}

static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef,
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3975,6 +3975,10 @@ static void handleLifetimeCaptureByAttr(Sema &S, Decl *D,
D->addAttr(CaptureByAttr);
}

static void handleLifetimeBoundIfAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(LifetimeBoundIfAttr::Create(S.Context, AL.getArgAsExpr(0)));
}

void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) {
bool HasImplicitThisParam = isInstanceMethod(FD);
SmallVector<LifetimeCaptureByAttr *, 1> Attrs;
Expand Down Expand Up @@ -6823,6 +6827,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_LifetimeCaptureBy:
handleLifetimeCaptureByAttr(S, D, AL);
break;
case ParsedAttr::AT_LifetimeBoundIf:
handleLifetimeBoundIfAttr(S, D, AL);
break;
case ParsedAttr::AT_CalledOnce:
handleCalledOnceAttr(S, D, AL);
break;
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,22 @@ static void instantiateDependentDiagnoseIfAttr(
DIA->getArgDependent(), New));
}

static void instantiateDependentLifetimeBoundIfAttr(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const LifetimeBoundIfAttr *DIA, const Decl *Tmpl, Decl *New) {
const auto PVD = dyn_cast<ParmVarDecl>(New);
if (!PVD)
return;
auto *FD = dyn_cast<FunctionDecl>(PVD->getDeclContext());
if (!FD)
return;
Expr *Cond = instantiateDependentFunctionAttrCondition(
S, TemplateArgs, DIA, DIA->getCond(), Tmpl, FD);
if (Cond)
New->addAttr(new (S.getASTContext())
LifetimeBoundIfAttr(S.getASTContext(), *DIA, Cond));
}

// Constructs and adds to New a new instance of CUDALaunchBoundsAttr using
// template A as the base and arguments from TemplateArgs.
static void instantiateDependentCUDALaunchBoundsAttr(
Expand Down Expand Up @@ -788,6 +804,11 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
cast<FunctionDecl>(New));
continue;
}
if (const auto *LifetimeBoundIf = dyn_cast<LifetimeBoundIfAttr>(TmplAttr)) {
instantiateDependentLifetimeBoundIfAttr(*this, TemplateArgs,
LifetimeBoundIf, Tmpl, (New));
continue;
}

if (const auto *CUDALaunchBounds =
dyn_cast<CUDALaunchBoundsAttr>(TmplAttr)) {
Expand Down