Skip to content

Commit 7a92acb

Browse files
authored
Merge pull request #10870 from jckarter/key-path-failure-diagnosis-with-resolved-components
Sema: Allow KeyPath and KeyPathApplicationExprs to re-type-check during failure diagnosis.
2 parents 51eac53 + 51672d3 commit 7a92acb

File tree

4 files changed

+69
-28
lines changed

4 files changed

+69
-28
lines changed

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3581,7 +3581,9 @@ namespace {
35813581
}
35823582

35833583
Expr *visitKeyPathApplicationExpr(KeyPathApplicationExpr *expr){
3584-
llvm_unreachable("Already type-checked");
3584+
// This should already be type-checked, but we may have had to re-
3585+
// check it for failure diagnosis.
3586+
return simplifyExprType(expr);
35853587
}
35863588

35873589
Expr *visitEnumIsCaseExpr(EnumIsCaseExpr *expr) {

lib/Sema/CSGen.cpp

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,7 +2713,16 @@ namespace {
27132713
llvm_unreachable("Already type-checked");
27142714
}
27152715
Type visitKeyPathApplicationExpr(KeyPathApplicationExpr *expr) {
2716-
llvm_unreachable("Already type-checked");
2716+
// This should only appear in already-type-checked solutions, but we may
2717+
// need to re-check for failure diagnosis.
2718+
auto locator = CS.getConstraintLocator(expr);
2719+
auto projectedTy = CS.createTypeVariable(locator,
2720+
TVO_CanBindToLValue);
2721+
CS.addKeyPathApplicationConstraint(expr->getKeyPath()->getType(),
2722+
expr->getBase()->getType(),
2723+
projectedTy,
2724+
locator);
2725+
return projectedTy;
27172726
}
27182727

27192728
Type visitEnumIsCaseExpr(EnumIsCaseExpr *expr) {
@@ -2797,19 +2806,6 @@ namespace {
27972806
locator);
27982807
}
27992808

2800-
// If a component is already resolved, then all of them should be
2801-
// resolved, and we can let the expression be. This might happen when
2802-
// re-checking a failed system for diagnostics.
2803-
if (E->getComponents().front().isResolved()) {
2804-
assert([&]{
2805-
for (auto &c : E->getComponents())
2806-
if (!c.isResolved())
2807-
return false;
2808-
return true;
2809-
}());
2810-
return E->getType();
2811-
}
2812-
28132809
bool didOptionalChain = false;
28142810
// We start optimistically from an lvalue base.
28152811
Type base = LValueType::get(root);
@@ -2820,16 +2816,23 @@ namespace {
28202816
case KeyPathExpr::Component::Kind::Invalid:
28212817
break;
28222818

2823-
case KeyPathExpr::Component::Kind::UnresolvedProperty: {
2819+
case KeyPathExpr::Component::Kind::UnresolvedProperty:
2820+
// This should only appear in resolved ASTs, but we may need to
2821+
// re-type-check the constraints during failure diagnosis.
2822+
case KeyPathExpr::Component::Kind::Property: {
28242823
auto memberTy = CS.createTypeVariable(locator,
28252824
TVO_CanBindToLValue |
28262825
TVO_CanBindToInOut);
2827-
auto refKind = component.getUnresolvedDeclName().isSimpleName()
2826+
auto lookupName = kind == KeyPathExpr::Component::Kind::UnresolvedProperty
2827+
? component.getUnresolvedDeclName()
2828+
: component.getDeclRef().getDecl()->getFullName();
2829+
2830+
auto refKind = lookupName.isSimpleName()
28282831
? FunctionRefKind::Unapplied
28292832
: FunctionRefKind::Compound;
28302833
auto memberLocator = CS.getConstraintLocator(E,
28312834
ConstraintLocator::PathElement::getKeyPathComponent(i));
2832-
CS.addValueMemberConstraint(base, component.getUnresolvedDeclName(),
2835+
CS.addValueMemberConstraint(base, lookupName,
28332836
memberTy,
28342837
CurDC,
28352838
refKind,
@@ -2838,7 +2841,11 @@ namespace {
28382841
break;
28392842
}
28402843

2841-
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
2844+
case KeyPathExpr::Component::Kind::UnresolvedSubscript:
2845+
// Subscript should only appear in resolved ASTs, but we may need to
2846+
// re-type-check the constraints during failure diagnosis.
2847+
case KeyPathExpr::Component::Kind::Subscript: {
2848+
28422849
auto memberLocator = CS.getConstraintLocator(E,
28432850
ConstraintLocator::PathElement::getKeyPathComponent(i));
28442851
base = addSubscriptConstraints(E, base, component.getIndexExpr(),
@@ -2869,11 +2876,13 @@ namespace {
28692876
break;
28702877
}
28712878

2872-
case KeyPathExpr::Component::Kind::Property:
2873-
case KeyPathExpr::Component::Kind::Subscript:
2874-
case KeyPathExpr::Component::Kind::OptionalWrap:
2875-
llvm_unreachable("already resolved");
2876-
}
2879+
case KeyPathExpr::Component::Kind::OptionalWrap: {
2880+
// This should only appear in resolved ASTs, but we may need to
2881+
// re-type-check the constraints during failure diagnosis.
2882+
base = OptionalType::get(base);
2883+
break;
2884+
}
2885+
}
28772886
}
28782887

28792888
// If there was an optional chaining component, the end result must be

lib/Sema/CSSimplify.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3905,6 +3905,8 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
39053905
case KeyPathExpr::Component::Kind::Invalid:
39063906
break;
39073907

3908+
case KeyPathExpr::Component::Kind::Property:
3909+
case KeyPathExpr::Component::Kind::Subscript:
39083910
case KeyPathExpr::Component::Kind::UnresolvedProperty:
39093911
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
39103912
// If no choice was made, leave the constraint unsolved.
@@ -3949,10 +3951,11 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
39493951
// Forcing an optional preserves its lvalue-ness.
39503952
break;
39513953

3952-
case KeyPathExpr::Component::Kind::Property:
3953-
case KeyPathExpr::Component::Kind::Subscript:
39543954
case KeyPathExpr::Component::Kind::OptionalWrap:
3955-
llvm_unreachable("already resolved");
3955+
// An optional chain should already have forced the entire key path to
3956+
// be read-only.
3957+
assert(capability == ReadOnly);
3958+
break;
39563959
}
39573960
}
39583961
done:

test/expr/unary/keypath/salvage-with-other-type-errors.swift

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,32 @@ struct A {
2727
}
2828

2929
extension A: K {
30-
static let j = S(\A.id + "id")
30+
static let j = S(\A.id + "id") // expected-error {{'+' cannot be applied}} expected-note {{}}
31+
}
32+
33+
// SR-5034
34+
35+
struct B {
36+
let v: String
37+
func f1<T, E>(block: (T) -> E) -> B {
38+
return self
39+
}
40+
41+
func f2<T, E: Equatable>(keyPath: KeyPath<T, E>) {
42+
}
43+
}
44+
func f3() {
45+
B(v: "").f1(block: { _ in }).f2(keyPath: \B.v) // expected-error{{}}
46+
}
47+
48+
// SR-5375
49+
50+
protocol Bindable: class { }
51+
52+
extension Bindable {
53+
func test<Value>(to targetKeyPath: ReferenceWritableKeyPath<Self, Value>, change: Value?) {
54+
if self[keyPath:targetKeyPath] != change { // expected-error{{}}
55+
self[keyPath: targetKeyPath] = change!
56+
}
57+
}
3158
}

0 commit comments

Comments
 (0)