Skip to content

[Compile Time Constant Extraction] Extract Interpolated String Literals #74491

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 66 additions & 1 deletion include/swift/AST/ConstTypeInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class CompileTimeValue {
Enum,
Type,
KeyPath,
FunctionCall,
MemberReference,
InterpolatedString,
Runtime
};

Expand Down Expand Up @@ -185,7 +188,7 @@ class EnumValue : public CompileTimeValue {
std::optional<std::vector<FunctionParameter>> Parameters;
};

/// An type value representation
/// A type value representation
class TypeValue : public CompileTimeValue {
public:
TypeValue(swift::Type Type) : CompileTimeValue(ValueKind::Type), Type(Type) {}
Expand Down Expand Up @@ -228,6 +231,68 @@ class KeyPathValue : public CompileTimeValue {
std::vector<Component> Components;
};

/// A function call representation. This is for a function declaration such as
/// let foo = bar(baz: "abc")
class FunctionCallValue : public CompileTimeValue {
public:
FunctionCallValue(std::string Identifier,
std::optional<std::vector<FunctionParameter>> Parameters)
: CompileTimeValue(ValueKind::FunctionCall), Identifier(Identifier),
Parameters(Parameters) {}

std::string getIdentifier() const { return Identifier; }
std::optional<std::vector<FunctionParameter>> getParameters() const {
return Parameters;
}

static bool classof(const CompileTimeValue *T) {
return T->getKind() == ValueKind::FunctionCall;
}

private:
std::string Identifier;
std::optional<std::vector<FunctionParameter>> Parameters;
};

/// A member reference representation such as
/// let foo = MyStruct.bar
class MemberReferenceValue : public CompileTimeValue {
public:
MemberReferenceValue(swift::Type BaseType, std::string MemberLabel)
: CompileTimeValue(ValueKind::MemberReference), BaseType(BaseType),
MemberLabel(MemberLabel) {}

std::string getMemberLabel() const { return MemberLabel; }
swift::Type getBaseType() const { return BaseType; }

static bool classof(const CompileTimeValue *T) {
return T->getKind() == ValueKind::MemberReference;
}

private:
swift::Type BaseType;
std::string MemberLabel;
};

/// A representation of an Interpolated String Literal
class InterpolatedStringLiteralValue : public CompileTimeValue {
public:
InterpolatedStringLiteralValue(
std::vector<std::shared_ptr<CompileTimeValue>> Segments)
: CompileTimeValue(ValueKind::InterpolatedString), Segments(Segments) {}

std::vector<std::shared_ptr<CompileTimeValue>> getSegments() const {
return Segments;
}

static bool classof(const CompileTimeValue *T) {
return T->getKind() == ValueKind::InterpolatedString;
}

private:
std::vector<std::shared_ptr<CompileTimeValue>> Segments;
};

/// A representation of an arbitrary value that does not fall under
/// any of the above categories.
class RuntimeValue : public CompileTimeValue {
Expand Down
118 changes: 104 additions & 14 deletions lib/ConstExtract/ConstExtract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,13 +286,25 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {

case ExprKind::Call: {
auto callExpr = cast<CallExpr>(expr);
if (callExpr->getFn()->getKind() == ExprKind::ConstructorRefCall) {
auto functionKind = callExpr->getFn()->getKind();

if (functionKind == ExprKind::DeclRef) {
auto declRefExpr = cast<DeclRefExpr>(callExpr->getFn());
auto caseName =
declRefExpr->getDecl()->getName().getBaseIdentifier().str().str();

std::vector<FunctionParameter> parameters =
extractFunctionArguments(callExpr->getArgs());
return std::make_shared<FunctionCallValue>(caseName, parameters);
}

if (functionKind == ExprKind::ConstructorRefCall) {
std::vector<FunctionParameter> parameters =
extractFunctionArguments(callExpr->getArgs());
return std::make_shared<InitCallValue>(callExpr->getType(), parameters);
}

if (callExpr->getFn()->getKind() == ExprKind::DotSyntaxCall) {
if (functionKind == ExprKind::DotSyntaxCall) {
auto dotSyntaxCallExpr = cast<DotSyntaxCallExpr>(callExpr->getFn());
auto fn = dotSyntaxCallExpr->getFn();
if (fn->getKind() == ExprKind::DeclRef) {
Expand Down Expand Up @@ -408,6 +420,38 @@ static std::shared_ptr<CompileTimeValue> extractCompileTimeValue(Expr *expr) {
return extractCompileTimeValue(injectIntoOptionalExpr->getSubExpr());
}

case ExprKind::Load: {
auto loadExpr = cast<LoadExpr>(expr);
return extractCompileTimeValue(loadExpr->getSubExpr());
}

case ExprKind::MemberRef: {
auto memberExpr = cast<MemberRefExpr>(expr);
if (isa<TypeExpr>(memberExpr->getBase())) {
auto baseTypeExpr = cast<TypeExpr>(memberExpr->getBase());
auto label = memberExpr->getDecl().getDecl()->getBaseIdentifier().str();
return std::make_shared<MemberReferenceValue>(
baseTypeExpr->getInstanceType(), label.str());
}
break;
}

case ExprKind::InterpolatedStringLiteral: {
auto interpolatedStringExpr = cast<InterpolatedStringLiteralExpr>(expr);
auto tapExpr = interpolatedStringExpr->getAppendingExpr();
auto &Ctx = tapExpr->getVar()->getASTContext();

std::vector<std::shared_ptr<CompileTimeValue>> segments;
interpolatedStringExpr->forEachSegment(
Ctx, [&](bool isInterpolation, CallExpr *segment) -> void {
auto arg = segment->getArgs()->get(0);
auto expr = arg.getExpr();
segments.push_back(extractCompileTimeValue(expr));
});

return std::make_shared<InterpolatedStringLiteralValue>(segments);
}

default: {
break;
}
Expand Down Expand Up @@ -725,23 +769,69 @@ void writeValue(llvm::json::OStream &JSON,
break;
}

case CompileTimeValue::KeyPath: {
auto keyPathValue = cast<KeyPathValue>(value);
JSON.attribute("valueKind", "KeyPath");
JSON.attributeObject("value", [&]() {
JSON.attribute("path", keyPathValue->getPath());
JSON.attribute("rootType", toFullyQualifiedTypeNameString(keyPathValue->getRootType()));
JSON.attributeArray("components", [&] {
auto components = keyPathValue->getComponents();
for (auto c : components) {
case CompileTimeValue::ValueKind::KeyPath: {
auto keyPathValue = cast<KeyPathValue>(value);
JSON.attribute("valueKind", "KeyPath");
JSON.attributeObject("value", [&]() {
JSON.attribute("path", keyPathValue->getPath());
JSON.attribute("rootType", toFullyQualifiedTypeNameString(
keyPathValue->getRootType()));
JSON.attributeArray("components", [&] {
auto components = keyPathValue->getComponents();
for (auto c : components) {
JSON.object([&] {
JSON.attribute("label", c.Label);
JSON.attribute("type", toFullyQualifiedTypeNameString(c.Type));
});
}
});
});
break;
}

case CompileTimeValue::ValueKind::FunctionCall: {
auto functionCallValue = cast<FunctionCallValue>(value);
JSON.attribute("valueKind", "FunctionCall");
JSON.attributeObject("value", [&]() {
JSON.attribute("name", functionCallValue->getIdentifier());
if (functionCallValue->getParameters().has_value()) {
auto params = functionCallValue->getParameters().value();
JSON.attributeArray("arguments", [&] {
for (auto FP : params) {
JSON.object([&] {
JSON.attribute("label", c.Label);
JSON.attribute("type", toFullyQualifiedTypeNameString(c.Type));
JSON.attribute("label", FP.Label);
JSON.attribute("type", toFullyQualifiedTypeNameString(FP.Type));
writeValue(JSON, FP.Value);
});
}
});
}
});
break;
}

case CompileTimeValue::ValueKind::MemberReference: {
auto memberReferenceValue = cast<MemberReferenceValue>(value);
JSON.attribute("valueKind", "MemberReference");
JSON.attributeObject("value", [&]() {
JSON.attribute("baseType", toFullyQualifiedTypeNameString(
memberReferenceValue->getBaseType()));
JSON.attribute("memberLabel", memberReferenceValue->getMemberLabel());
});
break;
}
case CompileTimeValue::ValueKind::InterpolatedString: {
auto interpolatedStringValue = cast<InterpolatedStringLiteralValue>(value);
JSON.attribute("valueKind", "InterpolatedStringLiteral");
JSON.attributeObject("value", [&]() {
JSON.attributeArray("segments", [&] {
auto segments = interpolatedStringValue->getSegments();
for (auto s : segments) {
JSON.object([&] { writeValue(JSON, s); });
}
});
break;
});
break;
}

case CompileTimeValue::ValueKind::Runtime: {
Expand Down
38 changes: 36 additions & 2 deletions test/ConstExtraction/ExtractCalls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,24 @@ public struct Bat {
// CHECK-NEXT: {
// CHECK-NEXT: "label": "fuz",
// CHECK-NEXT: "type": "Swift.Int",
// CHECK-NEXT: "valueKind": "Runtime"
// CHECK-NEXT: "valueKind": "FunctionCall",
// CHECK-NEXT: "value": {
// CHECK-NEXT: "name": "adder",
// CHECK-NEXT: "arguments": [
// CHECK-NEXT: {
// CHECK-NEXT: "label": "",
// CHECK-NEXT: "type": "Swift.Int",
// CHECK-NEXT: "valueKind": "RawLiteral",
// CHECK-NEXT: "value": "2"
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "label": "",
// CHECK-NEXT: "type": "Swift.Int",
// CHECK-NEXT: "valueKind": "RawLiteral",
// CHECK-NEXT: "value": "3"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }
Expand All @@ -123,7 +140,24 @@ public struct Bat {
// CHECK-NEXT: "isComputed": "false",
// CHECK-NEXT: "file": "{{.*}}test{{/|\\\\}}ConstExtraction{{/|\\\\}}ExtractCalls.swift",
// CHECK-NEXT: "line": 15,
// CHECK-NEXT: "valueKind": "Runtime"
// CHECK-NEXT: "valueKind": "FunctionCall",
// CHECK-NEXT: "value": {
// CHECK-NEXT: "name": "adder",
// CHECK-NEXT: "arguments": [
// CHECK-NEXT: {
// CHECK-NEXT: "label": "",
// CHECK-NEXT: "type": "Swift.Int",
// CHECK-NEXT: "valueKind": "RawLiteral",
// CHECK-NEXT: "value": "2"
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "label": "",
// CHECK-NEXT: "type": "Swift.Int",
// CHECK-NEXT: "valueKind": "RawLiteral",
// CHECK-NEXT: "value": "3"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: {
// CHECK-NEXT: "label": "init4",
Expand Down
12 changes: 11 additions & 1 deletion test/ConstExtraction/ExtractEnums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,17 @@ public struct Enums: MyProto {
// CHECK-NEXT: "mangledTypeName": "n/a - deprecated",
// CHECK-NEXT: "isStatic": "false",
// CHECK-NEXT: "isComputed": "true",
// CHECK-NEXT: "valueKind": "Runtime"
// CHECK-NEXT: "valueKind": "FunctionCall",
// CHECK-NEXT: "value": {
// CHECK-NEXT: "name": "_hashValue",
// CHECK-NEXT: "arguments": [
// CHECK-NEXT: {
// CHECK-NEXT: "label": "for",
// CHECK-NEXT: "type": "ExtractEnums.SimpleEnum",
// CHECK-NEXT: "valueKind": "Runtime"
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK-NEXT: }
// CHECK-NEXT: }
// CHECK-NEXT: ],
// CHECK-NEXT: "cases": [
Expand Down
Loading