Skip to content

Commit df64e8a

Browse files
committed
Allow #keyPath() to resolve type refs
1 parent 3be98d7 commit df64e8a

File tree

15 files changed

+89
-25
lines changed

15 files changed

+89
-25
lines changed

include/swift/AST/Expr.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4775,6 +4775,7 @@ class KeyPathExpr : public Expr {
47754775
OptionalChain,
47764776
OptionalWrap,
47774777
Identity,
4778+
Type,
47784779
};
47794780

47804781
private:
@@ -4871,7 +4872,17 @@ class KeyPathExpr : public Expr {
48714872
propertyType,
48724873
loc);
48734874
}
4874-
4875+
4876+
/// Create a component for a type.
4877+
static Component forType(ConcreteDeclRef typeRef,
4878+
Type type,
4879+
SourceLoc loc) {
4880+
return Component(nullptr, typeRef, nullptr, {}, {},
4881+
Kind::Type,
4882+
type,
4883+
loc);
4884+
}
4885+
48754886
/// Create a component for a subscript.
48764887
static Component forSubscript(ASTContext &ctx,
48774888
ConcreteDeclRef subscript,
@@ -4946,6 +4957,7 @@ class KeyPathExpr : public Expr {
49464957
case Kind::OptionalForce:
49474958
case Kind::Property:
49484959
case Kind::Identity:
4960+
case Kind::Type:
49494961
return true;
49504962

49514963
case Kind::UnresolvedSubscript:
@@ -4969,6 +4981,7 @@ class KeyPathExpr : public Expr {
49694981
case Kind::UnresolvedProperty:
49704982
case Kind::Property:
49714983
case Kind::Identity:
4984+
case Kind::Type:
49724985
return nullptr;
49734986
}
49744987
llvm_unreachable("unhandled kind");
@@ -4987,6 +5000,7 @@ class KeyPathExpr : public Expr {
49875000
case Kind::UnresolvedProperty:
49885001
case Kind::Property:
49895002
case Kind::Identity:
5003+
case Kind::Type:
49905004
llvm_unreachable("no subscript labels for this kind");
49915005
}
49925006
llvm_unreachable("unhandled kind");
@@ -5008,6 +5022,7 @@ class KeyPathExpr : public Expr {
50085022
case Kind::UnresolvedProperty:
50095023
case Kind::Property:
50105024
case Kind::Identity:
5025+
case Kind::Type:
50115026
return {};
50125027
}
50135028
llvm_unreachable("unhandled kind");
@@ -5029,6 +5044,7 @@ class KeyPathExpr : public Expr {
50295044
case Kind::OptionalForce:
50305045
case Kind::Property:
50315046
case Kind::Identity:
5047+
case Kind::Type:
50325048
llvm_unreachable("no unresolved name for this kind");
50335049
}
50345050
llvm_unreachable("unhandled kind");
@@ -5038,6 +5054,7 @@ class KeyPathExpr : public Expr {
50385054
switch (getKind()) {
50395055
case Kind::Property:
50405056
case Kind::Subscript:
5057+
case Kind::Type:
50415058
return Decl.ResolvedDecl;
50425059

50435060
case Kind::Invalid:

lib/AST/ASTDumper.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,10 +2610,15 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
26102610
component.getIndexExpr()->print(OS, Indent + 4);
26112611
OS.indent(Indent + 4);
26122612
break;
2613+
26132614
case KeyPathExpr::Component::Kind::Identity:
26142615
OS << "identity";
26152616
OS << '\n';
26162617
break;
2618+
2619+
case KeyPathExpr::Component::Kind::Type:
2620+
OS << "type_ref ";
2621+
break;
26172622
}
26182623
OS << "type=";
26192624
component.getComponentType().print(OS);

lib/AST/ASTWalker.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,7 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
10341034
case KeyPathExpr::Component::Kind::UnresolvedProperty:
10351035
case KeyPathExpr::Component::Kind::Invalid:
10361036
case KeyPathExpr::Component::Kind::Identity:
1037+
case KeyPathExpr::Component::Kind::Type:
10371038
// No subexpr to visit.
10381039
break;
10391040
}

lib/AST/Expr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2179,6 +2179,7 @@ void KeyPathExpr::Component::setSubscriptIndexHashableConformances(
21792179
case Kind::UnresolvedProperty:
21802180
case Kind::Property:
21812181
case Kind::Identity:
2182+
case Kind::Type:
21822183
llvm_unreachable("no hashable conformances for this kind");
21832184
}
21842185
}

lib/IDE/SourceEntityWalker.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,8 @@ std::pair<bool, Expr *> SemaAnnotator::walkToExprPre(Expr *E) {
348348
for (auto &component : KPE->getComponents()) {
349349
switch (component.getKind()) {
350350
case KeyPathExpr::Component::Kind::Property:
351-
case KeyPathExpr::Component::Kind::Subscript: {
351+
case KeyPathExpr::Component::Kind::Subscript:
352+
case KeyPathExpr::Component::Kind::Type: {
352353
auto *decl = component.getDeclRef().getDecl();
353354
auto loc = component.getLoc();
354355
SourceRange range(loc, loc);

lib/SILGen/SILGenExpr.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3816,7 +3816,8 @@ RValue RValueEmitter::visitKeyPathExpr(KeyPathExpr *E, SGFContext C) {
38163816
for (auto &component : E->getComponents()) {
38173817
switch (auto kind = component.getKind()) {
38183818
case KeyPathExpr::Component::Kind::Property:
3819-
case KeyPathExpr::Component::Kind::Subscript: {
3819+
case KeyPathExpr::Component::Kind::Subscript:
3820+
case KeyPathExpr::Component::Kind::Type: {
38203821
auto decl = cast<AbstractStorageDecl>(component.getDeclRef().getDecl());
38213822

38223823
unsigned numOperands = operands.size();

lib/Sema/CSApply.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,9 @@ static bool buildObjCKeyPathString(KeyPathExpr *E,
295295
buf.append(objcName.begin(), objcName.end());
296296
continue;
297297
}
298-
case KeyPathExpr::Component::Kind::Subscript: {
299-
// Subscripts aren't generally represented in KVC.
298+
case KeyPathExpr::Component::Kind::Subscript:
299+
case KeyPathExpr::Component::Kind::Type: {
300+
// Subscripts and types aren't generally represented in KVC.
300301
// TODO: There are some subscript forms we could map to KVC, such as
301302
// when indexing a Dictionary or NSDictionary by string, or when applying
302303
// a mapping subscript operation to Array/Set or NSArray/NSSet.
@@ -4524,6 +4525,7 @@ namespace {
45244525
case KeyPathExpr::Component::Kind::Property:
45254526
case KeyPathExpr::Component::Kind::Subscript:
45264527
case KeyPathExpr::Component::Kind::OptionalWrap:
4528+
case KeyPathExpr::Component::Kind::Type:
45274529
llvm_unreachable("already resolved");
45284530
}
45294531
}

lib/Sema/CSDiag.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6676,6 +6676,7 @@ static bool diagnoseKeyPathComponents(ConstraintSystem &CS, KeyPathExpr *KPE,
66766676
case KeyPathExpr::Component::Kind::OptionalWrap:
66776677
case KeyPathExpr::Component::Kind::Property:
66786678
case KeyPathExpr::Component::Kind::Subscript:
6679+
case KeyPathExpr::Component::Kind::Type:
66796680
llvm_unreachable("already resolved!");
66806681
}
66816682

lib/Sema/CSGen.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3005,7 +3005,8 @@ namespace {
30053005
case KeyPathExpr::Component::Kind::UnresolvedProperty:
30063006
// This should only appear in resolved ASTs, but we may need to
30073007
// re-type-check the constraints during failure diagnosis.
3008-
case KeyPathExpr::Component::Kind::Property: {
3008+
case KeyPathExpr::Component::Kind::Property:
3009+
case KeyPathExpr::Component::Kind::Type: {
30093010
auto memberTy = CS.createTypeVariable(locator, TVO_CanBindToLValue);
30103011
auto lookupName = kind == KeyPathExpr::Component::Kind::UnresolvedProperty
30113012
? component.getUnresolvedDeclName()

lib/Sema/CSSimplify.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ getCalleeDeclAndArgs(ConstraintSystem &cs,
643643
case KeyPathExpr::Component::Kind::OptionalChain:
644644
case KeyPathExpr::Component::Kind::OptionalWrap:
645645
case KeyPathExpr::Component::Kind::Identity:
646+
case KeyPathExpr::Component::Kind::Type:
646647
return std::make_tuple(nullptr, /*hasCurriedSelf=*/false, argLabels,
647648
hasTrailingClosure);
648649
}
@@ -4152,6 +4153,7 @@ ConstraintSystem::simplifyKeyPathConstraint(Type keyPathTy,
41524153

41534154
case KeyPathExpr::Component::Kind::Property:
41544155
case KeyPathExpr::Component::Kind::Subscript:
4156+
case KeyPathExpr::Component::Kind::Type:
41554157
case KeyPathExpr::Component::Kind::UnresolvedProperty:
41564158
case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
41574159
// If no choice was made, leave the constraint unsolved.

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2433,7 +2433,8 @@ class AvailabilityWalker : public ASTWalker {
24332433
for (auto &component : KP->getComponents()) {
24342434
switch (component.getKind()) {
24352435
case KeyPathExpr::Component::Kind::Property:
2436-
case KeyPathExpr::Component::Kind::Subscript: {
2436+
case KeyPathExpr::Component::Kind::Subscript:
2437+
case KeyPathExpr::Component::Kind::Type: {
24372438
auto *decl = component.getDeclRef().getDecl();
24382439
auto loc = component.getLoc();
24392440
SourceRange range(loc, loc);

lib/Sema/TypeCheckExprObjC.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,16 @@ Optional<Type> TypeChecker::checkObjCKeyPathExpr(DeclContext *dc,
215215
diag::expr_unsupported_objc_key_path_component,
216216
(unsigned)kind);
217217
continue;
218-
case KeyPathExpr::Component::Kind::OptionalWrap:
219218
case KeyPathExpr::Component::Kind::Property:
219+
case KeyPathExpr::Component::Kind::Type: {
220+
//For code completion purposes, we only care about the last expression.
221+
if (&component == &expr->getComponents().back()) {
222+
return component.getComponentType();
223+
} else {
224+
continue;
225+
}
226+
}
227+
case KeyPathExpr::Component::Kind::OptionalWrap:
220228
case KeyPathExpr::Component::Kind::Subscript:
221229
llvm_unreachable("already resolved!");
222230
}
@@ -321,10 +329,11 @@ Optional<Type> TypeChecker::checkObjCKeyPathExpr(DeclContext *dc,
321329

322330
// Resolve this component to the variable we found.
323331
auto varRef = ConcreteDeclRef(var);
332+
Type type = var->getInterfaceType();
324333
auto resolved =
325-
KeyPathExpr::Component::forProperty(varRef, Type(), componentNameLoc);
334+
KeyPathExpr::Component::forProperty(varRef, type, componentNameLoc);
326335
resolvedComponents.push_back(resolved);
327-
updateState(/*isProperty=*/true, var->getInterfaceType());
336+
updateState(/*isProperty=*/true, type);
328337

329338
// Check that the property is @objc.
330339
if (!var->isObjC()) {
@@ -388,6 +397,11 @@ Optional<Type> TypeChecker::checkObjCKeyPathExpr(DeclContext *dc,
388397
break;
389398
}
390399

400+
// Resolve this component to the type we found.
401+
auto typeRef = ConcreteDeclRef(type);
402+
auto resolved =
403+
KeyPathExpr::Component::forType(typeRef, newType, componentNameLoc);
404+
resolvedComponents.push_back(resolved);
391405
updateState(/*isProperty=*/false, newType);
392406
continue;
393407
}

test/IDE/complete_pound_keypath.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=IN_KEYPATH_2 | %FileCheck -check-prefix=CHECK-IN_KEYPATH %s
88

9+
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=IN_KEYPATH_3 | %FileCheck -check-prefix=CHECK-IN_KEYPATH_3 %s
10+
911

1012
// REQUIRES: objc_interop
1113

@@ -34,6 +36,10 @@ func completeInKeyPath2() {
3436
_ = #keyPath(ObjCClass.#^IN_KEYPATH_2^#
3537
}
3638

39+
func completeInKeyPath3() {
40+
_ = #keyPath(ObjCClass.prop1.#^IN_KEYPATH_3^#
41+
}
42+
3743
// CHECK-AFTER_POUND: Keyword/ExprSpecific: keyPath({#@objc property sequence#}); name=keyPath(@objc property sequence)
3844

3945
// CHECK-KEYPATH_ARG: Keyword/None: #keyPath({#@objc property sequence#}); name=#keyPath(@objc property sequence)
@@ -42,4 +48,5 @@ func completeInKeyPath2() {
4248
// CHECK-IN_KEYPATH: Decl[InstanceVar]/CurrNominal: prop2[#ObjCClass?#]; name=prop2
4349
// CHECK-IN_KEYPATH: Decl[InstanceVar]/Super: hashValue[#Int#]; name=hashValue
4450

51+
// CHECK-IN_KEYPATH_3: Decl[InstanceVar]/CurrNominal: hashValue[#Int#]; name=hashValue
4552

test/Index/index_keypaths.swift

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,31 @@ struct MyStruct {
77
}
88
}
99

10-
class MyClass {
11-
class Inner {
12-
@objc var myProp = 1
13-
}
14-
}
15-
1610
let a = \MyStruct.Inner.myProp
1711
// CHECK: [[@LINE-1]]:25 | {{.*}} | myProp
1812
// CHECK: [[@LINE-2]]:10 | {{.*}} | MyStruct
1913
// CHECK: [[@LINE-3]]:19 | {{.*}} | Inner
2014
let b: KeyPath<MyStruct.Inner, Int> = \.myProp
2115
// CHECK: [[@LINE-1]]:41 | {{.*}} | myProp
22-
let c = \MyClass.Inner.myProp
16+
17+
class MyClass {
18+
class Inner {
19+
@objc var myProp = 1
20+
func method() {
21+
let c: String = #keyPath(myProp)
22+
// CHECK: [[@LINE-1]]:32 | {{.*}} | myProp
23+
}
24+
}
25+
}
26+
27+
let d: String = #keyPath(MyClass.Inner.myProp)
28+
// CHECK: [[@LINE-1]]:26 | {{.*}} | MyClass
29+
// CHECK: [[@LINE-2]]:34 | {{.*}} | Inner
30+
// CHECK: [[@LINE-3]]:40 | {{.*}} | myProp
31+
32+
let e = \MyClass.Inner.myProp
2333
// CHECK: [[@LINE-1]]:24 | {{.*}} | myProp
2434
// CHECK: [[@LINE-2]]:10 | {{.*}} | MyClass
2535
// CHECK: [[@LINE-3]]:18 | {{.*}} | Inner
26-
let d: KeyPath<MyClass.Inner, Int> = \.myProp
36+
let f: KeyPath<MyClass.Inner, Int> = \.myProp
2737
// CHECK: [[@LINE-1]]:40 | {{.*}} | myProp

test/expr/primary/keypath/keypath-objc.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func testKeyPath(a: A, b: B) {
5757
let _: String = #keyPath(A.propString)
5858

5959
// Property of String property (which looks on NSString)
60-
let _: String = #keyPath(A.propString.URLsInText)
60+
let _: String = #keyPath(A.propString.urlsInText)
6161

6262
// String property with a suffix
6363
let _: String = #keyPath(A.propString).description
@@ -72,36 +72,36 @@ func testKeyPath(a: A, b: B) {
7272

7373
// Array property (make sure we look at the array element).
7474
let _: String = #keyPath(A.propArray)
75-
let _: String = #keyPath(A.propArray.URLsInText)
75+
let _: String = #keyPath(A.propArray.urlsInText)
7676

7777
// Dictionary property (make sure we look at the value type).
7878
let _: String = #keyPath(A.propDict.anyKeyName)
7979
let _: String = #keyPath(A.propDict.anyKeyName.propA)
8080

8181
// Set property (make sure we look at the set element).
8282
let _: String = #keyPath(A.propSet)
83-
let _: String = #keyPath(A.propSet.URLsInText)
83+
let _: String = #keyPath(A.propSet.urlsInText)
8484

8585
// AnyObject property
86-
let _: String = #keyPath(A.propAnyObject.URLsInText)
86+
let _: String = #keyPath(A.propAnyObject.urlsInText)
8787
let _: String = #keyPath(A.propAnyObject.propA)
8888
let _: String = #keyPath(A.propAnyObject.propB)
8989
let _: String = #keyPath(A.propAnyObject.description)
9090

9191
// NSString property
92-
let _: String = #keyPath(A.propNSString.URLsInText)
92+
let _: String = #keyPath(A.propNSString.urlsInText)
9393

9494
// NSArray property (AnyObject array element).
9595
let _: String = #keyPath(A.propNSArray)
96-
let _: String = #keyPath(A.propNSArray.URLsInText)
96+
let _: String = #keyPath(A.propNSArray.urlsInText)
9797

9898
// NSDictionary property (AnyObject value type).
9999
let _: String = #keyPath(A.propNSDict.anyKeyName)
100100
let _: String = #keyPath(A.propNSDict.anyKeyName.propA)
101101

102102
// NSSet property (AnyObject set element).
103103
let _: String = #keyPath(A.propNSSet)
104-
let _: String = #keyPath(A.propNSSet.URLsInText)
104+
let _: String = #keyPath(A.propNSSet.urlsInText)
105105

106106
// Property with keyword name.
107107
let _: String = #keyPath(A.repeat)

0 commit comments

Comments
 (0)