diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-08-01 00:33:25 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-08-01 00:33:25 +0000 |
commit | f4e248c23e05e374910a8becbb91f5d3d7a76a01 (patch) | |
tree | 1a3f3fff462a6670e6dc5a7f3770882462294302 /clang/lib/Sema | |
parent | eb159e56a65d71afd2db913ea389866295a24c00 (diff) | |
download | bcm5719-llvm-f4e248c23e05e374910a8becbb91f5d3d7a76a01.tar.gz bcm5719-llvm-f4e248c23e05e374910a8becbb91f5d3d7a76a01.zip |
[P0936R0] add [[clang::lifetimebound]] attribute
This patch adds support for a new attribute, [[clang::lifetimebound]], that
indicates that the lifetime of a function result is related to one of the
function arguments. When walking an initializer to make sure that the lifetime
of the initial value is at least as long as the lifetime of the initialized
object, we step through parameters (including the implicit object parameter of
a non-static member function) that are marked with this attribute.
There's nowhere to write an attribute on the implicit object parameter, so in
lieu of that, it may be applied to a function type (where it appears
immediately after the cv-qualifiers and ref-qualifier, which is as close to a
declaration of the implicit object parameter as we have). I'm currently
modeling this in the AST as the attribute appertaining to the function type.
Differential Revision: https://reviews.llvm.org/D49922
llvm-svn: 338464
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 21 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclAttr.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaInit.cpp | 231 | ||||
-rw-r--r-- | clang/lib/Sema/SemaType.cpp | 21 |
4 files changed, 202 insertions, 74 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index ccdb4d288e9..50ea98e7f60 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6008,6 +6008,27 @@ static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) { << Attr; ND.dropAttr<NotTailCalledAttr>(); } + + // Check the attributes on the function type, if any. + if (const auto *FD = dyn_cast<FunctionDecl>(&ND)) { + for (TypeLoc TL = FD->getTypeSourceInfo()->getTypeLoc(); + auto ATL = TL.getAsAdjusted<AttributedTypeLoc>(); + TL = ATL.getModifiedLoc()) { + // The [[lifetimebound]] attribute can be applied to the implicit object + // parameter of a non-static member function (other than a ctor or dtor) + // by applying it to the function type. + if (ATL.getAttrKind() == AttributedType::attr_lifetimebound) { + const auto *MD = dyn_cast<CXXMethodDecl>(FD); + if (!MD || MD->isStatic()) { + S.Diag(ATL.getAttrNameLoc(), diag::err_lifetimebound_no_object_param) + << !MD << ATL.getLocalSourceRange(); + } else if (isa<CXXConstructorDecl>(MD) || isa<CXXDestructorDecl>(MD)) { + S.Diag(ATL.getAttrNameLoc(), diag::err_lifetimebound_ctor_dtor) + << isa<CXXDestructorDecl>(MD) << ATL.getLocalSourceRange(); + } + } + } + } } static void checkDLLAttributeRedeclaration(Sema &S, NamedDecl *OldDecl, diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index f6caff6b7d2..77deed6047f 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6150,6 +6150,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_Restrict: handleRestrictAttr(S, D, AL); break; + case ParsedAttr::AT_LifetimeBound: + handleSimpleAttribute<LifetimeBoundAttr>(S, D, AL); + break; case ParsedAttr::AT_MayAlias: handleSimpleAttribute<MayAliasAttr>(S, D, AL); break; diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index cbb9f78e755..5fa8a3c6c44 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6322,12 +6322,14 @@ struct IndirectLocalPathEntry { AddressOf, VarInit, LValToRVal, + LifetimeBoundCall, } Kind; Expr *E; - Decl *D = nullptr; + const Decl *D = nullptr; IndirectLocalPathEntry() {} IndirectLocalPathEntry(EntryKind K, Expr *E) : Kind(K), E(E) {} - IndirectLocalPathEntry(EntryKind K, Expr *E, Decl *D) : Kind(K), E(E), D(D) {} + IndirectLocalPathEntry(EntryKind K, Expr *E, const Decl *D) + : Kind(K), E(E), D(D) {} }; using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>; @@ -6361,6 +6363,68 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, Expr *Init, LocalVisitor Visit, bool RevisitSubinits); +static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, + Expr *Init, ReferenceKind RK, + LocalVisitor Visit); + +static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { + const TypeSourceInfo *TSI = FD->getTypeSourceInfo(); + if (!TSI) + return false; + for (TypeLoc TL = TSI->getTypeLoc(); + auto ATL = TL.getAsAdjusted<AttributedTypeLoc>(); + TL = ATL.getModifiedLoc()) { + if (ATL.getAttrKind() == AttributedType::attr_lifetimebound) + return true; + } + return false; +} + +static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call, + LocalVisitor Visit) { + const FunctionDecl *Callee; + ArrayRef<Expr*> Args; + + if (auto *CE = dyn_cast<CallExpr>(Call)) { + Callee = CE->getDirectCallee(); + Args = llvm::makeArrayRef(CE->getArgs(), CE->getNumArgs()); + } else { + auto *CCE = cast<CXXConstructExpr>(Call); + Callee = CCE->getConstructor(); + Args = llvm::makeArrayRef(CCE->getArgs(), CCE->getNumArgs()); + } + if (!Callee) + return; + + Expr *ObjectArg = nullptr; + if (isa<CXXOperatorCallExpr>(Call) && Callee->isCXXInstanceMember()) { + ObjectArg = Args[0]; + Args = Args.slice(1); + } else if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) { + ObjectArg = MCE->getImplicitObjectArgument(); + } + + auto VisitLifetimeBoundArg = [&](const Decl *D, Expr *Arg) { + Path.push_back({IndirectLocalPathEntry::LifetimeBoundCall, Arg, D}); + if (Arg->isGLValue()) + visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding, + Visit); + else + visitLocalsRetainedByInitializer(Path, Arg, Visit, true); + Path.pop_back(); + }; + + if (ObjectArg && implicitObjectParamIsLifetimeBound(Callee)) + VisitLifetimeBoundArg(Callee, ObjectArg); + + for (unsigned I = 0, + N = std::min<unsigned>(Callee->getNumParams(), Args.size()); + I != N; ++I) { + if (Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>()) + VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]); + } +} + /// Visit the locals that would be reachable through a reference bound to the /// glvalue expression \c Init. static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, @@ -6420,6 +6484,9 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, true); } + if (isa<CallExpr>(Init)) + return visitLifetimeBoundArguments(Path, Init, Visit); + switch (Init->getStmtClass()) { case Stmt::DeclRefExprClass: { // If we find the name of a local non-reference parameter, we could have a @@ -6483,21 +6550,90 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, bool RevisitSubinits) { RevertToOldSizeRAII RAII(Path); - // Step into CXXDefaultInitExprs so we can diagnose cases where a - // constructor inherits one as an implicit mem-initializer. - if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) { - Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); - Init = DIE->getExpr(); - } + Expr *Old; + do { + Old = Init; - if (auto *EWC = dyn_cast<ExprWithCleanups>(Init)) - Init = EWC->getSubExpr(); + // Step into CXXDefaultInitExprs so we can diagnose cases where a + // constructor inherits one as an implicit mem-initializer. + if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) { + Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); + Init = DIE->getExpr(); + } - // Dig out the expression which constructs the extended temporary. - Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments()); + if (auto *EWC = dyn_cast<ExprWithCleanups>(Init)) + Init = EWC->getSubExpr(); + + // Dig out the expression which constructs the extended temporary. + Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments()); + + if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Init)) + Init = BTE->getSubExpr(); + + Init = Init->IgnoreParens(); + + // Step over value-preserving rvalue casts. + if (auto *CE = dyn_cast<CastExpr>(Init)) { + switch (CE->getCastKind()) { + case CK_LValueToRValue: + // If we can match the lvalue to a const object, we can look at its + // initializer. + Path.push_back({IndirectLocalPathEntry::LValToRVal, CE}); + return visitLocalsRetainedByReferenceBinding( + Path, Init, RK_ReferenceBinding, + [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool { + if (auto *DRE = dyn_cast<DeclRefExpr>(L)) { + auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); + if (VD && VD->getType().isConstQualified() && VD->getInit() && + !isVarOnPath(Path, VD)) { + Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD}); + visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, true); + } + } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) { + if (MTE->getType().isConstQualified()) + visitLocalsRetainedByInitializer(Path, MTE->GetTemporaryExpr(), + Visit, true); + } + return false; + }); + + // We assume that objects can be retained by pointers cast to integers, + // but not if the integer is cast to floating-point type or to _Complex. + // We assume that casts to 'bool' do not preserve enough information to + // retain a local object. + case CK_NoOp: + case CK_BitCast: + case CK_BaseToDerived: + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: + case CK_Dynamic: + case CK_ToUnion: + case CK_UserDefinedConversion: + case CK_ConstructorConversion: + case CK_IntegralToPointer: + case CK_PointerToIntegral: + case CK_VectorSplat: + case CK_IntegralCast: + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_AddressSpaceConversion: + break; + + case CK_ArrayToPointerDecay: + // Model array-to-pointer decay as taking the address of the array + // lvalue. + Path.push_back({IndirectLocalPathEntry::AddressOf, CE}); + return visitLocalsRetainedByReferenceBinding(Path, CE->getSubExpr(), + RK_ReferenceBinding, Visit); + + default: + return; + } - if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Init)) - Init = BTE->getSubExpr(); + Init = CE->getSubExpr(); + } + } while (Old != Init); // C++17 [dcl.init.list]p6: // initializing an initializer_list object from the array extends the @@ -6558,67 +6694,9 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, return; } - // Step over value-preserving rvalue casts. - while (auto *CE = dyn_cast<CastExpr>(Init)) { - switch (CE->getCastKind()) { - case CK_LValueToRValue: - // If we can match the lvalue to a const object, we can look at its - // initializer. - Path.push_back({IndirectLocalPathEntry::LValToRVal, CE}); - return visitLocalsRetainedByReferenceBinding( - Path, Init, RK_ReferenceBinding, - [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool { - if (auto *DRE = dyn_cast<DeclRefExpr>(L)) { - auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (VD && VD->getType().isConstQualified() && VD->getInit() && - !isVarOnPath(Path, VD)) { - Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD}); - visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, true); - } - } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) { - if (MTE->getType().isConstQualified()) - visitLocalsRetainedByInitializer(Path, MTE->GetTemporaryExpr(), - Visit, true); - } - return false; - }); - - // We assume that objects can be retained by pointers cast to integers, - // but not if the integer is cast to floating-point type or to _Complex. - // We assume that casts to 'bool' do not preserve enough information to - // retain a local object. - case CK_NoOp: - case CK_BitCast: - case CK_BaseToDerived: - case CK_DerivedToBase: - case CK_UncheckedDerivedToBase: - case CK_Dynamic: - case CK_ToUnion: - case CK_IntegralToPointer: - case CK_PointerToIntegral: - case CK_VectorSplat: - case CK_IntegralCast: - case CK_CPointerToObjCPointerCast: - case CK_BlockPointerToObjCPointerCast: - case CK_AnyPointerToBlockPointerCast: - case CK_AddressSpaceConversion: - break; - - case CK_ArrayToPointerDecay: - // Model array-to-pointer decay as taking the address of the array - // lvalue. - Path.push_back({IndirectLocalPathEntry::AddressOf, CE}); - return visitLocalsRetainedByReferenceBinding(Path, CE->getSubExpr(), - RK_ReferenceBinding, Visit); - - default: - return; - } - - Init = CE->getSubExpr(); - } + if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init)) + return visitLifetimeBoundArguments(Path, Init, Visit); - Init = Init->IgnoreParens(); switch (Init->getStmtClass()) { case Stmt::UnaryOperatorClass: { auto *UO = cast<UnaryOperator>(Init); @@ -6698,6 +6776,7 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I, switch (Path[I].Kind) { case IndirectLocalPathEntry::AddressOf: case IndirectLocalPathEntry::LValToRVal: + case IndirectLocalPathEntry::LifetimeBoundCall: // These exist primarily to mark the path as not permitting or // supporting lifetime extension. break; @@ -6876,6 +6955,10 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, // supporting lifetime extension. break; + case IndirectLocalPathEntry::LifetimeBoundCall: + // FIXME: Consider adding a note for this. + break; + case IndirectLocalPathEntry::DefaultInit: { auto *FD = cast<FieldDecl>(Elem.D); Diag(FD->getLocation(), diag::note_init_with_default_member_initalizer) diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index a374dd8bc23..284d34b22c0 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -5233,6 +5233,8 @@ static ParsedAttr::Kind getAttrListKind(AttributedType::Kind kind) { return ParsedAttr::AT_ObjCKindOf; case AttributedType::attr_ns_returns_retained: return ParsedAttr::AT_NSReturnsRetained; + case AttributedType::attr_lifetimebound: + return ParsedAttr::AT_LifetimeBound; } llvm_unreachable("unexpected attribute kind!"); } @@ -7194,6 +7196,18 @@ static void deduceOpenCLImplicitAddrSpace(TypeProcessingState &State, T = State.getSema().Context.getAddrSpaceQualType(T, ImpAddr); } +static void HandleLifetimeBoundAttr(QualType &CurType, + const ParsedAttr &Attr, + Sema &S, Declarator &D) { + if (D.isDeclarationOfFunction()) { + CurType = S.Context.getAttributedType(AttributedType::attr_lifetimebound, + CurType, CurType); + } else { + Attr.diagnoseAppertainsTo(S, nullptr); + } +} + + static void processTypeAttrs(TypeProcessingState &state, QualType &type, TypeAttrLocation TAL, ParsedAttributesView &attrs) { @@ -7298,6 +7312,13 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, HandleOpenCLAccessAttr(type, attr, state.getSema()); attr.setUsedAsTypeAttr(); break; + case ParsedAttr::AT_LifetimeBound: + if (TAL == TAL_DeclChunk) { + HandleLifetimeBoundAttr(type, attr, state.getSema(), + state.getDeclarator()); + attr.setUsedAsTypeAttr(); + } + break; MS_TYPE_ATTRS_CASELIST: if (!handleMSPointerTypeQualifierAttr(state, attr, type)) |