Skip to content

Commit c0295bf

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

File tree

3 files changed

+212
-2
lines changed

3 files changed

+212
-2
lines changed

include/swift/AST/ConstTypeInfo.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class CompileTimeValue {
3838
Enum,
3939
Type,
4040
KeyPath,
41+
InterpolatedString,
4142
Runtime
4243
};
4344

@@ -185,7 +186,7 @@ class EnumValue : public CompileTimeValue {
185186
std::optional<std::vector<FunctionParameter>> Parameters;
186187
};
187188

188-
/// An type value representation
189+
/// A type value representation
189190
class TypeValue : public CompileTimeValue {
190191
public:
191192
TypeValue(swift::Type Type) : CompileTimeValue(ValueKind::Type), Type(Type) {}
@@ -228,6 +229,34 @@ class KeyPathValue : public CompileTimeValue {
228229
std::vector<Component> Components;
229230
};
230231

232+
/// A representation of an Interpolated String Literal
233+
class InterpolatedStringLiteralValue : public CompileTimeValue {
234+
public:
235+
struct InterpolationSegment {
236+
std::string SegmentKey;
237+
std::string Label;
238+
swift::Type BaseType;
239+
};
240+
InterpolatedStringLiteralValue(std::vector<std::string> Segments,
241+
std::vector<InterpolationSegment> Interpolations)
242+
: CompileTimeValue(ValueKind::InterpolatedString), Segments(Segments), Interpolations(Interpolations) {}
243+
244+
std::vector<std::string> getSegments() const {
245+
return Segments;
246+
}
247+
std::vector<InterpolationSegment> getInterpolations() const {
248+
return Interpolations;
249+
}
250+
251+
static bool classof(const CompileTimeValue *T) {
252+
return T->getKind() == ValueKind::InterpolatedString;
253+
}
254+
255+
private:
256+
std::vector<std::string> Segments;
257+
std::vector<InterpolationSegment> Interpolations;
258+
};
259+
231260
/// A representation of an arbitrary value that does not fall under
232261
/// any of the above categories.
233262
class RuntimeValue : public CompileTimeValue {

lib/ConstExtract/ConstExtract.cpp

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,65 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {
408408
return extractCompileTimeValue(injectIntoOptionalExpr->getSubExpr());
409409
}
410410

411+
case ExprKind::InterpolatedStringLiteral: {
412+
auto interpolatedStringExpr = cast<InterpolatedStringLiteralExpr>(expr);
413+
auto tapExpr = interpolatedStringExpr->getAppendingExpr();
414+
auto &Ctx = tapExpr->getVar()->getASTContext();
415+
int index = 0;
416+
417+
std::vector<std::string> segments;
418+
std::vector<InterpolatedStringLiteralValue::InterpolationSegment> interpolations;
419+
interpolatedStringExpr->forEachSegment(Ctx, [&](bool isInterpolation, CallExpr *segment) -> void {
420+
auto arg = segment->getArgs()->get(0);
421+
if (isInterpolation) {
422+
auto expr = arg.getExpr();
423+
auto exprKind = expr->getKindName(expr->getKind()).str();
424+
425+
if (exprKind == "Load") {
426+
auto loadExpr = cast<LoadExpr>(expr);
427+
auto memberExpr = cast<MemberRefExpr>(loadExpr->getSubExpr());
428+
auto baseTypeExpr = cast<TypeExpr>(memberExpr->getBase());
429+
auto identifier = memberExpr->getDecl().getDecl()->getBaseIdentifier().str();
430+
auto segmentKey = "${Interpolation__" + std::to_string(index) + "}";
431+
segments.push_back(segmentKey);
432+
interpolations.push_back({segmentKey, identifier.str(), baseTypeExpr->getInstanceType()});
433+
} else if (exprKind == "Call") {
434+
auto callExpr = cast<CallExpr>(expr);
435+
auto declRef = cast<DeclRefExpr>(callExpr->getFn());
436+
llvm::SmallVector<char, 16> scratch;
437+
auto identifier = declRef->getDecl()->getName().getString(scratch);
438+
segments.push_back("${FunctionCall__" + identifier.str() + "}");
439+
} else if (exprKind == "KeyPath") {
440+
auto keyPathExpr = cast<KeyPathExpr>(expr);
441+
auto rootType = keyPathExpr->getRootType();
442+
auto components = keyPathExpr->getComponents();
443+
std::string path = "";
444+
auto numberOfComponents = static_cast<int>(components.size());
445+
for (int i = 0; i < numberOfComponents; i++) {
446+
auto component = components[i];
447+
if (component.isResolved()) {
448+
if (i != 0) {
449+
path += ".";
450+
}
451+
path += component.getDeclRef().getDecl()->getBaseIdentifier().str();
452+
}
453+
}
454+
auto segmentKey = "${KeyPath__" + std::to_string(index) + "}";
455+
segments.push_back(segmentKey);
456+
interpolations.push_back({segmentKey, path, rootType});
457+
} else {
458+
segments.push_back("${Runtime}");
459+
}
460+
} else {
461+
auto stringLiteralExpr = cast<StringLiteralExpr>(arg.getExpr());
462+
segments.push_back(stringLiteralExpr->getValue().str());
463+
}
464+
index++;
465+
});
466+
467+
return std::make_shared<InterpolatedStringLiteralValue>(segments, interpolations);
468+
}
469+
411470
default: {
412471
break;
413472
}
@@ -725,7 +784,7 @@ void writeValue(llvm::json::OStream &JSON,
725784
break;
726785
}
727786

728-
case CompileTimeValue::KeyPath: {
787+
case CompileTimeValue::ValueKind::KeyPath: {
729788
auto keyPathValue = cast<KeyPathValue>(value);
730789
JSON.attribute("valueKind", "KeyPath");
731790
JSON.attributeObject("value", [&]() {
@@ -744,6 +803,26 @@ void writeValue(llvm::json::OStream &JSON,
744803
break;
745804
}
746805

806+
case CompileTimeValue::ValueKind::InterpolatedString: {
807+
auto interpolatedStringValue = cast<InterpolatedStringLiteralValue>(value);
808+
JSON.attribute("valueKind", "InterpolatedStringLiteral");
809+
JSON.attributeObject("value", [&]() {
810+
JSON.attribute("segments", interpolatedStringValue->getSegments());
811+
JSON.attributeArray("interpolations", [&] {
812+
auto interpolations = interpolatedStringValue->getInterpolations();
813+
for (auto i : interpolations) {
814+
JSON.object([&] {
815+
JSON.attribute("segmentKey", i.SegmentKey);
816+
JSON.attribute("baseTypeName", toFullyQualifiedTypeNameString(i.BaseType));
817+
JSON.attribute("mangledBaseTypeName", toMangledTypeNameString(i.BaseType));
818+
JSON.attribute("label", i.Label);
819+
});
820+
}
821+
});
822+
});
823+
break;
824+
}
825+
747826
case CompileTimeValue::ValueKind::Runtime: {
748827
JSON.attribute("valueKind", "Runtime");
749828
break;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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: "Start Interpolation with Member Reference: ",
77+
// CHECK-NEXT: "${Interpolation__1}",
78+
// CHECK-NEXT: ". Followed By Function Call: ",
79+
// CHECK-NEXT: "${FunctionCall__generateString(input:)}",
80+
// CHECK-NEXT: ". End with KeyPath: ",
81+
// CHECK-NEXT: "${KeyPath__5}",
82+
// CHECK-NEXT: "."
83+
// CHECK-NEXT: ],
84+
// CHECK-NEXT: "interpolations": [
85+
// CHECK-NEXT: {
86+
// CHECK-NEXT: "segmentKey": "${Interpolation__1}",
87+
// CHECK-NEXT: "baseTypeName": "ExtractInterpolatedStringLiterals.Internal",
88+
// CHECK-NEXT: "mangledBaseTypeName": "33ExtractInterpolatedStringLiterals8InternalV",
89+
// CHECK-NEXT: "label": "internalTitle"
90+
// CHECK-NEXT: },
91+
// CHECK-NEXT: {
92+
// CHECK-NEXT: "segmentKey": "${KeyPath__5}",
93+
// CHECK-NEXT: "baseTypeName": "ExtractInterpolatedStringLiterals.MyType",
94+
// CHECK-NEXT: "mangledBaseTypeName": "33ExtractInterpolatedStringLiterals6MyTypeV",
95+
// CHECK-NEXT: "label": "nested.foo"
96+
// CHECK-NEXT: }
97+
// CHECK-NEXT: ]
98+
// CHECK-NEXT: }
99+
// CHECK-NEXT: }
100+
// CHECK-NEXT: ]
101+
// CHECK-NEXT: }
102+
// CHECK-NEXT: ]

0 commit comments

Comments
 (0)