Skip to content

Commit d3002ed

Browse files
committed
[nonescapable] remove '@_lifetime' requirement on implicit accessors
This avoids diagnostic errors on synthesized accessors, which are impossible for developers to understand. Fixes rdar://153793344 (Lifetime-dependent value returned by generated accessor '_read') (cherry picked from commit 855b3e4)
1 parent 2052d2c commit d3002ed

File tree

2 files changed

+189
-22
lines changed

2 files changed

+189
-22
lines changed

lib/AST/LifetimeDependence.cpp

Lines changed: 108 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ class LifetimeDependenceChecker {
464464
return false;
465465
}
466466

467+
// Infer ambiguous cases for backward compatibility.
467468
bool useLazyInference() const {
468469
return isInterfaceFile()
469470
|| ctx.LangOpts.EnableExperimentalLifetimeDependenceInference;
@@ -935,7 +936,7 @@ class LifetimeDependenceChecker {
935936
// Infer non-Escapable results.
936937
if (isDiagnosedNonEscapable(getResultOrYield())) {
937938
if (hasImplicitSelfParam()) {
938-
// Methods and accessors that return or yield a non-Escapable value.
939+
// Methods that return a non-Escapable value.
939940
inferNonEscapableResultOnSelf();
940941
return;
941942
}
@@ -973,32 +974,43 @@ class LifetimeDependenceChecker {
973974
return true;
974975
}
975976

976-
// Infer method dependence: result depends on self. This includes _modify.
977+
// Infer method dependence of result on self for
978+
// methods, getters, and _modify accessors.
977979
void inferNonEscapableResultOnSelf() {
978980
auto *afd = cast<AbstractFunctionDecl>(decl);
979981
Type selfTypeInContext = dc->getSelfTypeInContext();
980982
if (selfTypeInContext->hasError()) {
981983
return;
982984
}
983-
bool nonEscapableSelf = isDiagnosedNonEscapable(selfTypeInContext);
984985

985-
// Avoid diagnosing inference on mutating methods when 'self' is
986-
// non-Escapable. The inout 'self' also needs an inferred dependence on
987-
// itself. This will be diagnosed when checking for missing dependencies.
988-
if (nonEscapableSelf && afd->getImplicitSelfDecl()->isInOut()) {
989-
if (auto accessor = dyn_cast<AccessorDecl>(afd)) {
990-
inferMutatingAccessor(accessor);
986+
bool nonEscapableSelf = isDiagnosedNonEscapable(selfTypeInContext);
987+
if (auto accessor = dyn_cast<AccessorDecl>(afd)) {
988+
if (isImplicitOrSIL() || useLazyInference()) {
989+
if (nonEscapableSelf && afd->getImplicitSelfDecl()->isInOut()) {
990+
// Implicit accessors that return or yield a non-Escapable value may
991+
// infer dependency on both self and result.
992+
inferMutatingAccessor(accessor);
993+
}
994+
// Infer the result dependency on self based on the kind of accessor
995+
// that is wrapped by this synthesized accessors.
996+
if (auto dependenceKind =
997+
getAccessorDependence(accessor, selfTypeInContext)) {
998+
pushDeps(createDeps(resultIndex).add(selfIndex, *dependenceKind));
999+
}
1000+
return;
9911001
}
1002+
// Explicit accessors are inferred the same way as regular methods.
1003+
}
1004+
// Do infer the result of a mutating method when 'self' is
1005+
// non-Escapable. The missing dependence on inout 'self' will be diagnosed
1006+
// later anyway, so an explicit annotation will still be needed.
1007+
if (nonEscapableSelf && afd->getImplicitSelfDecl()->isInOut()) {
9921008
return;
9931009
}
994-
9951010
// Methods with parameters only apply to lazy inference.
9961011
if (!useLazyInference() && afd->getParameters()->size() > 0) {
9971012
return;
9981013
}
999-
// Allow inference for implicit getters, which simply return a stored,
1000-
// property, and for implicit _read/_modify, which cannot be defined
1001-
// explicitly alongside a regular getter.
10021014
if (!useLazyInference() && !isImplicitOrSIL()) {
10031015
// Require explicit @_lifetime(borrow self) for UnsafePointer-like self.
10041016
if (!nonEscapableSelf && isBitwiseCopyable(selfTypeInContext, ctx)) {
@@ -1014,9 +1026,12 @@ class LifetimeDependenceChecker {
10141026
return;
10151027
}
10161028
}
1029+
// Infer based on ownership if possible for either explicit accessors or
1030+
// methods as long as they pass preceding ambiguity checks.
10171031
auto kind = inferLifetimeDependenceKind(
10181032
selfTypeInContext, afd->getImplicitSelfDecl()->getValueOwnership());
10191033
if (!kind) {
1034+
// Special diagnostic for an attempt to depend on a consuming parameter.
10201035
diagnose(returnLoc,
10211036
diag::lifetime_dependence_cannot_infer_scope_ownership,
10221037
"self", diagnosticQualifier());
@@ -1025,6 +1040,8 @@ class LifetimeDependenceChecker {
10251040
pushDeps(createDeps(resultIndex).add(selfIndex, *kind));
10261041
}
10271042

1043+
// Infer the kind of dependence that makes sense for reading or writing a
1044+
// stored property (for getters or initializers).
10281045
std::optional<LifetimeDependenceKind>
10291046
inferLifetimeDependenceKind(Type sourceType, ValueOwnership ownership) {
10301047
auto *afd = cast<AbstractFunctionDecl>(decl);
@@ -1039,8 +1056,9 @@ class LifetimeDependenceChecker {
10391056
auto loweredOwnership = ownership != ValueOwnership::Default
10401057
? ownership
10411058
: getLoweredOwnership(afd);
1042-
if (loweredOwnership != ValueOwnership::Shared &&
1043-
loweredOwnership != ValueOwnership::InOut) {
1059+
// It is impossible to depend on a consumed Escapable value (unless it is
1060+
// BitwiseCopyable as checked above).
1061+
if (loweredOwnership == ValueOwnership::Owned) {
10441062
return std::nullopt;
10451063
}
10461064
return LifetimeDependenceKind::Scope;
@@ -1194,7 +1212,10 @@ class LifetimeDependenceChecker {
11941212
// Handle implicit setters before diagnosing mutating methods. This
11951213
// does not include global accessors, which have no implicit 'self'.
11961214
if (auto accessor = dyn_cast<AccessorDecl>(afd)) {
1197-
inferMutatingAccessor(accessor);
1215+
// Explicit setters require explicit lifetime dependencies.
1216+
if (isImplicitOrSIL() || useLazyInference()) {
1217+
inferMutatingAccessor(accessor);
1218+
}
11981219
return;
11991220
}
12001221
if (afd->getParameters()->size() > 0) {
@@ -1211,13 +1232,31 @@ class LifetimeDependenceChecker {
12111232
LifetimeDependenceKind::Inherit));
12121233
}
12131234

1214-
// Infer a mutating accessor's non-Escapable 'self' dependencies.
1215-
void inferMutatingAccessor(AccessorDecl *accessor) {
1235+
// Infer dependence for an accessor whose non-escapable result depends on
1236+
// self. This includes _read and _modify.
1237+
void inferAccessor(AccessorDecl *accessor, Type selfTypeInContext) {
1238+
// Explicit accessors require explicit lifetime dependencies.
12161239
auto *afd = cast<AbstractFunctionDecl>(decl);
12171240
if (!isImplicitOrSIL() && !useLazyInference()) {
1218-
// Explicit setters require explicit lifetime dependencies.
12191241
return;
12201242
}
1243+
bool nonEscapableSelf = isDiagnosedNonEscapable(selfTypeInContext);
1244+
if (nonEscapableSelf && afd->getImplicitSelfDecl()->isInOut()) {
1245+
// First, infer the dependency on the inout non-Escapable self. This may
1246+
// result in two inferred dependencies for accessors.
1247+
inferMutatingAccessor(accessor);
1248+
}
1249+
// Infer the result dependency on self based on the kind of accessor that
1250+
// is wrapped by this synthesized accessors.
1251+
if (auto dependenceKind =
1252+
getAccessorDependence(accessor, selfTypeInContext)) {
1253+
pushDeps(createDeps(resultIndex).add(selfIndex, *dependenceKind));
1254+
}
1255+
}
1256+
1257+
// Infer a mutating accessor's non-Escapable 'self' dependencies.
1258+
void inferMutatingAccessor(AccessorDecl *accessor) {
1259+
auto *afd = cast<AbstractFunctionDecl>(decl);
12211260
switch (accessor->getAccessorKind()) {
12221261
case AccessorKind::Read:
12231262
case AccessorKind::Read2:
@@ -1234,8 +1273,6 @@ class LifetimeDependenceChecker {
12341273
// _modify for them even though it won't be emitted.
12351274
pushDeps(createDeps(selfIndex).add(selfIndex,
12361275
LifetimeDependenceKind::Inherit));
1237-
pushDeps(createDeps(resultIndex).add(selfIndex,
1238-
LifetimeDependenceKind::Scope));
12391276
break;
12401277
case AccessorKind::Set: {
12411278
const unsigned newValIdx = 0;
@@ -1255,7 +1292,7 @@ class LifetimeDependenceChecker {
12551292
// dependency, so the setter cannot depend on 'newValue'.
12561293
if (!paramTypeInContext->isEscapable()) {
12571294
targetDeps = std::move(targetDeps)
1258-
.add(newValIdx, LifetimeDependenceKind::Inherit);
1295+
.add(newValIdx, LifetimeDependenceKind::Inherit);
12591296
}
12601297
pushDeps(std::move(targetDeps));
12611298
break;
@@ -1277,6 +1314,55 @@ class LifetimeDependenceChecker {
12771314
}
12781315
}
12791316

1317+
// Implicit accessors must be consistent with the accessor that they
1318+
// wrap. Otherwise, the sythesized implementation will report a diagnostic
1319+
// error.
1320+
std::optional<LifetimeDependenceKind>
1321+
getAccessorDependence(AccessorDecl *accessor, Type selfTypeInContext) {
1322+
auto *afd = cast<AbstractFunctionDecl>(decl);
1323+
std::optional<AccessorKind> wrappedAccessorKind = std::nullopt;
1324+
switch (accessor->getAccessorKind()) {
1325+
case AccessorKind::Read:
1326+
case AccessorKind::Read2:
1327+
case AccessorKind::Modify:
1328+
case AccessorKind::Modify2:
1329+
// read/modify are syntesized as calls to the getter.
1330+
wrappedAccessorKind = AccessorKind::Get;
1331+
break;
1332+
case AccessorKind::Get:
1333+
// getters are synthesized as access to a stored property.
1334+
break;
1335+
default:
1336+
// Unknown synthesized accessor.
1337+
// Setters are handled in inferMutatingAccessor() because they don't
1338+
// return a value.
1339+
return std::nullopt;
1340+
}
1341+
if (wrappedAccessorKind) {
1342+
auto *var = cast<VarDecl>(accessor->getStorage());
1343+
for (auto *wrappedAccessor : var->getAllAccessors()) {
1344+
if (wrappedAccessor->isImplicit())
1345+
continue;
1346+
if (wrappedAccessor->getAccessorKind() == wrappedAccessorKind) {
1347+
if (auto deps = wrappedAccessor->getLifetimeDependencies()) {
1348+
for (auto &dep : *deps) {
1349+
if (dep.getTargetIndex() != resultIndex)
1350+
continue;
1351+
if (dep.checkInherit(selfIndex))
1352+
return LifetimeDependenceKind::Inherit;
1353+
if (dep.checkScope(selfIndex))
1354+
return LifetimeDependenceKind::Scope;
1355+
}
1356+
}
1357+
}
1358+
}
1359+
}
1360+
// Either a Get or Modify without any wrapped accessor. Handle these like a
1361+
// read of the stored property.
1362+
return inferLifetimeDependenceKind(
1363+
selfTypeInContext, afd->getImplicitSelfDecl()->getValueOwnership());
1364+
}
1365+
12801366
// Infer 'inout' parameter dependency when the only parameter is
12811367
// non-Escapable.
12821368
//
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// RUN: %target-swift-emit-sil -enable-experimental-feature Lifetimes -primary-file %s | %FileCheck %s
2+
3+
// REQUIRES: swift_feature_Lifetimes
4+
5+
struct NE : ~Escapable {
6+
let _p: UnsafeRawPointer
7+
8+
@_lifetime(borrow p)
9+
init(_ p: UnsafeRawPointer) { self._p = p }
10+
}
11+
12+
struct NCNE : ~Copyable, ~Escapable {
13+
let _p: UnsafeRawPointer
14+
15+
@_lifetime(borrow p)
16+
init(_ p: UnsafeRawPointer) {
17+
self._p = p
18+
}
19+
}
20+
21+
struct NEStoredProp : ~Escapable {
22+
var ne: NE
23+
// NEStoredProp.ne.getter
24+
// CHECK-LABEL: sil hidden [transparent] @$s9accessors12NEStoredPropV2neAA2NEVvg : $@convention(method) (@guaranteed NEStoredProp) -> @lifetime(copy 0) @owned NE {
25+
26+
// NEStoredProp.ne.setter
27+
// CHECK-LABEL: sil hidden [transparent] @$s9accessors12NEStoredPropV2neAA2NEVvs : $@convention(method) (@owned NE, @lifetime(copy 0, copy 1) @inout NEStoredProp) -> () {
28+
29+
// NEStoredProp.ne.modify
30+
// CHECK-LABEL: sil hidden [transparent] @$s9accessors12NEStoredPropV2neAA2NEVvM : $@yield_once @convention(method) (@lifetime(copy 0) @inout NEStoredProp) -> @lifetime(copy 0) @yields @inout NE {
31+
32+
// NEStoredProp.init(ne:)
33+
// CHECK-LABEL: sil hidden @$s9accessors12NEStoredPropV2neAcA2NEV_tcfC : $@convention(method) (@owned NE, @thin NEStoredProp.Type) -> @lifetime(copy 0) @owned NEStoredProp {
34+
}
35+
36+
struct NCNEStoredProp : ~Copyable & ~Escapable {
37+
var ncne: NCNE
38+
// NCNEStoredProp.ncne.read
39+
// CHECK-LABEL: sil hidden [transparent] @$s9accessors14NCNEStoredPropV4ncneAA4NCNEVvr : $@yield_once @convention(method) (@guaranteed NCNEStoredProp) -> @lifetime(copy 0) @yields @guaranteed NCNE {
40+
41+
// NCNEStoredProp.ncne.setter
42+
// CHECK-LABEL: sil hidden [transparent] @$s9accessors14NCNEStoredPropV4ncneAA4NCNEVvs : $@convention(method) (@owned NCNE, @lifetime(copy 0, copy 1) @inout NCNEStoredProp) -> () {
43+
44+
// NCNEStoredProp.ncne.modify
45+
// CHECK-LABEL: sil hidden [transparent] @$s9accessors14NCNEStoredPropV4ncneAA4NCNEVvM : $@yield_once @convention(method) (@lifetime(copy 0) @inout NCNEStoredProp) -> @lifetime(copy 0) @yields @inout NCNE {
46+
47+
// NCNEStoredProp.init(ncne:)
48+
// CHECK-LABEL: sil hidden @$s9accessors14NCNEStoredPropV4ncneAcA4NCNEV_tcfC : $@convention(method) (@owned NCNE, @thin NCNEStoredProp.Type) -> @lifetime(copy 0) @owned NCNEStoredProp {
49+
}
50+
51+
struct NCNEHolder : ~Copyable, ~Escapable {
52+
var p: UnsafeRawPointer
53+
54+
// this cannot be public in order to synthesize the _read accessor.
55+
var ncneBorrow: NCNE {
56+
// CHECK-LABEL: sil{{.*}} @$s9accessors10NCNEHolderV10ncneBorrowAA4NCNEVvr : $@yield_once @convention(method) (@guaranteed NCNEHolder) -> @lifetime(borrow 0) @yields @guaranteed NCNE {
57+
@_lifetime(borrow self)
58+
borrowing get {
59+
_overrideLifetime(NCNE(p), borrowing: self)
60+
}
61+
// CHECK-LABEL: sil{{.*}} @$s9accessors10NCNEHolderV10ncneBorrowAA4NCNEVvM : $@yield_once @convention(method) (@lifetime(copy 0) @inout NCNEHolder) -> @lifetime(borrow 0) @yields @inout NCNE {
62+
@_lifetime(self: copy self)
63+
set {
64+
p = newValue._p
65+
}
66+
}
67+
68+
// this cannot be public in order to synthesize the _read accessor.
69+
var ncneCopy: NCNE {
70+
// CHECK-LABEL: sil{{.*}} @$s9accessors10NCNEHolderV8ncneCopyAA4NCNEVvr : $@yield_once @convention(method) (@guaranteed NCNEHolder) -> @lifetime(copy 0) @yields @guaranteed NCNE {
71+
@_lifetime(copy self)
72+
borrowing get {
73+
_overrideLifetime(NCNE(p), copying: self)
74+
}
75+
// CHECK-LABEL: sil{{.*}} @$s9accessors10NCNEHolderV8ncneCopyAA4NCNEVvM : $@yield_once @convention(method) (@lifetime(copy 0) @inout NCNEHolder) -> @lifetime(copy 0) @yields @inout NCNE {
76+
@_lifetime(self: copy self)
77+
set {
78+
p = newValue._p
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)