Skip to content

Commit ade3977

Browse files
author
Venkatesh Sriram
committed
[Compile Time Constant Extraction] Extract Interpolated String Literals
1 parent da65626 commit ade3977

File tree

3 files changed

+299
-13
lines changed

3 files changed

+299
-13
lines changed

include/swift/AST/ConstTypeInfo.h

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ class CompileTimeValue {
3838
Enum,
3939
Type,
4040
KeyPath,
41+
FunctionCall,
42+
MemberReference,
43+
InterpolatedString,
4144
Runtime
4245
};
4346

@@ -185,7 +188,7 @@ class EnumValue : public CompileTimeValue {
185188
std::optional<std::vector<FunctionParameter>> Parameters;
186189
};
187190

188-
/// An type value representation
191+
/// A type value representation
189192
class TypeValue : public CompileTimeValue {
190193
public:
191194
TypeValue(swift::Type Type) : CompileTimeValue(ValueKind::Type), Type(Type) {}
@@ -228,6 +231,68 @@ class KeyPathValue : public CompileTimeValue {
228231
std::vector<Component> Components;
229232
};
230233

234+
/// A function call representation. This is for a function declaration such as
235+
/// let foo = bar(baz: "abc")
236+
class FunctionCallValue : public CompileTimeValue {
237+
public:
238+
FunctionCallValue(std::string Identifier,
239+
std::optional<std::vector<FunctionParameter>> Parameters)
240+
: CompileTimeValue(ValueKind::FunctionCall), Identifier(Identifier),
241+
Parameters(Parameters) {}
242+
243+
std::string getIdentifier() const { return Identifier; }
244+
std::optional<std::vector<FunctionParameter>> getParameters() const {
245+
return Parameters;
246+
}
247+
248+
static bool classof(const CompileTimeValue *T) {
249+
return T->getKind() == ValueKind::FunctionCall;
250+
}
251+
252+
private:
253+
std::string Identifier;
254+
std::optional<std::vector<FunctionParameter>> Parameters;
255+
};
256+
257+
/// A member reference representation such as
258+
/// let foo = MyStruct.bar
259+
class MemberReferenceValue : public CompileTimeValue {
260+
public:
261+
MemberReferenceValue(swift::Type BaseType, std::string MemberLabel)
262+
: CompileTimeValue(ValueKind::MemberReference), BaseType(BaseType),
263+
MemberLabel(MemberLabel) {}
264+
265+
std::string getMemberLabel() const { return MemberLabel; }
266+
swift::Type getBaseType() const { return BaseType; }
267+
268+
static bool classof(const CompileTimeValue *T) {
269+
return T->getKind() == ValueKind::MemberReference;
270+
}
271+
272+
private:
273+
swift::Type BaseType;
274+
std::string MemberLabel;
275+
};
276+
277+
/// A representation of an Interpolated String Literal
278+
class InterpolatedStringLiteralValue : public CompileTimeValue {
279+
public:
280+
InterpolatedStringLiteralValue(
281+
std::vector<std::shared_ptr<CompileTimeValue>> Segments)
282+
: CompileTimeValue(ValueKind::InterpolatedString), Segments(Segments) {}
283+
284+
std::vector<std::shared_ptr<CompileTimeValue>> getSegments() const {
285+
return Segments;
286+
}
287+
288+
static bool classof(const CompileTimeValue *T) {
289+
return T->getKind() == ValueKind::InterpolatedString;
290+
}
291+
292+
private:
293+
std::vector<std::shared_ptr<CompileTimeValue>> Segments;
294+
};
295+
231296
/// A representation of an arbitrary value that does not fall under
232297
/// any of the above categories.
233298
class RuntimeValue : public CompileTimeValue {

lib/ConstExtract/ConstExtract.cpp

Lines changed: 98 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,17 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {
286286

287287
case ExprKind::Call: {
288288
auto callExpr = cast<CallExpr>(expr);
289+
290+
if (callExpr->getFn()->getKind() == ExprKind::DeclRef) {
291+
auto declRefExpr = cast<DeclRefExpr>(callExpr->getFn());
292+
auto caseName =
293+
declRefExpr->getDecl()->getName().getBaseIdentifier().str().str();
294+
295+
std::vector<FunctionParameter> parameters =
296+
extractFunctionArguments(callExpr->getArgs());
297+
return std::make_shared<FunctionCallValue>(caseName, parameters);
298+
}
299+
289300
if (callExpr->getFn()->getKind() == ExprKind::ConstructorRefCall) {
290301
std::vector<FunctionParameter> parameters =
291302
extractFunctionArguments(callExpr->getArgs());
@@ -408,6 +419,35 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {
408419
return extractCompileTimeValue(injectIntoOptionalExpr->getSubExpr());
409420
}
410421

422+
case ExprKind::Load: {
423+
auto loadExpr = cast<LoadExpr>(expr);
424+
return extractCompileTimeValue(loadExpr->getSubExpr());
425+
}
426+
427+
case ExprKind::MemberRef: {
428+
auto memberExpr = cast<MemberRefExpr>(expr);
429+
auto baseTypeExpr = cast<TypeExpr>(memberExpr->getBase());
430+
auto label = memberExpr->getDecl().getDecl()->getBaseIdentifier().str();
431+
return std::make_shared<MemberReferenceValue>(
432+
baseTypeExpr->getInstanceType(), label.str());
433+
}
434+
435+
case ExprKind::InterpolatedStringLiteral: {
436+
auto interpolatedStringExpr = cast<InterpolatedStringLiteralExpr>(expr);
437+
auto tapExpr = interpolatedStringExpr->getAppendingExpr();
438+
auto &Ctx = tapExpr->getVar()->getASTContext();
439+
440+
std::vector<std::shared_ptr<CompileTimeValue>> segments;
441+
interpolatedStringExpr->forEachSegment(
442+
Ctx, [&](bool isInterpolation, CallExpr *segment) -> void {
443+
auto arg = segment->getArgs()->get(0);
444+
auto expr = arg.getExpr();
445+
segments.push_back(extractCompileTimeValue(expr));
446+
});
447+
448+
return std::make_shared<InterpolatedStringLiteralValue>(segments);
449+
}
450+
411451
default: {
412452
break;
413453
}
@@ -725,23 +765,69 @@ void writeValue(llvm::json::OStream &JSON,
725765
break;
726766
}
727767

728-
case CompileTimeValue::KeyPath: {
729-
auto keyPathValue = cast<KeyPathValue>(value);
730-
JSON.attribute("valueKind", "KeyPath");
731-
JSON.attributeObject("value", [&]() {
732-
JSON.attribute("path", keyPathValue->getPath());
733-
JSON.attribute("rootType", toFullyQualifiedTypeNameString(keyPathValue->getRootType()));
734-
JSON.attributeArray("components", [&] {
735-
auto components = keyPathValue->getComponents();
736-
for (auto c : components) {
768+
case CompileTimeValue::ValueKind::KeyPath: {
769+
auto keyPathValue = cast<KeyPathValue>(value);
770+
JSON.attribute("valueKind", "KeyPath");
771+
JSON.attributeObject("value", [&]() {
772+
JSON.attribute("path", keyPathValue->getPath());
773+
JSON.attribute("rootType", toFullyQualifiedTypeNameString(
774+
keyPathValue->getRootType()));
775+
JSON.attributeArray("components", [&] {
776+
auto components = keyPathValue->getComponents();
777+
for (auto c : components) {
778+
JSON.object([&] {
779+
JSON.attribute("label", c.Label);
780+
JSON.attribute("type", toFullyQualifiedTypeNameString(c.Type));
781+
});
782+
}
783+
});
784+
});
785+
break;
786+
}
787+
788+
case CompileTimeValue::ValueKind::FunctionCall: {
789+
auto functionCallValue = cast<FunctionCallValue>(value);
790+
JSON.attribute("valueKind", "FunctionCall");
791+
JSON.attributeObject("value", [&]() {
792+
JSON.attribute("name", functionCallValue->getIdentifier());
793+
if (functionCallValue->getParameters().has_value()) {
794+
auto params = functionCallValue->getParameters().value();
795+
JSON.attributeArray("arguments", [&] {
796+
for (auto FP : params) {
737797
JSON.object([&] {
738-
JSON.attribute("label", c.Label);
739-
JSON.attribute("type", toFullyQualifiedTypeNameString(c.Type));
798+
JSON.attribute("label", FP.Label);
799+
JSON.attribute("type", toFullyQualifiedTypeNameString(FP.Type));
800+
writeValue(JSON, FP.Value);
740801
});
741802
}
742803
});
804+
}
805+
});
806+
break;
807+
}
808+
809+
case CompileTimeValue::ValueKind::MemberReference: {
810+
auto memberReferenceValue = cast<MemberReferenceValue>(value);
811+
JSON.attribute("valueKind", "MemberReference");
812+
JSON.attributeObject("value", [&]() {
813+
JSON.attribute("baseType", toFullyQualifiedTypeNameString(
814+
memberReferenceValue->getBaseType()));
815+
JSON.attribute("memberLabel", memberReferenceValue->getMemberLabel());
816+
});
817+
break;
818+
}
819+
case CompileTimeValue::ValueKind::InterpolatedString: {
820+
auto interpolatedStringValue = cast<InterpolatedStringLiteralValue>(value);
821+
JSON.attribute("valueKind", "InterpolatedStringLiteral");
822+
JSON.attributeObject("value", [&]() {
823+
JSON.attributeArray("segments", [&] {
824+
auto segments = interpolatedStringValue->getSegments();
825+
for (auto s : segments) {
826+
JSON.object([&] { writeValue(JSON, s); });
827+
}
743828
});
744-
break;
829+
});
830+
break;
745831
}
746832

747833
case CompileTimeValue::ValueKind::Runtime: {
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: echo "[MyProto]" > %t/protocols.json
3+
4+
// RUN: %target-swift-frontend -typecheck -emit-const-values-path %t/ExtractInterpolatedStringLiterals.swiftconstvalues -const-gather-protocols-file %t/protocols.json -primary-file %s
5+
// RUN: cat %t/ExtractInterpolatedStringLiterals.swiftconstvalues 2>&1 | %FileCheck %s
6+
7+
protocol MyProto {}
8+
9+
func generateString(input: String) -> String {
10+
return "function" + input
11+
}
12+
13+
public struct Internal: MyProto {
14+
static var internalTitle: String = "Inner title"
15+
}
16+
17+
public struct MyType {
18+
var nested: NestedOne
19+
20+
struct NestedOne {
21+
var foo: String
22+
}
23+
}
24+
25+
public struct External: MyProto {
26+
var interpolatedTitle = "Start Interpolation with Member Reference: \(Internal.internalTitle). Followed By Function Call: \(generateString(input: "test")). End with KeyPath: \(\MyType.nested.foo)."
27+
}
28+
29+
// CHECK: [
30+
// CHECK-NEXT: {
31+
// CHECK-NEXT: "typeName": "ExtractInterpolatedStringLiterals.Internal",
32+
// CHECK-NEXT: "mangledTypeName": "33ExtractInterpolatedStringLiterals8InternalV",
33+
// CHECK-NEXT: "kind": "struct",
34+
// CHECK-NEXT: "file": "{{.*}}test{{/|\\\\}}ConstExtraction{{/|\\\\}}ExtractInterpolatedStringLiterals.swift",
35+
// CHECK-NEXT: "line": 13,
36+
// CHECK-NEXT: "conformances": [
37+
// CHECK-NEXT: "ExtractInterpolatedStringLiterals.MyProto"
38+
// CHECK-NEXT: ],
39+
// CHECK-NEXT: "associatedTypeAliases": [],
40+
// CHECK-NEXT: "properties": [
41+
// CHECK-NEXT: {
42+
// CHECK-NEXT: "label": "internalTitle",
43+
// CHECK-NEXT: "type": "Swift.String",
44+
// CHECK-NEXT: "mangledTypeName": "n/a - deprecated",
45+
// CHECK-NEXT: "isStatic": "true",
46+
// CHECK-NEXT: "isComputed": "false",
47+
// CHECK-NEXT: "file": "{{.*}}test{{/|\\\\}}ConstExtraction{{/|\\\\}}ExtractInterpolatedStringLiterals.swift",
48+
// CHECK-NEXT: "line": 14,
49+
// CHECK-NEXT: "valueKind": "RawLiteral",
50+
// CHECK-NEXT: "value": "Inner title"
51+
// CHECK-NEXT: }
52+
// CHECK-NEXT: ]
53+
// CHECK-NEXT: },
54+
// CHECK-NEXT: {
55+
// CHECK-NEXT: "typeName": "ExtractInterpolatedStringLiterals.External",
56+
// CHECK-NEXT: "mangledTypeName": "33ExtractInterpolatedStringLiterals8ExternalV",
57+
// CHECK-NEXT: "kind": "struct",
58+
// CHECK-NEXT: "file": "{{.*}}test{{/|\\\\}}ConstExtraction{{/|\\\\}}ExtractInterpolatedStringLiterals.swift",
59+
// CHECK-NEXT: "line": 25,
60+
// CHECK-NEXT: "conformances": [
61+
// CHECK-NEXT: "ExtractInterpolatedStringLiterals.MyProto"
62+
// CHECK-NEXT: ],
63+
// CHECK-NEXT: "associatedTypeAliases": [],
64+
// CHECK-NEXT: "properties": [
65+
// CHECK-NEXT: {
66+
// CHECK-NEXT: "label": "interpolatedTitle",
67+
// CHECK-NEXT: "type": "Swift.String",
68+
// CHECK-NEXT: "mangledTypeName": "n/a - deprecated",
69+
// CHECK-NEXT: "isStatic": "false",
70+
// CHECK-NEXT: "isComputed": "false",
71+
// CHECK-NEXT: "file": "{{.*}}test{{/|\\\\}}ConstExtraction{{/|\\\\}}ExtractInterpolatedStringLiterals.swift",
72+
// CHECK-NEXT: "line": 26,
73+
// CHECK-NEXT: "valueKind": "InterpolatedStringLiteral",
74+
// CHECK-NEXT: "value": {
75+
// CHECK-NEXT: "segments": [
76+
// CHECK-NEXT: {
77+
// CHECK-NEXT: "valueKind": "RawLiteral",
78+
// CHECK-NEXT: "value": "Start Interpolation with Member Reference: "
79+
// CHECK-NEXT: },
80+
// CHECK-NEXT: {
81+
// CHECK-NEXT: "valueKind": "MemberReference",
82+
// CHECK-NEXT: "value": {
83+
// CHECK-NEXT: "baseType": "ExtractInterpolatedStringLiterals.Internal",
84+
// CHECK-NEXT: "memberLabel": "internalTitle"
85+
// CHECK-NEXT: }
86+
// CHECK-NEXT: },
87+
// CHECK-NEXT: {
88+
// CHECK-NEXT: "valueKind": "RawLiteral",
89+
// CHECK-NEXT: "value": ". Followed By Function Call: "
90+
// CHECK-NEXT: },
91+
// CHECK-NEXT: {
92+
// CHECK-NEXT: "valueKind": "FunctionCall",
93+
// CHECK-NEXT: "value": {
94+
// CHECK-NEXT: "name": "generateString",
95+
// CHECK-NEXT: "arguments": [
96+
// CHECK-NEXT: {
97+
// CHECK-NEXT: "label": "input",
98+
// CHECK-NEXT: "type": "Swift.String",
99+
// CHECK-NEXT: "valueKind": "RawLiteral",
100+
// CHECK-NEXT: "value": "test"
101+
// CHECK-NEXT: }
102+
// CHECK-NEXT: ]
103+
// CHECK-NEXT: }
104+
// CHECK-NEXT: },
105+
// CHECK-NEXT: {
106+
// CHECK-NEXT: "valueKind": "RawLiteral",
107+
// CHECK-NEXT: "value": ". End with KeyPath: "
108+
// CHECK-NEXT: },
109+
// CHECK-NEXT: {
110+
// CHECK-NEXT: "valueKind": "KeyPath",
111+
// CHECK-NEXT: "value": {
112+
// CHECK-NEXT: "path": "nested.foo",
113+
// CHECK-NEXT: "rootType": "ExtractInterpolatedStringLiterals.MyType",
114+
// CHECK-NEXT: "components": [
115+
// CHECK-NEXT: {
116+
// CHECK-NEXT: "label": "nested",
117+
// CHECK-NEXT: "type": "ExtractInterpolatedStringLiterals.MyType.NestedOne"
118+
// CHECK-NEXT: },
119+
// CHECK-NEXT: {
120+
// CHECK-NEXT: "label": "foo",
121+
// CHECK-NEXT: "type": "Swift.String"
122+
// CHECK-NEXT: }
123+
// CHECK-NEXT: ]
124+
// CHECK-NEXT: }
125+
// CHECK-NEXT: },
126+
// CHECK-NEXT: {
127+
// CHECK-NEXT: "valueKind": "RawLiteral",
128+
// CHECK-NEXT: "value": "."
129+
// CHECK-NEXT: }
130+
// CHECK-NEXT: ]
131+
// CHECK-NEXT: }
132+
// CHECK-NEXT: }
133+
// CHECK-NEXT: ]
134+
// CHECK-NEXT: }
135+
// CHECK-NEXT: ]

0 commit comments

Comments
 (0)