Skip to content

Commit 7fb3d6c

Browse files
authored
Merge pull request #10880 from jckarter/key-path-failure-diagnosis-with-resolved-components-4.0
[4.0] Allow resolved KeyPath and KeyPathApplication exprs to re-type-check.
2 parents 1def7a9 + 1507e2a commit 7fb3d6c

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
@@ -3712,7 +3712,9 @@ namespace {
37123712
}
37133713

37143714
Expr *visitKeyPathApplicationExpr(KeyPathApplicationExpr *expr){
3715-
llvm_unreachable("Already type-checked");
3715+
// This should already be type-checked, but we may have had to re-
3716+
// check it for failure diagnosis.
3717+
return simplifyExprType(expr);
37163718
}
37173719

37183720
Expr *visitEnumIsCaseExpr(EnumIsCaseExpr *expr) {

lib/Sema/CSGen.cpp

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

27202729
Type visitEnumIsCaseExpr(EnumIsCaseExpr *expr) {
@@ -2798,19 +2807,6 @@ namespace {
27982807
locator);
27992808
}
28002809

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

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

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

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

28802889
// 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
@@ -3894,6 +3894,8 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
38943894
case KeyPathExpr::Component::Kind::Invalid:
38953895
break;
38963896

3897+
case KeyPathExpr::Component::Kind::Property:
3898+
case KeyPathExpr::Component::Kind::Subscript:
38973899
case KeyPathExpr::Component::Kind::UnresolvedProperty:
38983900
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
38993901
// If no choice was made, leave the constraint unsolved.
@@ -3938,10 +3940,11 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
39383940
// Forcing an optional preserves its lvalue-ness.
39393941
break;
39403942

3941-
case KeyPathExpr::Component::Kind::Property:
3942-
case KeyPathExpr::Component::Kind::Subscript:
39433943
case KeyPathExpr::Component::Kind::OptionalWrap:
3944-
llvm_unreachable("already resolved");
3944+
// An optional chain should already have forced the entire key path to
3945+
// be read-only.
3946+
assert(capability == ReadOnly);
3947+
break;
39453948
}
39463949
}
39473950
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)