Skip to content

Commit d13e997

Browse files
committed
[TypeCheckPattern] Attempt ExprPattern conversion before failing pattern coercion to optional
1 parent d163261 commit d13e997

File tree

3 files changed

+107
-15
lines changed

3 files changed

+107
-15
lines changed

Diff for: lib/Sema/TypeCheckPattern.cpp

+42-15
Original file line numberDiff line numberDiff line change
@@ -128,25 +128,49 @@ lookupUnqualifiedEnumMemberElement(DeclContext *DC, DeclNameRef name,
128128
/*unqualifiedLookup=*/true, lookup);
129129
}
130130

131-
/// Find an enum element in an enum type.
132-
static EnumElementDecl *
133-
lookupEnumMemberElement(DeclContext *DC, Type ty,
134-
DeclNameRef name, SourceLoc UseLoc) {
131+
static LookupResult lookupMembers(DeclContext *DC, Type ty, DeclNameRef name,
132+
SourceLoc UseLoc) {
135133
if (!ty->mayHaveMembers())
136-
return nullptr;
134+
return LookupResult();
137135

138136
// FIXME: We should probably pay attention to argument labels someday.
139137
name = name.withoutArgumentLabels();
140138

141139
// Look up the case inside the enum.
142140
// FIXME: We should be able to tell if this is a private lookup.
143141
NameLookupOptions lookupOptions = defaultMemberLookupOptions;
144-
LookupResult foundElements =
145-
TypeChecker::lookupMember(DC, ty, name, UseLoc, lookupOptions);
142+
return TypeChecker::lookupMember(DC, ty, name, UseLoc, lookupOptions);
143+
}
144+
145+
/// Find an enum element in an enum type.
146+
static EnumElementDecl *lookupEnumMemberElement(DeclContext *DC, Type ty,
147+
DeclNameRef name,
148+
SourceLoc UseLoc) {
149+
LookupResult foundElements = lookupMembers(DC, ty, name, UseLoc);
146150
return filterForEnumElement(DC, UseLoc,
147151
/*unqualifiedLookup=*/false, foundElements);
148152
}
149153

154+
/// Whether the type contains an enum element or static var member with the
155+
/// given name. Used for potential ambiguity diagnostics when matching against
156+
/// \c .none on \c Optional since users might be trying to match against an
157+
/// underlying \c .none member on the wrapped type.
158+
static bool hasEnumElementOrStaticVarMember(DeclContext *DC, Type ty,
159+
DeclNameRef name,
160+
SourceLoc UseLoc) {
161+
LookupResult foundElements = lookupMembers(DC, ty, name, UseLoc);
162+
return llvm::any_of(foundElements, [](const LookupResultEntry &result) {
163+
auto *VD = result.getValueDecl();
164+
if (isa<VarDecl>(VD) && VD->isStatic())
165+
return true;
166+
167+
if (isa<EnumElementDecl>(VD))
168+
return true;
169+
170+
return false;
171+
});
172+
}
173+
150174
namespace {
151175
/// Build up an \c DeclRefTypeRepr and see what it resolves to.
152176
/// FIXME: Support DeclRefTypeRepr nodes with non-identifier base components.
@@ -1480,13 +1504,10 @@ Pattern *TypeChecker::coercePatternToType(
14801504
return coercePatternToType(
14811505
pattern.forSubPattern(P, /*retainTopLevel=*/true), type,
14821506
options, tryRewritePattern);
1483-
} else {
1484-
diags.diagnose(EEP->getLoc(),
1485-
diag::enum_element_pattern_member_not_found,
1486-
EEP->getName(), type);
1487-
return nullptr;
14881507
}
1489-
} else if (EEP->hasUnresolvedOriginalExpr()) {
1508+
}
1509+
1510+
if (EEP->hasUnresolvedOriginalExpr()) {
14901511
// If we have the original expression parse tree, try reinterpreting
14911512
// it as an expr-pattern if enum element lookup failed, since `.foo`
14921513
// could also refer to a static member of the context type.
@@ -1495,6 +1516,12 @@ Pattern *TypeChecker::coercePatternToType(
14951516
return coercePatternToType(
14961517
pattern.forSubPattern(P, /*retainTopLevel=*/true), type,
14971518
options, tryRewritePattern);
1519+
} else {
1520+
// Otherwise, treat this as a failed enum element lookup.
1521+
diags.diagnose(EEP->getLoc(),
1522+
diag::enum_element_pattern_member_not_found,
1523+
EEP->getName(), type);
1524+
return nullptr;
14981525
}
14991526
}
15001527
}
@@ -1510,8 +1537,8 @@ Pattern *TypeChecker::coercePatternToType(
15101537
type->getOptionalObjectType()) {
15111538
SmallVector<Type, 4> allOptionals;
15121539
auto baseTyUnwrapped = type->lookThroughAllOptionalTypes(allOptionals);
1513-
if (lookupEnumMemberElement(dc, baseTyUnwrapped, EEP->getName(),
1514-
EEP->getLoc())) {
1540+
if (hasEnumElementOrStaticVarMember(dc, baseTyUnwrapped, EEP->getName(),
1541+
EEP->getLoc())) {
15151542
auto baseTyName = type->getCanonicalType().getString();
15161543
auto baseTyUnwrappedName = baseTyUnwrapped->getString();
15171544
diags.diagnoseWithNotes(

Diff for: test/Constraints/patterns.swift

+41
Original file line numberDiff line numberDiff line change
@@ -412,13 +412,34 @@ do {
412412
enum E {
413413
case baz
414414
case bar
415+
416+
static var member: Self {
417+
.baz
418+
}
419+
420+
static func method() -> Self {
421+
.bar
422+
}
423+
424+
static func methodArg(_: Void) -> Self {
425+
.baz
426+
}
427+
428+
static var none: Self {
429+
.baz
430+
}
415431
}
416432

417433
let oe: E? = .bar
418434

419435
switch oe {
420436
case .bar?: break // Ok
421437
case .baz: break // Ok
438+
case .member: break // Ok
439+
case .missingMember: break // expected-error {{type 'E?' has no member 'missingMember'}}
440+
case .method(): break // Ok
441+
case .methodArg(()): break // Ok
442+
case .none: break // expected-warning {{assuming you mean 'Optional<E>.none'; did you mean 'E.none' instead?}} expected-note {{use 'nil' to silence this warning}} expected-note {{use 'none?' instead}}
422443
default: break
423444
}
424445

@@ -427,11 +448,31 @@ do {
427448
switch ooe {
428449
case .bar?: break // Ok
429450
case .baz: break // Ok
451+
case .member: break // Ok
452+
case .missingMember: break // expected-error {{type 'E??' has no member 'missingMember'}}
453+
case .method(): break // Ok
454+
case .methodArg(()): break // Ok
430455
default: break
431456
}
432457

433458
if case .baz = ooe {} // Ok
434459
if case .bar? = ooe {} // Ok
460+
if case .member = ooe {} // Ok
461+
if case .missingMember = ooe {} // expected-error {{type 'E??' has no member 'missingMember'}}
462+
if case .method() = ooe {} // Ok
463+
if case .methodArg(()) = ooe {} // Ok
464+
465+
enum M {
466+
case m
467+
static func `none`(_: Void) -> Self { .m }
468+
}
469+
470+
let om: M? = .m
471+
472+
switch om {
473+
case .none: break // Ok
474+
default: break
475+
}
435476
}
436477

437478
// rdar://problem/60048356 - `if case` fails when `_` pattern doesn't have a label

Diff for: test/SILGen/enum.swift

+24
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,30 @@ func matchOptionalEnum2(bar: OneOrTwo??) {
222222
}
223223
}
224224

225+
enum OneTwoOrNone {
226+
case one
227+
case two
228+
229+
var none: Self {
230+
.one
231+
}
232+
}
233+
234+
// CHECK-LABEL: sil hidden [ossa] @$s4enum18matchOptionalEnum33baryAA12OneTwoOrNoneOSg_tF : $@convention(thin) (Optional<OneTwoOrNone>) -> () {
235+
// CHECK: bb0(%0 : $Optional<OneTwoOrNone>):
236+
// CHECK-NEXT: debug_value %0 : $Optional<OneTwoOrNone>, let, name "bar", argno 1
237+
// CHECK-NEXT: switch_enum %0 : $Optional<OneTwoOrNone>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb4
238+
// CHECK: bb1([[PHI_ARG:%.*]] : $OneTwoOrNone):
239+
// CHECK-NEXT: switch_enum [[PHI_ARG]] : $OneTwoOrNone, case #OneTwoOrNone.one!enumelt: bb2, case #OneTwoOrNone.two!enumelt: bb3
240+
func matchOptionalEnum3(bar: OneTwoOrNone?) {
241+
switch bar {
242+
case .one: print("one")
243+
case .two?: print("two")
244+
case .none: print("none")
245+
default: print("default")
246+
}
247+
}
248+
225249
// Make sure that we handle enum, tuple initialization composed
226250
// correctly. Previously, we leaked down a failure path due to us misusing
227251
// scopes.

0 commit comments

Comments
 (0)