@@ -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 //
0 commit comments