Skip to content

Commit b9e8e2c

Browse files
authored
Merge pull request #59179 from slavapestov/parameterized-protocol-improvements
Parameterized protocol improvements
2 parents 49b489b + bf00a71 commit b9e8e2c

File tree

8 files changed

+168
-90
lines changed

8 files changed

+168
-90
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3748,8 +3748,6 @@ ERROR(protocol_declares_unknown_primary_assoc_type,none,
37483748
(Identifier, Type))
37493749
ERROR(protocol_declares_duplicate_primary_assoc_type,none,
37503750
"duplicate primary associated type name %0", (Identifier))
3751-
ERROR(parameterized_protocol_not_supported,none,
3752-
"protocol type with type arguments can only be used as a generic constraint", ())
37533751
ERROR(protocol_does_not_have_primary_assoc_type,none,
37543752
"cannot specialize protocol type %0", (Type))
37553753
ERROR(parameterized_protocol_type_argument_count_mismatch,none,

include/swift/AST/Types.h

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6780,38 +6780,6 @@ inline TypeBase *TypeBase::getDesugaredType() {
67806780
return cast<SugarType>(this)->getSinglyDesugaredType()->getDesugaredType();
67816781
}
67826782

6783-
inline bool TypeBase::hasSimpleTypeRepr() const {
6784-
// NOTE: Please keep this logic in sync with TypeRepr::isSimple().
6785-
switch (getKind()) {
6786-
case TypeKind::Function:
6787-
case TypeKind::GenericFunction:
6788-
return false;
6789-
6790-
case TypeKind::Metatype:
6791-
return !cast<const AnyMetatypeType>(this)->hasRepresentation();
6792-
6793-
case TypeKind::ExistentialMetatype:
6794-
case TypeKind::Existential:
6795-
return false;
6796-
6797-
case TypeKind::OpaqueTypeArchetype:
6798-
case TypeKind::OpenedArchetype:
6799-
return false;
6800-
6801-
case TypeKind::ProtocolComposition: {
6802-
// 'Any', 'AnyObject' and single protocol compositions are simple
6803-
auto composition = cast<const ProtocolCompositionType>(this);
6804-
auto memberCount = composition->getMembers().size();
6805-
if (composition->hasExplicitAnyObject())
6806-
return memberCount == 0;
6807-
return memberCount <= 1;
6808-
}
6809-
6810-
default:
6811-
return true;
6812-
}
6813-
}
6814-
68156783
} // end namespace swift
68166784

