[Sema] Try limit kicking interface type in filterForEnumElement

Kicking the interface type request of the base decl here is wrong
if the decl is e.g a `self` capture in a closure, since we'll be in
the middle of type-checking the closure. I'm planning on properly
fixing this by folding the lookup into the constraint system, but for
now let's at least avoid kicking the request if we don't have an enum
case or enum var. That at least prevents it from affecting cases where
e.g you're pattern matching against a property in a class.

We could potentially tighten up the checking here even further, but
that could potentially impact source compatibility for ambiguous
cases. I'd like to keep this patch low risk, and then deal with any
fallout as part of the pattern type-checking work.

rdar://146952007
This commit is contained in:
Hamish Knight
2025-04-08 22:35:33 +01:00
parent 345aa9ddca
commit 9fccfa6255
3 changed files with 81 additions and 5 deletions

View File

@@ -90,7 +90,21 @@ filterForEnumElement(DeclContext *DC, SourceLoc UseLoc,
ValueDecl *e = result.getValueDecl();
assert(e);
// Skip if the enum element was referenced as an instance member
// We only care about enum members, and must either have an EnumElementDecl,
// or a VarDecl which could be wrapping an underlying enum element.
// FIXME: We check this up-front to avoid kicking InterfaceTypeRequest
// below to help workaround https://github.com/swiftlang/swift/issues/80657
// for non-enum cases. The proper fix is to move this filtering logic
// into the constraint system.
if (!e->getDeclContext()->getSelfEnumDecl())
continue;
auto *EED = dyn_cast<EnumElementDecl>(e);
auto *VD = dyn_cast<VarDecl>(e);
if (!EED && !VD)
continue;
// Skip if referenced as an instance member
if (unqualifiedLookup) {
if (!result.getBaseDecl() ||
!result.getBaseDecl()->getInterfaceType()->is<MetatypeType>()) {
@@ -98,17 +112,17 @@ filterForEnumElement(DeclContext *DC, SourceLoc UseLoc,
}
}
if (auto *oe = dyn_cast<EnumElementDecl>(e)) {
if (EED) {
// Note that there could be multiple elements with the same
// name, such results in a re-declaration error, so let's
// just always pick the last element, just like in `foundConstant`
// case.
foundElement = oe;
foundElement = EED;
continue;
}
if (auto *var = dyn_cast<VarDecl>(e)) {
foundConstant = var;
if (VD) {
foundConstant = VD;
continue;
}
}

View File

@@ -0,0 +1,17 @@
// RUN: not --crash %target-swift-frontend -emit-sil %s
// https://github.com/swiftlang/swift/issues/80657
enum E {
case e
static func foo() {
_ = { [self] in
switch e {
case e:
break
default:
break
}
}
}
}

View File

@@ -0,0 +1,45 @@
// RUN: %target-swift-frontend -emit-sil %s
// These cases are similar to https://github.com/swiftlang/swift/issues/80657,
// but we can avoid hitting the same issue for non-enum members.
struct S {
let y = 0
func foo(_ x: Int) {
let _ = { [self] in
switch x {
case y: break
default: break
}
}
}
}
class C {
let y = 0
func foo(_ x: Int) {
let _ = { [self] in
switch x {
case y: break
default: break
}
}
}
}
enum E {
case e
func bar() -> Int {0}
func foo() {
_ = { [self] in
switch 0 {
case bar():
break
default:
break
}
}
}
}