Skip to content

Commit 43aa9af

Browse files
authored
Merge pull request #9908 from jckarter/partial-key-path
Sema: Don't crash when key path literals appear in PartialKeyPath context.
2 parents 52cf9e5 + a6fb6d6 commit 43aa9af

File tree

6 files changed

+117
-41
lines changed

6 files changed

+117
-41
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ ERROR(expr_swift_keypath_unimplemented_component,none,
450450
ERROR(expr_smart_keypath_value_covert_to_contextual_type,none,
451451
"KeyPath value type %0 cannot be converted to contextual type %1",
452452
(Type, Type))
453+
ERROR(expr_swift_keypath_empty, none,
454+
"key path must have at least one component", ())
453455
ERROR(expr_string_interpolation_outside_string,none,
454456
"string interpolation can only appear inside a string literal", ())
455457
ERROR(unsupported_keypath_tuple_element_reference,none,

lib/Sema/CSApply.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4133,10 +4133,14 @@ namespace {
41334133
origComponent.getLoc());
41344134
break;
41354135
}
4136+
case KeyPathExpr::Component::Kind::Invalid:
4137+
component = origComponent;
4138+
component.setComponentType(leafTy);
4139+
break;
4140+
41364141
case KeyPathExpr::Component::Kind::Property:
41374142
case KeyPathExpr::Component::Kind::Subscript:
41384143
case KeyPathExpr::Component::Kind::OptionalWrap:
4139-
case KeyPathExpr::Component::Kind::Invalid:
41404144
llvm_unreachable("already resolved");
41414145
}
41424146

lib/Sema/CSGen.cpp

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2757,27 +2757,6 @@ namespace {
27572757
return ErrorType::get(CS.getASTContext());
27582758
}
27592759

2760-
for (auto &c : E->getComponents()) {
2761-
// If the key path contained any syntactically invalid components, bail
2762-
// out.
2763-
if (!c.isValid()) {
2764-
return ErrorType::get(CS.getASTContext());
2765-
}
2766-
2767-
// If a component is already resolved, then all of them should be
2768-
// resolved, and we can let the expression be. This might happen when
2769-
// re-checking a failed system for diagnostics.
2770-
if (c.isResolved()) {
2771-
assert([&]{
2772-
for (auto &c : E->getComponents())
2773-
if (!c.isResolved())
2774-
return false;
2775-
return true;
2776-
}());
2777-
return E->getType();
2778-
}
2779-
}
2780-
27812760
// For native key paths, traverse the key path components to set up
27822761
// appropriate type relationships at each level.
27832762
auto locator = CS.getConstraintLocator(E);
@@ -2793,6 +2772,19 @@ namespace {
27932772
locator);
27942773
}
27952774

