Skip to content

Commit 3534c5a

Browse files
authored
Merge pull request swiftlang#75225 from atrick/init-proj
LocalVariableUtils: handle projections inside initializers.
2 parents 24b0290 + c41dc33 commit 3534c5a

File tree

2 files changed

+69
-17
lines changed

2 files changed

+69
-17
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,11 @@ class LocalVariableAccessInfo: CustomStringConvertible {
149149
case .load:
150150
self._isFullyAssigned = false
151151
case .store:
152-
self._isFullyAssigned = true
152+
if let store = localAccess.instruction as? StoringInstruction {
153+
self._isFullyAssigned = LocalVariableAccessInfo.isBase(address: store.source)
154+
} else {
155+
self._isFullyAssigned = true
156+
}
153157
case .apply:
154158
let apply = localAccess.instruction as! FullApplySite
155159
if let convention = apply.convention(of: localAccess.operand!) {
@@ -191,6 +195,18 @@ class LocalVariableAccessInfo: CustomStringConvertible {
191195
return "full-assign: \(_isFullyAssigned == nil ? "unknown" : String(describing: _isFullyAssigned!)) "
192196
+ "\(access)"
193197
}
198+
199+
// Does this address correspond to the local variable's base address? Any writes to this address will be a full
200+
// assignment. This should match any instructions that the LocalVariableAccessMap initializer below recognizes as an
201+
// allocation.
202+
static private func isBase(address: Value) -> Bool {
203+
switch address {
204+
case is AllocBoxInst, is AllocStackInst, is BeginAccessInst:
205+
return true
206+
default:
207+
return false
208+
}
209+
}
194210
}
195211

196212
/// Model the formal accesses of an addressible variable introduced by an alloc_box, alloc_stack, or indirect
@@ -363,24 +379,14 @@ extension LocalVariableAccessWalker: AddressUseVisitor {
363379
return .continueWalk
364380
}
365381

366-
// Handle storage type projections, like MarkUninitializedInst. Path projections should not be visited. They only
367-
// occur inside the access.
382+
// Handle storage type projections, like MarkUninitializedInst. Path projections are visited for field
383+
// initialization because SILGen does not emit begin_access [init] consistently.
368384
//
369-
// Exception: stack-allocated temporaries may be treated like local variables for the purpose of finding all
370-
// uses. Such temporaries do not have access scopes, so we need to walk down any projection that may be used to
371-
// initialize the temporary.
385+
// Stack-allocated temporaries are also treated like local variables for the purpose of finding all uses. Such
386+
// temporaries do not have access scopes, so we need to walk down any projection that may be used to initialize the
387+
// temporary.
372388
mutating func projectedAddressUse(of operand: Operand, into value: Value) -> WalkResult {
373-
// TODO: we need an abstraction for path projections. For local variables, these cannot occur outside of an access.
374-
switch operand.instruction {
375-
case is StructElementAddrInst, is TupleElementAddrInst, is IndexAddrInst, is TailAddrInst,
376-
is UncheckedTakeEnumDataAddrInst, is OpenExistentialAddrInst:
377-
return .abortWalk
378-
// Projections used to initialize a temporary
379-
case is InitEnumDataAddrInst, is InitExistentialAddrInst:
380-
fallthrough
381-
default:
382-
return walkDownAddressUses(address: value)
383-
}
389+
return walkDownAddressUses(address: value)
384390
}
385391

386392
mutating func scopedAddressUse(of operand: Operand) -> WalkResult {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
struct Wrapper<T: BitwiseCopyable>: ~Escapable {
28+
private let span: Span<T>
29+
30+
init(span: borrowing Span<T>) {
31+
self.span = copy span
32+
}
33+
}
34+
35+
struct SuperWrapper<T: BitwiseCopyable>: ~Escapable {
36+
private let wrapper: Wrapper<T>
37+
38+
// An extra field forces a projection on 'self' within the initializer without any access scope.
39+
var depth: Int = 0
40+
41+
// Make sure that LocalVariableUtils can successfully analyze 'self'. That's required to determine that the assignment
42+
// of `wrapper` is returned without escaping
43+
init(span: borrowing Span<T>) {
44+
self.wrapper = Wrapper(span: span)
45+
}
46+
}

0 commit comments

Comments
 (0)