Skip to content

Commit fc60925

Browse files
committed
LocalVariableUtils: add support for temporary enum initialization.
1 parent ec437fa commit fc60925

File tree

3 files changed

+228
-3
lines changed

3 files changed

+228
-3
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/LocalVariableUtils.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,12 +365,19 @@ extension LocalVariableAccessWalker: AddressUseVisitor {
365365

366366
// Handle storage type projections, like MarkUninitializedInst. Path projections should not be visited. They only
367367
// occur inside the access.
368+
//
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.
368372
mutating func projectedAddressUse(of operand: Operand, into value: Value) -> WalkResult {
369373
// TODO: we need an abstraction for path projections. For local variables, these cannot occur outside of an access.
370374
switch operand.instruction {
371-
case is StructExtractInst, is TupleElementAddrInst, is IndexAddrInst, is TailAddrInst, is InitEnumDataAddrInst,
372-
is UncheckedTakeEnumDataAddrInst, is InitExistentialAddrInst, is OpenExistentialAddrInst:
375+
case is StructElementAddrInst, is TupleElementAddrInst, is IndexAddrInst, is TailAddrInst,
376+
is UncheckedTakeEnumDataAddrInst, is OpenExistentialAddrInst:
373377
return .abortWalk
378+
// Projections used to initialize a temporary
379+
case is InitEnumDataAddrInst, is InitExistentialAddrInst:
380+
fallthrough
374381
default:
375382
return walkDownAddressUses(address: value)
376383
}
@@ -401,7 +408,9 @@ extension LocalVariableAccessWalker: AddressUseVisitor {
401408

402409
mutating func leafAddressUse(of operand: Operand) -> WalkResult {
403410
switch operand.instruction {
404-
case is StoringInstruction, is SourceDestAddrInstruction, is DestroyAddrInst:
411+
case is StoringInstruction, is SourceDestAddrInstruction, is DestroyAddrInst, is DeinitExistentialAddrInst,
412+
is InjectEnumAddrInst, is TupleAddrConstructorInst, is InitBlockStorageHeaderInst, is PackElementSetInst:
413+
// Handle instructions that initialize both temporaries and local variables.
405414
visit(LocalVariableAccess(.store, operand))
406415
case is DeallocStackInst:
407416
break

test/SILOptimizer/lifetime_dependence_diagnostics.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ public struct NEInt: ~Escapable {
4848
}
4949
}
5050

51+
public enum NEOptional<Wrapped: ~Escapable>: ~Escapable {
52+
case none
53+
case some(Wrapped)
54+
}
55+
56+
extension NEOptional where Wrapped: ~Escapable {
57+
// Test that enum initialization passes diagnostics.
58+
public init(_ some: consuming Wrapped) { self = .some(some) }
59+
}
60+
5161
func takeClosure(_: () -> ()) {}
5262

5363
// No mark_dependence is needed for a inherited scope.
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// RUN: %target-swift-frontend %s -emit-sil \
2+
// RUN: -verify \
3+
// RUN: -sil-verify-all \
4+
// RUN: -module-name test \
5+
// RUN: -enable-experimental-feature NoncopyableGenerics \
6+
// RUN: -enable-experimental-feature NonescapableTypes \
7+
// RUN: -enable-experimental-feature BorrowingSwitch
8+
9+
// REQUIRES: asserts
10+
// REQUIRES: swift_in_compiler
11+
12+
// Simply test that it is possible for a module to define a pseudo-Optional type without triggering any compiler errors.
13+
14+
public protocol ExpressibleByNilLiteral: ~Copyable & ~Escapable {
15+
@_unsafeNonescapableResult
16+
init(nilLiteral: ())
17+
}
18+
19+
@frozen
20+
public enum Nillable<Wrapped: ~Copyable & ~Escapable>: ~Copyable & ~Escapable {
21+
case none
22+
case some(Wrapped)
23+
}
24+
25+
extension Nillable: Copyable where Wrapped: ~Escapable /* & Copyable */ {}
26+
27+
extension Nillable: Escapable where Wrapped: ~Copyable /* & Escapable */ {}
28+
29+
extension Nillable: Sendable where Wrapped: ~Copyable & ~Escapable & Sendable { }
30+
31+
extension Nillable: _BitwiseCopyable where Wrapped: _BitwiseCopyable { }
32+
33+
extension Nillable: ExpressibleByNilLiteral where Wrapped: ~Copyable & ~Escapable {
34+
@_transparent
35+
@_unsafeNonescapableResult
36+
public init(nilLiteral: ()) {
37+
self = .none
38+
}
39+
}
40+
41+
extension Nillable where Wrapped: ~Copyable & ~Escapable {
42+
@_transparent
43+
public init(_ some: consuming Wrapped) { self = .some(some) }
44+
}
45+
46+
extension Nillable where Wrapped: ~Copyable {
47+
public consuming func _consumingMap<U: ~Copyable, E: Error>(
48+
_ transform: (consuming Wrapped) throws(E) -> U
49+
) throws(E) -> U? {
50+
switch consume self {
51+
case .some(let y):
52+
return .some(try transform(y))
53+
case .none:
54+
return .none
55+
}
56+
}
57+
58+
public borrowing func _borrowingMap<U: ~Copyable, E: Error>(
59+
_ transform: (borrowing Wrapped) throws(E) -> U
60+
) throws(E) -> U? {
61+
switch self {
62+
case .some(_borrowing y):
63+
return .some(try transform(y))
64+
case .none:
65+
return .none
66+
}
67+
}
68+
}
69+
70+
extension Nillable where Wrapped: ~Copyable {
71+
public consuming func _consumingFlatMap<U: ~Copyable, E: Error>(
72+
_ transform: (consuming Wrapped) throws(E) -> U?
73+
) throws(E) -> U? {
74+
switch consume self {
75+
case .some(let y):
76+
return try transform(consume y)
77+
case .none:
78+
return .none
79+
}
80+
}
81+
82+
public func _borrowingFlatMap<U: ~Copyable, E: Error>(
83+
_ transform: (borrowing Wrapped) throws(E) -> U?
84+
) throws(E) -> U? {
85+
switch self {
86+
case .some(_borrowing y):
87+
return try transform(y)
88+
case .none:
89+
return .none
90+
}
91+
}
92+
}
93+
94+
extension Nillable where Wrapped: ~Copyable {
95+
public consuming func _consumingUnsafelyUnwrap() -> Wrapped {
96+
switch consume self {
97+
case .some(let x):
98+
return x
99+
case .none:
100+
fatalError("consumingUsafelyUnwrap of nil optional")
101+
}
102+
}
103+
}
104+
105+
extension Optional where Wrapped: ~Copyable {
106+
internal consuming func _consumingUncheckedUnwrapped() -> Wrapped {
107+
if let x = self {
108+
return x
109+
}
110+
fatalError("_uncheckedUnwrapped of nil optional")
111+
}
112+
}
113+
114+
extension Optional where Wrapped: ~Copyable {
115+
public mutating func _take() -> Self {
116+
let result = consume self
117+
self = nil
118+
return result
119+
}
120+
}
121+
122+
extension Optional where Wrapped: ~Copyable {
123+
public static func ~=(
124+
lhs: _OptionalNilComparisonType,
125+
rhs: borrowing Wrapped?
126+
) -> Bool {
127+
switch rhs {
128+
case .some:
129+
return false
130+
case .none:
131+
return true
132+
}
133+
}
134+
135+
public static func ==(
136+
lhs: borrowing Wrapped?,
137+
rhs: _OptionalNilComparisonType
138+
) -> Bool {
139+
switch lhs {
140+
case .some:
141+
return false
142+
case .none:
143+
return true
144+
}
145+
}
146+
147+
public static func !=(
148+
lhs: borrowing Wrapped?,
149+
rhs: _OptionalNilComparisonType
150+
) -> Bool {
151+
switch lhs {
152+
case .some:
153+
return true
154+
case .none:
155+
return false
156+
}
157+
}
158+
159+
public static func ==(
160+
lhs: _OptionalNilComparisonType,
161+
rhs: borrowing Wrapped?
162+
) -> Bool {
163+
switch rhs {
164+
case .some:
165+
return false
166+
case .none:
167+
return true
168+
}
169+
}
170+
171+
public static func !=(
172+
lhs: _OptionalNilComparisonType,
173+
rhs: borrowing Wrapped?
174+
) -> Bool {
175+
switch rhs {
176+
case .some:
177+
return true
178+
case .none:
179+
return false
180+
}
181+
}
182+
}
183+
184+
public func ?? <T: ~Copyable>(
185+
optional: consuming T?,
186+
defaultValue: @autoclosure () throws -> T
187+
) rethrows -> T {
188+
switch consume optional {
189+
case .some(let value):
190+
return value
191+
case .none:
192+
return try defaultValue()
193+
}
194+
}
195+
196+
public func ?? <T: ~Copyable>(
197+
optional: consuming T?,
198+
defaultValue: @autoclosure () throws -> T?
199+
) rethrows -> T? {
200+
switch consume optional {
201+
case .some(let value):
202+
return value
203+
case .none:
204+
return try defaultValue()
205+
}
206+
}

0 commit comments

Comments
 (0)