Skip to content

Commit ea0af30

Browse files
authored
Merge pull request #31862 from hamishknight/solved-holistically-5.3
[5.3] [CS] Diagnostic improvements for key paths
2 parents 12a5243 + cd20d14 commit ea0af30

File tree

10 files changed

+183
-31
lines changed

10 files changed

+183
-31
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ ERROR(could_not_find_value_dynamic_member_corrected,none,
7171
ERROR(could_not_find_value_dynamic_member,none,
7272
"value of type %0 has no dynamic member %2 using key path from root type %1",
7373
(Type, Type, DeclNameRef))
74+
ERROR(cannot_infer_contextual_keypath_type_specify_root,none,
75+
"cannot infer key path type from context; consider explicitly specifying a root type", ())
76+
ERROR(cannot_infer_keypath_root_anykeypath_context,none,
77+
"'AnyKeyPath' does not provide enough context for root type to be inferred; "
78+
"consider explicitly specifying a root type", ())
7479

7580
ERROR(could_not_find_type_member,none,
7681
"type %0 has no member %1", (Type, DeclNameRef))

lib/Sema/CSBindings.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,8 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
11051105
} else if (srcLocator->getAnchor() &&
11061106
isa<ObjectLiteralExpr>(srcLocator->getAnchor())) {
11071107
fix = SpecifyObjectLiteralTypeImport::create(cs, dstLocator);
1108+
} else if (srcLocator->isKeyPathRoot()) {
1109+
fix = SpecifyKeyPathRootType::create(cs, dstLocator);
11081110
}
11091111

11101112
if (fix && cs.recordFix(fix))

lib/Sema/CSDiagnostics.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6347,3 +6347,25 @@ bool MultiArgFuncKeyPathFailure::diagnoseAsError() {
63476347
resolveType(functionType));
63486348
return true;
63496349
}
6350+
6351+
bool UnableToInferKeyPathRootFailure::diagnoseAsError() {
6352+
assert(isExpr<KeyPathExpr>(getAnchor()) && "Expected key path expression");
6353+
auto &ctx = getASTContext();
6354+
auto contextualType = getContextualType(getAnchor());
6355+
auto *keyPathExpr = castToExpr<KeyPathExpr>(getAnchor());
6356+
6357+
auto emitKeyPathDiagnostic = [&]() {
6358+
if (contextualType &&
6359+
contextualType->getAnyNominal() == ctx.getAnyKeyPathDecl()) {
6360+
return emitDiagnostic(
6361+
diag::cannot_infer_keypath_root_anykeypath_context);
6362+
}
6363+
return emitDiagnostic(
6364+
diag::cannot_infer_contextual_keypath_type_specify_root);
6365+
};
6366+
6367+
emitKeyPathDiagnostic()
6368+
.highlight(keyPathExpr->getLoc())
6369+
.fixItInsertAfter(keyPathExpr->getStartLoc(), "<#Root#>");
6370+
return true;
6371+
}

lib/Sema/CSDiagnostics.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,6 +2069,21 @@ class MultiArgFuncKeyPathFailure final : public FailureDiagnostic {
20692069
bool diagnoseAsError() override;
20702070
};
20712071

2072+
/// Diagnose a failure to infer a KeyPath type by context.
2073+
///
2074+
/// ```swift
2075+
/// _ = \.x
2076+
/// let _ : AnyKeyPath = \.x
2077+
/// ```
2078+
class UnableToInferKeyPathRootFailure final : public FailureDiagnostic {
2079+
public:
2080+
UnableToInferKeyPathRootFailure(const Solution &solution,
2081+
ConstraintLocator *locator)
2082+
: FailureDiagnostic(solution, locator) {}
2083+
2084+
bool diagnoseAsError() override;
2085+
};
2086+
20722087
} // end namespace constraints
20732088
} // end namespace swift
20742089

