Skip to content

Commit feea84d

Browse files
committed
Sema: Ban uncallable protocol member operators
Member operators of concrete nominal types must declare at least one parameter with that type, like ``` struct S { static func +(lhs: S, rhs: Int) -> S {} } ``` For protocol member operators, we would look for a parameter of type `Self`, or an existential type `any P`. While the latter was consistent with the concrete nominal type case, it was actually wrong because then the resulting interface type does not give the type checker any way to bind the `Self` type parameter. There were two existing test cases that now produce errors, which I believe is now correct. While this is technically a source break, because these bogus operators seemingly cannot be witnessed or called, such a protocol probably had no conforming types. Fixes swiftlang#73201.
1 parent e342a38 commit feea84d

File tree

5 files changed

+54
-47
lines changed

5 files changed

+54
-47
lines changed

lib/Sema/TypeCheckDecl.cpp

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,48 +2034,59 @@ FunctionOperatorRequest::evaluate(Evaluator &evaluator, FuncDecl *FD) const {
20342034
return op.get();
20352035
}
20362036

2037-
bool swift::isMemberOperator(FuncDecl *decl, Type type) {
2037+
/// This means two things:
2038+
/// - If selfTy is null, 'decl' is assumed to be a member of a nominal type
2039+
/// or extension. We check if its a valid member operator.
2040+
/// - Otherwise, 'decl' is a member or top-level operator. We check if it
2041+
/// is a suitable witness for the given conforming type.
2042+
bool swift::isMemberOperator(FuncDecl *decl, Type selfTy) {
20382043
// Check that member operators reference the type of 'Self'.
20392044
if (decl->isInvalid())
20402045
return true;
20412046

20422047
auto *DC = decl->getDeclContext();
20432048

20442049
auto selfNominal = DC->getSelfNominalTypeDecl();
2050+
assert(selfNominal || selfTy);
20452051

2046-
// Check the parameters for a reference to 'Self'.
2052+
// Is the operator a member of a protocol or protocol extension?
20472053
bool isProtocol = isa_and_nonnull<ProtocolDecl>(selfNominal);
2054+
2055+
// Is the operator a member of a tuple extension?
20482056
bool isTuple = isa_and_nonnull<BuiltinTupleDecl>(selfNominal);
20492057

2058+
// Check the parameters for a reference to 'Self'.
20502059
for (auto param : *decl->getParameters()) {
20512060
// Look through a metatype reference, if there is one.
20522061
auto paramType = param->getInterfaceType()->getMetatypeInstanceType();
20532062

2063+
if (isProtocol || isTuple) {
2064+
// For a member of a protocol or tuple extension, is it the 'Self'
2065+
// type parameter?
2066+
if (paramType->isEqual(DC->getSelfInterfaceType()))
2067+
return true;
2068+
2069+
continue;
2070+
}
2071+
2072+
// We have a member operator of a concrete nominal type, or a global operator.
20542073
auto nominal = paramType->getAnyNominal();
2055-
if (type.isNull()) {
2056-
// Is it the same nominal type?
2057-
if (selfNominal && nominal == selfNominal)
2074+
2075+
if (selfTy.isNull()) {
2076+
// We're validating a member operator.
2077+
2078+
// Does the parameter have the right nominal type?
2079+
if (nominal == selfNominal)
20582080
return true;
20592081
} else {
2060-
// Is it the same nominal type? Or a generic (which may or may not match)?
2061-
if (paramType->is<GenericTypeParamType>() ||
2062-
nominal == type->getAnyNominal())
2063-
return true;
2064-
}
2082+
// We're checking a conformance and this operator is a candidate witness.
20652083

2066-
if (isProtocol) {
2067-
// FIXME: Source compatibility hack for Swift 5. The compiler
2068-
// accepts member operators on protocols with existential
2069-
// type arguments. We should consider banning this in Swift 6.
2070-
if (auto existential = paramType->getAs<ExistentialType>()) {
2071-
if (selfNominal == existential->getConstraintType()->getAnyNominal())
2072-
return true;
2073-
}
2074-
}
2084+
// Does the parameter have the right nominal type for the conformance?
2085+
if (nominal == selfTy->getAnyNominal())
2086+
return true;
20752087

2076-
if (isProtocol || isTuple) {
2077-
// For a protocol or tuple extension, is it the 'Self' type parameter?
2078-
if (paramType->isEqual(DC->getSelfInterfaceType()))
2088+
// Otherwise, we might also have a match if the top-level operator is generic.
2089+
if (paramType->is<GenericTypeParamType>())
20792090
return true;
20802091
}
20812092
}

test/api-digester/Inputs/cake.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public protocol PSub: PSuper {
8585
public let GlobalVar = 1
8686

8787
public extension P1 {
88-
static func +(lhs: P1, rhs: P1) -> P1 { return lhs }
88+
static func +(lhs: Self, rhs: Self) -> Self { return lhs }
8989
}
9090

9191
infix operator ..*..

test/api-digester/Outputs/cake-abi.json

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,26 +54,23 @@
5454
"children": [
5555
{
5656
"kind": "TypeNominal",
57-
"name": "P1",
58-
"printedName": "any cake.P1",
59-
"usr": "s:4cake2P1P"
57+
"name": "GenericTypeParam",
58+
"printedName": "τ_0_0"
6059
},
6160
{
6261
"kind": "TypeNominal",
63-
"name": "P1",
64-
"printedName": "any cake.P1",
65-
"usr": "s:4cake2P1P"
62+
"name": "GenericTypeParam",
63+
"printedName": "τ_0_0"
6664
},
6765
{
6866
"kind": "TypeNominal",
69-
"name": "P1",
70-
"printedName": "any cake.P1",
71-
"usr": "s:4cake2P1P"
67+
"name": "GenericTypeParam",
68+
"printedName": "τ_0_0"
7269
}
7370
],
7471
"declKind": "Func",
75-
"usr": "s:4cake2P1PAAE1poiyAaB_pAaB_p_AaB_ptFZ",
76-
"mangledName": "$s4cake2P1PAAE1poiyAaB_pAaB_p_AaB_ptFZ",
72+
"usr": "s:4cake2P1PAAE1poiyxx_xtFZ",
73+
"mangledName": "$s4cake2P1PAAE1poiyxx_xtFZ",
7774
"moduleName": "cake",
7875
"genericSig": "<τ_0_0 where τ_0_0 : cake.P1>",
7976
"sugared_genericSig": "<Self where Self : cake.P1>",

test/api-digester/Outputs/cake.json

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,26 +54,23 @@
5454
"children": [
5555
{
5656
"kind": "TypeNominal",
57-
"name": "P1",
58-
"printedName": "any cake.P1",
59-
"usr": "s:4cake2P1P"
57+
"name": "GenericTypeParam",
58+
"printedName": "Self"
6059
},
6160
{
6261
"kind": "TypeNominal",
63-
"name": "P1",
64-
"printedName": "any cake.P1",
65-
"usr": "s:4cake2P1P"
62+
"name": "GenericTypeParam",
63+
"printedName": "Self"
6664
},
6765
{
6866
"kind": "TypeNominal",
69-
"name": "P1",
70-
"printedName": "any cake.P1",
71-
"usr": "s:4cake2P1P"
67+
"name": "GenericTypeParam",
68+
"printedName": "Self"
7269
}
7370
],
7471
"declKind": "Func",
75-
"usr": "s:4cake2P1PAAE1poiyAaB_pAaB_p_AaB_ptFZ",
76-
"mangledName": "$s4cake2P1PAAE1poiyAaB_pAaB_p_AaB_ptFZ",
72+
"usr": "s:4cake2P1PAAE1poiyxx_xtFZ",
73+
"mangledName": "$s4cake2P1PAAE1poiyxx_xtFZ",
7774
"moduleName": "cake",
7875
"genericSig": "<Self where Self : cake.P1>",
7976
"static": true,

test/decl/func/operator.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,13 +380,15 @@ extension P2 {
380380
}
381381

382382
protocol P3 {
383-
// Okay: refers to P3
383+
// Not allowed: there's no way to infer 'Self' from this interface type
384384
static func %%%(lhs: P3, rhs: Unrelated) -> Unrelated
385+
// expected-error@-1 {{member operator '%%%' of protocol 'P3' must have at least one argument of type 'Self'}}
385386
}
386387

387388
extension P3 {
388-
// Okay: refers to P3
389+
// Not allowed: there's no way to infer 'Self' from this interface type
389390
static func %%%%(lhs: P3, rhs: Unrelated) -> Unrelated { }
391+
// expected-error@-1 {{member operator '%%%%' of protocol 'P3' must have at least one argument of type 'Self'}}
390392
}
391393

392394
// rdar://problem/27940842 - recovery with a non-static '=='.

0 commit comments

Comments
 (0)