Skip to content

Commit 4a39ed2

Browse files
authored
Merge pull request #82451 from atrick/62-unsafe-mutablespan
[6.2] Fix MutableSpan exclusive access to unsafe pointers
2 parents 11d3547 + 2c98863 commit 4a39ed2

File tree

5 files changed

+79
-16
lines changed

5 files changed

+79
-16
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,16 @@ private struct DiagnoseDependence {
199199
if function.hasUnsafeNonEscapableResult {
200200
return .continueWalk
201201
}
202-
// If the dependence scope is global, then it has immortal lifetime.
203-
if case .global = dependence.scope {
202+
// Check for immortal lifetime.
203+
switch dependence.scope {
204+
case .global:
204205
return .continueWalk
206+
case let .unknown(value):
207+
if value.type.isVoid {
208+
return .continueWalk
209+
}
210+
default:
211+
break
205212
}
206213
// Check that the parameter dependence for this result is the same
207214
// as the current dependence scope.
@@ -352,7 +359,7 @@ private struct LifetimeVariable {
352359

353360
private func getFirstVariableIntroducer(of value: Value, _ context: some Context) -> Value? {
354361
var introducer: Value?
355-
var useDefVisitor = VariableIntroducerUseDefWalker(context, scopedValue: value) {
362+
var useDefVisitor = VariableIntroducerUseDefWalker(context, scopedValue: value, ignoreTrivialCopies: false) {
356363
introducer = $0
357364
return .abortWalk
358365
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ extension LifetimeDependentApply {
9595
struct LifetimeSource {
9696
let targetKind: TargetKind
9797
let convention: LifetimeDependenceConvention
98+
let isInout: Bool
9899
let value: Value
99100
}
100101

@@ -116,7 +117,8 @@ extension LifetimeDependentApply {
116117
guard let dep = applySite.resultDependence(on: operand) else {
117118
continue
118119
}
119-
info.sources.push(LifetimeSource(targetKind: .result, convention: dep, value: operand.value))
120+
let isInout = applySite.convention(of: operand)?.isInout ?? false
121+
info.sources.push(LifetimeSource(targetKind: .result, convention: dep, isInout: isInout, value: operand.value))
120122
}
121123
return info
122124
}
@@ -135,6 +137,7 @@ extension LifetimeDependentApply {
135137
? TargetKind.yieldAddress : TargetKind.yield
136138
info.sources.push(LifetimeSource(targetKind: targetKind,
137139
convention: .scope(addressable: false, addressableForDeps: false),
140+
isInout: false,
138141
value: beginApply.token))
139142
}
140143
for operand in applySite.parameterOperands {
@@ -151,7 +154,9 @@ extension LifetimeDependentApply {
151154
// However this is neccessary for safety when begin_apply gets inlined which will delete the dependence on the token.
152155
for yieldedValue in beginApply.yieldedValues {
153156
let targetKind = yieldedValue.type.isAddress ? TargetKind.yieldAddress : TargetKind.yield
154-
info.sources.push(LifetimeSource(targetKind: targetKind, convention: dep, value: operand.value))
157+
let isInout = applySite.convention(of: operand)?.isInout ?? false
158+
info.sources.push(LifetimeSource(targetKind: targetKind, convention: dep, isInout: isInout,
159+
value: operand.value))
155160
}
156161
}
157162
}
@@ -180,7 +185,8 @@ extension LifetimeDependentApply {
180185
guard let dep = dep else {
181186
continue
182187
}
183-
info.sources.push(LifetimeSource(targetKind: targetKind, convention: dep, value: operand.value))
188+
let isInout = applySite.convention(of: operand)?.isInout ?? false
189+
info.sources.push(LifetimeSource(targetKind: targetKind, convention: dep, isInout: isInout, value: operand.value))
184190
}
185191
return info
186192
}
@@ -223,8 +229,8 @@ private extension LifetimeDependentApply.LifetimeSourceInfo {
223229
return
224230
}
225231
// Create a new dependence on the apply's access to the argument.
226-
for varIntoducer in gatherVariableIntroducers(for: source.value, context) {
227-
let scope = LifetimeDependence.Scope(base: varIntoducer, context)
232+
for varIntroducer in gatherVariableIntroducers(for: source.value, ignoreTrivialCopies: !source.isInout, context) {
233+
let scope = LifetimeDependence.Scope(base: varIntroducer, context)
228234
log("Scoped lifetime from \(source.value)")
229235
log(" scope: \(scope)")
230236
bases.append(scope.parentValue)
@@ -316,11 +322,12 @@ private func insertMarkDependencies(value: Value, initializer: Instruction?,
316322
/// - a variable declaration (begin_borrow [var_decl], move_value [var_decl])
317323
/// - a begin_access for a mutable variable access
318324
/// - the value or address "root" of the dependence chain
319-
func gatherVariableIntroducers(for value: Value, _ context: Context)
325+
func gatherVariableIntroducers(for value: Value, ignoreTrivialCopies: Bool, _ context: Context)
320326
-> SingleInlineArray<Value>
321327
{
322328
var introducers = SingleInlineArray<Value>()
323-
var useDefVisitor = VariableIntroducerUseDefWalker(context, scopedValue: value) {
329+
var useDefVisitor = VariableIntroducerUseDefWalker(context, scopedValue: value,
330+
ignoreTrivialCopies: ignoreTrivialCopies) {
324331
introducers.push($0)
325332
return .continueWalk
326333
}
@@ -403,11 +410,15 @@ struct VariableIntroducerUseDefWalker : LifetimeDependenceUseDefValueWalker, Lif
403410
// Call \p visit rather than calling this directly.
404411
private let visitorClosure: (Value) -> WalkResult
405412

406-
init(_ context: Context, scopedValue: Value, _ visitor: @escaping (Value) -> WalkResult) {
413+
init(_ context: Context, scopedValue: Value, ignoreTrivialCopies: Bool, _ visitor: @escaping (Value) -> WalkResult) {
407414
self.context = context
408-
self.isTrivialScope = scopedValue.type.isAddress
409-
? scopedValue.type.objectType.isTrivial(in: scopedValue.parentFunction)
410-
: scopedValue.isTrivial(context)
415+
if ignoreTrivialCopies {
416+
self.isTrivialScope = scopedValue.type.isAddress
417+
? scopedValue.type.objectType.isTrivial(in: scopedValue.parentFunction)
418+
: scopedValue.isTrivial(context)
419+
} else {
420+
self.isTrivialScope = false
421+
}
411422
self.visitedValues = ValueSet(context)
412423
self.visitorClosure = visitor
413424
}
@@ -467,5 +478,5 @@ let variableIntroducerTest = FunctionTest("variable_introducer") {
467478
function, arguments, context in
468479
let value = arguments.takeValue()
469480
print("Variable introducers of: \(value)")
470-
print(gatherVariableIntroducers(for: value, context))
481+
print(gatherVariableIntroducers(for: value, ignoreTrivialCopies: false, context))
471482
}

SwiftCompilerSources/Sources/Optimizer/Utilities/ForwardingUtils.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ extension ForwardingUseDefWalker {
118118
}
119119
mutating func walkUpDefault(forwarded value: Value, _ path: PathContext)
120120
-> WalkResult {
121-
if let inst = value.forwardingInstruction {
121+
if let inst = value.forwardingInstruction, !inst.forwardedOperands.isEmpty {
122122
return walkUp(instruction: inst, path)
123123
}
124124
if let phi = Phi(value) {

test/SILOptimizer/lifetime_dependence/semantics.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,21 @@ struct InnerTrivial {
106106
struct TrivialHolder {
107107
var p: UnsafePointer<Int>
108108
var pa: UnsafePointer<AddressableInt>
109+
var mp: UnsafeMutablePointer<Int>
109110

110111
var addressableInt: AddressableInt { unsafeAddress { pa } }
111112

112113
@_lifetime(borrow self)
113114
borrowing func span() -> Span<Int> {
114115
Span(base: p, count: 1)
115116
}
117+
118+
@_lifetime(&self)
119+
mutating func mutableSpan() -> MutableSpan<Int> {
120+
MutableSpan(base: mp, count: 1)
121+
}
122+
123+
mutating func modify() {}
116124
}
117125

118126
struct Holder {
@@ -456,6 +464,28 @@ func testInoutMutableBorrow(a: inout [Int]) -> MutableSpan<Int> {
456464
a.mutableSpan()
457465
}
458466

467+
@_lifetime(&h)
468+
func testTrivialWriteConflict(h: inout TrivialHolder) -> MutableSpan<Int> {
469+
let span = h.mutableSpan() // expected-error{{overlapping accesses to 'h', but modification requires exclusive access; consider copying to a local variable}}
470+
h.modify() // expected-note{{conflicting access is here}}
471+
return span
472+
}
473+
474+
func makeMutableSpan(_ p: inout UnsafeMutablePointer<UInt8>) -> MutableSpan<UInt8> {
475+
MutableSpan(base: p, count: 1)
476+
}
477+
478+
struct TestInoutUnsafePointerExclusivity {
479+
var pointer: UnsafeMutablePointer<UInt8>
480+
481+
@_lifetime(&self)
482+
mutating func testInoutUnsafePointerExclusivity() -> MutableSpan<UInt8> {
483+
let span1 = makeMutableSpan(&self.pointer) // expected-error{{overlapping accesses to 'self.pointer', but modification requires exclusive access; consider copying to a local variable}}
484+
_ = makeMutableSpan(&self.pointer) // expected-note{{conflicting access is here}}
485+
return span1
486+
}
487+
}
488+
459489
// =============================================================================
460490
// Scoped dependence on property access
461491
// =============================================================================

test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ public struct NEInt: ~Escapable {
106106
init(owner: borrowing NCInt) {
107107
self.i = owner.i
108108
}
109+
110+
@_lifetime(immortal)
111+
init(immortal i: Int) {
112+
self.i = i
113+
}
109114
}
110115

111116
struct TestDeinitCallsAddressor: ~Copyable, ~Escapable {
@@ -228,3 +233,13 @@ class ClassStorage {
228233
_ = ne
229234
}
230235
}
236+
237+
// =============================================================================
238+
// Immortal
239+
// =============================================================================
240+
241+
@_lifetime(immortal)
242+
func testVoid() -> NEInt {
243+
let ne = NEInt(immortal: 3)
244+
return _overrideLifetime(ne, borrowing: ())
245+
}

0 commit comments

Comments
 (0)