68176785
namespace llvm {

lib/AST/Type.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6224,6 +6224,46 @@ bool TypeBase::isForeignReferenceType() {
62246224
return false;
62256225
}
62266226

6227+
bool TypeBase::hasSimpleTypeRepr() const {
6228+
// NOTE: Please keep this logic in sync with TypeRepr::isSimple().
6229+
switch (getKind()) {
6230+
case TypeKind::Function:
6231+
case TypeKind::GenericFunction:
6232+
return false;
6233+
6234+
case TypeKind::Metatype:
6235+
return !cast<const AnyMetatypeType>(this)->hasRepresentation();
6236+
6237+
case TypeKind::ExistentialMetatype:
6238+
case TypeKind::Existential:
6239+
return false;
6240+
6241+
case TypeKind::OpaqueTypeArchetype:
6242+
case TypeKind::OpenedArchetype:
6243+
return false;
6244+
6245+
case TypeKind::ProtocolComposition: {
6246+
// 'Any', 'AnyObject' and single protocol compositions are simple
6247+
auto composition = cast<const ProtocolCompositionType>(this);
6248+
auto memberCount = composition->getMembers().size();
6249+
if (composition->hasExplicitAnyObject())
6250+
return memberCount == 0;
6251+
return memberCount <= 1;
6252+
}
6253+
6254+
case TypeKind::GenericTypeParam: {
6255+
if (auto *decl = cast<const GenericTypeParamType>(this)->getDecl()) {
6256+
return !decl->isOpaqueType();
6257+
}
6258+
6259+
return true;
6260+
}
6261+
6262+
default:
6263+
return true;
6264+
}
6265+
}
6266+
62276267
bool CanType::isForeignReferenceType() {
62286268
if (auto *classDecl = getPointer()->lookThroughAllOptionalTypes()->getClassOrBoundGenericClass())
62296269
return classDecl->isForeignReferenceType();

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: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -629,15 +629,8 @@ static Type applyGenericArguments(Type type, TypeResolution resolution,
629629
auto &diags = ctx.Diags;
630630

631631
if (auto *protoType = type->getAs<ProtocolType>()) {
632-
// Build ParameterizedProtocolType if the protocol has a primary associated
633-
// type and we're in a supported context (for now just generic requirements,
634-
// inheritance clause, extension binding).
635-
if (!resolution.getOptions().isParameterizedProtocolSupported()) {
636-
diags.diagnose(loc, diag::parameterized_protocol_not_supported);
637-
return ErrorType::get(ctx);
638-
}
639-
640632
auto *protoDecl = protoType->getDecl();
633+
641634
auto assocTypes = protoDecl->getPrimaryAssociatedTypes();
642635
if (assocTypes.empty()) {
643636
diags.diagnose(loc, diag::protocol_does_not_have_primary_assoc_type,
@@ -657,6 +650,18 @@ static Type applyGenericArguments(Type type, TypeResolution resolution,
657650
return ErrorType::get(ctx);
658651
}
659652

653+
// Build ParameterizedProtocolType if the protocol has a primary associated
654+
// type and we're in a supported context (for now just generic requirements,
655+
// inheritance clause, extension binding).
656+
if (!resolution.getOptions().isParameterizedProtocolSupported()) {
657+
diags.diagnose(loc, diag::existential_requires_any,
658+
protoDecl->getDeclaredInterfaceType(),
659+
protoDecl->getExistentialType(),
660+
/*isAlias=*/isa<TypeAliasType>(type.getPointer()));
661+
662+
return ErrorType::get(ctx);
663+
}
664+
660665
// Disallow opaque types anywhere in the structure of the generic arguments
661666
// to a parameterized existential type.
662667
if (options.is(TypeResolverContext::ExistentialConstraint))
@@ -3932,18 +3937,33 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr,
39323937
}
39333938

39343939
// FIXME: Support compositions involving parameterized protocol types,
3935-
// like Collection<String> & Sendable, etc.
3936-
if (ty->isConstraintType() &&
3937-
!ty->is<ParameterizedProtocolType>()) {
3938-
auto layout = ty->getExistentialLayout();
3939-
if (auto superclass = layout.explicitSuperclass)
3940-
if (checkSuperclass(tyR->getStartLoc(), superclass))
3941-
continue;
3942-
if (!layout.getProtocols().empty())
3940+
// like 'any Collection<String> & Sendable', etc.
3941+
if (ty->isConstraintType()) {
3942+
if (ty->is<ProtocolType>()) {
39433943
HasProtocol = true;
3944+
Members.push_back(ty);
3945+
continue;
3946+
}
39443947

3945-
Members.push_back(ty);
3946-
continue;
3948+
if (ty->is<ParameterizedProtocolType>() &&
3949+
options.isParameterizedProtocolSupported() &&
3950+
options.getContext() != TypeResolverContext::ExistentialConstraint) {
3951+
HasProtocol = true;
3952+
Members.push_back(ty);
3953+
continue;
3954+
}
3955+
3956+
if (ty->is<ProtocolCompositionType>()) {
3957+
auto layout = ty->getExistentialLayout();
3958+
if (auto superclass = layout.explicitSuperclass)
3959+
if (checkSuperclass(tyR->getStartLoc(), superclass))
3960+
continue;
3961+
if (!layout.getProtocols().empty())
3962+
HasProtocol = true;
3963+
3964+
Members.push_back(ty);
3965+
continue;
3966+
}
39473967
}
39483968

39493969
diagnose(tyR->getStartLoc(),

lib/Sema/TypeCheckType.h

Lines changed: 3 additions & 3 deletions
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
@@ -288,13 +288,13 @@ class TypeResolutionOptions {
288288
switch (context) {
289289
case Context::Inherited:
290290
case Context::ExtensionBinding:
291+
case Context::TypeAliasDecl:
292+
case Context::GenericTypeAliasDecl:
291293
case Context::GenericRequirement:
292294
case Context::ExistentialConstraint:
293295
case Context::MetatypeBase:
294296
return true;
295297
case Context::None:
296-
case Context::TypeAliasDecl:
297-
case Context::GenericTypeAliasDecl:
298298
case Context::InExpression:
299299
case Context::ExplicitCastExpr:
300300
case Context::ForEachStmt:

test/type/parameterized_existential.swift

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
// RUN: %target-typecheck-verify-swift -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 {{use of protocol 'Sequence' as a type must be written 'any Sequence'}}
11+
12+
func returnsSequenceOfInt1() -> Sequence<Int> {}
13+
// expected-error@-1 {{use of protocol 'Sequence' as a type must be written 'any Sequence'}}
14+
715
struct ConcreteSequence<Element> : Sequence {}
816

917
extension Sequence {
@@ -65,10 +73,22 @@ func saturation(_ dry: any Sponge, _ wet: any Sponge<Int, Int>) {
6573
// expected-note@-1 {{did you mean to use 'as!' to force downcast?}}
6674
}
6775

68-
protocol Pair<X, Y> where Self.X == Self.Y {
69-
associatedtype X
70-
associatedtype Y
76+
func typeExpr() {
77+
_ = Sequence<Int>.self
78+
// expected-error@-1 {{use of protocol 'Sequence' as a type must be written 'any Sequence'}}
79+
80+
_ = any Sequence<Int>.self
81+
// expected-error@-1 {{'self' is not a member type of protocol 'parameterized_existential.Sequence<Swift.Int>'}}
82+
83+
_ = (any Sequence<Int>).self
7184
}
7285

73-
func splay(_ x: some Pair<Int, String>) -> (Int, String) { fatalError() }
74-
// 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'}}
86+
/// Not supported as a protocol composition term for now
87+
88+
protocol SomeProto {}
89+
90+
func protocolCompositionNotSupported1(_: SomeProto & Sequence<Int>) {}
91+
// expected-error@-1 {{non-protocol, non-class type 'Sequence<Int>' cannot be used within a protocol-constrained type}}
92+
93+
func protocolCompositionNotSupported2(_: any SomeProto & Sequence<Int>) {}
94+
// 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: 36 additions & 19 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,48 @@ extension Sequence<Int> {
180180
}
181181

182182

183-
/// Cannot use parameterized protocol as the type of a value
183+
/// Constraint aliases
184184

185-
func takesSequenceOfInt1(_: Sequence<Int>) {}
186-
// expected-error@-1 {{protocol type with type arguments can only be used as a generic constraint}}
185+
typealias SequenceOfInt = Sequence<Int>
186+
typealias SequenceOf<T> = Sequence<T>
187187

188-
func returnsSequenceOfInt1() -> Sequence<Int> {}
189-
// expected-error@-1 {{protocol type with type arguments can only be used as a generic constraint}}
188+
// CHECK-LABEL: .testConstraintAlias1@
189+
// CHECK-NEXT: Generic signature: <T where T : Sequence, T.[Sequence]Element == Int>
190+
func testConstraintAlias1<T : SequenceOfInt>(_: T) {}
190191

191-
func takesSequenceOfInt2(_: any Sequence<Int>) {}
192+
// CHECK-LABEL: .testConstraintAlias2@
193+
// CHECK-NEXT: Generic signature: <T where T : Sequence, T.[Sequence]Element == String>
194+
func testConstraintAlias2<T : SequenceOf<String>>(_: T) {}
192195

193-
func returnsSequenceOfInt2() -> any Sequence<Int> {}
194196

195-
func typeExpr() {
196-
_ = Sequence<Int>.self
197-
// expected-error@-1 {{protocol type with type arguments can only be used as a generic constraint}}
197+
/// Protocol compositions
198198

199-
_ = any Sequence<Int>.self
200-
// expected-error@-1 {{'self' is not a member type of protocol 'parameterized_protocol.Sequence<Swift.Int>'}}
199+
// CHECK-LABEL: .testComposition1@
200+
// CHECK-NEXT: Generic signature: <T where T : Sendable, T : Sequence, T.[Sequence]Element == Int>
201+
func testComposition1<T : Sequence<Int> & Sendable>(_: T) {}
201202

202-
_ = (any Sequence<Int>).self
203+
// CHECK-LABEL: .testComposition2@
204+
// CHECK-NEXT: Generic signature:
205+
// CHECK-NEXT: Canonical generic signature: <τ_0_0 where τ_0_0 : Sendable, τ_0_0 : Sequence, τ_0_0.[Sequence]Element == Int>
206+
func testComposition2(_: some Sequence<Int> & Sendable) {}
207+
208+
// CHECK-LABEL: parameterized_protocol.(file).TestCompositionProtocol1@
209+
// CHECK: Requirement signature: <Self where Self.[TestCompositionProtocol1]S : Sendable, Self.[TestCompositionProtocol1]S : Sequence, Self.[TestCompositionProtocol1]S.[Sequence]Element == Int>
210+
protocol TestCompositionProtocol1 {
211+
associatedtype S : Sequence<Int> & Sendable
203212
}
204213

205-
/// Not supported as a protocol composition term for now
214+
struct TestStructComposition : Sequence<Int> & Sendable {}
215+
// expected-error@-1 {{cannot inherit from protocol type with generic argument 'Sequence<Int>'}}
216+
// expected-error@-2 {{type 'TestStructComposition' does not conform to protocol 'Sequence'}}
217+
206218

207-
protocol SomeProto {}
219+
/// Conflicts
220+
221+
protocol Pair<X, Y> where Self.X == Self.Y {
222+
associatedtype X
223+
associatedtype Y
224+
}
208225

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}}
226+
func splay(_ x: some Pair<Int, String>) -> (Int, String) { fatalError() }
227+
// 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'}}

0 commit comments

Comments
 (0)