From ff45fec1d87b241476d4086fe26001c5c2a44b59 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 26 Oct 2023 14:54:59 -0700 Subject: [PATCH 1/2] [Typed throws] Handle key-path literal used with a function that has typed throws When providing a key-path literal for a parameter of function type where that function type has a generic parameter for its thrown error type, infer `Never` for the generic argument because key paths don't throw. Thanks to @xedin for realizing that this would be an issue. --- lib/Sema/CSSimplify.cpp | 10 ++++++++++ test/decl/func/typed_throws.swift | 11 ++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index e925f2cb657d1..310e6405f5566 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -12248,6 +12248,16 @@ ConstraintSystem::simplifyKeyPathConstraint( // { root in root[keyPath: kp] }. boundRoot = fnTy->getParams()[0].getParameterType(); boundValue = fnTy->getResult(); + + // Key paths never throw, so if the function has a thrown error type + // that is a type variable, infer it to be Never. + if (auto thrownError = fnTy->getThrownError()) { + if (thrownError->isTypeVariableOrMember()) { + (void)matchTypes( + thrownError, getASTContext().getNeverType(), + ConstraintKind::Equal, TMF_GenerateConstraints, locator); + } + } } if (boundRoot && diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift index f30a41068062f..83e358fe0c2fd 100644 --- a/test/decl/func/typed_throws.swift +++ b/test/decl/func/typed_throws.swift @@ -76,10 +76,14 @@ func mapArray(_ array: [T], body: (T) throws(E) -> U) throws(E) return resultArray } +struct Person { + var name: String +} + func addOrThrowUntyped(_ i: Int, _ j: Int) throws -> Int { i + j } func addOrThrowMyError(_ i: Int, _ j: Int) throws(MyError) -> Int { i + j } -func testMapArray(numbers: [Int]) { +func testMapArray(numbers: [Int], friends: [Person]) { // Note: try is not required, because this throws Never _ = mapArray(numbers) { $0 + 1 } @@ -101,6 +105,11 @@ func testMapArray(numbers: [Int]) { } catch { let _: Int = error // expected-error{{cannot convert value of type 'MyError' to specified type 'Int'}} } + + do { + // Keypath-as-function + _ = mapArray(friends, body: \Person.name) + } } // Inference of Error conformance from the use of a generic parameter in typed From d3ede1915089e20511c4b9dbcab88e4b85e74bc4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 27 Oct 2023 12:50:38 -0700 Subject: [PATCH 2/2] Generalize inference of Error type requirements from typed throws --- .../RequirementMachine/RequirementMachineRequests.cpp | 5 ++++- lib/Sema/TypeCheckGeneric.cpp | 10 ++++------ test/decl/func/typed_throws.swift | 11 +++++++++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp index da61e29d3d955..eab486be933b5 100644 --- a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp +++ b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp @@ -849,8 +849,11 @@ InferredGenericSignatureRequest::evaluate( // Finish by adding any remaining requirements. This is used to introduce // inferred same-type requirements when building the generic signature of // an extension whose extended type is a generic typealias. + SmallVector rawAddedRequirements; for (const auto &req : addedRequirements) - requirements.push_back({req, SourceLoc(), /*wasInferred=*/true}); + desugarRequirement(req, SourceLoc(), rawAddedRequirements, errors); + for (const auto &req : rawAddedRequirements) + requirements.push_back({req, SourceLoc(), /*inferred=*/true}); // Re-order requirements so that inferred requirements appear last. This // ensures that if an inferred requirement is redundant with some other diff --git a/lib/Sema/TypeCheckGeneric.cpp b/lib/Sema/TypeCheckGeneric.cpp index f4b61a6ec8bf2..2bfd2bb08b793 100644 --- a/lib/Sema/TypeCheckGeneric.cpp +++ b/lib/Sema/TypeCheckGeneric.cpp @@ -768,12 +768,10 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator, inferenceSources.emplace_back(thrownTypeRepr, thrownType); // Add conformance of this type to the Error protocol. - if (thrownType->isTypeParameter()) { - if (auto errorProtocol = ctx.getErrorDecl()) { - extraReqs.push_back( - Requirement(RequirementKind::Conformance, thrownType, - errorProtocol->getDeclaredInterfaceType())); - } + if (auto errorProtocol = ctx.getErrorDecl()) { + extraReqs.push_back( + Requirement(RequirementKind::Conformance, thrownType, + errorProtocol->getDeclaredInterfaceType())); } } } diff --git a/test/decl/func/typed_throws.swift b/test/decl/func/typed_throws.swift index 83e358fe0c2fd..25c5211d85d90 100644 --- a/test/decl/func/typed_throws.swift +++ b/test/decl/func/typed_throws.swift @@ -135,3 +135,14 @@ struct HasASubscript { // expected-error@+1{{thrown type 'any Codable & Error' (aka 'any Decodable & Encodable & Error') does not conform to the 'Error' protocol}} func throwCodableErrors() throws(any Codable & Error) { } + +enum Either { +case first(First) +case second(Second) +} + +extension Either: Error where First: Error, Second: Error { } + +func f(_ error: Either) throws(Either) { + throw error +}