2775+
// If a component is already resolved, then all of them should be
2776+
// resolved, and we can let the expression be. This might happen when
2777+
// re-checking a failed system for diagnostics.
2778+
if (E->getComponents().front().isResolved()) {
2779+
assert([&]{
2780+
for (auto &c : E->getComponents())
2781+
if (!c.isResolved())
2782+
return false;
2783+
return true;
2784+
}());
2785+
return E->getType();
2786+
}
2787+
27962788
bool didOptionalChain = false;
27972789
// We start optimistically from an lvalue base.
27982790
Type base = LValueType::get(root);
@@ -2801,7 +2793,7 @@ namespace {
28012793
auto &component = E->getComponents()[i];
28022794
switch (auto kind = component.getKind()) {
28032795
case KeyPathExpr::Component::Kind::Invalid:
2804-
llvm_unreachable("should have bailed out");
2796+
break;
28052797

28062798
case KeyPathExpr::Component::Kind::UnresolvedProperty: {
28072799
auto memberTy = CS.createTypeVariable(locator, TVO_CanBindToLValue);

lib/Sema/CSSimplify.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3776,7 +3776,7 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
37763776

37773777
keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true);
37783778
auto tryMatchRootAndValueFromKeyPathType =
3779-
[&](BoundGenericType *bgt) -> SolutionKind {
3779+
[&](BoundGenericType *bgt, bool allowPartial) -> SolutionKind {
37803780
Type boundRoot, boundValue;
37813781

37823782
// We can get root and value from a concrete key path type.
@@ -3786,9 +3786,13 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
37863786
boundRoot = bgt->getGenericArgs()[0];
37873787
boundValue = bgt->getGenericArgs()[1];
37883788
} else if (bgt->getDecl() == getASTContext().getPartialKeyPathDecl()) {
3789-
// We can still get the root from a PartialKeyPath.
3790-
boundRoot = bgt->getGenericArgs()[0];
3791-
boundValue = Type();
3789+
if (allowPartial) {
3790+
// We can still get the root from a PartialKeyPath.
3791+
boundRoot = bgt->getGenericArgs()[0];
3792+
boundValue = Type();
3793+
} else {
3794+
return SolutionKind::Error;
3795+
}
37923796
} else {
37933797
// We can't bind anything from this type.
37943798
return SolutionKind::Solved;
@@ -3804,14 +3808,26 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
38043808

38053809
return SolutionKind::Solved;
38063810
};
3807-
3808-
// If the key path type is bound from context, feed that into the root and
3809-
// value types.
3811+
3812+
// If we're fixed to a bound generic type, trying harvesting context from it.
3813+
// However, we don't want a solution that fixes the expression type to
3814+
// PartialKeyPath; we'd rather that be represented using an upcast conversion.
38103815
if (auto keyPathBGT = keyPathTy->getAs<BoundGenericType>()) {
3811-
if (tryMatchRootAndValueFromKeyPathType(keyPathBGT) == SolutionKind::Error)
3816+
if (tryMatchRootAndValueFromKeyPathType(keyPathBGT, /*allowPartial*/false)
3817+
== SolutionKind::Error)
38123818
return SolutionKind::Error;
38133819
}
38143820

3821+
// If the expression has contextual type information, try using that too.
3822+
if (auto contextualTy = getContextualType(keyPath)) {
3823+
if (auto contextualBGT = contextualTy->getAs<BoundGenericType>()) {
3824+
if (tryMatchRootAndValueFromKeyPathType(contextualBGT,
3825+
/*allowPartial*/true)
3826+
== SolutionKind::Error)
3827+
return SolutionKind::Error;
3828+
}
3829+
}
3830+
38153831
// See if we resolved overloads for all the components involved.
38163832
enum {
38173833
ReadOnly,
@@ -3824,7 +3840,7 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
38243840

38253841
switch (component.getKind()) {
38263842
case KeyPathExpr::Component::Kind::Invalid:
3827-
return SolutionKind::Error;
3843+
break;
38283844

38293845
case KeyPathExpr::Component::Kind::UnresolvedProperty:
38303846
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,14 @@ void PreCheckExpression::resolveKeyPathExpr(KeyPathExpr *KPE) {
15421542
traversePath(root, /*isInParsedPath=*/false);
15431543
}
15441544

1545+
// Key paths must have at least one component.
1546+
if (components.empty()) {
1547+
TC.diagnose(KPE->getLoc(), diag::expr_swift_keypath_empty);
1548+
// Passes further down the pipeline expect keypaths to always have at least
1549+
// one component, so stuff an invalid component in the AST for recovery.
1550+
components.push_back(KeyPathExpr::Component());
1551+
}
1552+
15451553
std::reverse(components.begin(), components.end());
15461554

15471555
KPE->setRootType(rootType);

test/expr/unary/keypath/keypath.swift

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,26 +82,28 @@ func testKeyPath(sub: Sub, optSub: OptSub, x: Int) {
8282
// expected-error@+1{{}}
8383
_ = \(() -> ()).noMember
8484

85-
// FIXME crash let _: PartialKeyPath<A> = \.property
85+
let _: PartialKeyPath<A> = \.property
8686
let _: KeyPath<A, Prop> = \.property
8787
let _: WritableKeyPath<A, Prop> = \.property
8888
// expected-error@+1{{ambiguous}} (need to improve diagnostic)
8989
let _: ReferenceWritableKeyPath<A, Prop> = \.property
9090

91-
// FIXME crash let _: PartialKeyPath<A> = \[sub]
91+
// FIXME: shouldn't be ambiguous
92+
// expected-error@+1{{ambiguous}}
93+
let _: PartialKeyPath<A> = \.[sub]
9294
let _: KeyPath<A, A> = \.[sub]
9395
let _: WritableKeyPath<A, A> = \.[sub]
9496
// expected-error@+1{{ambiguous}} (need to improve diagnostic)
9597
let _: ReferenceWritableKeyPath<A, A> = \.[sub]
9698

97-
// FIXME crash let _: PartialKeyPath<A> = \.optProperty?
99+
let _: PartialKeyPath<A> = \.optProperty?
98100
let _: KeyPath<A, Prop?> = \.optProperty?
99101
// expected-error@+1{{cannot convert}}
100102
let _: WritableKeyPath<A, Prop?> = \.optProperty?
101103
// expected-error@+1{{cannot convert}}
102104
let _: ReferenceWritableKeyPath<A, Prop?> = \.optProperty?
103105

104-
// FIXME crash let _: PartialKeyPath<A> = \.optProperty?[sub]
106+
let _: PartialKeyPath<A> = \.optProperty?[sub]
105107
let _: KeyPath<A, A?> = \.optProperty?[sub]
106108
// expected-error@+1{{cannot convert}}
107109
let _: WritableKeyPath<A, A?> = \.optProperty?[sub]
@@ -113,27 +115,55 @@ func testKeyPath(sub: Sub, optSub: OptSub, x: Int) {
113115
let _: KeyPath<A, Prop?> = \.property[optSub]?.optProperty!
114116
let _: KeyPath<A, A?> = \.property[optSub]?.optProperty![sub]
115117

116-
// FIXME crash let _: PartialKeyPath<C<A>> = \.value
118+
let _: PartialKeyPath<C<A>> = \.value
117119
let _: KeyPath<C<A>, A> = \.value
118120
let _: WritableKeyPath<C<A>, A> = \.value
119121
// expected-error@+1{{ambiguous}} (need to improve diagnostic)
120122
let _: ReferenceWritableKeyPath<C<A>, A> = \.value
121123

122-
// FIXME crash let _: PartialKeyPath<C<A>> = \C, .value
124+
let _: PartialKeyPath<C<A>> = \C.value
123125
let _: KeyPath<C<A>, A> = \C.value
124126
let _: WritableKeyPath<C<A>, A> = \C.value
125127
// expected-error@+1{{cannot convert}}
126128
let _: ReferenceWritableKeyPath<C<A>, A> = \C.value
127129

128-
// FIXME crash let _: PartialKeyPath<Prop> = \.nonMutatingProperty
130+
let _: PartialKeyPath<Prop> = \.nonMutatingProperty
129131
let _: KeyPath<Prop, B> = \.nonMutatingProperty
130132
let _: WritableKeyPath<Prop, B> = \.nonMutatingProperty
131133
let _: ReferenceWritableKeyPath<Prop, B> = \.nonMutatingProperty
134+
135+
var m = [\A.property, \A.[sub], \A.optProperty!]
136+
expect(&m, toHaveType: Exactly<[PartialKeyPath<A>]>.self)
137+
138+
// FIXME: shouldn't be ambiguous
139+
// expected-error@+1{{ambiguous}}
140+
var n = [\A.property, \.optProperty, \.[sub], \.optProperty!]
141+
expect(&n, toHaveType: Exactly<[PartialKeyPath<A>]>.self)
142+
143+
// FIXME: shouldn't be ambiguous
144+
// expected-error@+1{{ambiguous}}
145+
let _: [PartialKeyPath<A>] = [\.property, \.optProperty, \.[sub], \.optProperty!]
146+
147+
var o = [\A.property, \C<A>.value]
148+
expect(&o, toHaveType: Exactly<[AnyKeyPath]>.self)
149+
150+
let _: AnyKeyPath = \A.property
151+
let _: AnyKeyPath = \C<A>.value
152+
let _: AnyKeyPath = \.property // expected-error{{ambiguous}}
153+
let _: AnyKeyPath = \C.value // expected-error{{cannot convert}} (need to improve diagnostic)
154+
let _: AnyKeyPath = \.value // expected-error{{ambiguous}}
132155
}
133156

134157
func testDisembodiedStringInterpolation(x: Int) {
135-
\(x) // expected-error{{string interpolation}}
136-
\(x, radix: 16) // expected-error{{string interpolation}}
158+
\(x) // expected-error{{string interpolation}} expected-error{{}}
159+
\(x, radix: 16) // expected-error{{string interpolation}} expected-error{{}}
160+
161+
_ = \(Int, Int).0 // expected-error{{cannot reference tuple elements}}
162+
}
163+
164+
func testNoComponents() {
165+
let _: KeyPath<A, A> = \A // expected-error{{must have at least one component}}
166+
let _: KeyPath<C, A> = \C // expected-error{{must have at least one component}} expected-error{{}}
137167
}
138168

139169
struct TupleStruct {
@@ -178,6 +208,30 @@ func testKeyPathSubscript(readonly: Z, writable: inout Z,
178208
writable[keyPath: wkp] = sink
179209
readonly[keyPath: rkp] = sink
180210
writable[keyPath: rkp] = sink
211+
212+
// TODO: PartialKeyPath and AnyKeyPath application
213+
214+
/*
215+
let pkp: PartialKeyPath = rkp
216+
217+
var anySink1 = readonly[keyPath: pkp]
218+
expect(&anySink1, toHaveType: Exactly<Any>.self)
219+
var anySink2 = writable[keyPath: pkp]
220+
expect(&anySink2, toHaveType: Exactly<Any>.self)
221+
222+
readonly[keyPath: pkp] = anySink1 // e/xpected-error{{cannot assign to immutable}}
223+
writable[keyPath: pkp] = anySink2 // e/xpected-error{{cannot assign to immutable}}
224+
225+
let akp: AnyKeyPath = pkp
226+
227+
var anyqSink1 = readonly[keyPath: akp]
228+
expect(&anyqSink1, toHaveType: Exactly<Any?>.self)
229+
var anyqSink2 = writable[keyPath: akp]
230+
expect(&anyqSink2, toHaveType: Exactly<Any?>.self)
231+
232+
readonly[keyPath: akp] = anyqSink1 // e/xpected-error{{cannot assign to immutable}}
233+
writable[keyPath: akp] = anyqSink2 // e/xpected-error{{cannot assign to immutable}}
234+
*/
181235
}
182236

183237
func testKeyPathSubscriptMetatype(readonly: Z.Type, writable: inout Z.Type,

0 commit comments

Comments
 (0)