Skip to content

Commit 8d514ba

Browse files
authored
Merge pull request #24328 from xedin/improve-contextual-diags-for-keypath-5.1
[5.1][TypeChecker] Improve contextual mismatch diagnostics for key path
2 parents ce9945f + ea0fb6e commit 8d514ba

File tree

8 files changed

+69
-8
lines changed

8 files changed

+69
-8
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,6 +1367,14 @@ bool ContextualFailure::diagnoseAsError() {
13671367
break;
13681368
}
13691369

1370+
case ConstraintLocator::ContextualType: {
1371+
if (isKnownKeyPathType(FromType) && isKnownKeyPathType(ToType)) {
1372+
diagnostic = diag::cannot_convert_initializer_value;
1373+
break;
1374+
}
1375+
1376+
LLVM_FALLTHROUGH;
1377+
}
13701378
default:
13711379
return false;
13721380
}

lib/Sema/CSFix.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,10 @@ AllowInvalidRefInKeyPath::create(ConstraintSystem &cs, RefKind kind,
477477
return new (cs.getAllocator())
478478
AllowInvalidRefInKeyPath(cs, kind, member, locator);
479479
}
480+
481+
KeyPathContextualMismatch *
482+
KeyPathContextualMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
483+
ConstraintLocator *locator) {
484+
return new (cs.getAllocator())
485+
KeyPathContextualMismatch(cs, lhs, rhs, locator);
486+
}

lib/Sema/CSFix.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,31 @@ class ContextualMismatch : public ConstraintFix {
458458
ConstraintLocator *locator);
459459
};
460460

461+
/// Detect situations where key path doesn't have capability required
462+
/// by the context e.g. read-only vs. writable, or either root or value
463+
/// types are incorrect e.g.
464+
///
465+
/// ```swift
466+
/// struct S { let foo: Int }
467+
/// let _: WritableKeyPath<S, Int> = \.foo
468+
/// ```
469+
///
470+
/// Here context requires a writable key path but `foo` property is
471+
/// read-only.
472+
class KeyPathContextualMismatch final : public ContextualMismatch {
473+
KeyPathContextualMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
474+
ConstraintLocator *locator)
475+
: ContextualMismatch(cs, lhs, rhs, locator) {}
476+
477+
public:
478+
std::string getName() const override {
479+
return "fix key path contextual mismatch";
480+
}
481+
482+
static KeyPathContextualMismatch *
483+
create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator);
484+
};
485+
461486
/// Detect situations when argument of the @autoclosure parameter is itself
462487
/// marked as @autoclosure and is not applied. Form a fix which suggests a
463488
/// proper way to forward such arguments, e.g.:

lib/Sema/CSSimplify.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2052,6 +2052,14 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
20522052
}
20532053

20542054
case ConstraintLocator::ContextualType: {
2055+
// If both types are key path, the only differences
2056+
// between them are mutability and/or root, value type mismatch.
2057+
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
2058+
auto *fix = KeyPathContextualMismatch::create(
2059+
cs, lhs, rhs, cs.getConstraintLocator(locator));
2060+
conversionsOrFixes.push_back(fix);
2061+
}
2062+
20552063
if (lhs->is<FunctionType>() && !rhs->is<AnyFunctionType>() &&
20562064
isa<ClosureExpr>(anchor)) {
20572065
auto *fix = ContextualMismatch::create(cs, lhs, rhs,
@@ -5228,8 +5236,10 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
52285236

52295237
auto resolvedKPTy = BoundGenericType::get(kpDecl, nullptr,
52305238
{rootTy, valueTy});
5231-
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind,
5232-
subflags, locator);
5239+
// Let's check whether deduced key path type would match
5240+
// expected contextual one.
5241+
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind, subflags,
5242+
locator.withPathElement(ConstraintLocator::ContextualType));
52335243
}
52345244

52355245
ConstraintSystem::SolutionKind

lib/Sema/ConstraintSystem.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2707,6 +2707,12 @@ void ConstraintSystem::generateConstraints(
27072707
}
27082708
}
27092709

2710+
bool constraints::isKnownKeyPathType(Type type) {
2711+
if (auto *BGT = type->getAs<BoundGenericType>())
2712+
return isKnownKeyPathDecl(type->getASTContext(), BGT->getDecl());
2713+
return false;
2714+
}
2715+
27102716
bool constraints::isKnownKeyPathDecl(ASTContext &ctx, ValueDecl *decl) {
27112717
return decl == ctx.getKeyPathDecl() || decl == ctx.getWritableKeyPathDecl() ||
27122718
decl == ctx.getReferenceWritableKeyPathDecl() ||

lib/Sema/ConstraintSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3948,6 +3948,10 @@ class DisjunctionChoiceProducer : public BindingProducer<DisjunctionChoice> {
39483948
}
39493949
};
39503950

