Skip to content

Commit 1164046

Browse files
committed
Lifetime dependence: add inferrence for setters.
A nonescapable computed property should always depend on 'newValue'. We simply infer that now. There's no way to explicitly spell the dependence.
1 parent ba9f12a commit 1164046

File tree

5 files changed

+104
-1
lines changed

5 files changed

+104
-1
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ extension LifetimeDependence {
215215
if (value.definingInstructionOrTerminator as! FullApplySite).hasResultDependence {
216216
return nil
217217
}
218-
assert(value.ownership == .owned, "apply result must be owned")
218+
assert(value.ownership == .owned, "unsafe apply result must be owned")
219219
self.scope = Scope(base: value, context)!
220220
self.dependentValue = value
221221
}

include/swift/AST/LifetimeDependence.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ class LifetimeDependenceInfo {
159159
/// Infer LifetimeDependenceInfo on result
160160
static std::optional<LifetimeDependenceInfo> infer(AbstractFunctionDecl *afd);
161161

162+
/// Infer LifetimeDependenceInfo on setter
163+
static std::optional<LifetimeDependenceInfo>
164+
inferSetter(AbstractFunctionDecl *afd);
165+
162166
/// Builds LifetimeDependenceInfo from SIL function type
163167
static std::optional<LifetimeDependenceInfo>
164168
fromTypeRepr(LifetimeDependentTypeRepr *lifetimeDependentRepr,

lib/AST/LifetimeDependence.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ getLifetimeDependenceKindFromType(Type sourceType) {
8080
return LifetimeDependenceKind::Inherit;
8181
}
8282

83+
// Warning: this is incorrect for Setter 'newValue' parameters. It should only
84+
// be called for a Setter's 'self'.
8385
static ValueOwnership getLoweredOwnership(AbstractFunctionDecl *afd) {
8486
if (isa<ConstructorDecl>(afd)) {
8587
return ValueOwnership::Owned;
@@ -437,6 +439,13 @@ LifetimeDependenceInfo::infer(AbstractFunctionDecl *afd) {
437439
return std::nullopt;
438440
}
439441

442+
// Setters infer 'self' dependence on 'newValue'.
443+
if (auto accessor = dyn_cast<AccessorDecl>(afd)) {
444+
if (accessor->getAccessorKind() == AccessorKind::Set) {
445+
return inferSetter(accessor);
446+
}
447+
}
448+
440449
if (hasEscapableResultOrYield(afd)) {
441450
return std::nullopt;
442451
}
@@ -542,6 +551,53 @@ LifetimeDependenceInfo::infer(AbstractFunctionDecl *afd) {
542551
afd, resultIndex, *candidateParamIndex, *candidateLifetimeKind);
543552
}
544553

554+
/// Infer LifetimeDependenceInfo on a setter where 'self' is nonescapable.
555+
std::optional<LifetimeDependenceInfo> LifetimeDependenceInfo::inferSetter(
556+
AbstractFunctionDecl *afd) {
557+
558+
auto *param = afd->getParameters()->get(0);
559+
Type paramTypeInContext =
560+
afd->mapTypeIntoContext(param->getInterfaceType());
561+
if (paramTypeInContext->hasError()) {
562+
return std::nullopt;
563+
}
564+
if (paramTypeInContext->isEscapable()) {
565+
return std::nullopt;
566+
}
567+
auto kind = getLifetimeDependenceKindFromType(paramTypeInContext);
568+
return LifetimeDependenceInfo::getForIndex(
569+
afd, /*selfIndex */ afd->getParameters()->size(), 0, kind);
570+
}
571+
572+
/// Infer LifetimeDependenceInfo on a mutating method where 'self' is
573+
/// nonescapable and the result is 'void'. For now, we'll assume that 'self'
574+
/// depends on a single nonescapable argument.
575+
std::optional<LifetimeDependenceInfo> LifetimeDependenceInfo::inferMutatingSelf(
576+
AbstractFunctionDecl *afd) {
577+
std::optional<LifetimeDependenceInfo> dep;
578+
for (unsigned paramIndex : range(afd->getParameters()->size())) {
579+
auto *param = afd->getParameters()->get(paramIndex);
580+
Type paramTypeInContext =
581+
afd->mapTypeIntoContext(param->getInterfaceType());
582+
if (paramTypeInContext->hasError()) {
583+
continue;
584+
}
585+
if (paramTypeInContext->isEscapable()) {
586+
continue;
587+
}
588+
if (dep) {
589+
// Don't infer dependence on multiple nonescapable parameters. We may want
590+
// to do this in the future if dependsOn(self: arg1, arg2) syntax is too
591+
// cumbersome.
592+
return std::nullopt;
593+
}
594+
int selfIndex = afd->getParameters()->size();
595+
dep = LifetimeDependenceInfo::getForIndex(
596+
afd, selfIndex, paramIndex, LifetimeDependenceKind::Inherit);
597+
}
598+
return dep;
599+
}
600+
545601
std::optional<llvm::ArrayRef<LifetimeDependenceInfo>>
546602
LifetimeDependenceInfo::get(AbstractFunctionDecl *afd) {
547603
if (!afd->getASTContext().LangOpts.hasFeature(Feature::NonescapableTypes)) {

test/SIL/implicit_lifetime_dependence.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,25 @@ func tupleLifetimeDependence(_ x: borrowing BufferView) -> (BufferView, BufferVi
168168
return (BufferView(x.ptr, x.c), BufferView(x.ptr, x.c))
169169
}
170170

171+
public struct OuterNE: ~Escapable {
172+
// A public property generates an implicit setter with an infered dependence on 'newValue'.
173+
//
174+
// CHECK-LABEL: sil [transparent] @$s28implicit_lifetime_dependence7OuterNEV5innerAC05InnerE0Vvs : $@convention(method) (@owned OuterNE.InnerNE, _inherit(0) @inout OuterNE) -> () {
175+
public var inner1: InnerNE
176+
177+
// Explicit setter with an infered dependence on 'newValue'.
178+
public var inner2: InnerNE {
179+
get { inner1 }
180+
set { inner1 = newValue }
181+
}
182+
183+
public struct InnerNE: ~Escapable {
184+
init<Owner: ~Escapable & ~Copyable>(
185+
owner: borrowing Owner
186+
) -> dependsOn(owner) Self {}
187+
}
188+
189+
init<Owner: ~Copyable & ~Escapable>(owner: borrowing Owner) -> dependsOn(owner) Self {
190+
self.inner1 = InnerNE(owner: owner)
191+
}
192+
}

test/SILOptimizer/lifetime_dependence/inout.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,24 @@ extension Array {
3737
func mayReassign(span: dependsOn(a) inout Span<Int>, to a: Array<Int>) {
3838
span = a.span()
3939
}
40+
41+
public struct OuterNE: ~Escapable {
42+
// Generates a setter with an inferred dependence on the new value.
43+
public var inner1: InnerNE
44+
45+
// Explicit setter with an infered dependence on 'newValue'.
46+
public var inner2: InnerNE {
47+
_read { yield inner1 }
48+
_modify { yield &inner1 }
49+
}
50+
51+
public struct InnerNE: ~Escapable {
52+
init<Owner: ~Escapable & ~Copyable>(
53+
owner: borrowing Owner
54+
) -> dependsOn(owner) Self {}
55+
}
56+
57+
init<Owner: ~Copyable & ~Escapable>(owner: borrowing Owner) -> dependsOn(owner) Self {
58+
self.inner1 = InnerNE(owner: owner)
59+
}
60+
}

0 commit comments

Comments
 (0)