Skip to content

Commit 7d2a240

Browse files
LucianoPAlmeidahamishknight
authored andcommitted
[Diagnostics] Diagnose that we cannot infer the key path type when binding to a hole
1 parent 83697ed commit 7d2a240

File tree

9 files changed

+102
-7
lines changed

9 files changed

+102
-7
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: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7696,6 +7696,12 @@ ConstraintSystem::simplifyKeyPathConstraint(
76967696

76977697
return true;
76987698
};
7699+
7700+
// We have a hole, the solver can't infer the key path type. So let's
7701+
// just assume this is solved.
7702+
if (shouldAttemptFixes() && keyPathTy->isHole()) {
7703+
return SolutionKind::Solved;
7704+
}
76997705

77007706
// If we're fixed to a bound generic type, trying harvesting context from it.
77017707
// However, we don't want a solution that fixes the expression type to
@@ -9502,7 +9508,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
95029508
case FixKind::CoerceToCheckedCast:
95039509
case FixKind::SpecifyObjectLiteralTypeImport:
95049510
case FixKind::AllowKeyPathRootTypeMismatch:
9505-
case FixKind::AllowCoercionToForceCast: {
9511+
case FixKind::AllowCoercionToForceCast:
9512+
case FixKind::SpecifyKeyPathRootType: {
95069513
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
95079514
}
95089515

test/expr/unary/keypath/keypath.swift

Lines changed: 11 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,15 @@ 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+
let _ : AnyKeyPath = \.x
899+
// expected-error@-1 {{'AnyKeyPath' does not provide enough context for root type to be inferred; consider explicitly specifying a root type}} {{25-25=<#Root#>}}
900+
901+
func f(_ i: Int) {}
902+
f(\.x) // expected-error {{cannot infer key path type from context; consider explicitly specifying a root type}} {{6-6=<#Root#>}}
903+
}
904+
896905
func testSyntaxErrors() { // expected-note{{}}
897906
_ = \. ; // expected-error{{expected member name following '.'}}
898907
_ = \.a ;

0 commit comments

Comments
 (0)