Skip to content

Commit c5342a5

Browse files
authored
Merge pull request swiftlang#10088 from gottesmm/error_on_multiple_pattern_cases_with_address_only_patterns
[silgen] Error nicely on multiple pattern cases with address only patterns.
2 parents c04b8a4 + bf71ec8 commit c5342a5

File tree

3 files changed

+134
-14
lines changed

3 files changed

+134
-14
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ ERROR(writeback_overlap_subscript,none,
7979
NOTE(writebackoverlap_note,none,
8080
"concurrent writeback occurred here", ())
8181

82+
ERROR(addressonly_type_used_in_multipattern_case,none,
83+
"matching %select{type '%1'|a protocol value|a generic value}0 in multiple patterns "
84+
"is not yet supported; use separate cases instead",
85+
(unsigned, Type))
86+
8287
ERROR(inout_argument_alias,none,
8388
"inout arguments are not allowed to alias each other", ())
8489
NOTE(previous_inout_alias,none,

lib/SILGen/SILGenPattern.cpp

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,8 +2300,15 @@ JumpDest PatternMatchEmission::getSharedCaseBlockDest(CaseStmt *caseBlock,
23002300
pattern->forEachVariable([&](VarDecl *V) {
23012301
if (!V->hasName())
23022302
return;
2303-
block->createPHIArgument(SGF.getLoweredType(V->getType()),
2304-
ValueOwnershipKind::Owned, V);
2303+
// We should never PHI addresses. To eliminate that possibility, we:
2304+
//
2305+
// 1. Load all loadable types and pass them as objects to the block.
2306+
// 2. We do not emit arguments for address only types. We instead just
2307+
// assign SILUndef to the VarLoc.
2308+
SILType ty = SGF.getLoweredType(V->getType());
2309+
if (ty.isAddressOnly(SGF.F.getModule()))
2310+
return;
2311+
block->createPHIArgument(ty, ValueOwnershipKind::Owned, V);
23052312
});
23062313
}
23072314
}
@@ -2343,7 +2350,7 @@ void PatternMatchEmission::emitSharedCaseBlocks() {
23432350
}
23442351

23452352
assert(SGF.getCleanupsDepth() == PatternMatchStmtDepth);
2346-
2353+
23472354
// If we have a shared case with bound decls, then the 0th pattern has the
23482355
// order of variables that are the incoming BB arguments. Setup the VarLocs
23492356
// to point to the incoming args and setup initialization so any args needing
@@ -2355,6 +2362,14 @@ void PatternMatchEmission::emitSharedCaseBlocks() {
23552362
pattern->forEachVariable([&](VarDecl *V) {
23562363
if (!V->hasName())
23572364
return;
2365+
2366+
SILType ty = SGF.getLoweredType(V->getType());
2367+
if (ty.isAddressOnly(SGF.F.getModule())) {
2368+
// Just assign SILUndef as a value for address only values.
2369+
SGF.VarLocs[V].value = SILUndef::get(ty, SGF.F.getModule());
2370+
return;
2371+
}
2372+
23582373
if (V->isLet()) {
23592374
// Just emit a let with cleanup.
23602375
SGF.VarLocs[V].value = caseBB->getArgument(argIndex++);
@@ -2467,6 +2482,35 @@ void SILGenFunction::usingImplicitVariablesForPattern(Pattern *pattern, CaseStmt
24672482
variableSwapper();
24682483
}
24692484

2485+
static void diagnoseMultiPatternCaseAddressOnlyBinding(SILGenFunction &SGF,
2486+
ValueDecl *decl,
2487+
SILValue value) {
2488+
SILLocation loc(decl);
2489+
2490+
// Try to figure out why this is an address only type. This is just an
2491+
// approximation. The targets of interest are:
2492+
//
2493+
// 1. existentials.
2494+
// 2. generics.
2495+
//
2496+
// If we are unable to show that we have an existential or generic, we use the
2497+
// more general unknown_addressonly_type_used_in_multipattern_case diagnostic.
2498+
unsigned errorPatternIndex = 0;
2499+
CanType ty = value->getType().getSwiftRValueType();
2500+
2501+
if (ty.findIf([&](Type ty) -> bool {
2502+
return ty->is<ProtocolType>() || ty->is<ProtocolCompositionType>();
2503+
})) {
2504+
errorPatternIndex = 1;
2505+
} else if (ty.findIf(
2506+
[&](Type ty) -> bool { return ty->is<ArchetypeType>(); })) {
2507+
errorPatternIndex = 2;
2508+
}
2509+
2510+
SGF.SGM.diagnose(loc, diag::addressonly_type_used_in_multipattern_case,
2511+
errorPatternIndex, ty);
2512+
}
2513+
24702514
void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
24712515
DEBUG(llvm::dbgs() << "emitting switch stmt\n";
24722516
S->print(llvm::dbgs());
@@ -2475,7 +2519,8 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
24752519
emitProfilerIncrement(S);
24762520
JumpDest contDest(contBB, Cleanups.getCleanupsDepth(), CleanupLocation(S));
24772521

2478-
2522+
bool diagnosedError = false;
2523+
24792524
auto completionHandler = [&](PatternMatchEmission &emission,
24802525
ArgArray argArray,
24812526
ClauseRow &row) {
@@ -2492,33 +2537,56 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
24922537
row.hasFallthroughTo());
24932538
Cleanups.emitBranchAndCleanups(sharedDest, caseBlock);
24942539
} else if (caseBlock->getCaseLabelItems().size() > 1) {
2495-
JumpDest sharedDest = emission.getSharedCaseBlockDest(caseBlock,
2496-
row.hasFallthroughTo());
2497-
2540+
JumpDest sharedDest =
2541+
emission.getSharedCaseBlockDest(caseBlock, row.hasFallthroughTo());
2542+
24982543
// Generate the arguments from this row's pattern in the case block's expected order,
24992544
// and keep those arguments from being cleaned up, as we're passing the +1 along to
25002545
// the shared case block dest. (The cleanups still happen, as they are threaded through
25012546
// here messily, but the explicit retains here counteract them, and then the
25022547
// retain/release pair gets optimized out.)
2548+
//
2549+
// *NOTE*. We assume that all values are passed as objects for
2550+
// simplicity. This is ok to do since any time we diagnose an error, we
2551+
// pass SILUndef to the shared case block. This is to maintain the CFG
2552+
// structure and thus prevent spurious 'dead code' warnings.
25032553
ArrayRef<CaseLabelItem> labelItems = caseBlock->getCaseLabelItems();
25042554
SmallVector<SILValue, 4> args;
25052555
SmallVector<VarDecl *, 4> expectedVarOrder;
25062556
SmallVector<VarDecl *, 4> Vars;
25072557
labelItems[0].getPattern()->collectVariables(expectedVarOrder);
25082558
row.getCasePattern()->collectVariables(Vars);
2509-
2559+
2560+
SILModule &M = F.getModule();
25102561
for (auto expected : expectedVarOrder) {
25112562
if (!expected->hasName())
2512-
continue;
2563+
continue;
25132564
for (auto var : Vars) {
25142565
if (var->hasName() && var->getName() == expected->getName()) {
2515-
auto value = B.emitCopyValueOperation(CurrentSILLoc,
2516-
VarLocs[var].value);
2566+
SILValue value = VarLocs[var].value;
2567+
SILType type = value->getType();
2568+
if (type.isAddressOnly(M)) {
2569+
if (!diagnosedError)
2570+
diagnoseMultiPatternCaseAddressOnlyBinding(*this, var, value);
2571+
diagnosedError = true;
2572+
break;
2573+
}
2574+
2575+
// If we have a loadable address, perform a load [copy].
2576+
if (type.isAddress()) {
2577+
value = B.emitLoadValueOperation(CurrentSILLoc, value,
2578+
LoadOwnershipQualifier::Copy);
2579+
args.push_back(value);
2580+
break;
2581+
}
2582+
2583+
value = B.emitCopyValueOperation(CurrentSILLoc, value);
25172584
args.push_back(value);
25182585
break;
25192586
}
25202587
}
25212588
}
2589+
25222590
Cleanups.emitBranchAndCleanups(sharedDest, caseBlock, args);
25232591
} else {
25242592
// However, if we don't have a fallthrough or a multi-pattern 'case', we
@@ -2544,8 +2612,9 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
25442612
auto subject = ConsumableManagedValue::forOwned(subjectMV);
25452613

25462614
// Add a row for each label of each case.
2547-
// We use std::vector because it supports emplace_back; moving
2548-
// a ClauseRow is expensive.
2615+
//
2616+
// We use std::vector because it supports emplace_back; moving a ClauseRow is
2617+
// expensive.
25492618
std::vector<ClauseRow> clauseRows;
25502619
clauseRows.reserve(S->getCases().size());
25512620
bool hasFallthrough = false;
@@ -2575,7 +2644,7 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
25752644

25762645
switchScope.pop();
25772646

2578-
// Emit any shared case blocks we generated.
2647+
// Then emit the case blocks shared by multiple pattern cases.
25792648
emission.emitSharedCaseBlocks();
25802649

25812650
// Bookkeeping.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %target-swift-frontend %s -o /dev/null -emit-silgen -verify
2+
3+
protocol GestureData {}
4+
struct PanData : GestureData {}
5+
struct PinchData : GestureData {}
6+
7+
enum Gesture {
8+
case pan(PanData)
9+
case pinch(PinchData)
10+
}
11+
12+
func testProtocolType(_ a : Gesture) {
13+
switch a {
14+
case .pan(let data as GestureData), // expected-error {{matching a protocol value in multiple patterns is not yet supported; use separate cases instead}}
15+
.pinch(let data as GestureData):
16+
print(data)
17+
}
18+
19+
// This switch makes sure that we preserve the CFG so that dead code warnings do not show up. It also ensures that in at least two cases, we get one error per switch.
20+
switch a {
21+
case .pan(let data as GestureData), // expected-error {{matching a protocol value in multiple patterns is not yet supported; use separate cases instead}}
22+
.pinch(let data as GestureData):
23+
print(data)
24+
}
25+
}
26+
27+
func testGenericType<T, T2>(_ t : T, _ t2 : T2, _ a : Any, _ b : Any) -> T? {
28+
switch (a, b) {
29+
case (let x as T, _), // expected-error {{matching a generic value in multiple patterns is not yet supported; use separate cases instead}}
30+
(_, let x as T):
31+
return x
32+
// This warning check is to ensure that we allow for warnings to be emitting in case blocks.
33+
print("found it!") // expected-warning {{code after 'return' will never be executed}}
34+
case (let x as T, let y as T2):
35+
print(x)
36+
print(y)
37+
break
38+
default:
39+
return nil
40+
// This warning check is to ensure that we allow for warnings to be emitting in case blocks.
41+
print("we failed = (") // expected-warning {{code after 'return' will never be executed}}
42+
}
43+
44+
return nil
45+
print("we failed = (") // expected-warning {{code after 'return' will never be executed}}
46+
}

0 commit comments

Comments
 (0)