lib/Sema/CSFix.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,3 +1353,17 @@ AllowKeyPathRootTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
13531353
return new (cs.getAllocator())
13541354
AllowKeyPathRootTypeMismatch(cs, lhs, rhs, locator);
13551355
}
1356+
1357+
SpecifyKeyPathRootType *
1358+
SpecifyKeyPathRootType::create(ConstraintSystem &cs,
1359+
ConstraintLocator *locator) {
1360+
return new (cs.getAllocator())
1361+
SpecifyKeyPathRootType(cs, locator);
1362+
}
1363+
1364+
bool SpecifyKeyPathRootType::diagnose(const Solution &solution,
1365+
bool asNote) const {
1366+
UnableToInferKeyPathRootFailure failure(solution, getLocator());
1367+
1368+
return failure.diagnose(asNote);
1369+
}

lib/Sema/CSFix.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,11 @@ enum class FixKind : uint8_t {
261261
AllowKeyPathRootTypeMismatch,
262262

263263
/// Allow key path to be bound to a function type with more than 1 argument
264-
AllowMultiArgFuncKeyPathMismatch
264+
AllowMultiArgFuncKeyPathMismatch,
265+
266+
/// Specify key path root type when it cannot be infered from context.
267+
SpecifyKeyPathRootType,
268+
265269
};
266270

267271
class ConstraintFix {
@@ -1820,7 +1824,7 @@ class AllowCoercionToForceCast final : public ContextualMismatch {
18201824
/// bar[keyPath: keyPath]
18211825
/// }
18221826
/// \endcode
1823-
class AllowKeyPathRootTypeMismatch : public ContextualMismatch {
1827+
class AllowKeyPathRootTypeMismatch final : public ContextualMismatch {
18241828
protected:
18251829
AllowKeyPathRootTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
18261830
ConstraintLocator *locator)
@@ -1838,6 +1842,21 @@ class AllowKeyPathRootTypeMismatch : public ContextualMismatch {
18381842
create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator);
18391843
};
18401844

1845+
class SpecifyKeyPathRootType final : public ConstraintFix {
1846+
SpecifyKeyPathRootType(ConstraintSystem &cs, ConstraintLocator *locator)
1847+
: ConstraintFix(cs, FixKind::SpecifyKeyPathRootType, locator) {}
1848+
1849+
public:
1850+
std::string getName() const {
1851+
return "specify key path root type";
1852+
}
1853+
1854+
bool diagnose(const Solution &solution, bool asNote = false) const;
1855+
1856+
static SpecifyKeyPathRootType *create(ConstraintSystem &cs,
1857+
ConstraintLocator *locator);
1858+
};
1859+
18411860
} // end namespace constraints
18421861
} // end namespace swift
18431862

