Skip to content

Commit 8cc0ffb

Browse files
committed
Sema: Allow protocol compositions containing parameterized protocol types
For now, this only works with conformance requirements and not existential types. Fixes rdar://problem/92197412.
1 parent f517e06 commit 8cc0ffb

File tree

5 files changed

+97
-47
lines changed

5 files changed

+97
-47
lines changed

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,21 @@ using namespace swift;
6464

6565
#define DEBUG_TYPE "TypeCheckDeclPrimary"
6666

67+
static Type containsParameterizedProtocolType(Type inheritedTy) {
68+
if (inheritedTy->is<ParameterizedProtocolType>()) {
69+
return inheritedTy;
70+
}
71+
72+
if (auto *compositionTy = inheritedTy->getAs<ProtocolCompositionType>()) {
73+
for (auto memberTy : compositionTy->getMembers()) {
74+
if (auto paramTy = containsParameterizedProtocolType(memberTy))
75+
return paramTy;
76+
}
77+
}
78+
79+
return Type();
80+
}
81+
6782
/// Check the inheritance clause of a type declaration or extension thereof.
6883
///
6984
/// This routine performs detailed checking of the inheritance clause of the
@@ -212,10 +227,10 @@ static void checkInheritanceClause(
212227
inheritedAnyObject = { i, inherited.getSourceRange() };
213228
}
214229

215-
if (inheritedTy->is<ParameterizedProtocolType>()) {
230+
if (auto paramTy = containsParameterizedProtocolType(inheritedTy)) {
216231
if (!isa<ProtocolDecl>(decl)) {
217232
decl->diagnose(diag::inheritance_from_parameterized_protocol,
218-
inheritedTy);
233+
paramTy);
219234
}
220235
continue;
221236
}
@@ -232,9 +247,15 @@ static void checkInheritanceClause(
232247
continue;
233248
}
234249

