Skip to content

Commit 1c222a8

Browse files
authored
Merge pull request #59183 from slavapestov/parameterized-protocol-improvements-5.7
Parameterized protocol improvements [5.7]
2 parents b53946a + b05df02 commit 1c222a8

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
@@ -3788,8 +3788,6 @@ ERROR(protocol_declares_unknown_primary_assoc_type,none,
37883788
(Identifier, Type))
37893789
ERROR(protocol_declares_duplicate_primary_assoc_type,none,
37903790
"duplicate primary associated type name %0", (Identifier))
3791-
ERROR(parameterized_protocol_not_supported,none,
3792-
"protocol type with type arguments can only be used as a generic constraint", ())
37933791
ERROR(protocol_does_not_have_primary_assoc_type,none,
37943792
"cannot specialize protocol type %0", (Type))
37953793
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
@@ -6752,38 +6752,6 @@ inline TypeBase *TypeBase::getDesugaredType() {
67526752
return cast<SugarType>(this)->getSinglyDesugaredType()->getDesugaredType();
67536753
}
67546754

6755-
inline bool TypeBase::hasSimpleTypeRepr() const {
6756-
// NOTE: Please keep this logic in sync with TypeRepr::isSimple().
6757-
switch (getKind()) {
6758-
case TypeKind::Function:
6759-
case TypeKind::GenericFunction:
6760-
return false;
6761-
6762-
case TypeKind::Metatype:
6763-
return !cast<const AnyMetatypeType>(this)->hasRepresentation();
6764-
6765-
case TypeKind::ExistentialMetatype:
6766-
case TypeKind::Existential:
6767-
return false;
6768-
6769-
case TypeKind::OpaqueTypeArchetype:
6770-
case TypeKind::OpenedArchetype:
6771-
return false;
6772-
6773-
case TypeKind::ProtocolComposition: {
6774-
// 'Any', 'AnyObject' and single protocol compositions are simple
6775-
auto composition = cast<const ProtocolCompositionType>(this);
6776-
auto memberCount = composition->getMembers().size();
6777-
if (composition->hasExplicitAnyObject())
6778-
return memberCount == 0;
6779-
return memberCount <= 1;
6780-
}
6781-
6782-
default:
6783-
return true;
6784-
}
6785-
}
6786-
67876755
} // end namespace swift
67886756

67896757
namespace llvm {

lib/AST/Type.cpp

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

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

39233928
// 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())
3929+
// like 'any Collection<String> & Sendable', etc.
3930+
if (ty->isConstraintType()) {
3931+
if (ty->is<ProtocolType>()) {
39323932
HasProtocol = true;
3933+
Members.push_back(ty);
3934+
continue;
3935+
}
39333936

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

39383958
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 -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 {{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)