Skip to content

Commit 04076a5

Browse files
committed
Store parsed @Lifetime attribute on the function type
1 parent d6d7b03 commit 04076a5

File tree

6 files changed

+312
-7
lines changed

6 files changed

+312
-7
lines changed

include/swift/AST/Attr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2650,6 +2650,10 @@ class LifetimeAttr final
26502650
SourceRange baseRange, bool implicit,
26512651
ArrayRef<LifetimeEntry> entries);
26522652

2653+
ArrayRef<LifetimeEntry> getLifetimeEntries() const {
2654+
return {getTrailingObjects<LifetimeEntry>(), NumEntries};
2655+
}
2656+
26532657
static bool classof(const DeclAttribute *DA) {
26542658
return DA->getKind() == DeclAttrKind::Lifetime;
26552659
}

include/swift/AST/LifetimeDependence.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ class LifetimeDependenceInfo {
153153
unsigned sourceIndex,
154154
LifetimeDependenceKind kind);
155155

156+
/// Builds LifetimeDependenceInfo from @lifetime attribute
157+
static std::optional<ArrayRef<LifetimeDependenceInfo>>
158+
fromLifetimeAttribute(AbstractFunctionDecl *afd);
159+
156160
/// Builds LifetimeDependenceInfo from dependsOn type modifier
157161
static std::optional<LifetimeDependenceInfo>
158162
fromDependsOn(AbstractFunctionDecl *afd, TypeRepr *targetRepr,

lib/AST/Attr.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,6 +1812,20 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
18121812
break;
18131813
}
18141814

1815+
case DeclAttrKind::Lifetime: {
1816+
auto *attr = cast<LifetimeAttr>(this);
1817+
bool firstElem = true;
1818+
Printer << "@lifetime(";
1819+
for (auto entry : attr->getLifetimeEntries()) {
1820+
if (!firstElem) {
1821+
Printer << ", ";
1822+
}
1823+
Printer << entry.getParamString();
1824+
}
1825+
Printer << ")";
1826+
break;
1827+
}
1828+
18151829
#define SIMPLE_DECL_ATTR(X, CLASS, ...) case DeclAttrKind::CLASS:
18161830
#include "swift/AST/DeclAttr.def"
18171831
llvm_unreachable("handled above");