lib/Sema/CSGen.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3427,7 +3427,8 @@ namespace {
34273427
auto rootLocator =
34283428
CS.getConstraintLocator(E, ConstraintLocator::KeyPathRoot);
34293429
auto locator = CS.getConstraintLocator(E);
3430-
Type root = CS.createTypeVariable(rootLocator, TVO_CanBindToNoEscape);
3430+
Type root = CS.createTypeVariable(rootLocator, TVO_CanBindToNoEscape |
3431+
TVO_CanBindToHole);
34313432

34323433
// If a root type was explicitly given, then resolve it now.
34333434
if (auto rootRepr = E->getRootType()) {
@@ -3569,7 +3570,8 @@ namespace {
35693570
// path components.
35703571
auto typeLoc =
35713572
CS.getConstraintLocator(locator, ConstraintLocator::KeyPathType);
3572-
Type kpTy = CS.createTypeVariable(typeLoc, TVO_CanBindToNoEscape);
3573+
Type kpTy = CS.createTypeVariable(typeLoc, TVO_CanBindToNoEscape |
3574+
TVO_CanBindToHole);
35733575
CS.addKeyPathConstraint(kpTy, root, rvalueBase, componentTypeVars,
35743576
locator);
35753577
return kpTy;

lib/Sema/CSSimplify.cpp

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7696,6 +7696,32 @@ ConstraintSystem::simplifyKeyPathConstraint(
76967696

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

77007726
// If we're fixed to a bound generic type, trying harvesting context from it.
77017727
// However, we don't want a solution that fixes the expression type to
@@ -7745,34 +7771,11 @@ ConstraintSystem::simplifyKeyPathConstraint(
77457771
// to determine whether the result will be a function type vs BGT KeyPath
77467772
// type, so continue through components to create new constraint at the
77477773
// end.
7748-
if (!overload || anyComponentsUnresolved) {
7774+
if (!overload) {
77497775
if (flags.contains(TMF_GenerateConstraints)) {
77507776
anyComponentsUnresolved = true;
77517777
continue;
77527778
}
7753-
7754-
if (shouldAttemptFixes()) {
7755-
auto typeVar =
7756-
llvm::find_if(componentTypeVars, [&](TypeVariableType *typeVar) {
7757-
auto *locator = typeVar->getImpl().getLocator();
7758-
auto elt = locator->findLast<LocatorPathElt::KeyPathComponent>();
7759-
return elt && elt->getIndex() == i;
7760-
});
7761-
7762-
// If one of the components haven't been resolved, let's check
7763-
// whether it has been determined to be a "hole" and if so,
7764-
// let's allow component validation to contiunue.
7765-
//
7766-
// This helps to, for example, diagnose problems with missing
7767-
// members used as part of a key path.
7768-
if (typeVar != componentTypeVars.end() &&
7769-
(*typeVar)->getImpl().canBindToHole()) {
7770-
anyComponentsUnresolved = true;
7771-
capability = ReadOnly;
7772-
continue;
7773-
}
7774-
}
7775-
77767779
return SolutionKind::Unsolved;
77777780
}
77787781

@@ -9502,7 +9505,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
95029505
case FixKind::CoerceToCheckedCast:
95039506
case FixKind::SpecifyObjectLiteralTypeImport:
95049507
case FixKind::AllowKeyPathRootTypeMismatch:
9505-
case FixKind::AllowCoercionToForceCast: {
9508+
case FixKind::AllowCoercionToForceCast:
9509+
case FixKind::SpecifyKeyPathRootType: {
95069510
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
95079511
}
95089512

test/Constraints/rdar62201037.swift

Lines changed: 31 additions & 0 deletions
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

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,9 @@ func testKeyPath(sub: Sub, optSub: OptSub,
195195

196196
let _: AnyKeyPath = \A.property
197197
let _: AnyKeyPath = \C<A>.value
198-
let _: AnyKeyPath = \.property // expected-error{{ambiguous}}
198+
let _: AnyKeyPath = \.property // expected-error {{'AnyKeyPath' does not provide enough context for root type to be inferred; consider explicitly specifying a root type}} {{24-24=<#Root#>}}
199199
let _: AnyKeyPath = \C.value // expected-error{{generic parameter 'T' could not be inferred}}
200-
let _: AnyKeyPath = \.value // expected-error{{ambiguous}}
200+
let _: AnyKeyPath = \.value // expected-error {{'AnyKeyPath' does not provide enough context for root type to be inferred; consider explicitly specifying a root type}} {{24-24=<#Root#>}}
201201

202202
let _ = \Prop.[nonHashableSub] // expected-error{{subscript index of type 'NonHashableSub' in a key path must be Hashable}}
203203
let _ = \Prop.[sub, sub]
@@ -893,6 +893,44 @@ struct SR_12290 {
893893
}
894894
}
895895

896+
func testKeyPathHole() {
897+
_ = \.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+
900+
let _ : AnyKeyPath = \.x
901+
// 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#>}}
904+
905+
func f(_ i: Int) {}
906+
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'}}
932+
}
933+
896934
func testSyntaxErrors() { // expected-note{{}}
897935
_ = \. ; // expected-error{{expected member name following '.'}}
898936
_ = \.a ;

0 commit comments

Comments
 (0)