250+
// Classes and protocols can inherit from subclass existentials.
251+
// For classes, we check for a duplicate superclass below.
252+
// For protocols, the requirement machine emits a requirement
253+
// conflict instead.
254+
if (isa<ProtocolDecl>(decl))
255+
continue;
256+
235257
// AnyObject is not allowed except on protocols.
236-
if (layout.hasExplicitAnyObject &&
237-
!isa<ProtocolDecl>(decl)) {
258+
if (layout.hasExplicitAnyObject) {
238259
decl->diagnose(diag::inheritance_from_anyobject);
239260
continue;
240261
}
@@ -243,12 +264,6 @@ static void checkInheritanceClause(
243264
if (!layout.explicitSuperclass)
244265
continue;
245266

246-
// Classes and protocols can inherit from subclass existentials.
247-
// For classes, we check for a duplicate superclass below.
248-
// For protocols, the GSB emits its own warning instead.
249-
if (isa<ProtocolDecl>(decl))
250-
continue;
251-
252267
assert(isa<ClassDecl>(decl));
253268
assert(canHaveSuperclass);
254269
inheritedTy = layout.explicitSuperclass;

lib/Sema/TypeCheckType.cpp

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3921,18 +3921,33 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr,
39213921
}
39223922

39233923
// FIXME: Support compositions involving parameterized protocol types,
3924-
// like Collection<String> & Sendable, etc.
3925-
if (ty->isConstraintType() &&
3926-
!ty->is<ParameterizedProtocolType>()) {
3927-
auto layout = ty->getExistentialLayout();
3928-
if (auto superclass = layout.explicitSuperclass)
3929-
if (checkSuperclass(tyR->getStartLoc(), superclass))
3930-
continue;
3931-
if (!layout.getProtocols().empty())
3924+
// like 'any Collection<String> & Sendable', etc.
3925+
if (ty->isConstraintType()) {
3926+
if (ty->is<ProtocolType>()) {
39323927
HasProtocol = true;
3928+
Members.push_back(ty);
3929+
continue;
3930+
}
39333931

3934-
Members.push_back(ty);
3935-
continue;
3932+
if (ty->is<ParameterizedProtocolType>() &&
3933+
options.isParameterizedProtocolSupported() &&
3934+
options.getContext() != TypeResolverContext::ExistentialConstraint) {
3935+
HasProtocol = true;
3936+
Members.push_back(ty);
3937+
continue;
3938+
}
3939+
3940+
if (ty->is<ProtocolCompositionType>()) {
3941+
auto layout = ty->getExistentialLayout();
3942+
if (auto superclass = layout.explicitSuperclass)
3943+
if (checkSuperclass(tyR->getStartLoc(), superclass))
3944+
continue;
3945+
if (!layout.getProtocols().empty())
3946+
HasProtocol = true;
3947+
3948+
Members.push_back(ty);
3949+
continue;
3950+
}
39363951
}
39373952

39383953
diagnose(tyR->getStartLoc(),

lib/Sema/TypeCheckType.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ enum class TypeResolverContext : uint8_t {
132132
/// Whether we are in the constraint type of an existential type.
133133
ExistentialConstraint,
134134

135-
/// Whether we are in a requirement of a generic declaration.
135+
/// Whether we are in the constraint type of a conformance requirement.
136136
GenericRequirement,
137137

138138
/// Whether we are in a same-type requirement of a generic

test/type/parameterized_existential.swift

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=on -requirement-machine-inferred-signatures=on -disable-availability-checking
22

3-
protocol Sequence<Element> {
3+
protocol Sequence<Element> { // expected-note {{'Sequence' declared here}}
44
associatedtype Element
55
}
66

7+
// 'any' is required here
8+
9+
func takesSequenceOfInt1(_: Sequence<Int>) {}
10+
// expected-error@-1 {{protocol type with type arguments can only be used as a generic constraint}}
11+
12+
func returnsSequenceOfInt1() -> Sequence<Int> {}
13+
// expected-error@-1 {{protocol type with type arguments can only be used as a generic constraint}}
14+
715
struct ConcreteSequence<Element> : Sequence {}
816

917
extension Sequence {
@@ -72,3 +80,23 @@ protocol Pair<X, Y> where Self.X == Self.Y {
7280

7381
func splay(_ x: some Pair<Int, String>) -> (Int, String) { fatalError() }
7482
// expected-error@-1 {{no type for 'some Pair<Int, String>.X' can satisfy both 'some Pair<Int, String>.X == String' and 'some Pair<Int, String>.X == Int'}}
83+
84+
func typeExpr() {
85+
_ = Sequence<Int>.self
86+
// expected-error@-1 {{protocol type with type arguments can only be used as a generic constraint}}
87+
88+
_ = any Sequence<Int>.self
89+
// expected-error@-1 {{'self' is not a member type of protocol 'parameterized_existential.Sequence<Swift.Int>'}}
90+
91+
_ = (any Sequence<Int>).self
92+
}
93+
94+
/// Not supported as a protocol composition term for now
95+
96+
protocol SomeProto {}
97+
98+
func protocolCompositionNotSupported1(_: SomeProto & Sequence<Int>) {}
99+
// expected-error@-1 {{non-protocol, non-class type 'Sequence<Int>' cannot be used within a protocol-constrained type}}
100+
101+
func protocolCompositionNotSupported2(_: any SomeProto & Sequence<Int>) {}
102+
// expected-error@-1 {{non-protocol, non-class type 'Sequence<Int>' cannot be used within a protocol-constrained type}}

test/type/parameterized_protocol.swift

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ protocol Invalid5<Element, Element> {
2929

3030
/// Test semantics
3131

32-
protocol Sequence<Element> { // expected-note {{'Sequence' declared here}}
32+
protocol Sequence<Element> {
3333
associatedtype Element
34-
// expected-note@-1 {{protocol requires nested type 'Element'; do you want to add it?}}
34+
// expected-note@-1 2{{protocol requires nested type 'Element'; do you want to add it?}}
3535
}
3636

3737
extension Sequence {
@@ -180,31 +180,23 @@ extension Sequence<Int> {
180180
}
181181

182182

183-
/// Cannot use parameterized protocol as the type of a value
183+
/// Protocol compositions
184184

185-
func takesSequenceOfInt1(_: Sequence<Int>) {}
186-
// expected-error@-1 {{protocol type with type arguments can only be used as a generic constraint}}
185+
// CHECK-LABEL: .testComposition1@
186+
// CHECK-NEXT: Generic signature: <T where T : Sendable, T : Sequence, T.[Sequence]Element == Int>
187+
func testComposition1<T : Sequence<Int> & Sendable>(_: T) {}
187188

188-
func returnsSequenceOfInt1() -> Sequence<Int> {}
189-
// expected-error@-1 {{protocol type with type arguments can only be used as a generic constraint}}
189+
// CHECK-LABEL: .testComposition2@
190+
// CHECK-NEXT: Generic signature:
191+
// CHECK-NEXT: Canonical generic signature: <τ_0_0 where τ_0_0 : Sendable, τ_0_0 : Sequence, τ_0_0.[Sequence]Element == Int>
192+
func testComposition2(_: some Sequence<Int> & Sendable) {}
190193

191-
func takesSequenceOfInt2(_: any Sequence<Int>) {}
192-
193-
func returnsSequenceOfInt2() -> any Sequence<Int> {}
194-
195-
func typeExpr() {
196-
_ = Sequence<Int>.self
197-
// expected-error@-1 {{protocol type with type arguments can only be used as a generic constraint}}
198-
199-
_ = any Sequence<Int>.self
200-
// expected-error@-1 {{'self' is not a member type of protocol 'parameterized_protocol.Sequence<Swift.Int>'}}
201-
202-
_ = (any Sequence<Int>).self
194+
// CHECK-LABEL: parameterized_protocol.(file).TestCompositionProtocol1@
195+
// CHECK: Requirement signature: <Self where Self.[TestCompositionProtocol1]S : Sendable, Self.[TestCompositionProtocol1]S : Sequence, Self.[TestCompositionProtocol1]S.[Sequence]Element == Int>
196+
protocol TestCompositionProtocol1 {
197+
associatedtype S : Sequence<Int> & Sendable
203198
}
204199

205-
/// Not supported as a protocol composition term for now
206-
207-
protocol SomeProto {}
208-
209-
func protocolCompositionNotSupported(_: SomeProto & Sequence<Int>) {}
210-
// expected-error@-1 {{non-protocol, non-class type 'Sequence<Int>' cannot be used within a protocol-constrained type}}
200+
struct TestStructComposition : Sequence<Int> & Sendable {}
201+
// expected-error@-1 {{cannot inherit from protocol type with generic argument 'Sequence<Int>'}}
202+
// expected-error@-2 {{type 'TestStructComposition' does not conform to protocol 'Sequence'}}

0 commit comments

Comments
 (0)