Skip to content

Commit b4cc626

Browse files
authored
Merge pull request #31671 from LucianoPAlmeida/SR-12745-diagnostics-keypath
[Diagnostics] Improve keypath diagnostic when cannot be inferred by context
2 parents 9aa4433 + 9f5c113 commit b4cc626

File tree

9 files changed

+105
-7
lines changed

9 files changed

+105
-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: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,11 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
11071107
cs, TypeVar->getImpl().getLocator());
11081108
if (cs.recordFix(fix))
11091109
return true;
1110+
} else if (srcLocator->isKeyPathRoot()) {
1111+
auto *fix =
1112+
SpecifyKeyPathRootType::create(cs, TypeVar->getImpl().getLocator());
1113+
if (cs.recordFix(fix))
1114+
return true;
11101115
}
11111116
}
11121117
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6271,3 +6271,25 @@ bool MultiArgFuncKeyPathFailure::diagnoseAsError() {
62716271
resolveType(functionType));
62726272
return true;
62736273
}
6274+
6275+
bool UnableToInferKeyPathRootFailure::diagnoseAsError() {
6276+
assert(isExpr<KeyPathExpr>(getAnchor()) && "Expected key path expression");
6277+
auto &ctx = getASTContext();
6278+
auto contextualType = getContextualType(getAnchor());
6279+
auto *keyPathExpr = castToExpr<KeyPathExpr>(getAnchor());
6280+
6281+
auto emitKeyPathDiagnostic = [&]() {
6282+
if (contextualType &&
6283+
contextualType->getAnyNominal() == ctx.getAnyKeyPathDecl()) {
6284+
return emitDiagnostic(
6285+
diag::cannot_infer_keypath_root_anykeypath_context);
6286+
}
6287+
return emitDiagnostic(
6288+
diag::cannot_infer_contextual_keypath_type_specify_root);
6289+
};
6290+
6291+
emitKeyPathDiagnostic()
6292+
.highlight(keyPathExpr->getLoc())
6293+
.fixItInsertAfter(keyPathExpr->getStartLoc(), "<#Root#>");
6294+
return true;
6295+
}

lib/Sema/CSDiagnostics.h

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

2061+
/// Diagnose a failure to infer a KeyPath type by context.
2062+
///
2063+
/// ```swift
2064+
/// _ = \.x
2065+
/// let _ : AnyKeyPath = \.x
2066+
/// ```
2067+
class UnableToInferKeyPathRootFailure final : public FailureDiagnostic {
2068+
public:
2069+
UnableToInferKeyPathRootFailure(const Solution &solution,
2070+
ConstraintLocator *locator)
2071+
: FailureDiagnostic(solution, locator) {}
2072+
2073+
bool diagnoseAsError() override;
2074+
};
2075+
20612076
} // end namespace constraints
20622077
} // end namespace swift
20632078

lib/Sema/CSFix.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,3 +1323,17 @@ AllowKeyPathRootTypeMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
13231323
return new (cs.getAllocator())
13241324
AllowKeyPathRootTypeMismatch(cs, lhs, rhs, locator);
13251325
}
1326+
1327+
SpecifyKeyPathRootType *
1328+
SpecifyKeyPathRootType::create(ConstraintSystem &cs,
1329+
ConstraintLocator *locator) {
1330+
return new (cs.getAllocator())
1331+
SpecifyKeyPathRootType(cs, locator);
1332+
}
1333+
1334+
bool SpecifyKeyPathRootType::diagnose(const Solution &solution,
1335+
bool asNote) const {
1336+
UnableToInferKeyPathRootFailure failure(solution, getLocator());
1337+
1338+
return failure.diagnose(asNote);
1339+
}

lib/Sema/CSFix.h

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

261261
/// Allow key path to be bound to a function type with more than 1 argument
262-
AllowMultiArgFuncKeyPathMismatch
262+
AllowMultiArgFuncKeyPathMismatch,
263+
264+
/// Specify key path root type when it cannot be infered from context.
265+
SpecifyKeyPathRootType,
266+
263267
};
264268

