Skip to content

Commit 5a5b81d

Browse files
committed
Add support for parsing @Lifetime attribute to specify lifetime dependencies on declarations
1 parent 9ed4d36 commit 5a5b81d

File tree

12 files changed

+200
-8
lines changed

12 files changed

+200
-8
lines changed

include/swift/AST/Attr.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/DeclNameLoc.h"
2525
#include "swift/AST/Identifier.h"
2626
#include "swift/AST/KnownProtocols.h"
27+
#include "swift/AST/LifetimeDependence.h"
2728
#include "swift/AST/MacroDeclaration.h"
2829
#include "swift/AST/Ownership.h"
2930
#include "swift/AST/PlatformKind.h"
@@ -2633,6 +2634,27 @@ class RawLayoutAttr final : public DeclAttribute {
26332634
}
26342635
};
26352636

2637+
class LifetimeAttr final
2638+
: public DeclAttribute,
2639+
private llvm::TrailingObjects<LifetimeAttr, LifetimeDependenceSpecifier> {
2640+
2641+
friend TrailingObjects;
2642+
2643+
unsigned NumEntries = 0;
2644+
2645+
explicit LifetimeAttr(SourceLoc atLoc, SourceRange baseRange, bool implicit,
2646+
ArrayRef<LifetimeDependenceSpecifier> entries);
2647+
2648+
public:
2649+
static LifetimeAttr *create(ASTContext &context, SourceLoc atLoc,
2650+
SourceRange baseRange, bool implicit,
2651+
ArrayRef<LifetimeDependenceSpecifier> entries);
2652+
2653+
static bool classof(const DeclAttribute *DA) {
2654+
return DA->getKind() == DeclAttrKind::Lifetime;
2655+
}
2656+
};
2657+
26362658
/// Predicate used to filter MatchingAttributeRange.
26372659
template <typename ATTR, bool AllowInvalid> struct ToAttributeKind {
26382660
ToAttributeKind() {}

include/swift/AST/DeclAttr.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ SIMPLE_DECL_ATTR(unsafe, Unsafe,
506506
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
507507
160)
508508

509+
DECL_ATTR(lifetime, Lifetime,
510+
OnAccessor | OnConstructor | OnFunc | OnSubscript | LongAttribute | ABIBreakingToAdd | ABIStableToRemove | APIBreakingToAdd | APIStableToRemove,
511+
161)
512+
509513
LAST_DECL_ATTR(Unsafe)
510514

511515
#undef DECL_ATTR_ALIAS

include/swift/AST/LifetimeDependence.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ class LifetimeDependenceSpecifier {
7474

7575
public:
7676
static LifetimeDependenceSpecifier getNamedLifetimeDependenceSpecifier(
77-
SourceLoc loc, ParsedLifetimeDependenceKind kind, Identifier name) {
77+
SourceLoc loc, Identifier name,
78+
ParsedLifetimeDependenceKind kind =
79+
ParsedLifetimeDependenceKind::Default) {
7880
return {loc, SpecifierKind::Named, kind, name};
7981
}
8082

@@ -84,13 +86,15 @@ class LifetimeDependenceSpecifier {
8486
}
8587

8688
static LifetimeDependenceSpecifier getOrderedLifetimeDependenceSpecifier(
87-
SourceLoc loc, ParsedLifetimeDependenceKind kind, unsigned index) {
89+
SourceLoc loc, unsigned index,
90+
ParsedLifetimeDependenceKind kind =
91+
ParsedLifetimeDependenceKind::Default) {
8892
return {loc, SpecifierKind::Ordered, kind, index};
8993
}
9094

91-
static LifetimeDependenceSpecifier
92-
getSelfLifetimeDependenceSpecifier(SourceLoc loc,
93-
ParsedLifetimeDependenceKind kind) {
95+
static LifetimeDependenceSpecifier getSelfLifetimeDependenceSpecifier(
96+
SourceLoc loc, ParsedLifetimeDependenceKind kind =
97+
ParsedLifetimeDependenceKind::Default) {
9498
return {loc, SpecifierKind::Self, kind, {}};
9599
}
96100

include/swift/Parse/Parser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,10 @@ class Parser {
11781178
MacroSyntax syntax, SourceLoc AtLoc, SourceLoc Loc
11791179
);
11801180

1181+
/// Parse the @lifetime attribute.
1182+
ParserResult<LifetimeAttr> parseLifetimeAttribute(SourceLoc AtLoc,
1183+
SourceLoc Loc);
1184+
11811185
/// Parse a specific attribute.
11821186
ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
11831187
SourceLoc AtEndLoc,

lib/AST/Attr.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,6 +2010,8 @@ StringRef DeclAttribute::getAttrName() const {
20102010
} else {
20112011
return "_allowFeatureSuppression";
20122012
}
2013+
case DeclAttrKind::Lifetime:
2014+
return "lifetime";
20132015
}
20142016
llvm_unreachable("bad DeclAttrKind");
20152017
}
@@ -3029,6 +3031,24 @@ AllowFeatureSuppressionAttr *AllowFeatureSuppressionAttr::create(
30293031
AllowFeatureSuppressionAttr(atLoc, range, implicit, inverted, features);
30303032
}
30313033

3034+
LifetimeAttr::LifetimeAttr(SourceLoc atLoc, SourceRange baseRange,
3035+
bool implicit,
3036+
ArrayRef<LifetimeDependenceSpecifier> entries)
3037+
: DeclAttribute(DeclAttrKind::Lifetime, atLoc, baseRange, implicit),
3038+
NumEntries(entries.size()) {
3039+
std::copy(entries.begin(), entries.end(),
3040+
getTrailingObjects<LifetimeDependenceSpecifier>());
3041+
}
3042+
3043+
LifetimeAttr *
3044+
LifetimeAttr::create(ASTContext &context, SourceLoc atLoc,
3045+
SourceRange baseRange, bool implicit,
3046+
ArrayRef<LifetimeDependenceSpecifier> entries) {
3047+
unsigned size = totalSizeToAlloc<LifetimeDependenceSpecifier>(entries.size());
3048+
void *mem = context.Allocate(size, alignof(LifetimeDependenceSpecifier));
3049+
return new (mem) LifetimeAttr(atLoc, baseRange, implicit, entries);
3050+
}
3051+
30323052
void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) {
30333053
if (attr)
30343054
attr->print(out);

lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ extension ASTGenVisitor {
189189
fatalError("unimplemented")
190190
case .allowFeatureSuppression:
191191
return self.generateAllowFeatureSuppressionAttr(attribute: node)?.asDeclAttribute
192+
case .lifetime:
193+
fatalError("unimplemented")
192194

193195
// Simple attributes.
194196
case .alwaysEmitConformanceMetadata,

lib/Parse/ParseDecl.cpp

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2799,6 +2799,96 @@ static std::optional<Identifier> parseSingleAttrOptionImpl(
27992799
return P.Context.getIdentifier(parsedName);
28002800
}
28012801

2802+
ParserResult<LifetimeAttr> Parser::parseLifetimeAttribute(SourceLoc atLoc,
2803+
SourceLoc loc) {
2804+
ParserStatus status;
2805+
SmallVector<LifetimeDependenceSpecifier> lifetimeEntries;
2806+
2807+
if (!Context.LangOpts.hasFeature(Feature::NonescapableTypes)) {
2808+
diagnose(loc, diag::requires_experimental_feature, "lifetime attribute",
2809+
false, getFeatureName(Feature::NonescapableTypes));
2810+
status.setIsParseError();
2811+
return status;
2812+
}
2813+
2814+
if (!Tok.isFollowingLParen()) {
2815+
diagnose(loc, diag::expected_lparen_after_lifetime_dependence);
2816+
status.setIsParseError();
2817+
return status;
2818+
}
2819+
// consume the l_paren
2820+
auto lParenLoc = consumeToken();
2821+
2822+
SourceLoc rParenLoc;
2823+
bool foundParamId = false;
2824+
status = parseList(
2825+
tok::r_paren, lParenLoc, rParenLoc, /*AllowSepAfterLast*/ false,
2826+
diag::expected_rparen_after_lifetime_dependence, [&]() -> ParserStatus {
2827+
ParserStatus listStatus;
2828+
foundParamId = true;
2829+
switch (Tok.getKind()) {
2830+
case tok::identifier: {
2831+
Identifier paramName;
2832+
auto paramLoc =
2833+
consumeIdentifier(paramName, /*diagnoseDollarPrefix=*/false);
2834+
if (paramName.is("immortal")) {
2835+
lifetimeEntries.push_back(
2836+
LifetimeDependenceSpecifier::
2837+
getImmortalLifetimeDependenceSpecifier(paramLoc));
2838+
} else {
2839+
lifetimeEntries.push_back(
2840+
LifetimeDependenceSpecifier::
2841+
getNamedLifetimeDependenceSpecifier(paramLoc, paramName));
2842+
}
2843+
break;
2844+
}
2845+
case tok::integer_literal: {
2846+
SourceLoc paramLoc;
2847+
unsigned paramNum;
2848+
if (parseUnsignedInteger(
2849+
paramNum, paramLoc,
2850+
diag::expected_param_index_lifetime_dependence)) {
2851+
listStatus.setIsParseError();
2852+
return listStatus;
2853+
}
2854+
lifetimeEntries.push_back(
2855+
LifetimeDependenceSpecifier::
2856+
getOrderedLifetimeDependenceSpecifier(paramLoc, paramNum));
2857+
break;
2858+
}
2859+
case tok::kw_self: {
2860+
auto paramLoc = consumeToken(tok::kw_self);
2861+
lifetimeEntries.push_back(
2862+
LifetimeDependenceSpecifier::getSelfLifetimeDependenceSpecifier(
2863+
paramLoc));
2864+
break;
2865+
}
2866+
default:
2867+
diagnose(
2868+
Tok,
2869+
diag::
2870+
expected_identifier_or_index_or_self_after_lifetime_dependence);
2871+
listStatus.setIsParseError();
2872+
return listStatus;
2873+
}
2874+
return listStatus;
2875+
});
2876+
2877+
if (!foundParamId) {
2878+
diagnose(
2879+
Tok,
2880+
diag::expected_identifier_or_index_or_self_after_lifetime_dependence);
2881+
status.setIsParseError();
2882+
return status;
2883+
}
2884+
2885+
assert(!lifetimeEntries.empty());
2886+
SourceRange range(loc, rParenLoc);
2887+
return ParserResult<LifetimeAttr>(
2888+
LifetimeAttr::create(Context, atLoc, SourceRange(loc, rParenLoc),
2889+
/* implicit */ false, lifetimeEntries));
2890+
}
2891+
28022892
/// Parses a (possibly optional) argument for an attribute containing a single, arbitrary identifier.
28032893
///
28042894
/// \param P The parser object.
@@ -4060,6 +4150,13 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
40604150
Attributes.add(attr);
40614151
break;
40624152
}
4153+
case DeclAttrKind::Lifetime: {
4154+
auto Attr = parseLifetimeAttribute(AtLoc, Loc);
4155+
Status |= Attr;
4156+
if (Attr.isNonNull())
4157+
Attributes.add(Attr.get());
4158+
break;
4159+
}
40634160
}
40644161

