Skip to content

Commit e2da5c5

Browse files
authored
Merge pull request swiftlang#31829 from hamishknight/solved-holistically
2 parents d67638a + aa0ad55 commit e2da5c5

File tree

3 files changed

+85
-28
lines changed

3 files changed

+85
-28
lines changed

lib/Sema/CSSimplify.cpp

+25-28
Original file line numberDiff line numberDiff line change
@@ -7683,10 +7683,30 @@ ConstraintSystem::simplifyKeyPathConstraint(
76837683
return true;
76847684
};
76857685

7686-
// We have a hole, the solver can't infer the key path type. So let's
7687-
// just assume this is solved.
7688-
if (shouldAttemptFixes() && keyPathTy->isHole()) {
7689-
return SolutionKind::Solved;
7686+
// If we have a hole somewhere in the key path, the solver won't be able to
7687+
// infer the key path type. So let's just assume this is solved.
7688+
if (shouldAttemptFixes()) {
7689+
if (keyPathTy->isHole())
7690+
return SolutionKind::Solved;
7691+
7692+
// If the root type has been bound to a hole, we cannot infer it.
7693+
if (getFixedTypeRecursive(rootTy, /*wantRValue*/ true)->isHole())
7694+
return SolutionKind::Solved;
7695+
7696+
// If we have e.g a missing member somewhere, a component type variable
7697+
// will have been marked as a potential hole.
7698+
// FIXME: This relies on the fact that we only mark an overload type
7699+
// variable as a potential hole once we've added a corresponding fix. We
7700+
// can't use 'isHole' instead, as that doesn't handle cases where the
7701+
// overload type variable gets bound to another type from the context rather
7702+
// than a hole. We need to come up with a better way of handling the
7703+
// relationship between key paths and overloads.
7704+
if (llvm::any_of(componentTypeVars, [&](TypeVariableType *tv) {
7705+
return tv->getImpl().getLocator()->isForKeyPathComponent() &&
7706+
tv->getImpl().canBindToHole();
7707+
})) {
7708+
return SolutionKind::Solved;
7709+
}
76907710
}
76917711

76927712
// If we're fixed to a bound generic type, trying harvesting context from it.
@@ -7737,34 +7757,11 @@ ConstraintSystem::simplifyKeyPathConstraint(
77377757
// to determine whether the result will be a function type vs BGT KeyPath
77387758
// type, so continue through components to create new constraint at the
77397759
// end.
7740-
if (!overload || anyComponentsUnresolved) {
7760+
if (!overload) {
77417761
if (flags.contains(TMF_GenerateConstraints)) {
77427762
anyComponentsUnresolved = true;
77437763
continue;
77447764
}
7745-
7746-
if (shouldAttemptFixes()) {
7747-
auto typeVar =
7748-
llvm::find_if(componentTypeVars, [&](TypeVariableType *typeVar) {
7749-
auto *locator = typeVar->getImpl().getLocator();
7750-
auto elt = locator->findLast<LocatorPathElt::KeyPathComponent>();
7751-
return elt && elt->getIndex() == i;
7752-
});
7753-
7754-
// If one of the components haven't been resolved, let's check
7755-
// whether it has been determined to be a "hole" and if so,
7756-
// let's allow component validation to contiunue.
7757-
//
7758-
// This helps to, for example, diagnose problems with missing
7759-
// members used as part of a key path.
7760-
if (typeVar != componentTypeVars.end() &&
7761-
(*typeVar)->getImpl().canBindToHole()) {
7762-
anyComponentsUnresolved = true;
7763-
capability = ReadOnly;
7764-
continue;
7765-
}
7766-
}
7767-
77687765
return SolutionKind::Unsolved;
77697766
}
77707767

test/Constraints/rdar62201037.swift

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %target-swift-frontend %s -verify -emit-sil -o /dev/null
2+
3+
struct R<T> {
4+
var str: String?
5+
}
6+
7+
func map<A, B>(e: (A) -> B) -> () -> R<B> {
8+
fatalError()
9+
}
10+
func map<A, B>(_ : (A) -> B) -> (A?) -> B? {
11+
fatalError()
12+
}
13+
14+
infix operator |>
15+
func |> <A, B> (g: A, h: (A) -> B) -> B { h(g) }
16+
17+
infix operator ^^^
18+
func ^^^ <A, B, C>(j: ((B) -> C) -> A, k: String) {}
19+
20+
extension WritableKeyPath {
21+
static func ^^^ (l: WritableKeyPath, m: Value) -> (Root) -> Root {
22+
fatalError()
23+
}
24+
}
25+
26+
func foo<T>(_ s: String, _ rt: R<T>?) -> String? {
27+
return rt.flatMap { _ in
28+
rt |> map(\.str ^^^ s)
29+
}
30+
.flatMap(\.str)
31+
}

test/expr/unary/keypath/keypath.swift

+29
Original file line numberDiff line numberDiff line change
@@ -895,11 +895,40 @@ struct SR_12290 {
895895

896896
func testKeyPathHole() {
897897
_ = \.x // expected-error {{cannot infer key path type from context; consider explicitly specifying a root type}} {{8-8=<#Root#>}}
898+
_ = \.x.y // expected-error {{cannot infer key path type from context; consider explicitly specifying a root type}} {{8-8=<#Root#>}}
899+
898900
let _ : AnyKeyPath = \.x
899901
// expected-error@-1 {{'AnyKeyPath' does not provide enough context for root type to be inferred; consider explicitly specifying a root type}} {{25-25=<#Root#>}}
902+
let _ : AnyKeyPath = \.x.y
903+
// expected-error@-1 {{'AnyKeyPath' does not provide enough context for root type to be inferred; consider explicitly specifying a root type}} {{25-25=<#Root#>}}
900904

901905
func f(_ i: Int) {}
902906
f(\.x) // expected-error {{cannot infer key path type from context; consider explicitly specifying a root type}} {{6-6=<#Root#>}}
907+
f(\.x.y) // expected-error {{cannot infer key path type from context; consider explicitly specifying a root type}} {{6-6=<#Root#>}}
908+
909+
// FIXME(SR-12827): Instead of "generic parameter 'T' could not be inferred",
910+
// we should offer the same diagnostic as above.
911+
func provideValueButNotRoot<T>(_ fn: (T) -> String) {} // expected-note 2{{in call to function 'provideValueButNotRoot'}}
912+
provideValueButNotRoot(\.x) // expected-error {{generic parameter 'T' could not be inferred}}
913+
provideValueButNotRoot(\.x.y) // expected-error {{generic parameter 'T' could not be inferred}}
914+
provideValueButNotRoot(\String.foo) // expected-error {{value of type 'String' has no member 'foo'}}
915+
916+
func provideKPValueButNotRoot<T>(_ kp: KeyPath<T, String>) {} // expected-note 3{{in call to function 'provideKPValueButNotRoot'}}
917+
provideKPValueButNotRoot(\.x) // expected-error {{generic parameter 'T' could not be inferred}}
918+
provideKPValueButNotRoot(\.x.y) // expected-error {{generic parameter 'T' could not be inferred}}
919+
provideKPValueButNotRoot(\String.foo)
920+
// expected-error@-1 {{value of type 'String' has no member 'foo'}}
921+
// expected-error@-2 {{generic parameter 'T' could not be inferred}}
922+
}
923+
924+
func testMissingMember() {
925+
let _: KeyPath<String, String> = \.foo // expected-error {{value of type 'String' has no member 'foo'}}
926+
let _: KeyPath<String, String> = \.foo.bar // expected-error {{value of type 'String' has no member 'foo'}}
927+
928+
let _: PartialKeyPath<String> = \.foo // expected-error {{value of type 'String' has no member 'foo'}}
929+
let _: PartialKeyPath<String> = \.foo.bar // expected-error {{value of type 'String' has no member 'foo'}}
930+
931+
_ = \String.x.y // expected-error {{value of type 'String' has no member 'x'}}
903932
}
904933

905934
func testSyntaxErrors() { // expected-note{{}}

0 commit comments

Comments
 (0)