Skip to content

Commit 3706583

Browse files
authored
Merge pull request #71646 from atrick/lifetime-mutate
LifetimeDependenceDiagnostics: enable mutably borrowed lifetimes.
2 parents 3ecbfcf + 4104394 commit 3706583

File tree

3 files changed

+73
-14
lines changed

3 files changed

+73
-14
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func gatherVariableIntroducers(for value: Value, _ context: Context)
110110
/// argument value would be overly strict.
111111
struct LifetimeDependence : CustomStringConvertible {
112112
enum Scope : CustomStringConvertible {
113-
/// A guaranteed argument whose scope is provided by the caller
113+
/// A guaranteed or inout argument whose scope is provided by the caller
114114
/// and covers the entire function.
115115
case caller(Argument)
116116
/// An access scope.
@@ -120,11 +120,12 @@ struct LifetimeDependence : CustomStringConvertible {
120120
/// An owned value whose OSSA lifetime encloses nonescapable values
121121
case owned(Value)
122122
/// Singly-initialized addressible storage (likely for an
123-
/// immutable address-only value). The entire initialized region
124-
/// is a lifetime (as opposed to an individual access for mutable
125-
/// variables). e.g. A value produced by an @in FunctionArgument
126-
/// or @out apply. We don't need to hande mutable variables,
127-
/// because those require an access scope.
123+
/// immutable address-only value). The lifetime extends until the
124+
/// memory is destroyed. e.g. A value produced by an @in
125+
/// FunctionArgument or @out apply. @inout has caller scope
126+
/// instead because its lifetime does not end inside the callee. A
127+
/// separate analysis diagnoses mutation after the dependence is
128+
/// formed.
128129
///
129130
/// If `initializingStore` is nil, then the `initialAddress` is
130131
/// initialized on function entry.
@@ -346,6 +347,8 @@ extension LifetimeDependence.Scope {
346347
case let .argument(arg):
347348
if arg.convention.isIndirectIn {
348349
self = .initialized(initialAddress: arg, initializingStore: nil)
350+
} else if arg.convention.isInout {
351+
self = .caller(arg)
349352
} else {
350353
// Note: we do not expect arg.convention.isInout because
351354
// mutable variables require an access scope. The .caller
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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: -disable-experimental-parser-round-trip \
7+
// RUN: -enable-experimental-feature NonescapableTypes \
8+
// RUN: -Xllvm -enable-lifetime-dependence-diagnostics
9+
10+
// REQUIRES: swift_in_compiler
11+
12+
@_nonescapable
13+
struct MBV : ~Copyable {
14+
let p: UnsafeMutableRawPointer
15+
let c: Int
16+
17+
@_unsafeNonescapableResult
18+
init(_ p: UnsafeMutableRawPointer, _ c: Int) {
19+
self.p = p
20+
self.c = c
21+
}
22+
23+
subscript(position: Int) -> Int {
24+
get {
25+
let offset = position * MemoryLayout<Int>.stride
26+
return p.loadUnaligned(fromByteOffset: offset, as: Int.self)
27+
}
28+
nonmutating set(newValue) {
29+
let offset = position * MemoryLayout<Int>.stride
30+
p.storeBytes(of: newValue, toByteOffset: offset,
31+
as: Int.self)
32+
}
33+
}
34+
}
35+
36+
struct NC : ~Copyable {
37+
let p: UnsafeMutableRawPointer
38+
let c: Int
39+
40+
// Requires a mutable borrow.
41+
mutating func getMBV() -> _mutate(self) MBV {
42+
MBV(p, c)
43+
}
44+
}
45+
46+
func mbv_set_element(nc: inout NC, e: Int) {
47+
nc.getMBV()[3] = e
48+
}

test/SILOptimizer/lifetime_dependence_util.sil

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ entry(%0 : @owned $C):
7575
return %99 : $()
7676
}
7777

78-
sil [ossa] @dependence_scope : $@convention(thin) (@owned C, @owned D, @guaranteed D, @in_guaranteed D) -> () {
79-
entry(%0 : @owned $C, %1 : @owned $D, %2 : @guaranteed $D, %3 : $*D):
78+
sil [ossa] @dependence_scope : $@convention(thin) (@owned C, @owned D, @guaranteed D, @in_guaranteed D, @inout D) -> () {
79+
entry(%0 : @owned $C, %1 : @owned $D, %2 : @guaranteed $D, %3 : $*D, %4 : $*D):
8080
%move = move_value %1 : $D
8181
%owned_mark = mark_dependence [nonescaping] %0 : $C on %move : $D
8282
specify_test "lifetime_dependence_scope %owned_mark"
@@ -93,7 +93,7 @@ entry(%0 : @owned $C, %1 : @owned $D, %2 : @guaranteed $D, %3 : $*D):
9393
%guaranteed_mark = mark_dependence [nonescaping] %owned_mark : $C on %pair : $PairC
9494
specify_test "lifetime_dependence_scope %guaranteed_mark"
9595
// CHECK-LABEL: dependence_scope: lifetime_dependence_scope with: %guaranteed_mark
96-
// CHECK-NEXT: Caller: %{{.*}} = argument of bb0 : $D
96+
// CHECK-NEXT: Caller: %2 = argument of bb0 : $D
9797
// CHECK-NEXT: Dependent: %{{.*}} = mark_dependence [nonescaping] %{{.*}} : $C on %{{.*}} : $PairC
9898
// CHECK-NEXT: Caller range
9999
// CHECK: dependence_scope: lifetime_dependence_scope with: %guaranteed_mark
@@ -130,21 +130,29 @@ entry(%0 : @owned $C, %1 : @owned $D, %2 : @guaranteed $D, %3 : $*D):
130130
%guaranteed_arg_mark = mark_dependence [nonescaping] %access_mark : $C on %2 : $D
131131
specify_test "lifetime_dependence_scope %guaranteed_arg_mark"
132132
// CHECK-LABEL: dependence_scope: lifetime_dependence_scope with: %guaranteed_arg_mark
133-
// CHECK-NEXT: Caller: %{{.*}} = argument of bb0 : $D
134-
// CHECK-NEXT: Dependent: %{{.*}} = mark_dependence [nonescaping] %{{.*}} : $C on %{{.*}} : $D
133+
// CHECK-NEXT: Caller: %2 = argument of bb0 : $D
134+
// CHECK-NEXT: Dependent: %{{.*}} = mark_dependence [nonescaping] %{{.*}} : $C on %2 : $D
135135
// CHECK-NEXT: Caller range
136136
// CHECK: dependence_scope: lifetime_dependence_scope with: %guaranteed_arg_mark
137137

138138
%inguaranteed_arg_mark = mark_dependence [nonescaping] %guaranteed_arg_mark : $C on %3 : $*D
139139
specify_test "lifetime_dependence_scope %inguaranteed_arg_mark"
140140
// CHECK-LABEL: dependence_scope: lifetime_dependence_scope with: %inguaranteed_arg_mark
141-
// CHECK-NEXT: Initialized: %{{.*}} = argument of bb0 : $*D
142-
// CHECK-NEXT: Dependent: %{{.*}} = mark_dependence [nonescaping] %{{.*}} : $C on %{{.*}} : $*D
141+
// CHECK-NEXT: Initialized: %3 = argument of bb0 : $*D
142+
// CHECK-NEXT: Dependent: %{{.*}} = mark_dependence [nonescaping] %{{.*}} : $C on %3 : $*D
143143
// CHECK-NEXT: begin: %{{.*}} = move_value %{{.*}} : $D
144144
// CHECK-NEXT: ends:
145145
// CHECK: dependence_scope: lifetime_dependence_scope with: %inguaranteed_arg_mark
146146

147-
destroy_value %inguaranteed_arg_mark : $C
147+
%inout_arg_mark = mark_dependence [nonescaping] %inguaranteed_arg_mark : $C on %4 : $*D
148+
specify_test "lifetime_dependence_scope %inout_arg_mark"
149+
// CHECK-LABEL: dependence_scope: lifetime_dependence_scope with: %inout_arg_mark
150+
// CHECK-NEXT: Caller: %4 = argument of bb0 : $*D
151+
// CHECK-NEXT: Dependent: %{{.*}} = mark_dependence [nonescaping] %{{.*}} : $C on %4 : $*D
152+
// CHECK-NEXT: Caller range
153+
// CHECK: dependence_scope: lifetime_dependence_scope with: %inout_arg_mark
154+
155+
destroy_value %inout_arg_mark : $C
148156
end_access %access : $*C
149157
end_apply %token
150158
end_borrow %borrow : $D

0 commit comments

Comments
 (0)