40654162
if (DuplicateAttribute) {
@@ -5145,7 +5242,7 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers(
51455242
specifierList.push_back(
51465243
LifetimeDependenceSpecifier::
51475244
getNamedLifetimeDependenceSpecifier(
5148-
paramLoc, lifetimeDependenceKind, paramName));
5245+
paramLoc, paramName, lifetimeDependenceKind));
51495246
}
51505247
break;
51515248
}
@@ -5161,7 +5258,7 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers(
51615258
specifierList.push_back(
51625259
LifetimeDependenceSpecifier::
51635260
getOrderedLifetimeDependenceSpecifier(
5164-
paramLoc, lifetimeDependenceKind, paramNum));
5261+
paramLoc, paramNum, lifetimeDependenceKind));
51655262
break;
51665263
}
51675264
case tok::kw_self: {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
366366
void visitWeakLinkedAttr(WeakLinkedAttr *attr);
367367
void visitSILGenNameAttr(SILGenNameAttr *attr);
368368
void visitUnsafeAttr(UnsafeAttr *attr);
369+
void visitLifetimeAttr(LifetimeAttr *attr);
369370
};
370371

371372
} // end anonymous namespace
@@ -7655,6 +7656,8 @@ void AttributeChecker::visitUnsafeAttr(UnsafeAttr *attr) {
76557656
diagnoseAndRemoveAttr(attr, diag::unsafe_attr_disabled);
76567657
}
76577658

