Skip to content

[Typechecker] Allow matching an enum case against an optional enum without '?' #22486

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
merged 8 commits into from
Mar 4, 2019
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
27 changes: 17 additions & 10 deletions lib/Sema/TypeCheckPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1388,19 +1388,26 @@ bool TypeChecker::coercePatternToType(Pattern *&P, TypeResolution resolution,
goto recur;
}

auto diag = diagnose(EEP->getLoc(),
diag::enum_element_pattern_member_not_found,
EEP->getName().str(), type);

// If we have an optional type let's try to see if the case
// exists in its base type, if so we can suggest a fix-it for that.
// If we have an optional type, let's try to see if the case
// exists in its base type and if it does then synthesize an
// OptionalSomePattern that wraps the case. This uses recursion
// to add multiple levels of OptionalSomePattern if the optional
// is nested.
if (auto baseType = type->getOptionalObjectType()) {
if (lookupEnumMemberElement(*this, dc, baseType, EEP->getName(),
EEP->getLoc()))
diag.fixItInsertAfter(EEP->getEndLoc(), "?");
if (lookupEnumMemberElement(*this, dc,
baseType->lookThroughAllOptionalTypes(),
EEP->getName(), EEP->getLoc())) {
P = new (Context)
OptionalSomePattern(EEP, EEP->getEndLoc(), /*implicit*/true);
return coercePatternToType(P, resolution, type, options);
} else {
diagnose(EEP->getLoc(),
diag::enum_element_pattern_member_not_found,
EEP->getName().str(), type);
return true;
}
}
}
return true;
}
enumTy = type;
} else {
Expand Down
30 changes: 26 additions & 4 deletions test/Constraints/patterns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ enum SR2057 {
}

let sr2057: SR2057?
if case .foo = sr2057 { } // expected-error{{enum case 'foo' not found in type 'SR2057?'}}
if case .foo = sr2057 { } // Ok


// Invalid 'is' pattern
Expand Down Expand Up @@ -330,10 +330,10 @@ struct S_32241441 {
func rdar32241441() {
let s: S_32241441? = S_32241441()

switch s?.type {
case .foo: // expected-error {{enum case 'foo' not found in type 'S_32241441.E_32241441?'}} {{12-12=?}}
switch s?.type { // expected-error {{switch must be exhaustive}} expected-note {{add missing case: '.none'}}
case .foo: // Ok
break;
case .bar: // expected-error {{enum case 'bar' not found in type 'S_32241441.E_32241441?'}} {{12-12=?}}
case .bar: // Ok
break;
}
}
Expand Down Expand Up @@ -406,4 +406,26 @@ func test8347() -> String {
}
}

enum SR_7799 {
case baz
case bar
}

let sr7799: SR_7799? = .bar

switch sr7799 {
case .bar?: break // Ok
case .baz: break // Ok
default: break
}

let sr7799_1: SR_7799?? = .baz

switch sr7799_1 {
case .bar?: break // Ok
case .baz: break // Ok
default: break
}

if case .baz = sr7799_1 {} // Ok
if case .bar? = sr7799_1 {} // Ok
32 changes: 31 additions & 1 deletion test/SILGen/enum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,34 @@ func useTrailingClosureGeneric<T>(t: T) {
_ = TrailingClosureGeneric<T>.noLabel { t }
_ = TrailingClosureGeneric<T>.twoElementsLabel(x: t) { t }
_ = TrailingClosureGeneric<T>.twoElementsNoLabel(t) { t }
}
}

enum SR7799 {
case one
case two
}

// CHECK-LABEL: sil hidden [ossa] @$s4enum6sr77993baryAA6SR7799OSg_tF : $@convention(thin) (Optional<SR7799>) -> () {
// CHECK: bb0(%0 : $Optional<SR7799>):
// CHECK-NEXT: debug_value %0 : $Optional<SR7799>, let, name "bar", argno 1
// CHECK-NEXT: switch_enum %0 : $Optional<SR7799>, case #Optional.some!enumelt.1: bb1, default bb4
// CHECK: bb1(%3 : $SR7799):
// CHECK-NEXT: switch_enum %3 : $SR7799, case #SR7799.one!enumelt: bb2, case #SR7799.two!enumelt: bb3
func sr7799(bar: SR7799?) {
switch bar {
case .one: print("one")
case .two?: print("two")
default: print("default")
}
}

// CHECK-LABEL: sil hidden [ossa] @$s4enum8sr7799_13baryAA6SR7799OSgSg_tF : $@convention(thin) (Optional<Optional<SR7799>>) -> () {
// CHECK: bb0(%0 : $Optional<Optional<SR7799>>):
// CHECK-NEXT: debug_value %0 : $Optional<Optional<SR7799>>, let, name "bar", argno 1
// CHECK-NEXT: switch_enum %0 : $Optional<Optional<SR7799>>, case #Optional.none!enumelt: bb1, default bb2
func sr7799_1(bar: SR7799??) {
switch bar {
case .none: print("none")
default: print("default")
}
}