Skip to content

Commit 62de52a

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

File tree

3 files changed

+302
-15
lines changed

3 files changed

+302
-15
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: 101 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -286,13 +286,25 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {
286286

287287
case ExprKind::Call: {
288288
auto callExpr = cast<CallExpr>(expr);
289-
if (callExpr->getFn()->getKind() == ExprKind::ConstructorRefCall) {
289+
auto functionKind = callExpr->getFn()->getKind();
290+
291+
if (functionKind == ExprKind::DeclRef) {
292+
auto declRefExpr = cast<DeclRefExpr>(callExpr->getFn());
293+
auto caseName =
294+
declRefExpr->getDecl()->getName().getBaseIdentifier().str().str();
295+
296+
std::vector<FunctionParameter> parameters =
297+
extractFunctionArguments(callExpr->getArgs());
298+
return std::make_shared<FunctionCallValue>(caseName, parameters);
299+
}
300+
301+
if (functionKind == ExprKind::ConstructorRefCall) {
290302
std::vector<FunctionParameter> parameters =
291303
extractFunctionArguments(callExpr->getArgs());
292304
return std::make_shared<InitCallValue>(callExpr->getType(), parameters);
293305
}
294306

295-
if (callExpr->getFn()->getKind() == ExprKind::DotSyntaxCall) {
307+
if (functionKind == ExprKind::DotSyntaxCall) {
296308
auto dotSyntaxCallExpr = cast<DotSyntaxCallExpr>(callExpr->getFn());
297309
auto fn = dotSyntaxCallExpr->getFn();
298310
if (fn->getKind() == ExprKind::DeclRef) {
@@ -408,6 +420,35 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {
408420
return extractCompileTimeValue(injectIntoOptionalExpr->getSubExpr());
409421
}
410422

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

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) {
769+
case CompileTimeValue::ValueKind::KeyPath: {
770+
auto keyPathValue = cast<KeyPathValue>(value);
771+
JSON.attribute("valueKind", "KeyPath");
772+
JSON.attributeObject("value", [&]() {
773+
JSON.attribute("path", keyPathValue->getPath());
774+
JSON.attribute("rootType", toFullyQualifiedTypeNameString(
775+
keyPathValue->getRootType()));
776+
JSON.attributeArray("components", [&] {
777+
auto components = keyPathValue->getComponents();
778+
for (auto c : components) {
779+
JSON.object([&] {
780+
JSON.attribute("label", c.Label);
781+
JSON.attribute("type", toFullyQualifiedTypeNameString(c.Type));
782+
});
783+
}
784+
});
785+
});
786+
break;
787+
}
788+
789+
case CompileTimeValue::ValueKind::FunctionCall: {
790+
auto functionCallValue = cast<FunctionCallValue>(value);
791+
JSON.attribute("valueKind", "FunctionCall");
792+
JSON.attributeObject("value", [&]() {
793+
JSON.attribute("name", functionCallValue->getIdentifier());
794+
if (functionCallValue->getParameters().has_value()) {
795+
auto params = functionCallValue->getParameters().value();
796+
JSON.attributeArray("arguments", [&] {
797+
for (auto FP : params) {
737798
JSON.object([&] {
738-
JSON.attribute("label", c.Label);
739-
JSON.attribute("type", toFullyQualifiedTypeNameString(c.Type));
799+
JSON.attribute("label", FP.Label);
800+
JSON.attribute("type", toFullyQualifiedTypeNameString(FP.Type));
801+
writeValue(JSON, FP.Value);
740802
});
741803
}
742804
});
805+
}
806+
});
807+
break;
808+
}
809+
810+
case CompileTimeValue::ValueKind::MemberReference: {
811+
auto memberReferenceValue = cast<MemberReferenceValue>(value);
812+
JSON.attribute("valueKind", "MemberReference");
813+
JSON.attributeObject("value", [&]() {
814+
JSON.attribute("baseType", toFullyQualifiedTypeNameString(
815+
memberReferenceValue->getBaseType()));
816+
JSON.attribute("memberLabel", memberReferenceValue->getMemberLabel());
817+
});
818+
break;
819+
}
820+
case CompileTimeValue::ValueKind::InterpolatedString: {
821+
auto interpolatedStringValue = cast<InterpolatedStringLiteralValue>(value);
822+
JSON.attribute("valueKind", "InterpolatedStringLiteral");
823+
JSON.attributeObject("value", [&]() {
824+
JSON.attributeArray("segments", [&] {
825+
auto segments = interpolatedStringValue->getSegments();
826+
for (auto s : segments) {
827+
JSON.object([&] { writeValue(JSON, s); });
828+
}
743829
});
744-
break;
830+
});
831+
break;
745832
}
746833

747834
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)