Skip to content

Commit e61d87c

Browse files
committed
Add support for parsing @Lifetime attribute to specify lifetime dependencies on declarations
1 parent 854298a commit e61d87c

File tree

12 files changed

+200
-8
lines changed

12 files changed

+200
-8
lines changed

Diff for: include/swift/AST/Attr.h

+22
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"
@@ -2627,6 +2628,27 @@ class RawLayoutAttr final : public DeclAttribute {
26272628
}
26282629
};
26292630

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

Diff for: include/swift/AST/DeclAttr.def

+4
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

Diff for: include/swift/AST/LifetimeDependence.h

+9-5
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

Diff for: include/swift/Parse/Parser.h

+4
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,

Diff for: lib/AST/Attr.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -2011,6 +2011,8 @@ StringRef DeclAttribute::getAttrName() const {
20112011
} else {
20122012
return "_allowFeatureSuppression";
20132013
}
2014+
case DeclAttrKind::Lifetime:
2015+
return "lifetime";
20142016
}
20152017
llvm_unreachable("bad DeclAttrKind");
20162018
}
@@ -3040,6 +3042,24 @@ AllowFeatureSuppressionAttr *AllowFeatureSuppressionAttr::create(
30403042
AllowFeatureSuppressionAttr(atLoc, range, implicit, inverted, features);
30413043
}
30423044

3045+
LifetimeAttr::LifetimeAttr(SourceLoc atLoc, SourceRange baseRange,
3046+
bool implicit,
3047+
ArrayRef<LifetimeDependenceSpecifier> entries)
3048+
: DeclAttribute(DeclAttrKind::Lifetime, atLoc, baseRange, implicit),
3049+
NumEntries(entries.size()) {
3050+
std::copy(entries.begin(), entries.end(),
3051+
getTrailingObjects<LifetimeDependenceSpecifier>());
3052+
}
3053+
3054+
LifetimeAttr *
3055+
LifetimeAttr::create(ASTContext &context, SourceLoc atLoc,
3056+
SourceRange baseRange, bool implicit,
3057+
ArrayRef<LifetimeDependenceSpecifier> entries) {
3058+
unsigned size = totalSizeToAlloc<LifetimeDependenceSpecifier>(entries.size());
3059+
void *mem = context.Allocate(size, alignof(LifetimeDependenceSpecifier));
3060+
return new (mem) LifetimeAttr(atLoc, baseRange, implicit, entries);
3061+
}
3062+
30433063
void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) {
30443064
if (attr)
30453065
attr->print(out);

Diff for: lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

+2
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,

Diff for: lib/Parse/ParseDecl.cpp

+99-2
Original file line numberDiff line numberDiff line change
@@ -2801,6 +2801,96 @@ static std::optional<Identifier> parseSingleAttrOptionImpl(
28012801
return P.Context.getIdentifier(parsedName);
28022802
}
28032803

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

40674164
if (DuplicateAttribute) {
@@ -5147,7 +5244,7 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers(
51475244
specifierList.push_back(
51485245
LifetimeDependenceSpecifier::
51495246
getNamedLifetimeDependenceSpecifier(
5150-
paramLoc, lifetimeDependenceKind, paramName));
5247+
paramLoc, paramName, lifetimeDependenceKind));
51515248
}
51525249
break;
51535250
}
@@ -5163,7 +5260,7 @@ ParserStatus Parser::parseLifetimeDependenceSpecifiers(
51635260
specifierList.push_back(
51645261
LifetimeDependenceSpecifier::
51655262
getOrderedLifetimeDependenceSpecifier(
5166-
paramLoc, lifetimeDependenceKind, paramNum));
5263+
paramLoc, paramNum, lifetimeDependenceKind));
51675264
break;
51685265
}
51695266
case tok::kw_self: {

Diff for: lib/Sema/TypeCheckAttr.cpp

+3
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
@@ -7674,6 +7675,8 @@ void AttributeChecker::visitUnsafeAttr(UnsafeAttr *attr) {
76747675
diagnoseAndRemoveAttr(attr, diag::unsafe_attr_disabled);
76757676
}
76767677

7678+
void AttributeChecker::visitLifetimeAttr(LifetimeAttr *attr) {}
7679+
76777680
namespace {
76787681

76797682
class ClosureAttributeChecker

Diff for: lib/Sema/TypeCheckDeclOverride.cpp

+1
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) {

Diff for: lib/Serialization/ModuleFormat.h

+10-1
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 = 889; // public-module-name
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 890; // @lifetime attribute
6262

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

2493+
using LifetimeDeclAttrLayout =
2494+
BCRecordLayout<Lifetime_DECL_ATTR,
2495+
BCVBR<4>, // targetIndex
2496+
BCFixed<1>, // isImmortal
2497+
BCFixed<1>, // hasInheritLifetimeParamIndices
2498+
BCFixed<1>, // hasScopeLifetimeParamIndices
2499+
BCArray<BCFixed<1>> // concatenated param indices
2500+
>;
2501+
24932502
#undef SYNTAX_SUGAR_TYPE_LAYOUT
24942503
#undef TYPE_LAYOUT
24952504
#undef TYPE_LAYOUT_IMPL

Diff for: lib/Serialization/Serialization.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -3382,6 +3382,10 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
33823382
RawLayoutDeclAttrLayout::emitRecord(
33833383
S.Out, S.ScratchRecord, abbrCode, attr->isImplicit(),
33843384
typeID, countID, rawSize, rawAlign, attr->shouldMoveAsLikeType());
3385+
return;
3386+
}
3387+
case DeclAttrKind::Lifetime: {
3388+
return;
33853389
}
33863390
}
33873391
}

Diff for: test/Parse/lifetime_attr.swift

+22
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)