Skip to content

Commit 003d9c0

Browse files
authored
Merge pull request #18831 from mdiep/pare-down-operator-candidates
[Sema] Pare down operator candidates during protocol type checking
2 parents c73b2be + 5e9da14 commit 003d9c0

10 files changed

+63
-41
lines changed

Diff for: lib/Sema/TypeCheckDecl.cpp

+27-15
Original file line numberDiff line numberDiff line change
@@ -3554,38 +3554,44 @@ void bindFuncDeclToOperator(TypeChecker &TC, FuncDecl *FD) {
35543554
FD->setOperatorDecl(op);
35553555
}
35563556

3557-
void checkMemberOperator(TypeChecker &TC, FuncDecl *FD) {
3557+
bool swift::isMemberOperator(FuncDecl *decl, Type type) {
35583558
// Check that member operators reference the type of 'Self'.
3559-
if (FD->isInvalid()) return;
3559+
if (decl->isInvalid())
3560+
return true;
35603561

3561-
auto *DC = FD->getDeclContext();
3562+
auto *DC = decl->getDeclContext();
35623563
auto selfNominal = DC->getSelfNominalTypeDecl();
3563-
if (!selfNominal) return;
35643564

35653565
// Check the parameters for a reference to 'Self'.
3566-
bool isProtocol = isa<ProtocolDecl>(selfNominal);
3567-
for (auto param : *FD->getParameters()) {
3566+
bool isProtocol = selfNominal && isa<ProtocolDecl>(selfNominal);
3567+
for (auto param : *decl->getParameters()) {
35683568
auto paramType = param->getInterfaceType();
35693569
if (!paramType) break;
35703570

35713571
// Look through a metatype reference, if there is one.
35723572
paramType = paramType->getMetatypeInstanceType();
35733573

3574-
// Is it the same nominal type?
3575-
if (paramType->getAnyNominal() == selfNominal) return;
3574+
auto nominal = paramType->getAnyNominal();
3575+
if (type.isNull()) {
3576+
// Is it the same nominal type?
3577+
if (selfNominal && nominal == selfNominal)
3578+
return true;
3579+
} else {
3580+
// Is it the same nominal type? Or a generic (which may or may not match)?
3581+
if (paramType->is<GenericTypeParamType>() ||
3582+
nominal == type->getAnyNominal())
3583+
return true;
3584+
}
35763585

35773586
if (isProtocol) {
35783587
// For a protocol, is it the 'Self' type parameter?
35793588
if (auto genericParam = paramType->getAs<GenericTypeParamType>())
35803589
if (genericParam->isEqual(DC->getSelfInterfaceType()))
3581-
return;
3590+
return true;
35823591
}
35833592
}
35843593

3585-
// We did not find 'Self'. Complain.
3586-
TC.diagnose(FD, diag::operator_in_unrelated_type,
3587-
FD->getDeclContext()->getDeclaredInterfaceType(),
3588-
isProtocol, FD->getFullName());
3594+
return false;
35893595
}
35903596

35913597
bool checkDynamicSelfReturn(FuncDecl *func,
@@ -4174,8 +4180,14 @@ void TypeChecker::validateDecl(ValueDecl *D) {
41744180

41754181
// Member functions need some special validation logic.
41764182
if (FD->getDeclContext()->isTypeContext()) {
4177-
if (FD->isOperator())
4178-
checkMemberOperator(*this, FD);
4183+
if (FD->isOperator() && !isMemberOperator(FD, nullptr)) {
4184+
auto selfNominal = FD->getDeclContext()->getSelfNominalTypeDecl();
4185+
auto isProtocol = selfNominal && isa<ProtocolDecl>(selfNominal);
4186+
// We did not find 'Self'. Complain.
4187+
diagnose(FD, diag::operator_in_unrelated_type,
4188+
FD->getDeclContext()->getDeclaredInterfaceType(), isProtocol,
4189+
FD->getFullName());
4190+
}
41794191

41804192
auto accessor = dyn_cast<AccessorDecl>(FD);
41814193

Diff for: lib/Sema/TypeCheckProtocol.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -930,7 +930,7 @@ WitnessChecker::lookupValueWitnessesViaImplementsAttr(
930930
}
931931
}
932932

933-
SmallVector<ValueDecl *, 4>
933+
SmallVector<ValueDecl *, 4>
934934
WitnessChecker::lookupValueWitnesses(ValueDecl *req, bool *ignoringNames) {
935935
assert(!isa<AssociatedTypeDecl>(req) && "Not for lookup for type witnesses*");
936936

@@ -950,7 +950,10 @@ WitnessChecker::lookupValueWitnesses(ValueDecl *req, bool *ignoringNames) {
950950
SourceLoc(),
951951
lookupOptions);
952952
for (auto candidate : lookup) {
953-
witnesses.push_back(candidate.getValueDecl());
953+
auto decl = candidate.getValueDecl();
954+
if (swift::isMemberOperator(cast<FuncDecl>(decl), Adoptee)) {
955+
witnesses.push_back(decl);
956+
}
954957
}
955958
} else {
956959
// Variable/function/subscript requirements.

Diff for: lib/Sema/TypeCheckProtocol.h

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class AccessScope;
3333
class AssociatedTypeDecl;
3434
class AvailabilityContext;
3535
class DeclContext;
36+
class FuncDecl;
3637
class NormalProtocolConformance;
3738
class ProtocolDecl;
3839
class TypeChecker;
@@ -452,6 +453,8 @@ class WitnessChecker {
452453
WitnessChecker(TypeChecker &tc, ProtocolDecl *proto,
453454
Type adoptee, DeclContext *dc);
454455

456+
bool isMemberOperator(FuncDecl *decl, Type type);
457+
455458
/// Gather the value witnesses for the given requirement.
456459
///
457460
/// \param ignoringNames If non-null and there are no value

Diff for: lib/Sema/TypeCheckProtocolInference.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,8 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
230230
auto typeInContext =
231231
conformance->getDeclContext()->mapTypeIntoContext(conformance->getType());
232232

233-
for (auto witness : checker.lookupValueWitnesses(req,
234-
/*ignoringNames=*/nullptr)) {
233+
for (auto witness :
234+
checker.lookupValueWitnesses(req, /*ignoringNames=*/nullptr)) {
235235
LLVM_DEBUG(llvm::dbgs() << "Inferring associated types from decl:\n";
236236
witness->dump(llvm::dbgs()));
237237

@@ -399,7 +399,7 @@ AssociatedTypeInference::inferTypeWitnessesViaValueWitnesses(
399399

400400
result.push_back(std::move(witnessResult));
401401
next_witness:;
402-
}
402+
}
403403

404404
return result;
405405
}

Diff for: lib/Sema/TypeChecker.h

+5
Original file line numberDiff line numberDiff line change
@@ -2209,6 +2209,11 @@ Type getMemberTypeForComparison(ASTContext &ctx, ValueDecl *member,
22092209
bool isOverrideBasedOnType(ValueDecl *decl, Type declTy,
22102210
ValueDecl *parentDecl, Type parentDeclTy);
22112211

2212+
/// Determine whether the given declaration is an operator defined in a
2213+
/// protocol. If \p type is not null, check specifically whether \p decl
2214+
/// could fulfill a protocol requirement for it.
2215+
bool isMemberOperator(FuncDecl *decl, Type type);
2216+
22122217
} // end namespace swift
22132218

22142219
#endif

Diff for: test/Compatibility/accessibility.swift

-4
Original file line numberDiff line numberDiff line change
@@ -641,12 +641,10 @@ fileprivate struct EquatablishOuter {
641641
internal struct Inner : Equatablish {}
642642
}
643643
private func ==(lhs: EquatablishOuter.Inner, rhs: EquatablishOuter.Inner) {}
644-
// expected-note@-1 {{candidate has non-matching type}}
645644

646645
fileprivate struct EquatablishOuter2 {
647646
internal struct Inner : Equatablish {
648647
fileprivate static func ==(lhs: Inner, rhs: Inner) {}
649-
// expected-note@-1 {{candidate has non-matching type}}
650648
}
651649
}
652650

@@ -660,7 +658,6 @@ internal struct EquatablishOuterProblem2 {
660658
public struct Inner : Equatablish {
661659
fileprivate static func ==(lhs: Inner, rhs: Inner) {} // expected-error {{method '==' must be as accessible as its enclosing type because it matches a requirement in protocol 'Equatablish'}} {{none}}
662660
// expected-note@-1 {{mark the operator function as 'internal' to satisfy the requirement}} {{5-16=internal}}
663-
// expected-note@-2 {{candidate has non-matching type}}
664661
}
665662
}
666663

@@ -671,7 +668,6 @@ internal struct EquatablishOuterProblem3 {
671668
}
672669
private func ==(lhs: EquatablishOuterProblem3.Inner, rhs: EquatablishOuterProblem3.Inner) {}
673670
// expected-note@-1 {{mark the operator function as 'internal' to satisfy the requirement}} {{1-8=internal}}
674-
// expected-note@-2 {{candidate has non-matching type}}
675671

676672

677673
public protocol AssocTypeProto {

Diff for: test/Sema/accessibility.swift

-4
Original file line numberDiff line numberDiff line change
@@ -650,12 +650,10 @@ fileprivate struct EquatablishOuter {
650650
internal struct Inner : Equatablish {}
651651
}
652652
private func ==(lhs: EquatablishOuter.Inner, rhs: EquatablishOuter.Inner) {}
653-
// expected-note@-1 {{candidate has non-matching type}}
654653

655654
fileprivate struct EquatablishOuter2 {
656655
internal struct Inner : Equatablish {
657656
fileprivate static func ==(lhs: Inner, rhs: Inner) {}
658-
// expected-note@-1 {{candidate has non-matching type}}
659657
}
660658
}
661659

@@ -669,7 +667,6 @@ internal struct EquatablishOuterProblem2 {
669667
public struct Inner : Equatablish {
670668
fileprivate static func ==(lhs: Inner, rhs: Inner) {} // expected-error {{method '==' must be as accessible as its enclosing type because it matches a requirement in protocol 'Equatablish'}} {{none}}
671669
// expected-note@-1 {{mark the operator function as 'internal' to satisfy the requirement}} {{5-16=internal}}
672-
// expected-note@-2 {{candidate has non-matching type}}
673670
}
674671
}
675672

@@ -679,7 +676,6 @@ internal struct EquatablishOuterProblem3 {
679676
}
680677
private func ==(lhs: EquatablishOuterProblem3.Inner, rhs: EquatablishOuterProblem3.Inner) {}
681678
// expected-note@-1 {{mark the operator function as 'internal' to satisfy the requirement}} {{1-8=internal}}
682-
// expected-note@-2 {{candidate has non-matching type}}
683679

684680

685681
public protocol AssocTypeProto {

Diff for: test/Sema/enum_conformance_synthesis.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ enum CustomHashable {
4545

4646
var hashValue: Int { return 0 }
4747
}
48-
func ==(x: CustomHashable, y: CustomHashable) -> Bool { // expected-note 4 {{non-matching type}}
48+
func ==(x: CustomHashable, y: CustomHashable) -> Bool {
4949
return true
5050
}
5151

@@ -63,7 +63,7 @@ enum InvalidCustomHashable {
6363

6464
var hashValue: String { return "" } // expected-note{{previously declared here}}
6565
}
66-
func ==(x: InvalidCustomHashable, y: InvalidCustomHashable) -> String { // expected-note 4 {{non-matching type}}
66+
func ==(x: InvalidCustomHashable, y: InvalidCustomHashable) -> String {
6767
return ""
6868
}
6969
func invalidCustomHashable() {
@@ -213,7 +213,7 @@ public enum Medicine {
213213

214214
extension Medicine : Equatable {}
215215

216-
public func ==(lhs: Medicine, rhs: Medicine) -> Bool { // expected-note 4 {{non-matching type}}
216+
public func ==(lhs: Medicine, rhs: Medicine) -> Bool {
217217
return true
218218
}
219219

@@ -236,7 +236,7 @@ extension NotExplicitlyHashableAndCannotDerive : CaseIterable {} // expected-err
236236
// Verify that conformance (albeit manually implemented) can still be added to
237237
// a type in a different file.
238238
extension OtherFileNonconforming: Hashable {
239-
static func ==(lhs: OtherFileNonconforming, rhs: OtherFileNonconforming) -> Bool { // expected-note 4 {{non-matching type}}
239+
static func ==(lhs: OtherFileNonconforming, rhs: OtherFileNonconforming) -> Bool {
240240
return true
241241
}
242242
var hashValue: Int { return 0 }

Diff for: test/Sema/struct_equatable_hashable.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct CustomHashValue: Hashable {
5050

5151
var hashValue: Int { return 0 }
5252

53-
static func ==(x: CustomHashValue, y: CustomHashValue) -> Bool { return true } // expected-note 3 {{non-matching type}}
53+
static func ==(x: CustomHashValue, y: CustomHashValue) -> Bool { return true }
5454
}
5555

5656
func customHashValue() {
@@ -72,7 +72,7 @@ struct CustomHashInto: Hashable {
7272
hasher.combine(y)
7373
}
7474

75-
static func ==(x: CustomHashInto, y: CustomHashInto) -> Bool { return true } // expected-note 3 {{non-matching type}}
75+
static func ==(x: CustomHashInto, y: CustomHashInto) -> Bool { return true }
7676
}
7777

7878
func customHashInto() {
@@ -172,9 +172,9 @@ public struct StructConformsAndImplementsInExtension {
172172
let v: Int
173173
}
174174
extension StructConformsAndImplementsInExtension : Equatable {
175-
public static func ==(lhs: StructConformsAndImplementsInExtension, rhs: StructConformsAndImplementsInExtension) -> Bool { // expected-note 3 {{non-matching type}}
175+
public static func ==(lhs: StructConformsAndImplementsInExtension, rhs: StructConformsAndImplementsInExtension) -> Bool {
176176
return true
177-
}
177+
}
178178
}
179179

180180
// No explicit conformance and it cannot be derived.
@@ -189,7 +189,7 @@ struct NoStoredProperties: Hashable {}
189189
// Verify that conformance (albeit manually implemented) can still be added to
190190
// a type in a different file.
191191
extension OtherFileNonconforming: Hashable {
192-
static func ==(lhs: OtherFileNonconforming, rhs: OtherFileNonconforming) -> Bool { // expected-note 3 {{non-matching type}}
192+
static func ==(lhs: OtherFileNonconforming, rhs: OtherFileNonconforming) -> Bool {
193193
return true
194194
}
195195
var hashValue: Int { return 0 }

Diff for: test/decl/protocol/req/func.swift

+11-4
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ struct X3a : P3 {
9696
typealias Assoc = X1a
9797
}
9898

99-
prefix func ~~(_: X3a) -> X1a {} // expected-note{{candidate has non-matching type '(X3a) -> X1a'}} expected-note{{candidate is prefix, not postfix as required}}
99+
prefix func ~~(_: X3a) -> X1a {}
100100

101101
// FIXME: Add example with overloaded prefix/postfix
102102

@@ -105,7 +105,7 @@ struct X3z : P3 { // expected-error{{type 'X3z' does not conform to protocol 'P3
105105
typealias Assoc = X1a
106106
}
107107

108-
postfix func ~~(_: X3z) -> X1a {} // expected-note{{candidate is postfix, not prefix as required}} expected-note{{candidate has non-matching type '(X3z) -> X1a'}}
108+
postfix func ~~(_: X3z) -> X1a {} // expected-note{{candidate is postfix, not prefix as required}}
109109

110110
// Protocol with postfix unary function
111111
postfix operator ~~
@@ -119,14 +119,14 @@ struct X4a : P4 {
119119
typealias Assoc = X1a
120120
}
121121

122-
postfix func ~~(_: X4a) -> X1a {} // expected-note{{candidate has non-matching type '(X4a) -> X1a'}} expected-note{{candidate is postfix, not prefix as required}}
122+
postfix func ~~(_: X4a) -> X1a {}
123123

124124
// Prefix/postfix mismatch.
125125
struct X4z : P4 { // expected-error{{type 'X4z' does not conform to protocol 'P4'}}
126126
typealias Assoc = X1a
127127
}
128128

129-
prefix func ~~(_: X4z) -> X1a {} // expected-note{{candidate has non-matching type '(X4z) -> X1a'}} expected-note{{candidate is prefix, not postfix as required}}
129+
prefix func ~~(_: X4z) -> X1a {} // expected-note{{candidate is prefix, not postfix as required}}
130130

131131
// Objective-C protocol
132132
@objc protocol P5 {
@@ -270,3 +270,10 @@ struct X12 : P12 { // expected-error{{type 'X12' does not conform to protocol 'P
270270
}
271271

272272
func ==(x: X12.Index, y: X12.Index) -> Bool { return true }
273+
274+
protocol P13 {}
275+
protocol P14 {
276+
static prefix func %%%(_: Self.Type)
277+
}
278+
prefix func %%%<P: P13>(_: P.Type) { }
279+
struct X13: P14, P13 { }

0 commit comments

Comments
 (0)