Skip to content

SILGen: Diagnose unsupported shared case blocks for noncopyable switch subjects. #74041

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -1038,5 +1038,8 @@ NOTE(lifetime_outside_scope_use, none,
NOTE(lifetime_outside_scope_escape, none,
"this use causes the lifetime-dependent value to escape", ())

ERROR(noncopyable_shared_case_block_unimplemented, none,
"matching a non-'Copyable' value using a case label that has multiple patterns is not implemented", ())

#define UNDEFINE_DIAGNOSTIC_MACROS
#include "DefineDiagnosticMacros.h"
31 changes: 26 additions & 5 deletions lib/SILGen/SILGenPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,8 @@ class PatternMatchEmission {
void emitDestructiveCaseBlocks();

JumpDest getSharedCaseBlockDest(CaseStmt *caseStmt);
void emitSharedCaseBlocks(llvm::function_ref<void(CaseStmt *)> bodyEmitter);
void emitSharedCaseBlocks(ValueOwnership ownership,
llvm::function_ref<void(CaseStmt *)> bodyEmitter);

void emitCaseBody(CaseStmt *caseBlock);

Expand Down Expand Up @@ -2790,8 +2791,13 @@ void PatternMatchEmission::emitDestructiveCaseBlocks() {
// TODO: handle fallthroughs and multiple cases bindings
// In those cases we'd need to forward bindings through the shared case
// destination blocks.
assert(!stmt->hasFallthroughDest()
&& stmt->getCaseLabelItems().size() == 1);
if (stmt->hasFallthroughDest()
|| stmt->getCaseLabelItems().size() != 1) {
// This should already have been diagnosed as unsupported, so just emit
// an unreachable here.
SGF.B.createUnreachable(stmt);
continue;
}

// Bind variables from the pattern.
if (stmt->hasCaseBodyVariables()) {
Expand Down Expand Up @@ -2899,7 +2905,21 @@ emitAddressOnlyInitialization(VarDecl *dest, SILValue value) {

/// Emit all the shared case statements.
void PatternMatchEmission::emitSharedCaseBlocks(
ValueOwnership ownership,
llvm::function_ref<void(CaseStmt *)> bodyEmitter) {
if (ownership >= ValueOwnership::Shared
&& !SharedCases.empty()) {
SGF.SGM.diagnose(SharedCases.front().first,
diag::noncopyable_shared_case_block_unimplemented);

for (auto &entry : SharedCases) {
SILBasicBlock *caseBB = entry.second.first;
SGF.B.setInsertionPoint(caseBB);
SGF.B.createUnreachable(entry.first);
}

return;
}
for (auto &entry : SharedCases) {
CaseStmt *caseBlock = entry.first;
SILBasicBlock *caseBB = entry.second.first;
Expand Down Expand Up @@ -3742,7 +3762,7 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
}

// Then emit the case blocks shared by multiple pattern cases.
emission.emitSharedCaseBlocks(
emission.emitSharedCaseBlocks(ownership,
[&](CaseStmt *caseStmt) { emission.emitCaseBody(caseStmt); });

// Bookkeeping.
Expand Down Expand Up @@ -4015,7 +4035,8 @@ void SILGenFunction::emitCatchDispatch(DoCatchStmt *S, ManagedValue exn,
stmtScope.pop();

// Then emit the case blocks shared by multiple pattern cases.
emission.emitSharedCaseBlocks([&](CaseStmt *caseStmt) {
emission.emitSharedCaseBlocks(ValueOwnership::Default,
[&](CaseStmt *caseStmt) {
emitStmt(caseStmt->getBody());

// If we fell out of the catch clause, branch to the fallthrough dest.
Expand Down
50 changes: 50 additions & 0 deletions test/SILGen/borrowing_switch_multiple_patterns.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// RUN: %target-swift-emit-silgen -verify %s

struct Inner: ~Copyable {}
enum Outer: ~Copyable { case value(Inner, Int) }

func borrow(_: borrowing Inner) {}
func consume(_: consuming Inner) {}

func foo(x: borrowing Outer) {
switch x {
case .value(let y, 0), // expected-error{{not implemented}}
.value(let y, _):
borrow(y)
}

}

func bar(x: borrowing Outer) {
switch x {
case .value(let y, 0):
borrow(y)
fallthrough

case .value(let y, _): // expected-error{{not implemented}}
borrow(y)
}

}

func zim(x: consuming Outer) {
switch consume x {
case .value(let y, 0), // expected-error{{not implemented}}
.value(let y, _):
consume(y)
}

}

func zang(x: consuming Outer) {
switch consume x {
case .value(let y, 0):
// should eventually test that this gets diagnosed as a double-consume
//consume(y)
fallthrough

case .value(let y, _): // expected-error{{not implemented}}
consume(y)
}

}