Skip to content

Commit f5f715f

Browse files
committed
SILGen: Support multiple-entry case blocks with address-only bindings
Fixes <rdar://problem/30870493>, <https://bugs.swift.org/browse/SR-4163>.
1 parent e6a984d commit f5f715f

File tree

5 files changed

+381
-106
lines changed

5 files changed

+381
-106
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,6 @@ ERROR(writeback_overlap_subscript,none,
8484
NOTE(writebackoverlap_note,none,
8585
"concurrent writeback occurred here", ())
8686

87-
ERROR(addressonly_type_used_in_multipattern_case,none,
88-
"matching %select{type '%1'|a protocol value|a generic value}0 in multiple patterns "
89-
"is not yet supported; use separate cases instead",
90-
(unsigned, Type))
91-
9287
ERROR(inout_argument_alias,none,
9388
"inout arguments are not allowed to alias each other", ())
9489
NOTE(previous_inout_alias,none,

lib/SILGen/SILGenPattern.cpp

Lines changed: 77 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ class PatternMatchEmission {
408408
CleanupsDepth PatternMatchStmtDepth;
409409
llvm::MapVector<CaseStmt*, std::pair<SILBasicBlock*, bool>> SharedCases;
410410

411+
llvm::DenseMap<VarDecl*, SILValue> Temporaries;
412+
411413
using CompletionHandlerTy =
412414
llvm::function_ref<void(PatternMatchEmission &, ArgArray, ClauseRow &)>;
413415
CompletionHandlerTy CompletionHandler;
@@ -416,14 +418,17 @@ class PatternMatchEmission {
416418
PatternMatchEmission(SILGenFunction &SGF, Stmt *S,
417419
CompletionHandlerTy completionHandler)
418420
: SGF(SGF), PatternMatchStmt(S),
419-
PatternMatchStmtDepth(SGF.getCleanupsDepth()),
420421
CompletionHandler(completionHandler) {}
421422

422423
void emitDispatch(ClauseMatrix &matrix, ArgArray args,
423424
const FailureHandler &failure);
424425

425426
void initSharedCaseBlockDest(CaseStmt *caseBlock, bool hasFallthroughTo);
426427

428+
void emitAddressOnlyAllocations();
429+
430+
void emitAddressOnlyInitialization(VarDecl *dest, SILValue value);
431+
427432
JumpDest getSharedCaseBlockDest(CaseStmt *caseStmt);
428433

429434
void emitSharedCaseBlocks();
@@ -2363,6 +2368,42 @@ JumpDest PatternMatchEmission::getSharedCaseBlockDest(CaseStmt *caseBlock) {
23632368
CleanupLocation(PatternMatchStmt));
23642369
}
23652370

2371+
void PatternMatchEmission::emitAddressOnlyAllocations() {
2372+
for (auto &entry: SharedCases) {
2373+
CaseStmt *caseBlock = entry.first;
2374+
2375+
// If we have a shared case with bound decls, then the 0th pattern has the
2376+
// order of variables that are the incoming BB arguments. Setup the VarLocs
2377+
// to point to the incoming args and setup initialization so any args needing
2378+
// cleanup will get that as well.
2379+
if (caseBlock->hasBoundDecls()) {
2380+
auto pattern = caseBlock->getCaseLabelItems()[0].getPattern();
2381+
pattern->forEachVariable([&](VarDecl *V) {
2382+
if (!V->hasName())
2383+
return;
2384+
2385+
SILType ty = SGF.getLoweredType(V->getType());
2386+
2387+
if (ty.isAddressOnly(SGF.F.getModule())) {
2388+
assert(!Temporaries[V]);
2389+
Temporaries[V] = SGF.emitTemporaryAllocation(V, ty);
2390+
return;
2391+
}
2392+
});
2393+
}
2394+
}
2395+
2396+
// Now we have all of our cleanups entered, so we can record the
2397+
// depth.
2398+
PatternMatchStmtDepth = SGF.getCleanupsDepth();
2399+
}
2400+
2401+
void PatternMatchEmission::
2402+
emitAddressOnlyInitialization(VarDecl *dest, SILValue value) {
2403+
auto found = Temporaries.find(dest);
2404+
assert(found != Temporaries.end());
2405+
SGF.B.createCopyAddr(dest, value, found->second, IsNotTake, IsInitialization);
2406+
}
23662407

23672408
/// Emit all the shared case statements.
23682409
void PatternMatchEmission::emitSharedCaseBlocks() {
@@ -2416,27 +2457,37 @@ void PatternMatchEmission::emitSharedCaseBlocks() {
24162457
return;
24172458

24182459
SILType ty = SGF.getLoweredType(V->getType());
2460+
2461+
SILValue value;
24192462
if (ty.isAddressOnly(SGF.F.getModule())) {
2420-
// Just assign SILUndef as a value for address only values.
2421-
SGF.VarLocs[V].value = SILUndef::get(ty, SGF.F.getModule());
2422-
return;
2463+
// There's no basic block argument, since we don't allow basic blocks
2464+
// to have address arguments.
2465+
//
2466+
// Instead, we map the variable to a temporary alloc_stack in
2467+
// emitAddressOnlyAllocations(), and store into it at each
2468+
// predecessor block.
2469+
//
2470+
// There's nothing to do here, since the value should already have
2471+
// been initialized on entry.
2472+
auto found = Temporaries.find(V);
2473+
assert(found != Temporaries.end());
2474+
value = found->second;
2475+
} else {
2476+
value = caseBB->getArgument(argIndex++);
24232477
}
24242478

24252479
if (V->isLet()) {
24262480
// Just emit a let with cleanup.
2427-
SGF.VarLocs[V].value = caseBB->getArgument(argIndex++);
2428-
SGF.emitInitializationForVarDecl(V, V->isLet())
2429-
->finishInitialization(SGF);
2481+
SGF.VarLocs[V].value = value;
2482+
SGF.enterDestroyCleanup(value);
24302483
} else {
24312484
// The pattern variables were all emitted as lets and one got passed in,
24322485
// now we finally alloc a box for the var and forward in the chosen value.
24332486
SGF.VarLocs.erase(V);
24342487
auto newVar = SGF.emitInitializationForVarDecl(V, V->isLet());
2435-
auto loc = SGF.CurrentSILLoc;
2436-
auto value =
2437-
ManagedValue::forUnmanaged(caseBB->getArgument(argIndex++));
2438-
auto formalType = V->getType()->getCanonicalType();
2439-
RValue(SGF, loc, formalType, value).forwardInto(SGF, loc, newVar.get());
2488+
auto mv = ManagedValue::forUnmanaged(value);
2489+
newVar->copyOrInitValueInto(SGF, V, mv, /*isInit*/ true);
2490+
newVar->finishInitialization(SGF);
24402491
}
24412492
});
24422493
emitCaseBody(caseBlock);
@@ -2535,35 +2586,6 @@ void SILGenFunction::usingImplicitVariablesForPattern(Pattern *pattern, CaseStmt
25352586
variableSwapper();
25362587
}
25372588

2538-
static void diagnoseMultiPatternCaseAddressOnlyBinding(SILGenFunction &SGF,
2539-
ValueDecl *decl,
2540-
SILValue value) {
2541-
SILLocation loc(decl);
2542-
2543-
// Try to figure out why this is an address only type. This is just an
2544-
// approximation. The targets of interest are:
2545-
//
2546-
// 1. existentials.
2547-
// 2. generics.
2548-
//
2549-
// If we are unable to show that we have an existential or generic, we use the
2550-
// more general unknown_addressonly_type_used_in_multipattern_case diagnostic.
2551-
unsigned errorPatternIndex = 0;
2552-
CanType ty = value->getType().getSwiftRValueType();
2553-
2554-
if (ty.findIf([&](Type ty) -> bool {
2555-
return ty->is<ProtocolType>() || ty->is<ProtocolCompositionType>();
2556-
})) {
2557-
errorPatternIndex = 1;
2558-
} else if (ty.findIf(
2559-
[&](Type ty) -> bool { return ty->is<ArchetypeType>(); })) {
2560-
errorPatternIndex = 2;
2561-
}
2562-
2563-
SGF.SGM.diagnose(loc, diag::addressonly_type_used_in_multipattern_case,
2564-
errorPatternIndex, ty);
2565-
}
2566-
25672589
void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
25682590
DEBUG(llvm::dbgs() << "emitting switch stmt\n";
25692591
S->print(llvm::dbgs());
@@ -2581,8 +2603,6 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
25812603
return failure(SILLocation(S));
25822604
}
25832605

2584-
bool diagnosedError = false;
2585-
25862606
auto completionHandler = [&](PatternMatchEmission &emission,
25872607
ArgArray argArray,
25882608
ClauseRow &row) {
@@ -2601,16 +2621,12 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
26012621
JumpDest sharedDest =
26022622
emission.getSharedCaseBlockDest(caseBlock);
26032623

2604-
// Generate the arguments from this row's pattern in the case block's expected order,
2605-
// and keep those arguments from being cleaned up, as we're passing the +1 along to
2606-
// the shared case block dest. (The cleanups still happen, as they are threaded through
2607-
// here messily, but the explicit retains here counteract them, and then the
2624+
// Generate the arguments from this row's pattern in the case block's
2625+
// expected order, and keep those arguments from being cleaned up, as
2626+
// we're passing the +1 along to the shared case block dest. (The
2627+
// cleanups still happen, as they are threaded through here messily,
2628+
// but the explicit retains here counteract them, and then the
26082629
// retain/release pair gets optimized out.)
2609-
//
2610-
// *NOTE*. We assume that all values are passed as objects for
2611-
// simplicity. This is ok to do since any time we diagnose an error, we
2612-
// pass SILUndef to the shared case block. This is to maintain the CFG
2613-
// structure and thus prevent spurious 'dead code' warnings.
26142630
ArrayRef<CaseLabelItem> labelItems = caseBlock->getCaseLabelItems();
26152631
SmallVector<SILValue, 4> args;
26162632
SmallVector<VarDecl *, 4> expectedVarOrder;
@@ -2626,10 +2642,12 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
26262642
if (var->hasName() && var->getName() == expected->getName()) {
26272643
SILValue value = VarLocs[var].value;
26282644
SILType type = value->getType();
2645+
2646+
// If we have an address-only type, initialize the temporary
2647+
// allocation. We're not going to pass the address as a block
2648+
// argument.
26292649
if (type.isAddressOnly(M)) {
2630-
if (!diagnosedError)
2631-
diagnoseMultiPatternCaseAddressOnlyBinding(*this, var, value);
2632-
diagnosedError = true;
2650+
emission.emitAddressOnlyInitialization(expected, value);
26332651
break;
26342652
}
26352653

@@ -2683,6 +2701,10 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
26832701
hasFallthrough = containsFallthrough(caseBlock->getBody());
26842702
}
26852703

2704+
// Emit alloc_stacks for address-only variables appearing in
2705+
// multiple-entry case blocks.
2706+
emission.emitAddressOnlyAllocations();
2707+
26862708
SILBasicBlock *contBB = createBasicBlock();
26872709
emitProfilerIncrement(S);
26882710
JumpDest contDest(contBB, Cleanups.getCleanupsDepth(), CleanupLocation(S));

test/Interpreter/switch.swift

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// RUN: %target-run-simple-swift
2+
// REQUIRES: executable_test
3+
4+
import StdlibUnittest
5+
6+
var SwitchTestSuite = TestSuite("Switch")
7+
8+
public enum Phase<Value> {
9+
case possible
10+
case active(Value)
11+
case paused(Value)
12+
case ended(Value)
13+
case failed
14+
}
15+
16+
extension Phase {
17+
public var valueLet: Value? {
18+
switch self {
19+
case .possible, .failed:
20+
return nil
21+
case let .active(value), let .paused(value), let .ended(value):
22+
return value
23+
}
24+
}
25+
26+
public var valueVar: Value? {
27+
switch self {
28+
case .possible, .failed:
29+
return nil
30+
case var .active(value), var .paused(value), var .ended(value):
31+
return value
32+
}
33+
}
34+
}
35+
36+
enum K {
37+
case A, B
38+
}
39+
40+
enum A<K> {
41+
case left(a: K, b: K)
42+
case right(a: K, b: K)
43+
44+
var valueLet: [K] {
45+
switch self {
46+
case let .left(a, b), let .right(a, b):
47+
return [a, b]
48+
}
49+
}
50+
51+
var valueVar: [K] {
52+
switch self {
53+
case var .left(a, b), var .right(a, b):
54+
return [a, b]
55+
}
56+
}
57+
}
58+
59+
SwitchTestSuite.test("GenericLet") {
60+
do {
61+
expectEqual(1.0, Phase.active(1.0).valueLet)
62+
expectEqual(2.0, Phase.paused(2.0).valueLet)
63+
expectEqual(3.0, Phase.ended(3.0).valueLet)
64+
}
65+
66+
do {
67+
let l = LifetimeTracked(0)
68+
expectTrue(l === Phase.active(l).valueLet)
69+
expectTrue(l === Phase.paused(l).valueLet)
70+
expectTrue(l === Phase.ended(l).valueLet)
71+
}
72+
73+
do {
74+
expectEqual([K.A, K.B], A.left(a: K.A, b: K.B).valueLet)
75+
expectEqual([K.A, K.B], A.right(a: K.A, b: K.B).valueLet)
76+
}
77+
78+
do {
79+
let l = LifetimeTracked(0)
80+
let r = LifetimeTracked(0)
81+
let arr = A.left(a: l, b: r).valueLet
82+
expectTrue(arr[0] === l)
83+
expectTrue(arr[1] === r)
84+
}
85+
86+
do {
87+
let l = LifetimeTracked(0)
88+
let r = LifetimeTracked(0)
89+
let arr = A.right(a: l, b: r).valueLet
90+
expectTrue(arr[0] === l)
91+
expectTrue(arr[1] === r)
92+
}
93+
}
94+
95+
SwitchTestSuite.test("GenericVar") {
96+
do {
97+
expectEqual(1.0, Phase.active(1.0).valueVar)
98+
expectEqual(2.0, Phase.paused(2.0).valueVar)
99+
expectEqual(3.0, Phase.ended(3.0).valueVar)
100+
}
101+
102+
do {
103+
let l = LifetimeTracked(0)
104+
expectTrue(l === Phase.active(l).valueVar)
105+
expectTrue(l === Phase.paused(l).valueVar)
106+
expectTrue(l === Phase.ended(l).valueVar)
107+
}
108+
109+
do {
110+
expectEqual([K.A, K.B], A.left(a: K.A, b: K.B).valueVar)
111+
expectEqual([K.A, K.B], A.right(a: K.A, b: K.B).valueVar)
112+
}
113+
114+
do {
115+
let l = LifetimeTracked(0)
116+
let r = LifetimeTracked(0)
117+
let arr = A.left(a: l, b: r).valueVar
118+
expectTrue(arr[0] === l)
119+
expectTrue(arr[1] === r)
120+
}
121+
122+
do {
123+
let l = LifetimeTracked(0)
124+
let r = LifetimeTracked(0)
125+
let arr = A.right(a: l, b: r).valueVar
126+
expectTrue(arr[0] === l)
127+
expectTrue(arr[1] === r)
128+
}
129+
}
130+
131+
enum Gesture {
132+
case pan(Any)
133+
case pinch(Any)
134+
}
135+
136+
extension Gesture {
137+
var valueLet: Any {
138+
switch self {
139+
case .pan(let data),
140+
.pinch(let data):
141+
return data
142+
}
143+
}
144+
var valueVar: Any {
145+
switch self {
146+
case .pan(var data),
147+
.pinch(var data):
148+
return data
149+
}
150+
}
151+
}
152+
153+
SwitchTestSuite.test("GenericLet") {
154+
expectEqual(1, Gesture.pan(1).valueLet as! Int)
155+
expectEqual(2, Gesture.pinch(2).valueLet as! Int)
156+
157+
let l = LifetimeTracked(0)
158+
expectTrue(l === Gesture.pan(l).valueLet as! LifetimeTracked)
159+
expectTrue(l === Gesture.pinch(l).valueLet as! LifetimeTracked)
160+
}
161+
162+
SwitchTestSuite.test("GenericVar") {
163+
expectEqual(1, Gesture.pan(1).valueVar as! Int)
164+
expectEqual(2, Gesture.pinch(2).valueVar as! Int)
165+
166+
let l = LifetimeTracked(0)
167+
expectTrue(l === Gesture.pan(l).valueVar as! LifetimeTracked)
168+
expectTrue(l === Gesture.pinch(l).valueVar as! LifetimeTracked)
169+
}
170+
171+
runAllTests()

0 commit comments

Comments
 (0)