Skip to content

Commit bde35c9

Browse files
committed
[lifetimes] add same-type default lifetime inference
Infer @Lifetime(result: copy arg) for every (result: R, arg: A) pair such that R == A and 'arg' is not 'inout'.
1 parent deb920e commit bde35c9

File tree

6 files changed

+141
-14
lines changed

6 files changed

+141
-14
lines changed

docs/ReferenceGuides/LifetimeAnnotation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ The `@lifetime` annotation is enforced both in the body of the function and at e
1818

1919
## Default lifetimes
2020

21-
The Swift 6.2 compiler provided default `@_lifetime` behavior whenever it can do so without ambiguity. Often, despite ambiguity, an "obvious" default exists, but we wanted to introduce defaults slowly after developers have enough experience to inform discussion about them. This document tracks the current state of the implementation as it progresses from the original 6.2 implementation. Corresponding tests are in `test/Sema/lifetime_depend_infer.swift`; searching for "DEFAULT:" highlights the rules defined below...
21+
The Swift 6.2 compiler provided default `@_lifetime` behavior whenever it can do so without ambiguity. Often, despite ambiguity, an obvious default exists, but we wanted to introduce defaults slowly after developers have enough experience to inform discussion about them. This document tracks the current state of the implementation as it progresses from the original 6.2 implementation. Corresponding tests are in `test/Sema/lifetime_depend_infer.swift`; searching for "DEFAULT:" highlights the rules defined below...
2222

2323
### Same-type default lifetime (unimplemented)
2424

