Skip to content

Commit 382529c

Browse files
authored
Merge pull request #85232 from atrick/rdar159793739-lifedep-cast
LifetimeDependenceDiagnostics: handle dynamic casting of Span<T>
2 parents a8cef44 + 5943d40 commit 382529c

File tree

5 files changed

+60
-16
lines changed

5 files changed

+60
-16
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceDiagnostics.swift

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -128,27 +128,36 @@ private func analyze(dependence: LifetimeDependence, _ context: FunctionPassCont
128128
var range = dependence.computeRange(context)
129129
defer { range?.deinitialize() }
130130

131-
var error = false
132-
let diagnostics =
133-
DiagnoseDependence(dependence: dependence, range: range,
134-
onError: { error = true }, context: context)
131+
let diagnostics = DiagnoseDependence(dependence: dependence, range: range, context: context)
135132

136133
// Check each lifetime-dependent use via a def-use visitor
137134
var walker = DiagnoseDependenceWalker(diagnostics, context)
138135
defer { walker.deinitialize() }
139136
let result = walker.walkDown(dependence: dependence)
140-
// The walk may abort without a diagnostic error.
141-
assert(!error || result == .abortWalk)
137+
assert(result == .continueWalk || diagnostics.errorStatus != nil,
138+
"Lifetime diagnostics failed without raising an error")
142139
return result == .continueWalk
143140
}
144141

