diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 2a3a29bd2ee1c..eb2a89487144c 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -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>]; diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index 8963cad86dbca..fa9c850865a22 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -557,6 +557,23 @@ bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { return isNormalAssignmentOperator(FD); } +static bool hasLifetimeBoundAttribute(const Decl *D) { + if (D->hasAttr()) + return true; + if (const auto *AI = D->getAttr()) { + bool Result; + // FIXME: we pay the cost of evaluating the binary condition everytime we + // 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) { @@ -659,7 +676,7 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call, Arg = DAE->getExpr(); } if (CheckCoroCall || - CanonCallee->getParamDecl(I)->hasAttr()) + hasLifetimeBoundAttribute(CanonCallee->getParamDecl(I))) VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg); else if (const auto *CaptureAttr = CanonCallee->getParamDecl(I)->getAttr(); @@ -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(); + hasLifetimeBoundAttribute(CMD->getParamDecl(0)); } static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef, diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9d7d22590bce4..b290e729c87c3 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -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 Attrs; @@ -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; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 4855e8a23689c..82b0cafac146f 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -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(New); + if (!PVD) + return; + auto *FD = dyn_cast(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( @@ -788,6 +804,11 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, cast(New)); continue; } + if (const auto *LifetimeBoundIf = dyn_cast(TmplAttr)) { + instantiateDependentLifetimeBoundIfAttr(*this, TemplateArgs, + LifetimeBoundIf, Tmpl, (New)); + continue; + } if (const auto *CUDALaunchBounds = dyn_cast(TmplAttr)) {