lib/AST/LifetimeDependence.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,8 @@ class LifetimeDependenceChecker {
10841084

10851085
// Infer non-Escapable results.
10861086
if (isDiagnosedNonEscapable(getResultOrYield())) {
1087+
inferNonEscapableResultOnSameTypeParam();
1088+
10871089
if (isInit() && isImplicitOrSIL()) {
10881090
inferImplicitInit();
10891091
} else if (hasImplicitSelfParam()) {
@@ -1105,6 +1107,41 @@ class LifetimeDependenceChecker {
11051107
inferInoutParams();
11061108
}
11071109

1110+
// Infer a dependency to the ~Escapable result from all parameters of the same
1111+
// type.
1112+
void inferNonEscapableResultOnSameTypeParam() {
1113+
CanType resultTy = getResultOrYield()->getCanonicalType();
1114+
1115+
TargetDeps *targetDeps = depBuilder.getInferredTargetDeps(resultIndex);
1116+
if (!targetDeps)
1117+
return;
1118+
1119+
if (hasImplicitSelfParam()
1120+
&& !afd->getImplicitSelfDecl()->isInOut()
1121+
&& dc->getSelfTypeInContext()->getCanonicalType() == resultTy) {
1122+
targetDeps->inheritIndices.set(selfIndex);
1123+
}
1124+
1125+
unsigned paramIndex = 0;
1126+
for (auto *param : *afd->getParameters()) {
1127+
SWIFT_DEFER { paramIndex++; };
1128+
1129+
// Ignore inout parameters--they are effectively considered to be a
1130+
// different type when applying the same-type rule. An inout parameter is
1131+
// only a default dependency source or target for the dependency on
1132+
// itself, which is covered by the 'inout' rule.
1133+
if (param->isInOut())
1134+
continue;
1135+
1136+
CanType paramTy = afd->mapTypeIntoEnvironment(
1137+
param->getInterfaceType())->getCanonicalType();
1138+
if (paramTy != resultTy)
1139+
continue;
1140+
1141+
targetDeps->inheritIndices.set(paramIndex);
1142+
}
1143+
}
1144+
11081145
// Infer dependence for an accessor whose non-escapable result depends on
11091146
// self. This includes _read and _modify.
11101147
//

test/Parse/lifetime_attr.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ func derive(_ ne1: NE, _ ne2: NE) -> NE {
2020
}
2121

2222
@_lifetime // expected-error{{expected '(' after lifetime dependence specifier}}
23-
func testMissingLParenError(_ ne: NE) -> NE { // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
23+
func testMissingLParenError(_ ne: NE) -> NE {
2424
ne
2525
}
2626

2727
@_lifetime() // expected-error{{expected 'copy', 'borrow', or '&' followed by an identifier, index or 'self' in lifetime dependence specifier}}
28-
func testMissingDependence(_ ne: NE) -> NE { // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
28+
func testMissingDependence(_ ne: NE) -> NE {
2929
ne
3030
}
3131

test/Sema/lifetime_depend_infer.swift

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,40 @@ struct NEImmortal: ~Escapable {
2121

2222
struct MutNE: ~Copyable & ~Escapable {}
2323

24+
// =============================================================================
25+
// Same-type default rule
26+
// =============================================================================
27+
28+
func sameTypeParam(ne: NE) -> NE { ne }
29+
30+
func sameTypeConsumingParam(ne: consuming NE) -> NE { ne }
31+
32+
func sameTypeBorrowingParam(ne: borrowing NE) -> NE { ne }
33+
34+
func sameTypeInoutParam(ne: inout NE) -> NE { ne } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
35+
36+
func sameTypeParam_sameTypeParam(ne1: NE, ne2: NE) -> NE { ne1 }
37+
38+
func sameTypeParam_otherTypeParam(ne: NE, c: C) -> NE { ne }
39+
40+
func sameTypeParam_sameTypeInoutParam(ne: NE, mutNE: inout NE) -> NE { ne }
41+
42+
struct NonEscapableSelf: ~Escapable {
43+
func sameTypeSelf_noParam() -> Self { self }
44+
45+
consuming func sameTypeConsumingSelf_noParam() -> Self { self }
46+
47+
borrowing func sameTypeBorrowingSelf_noParam() -> Self { self }
48+
49+
func sameTypeSelf_InoutParam(ne: inout NE) -> NE { ne } // expected-error{{a method with a ~Escapable result requires '@_lifetime(...)'}}
50+
51+
func sameTypeSelf_sameTypeParam(ne: NonEscapableSelf) -> Self { self }
52+
53+
func sameTypeSelf_otherTypeParam(c: C) -> Self { self }
54+
55+
func sameTypeSelf_sameTypeInoutParam(mutNE: inout NonEscapableSelf) -> Self { self }
56+
}
57+
2458
// =============================================================================
2559
// Single parameter default rule for functions
2660
// =============================================================================
@@ -71,22 +105,22 @@ func twoParamsBorrow_NEResult(c: borrowing C, _: Int) -> NEImmortal { NEImmortal
71105

72106
func twoParamsInout_NEResult(c: inout C, _: Int) -> NEImmortal { NEImmortal() } // expected-error{{a function with a ~Escapable result requires '@_lifetime(...)'}}
73107

74-
func neParam_NEResult(ne: NE) -> NE { ne } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
108+
func neParam_NEResult(ne: NE) -> NE { ne }
75109

76110
@_lifetime(ne) // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
77111
func neParamLifetime_NEResult(ne: NE) -> NE { ne }
78112

79-
func neParamBorrow_NEResult(ne: borrowing NE) -> NE { ne } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
113+
func neParamBorrow_NEResult(ne: borrowing NE) -> NE { ne }
80114

81115
@_lifetime(ne) // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
82116
func neParamBorrowLifetime_NEResult(ne: borrowing NE) -> NE { ne }
83117

84-
func neParamConsume_NEResult(ne: consuming NE) -> NE { ne } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
118+
func neParamConsume_NEResult(ne: consuming NE) -> NE { ne }
85119

86120
@_lifetime(ne) // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
87121
func neParamConsumeLifetime_NEResult(ne: consuming NE) -> NE { ne }
88122

89-
func neParam_IntParam_NEResult(ne: NE, _:Int) -> NE { ne } // expected-error{{a function with a ~Escapable result requires '@_lifetime(...)'}}
123+
func neParam_IntParam_NEResult(ne: NE, _:Int) -> NE { ne }
90124

91125
func inoutParam_inoutParam_NEResult(a: inout C, b: inout C) -> NEImmortal { NEImmortal() }
92126
// expected-error@-1{{a function with a ~Escapable result requires '@_lifetime(...)'}}
@@ -201,8 +235,8 @@ struct EscapableTrivialSelf {
201235
mutating func mutating_oneParamBorrow_NEResult(_: Int) -> NEImmortal { NEImmortal() }
202236
}
203237

204-
struct NonEscapableSelf: ~Escapable {
205-
func noParam_NEResult() -> NonEscapableSelf { self } // expected-error{{cannot infer the lifetime dependence scope on a method with a ~Escapable parameter, specify '@_lifetime(borrow self)' or '@_lifetime(copy self)'}}
238+
extension NonEscapableSelf /* where Self: ~Escapable */ {
239+
func noParam_NEResult() -> NonEscapableSelf { self }
206240

207241
@_lifetime(self) // expected-error{{cannot infer the lifetime dependence scope on a method with a ~Escapable parameter, specify '@_lifetime(borrow self)' or '@_lifetime(copy self)'}}
208242
func noParamLifetime_NEResult() -> NonEscapableSelf { self }
@@ -224,7 +258,7 @@ struct NonEscapableSelf: ~Escapable {
224258
@_lifetime(&self) // OK
225259
mutating func mutating_noParamBorrow_NEResult() -> NonEscapableSelf { self }
226260

227-
func oneParam_NEResult(_: Int) -> NonEscapableSelf { self } // expected-error{{a method with a ~Escapable result requires '@_lifetime(...)'}}
261+
func oneParam_NEResult(_: Int) -> NonEscapableSelf { self }
228262

229263
@_lifetime(self) // expected-error{{cannot infer the lifetime dependence scope on a method with a ~Escapable parameter, specify '@_lifetime(borrow self)' or '@_lifetime(copy self)'}}
230264
func oneParamLifetime_NEResult(_: Int) -> NonEscapableSelf { self }
@@ -263,6 +297,7 @@ func inoutNEParam_NEParam_void(_: inout NE, _: NE) {} // OK
263297
/* DEFAULT: @_lifetime(1: copy 1) */
264298
func inoutParam_inoutNEParam_void(_: inout NE, _: inout NE) {} // OK
265299

300+
266301
func inoutNEParam_NEResult(ne: inout NE) -> NE { ne } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow ne)' or '@_lifetime(copy ne)'}}
267302

268303
/* DEFAULT: @_lifetime(ne: copy ne) */

test/Sema/lifetime_depend_infer_defaults.swift

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,60 @@ struct NEImmortal: ~Escapable {
1818

1919
struct MutNE: ~Copyable & ~Escapable {}
2020

21+
// =============================================================================
22+
// Same-type default rule
23+
// =============================================================================
24+
25+
/* DEFAULT: @_lifetime(copy ne) */
26+
// CHECK: @$s30lifetime_depend_infer_defaults13sameTypeParam2neAA2NEVAE_tF : $@convention(thin) (@guaranteed NE) -> @lifetime(copy 0) @owned NE
27+
func sameTypeParam(ne: NE) -> NE { ne }
28+
29+
/* DEFAULT: @_lifetime(copy ne) */
30+
// CHECK: @$s30lifetime_depend_infer_defaults22sameTypeConsumingParam2neAA2NEVAEn_tF : $@convention(thin) (@owned NE) -> @lifetime(copy 0) @owned NE
31+
func sameTypeConsumingParam(ne: consuming NE) -> NE { ne }
32+
33+
/* DEFAULT: @_lifetime(copy ne) */
34+
// CHECK: @$s30lifetime_depend_infer_defaults22sameTypeBorrowingParam2neAA2NEVAE_tF : $@convention(thin) (@guaranteed NE) -> @lifetime(copy 0) @owned NE
35+
func sameTypeBorrowingParam(ne: borrowing NE) -> NE { ne }
36+
37+
/* DEFAULT: @_lifetime(copy ne, copy ne2) */
38+
// CHECK: @$s30lifetime_depend_infer_defaults014sameTypeParam_efG03ne13ne2AA2NEVAF_AFtF : $@convention(thin) (@guaranteed NE, @guaranteed NE) -> @lifetime(copy 0, copy 1) @owned NE
39+
func sameTypeParam_sameTypeParam(ne1: NE, ne2: NE) -> NE { ne1 }
40+
41+
/* DEFAULT: @_lifetime(copy ne) */
42+
// CHECK: @$s30lifetime_depend_infer_defaults019sameTypeParam_otherfG02ne1cAA2NEVAF_AA1CCtF : $@convention(thin) (@guaranteed NE, @guaranteed C) -> @lifetime(copy 0) @owned NE
43+
func sameTypeParam_otherTypeParam(ne: NE, c: C) -> NE { ne }
44+
45+
/* DEFAULT: @_lifetime(copy ne) */
46+
// CHECK: @$s30lifetime_depend_infer_defaults014sameTypeParam_ef5InoutG02ne5mutNEAA0K0VAF_AFztF : $@convention(thin) (@guaranteed NE, @lifetime(copy 1) @inout NE) -> @lifetime(copy 0) @owned NE
47+
func sameTypeParam_sameTypeInoutParam(ne: NE, mutNE: inout NE) -> NE { ne }
48+
49+
struct NonEscapableSelf: ~Escapable {
50+
/* DEFAULT: @_lifetime(copy self) */
51+
// CHECK: $s30lifetime_depend_infer_defaults16NonEscapableSelfV08sameTypeG8_noParamACyF : $@convention(method) (@guaranteed NonEscapableSelf) -> @lifetime(copy 0) @owned NonEscapableSelf
52+
func sameTypeSelf_noParam() -> Self { self }
53+
54+
/* DEFAULT: @_lifetime(copy self) */
55+
// CHECK: @$s30lifetime_depend_infer_defaults16NonEscapableSelfV017sameTypeConsumingG8_noParamACyF : $@convention(method) (@owned NonEscapableSelf) -> @lifetime(copy 0) @owned NonEscapableSelf
56+
consuming func sameTypeConsumingSelf_noParam() -> Self { self }
57+
58+
/* DEFAULT: @_lifetime(copy self) */
59+
// CHECK: @$s30lifetime_depend_infer_defaults16NonEscapableSelfV017sameTypeBorrowingG8_noParamACyF : $@convention(method) (@guaranteed NonEscapableSelf) -> @lifetime(copy 0) @owned NonEscapableSelf
60+
borrowing func sameTypeBorrowingSelf_noParam() -> Self { self }
61+
62+
/* DEFAULT: @_lifetime(copy self, copy ne) */
63+
// CHECK: @$s30lifetime_depend_infer_defaults16NonEscapableSelfV08sameTypeg1_hI5Param2neA2C_tF : $@convention(method) (@guaranteed NonEscapableSelf, @guaranteed NonEscapableSelf) -> @lifetime(copy 0, copy 1) @owned NonEscapableSelf
64+
func sameTypeSelf_sameTypeParam(ne: NonEscapableSelf) -> Self { self }
65+
66+
/* DEFAULT: @_lifetime(copy self) */
67+
// CHECK: @$s30lifetime_depend_infer_defaults16NonEscapableSelfV08sameTypeg6_otherI5Param1cAcA1CC_tF : $@convention(method) (@guaranteed C, @guaranteed NonEscapableSelf) -> @lifetime(copy 1) @owned NonEscapableSelf
68+
func sameTypeSelf_otherTypeParam(c: C) -> Self { self }
69+
70+
/* DEFAULT: @_lifetime(copy self) */
71+
// CHECK: @$s30lifetime_depend_infer_defaults16NonEscapableSelfV08sameTypeg1_hI10InoutParam5mutNEA2Cz_tF : $@convention(method) (@lifetime(copy 0) @inout NonEscapableSelf, @guaranteed NonEscapableSelf) -> @lifetime(copy 1) @owned NonEscapableSelf
72+
func sameTypeSelf_sameTypeInoutParam(mutNE: inout NonEscapableSelf) -> Self { self }
73+
}
74+
2175
// =============================================================================
2276
// Single parameter default rule for functions
2377
// =============================================================================
@@ -112,7 +166,8 @@ struct EscapableTrivialSelf {
112166
func inoutNEParam_void(ne: inout NE) {} // OK
113167

114168
/* DEFAULT: @_lifetime(0: copy 0) */
115-
// CHECK: @$s30lifetime_depend_infer_defaults013inoutNEParam_F5_voidyyAA2NEVz_ADtF : $@convention(thin) (@lifetime(copy 0) @inout NE, @guaranteed NE) -> ()
169+
// CHECK: @$s30lifetime_depend_infer_defaults013inoutNEParam_F5_voidyyAA2NEVz_ADtF : $@convention(thin) (@lifetime(copy
170+
// 0, copy 1) @inout NE, @guaranteed NE) -> ()
116171
func inoutNEParam_NEParam_void(_: inout NE, _: NE) {} // OK
117172

118173
/* DEFAULT: @_lifetime(0: copy 0) */

test/Sema/lifetime_depend_noattr.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@
1313
struct EmptyNonEscapable: ~Escapable {} // OK - no dependence
1414

1515
// Don't allow non-Escapable return values.
16-
func neReturn(span: RawSpan) -> RawSpan { span } // expected-error{{cannot infer the lifetime dependence scope on a function with a ~Escapable parameter, specify '@_lifetime(borrow span)' or '@_lifetime(copy span)'}}
16+
func neReturn(span: RawSpan) -> RawSpan { span }
1717

1818
func neInout(span: inout RawSpan) {} // OK - inferred
1919

2020
struct S {
21-
func neReturn(span: RawSpan) -> RawSpan { span } // expected-error{{a method with a ~Escapable result requires '@_lifetime(...)}}
21+
func neReturn(span: RawSpan) -> RawSpan { span }
2222

2323
func neInout(span: inout RawSpan) {} // OK - inferred
2424
}
2525

2626
class C {
27-
func neReturn(span: RawSpan) -> RawSpan { span } // expected-error{{a method with a ~Escapable result requires '@_lifetime(...)'}}
27+
func neReturn(span: RawSpan) -> RawSpan { span }
2828

2929
func neInout(span: inout RawSpan) {} // OK - inferred
3030
}

0 commit comments

Comments
 (0)