145142
/// Analyze and diagnose a single LifetimeDependence.
146-
private struct DiagnoseDependence {
143+
private class DiagnoseDependence {
144+
enum ErrorStatus {
145+
case diagnostic
146+
case unresolvedDependence
147+
}
148+
147149
let dependence: LifetimeDependence
148150
let range: InstructionRange?
149-
let onError: ()->()
150151
let context: FunctionPassContext
151152

153+
init(dependence: LifetimeDependence, range: InstructionRange?, context: FunctionPassContext) {
154+
self.dependence = dependence
155+
self.range = range
156+
self.context = context
157+
}
158+
159+
var errorStatus: ErrorStatus? = nil
160+
152161
var function: Function { dependence.function }
153162

154163
func diagnose(_ position: SourceLoc?, _ id: DiagID,
@@ -266,9 +275,10 @@ private struct DiagnoseDependence {
266275
func reportError(escapingValue: Value, user: Instruction, diagID: DiagID) {
267276
// If the dependent value is Escapable, then mark_dependence resolution fails, but this is not a diagnostic error.
268277
if dependence.dependentValue.isEscapable {
278+
errorStatus = .unresolvedDependence
269279
return
270280
}
271-
onError()
281+
errorStatus = .diagnostic
272282

273283
// Identify the escaping variable.
274284
let escapingVar = LifetimeVariable(definedBy: escapingValue, user: user, context)
@@ -598,10 +608,10 @@ extension DiagnoseDependenceWalker : LifetimeDependenceDefUseWalker {
598608
return .continueWalk
599609
}
600610

601-
// Override AddressUseVisitor here because LifetimeDependenceDefUseWalker
602-
// returns .abortWalk, and we want a more useful crash report.
611+
// Override AddressUseVisitor here because LifetimeDependenceDefUseWalker returns .abortWalk and
612+
// DiagnoseDependenceWalker requires a diagnostic error for all aborts.
603613
mutating func unknownAddressUse(of operand: Operand) -> WalkResult {
604614
diagnostics.reportUnknown(operand: operand)
605-
return .continueWalk
615+
return .abortWalk
606616
}
607617
}

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,9 @@ extension LifetimeDependence.Scope {
622622
/// walkDown(dependence: LifetimeDependence)
623623
///
624624
/// Note: this may visit values that are not dominated by `dependence` because of dependent phi operands.
625+
///
626+
/// WARNING: Do not return .abortWalk at this level or below. .abortWalk may only be returned by an overridden callback
627+
/// or the default implementation of unknownAddressUse().
625628
protocol LifetimeDependenceDefUseWalker : ForwardingDefUseWalker,
626629
OwnershipUseVisitor,
627630
AddressUseVisitor {
@@ -1069,12 +1072,14 @@ extension LifetimeDependenceDefUseWalker {
10691072
return loadedAddressUse(of: localAccess.operand!, intoValue: load)
10701073
case let copyAddr as SourceDestAddrInstruction:
10711074
return loadedAddressUse(of: localAccess.operand!, intoAddress: copyAddr.destinationOperand)
1075+
case let castAddr as CheckedCastAddrBranchInst:
1076+
return loadedAddressUse(of: localAccess.operand!, intoAddress: castAddr.destinationOperand)
10721077
case is SwitchEnumAddrInst:
10731078
// switch_enum_addr does not produce any values. Subsequent uses of the address (unchecked_enum_data_addr)
10741079
// directly use the original address.
10751080
return .continueWalk
10761081
default:
1077-
return .abortWalk
1082+
return unknownAddressUse(of: localAccess.operand!)
10781083
}
10791084
case .dependenceSource:
10801085
switch localAccess.instruction! {
@@ -1086,7 +1091,7 @@ extension LifetimeDependenceDefUseWalker {
10861091
case let md as MarkDependenceAddrInst:
10871092
return loadedAddressUse(of: localAccess.operand!, intoAddress: md.addressOperand)
10881093
default:
1089-
return .abortWalk
1094+
return unknownAddressUse(of: localAccess.operand!)
10901095
}
10911096
case .dependenceDest:
10921097
// Simply a marker that indicates the start of an in-memory dependent value. If this was a mark_dependence, uses

SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,13 @@ extension LocalVariableAccessWalker: AddressUseVisitor {
525525
// switch_enum_addr is truly a leaf address use. It does not produce a new value. But in every other respect it is
526526
// like a load.
527527
visit(LocalVariableAccess(.load, operand))
528+
case let castBr as CheckedCastAddrBranchInst:
529+
if operand == castBr.sourceOperand {
530+
visit(LocalVariableAccess(.load, operand))
531+
} else {
532+
assert(operand == castBr.destinationOperand)
533+
visit(LocalVariableAccess(.store, operand))
534+
}
528535
case is DeallocStackInst:
529536
break
530537
default:

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,8 +2049,11 @@ final public class CheckedCastBranchInst : TermInst, UnaryInstruction {
20492049
}
20502050

20512051
final public class CheckedCastAddrBranchInst : TermInst {
2052-
public var source: Value { operands[0].value }
2053-
public var destination: Value { operands[1].value }
2052+
public var sourceOperand: Operand { return operands[0] }
2053+
public var destinationOperand: Operand { return operands[1] }
2054+
2055+
public var source: Value { sourceOperand.value }
2056+
public var destination: Value { destinationOperand.value }
20542057

20552058
public var sourceFormalType: CanonicalType {
20562059
CanonicalType(bridged: bridged.CheckedCastAddrBranch_getSourceFormalType())

test/SILOptimizer/lifetime_dependence/verify_diagnostics.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,3 +429,22 @@ extension MutableSpan {
429429
return false
430430
}
431431
}
432+
433+
// =============================================================================
434+
// Local variable analysis - address uses
435+
// =============================================================================
436+
437+
func dynamicCastGood<T>(_ span: Span<T>) {
438+
if let intSpan = span as? Span<Int> {
439+
_ = intSpan
440+
}
441+
}
442+
443+
@_lifetime(immortal)
444+
func dynamicCastBad<T>(_ span: Span<T>) -> Span<Int> {
445+
if let intSpan = span as? Span<Int> { // expected-error{{lifetime-dependent variable 'intSpan' escapes its scope}}
446+
// expected-note @-2{{it depends on the lifetime of argument 'span'}}
447+
return intSpan // expected-note{{this use causes the lifetime-dependent value to escape}}
448+
}
449+
return Span<Int>()
450+
}

0 commit comments

Comments
 (0)