Skip to content

Commit 52f5453

Browse files
authored
Merge pull request #76705 from slavapestov/existential-parameterized-composition
Allow existential parameterized compositions: `any P<A> & Q`
2 parents 36859c0 + 1fa9afc commit 52f5453

File tree

7 files changed

+202
-58
lines changed

7 files changed

+202
-58
lines changed

lib/AST/ASTDemangler.cpp

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -767,65 +767,88 @@ Type ASTBuilder::createExistentialMetatypeType(
767767
Type ASTBuilder::createConstrainedExistentialType(
768768
Type base, ArrayRef<BuiltRequirement> constraints,
769769
ArrayRef<BuiltInverseRequirement> inverseRequirements) {
770-
Type constrainedBase;
771-
772-
if (auto baseTy = base->getAs<ProtocolType>()) {
773-
auto baseDecl = baseTy->getDecl();
774-
llvm::SmallDenseMap<Identifier, Type> cmap;
775-
for (const auto &req : constraints) {
776-
switch (req.getKind()) {
777-
case RequirementKind::SameShape:
778-
llvm_unreachable("Same-shape requirement not supported here");
779-
case RequirementKind::Conformance:
780-
case RequirementKind::Superclass:
781-
case RequirementKind::Layout:
782-
continue;
770+
llvm::SmallDenseMap<Identifier, Type> primaryAssociatedTypes;
771+
llvm::SmallDenseSet<Identifier> claimed;
772+
773+
for (const auto &req : constraints) {
774+
switch (req.getKind()) {
775+
case RequirementKind::SameShape:
776+
case RequirementKind::Conformance:
777+
case RequirementKind::Superclass:
778+
case RequirementKind::Layout:
779+
break;
783780

784-
case RequirementKind::SameType:
785-
if (auto *DMT = req.getFirstType()->getAs<DependentMemberType>())
786-
cmap[DMT->getName()] = req.getSecondType();
781+
case RequirementKind::SameType: {
782+
if (auto *memberTy = req.getFirstType()->getAs<DependentMemberType>()) {
783+
if (memberTy->getBase()->is<GenericTypeParamType>()) {
784+
// This is the only case we understand so far.
785+
primaryAssociatedTypes[memberTy->getName()] = req.getSecondType();
786+
continue;
787+
}
787788
}
789+
break;
790+
}
788791
}
792+
793+
// If we end here, we didn't recognize this requirement.
794+
return Type();
795+
}
796+
797+
auto maybeFormParameterizedProtocolType = [&](ProtocolType *protoTy) -> Type {
798+
auto *proto = protoTy->getDecl();
799+
789800
llvm::SmallVector<Type, 4> args;
790-
for (auto *assocTy : baseDecl->getPrimaryAssociatedTypes()) {
791-
auto argTy = cmap.find(assocTy->getName());
792-
if (argTy == cmap.end()) {
793-
return Type();
794-
}
795-
args.push_back(argTy->getSecond());
801+
for (auto *assocTy : proto->getPrimaryAssociatedTypes()) {
802+
auto found = primaryAssociatedTypes.find(assocTy->getName());
803+
if (found == primaryAssociatedTypes.end())
804+
return protoTy;
805+
args.push_back(found->second);
806+
claimed.insert(found->first);
796807
}
797808

798809
// We may not have any arguments because the constrained existential is a
799810
// plain protocol with an inverse requirement.
800-
if (args.empty()) {
801-
constrainedBase =
802-
ProtocolType::get(baseDecl, baseTy, base->getASTContext());
803-
} else {
804-
constrainedBase =
805-
ParameterizedProtocolType::get(base->getASTContext(), baseTy, args);
806-
}
807-
} else if (base->isAny()) {
808-
// The only other case should be that we got an empty PCT, which is equal to
809-
// the Any type. The other constraints should have been encoded in the
810-
// existential's generic signature (and arrive as BuiltInverseRequirement).
811-
constrainedBase = base;
811+
if (args.empty())
812+
return protoTy;
813+
814+
return ParameterizedProtocolType::get(Ctx, protoTy, args);
815+
};
816+
817+
SmallVector<Type, 2> members;
818+
bool hasExplicitAnyObject = false;
819+
InvertibleProtocolSet inverses;
820+
821+
// We're given either a single protocol type, or a composition of protocol
822+
// types. Transform each protocol type to add arguments, if necessary.
823+
if (auto protoTy = base->getAs<ProtocolType>()) {
824+
members.push_back(maybeFormParameterizedProtocolType(protoTy));
812825
} else {
813-
return Type();
826+
auto compositionTy = base->castTo<ProtocolCompositionType>();
827+
hasExplicitAnyObject = compositionTy->hasExplicitAnyObject();
828+
ASSERT(compositionTy->getInverses().empty());
829+
830+
for (auto member : compositionTy->getMembers()) {
831+
if (auto *protoTy = member->getAs<ProtocolType>()) {
832+
members.push_back(maybeFormParameterizedProtocolType(protoTy));
833+
continue;
834+
}
835+
ASSERT(member->getClassOrBoundGenericClass());
836+
members.push_back(member);
837+
}
814838
}
815839

816-
assert(constrainedBase);
840+
// Make sure that all arguments were actually used.
841+
ASSERT(claimed.size() == primaryAssociatedTypes.size());
817842

818843
// Handle inverse requirements.
819844
if (!inverseRequirements.empty()) {
820-
InvertibleProtocolSet inverseSet;
821845
for (const auto &inverseReq : inverseRequirements) {
822-
inverseSet.insert(inverseReq.getKind());
846+
inverses.insert(inverseReq.getKind());
823847
}
824-
constrainedBase = ProtocolCompositionType::get(
825-
Ctx, { constrainedBase }, inverseSet, /*hasExplicitAnyObject=*/false);
826848
}
827849

828-
return ExistentialType::get(constrainedBase);
850+
return ExistentialType::get(ProtocolCompositionType::get(
851+
Ctx, members, inverses, hasExplicitAnyObject));
829852
}
830853

831854
Type ASTBuilder::createSymbolicExtendedExistentialType(NodePointer shapeNode,

lib/Sema/TypeCheckType.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6002,11 +6002,7 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr,
60026002
continue;
60036003
}
60046004

6005-
// FIXME: Support compositions involving parameterized protocol types,
6006-
// like 'any Collection<String> & Sendable', etc.
6007-
if (ty->is<ParameterizedProtocolType>() &&
6008-
!options.isConstraintImplicitExistential() &&
6009-
options.getContext() != TypeResolverContext::ExistentialConstraint) {
6005+
if (ty->is<ParameterizedProtocolType>()) {
60106006
checkMember(tyR->getStartLoc(), ty);
60116007
Members.push_back(ty);
60126008
continue;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %target-typecheck-verify-swift -disable-availability-checking
2+
3+
func f1(_ s: any Sequence<Int> & Hashable) -> any Sequence<Int> {
4+
return s
5+
}
6+
7+
func f2(_ s: any Sequence<Int> & Hashable) -> any Hashable {
8+
return s
9+
}
10+
11+
func f3(_ s: any Sequence<Int> & Hashable) -> any Sequence {
12+
return s
13+
}
14+
15+
func f4(_ s: any Sequence<Int> & Hashable) -> any Sequence & Hashable {
16+
return s
17+
}
18+
19+
func f5(_ s: any Sequence<Int> & Hashable) -> any Sequence<Int> & Equatable {
20+
return s
21+
}
22+
23+
func f6(_ s: any Sequence<Int> & Hashable) -> any Sequence<String> & Hashable {
24+
return s // expected-error {{cannot convert return expression of type 'Int' to return type 'String'}}
25+
}
26+
27+
func f7(_ s: any Sequence<Int> & Hashable) -> any Sequence<String> {
28+
return s // expected-error {{cannot convert return expression of type 'Int' to return type 'String'}}
29+
}
30+
31+
func f8(_ s: any Collection<Int> & Hashable) -> any Sequence<Int> & Hashable {
32+
return s
33+
}
34+
35+
// https://github.com/swiftlang/swift/issues/71012
36+
37+
protocol A<T> {
38+
associatedtype T
39+
}
40+
protocol B {}
41+
typealias C = A & B
42+
typealias D<T> = A<T> & B
43+
44+
struct Foo: C {
45+
typealias T = Int
46+
}
47+
48+
struct Bar<Value> { // expected-note {{arguments to generic parameter 'Value' ('any C' (aka 'any A & B') and 'any A<Int> & B') are expected to be equal}}
49+
let value: Value
50+
}
51+
52+
struct Baz<U> {
53+
let bar: Bar<any D<U>>
54+
}
55+
56+
func run() {
57+
let foo: any C = Foo()
58+
let bar = Bar(value: foo)
59+
_ = Baz<Int>(bar: bar)
60+
// expected-error@-1 {{cannot convert value of type 'Bar<any C>' (aka 'Bar<any A & B>') to expected argument type 'Bar<any A<Int> & B>'}}
61+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// RUN: %target-swift-frontend -emit-ir %s -g -target %target-swift-5.9-abi-triple
2+
3+
public protocol P<A, B> {
4+
associatedtype A
5+
associatedtype B
6+
}
7+
8+
public protocol Q<C> {
9+
associatedtype C
10+
}
11+
12+
public protocol R {}
13+
14+
public class C<T: Equatable> {}
15+
16+
public func foo(_ a: any P<Int, Float> & R) {}
17+
public func foo(_ a: any P<Int, Float> & Q<String>) {}
18+
public func foo(_ a: any P<Int, Float> & Q<String> & R) {}
19+
public func foo(_ a: any P<Int, Float> & Q<String> & R & C<Bool>) {}
20+
public func foo(_ a: any P<Int, Float> & Q<String> & R & AnyObject) {}
21+
22+
public func foo(_ a: any (P<Int, Float> & R).Type) {}
23+
public func foo(_ a: any (P<Int, Float> & Q<String>).Type) {}
24+
public func foo(_ a: any (P<Int, Float> & Q<String> & R).Type) {}
25+
public func foo(_ a: any (P<Int, Float> & Q<String> & R & C<Bool>).Type) {}
26+
public func foo(_ a: any (P<Int, Float> & Q<String> & R & AnyObject).Type) {}
27+
28+
public func foo(_ a: (any P<Int, Float> & R).Type) {}
29+
public func foo(_ a: (any P<Int, Float> & Q<String>).Type) {}
30+
public func foo(_ a: (any P<Int, Float> & Q<String> & R).Type) {}
31+
public func foo(_ a: (any P<Int, Float> & Q<String> & R & C<Bool>).Type) {}
32+
public func foo(_ a: (any P<Int, Float> & Q<String> & R & AnyObject).Type) {}
33+
34+
public struct Foo<each T, U> {
35+
public var a1: (repeat any P<each T, U> & R)
36+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %target-swift-emit-silgen -disable-availability-checking %s | %FileCheck %s
2+
3+
protocol P<A> {
4+
associatedtype A
5+
}
6+
7+
protocol Q<B> {
8+
associatedtype B
9+
}
10+
11+
// All of these should have unique mangling.
12+
func overload(_: any P<Int> & Q<String>) {}
13+
func overload(_: any P<Float> & Q<String>) {}
14+
func overload(_: any P & Q<String>) {}
15+
func overload(_: any P<Int> & Q<Bool>) {}
16+
func overload(_: any P<Float> & Q<Bool>) {}
17+
func overload(_: any P & Q<Bool>) {}
18+
func overload(_: any P<Int> & Q) {}
19+
func overload(_: any P<Float> & Q) {}
20+
func overload(_: any P & Q) {}
21+
func overload(_: any P<Int>) {}
22+
func overload(_: any P<Float>) {}
23+
func overload(_: any P) {}
24+
func overload(_: any Q<Bool>) {}
25+
func overload(_: any Q) {}
26+
27+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSi1AAaCPRts_SS1BAaDPRtsXPF : $@convention(thin) (@in_guaranteed any P<Int> & Q<String>) -> () {
28+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSf1AAaCPRts_SS1BAaDPRtsXPF : $@convention(thin) (@in_guaranteed any P<Float> & Q<String>) -> () {
29+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSS1BAaDPRts_XPF : $@convention(thin) (@in_guaranteed any P & Q<String>) -> () {
30+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSi1AAaCPRts_Sb1BAaDPRtsXPF : $@convention(thin) (@in_guaranteed any P<Int> & Q<Bool>) -> () {
31+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSf1AAaCPRts_Sb1BAaDPRtsXPF : $@convention(thin) (@in_guaranteed any P<Float> & Q<Bool>) -> () {
32+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSb1BAaDPRts_XPF : $@convention(thin) (@in_guaranteed any P & Q<Bool>) -> () {
33+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSi1AAaCPRts_XPF : $@convention(thin) (@in_guaranteed any P<Int> & Q) -> () {
34+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSf1AAaCPRts_XPF : $@convention(thin) (@in_guaranteed any P<Float> & Q) -> () {
35+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpF : $@convention(thin) (@in_guaranteed any P & Q) -> () {
36+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_pSi1AAaCPRts_XPF : $@convention(thin) (@in_guaranteed any P<Int>) -> () {
37+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_pSf1AAaCPRts_XPF : $@convention(thin) (@in_guaranteed any P<Float>) -> () {
38+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_pF : $@convention(thin) (@in_guaranteed any P) -> () {
39+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1Q_pSb1BAaCPRts_XPF : $@convention(thin) (@in_guaranteed any Q<Bool>) -> () {
40+
// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1Q_pF : $@convention(thin) (@in_guaranteed any Q) -> () {

test/decl/protocol/existential_member_access/basic.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -506,8 +506,6 @@ do {
506506
func invariant11() -> Struct<${type}>.InnerGeneric<Void>
507507
// https://github.com/apple/swift/issues/61934
508508
func invariant12() -> any Sequence<${type}>
509-
// FIXME
510-
// expected-error@+1 {{non-protocol, non-class type 'Sequence<${type}>' cannot be used within a protocol-constrained type}}
511509
func invariant13() -> any P & Sequence<${type}>
512510
func invariant14() -> (any Sequence<${type}>).Type
513511
func invariant15() -> any (P & Class<${type}>).Type

test/type/parameterized_existential.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,6 @@ func typeExpr() {
8282
_ = (any Sequence<Int>).self
8383
}
8484

85-
/// Not supported as a protocol composition term for now
86-
87-
protocol SomeProto {}
88-
89-
func protocolCompositionNotSupported1(_: SomeProto & Sequence<Int>) {}
90-
// expected-error@-1 {{non-protocol, non-class type 'Sequence<Int>' cannot be used within a protocol-constrained type}}
91-
92-
func protocolCompositionNotSupported2(_: any SomeProto & Sequence<Int>) {}
93-
// expected-error@-1 {{non-protocol, non-class type 'Sequence<Int>' cannot be used within a protocol-constrained type}}
94-
9585
func increment(_ n : any Collection<Float>) {
9686
for value in n {
9787
_ = value + 1

0 commit comments

Comments
 (0)