Skip to content

Commit bde30ef

Browse files
author
Venkatesh Sriram
committed
[Compile Time Constant Extraction] Extract KeyPath Expressions
1 parent 0bbe26d commit bde30ef

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

include/swift/AST/ConstTypeInfo.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class CompileTimeValue {
3737
Tuple,
3838
Enum,
3939
Type,
40+
KeyPath,
4041
Runtime
4142
};
4243

@@ -199,6 +200,34 @@ class TypeValue : public CompileTimeValue {
199200
swift::Type Type;
200201
};
201202

203+
/// A representation of a Keypath
204+
class KeyPathValue : public CompileTimeValue {
205+
public:
206+
struct Component {
207+
std::string Label;
208+
swift::Type Type;
209+
};
210+
KeyPathValue(std::string Path,
211+
swift::Type RootType,
212+
std::vector<Component> Components)
213+
: CompileTimeValue(ValueKind::KeyPath), Path(Path), RootType(RootType), Components(Components) {}
214+
215+
std::string getPath() const { return Path; }
216+
swift::Type getRootType() const { return RootType; }
217+
std::vector<Component> getComponents() const {
218+
return Components;
219+
}
220+
221+
static bool classof(const CompileTimeValue *T) {
222+
return T->getKind() == ValueKind::KeyPath;
223+
}
224+
225+
private:
226+
std::string Path;
227+
swift::Type RootType;
228+
std::vector<Component> Components;
229+
};
230+
202231
/// A representation of an arbitrary value that does not fall under
203232
/// any of the above categories.
204233
class RuntimeValue : public CompileTimeValue {

lib/ConstExtract/ConstExtract.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,33 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {
376376
}
377377
} break;
378378