7659+
void AttributeChecker::visitLifetimeAttr(LifetimeAttr *attr) {}
7660+
76587661
namespace {
76597662

76607663
class ClosureAttributeChecker

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,6 +1701,7 @@ namespace {
17011701
UNINTERESTING_ATTR(UnsafeNonEscapableResult)
17021702
UNINTERESTING_ATTR(StaticExclusiveOnly)
17031703
UNINTERESTING_ATTR(PreInverseGenerics)
1704+
UNINTERESTING_ATTR(Lifetime)
17041705
#undef UNINTERESTING_ATTR
17051706

17061707
void visitAvailableAttr(AvailableAttr *attr) {

lib/Serialization/ModuleFormat.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 885; // opened existentials
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 886; // @lifetime attribute
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -2468,6 +2468,15 @@ namespace decls_block {
24682468
// trialed by introduced conformances
24692469
>;
24702470

2471+
using LifetimeDeclAttrLayout =
2472+
BCRecordLayout<Lifetime_DECL_ATTR,
2473+
BCVBR<4>, // targetIndex
2474+
BCFixed<1>, // isImmortal
2475+
BCFixed<1>, // hasInheritLifetimeParamIndices
2476+
BCFixed<1>, // hasScopeLifetimeParamIndices
2477+
BCArray<BCFixed<1>> // concatenated param indices
2478+
>;
2479+
24712480
#undef SYNTAX_SUGAR_TYPE_LAYOUT
24722481
#undef TYPE_LAYOUT
24732482
#undef TYPE_LAYOUT_IMPL

lib/Serialization/Serialization.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3359,6 +3359,10 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
33593359
RawLayoutDeclAttrLayout::emitRecord(
33603360
S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(),
33613361
typeID, rawSize, rawAlign, attr->shouldMoveAsLikeType());
3362+
return;
3363+
}
3364+
case DeclAttrKind::Lifetime: {
3365+
return;
33623366
}
33633367
}
33643368
}

test/Parse/lifetime_attr.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -enable-experimental-feature NonescapableTypes
2+
// REQUIRES: asserts
3+
4+
struct NE : ~Escapable {
5+
6+
}
7+
8+
@lifetime(ne)
9+
func derive(_ ne: NE) -> NE {
10+
ne
11+
}
12+
13+
@lifetime // expected-error{{expected '(' after lifetime dependence specifier}}
14+
func testMissingLParenError(_ ne: NE) -> NE {
15+
ne
16+
}
17+
18+
@lifetime() // expected-error{{expected identifier, index or self in lifetime dependence specifier}}
19+
func testMissingDependence(_ ne: NE) -> NE {
20+
ne
21+
}
22+

0 commit comments

Comments
 (0)