Skip to content

Commit de2394d

Browse files
Merge pull request #59246 from AnthonyLatsis/local-conformance-operator
Sema: Fix operator function witness lookup for local adoptees
2 parents e63df1e + 0772f68 commit de2394d

File tree

3 files changed

+160
-7
lines changed

3 files changed

+160
-7
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,25 +1315,41 @@ WitnessChecker::lookupValueWitnesses(ValueDecl *req, bool *ignoringNames) {
13151315
auto reqName = req->createNameRef();
13161316
auto reqBaseName = reqName.withoutArgumentLabels();
13171317

1318-
if (req->isOperator()) {
1319-
// Operator lookup is always global.
1318+
// An operator function is the only kind of witness that requires global
1319+
// lookup. However, because global lookup doesn't enter local contexts,
1320+
// an additional, qualified lookup is warranted when the conforming type
1321+
// is declared in a local context.
1322+
const bool doUnqualifiedLookup = req->isOperator();
1323+
const bool doQualifiedLookup =
1324+
!req->isOperator() || DC->getParent()->getLocalContext();
1325+
1326+
if (doUnqualifiedLookup) {
13201327
auto lookup = TypeChecker::lookupUnqualified(DC->getModuleScopeContext(),
13211328
reqBaseName, SourceLoc(),
13221329
defaultUnqualifiedLookupOptions);
13231330
for (auto candidate : lookup) {
13241331
auto decl = candidate.getValueDecl();
1325-
if (swift::isMemberOperator(cast<FuncDecl>(decl), Adoptee)) {
1332+
if (!isa<ProtocolDecl>(decl->getDeclContext()) &&
1333+
swift::isMemberOperator(cast<FuncDecl>(decl), Adoptee)) {
13261334
witnesses.push_back(decl);
13271335
}
13281336
}
1329-
} else {
1330-
// Variable/function/subscript requirements.
1337+
}
1338+
1339+
if (doQualifiedLookup) {
13311340
auto *nominal = Adoptee->getAnyNominal();
13321341
nominal->synthesizeSemanticMembersIfNeeded(reqName.getFullName());
13331342

1343+
// Unqualified lookup would have already found candidates from protocol
1344+
// extensions, including those that match only by base name. Take care not
1345+
// to restate them in the resulting list, or else an otherwise valid
1346+
// conformance will become ambiguous.
1347+
const NLOptions options =
1348+
doUnqualifiedLookup ? NLOptions(0) : NL_ProtocolMembers;
1349+
13341350
SmallVector<ValueDecl *, 4> lookupResults;
13351351
bool addedAny = false;
1336-
DC->lookupQualified(nominal, reqName, NL_ProtocolMembers, lookupResults);
1352+
DC->lookupQualified(nominal, reqName, options, lookupResults);
13371353
for (auto *decl : lookupResults) {
13381354
if (!isa<ProtocolDecl>(decl->getDeclContext())) {
13391355
witnesses.push_back(decl);
@@ -1345,7 +1361,7 @@ WitnessChecker::lookupValueWitnesses(ValueDecl *req, bool *ignoringNames) {
13451361
// again using only the base name.
13461362
if (!addedAny && ignoringNames) {
13471363
lookupResults.clear();
1348-
DC->lookupQualified(nominal, reqBaseName, NL_ProtocolMembers, lookupResults);
1364+
DC->lookupQualified(nominal, reqBaseName, options, lookupResults);
13491365
for (auto *decl : lookupResults) {
13501366
if (!isa<ProtocolDecl>(decl->getDeclContext()))
13511367
witnesses.push_back(decl);
@@ -1358,6 +1374,10 @@ WitnessChecker::lookupValueWitnesses(ValueDecl *req, bool *ignoringNames) {
13581374
removeShadowedDecls(witnesses, DC);
13591375
}
13601376

1377+
assert(llvm::none_of(witnesses, [](ValueDecl *decl) {
1378+
return isa<ProtocolDecl>(decl->getDeclContext());
1379+
}));
1380+
13611381
return witnesses;
13621382
}
13631383

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s
2+
//
3+
// Test that we find and pick the right witnesses for operator function
4+
// requirements when the conforming type is declared in a local context.
5+
6+
infix operator =*=
7+
protocol P1 {
8+
static func =*= (lhs: Self, rhs: Self) -> Bool
9+
}
10+
func test1() {
11+
struct S: P1 {
12+
static func =*= (lhs: S, rhs: S) -> Bool { true }
13+
}
14+
}
15+
// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s36protocol_operators_local_conformance5test1yyF1SL_VAA2P1A2aEP3emeoiySbx_xtFZTW : $@convention(witness_method: P1) (@in_guaranteed S, @in_guaranteed S, @thick S.Type) -> Bool {
16+
// CHECK: function_ref @$s36protocol_operators_local_conformance5test1yyF1SL_V3emeoiySbAD_ADtFZ : $@convention(method) (S, S, @thin S.Type) -> Bool
17+
18+
infix operator =**=
19+
protocol P2 {
20+
static func =**= (lhs: Self, rhs: Self) -> Bool
21+
}
22+
extension P2 {
23+
static func =**= (lhs: Self, rhs: Self) -> Bool { true }
24+
}
25+
func test2() {
26+
struct S: P2 {}
27+
}
28+
// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s36protocol_operators_local_conformance5test2yyF1SL_VAA2P2A2aEP4emmeoiySbx_xtFZTW : $@convention(witness_method: P2) (@in_guaranteed S, @in_guaranteed S, @thick S.Type) -> Bool {
29+
// CHECK: function_ref @$s36protocol_operators_local_conformance2P2PAAE4emmeoiySbx_xtFZ : $@convention(method) <τ_0_0 where τ_0_0 : P2> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0, @thick τ_0_0.Type) -> Bool
30+
31+
func test3() {
32+
struct S: P2 {
33+
static func =**= (lhs: S, rhs: S) -> Bool { true }
34+
}
35+
}
36+
// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s36protocol_operators_local_conformance5test3yyF1SL_VAA2P2A2aEP4emmeoiySbx_xtFZTW : $@convention(witness_method: P2) (@in_guaranteed S, @in_guaranteed S, @thick S.Type) -> Bool {
37+
// CHECK: function_ref @$s36protocol_operators_local_conformance5test3yyF1SL_V4emmeoiySbAD_ADtFZ : $@convention(method) (S, S, @thin S.Type) -> Bool
38+
39+
func test4() {
40+
enum E: Int, Equatable {
41+
case a, b
42+
43+
static func == (lhs: E, rhs: E) -> Bool { true }
44+
}
45+
}
46+
// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s36protocol_operators_local_conformance5test4yyF1EL_OSQAASQ2eeoiySbx_xtFZTW : $@convention(witness_method: Equatable) (@in_guaranteed E, @in_guaranteed E, @thick E.Type) -> Bool {
47+
// CHECK: function_ref @$s36protocol_operators_local_conformance5test4yyF1EL_O2eeoiySbAD_ADtFZ : $@convention(method) (E, E, @thin E.Type) -> Bool
48+
49+
func test5() {
50+
enum E: Equatable {
51+
case a, b
52+
}
53+
}
54+
// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s36protocol_operators_local_conformance5test5yyF1EL_OSQAASQ2eeoiySbx_xtFZTW : $@convention(witness_method: Equatable) (@in_guaranteed E, @in_guaranteed E, @thick E.Type) -> Bool {
55+
// CHECK: function_ref @$s36protocol_operators_local_conformance5test5yyF1EL_O21__derived_enum_equalsySbAD_ADtFZ : $@convention(method) (E, E, @thin E.Type) -> Bool
56+
57+
58+
func test6() {
59+
enum E: Int, Equatable {
60+
case a, b
61+
}
62+
}
63+
// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s36protocol_operators_local_conformance5test6yyF1EL_OSQAASQ2eeoiySbx_xtFZTW : $@convention(witness_method: Equatable) (@in_guaranteed E, @in_guaranteed E, @thick E.Type) -> Bool {
64+
// CHECK: function_ref @$[[TEST6_EQUALS_WITNESS:[_0-9a-zA-Z]+]]
65+
// CHECK: }
66+
67+
// CHECK: sil [serialized] @$[[TEST6_EQUALS_WITNESS]] : $@convention(thin) <τ_0_0 where τ_0_0 : RawRepresentable, τ_0_0.RawValue : Equatable> (@in_guaranteed τ_0_0, @in_guaranteed τ_0_0) -> Bool
68+
69+
func test7() {
70+
struct Outer {
71+
enum Inner: Int, Comparable {
72+
case a, b
73+
74+
static func < (lhs: Inner, rhs: Inner) -> Bool { true }
75+
}
76+
}
77+
}
78+
// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s36protocol_operators_local_conformance5test7yyF5OuterL_V5InnerOSLAASL1loiySbx_xtFZTW : $@convention(witness_method: Comparable) (@in_guaranteed Outer.Inner, @in_guaranteed Outer.Inner, @thick Outer.Inner.Type) -> Bool {
79+
// CHECK: function_ref @$s36protocol_operators_local_conformance5test7yyF5OuterL_V5InnerO1loiySbAF_AFtFZ : $@convention(method) (Outer.Inner, Outer.Inner, @thin Outer.Inner.Type) -> Bool
80+
81+
func test8() {
82+
class C: Equatable {
83+
static func == (lhs: C, rhs: C) -> Bool { true }
84+
}
85+
}
86+
// CHECK-LABEL: sil private [transparent] [thunk] [ossa] @$s36protocol_operators_local_conformance5test8yyF1CL_CSQAASQ2eeoiySbx_xtFZTW : $@convention(witness_method: Equatable) (@in_guaranteed C, @in_guaranteed C, @thick C.Type) -> Bool {
87+
// CHECK: function_ref @$s36protocol_operators_local_conformance5test8yyF1CL_C2eeoiySbAD_ADtFZ : $@convention(method) (@guaranteed C, @guaranteed C, @thick C.Type) -> Bool
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
infix operator =*=
4+
protocol P1 {
5+
static func =*= (lhs: Self, rhs: Self) -> Bool
6+
}
7+
extension P1 {
8+
static func =*= (lhs: Self, rhs: Self) -> Bool { true }
9+
}
10+
func test1() {
11+
struct S: P1 {}
12+
}
13+
14+
infix operator =**=
15+
protocol P2 {
16+
static func =**= (lhs: Self, rhs: Self) -> Bool
17+
}
18+
func test2() {
19+
struct S: P2 {
20+
static func =**= (lhs: S, rhs: S) -> Bool { true }
21+
}
22+
}
23+
24+
func test3() {
25+
enum E: Equatable {
26+
case a, b
27+
28+
static func == (lhs: E, rhs: E) -> Bool { true }
29+
}
30+
}
31+
32+
func test4() {
33+
struct Outer {
34+
enum Inner: Int, Comparable {
35+
case a, b
36+
37+
static func < (lhs: Inner, rhs: Inner) -> Bool { true }
38+
}
39+
}
40+
}
41+
42+
func test5() {
43+
class C: Equatable {
44+
static func == (lhs: C, rhs: C) -> Bool { true }
45+
}
46+
}

0 commit comments

Comments
 (0)