Skip to content

Commit

Permalink
Fix solution application for keypath function subtype conversions
Browse files Browse the repository at this point in the history
In \swiftlang#39612 we added subtyping for keypath-function conversions. However
the implementation naively coerced the keypath expression itself to the
inferred supertype, resulting in erroneous results. This patch updates the
solution application logic to build the keypath-function conversion expression
based entirely on the 'natural' keypath type, only converting to the inferred
supertype at the end via the usual coerceToType machinery for function
conversions.
  • Loading branch information
Jumhyn committed Feb 19, 2024
1 parent d35dcc8 commit f6bb955
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 7 deletions.
25 changes: 18 additions & 7 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5117,7 +5117,7 @@ namespace {
!componentTy->getWithoutSpecifierType()->isEqual(leafTy)) {
auto component = KeyPathExpr::Component::forOptionalWrap(leafTy);
resolvedComponents.push_back(component);
componentTy = leafTy;
componentTy = OptionalType::get(componentTy);
}

// Set the resolved components, and cache their types.
Expand All @@ -5133,13 +5133,23 @@ namespace {

// If we've gotten here, the user has used key path literal syntax to form
// a closure. The type checker has given E a function type to indicate
// this; we're going to change E's type to KeyPath<baseTy, leafTy> and
// then wrap it in a larger closure expression with the appropriate type.
// this.
//
// Since functions support more conversions than generic types, we may
// have ended up with a type of (baseTy) -> leafTy, where the actual type
// of the key path is some subclass of KeyPath<baseTy, componentTy>, and
// with componentTy: leafTy.
//
// We're going to change E's type to KeyPath<baseTy, componentTy> and
// then wrap it in a larger closure expression which we will convert to
// appropriate type.

auto kpResultTy = componentTy->getWithoutSpecifierType();

// Compute KeyPath<baseTy, leafTy> and set E's type back to it.
auto kpDecl = cs.getASTContext().getKeyPathDecl();
auto keyPathTy =
BoundGenericType::get(kpDecl, nullptr, { baseTy, leafTy });
BoundGenericType::get(kpDecl, nullptr, { baseTy, kpResultTy });
E->setType(keyPathTy);
cs.cacheType(E);

Expand All @@ -5154,9 +5164,10 @@ namespace {

FunctionType::ExtInfo closureInfo;
auto closureTy =
FunctionType::get({FunctionType::Param(baseTy)}, leafTy, closureInfo);
FunctionType::get({FunctionType::Param(baseTy)}, kpResultTy,
closureInfo);
auto closure = new (ctx)
AutoClosureExpr(/*set body later*/nullptr, leafTy, dc);
AutoClosureExpr(/*set body later*/nullptr, kpResultTy, dc);

auto param = new (ctx) ParamDecl(
SourceLoc(),
Expand Down Expand Up @@ -5208,7 +5219,7 @@ namespace {
auto *application = new (ctx)
KeyPathApplicationExpr(paramRef,
E->getStartLoc(), outerParamRef, E->getEndLoc(),
leafTy, /*implicit=*/true);
kpResultTy, /*implicit=*/true);
cs.cacheType(application);

// Finish up the inner closure.
Expand Down
15 changes: 15 additions & 0 deletions test/SILGen/keypaths.swift
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,18 @@ struct TestKeyPathWithSomeType : DefineSomeType {

}
}

// apple/swift#71423
protocol CodingKey {}

struct URICoderCodingKey : CodingKey {}

struct CodingStackEntry {
var key: URICoderCodingKey
}

struct Test {
var codingStack: [CodingStackEntry]
var codingPath: [any CodingKey] { codingStack.map(\.key) }
// CHECK: keypath $KeyPath<CodingStackEntry, URICoderCodingKey>, (root $CodingStackEntry; stored_property #CodingStackEntry.key : $URICoderCodingKey)
}

0 comments on commit f6bb955

Please sign in to comment.