379+
case ExprKind::KeyPath: {
380+
auto keyPathExpr = cast<KeyPathExpr>(expr);
381+
382+
auto rootType = keyPathExpr->getRootType();
383+
std::vector<KeyPathValue::Component> components;
384+
385+
for (auto component: keyPathExpr->getComponents()) {
386+
if (component.isResolved()) {
387+
auto declRef = component.getDeclRef();
388+
auto identifier = declRef.getDecl()->getBaseIdentifier().str();
389+
auto type = component.getComponentType()->getRValueType();
390+
components.push_back({identifier.str(), type});
391+
}
392+
}
393+
394+
std::string path = "";
395+
auto numberOfComponents = static_cast<int>(components.size());
396+
for (int i = 0; i < numberOfComponents; i++) {
397+
if (i != 0) {
398+
path += ".";
399+
}
400+
path += components[i].Label;
401+
}
402+
403+
return std::make_shared<KeyPathValue>(path, rootType, components);
404+
}
405+
379406
case ExprKind::InjectIntoOptional: {
380407
auto injectIntoOptionalExpr = cast<InjectIntoOptionalExpr>(expr);
381408
return extractCompileTimeValue(injectIntoOptionalExpr->getSubExpr());
@@ -703,6 +730,25 @@ void writeValue(llvm::json::OStream &JSON,
703730
break;
704731
}
705732

733+
case CompileTimeValue::KeyPath: {
734+
auto keyPathValue = cast<KeyPathValue>(value);
735+
JSON.attribute("valueKind", "KeyPath");
736+
JSON.attributeObject("value", [&]() {
737+
JSON.attribute("path", keyPathValue->getPath());
738+
JSON.attribute("rootType", toFullyQualifiedTypeNameString(keyPathValue->getRootType()));
739+
JSON.attributeArray("components", [&] {
740+
auto components = keyPathValue->getComponents();
741+
for (auto c : components) {
742+
JSON.object([&] {
743+
JSON.attribute("label", c.Label);
744+
JSON.attribute("type", toFullyQualifiedTypeNameString(c.Type));
745+
});
746+
}
747+
});
748+
});
749+
break;
750+
}
751+
706752
case CompileTimeValue::ValueKind::Runtime: {
707753
JSON.attribute("valueKind", "Runtime");
708754
break;
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: echo "[MyProto]" > %t/protocols.json
3+
4+
// RUN: %target-swift-frontend -typecheck -emit-const-values-path %t/ExtractTypes.swiftconstvalues -const-gather-protocols-file %t/protocols.json -primary-file %s
5+
// RUN: cat %t/ExtractTypes.swiftconstvalues 2>&1 | %FileCheck %s
6+
7+
protocol MyProto {}
8+
9+
public struct MyType {
10+
var nested: NestedOne
11+
12+
struct NestedOne {
13+
var foo: NestedTwo
14+
}
15+
16+
struct NestedTwo {
17+
var bar: NestedThree
18+
}
19+
20+
struct NestedThree {
21+
var baz: String
22+
}
23+
}
24+
25+
public struct KeyPaths: MyProto {
26+
static let nestedVariable = \MyType.nested.foo.bar.baz
27+
}
28+
29+
// CHECK: [
30+
// CHECK-NEXT: {
31+
// CHECK-NEXT: "typeName": "ExtractKeyPaths.KeyPaths",
32+
// CHECK-NEXT: "mangledTypeName": "15ExtractKeyPaths0bC0V",
33+
// CHECK-NEXT: "kind": "struct",
34+
// CHECK-NEXT: "file": "SOURCE_DIR/test/ConstExtraction/ExtractKeyPaths.swift",
35+
// CHECK-NEXT: "line": 25,
36+
// CHECK-NEXT: "conformances": [
37+
// CHECK-NEXT: "ExtractKeyPaths.MyProto"
38+
// CHECK-NEXT: ],
39+
// CHECK-NEXT: "associatedTypeAliases": [],
40+
// CHECK-NEXT: "properties": [
41+
// CHECK-NEXT: {
42+
// CHECK-NEXT: "label": "nestedVariable",
43+
// CHECK-NEXT: "type": "Swift.WritableKeyPath<ExtractKeyPaths.MyType, Swift.String>",
44+
// CHECK-NEXT: "mangledTypeName": "n/a - deprecated",
45+
// CHECK-NEXT: "isStatic": "true",
46+
// CHECK-NEXT: "isComputed": "false",
47+
// CHECK-NEXT: "file": "SOURCE_DIR/test/ConstExtraction/ExtractKeyPaths.swift",
48+
// CHECK-NEXT: "line": 26,
49+
// CHECK-NEXT: "valueKind": "KeyPath",
50+
// CHECK-NEXT: "value": {
51+
// CHECK-NEXT: "path": "nested.foo.bar.baz",
52+
// CHECK-NEXT: "rootType": "ExtractKeyPaths.MyType",
53+
// CHECK-NEXT: "components": [
54+
// CHECK-NEXT: {
55+
// CHECK-NEXT: "label": "nested",
56+
// CHECK-NEXT: "type": "ExtractKeyPaths.MyType.NestedOne"
57+
// CHECK-NEXT: },
58+
// CHECK-NEXT: {
59+
// CHECK-NEXT: "label": "foo",
60+
// CHECK-NEXT: "type": "ExtractKeyPaths.MyType.NestedTwo"
61+
// CHECK-NEXT: },
62+
// CHECK-NEXT: {
63+
// CHECK-NEXT: "label": "bar",
64+
// CHECK-NEXT: "type": "ExtractKeyPaths.MyType.NestedThree"
65+
// CHECK-NEXT: },
66+
// CHECK-NEXT: {
67+
// CHECK-NEXT: "label": "baz",
68+
// CHECK-NEXT: "type": "Swift.String"
69+
// CHECK-NEXT: }
70+
// CHECK-NEXT: ]
71+
// CHECK-NEXT: }
72+
// CHECK-NEXT: }
73+
// CHECK-NEXT: ]
74+
// CHECK-NEXT: }
75+
// CHECK-NEXT: ]

0 commit comments

Comments
 (0)