Skip to content

Commit 3ea2ca2

Browse files
authored
Merge pull request #24308 from xedin/improve-contextual-diags-for-keypath
[TypeChecker] Improve contextual mismatch diagnostics for key path
2 parents f3bda1c + 94977ee commit 3ea2ca2

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
@@ -1371,6 +1371,14 @@ bool ContextualFailure::diagnoseAsError() {
13711371
break;
13721372
}
13731373

1374+
case ConstraintLocator::ContextualType: {
1375+
if (isKnownKeyPathType(FromType) && isKnownKeyPathType(ToType)) {
1376+
diagnostic = diag::cannot_convert_initializer_value;
1377+
break;
1378+
}
1379+
1380+
LLVM_FALLTHROUGH;
1381+
}
13741382
default:
13751383
return false;
13761384
}

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
@@ -2066,6 +2066,14 @@ repairFailures(ConstraintSystem &cs, Type lhs, Type rhs,
20662066
}
20672067

20682068
case ConstraintLocator::ContextualType: {
2069+
// If both types are key path, the only differences
2070+
// between them are mutability and/or root, value type mismatch.
2071+
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
2072+
auto *fix = KeyPathContextualMismatch::create(
2073+
cs, lhs, rhs, cs.getConstraintLocator(locator));
2074+
conversionsOrFixes.push_back(fix);
2075+
}
2076+
20692077
if (lhs->is<FunctionType>() && !rhs->is<AnyFunctionType>() &&
20702078
isa<ClosureExpr>(anchor)) {
20712079
auto *fix = ContextualMismatch::create(cs, lhs, rhs,
@@ -5230,8 +5238,10 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
52305238

52315239
auto resolvedKPTy = BoundGenericType::get(kpDecl, nullptr,
52325240
{rootTy, valueTy});
5233-
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind,
5234-
subflags, locator);
5241+
// Let's check whether deduced key path type would match
5242+
// expected contextual one.
5243+
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind, subflags,
5244+
locator.withPathElement(ConstraintLocator::ContextualType));
52355245
}
52365246

52375247
ConstraintSystem::SolutionKind

lib/Sema/ConstraintSystem.cpp

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

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

lib/Sema/ConstraintSystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4011,6 +4011,10 @@ class DisjunctionChoiceProducer : public BindingProducer<DisjunctionChoice> {
40114011
}
40124012
};
40134013

4014+
/// Determine whether given type is a known one
4015+
/// for a key path `{Writable, ReferenceWritable}KeyPath`.
4016+
bool isKnownKeyPathType(Type type);
4017+
40144018
/// Determine whether given declaration is one for a key path
40154019
/// `{Writable, ReferenceWritable}KeyPath`.
40164020
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)