Skip to content

Commit ba9f12a

Browse files
committed
LifetimeDependenceDiagnostics support for inout reassignment.
This relies on parameter dependence targets. i.e. dependsOn in parameter position instead of result position.
1 parent 058d22b commit ba9f12a

File tree

4 files changed

+83
-9
lines changed

4 files changed

+83
-9
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,22 @@ private struct DiagnoseDependence {
131131
reportEscaping(operand: operand)
132132
}
133133

134+
func checkInoutResult(argument inoutArg: FunctionArgument, result operand: Operand) -> WalkResult {
135+
// Check that the parameter dependence for this inout argument is the same as the current dependence scope.
136+
if let sourceArg = dependence.scope.parentValue as? FunctionArgument {
137+
// If the inout result is also the inout source, then it's always ok.
138+
if inoutArg == sourceArg {
139+
return .continueWalk
140+
}
141+
if function.argumentConventions.getDependence(target: inoutArg.index, source: sourceArg.index) != nil {
142+
// The inout result depends on a lifetime that is inherited or borrowed in the caller.
143+
return .continueWalk
144+
}
145+
}
146+
reportEscaping(operand: operand)
147+
return .abortWalk
148+
}
149+
134150
func checkFunctionResult(operand: Operand) -> WalkResult {
135151

136152
if function.hasUnsafeNonEscapableResult {
@@ -151,7 +167,7 @@ private struct DiagnoseDependence {
151167
if dependence.isUnsafeApplyResult, function.hasResultDependence {
152168
return .continueWalk
153169
}
154-
// Check that the argument dependence for this result is the same
170+
// Check that the parameter dependence for this result is the same
155171
// as the current dependence scope.
156172
if let arg = dependence.scope.parentValue as? FunctionArgument,
157173
function.argumentConventions[resultDependsOn: arg.index] != nil {
@@ -363,12 +379,16 @@ extension DiagnoseDependenceWalker : LifetimeDependenceDefUseWalker {
363379
return .abortWalk
364380
}
365381

382+
mutating func inoutDependence(argument: FunctionArgument, on operand: Operand) -> WalkResult {
383+
return diagnostics.checkInoutResult(argument: argument, result: operand)
384+
}
385+
366386
mutating func returnedDependence(result: Operand) -> WalkResult {
367387
return diagnostics.checkFunctionResult(operand: result)
368388
}
369389

370390
mutating func returnedDependence(address: FunctionArgument,
371-
using operand: Operand) -> WalkResult {
391+
on operand: Operand) -> WalkResult {
372392
return diagnostics.checkFunctionResult(operand: operand)
373393
}
374394

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceScopeFixup.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,18 @@ private struct LifetimeDependenceScopeFixupWalker : LifetimeDependenceDefUseWalk
234234
return .continueWalk
235235
}
236236

237+
mutating func inoutDependence(argument: FunctionArgument, on operand: Operand) -> WalkResult {
238+
dependsOnCaller = true
239+
return visitor(operand)
240+
}
241+
237242
mutating func returnedDependence(result operand: Operand) -> WalkResult {
238243
dependsOnCaller = true
239244
return visitor(operand)
240245
}
241246

242247
mutating func returnedDependence(address: FunctionArgument,
243-
using operand: Operand) -> WalkResult {
248+
on operand: Operand) -> WalkResult {
244249
dependsOnCaller = true
245250
return visitor(operand)
246251
}

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ extension LifetimeDependence.Scope {
348348
case let .argument(arg):
349349
if arg.convention.isIndirectIn {
350350
self = .initialized(initialAddress: arg, initializingStore: nil)
351-
} else if arg.convention.isIndirectOut {
351+
} else if arg.convention.isIndirectOut || arg.convention.isInout {
352352
// TODO: verify that @out values are never reassigned.
353353
self = .caller(arg)
354354
} else {
@@ -772,8 +772,9 @@ extension LifetimeDependenceUseDefWalker {
772772
/// leafUse(of: Operand) -> WalkResult
773773
/// deadValue(_ value: Value, using operand: Operand?) -> WalkResult
774774
/// escapingDependence(on operand: Operand) -> WalkResult
775+
/// inoutDependence(argument: FunctionArgument, on: Operand) -> WalkResult
775776
/// returnedDependence(result: Operand) -> WalkResult
776-
/// returnedDependence(address: FunctionArgument, using: Operand) -> WalkResult
777+
/// returnedDependence(address: FunctionArgument, on: Operand) -> WalkResult
777778
/// yieldedDependence(result: Operand) -> WalkResult
778779
/// Start walking:
779780
/// walkDown(root: Value)
@@ -791,9 +792,13 @@ protocol LifetimeDependenceDefUseWalker : ForwardingDefUseWalker,
791792

792793
mutating func escapingDependence(on operand: Operand) -> WalkResult
793794

795+
// Assignment to an inout argument. This does not include the indirect out result, which is considered a return
796+
// value.
797+
mutating func inoutDependence(argument: FunctionArgument, on: Operand) -> WalkResult
798+
794799
mutating func returnedDependence(result: Operand) -> WalkResult
795800

796-
mutating func returnedDependence(address: FunctionArgument, using: Operand) -> WalkResult
801+
mutating func returnedDependence(address: FunctionArgument, on: Operand) -> WalkResult
797802

798803
mutating func yieldedDependence(result: Operand) -> WalkResult
799804
}
@@ -1072,7 +1077,7 @@ extension LifetimeDependenceDefUseWalker {
10721077
if arg.convention.isIndirectIn || arg.convention.isInout {
10731078
allocation = arg
10741079
} else if arg.convention.isIndirectOut, !arg.isEscapable {
1075-
return returnedDependence(address: arg, using: operand)
1080+
return returnedDependence(address: arg, on: operand)
10761081
}
10771082
break
10781083
case .global, .class, .tail, .yield, .storeBorrow, .pointer, .unidentified:
@@ -1140,7 +1145,7 @@ extension LifetimeDependenceDefUseWalker {
11401145
case .outgoingArgument:
11411146
let arg = allocation as! FunctionArgument
11421147
assert(arg.type.isAddress, "returned local must be allocated with an indirect argument")
1143-
return returnedDependence(address: arg, using: initialValue)
1148+
return inoutDependence(argument: arg, on: initialValue)
11441149
case .incomingArgument:
11451150
fatalError("Incoming arguments are never reachable")
11461151
}
@@ -1253,13 +1258,18 @@ private struct LifetimeDependenceUsePrinter : LifetimeDependenceDefUseWalker {
12531258
return .continueWalk
12541259
}
12551260

1261+
mutating func inoutDependence(argument: FunctionArgument, on operand: Operand) -> WalkResult {
1262+
print("Out use: \(operand) in: \(argument)")
1263+
return .continueWalk
1264+
}
1265+
12561266
mutating func returnedDependence(result: Operand) -> WalkResult {
12571267
print("Returned use: \(result)")
12581268
return .continueWalk
12591269
}
12601270

12611271
mutating func returnedDependence(address: FunctionArgument,
1262-
using operand: Operand) -> WalkResult {
1272+
on operand: Operand) -> WalkResult {
12631273
print("Returned use: \(operand) in: \(address)")
12641274
return .continueWalk
12651275
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %target-swift-frontend %s -emit-sil \
2+
// RUN: -o /dev/null \
3+
// RUN: -verify \
4+
// RUN: -sil-verify-all \
5+
// RUN: -module-name test \
6+
// RUN: -enable-experimental-feature NonescapableTypes
7+
8+
// REQUIRES: asserts
9+
// REQUIRES: swift_in_compiler
10+
11+
struct Span<T>: ~Escapable {
12+
private var base: UnsafePointer<T>
13+
private var count: Int
14+
15+
@_unsafeNonescapableResult
16+
init(base: UnsafePointer<T>, count: Int) {
17+
self.base = base
18+
self.count = count
19+
}
20+
21+
init<S>(base: UnsafePointer<T>, count: Int, generic: borrowing S) -> dependsOn(generic) Self {
22+
self.base = base
23+
self.count = count
24+
}
25+
}
26+
27+
extension Array {
28+
// TODO: comment out dependsOn(scoped)
29+
borrowing func span() -> /* dependsOn(scoped self) */ Span<Element> {
30+
/* not the real implementation */
31+
let p = self.withUnsafeBufferPointer { $0.baseAddress! }
32+
return Span(base: p, count: 1)
33+
}
34+
}
35+
36+
// Reassign an inout argument to a value that depends on the lifetime of another argument.
37+
func mayReassign(span: dependsOn(a) inout Span<Int>, to a: Array<Int>) {
38+
span = a.span()
39+
}

0 commit comments

Comments
 (0)