3951+
/// Determine whether given type is a known one
3952+
/// for a key path `{Writable, ReferenceWritable}KeyPath`.
3953+
bool isKnownKeyPathType(Type type);
3954+
39513955
/// Determine whether given declaration is one for a key path
39523956
/// `{Writable, ReferenceWritable}KeyPath`.
39533957
bool isKnownKeyPathDecl(ASTContext &ctx, ValueDecl *decl);

test/Constraints/keypath_swift_5.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ struct S {
44
let i: Int
55

66
init() {
7-
let _: WritableKeyPath<S, Int> = \.i // expected-error {{type of expression is ambiguous without more context}}
7+
let _: WritableKeyPath<S, Int> = \.i // expected-error {{cannot convert value of type 'KeyPath<S, Int>' to specified type 'WritableKeyPath<S, Int>'}}
88

99
S()[keyPath: \.i] = 1
1010
// expected-error@-1 {{cannot assign through subscript: immutable key path}}
1111
}
1212
}
1313

1414
func test() {
15-
let _: WritableKeyPath<C, Int> = \.i // expected-error {{type of expression is ambiguous without more context}}
15+
let _: WritableKeyPath<C, Int> = \.i // expected-error {{cannot convert value of type 'KeyPath<C, Int>' to specified type 'WritableKeyPath<C, Int>'}}
1616

1717
C()[keyPath: \.i] = 1
1818
// expected-error@-1 {{cannot assign through subscript: immutable key path}}

test/expr/unary/keypath/keypath.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,16 @@ func testKeyPath(sub: Sub, optSub: OptSub,
125125
let _: PartialKeyPath<A> = \.property
126126
let _: KeyPath<A, Prop> = \.property
127127
let _: WritableKeyPath<A, Prop> = \.property
128-
// expected-error@+1{{ambiguous}} (need to improve diagnostic)
129128
let _: ReferenceWritableKeyPath<A, Prop> = \.property
129+
//expected-error@-1 {{cannot convert value of type 'WritableKeyPath<A, Prop>' to specified type 'ReferenceWritableKeyPath<A, Prop>'}}
130130

131131
// FIXME: shouldn't be ambiguous
132132
// expected-error@+1{{ambiguous}}
133133
let _: PartialKeyPath<A> = \.[sub]
134134
let _: KeyPath<A, A> = \.[sub]
135135
let _: WritableKeyPath<A, A> = \.[sub]
136-
// expected-error@+1{{ambiguous}} (need to improve diagnostic)
137136
let _: ReferenceWritableKeyPath<A, A> = \.[sub]
137+
// expected-error@-1 {{cannot convert value of type 'WritableKeyPath<A, A>' to specified type 'ReferenceWritableKeyPath<A, A>}}
138138

139139
let _: PartialKeyPath<A> = \.optProperty?
140140
let _: KeyPath<A, Prop?> = \.optProperty?
@@ -158,8 +158,8 @@ func testKeyPath(sub: Sub, optSub: OptSub,
158158
let _: PartialKeyPath<C<A>> = \.value
159159
let _: KeyPath<C<A>, A> = \.value
160160
let _: WritableKeyPath<C<A>, A> = \.value
161-
// expected-error@+1{{ambiguous}} (need to improve diagnostic)
162161
let _: ReferenceWritableKeyPath<C<A>, A> = \.value
162+
// expected-error@-1 {{cannot convert value of type 'WritableKeyPath<C<A>, A>' to specified type 'ReferenceWritableKeyPath<C<A>, A>'}}
163163

164164
let _: PartialKeyPath<C<A>> = \C.value
165165
let _: KeyPath<C<A>, A> = \C.value
@@ -684,7 +684,8 @@ func testSubtypeKeypathClass(_ keyPath: ReferenceWritableKeyPath<Base, Int>) {
684684
}
685685

686686
func testSubtypeKeypathProtocol(_ keyPath: ReferenceWritableKeyPath<PP, Int>) {
687-
testSubtypeKeypathProtocol(\Base.i) // expected-error {{type 'PP' has no member 'i'}}
687+
testSubtypeKeypathProtocol(\Base.i)
688+
// expected-error@-1 {{cannot convert value of type 'ReferenceWritableKeyPath<Base, Int>' to specified type 'ReferenceWritableKeyPath<PP, Int>'}}
688689
}
689690

690691
// rdar://problem/32057712

0 commit comments

Comments
 (0)