diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index 084facdd8e180..9f9ce69996aa0 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -767,65 +767,88 @@ Type ASTBuilder::createExistentialMetatypeType( Type ASTBuilder::createConstrainedExistentialType( Type base, ArrayRef constraints, ArrayRef inverseRequirements) { - Type constrainedBase; - - if (auto baseTy = base->getAs()) { - auto baseDecl = baseTy->getDecl(); - llvm::SmallDenseMap cmap; - for (const auto &req : constraints) { - switch (req.getKind()) { - case RequirementKind::SameShape: - llvm_unreachable("Same-shape requirement not supported here"); - case RequirementKind::Conformance: - case RequirementKind::Superclass: - case RequirementKind::Layout: - continue; + llvm::SmallDenseMap primaryAssociatedTypes; + llvm::SmallDenseSet claimed; + + for (const auto &req : constraints) { + switch (req.getKind()) { + case RequirementKind::SameShape: + case RequirementKind::Conformance: + case RequirementKind::Superclass: + case RequirementKind::Layout: + break; - case RequirementKind::SameType: - if (auto *DMT = req.getFirstType()->getAs()) - cmap[DMT->getName()] = req.getSecondType(); + case RequirementKind::SameType: { + if (auto *memberTy = req.getFirstType()->getAs()) { + if (memberTy->getBase()->is()) { + // This is the only case we understand so far. + primaryAssociatedTypes[memberTy->getName()] = req.getSecondType(); + continue; + } } + break; + } } + + // If we end here, we didn't recognize this requirement. + return Type(); + } + + auto maybeFormParameterizedProtocolType = [&](ProtocolType *protoTy) -> Type { + auto *proto = protoTy->getDecl(); + llvm::SmallVector args; - for (auto *assocTy : baseDecl->getPrimaryAssociatedTypes()) { - auto argTy = cmap.find(assocTy->getName()); - if (argTy == cmap.end()) { - return Type(); - } - args.push_back(argTy->getSecond()); + for (auto *assocTy : proto->getPrimaryAssociatedTypes()) { + auto found = primaryAssociatedTypes.find(assocTy->getName()); + if (found == primaryAssociatedTypes.end()) + return protoTy; + args.push_back(found->second); + claimed.insert(found->first); } // We may not have any arguments because the constrained existential is a // plain protocol with an inverse requirement. - if (args.empty()) { - constrainedBase = - ProtocolType::get(baseDecl, baseTy, base->getASTContext()); - } else { - constrainedBase = - ParameterizedProtocolType::get(base->getASTContext(), baseTy, args); - } - } else if (base->isAny()) { - // The only other case should be that we got an empty PCT, which is equal to - // the Any type. The other constraints should have been encoded in the - // existential's generic signature (and arrive as BuiltInverseRequirement). - constrainedBase = base; + if (args.empty()) + return protoTy; + + return ParameterizedProtocolType::get(Ctx, protoTy, args); + }; + + SmallVector members; + bool hasExplicitAnyObject = false; + InvertibleProtocolSet inverses; + + // We're given either a single protocol type, or a composition of protocol + // types. Transform each protocol type to add arguments, if necessary. + if (auto protoTy = base->getAs()) { + members.push_back(maybeFormParameterizedProtocolType(protoTy)); } else { - return Type(); + auto compositionTy = base->castTo(); + hasExplicitAnyObject = compositionTy->hasExplicitAnyObject(); + ASSERT(compositionTy->getInverses().empty()); + + for (auto member : compositionTy->getMembers()) { + if (auto *protoTy = member->getAs()) { + members.push_back(maybeFormParameterizedProtocolType(protoTy)); + continue; + } + ASSERT(member->getClassOrBoundGenericClass()); + members.push_back(member); + } } - assert(constrainedBase); + // Make sure that all arguments were actually used. + ASSERT(claimed.size() == primaryAssociatedTypes.size()); // Handle inverse requirements. if (!inverseRequirements.empty()) { - InvertibleProtocolSet inverseSet; for (const auto &inverseReq : inverseRequirements) { - inverseSet.insert(inverseReq.getKind()); + inverses.insert(inverseReq.getKind()); } - constrainedBase = ProtocolCompositionType::get( - Ctx, { constrainedBase }, inverseSet, /*hasExplicitAnyObject=*/false); } - return ExistentialType::get(constrainedBase); + return ExistentialType::get(ProtocolCompositionType::get( + Ctx, members, inverses, hasExplicitAnyObject)); } Type ASTBuilder::createSymbolicExtendedExistentialType(NodePointer shapeNode, diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index fb4a10055eb7d..ea57c66aec80a 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -6002,11 +6002,7 @@ TypeResolver::resolveCompositionType(CompositionTypeRepr *repr, continue; } - // FIXME: Support compositions involving parameterized protocol types, - // like 'any Collection & Sendable', etc. - if (ty->is() && - !options.isConstraintImplicitExistential() && - options.getContext() != TypeResolverContext::ExistentialConstraint) { + if (ty->is()) { checkMember(tyR->getStartLoc(), ty); Members.push_back(ty); continue; diff --git a/test/Constraints/parameterized_existential_composition.swift b/test/Constraints/parameterized_existential_composition.swift new file mode 100644 index 0000000000000..b666d22cb2531 --- /dev/null +++ b/test/Constraints/parameterized_existential_composition.swift @@ -0,0 +1,61 @@ +// RUN: %target-typecheck-verify-swift -disable-availability-checking + +func f1(_ s: any Sequence & Hashable) -> any Sequence { + return s +} + +func f2(_ s: any Sequence & Hashable) -> any Hashable { + return s +} + +func f3(_ s: any Sequence & Hashable) -> any Sequence { + return s +} + +func f4(_ s: any Sequence & Hashable) -> any Sequence & Hashable { + return s +} + +func f5(_ s: any Sequence & Hashable) -> any Sequence & Equatable { + return s +} + +func f6(_ s: any Sequence & Hashable) -> any Sequence & Hashable { + return s // expected-error {{cannot convert return expression of type 'Int' to return type 'String'}} +} + +func f7(_ s: any Sequence & Hashable) -> any Sequence { + return s // expected-error {{cannot convert return expression of type 'Int' to return type 'String'}} +} + +func f8(_ s: any Collection & Hashable) -> any Sequence & Hashable { + return s +} + +// https://github.com/swiftlang/swift/issues/71012 + +protocol A { + associatedtype T +} +protocol B {} +typealias C = A & B +typealias D = A & B + +struct Foo: C { + typealias T = Int +} + +struct Bar { // expected-note {{arguments to generic parameter 'Value' ('any C' (aka 'any A & B') and 'any A & B') are expected to be equal}} + let value: Value +} + +struct Baz { + let bar: Bar> +} + +func run() { + let foo: any C = Foo() + let bar = Bar(value: foo) + _ = Baz(bar: bar) + // expected-error@-1 {{cannot convert value of type 'Bar' (aka 'Bar') to expected argument type 'Bar & B>'}} +} \ No newline at end of file diff --git a/test/DebugInfo/parameterized_existential_composition.swift b/test/DebugInfo/parameterized_existential_composition.swift new file mode 100644 index 0000000000000..89b448be390b5 --- /dev/null +++ b/test/DebugInfo/parameterized_existential_composition.swift @@ -0,0 +1,36 @@ +// RUN: %target-swift-frontend -emit-ir %s -g -target %target-swift-5.9-abi-triple + +public protocol P { + associatedtype A + associatedtype B +} + +public protocol Q { + associatedtype C +} + +public protocol R {} + +public class C {} + +public func foo(_ a: any P & R) {} +public func foo(_ a: any P & Q) {} +public func foo(_ a: any P & Q & R) {} +public func foo(_ a: any P & Q & R & C) {} +public func foo(_ a: any P & Q & R & AnyObject) {} + +public func foo(_ a: any (P & R).Type) {} +public func foo(_ a: any (P & Q).Type) {} +public func foo(_ a: any (P & Q & R).Type) {} +public func foo(_ a: any (P & Q & R & C).Type) {} +public func foo(_ a: any (P & Q & R & AnyObject).Type) {} + +public func foo(_ a: (any P & R).Type) {} +public func foo(_ a: (any P & Q).Type) {} +public func foo(_ a: (any P & Q & R).Type) {} +public func foo(_ a: (any P & Q & R & C).Type) {} +public func foo(_ a: (any P & Q & R & AnyObject).Type) {} + +public struct Foo { + public var a1: (repeat any P & R) +} diff --git a/test/SILGen/parameterized_existential_composition.swift b/test/SILGen/parameterized_existential_composition.swift new file mode 100644 index 0000000000000..e14a3a48a0c94 --- /dev/null +++ b/test/SILGen/parameterized_existential_composition.swift @@ -0,0 +1,40 @@ +// RUN: %target-swift-emit-silgen -disable-availability-checking %s | %FileCheck %s + +protocol P { + associatedtype A +} + +protocol Q { + associatedtype B +} + +// All of these should have unique mangling. +func overload(_: any P & Q) {} +func overload(_: any P & Q) {} +func overload(_: any P & Q) {} +func overload(_: any P & Q) {} +func overload(_: any P & Q) {} +func overload(_: any P & Q) {} +func overload(_: any P & Q) {} +func overload(_: any P & Q) {} +func overload(_: any P & Q) {} +func overload(_: any P) {} +func overload(_: any P) {} +func overload(_: any P) {} +func overload(_: any Q) {} +func overload(_: any Q) {} + +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSi1AAaCPRts_SS1BAaDPRtsXPF : $@convention(thin) (@in_guaranteed any P & Q) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSf1AAaCPRts_SS1BAaDPRtsXPF : $@convention(thin) (@in_guaranteed any P & Q) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSS1BAaDPRts_XPF : $@convention(thin) (@in_guaranteed any P & Q) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSi1AAaCPRts_Sb1BAaDPRtsXPF : $@convention(thin) (@in_guaranteed any P & Q) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSf1AAaCPRts_Sb1BAaDPRtsXPF : $@convention(thin) (@in_guaranteed any P & Q) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSb1BAaDPRts_XPF : $@convention(thin) (@in_guaranteed any P & Q) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSi1AAaCPRts_XPF : $@convention(thin) (@in_guaranteed any P & Q) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpSf1AAaCPRts_XPF : $@convention(thin) (@in_guaranteed any P & Q) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_AA1QpF : $@convention(thin) (@in_guaranteed any P & Q) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_pSi1AAaCPRts_XPF : $@convention(thin) (@in_guaranteed any P) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_pSf1AAaCPRts_XPF : $@convention(thin) (@in_guaranteed any P) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1P_pF : $@convention(thin) (@in_guaranteed any P) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1Q_pSb1BAaCPRts_XPF : $@convention(thin) (@in_guaranteed any Q) -> () { +// CHECK-LABEL: sil hidden [ossa] @$s37parameterized_existential_composition8overloadyyAA1Q_pF : $@convention(thin) (@in_guaranteed any Q) -> () { diff --git a/test/decl/protocol/existential_member_access/basic.swift b/test/decl/protocol/existential_member_access/basic.swift index e5590a15347af..c9122f4de11d3 100644 --- a/test/decl/protocol/existential_member_access/basic.swift +++ b/test/decl/protocol/existential_member_access/basic.swift @@ -506,8 +506,6 @@ do { func invariant11() -> Struct<${type}>.InnerGeneric // https://github.com/apple/swift/issues/61934 func invariant12() -> any Sequence<${type}> - // FIXME - // expected-error@+1 {{non-protocol, non-class type 'Sequence<${type}>' cannot be used within a protocol-constrained type}} func invariant13() -> any P & Sequence<${type}> func invariant14() -> (any Sequence<${type}>).Type func invariant15() -> any (P & Class<${type}>).Type diff --git a/test/type/parameterized_existential.swift b/test/type/parameterized_existential.swift index 55032bcf3c60e..0d9acb20d1f89 100644 --- a/test/type/parameterized_existential.swift +++ b/test/type/parameterized_existential.swift @@ -82,16 +82,6 @@ func typeExpr() { _ = (any Sequence).self } -/// Not supported as a protocol composition term for now - -protocol SomeProto {} - -func protocolCompositionNotSupported1(_: SomeProto & Sequence) {} -// expected-error@-1 {{non-protocol, non-class type 'Sequence' cannot be used within a protocol-constrained type}} - -func protocolCompositionNotSupported2(_: any SomeProto & Sequence) {} -// expected-error@-1 {{non-protocol, non-class type 'Sequence' cannot be used within a protocol-constrained type}} - func increment(_ n : any Collection) { for value in n { _ = value + 1