lib/AST/LifetimeDependence.cpp

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,22 @@ getLifetimeDependenceKind(LifetimeEntry specifier, AbstractFunctionDecl *afd,
218218
auto ownership = decl->getValueOwnership();
219219
auto type = decl->getTypeInContext();
220220

221+
// For @lifetime attribute, we determine lifetime dependence kind based on
222+
// type.
223+
if (afd->getAttrs().hasAttribute<LifetimeAttr>()) {
224+
auto lifetimeKind = getLifetimeDependenceKindFromType(type);
225+
bool isCompatible = isLifetimeDependenceCompatibleWithOwnership(
226+
lifetimeKind, type, ownership, afd);
227+
if (!isCompatible) {
228+
assert(lifetimeKind == LifetimeDependenceKind::Scope);
229+
diags.diagnose(
230+
loc, diag::lifetime_dependence_cannot_use_inferred_scoped_consuming);
231+
return std::nullopt;
232+
}
233+
}
234+
235+
// For dependsOn type modifier, we check if we had a "scoped" modifier, if not
236+
// we determine lifetime dependence kind based on type.
221237
auto parsedLifetimeKind = specifier.getParsedLifetimeDependenceKind();
222238
auto lifetimeKind =
223239
parsedLifetimeKind == ParsedLifetimeDependenceKind::Default
@@ -245,6 +261,10 @@ static bool populateLifetimeDependence(AbstractFunctionDecl *afd,
245261
SmallBitVector &inheritIndices,
246262
SmallBitVector &scopeIndices,
247263
bool &isImmortal) {
264+
auto *dc = afd->getDeclContext();
265+
auto &ctx = dc->getASTContext();
266+
auto &diags = ctx.Diags;
267+
248268
auto updateLifetimeIndices =
249269
[&](LifetimeEntry entry, unsigned paramIndexToSet,
250270
std::optional<LifetimeDependenceKind> lifetimeKind) {
@@ -278,7 +298,7 @@ static bool populateLifetimeDependence(AbstractFunctionDecl *afd,
278298
diag::lifetime_dependence_immortal_conflict_name);
279299
return true;
280300
}
281-
if (inheritIndicies.any() || scopeIndices.any()) {
301+
if (inheritIndices.any() || scopeIndices.any()) {
282302
diags.diagnose(entry.getLoc(), diag::lifetime_dependence_immortal_alone);
283303
return true;
284304
}
@@ -346,6 +366,45 @@ static bool populateLifetimeDependence(AbstractFunctionDecl *afd,
346366
}
347367
}
348368

369+
std::optional<ArrayRef<LifetimeDependenceInfo>>
370+
LifetimeDependenceInfo::fromLifetimeAttribute(AbstractFunctionDecl *afd) {
371+
auto *dc = afd->getDeclContext();
372+
auto &ctx = dc->getASTContext();
373+
374+
auto lifetimeAttrs = afd->getAttrs().getAttributes<LifetimeAttr>();
375+
376+
auto capacity = afd->hasImplicitSelfDecl()
377+
? (afd->getParameters()->size() + 1)
378+
: afd->getParameters()->size();
379+
380+
SmallBitVector inheritIndices(capacity);
381+
SmallBitVector scopeIndices(capacity);
382+
bool isImmortal = false;
383+
384+
bool hasError = false;
385+
for (auto attr : lifetimeAttrs) {
386+
for (auto entry : attr->getLifetimeEntries()) {
387+
hasError |= populateLifetimeDependence(afd, entry, inheritIndices,
388+
scopeIndices, isImmortal);
389+
}
390+
}
391+
392+
if (hasError) {
393+
return std::nullopt;
394+
}
395+
// TODO: Handle lifetime dependencies for targets other than function result.
396+
SmallVector<LifetimeDependenceInfo> lifetimeDependencies;
397+
auto resultIndex = afd->hasImplicitSelfDecl()
398+
? afd->getParameters()->size() + 1
399+
: afd->getParameters()->size();
400+
auto resultDependence = LifetimeDependenceInfo(
401+
inheritIndices.any() ? IndexSubset::get(ctx, inheritIndices) : nullptr,
402+
scopeIndices.any() ? IndexSubset::get(ctx, scopeIndices) : nullptr,
403+
resultIndex, isImmortal);
404+
lifetimeDependencies.push_back(resultDependence);
405+
return afd->getASTContext().AllocateCopy(lifetimeDependencies);
406+
}
407+
349408
std::optional<LifetimeDependenceInfo>
350409
LifetimeDependenceInfo::fromDependsOn(AbstractFunctionDecl *afd,
351410
TypeRepr *targetTypeRepr, Type targetType,
@@ -660,6 +719,10 @@ LifetimeDependenceInfo::get(AbstractFunctionDecl *afd) {
660719
}
661720
assert(isa<FuncDecl>(afd) || isa<ConstructorDecl>(afd));
662721

722+
if (afd->getAttrs().hasAttribute<LifetimeAttr>()) {
723+
return LifetimeDependenceInfo::fromLifetimeAttribute(afd);
724+
}
725+
663726
SmallVector<LifetimeDependenceInfo> lifetimeDependencies;
664727

665728
for (unsigned targetIndex : indices(*afd->getParameters())) {
@@ -672,20 +735,26 @@ LifetimeDependenceInfo::get(AbstractFunctionDecl *afd) {
672735
}
673736
}
674737

675-
std::optional<LifetimeDependenceInfo> resultDependence =
676-
LifetimeDependenceInfo::fromDependsOn(
677-
afd, afd->getResultTypeRepr(), getResultOrYield(afd),
678-
afd->hasImplicitSelfDecl() ? afd->getParameters()->size() + 1
679-
: afd->getParameters()->size());
738+
std::optional<LifetimeDependenceInfo> resultDependence;
680739

681-
if (!resultDependence.has_value()) {
740+
if (auto *lifetimeTypeRepr = dyn_cast_or_null<LifetimeDependentTypeRepr>(
741+
afd->getResultTypeRepr())) {
742+
resultDependence = LifetimeDependenceInfo::fromDependsOn(
743+
afd, lifetimeTypeRepr, getResultOrYield(afd),
744+
afd->hasImplicitSelfDecl() ? afd->getParameters()->size() + 1
745+
: afd->getParameters()->size());
746+
} else {
682747
resultDependence = LifetimeDependenceInfo::infer(afd);
683748
}
684749

685750
if (resultDependence.has_value()) {
686751
lifetimeDependencies.push_back(*resultDependence);
687752
}
688753

754+
if (lifetimeDependencies.empty()) {
755+
return std::nullopt;
756+
}
757+
689758
return afd->getASTContext().AllocateCopy(lifetimeDependencies);
690759
}
691760

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// RUN: %target-swift-frontend %s -emit-sil \
2+
// RUN: -enable-experimental-feature NonescapableTypes
3+
4+
// REQUIRES: asserts
5+
// REQUIRES: swift_in_compiler
6+
7+
// TODO: Use real Range
8+
public struct FakeRange<Bound> {
9+
public let lowerBound: Bound
10+
public let upperBound: Bound
11+
}
12+
13+
// TODO: Use real Optional
14+
public enum FakeOptional<T> {
15+
case none
16+
case some(T)
17+
}
18+
19+
public struct SpanIndex<Element> {
20+
let _rawValue: UnsafeRawPointer
21+
22+
internal init(rawValue: UnsafeRawPointer) {
23+
_rawValue = rawValue
24+
}
25+
26+
var isAligned: Bool {
27+
(Int(bitPattern: _rawValue) & (MemoryLayout<Element>.alignment-1)) == 0
28+
}
29+
}
30+
31+
extension SpanIndex: Equatable {}
32+
33+
extension SpanIndex: Hashable {}
34+
35+
extension SpanIndex: Strideable {
36+
public typealias Stride = Int
37+
38+
public func distance(to other: SpanIndex) -> Int {
39+
let bytes = _rawValue.distance(to: other._rawValue)
40+
let (q, r) = bytes.quotientAndRemainder(dividingBy: MemoryLayout<Element>.stride)
41+
precondition(r == 0)
42+
return q
43+
}
44+
45+
public func advanced(by n: Int) -> SpanIndex {
46+
.init(rawValue: _rawValue.advanced(by: n &* MemoryLayout<Element>.stride))
47+
}
48+
}
49+
50+
extension SpanIndex: Comparable {
51+
public static func <(lhs: SpanIndex, rhs: SpanIndex) -> Bool {
52+
lhs._rawValue < rhs._rawValue
53+
}
54+
}
55+
56+
public struct Span<Element> : ~Escapable {
57+
let start: SpanIndex<Element>
58+
public let count: Int
59+
private var baseAddress: UnsafeRawPointer { start._rawValue }
60+
// CHECK-LABEL: sil @$s025lifetime_dependence_span_A5_attr4SpanV11baseAddress5count9dependsOnACyxGSV_Siqd__htcRi_d__Ri0_d__lufC : $@convention(method) <Element><Owner where Owner : ~Copyable, Owner : ~Escapable> (UnsafeRawPointer, Int, @in_guaranteed Owner, @thin Span<Element>.Type) -> _inherit(2) @owned Span<Element> {
61+
@lifetime(owner)
62+
public init<Owner: ~Copyable & ~Escapable>(
63+
baseAddress: UnsafeRawPointer,
64+
count: Int,
65+
dependsOn owner: borrowing Owner
66+
) {
67+
self.init(
68+
start: .init(rawValue: baseAddress), count: count, dependsOn: owner
69+
)
70+
}
71+
// CHECK-LABEL: sil hidden @$s025lifetime_dependence_span_A5_attr4SpanV5start5count9dependsOnACyxGAA0E5IndexVyxG_Siqd__htcRi_d__Ri0_d__lufC : $@convention(method) <Element><Owner where Owner : ~Copyable, Owner : ~Escapable> (SpanIndex<Element>, Int, @in_guaranteed Owner, @thin Span<Element>.Type) -> _inherit(2) @owned Span<Element> {
72+
@lifetime(owner)
73+
init<Owner: ~Copyable & ~Escapable>(
74+
start index: SpanIndex<Element>,
75+
count: Int,
76+
dependsOn owner: borrowing Owner
77+
) {
78+
precondition(count >= 0, "Count must not be negative")
79+
if !_isPOD(Element.self) {
80+
precondition(
81+
index.isAligned,
82+
"baseAddress must be properly aligned for \(Element.self)"
83+
)
84+
}
85+
self.start = index
86+
self.count = count
87+
}
88+
@_unsafeNonescapableResult
89+
init(start index: SpanIndex<Element>,
90+
count: Int) {
91+
precondition(count >= 0, "Count must not be negative")
92+
if !_isPOD(Element.self) {
93+
precondition(
94+
index.isAligned,
95+
"baseAddress must be properly aligned for \(Element.self)"
96+
)
97+
}
98+
self.start = index
99+
self.count = count
100+
}
101+
}
102+
103+
extension Span {
104+
public typealias Index = SpanIndex<Element>
105+
public typealias SubSequence = Self
106+
107+
public var startIndex: Index { start }
108+
public var endIndex: Index { start.advanced(by: count) }
109+
110+
@inlinable @inline(__always)
111+
public func distance(from start: Index, to end: Index) -> Int {
112+
start.distance(to: end)
113+
}
114+
115+
public subscript(position: Index) -> Element {
116+
get {
117+
if _isPOD(Element.self) {
118+
return position._rawValue.loadUnaligned(as: Element.self)
119+
}
120+
else {
121+
return position._rawValue.load(as: Element.self)
122+
}
123+
}
124+
}
125+
126+
// CHECK-LABEL: sil @$s025lifetime_dependence_span_A5_attr4SpanVyACyxGAA9FakeRangeVyAA0E5IndexVyxGGcig : $@convention(method) <Element> (FakeRange<SpanIndex<Element>>, @guaranteed Span<Element>) -> _inherit(1) @owned Span<Element> {
127+
public subscript(bounds: FakeRange<SpanIndex<Element>>) -> Self {
128+
@lifetime(self)
129+
get {
130+
Span(
131+
start: bounds.lowerBound,
132+
count: bounds.upperBound.distance(to:bounds.lowerBound) / MemoryLayout<Element>.stride
133+
)
134+
}
135+
}
136+
137+
// CHECK-LABEL: sil @$s025lifetime_dependence_span_A5_attr4SpanV6prefix4upToACyxGAA0E5IndexVyxG_tF : $@convention(method) <Element> (SpanIndex<Element>, @guaranteed Span<Element>) -> _inherit(1) @owned Span<Element> {
138+
@lifetime(self)
139+
borrowing public func prefix(upTo index: SpanIndex<Element>) -> Self {
140+
index == startIndex
141+
? Self(start: start, count: 0, dependsOn: copy self)
142+
: prefix(through: index.advanced(by: -1))
143+
}
144+
145+
// CHECK-LABEL: sil @$s025lifetime_dependence_span_A5_attr4SpanV6prefix7throughACyxGAA0E5IndexVyxG_tF : $@convention(method) <Element> (SpanIndex<Element>, @guaranteed Span<Element>) -> _inherit(1) @owned Span<Element> {
146+
@lifetime(self)
147+
borrowing public func prefix(through index: Index) -> Self {
148+
let nc = distance(from: startIndex, to: index) &+ 1
149+
return Self(start: start, count: nc, dependsOn: copy self)
150+
}
151+
152+
// CHECK-LABEL: sil @$s025lifetime_dependence_span_A5_attr4SpanV6prefixyACyxGSiF : $@convention(method) <Element> (Int, @owned Span<Element>) -> _inherit(1) @owned Span<Element> {
153+
@lifetime(self)
154+
consuming public func prefix(_ maxLength: Int) -> Self {
155+
precondition(maxLength >= 0, "Can't have a prefix of negative length.")
156+
let nc = maxLength < count ? maxLength : count
157+
return Self(start: start, count: nc, dependsOn: self)
158+
}
159+
}
160+
161+
extension ContiguousArray {
162+
public var view: Span<Element> {
163+
@lifetime(self)
164+
borrowing _read {
165+
yield Span(
166+
baseAddress: _baseAddressIfContiguous!, count: count, dependsOn: self
167+
)
168+
}
169+
}
170+
}
171+
172+
public func array_view_element(a: ContiguousArray<Int> , i: SpanIndex<Int>) -> Int {
173+
a.view[i]
174+
}
175+
176+
public func array_view_slice_element(a: ContiguousArray<Int> , sliceIdx: FakeRange<SpanIndex<Int>>, Idx: SpanIndex<Int>) -> Int {
177+
a.view[sliceIdx][Idx]
178+
}
179+

test/Sema/lifetime_attr.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -enable-experimental-feature NonescapableTypes
2+
// REQUIRES: asserts
3+
4+
struct NE : ~Escapable {
5+
@lifetime(self) // expected-error{{invalid lifetime dependence on self in an initializer}}
6+
init() {}
7+
}
8+
9+
@lifetime(nonexisting) // expected-error{{invalid parameter name specified 'nonexisting'}}
10+
func invalidAttrOnNonExistingParam(_ ne: NE) -> NE {
11+
ne
12+
}
13+
14+
@lifetime(self) // expected-error{{invalid lifetime dependence specifier on non-existent self}}
15+
func invalidAttrOnNonExistingSelf(_ ne: NE) -> NE {
16+
ne
17+
}
18+
19+
@lifetime(2) // expected-error{{invalid parameter index specified 2}}
20+
func invalidAttrOnNonExistingParamIndex(_ ne: NE) -> NE {
21+
ne
22+
}
23+
24+
@lifetime(ne, ne) // expected-error{{duplicate lifetime dependence specifier}}
25+
func invalidDuplicateLifetimeDependence1(_ ne: borrowing NE) -> NE {
26+
ne
27+
}
28+
29+
class Klass {}
30+
31+
@lifetime(x) // expected-error{{invalid use of lifetime dependence on an Escapable parameter with consuming ownership}}
32+
func invalidDependence(_ x: consuming Klass) -> NE {
33+
NE()
34+
}
35+

0 commit comments

Comments
 (0)