265269
class ConstraintFix {
@@ -1805,7 +1809,7 @@ class AllowCoercionToForceCast final : public ContextualMismatch {
18051809
/// bar[keyPath: keyPath]
18061810
/// }
18071811
/// \endcode
1808-
class AllowKeyPathRootTypeMismatch : public ContextualMismatch {
1812+
class AllowKeyPathRootTypeMismatch final : public ContextualMismatch {
18091813
protected:
18101814
AllowKeyPathRootTypeMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
18111815
ConstraintLocator *locator)
@@ -1823,6 +1827,21 @@ class AllowKeyPathRootTypeMismatch : public ContextualMismatch {
18231827
create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator);
18241828
};
18251829

1830+
class SpecifyKeyPathRootType final : public ConstraintFix {
1831+
SpecifyKeyPathRootType(ConstraintSystem &cs, ConstraintLocator *locator)
1832+
: ConstraintFix(cs, FixKind::SpecifyKeyPathRootType, locator) {}
1833+
1834+
public:
1835+
std::string getName() const {
1836+
return "specify key path root type";
1837+
}
1838+
1839+
bool diagnose(const Solution &solution, bool asNote = false) const;
1840+
1841+
static SpecifyKeyPathRootType *create(ConstraintSystem &cs,
1842+
ConstraintLocator *locator);
1843+
};
1844+
18261845
} // end namespace constraints
18271846
} // end namespace swift
18281847

lib/Sema/CSGen.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3398,7 +3398,8 @@ namespace {
33983398
auto rootLocator =
33993399
CS.getConstraintLocator(E, ConstraintLocator::KeyPathRoot);
34003400
auto locator = CS.getConstraintLocator(E);
3401-
Type root = CS.createTypeVariable(rootLocator, TVO_CanBindToNoEscape);
3401+
Type root = CS.createTypeVariable(rootLocator, TVO_CanBindToNoEscape |
3402+
TVO_CanBindToHole);
34023403

34033404
// If a root type was explicitly given, then resolve it now.
34043405
if (auto rootRepr = E->getRootType()) {
@@ -3540,7 +3541,8 @@ namespace {
35403541
// path components.
35413542
auto typeLoc =
35423543
CS.getConstraintLocator(locator, ConstraintLocator::KeyPathType);
3543-
Type kpTy = CS.createTypeVariable(typeLoc, TVO_CanBindToNoEscape);
3544+
Type kpTy = CS.createTypeVariable(typeLoc, TVO_CanBindToNoEscape |
3545+
TVO_CanBindToHole);
35443546
CS.addKeyPathConstraint(kpTy, root, rvalueBase, componentTypeVars,
35453547
locator);
35463548
return kpTy;

lib/Sema/CSSimplify.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7663,6 +7663,12 @@ ConstraintSystem::simplifyKeyPathConstraint(
76637663

76647664
return true;
76657665
};
7666+
7667+
// We have a hole, the solver can't infer the key path type. So let's
7668+
// just assume this is solved.
7669+
if (shouldAttemptFixes() && keyPathTy->isHole()) {
7670+
return SolutionKind::Solved;
7671+
}
76667672

76677673
// If we're fixed to a bound generic type, trying harvesting context from it.
76687674
// However, we don't want a solution that fixes the expression type to
@@ -9482,7 +9488,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
94829488
case FixKind::CoerceToCheckedCast:
94839489
case FixKind::SpecifyObjectLiteralTypeImport:
94849490
case FixKind::AllowKeyPathRootTypeMismatch:
9485-
case FixKind::AllowCoercionToForceCast: {
9491+
case FixKind::AllowCoercionToForceCast:
9492+
case FixKind::SpecifyKeyPathRootType: {
94869493
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
94879494
}
94889495

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)