Files
swift-mirror/lib/ConstExtract/ConstExtract.cpp
Hamish Knight f8ab391737 Introduce type sugar for InlineArray (#80087)
* [CS] Decline to handle InlineArray in shrink

Previously we would try the contextual type `(<int>, <element>)`,
which is wrong. Given we want to eliminate shrink, let's just bail.

* [Sema] Sink `ValueMatchVisitor` into `applyUnboundGenericArguments`

Make sure it's called for sugar code paths too. Also let's just always
run it since it should be a pretty cheap check.

* [Sema] Diagnose passing integer to non-integer type parameter

This was previously missed, though would have been diagnosed later
as a requirement failure.

* [Parse] Split up `canParseType` 

While here, address the FIXME in `canParseTypeSimpleOrComposition`
and only check to see if we can parse a type-simple, including
`each`, `some`, and `any` for better recovery.

* Introduce type sugar for InlineArray

Parse e.g `[3 x Int]` as type sugar for InlineArray. Gated behind
an experimental feature flag for now.
2025-03-23 15:31:37 -07:00

1525 lines
53 KiB
C++

//===-------- ConstExtract.cpp -- Gather Compile-Time-Known Values --------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/ConstExtract/ConstExtract.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTMangler.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/AvailabilitySpec.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/AST/Evaluator.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/TypeID.h"
#include "swift/ConstExtract/ConstExtractRequests.h"
#include "swift/Subsystems.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
#include <set>
#include <sstream>
#include <string>
using namespace swift;
namespace {
/// A helper class to collect all nominal type declarations that conform to
/// specific protocols provided as input.
class NominalTypeConformanceCollector : public ASTWalker {
const std::unordered_set<std::string> &Protocols;
std::vector<NominalTypeDecl *> &ConformanceTypeDecls;
public:
NominalTypeConformanceCollector(
const std::unordered_set<std::string> &Protocols,
std::vector<NominalTypeDecl *> &ConformanceDecls)
: Protocols(Protocols), ConformanceTypeDecls(ConformanceDecls) {}
MacroWalking getMacroWalkingBehavior() const override {
return MacroWalking::ArgumentsAndExpansion;
}
PreWalkAction walkToDeclPre(Decl *D) override {
auto *NTD = llvm::dyn_cast<NominalTypeDecl>(D);
if (!NTD)
if (auto *ETD = dyn_cast<ExtensionDecl>(D))
NTD = ETD->getExtendedNominal();
if (NTD)
if (!isa<ProtocolDecl>(NTD) && CheckedDecls.insert(NTD).second) {
if (NTD->getAttrs().hasAttribute<ExtractConstantsFromMembersAttr>()) {
ConformanceTypeDecls.push_back(NTD);
goto visitAuxiliaryDecls;
}
for (auto &Protocol : NTD->getAllProtocols())
if (Protocol->getAttrs()
.hasAttribute<ExtractConstantsFromMembersAttr>() ||
Protocols.count(Protocol->getName().str().str()) != 0) {
ConformanceTypeDecls.push_back(NTD);
goto visitAuxiliaryDecls;
}
}
visitAuxiliaryDecls:
// Visit peers expanded from macros
D->visitAuxiliaryDecls([&](Decl *decl) { decl->walk(*this); },
/*visitFreestandingExpanded=*/false);
return Action::Continue();
}
private:
std::unordered_set<NominalTypeDecl *> CheckedDecls;
};
std::string toFullyQualifiedTypeNameString(const swift::Type &Type) {
std::string TypeNameOutput;
llvm::raw_string_ostream OutputStream(TypeNameOutput);
swift::PrintOptions Options;
Options.FullyQualifiedTypes = true;
Options.PreferTypeRepr = true;
Options.AlwaysDesugarArraySliceTypes = true;
Options.AlwaysDesugarInlineArrayTypes = true;
Options.AlwaysDesugarDictionaryTypes = true;
Options.AlwaysDesugarOptionalTypes = true;
Options.PrintTypeAliasUnderlyingType = true;
Options.OpaqueReturnTypePrinting =
PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword;
Type.print(OutputStream, Options);
OutputStream.flush();
return TypeNameOutput;
}
std::string toFullyQualifiedProtocolNameString(const swift::ProtocolDecl &Protocol) {
// Protocols cannot be nested in other declarations, so the only fully-qualified
// context is the declaring module name.
return Protocol.getParentModule()->getNameStr().str() + "." + Protocol.getNameStr().str();
}
std::string toMangledTypeNameString(const swift::Type &Type) {
auto PrintingType = Type;
if (Type->hasArchetype())
PrintingType = Type->mapTypeOutOfContext();
return Mangle::ASTMangler(Type->getASTContext()).mangleTypeWithoutPrefix(PrintingType->getCanonicalType());
}
} // namespace
namespace swift {
bool
parseProtocolListFromFile(StringRef protocolListFilePath,
DiagnosticEngine &diags,
std::unordered_set<std::string> &protocols) {
// Load the input file.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
llvm::MemoryBuffer::getFile(protocolListFilePath);
if (!FileBufOrErr) {
diags.diagnose(SourceLoc(),
diag::const_extract_protocol_list_input_file_missing,
protocolListFilePath);
return false;
}
// Parse a JSON file containing a list of the format:
// [proto1, proto2, proto3]
bool ParseFailed = false;
{
StringRef Buffer = FileBufOrErr->get()->getBuffer();
llvm::SourceMgr SM;
llvm::yaml::Stream S(Buffer, SM);
llvm::yaml::SequenceNode *Sequence =
dyn_cast<llvm::yaml::SequenceNode>(S.begin()->getRoot());
if (Sequence) {
for (auto &ProtocolNameNode : *Sequence) {
auto *ScalarNode = dyn_cast<llvm::yaml::ScalarNode>(&ProtocolNameNode);
if (!ScalarNode) {
ParseFailed = true;
break;
}
auto protocolNameStr = ScalarNode->getRawValue().str();
if (protocolNameStr.front() == '"' && protocolNameStr.back() == '"')
protocolNameStr = protocolNameStr.substr(1, protocolNameStr.size() - 2);
protocols.insert(protocolNameStr);
}
} else
ParseFailed = true;
}
if (ParseFailed) {
diags.diagnose(SourceLoc(),
diag::const_extract_protocol_list_input_file_corrupted,
protocolListFilePath);
return false;
}
return true;
}
std::vector<std::shared_ptr<BuilderValue::BuilderMember>>
getResultBuilderMembersFromBraceStmt(BraceStmt *braceStmt,
const DeclContext *declContext);
static std::shared_ptr<CompileTimeValue>
extractCompileTimeValue(Expr *expr, const DeclContext *declContext);
static std::vector<FunctionParameter>
extractFunctionArguments(const ArgumentList *args,
const DeclContext *declContext) {
std::vector<FunctionParameter> parameters;
for (auto arg : *args) {
auto argExpr = arg.getExpr();
const auto label = arg.getLabel().str().str();
const auto type = argExpr->getType();
if (auto defaultArgument = dyn_cast<DefaultArgumentExpr>(argExpr)) {
auto *decl = defaultArgument->getParamDecl();
if (decl->hasDefaultExpr()) {
argExpr = decl->getTypeCheckedDefaultExpr();
}
} else if (auto optionalInject = dyn_cast<InjectIntoOptionalExpr>(argExpr)) {
argExpr = optionalInject->getSubExpr();
}
parameters.push_back(
{label, type, extractCompileTimeValue(argExpr, declContext)});
}
return parameters;
}
static std::optional<std::string> extractRawLiteral(Expr *expr) {
if (expr) {
switch (expr->getKind()) {
case ExprKind::BooleanLiteral:
case ExprKind::FloatLiteral:
case ExprKind::IntegerLiteral: {
std::string literalOutput;
llvm::raw_string_ostream OutputStream(literalOutput);
expr->printConstExprValue(&OutputStream, nullptr);
if (!literalOutput.empty()) {
return literalOutput;
}
break;
}
case ExprKind::StringLiteral: {
auto stringLiteralExpression = cast<StringLiteralExpr>(expr);
std::string literalOutput;
llvm::raw_string_ostream OutputStream(literalOutput);
OutputStream << stringLiteralExpression->getValue();
return literalOutput;
}
default:
break;
}
}
return std::nullopt;
}
static std::shared_ptr<CompileTimeValue>
extractCompileTimeValue(Expr *expr, const DeclContext *declContext) {
if (expr) {
switch (expr->getKind()) {
case ExprKind::BooleanLiteral:
case ExprKind::FloatLiteral:
case ExprKind::IntegerLiteral:
case ExprKind::StringLiteral: {
auto rawLiteral = extractRawLiteral(expr);
if (rawLiteral.has_value()) {
return std::make_shared<RawLiteralValue>(rawLiteral.value());
}
break;
}
case ExprKind::NilLiteral: {
return std::make_shared<NilLiteralValue>();
}
case ExprKind::Array: {
auto arrayExpr = cast<ArrayExpr>(expr);
std::vector<std::shared_ptr<CompileTimeValue>> elementValues;
for (const auto elementExpr : arrayExpr->getElements()) {
elementValues.push_back(
extractCompileTimeValue(elementExpr, declContext));
}
return std::make_shared<ArrayValue>(elementValues);
}
case ExprKind::Dictionary: {
auto dictionaryExpr = cast<DictionaryExpr>(expr);
std::vector<std::shared_ptr<TupleValue>> tuples;
for (auto elementExpr : dictionaryExpr->getElements()) {
auto elementValue = extractCompileTimeValue(elementExpr, declContext);
if (isa<TupleValue>(elementValue.get())) {
tuples.push_back(std::static_pointer_cast<TupleValue>(elementValue));
}
}
return std::make_shared<DictionaryValue>(tuples);
}
case ExprKind::Tuple: {
auto tupleExpr = cast<TupleExpr>(expr);
std::vector<TupleElement> elements;
if (tupleExpr->hasElementNames()) {
for (auto pair : llvm::zip(tupleExpr->getElements(),
tupleExpr->getElementNames())) {
auto elementExpr = std::get<0>(pair);
auto elementName = std::get<1>(pair);
std::optional<std::string> label =
elementName.empty()
? std::nullopt
: std::optional<std::string>(elementName.str().str());
elements.push_back(
{label, elementExpr->getType(),
extractCompileTimeValue(elementExpr, declContext)});
}
} else {
for (auto elementExpr : tupleExpr->getElements()) {
elements.push_back(
{std::nullopt, elementExpr->getType(),
extractCompileTimeValue(elementExpr, declContext)});
}
}
return std::make_shared<TupleValue>(elements);
}
case ExprKind::Call: {
auto callExpr = cast<CallExpr>(expr);
auto functionKind = callExpr->getFn()->getKind();
if (functionKind == ExprKind::DeclRef) {
auto declRefExpr = cast<DeclRefExpr>(callExpr->getFn());
auto identifier =
declRefExpr->getDecl()->getName().getBaseIdentifier().str().str();
std::vector<FunctionParameter> parameters =
extractFunctionArguments(callExpr->getArgs(), declContext);
return std::make_shared<FunctionCallValue>(identifier, parameters);
}
if (functionKind == ExprKind::ConstructorRefCall) {
std::vector<FunctionParameter> parameters =
extractFunctionArguments(callExpr->getArgs(), declContext);
return std::make_shared<InitCallValue>(callExpr->getType(), parameters);
}
if (functionKind == ExprKind::DotSyntaxCall) {
auto dotSyntaxCallExpr = cast<DotSyntaxCallExpr>(callExpr->getFn());
auto fn = dotSyntaxCallExpr->getFn();
if (fn->getKind() == ExprKind::DeclRef) {
auto declRefExpr = cast<DeclRefExpr>(fn);
auto baseIdentifierName =
declRefExpr->getDecl()->getName().getBaseIdentifier().str().str();
std::vector<FunctionParameter> parameters =
extractFunctionArguments(callExpr->getArgs(), declContext);
auto declRef = dotSyntaxCallExpr->getFn()->getReferencedDecl();
switch (declRef.getDecl()->getKind()) {
case DeclKind::EnumElement: {
return std::make_shared<EnumValue>(baseIdentifierName, parameters);
}
case DeclKind::Func: {
auto identifier = declRefExpr->getDecl()
->getName()
.getBaseIdentifier()
.str()
.str();
return std::make_shared<StaticFunctionCallValue>(
identifier, callExpr->getType(), parameters);
}
default: {
break;
}
}
}
}
break;
}
case ExprKind::DotSyntaxCall: {
auto dotSyntaxCallExpr = cast<DotSyntaxCallExpr>(expr);
auto fn = dotSyntaxCallExpr->getFn();
if (fn->getKind() == ExprKind::DeclRef) {
auto declRefExpr = cast<DeclRefExpr>(fn);
auto caseName =
declRefExpr->getDecl()->getName().getBaseIdentifier().str().str();
return std::make_shared<EnumValue>(caseName, std::nullopt);
}
break;
}
case ExprKind::Erasure: {
auto erasureExpr = cast<ErasureExpr>(expr);
return extractCompileTimeValue(erasureExpr->getSubExpr(), declContext);
}
case ExprKind::Paren: {
auto parenExpr = cast<ParenExpr>(expr);
return extractCompileTimeValue(parenExpr->getSubExpr(), declContext);
}
case ExprKind::PropertyWrapperValuePlaceholder: {
auto placeholderExpr = cast<PropertyWrapperValuePlaceholderExpr>(expr);
return extractCompileTimeValue(placeholderExpr->getOriginalWrappedValue(),
declContext);
}
case ExprKind::Coerce: {
auto coerceExpr = cast<CoerceExpr>(expr);
return extractCompileTimeValue(coerceExpr->getSubExpr(), declContext);
}
case ExprKind::DotSelf: {
auto dotSelfExpr = cast<DotSelfExpr>(expr);
auto dotSelfMetaType = dotSelfExpr->getType()->getAs<AnyMetatypeType>();
if (dotSelfMetaType)
return std::make_shared<TypeValue>(dotSelfMetaType->getInstanceType());
else
break;
}
case ExprKind::UnderlyingToOpaque: {
auto underlyingToOpaque = cast<UnderlyingToOpaqueExpr>(expr);
return extractCompileTimeValue(underlyingToOpaque->getSubExpr(),
declContext);
}
case ExprKind::DefaultArgument: {
auto defaultArgExpr = cast<DefaultArgumentExpr>(expr);
auto *decl = defaultArgExpr->getParamDecl();
// If there is a default expr, we should have looked through to it
assert(!decl->hasDefaultExpr());
switch (decl->getDefaultArgumentKind()) {
case DefaultArgumentKind::NilLiteral:
return std::make_shared<RawLiteralValue>("nil");
case DefaultArgumentKind::EmptyArray:
return std::make_shared<ArrayValue>(
std::vector<std::shared_ptr<CompileTimeValue>>());
case DefaultArgumentKind::EmptyDictionary:
return std::make_shared<DictionaryValue>(
std::vector<std::shared_ptr<TupleValue>>());
default:
break;
}
} break;
case ExprKind::KeyPath: {
auto keyPathExpr = cast<KeyPathExpr>(expr);
auto rootType = keyPathExpr->getRootType();
std::vector<KeyPathValue::Component> components;
for (auto component: keyPathExpr->getComponents()) {
if (component.isResolved()) {
auto declRef = component.getDeclRef();
auto identifier = declRef.getDecl()->getBaseIdentifier().str();
auto type = component.getComponentType()->getRValueType();
components.push_back({identifier.str(), type});
}
}
std::string path = "";
auto numberOfComponents = static_cast<int>(components.size());
for (int i = 0; i < numberOfComponents; i++) {
if (i != 0) {
path += ".";
}
path += components[i].Label;
}
return std::make_shared<KeyPathValue>(path, rootType, components);
}
case ExprKind::InjectIntoOptional: {
auto injectIntoOptionalExpr = cast<InjectIntoOptionalExpr>(expr);
return extractCompileTimeValue(injectIntoOptionalExpr->getSubExpr(),
declContext);
}
case ExprKind::Load: {
auto loadExpr = cast<LoadExpr>(expr);
return extractCompileTimeValue(loadExpr->getSubExpr(), declContext);
}
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, declContext));
});
return std::make_shared<InterpolatedStringLiteralValue>(segments);
}
case ExprKind::Closure: {
auto closureExpr = cast<ClosureExpr>(expr);
auto body = closureExpr->getBody();
auto resultBuilderMembers =
getResultBuilderMembersFromBraceStmt(body, declContext);
if (!resultBuilderMembers.empty()) {
return std::make_shared<BuilderValue>(resultBuilderMembers);
}
break;
}
case ExprKind::DerivedToBase: {
auto derivedExpr = cast<DerivedToBaseExpr>(expr);
return extractCompileTimeValue(derivedExpr->getSubExpr(), declContext);
}
default: {
break;
}
}
}
return std::make_shared<RuntimeValue>();
}
static CustomAttrValue extractAttributeValue(const CustomAttr *attr,
const DeclContext *declContext) {
std::vector<FunctionParameter> parameters;
if (const auto *args = attr->getArgs()) {
for (auto arg : *args) {
const auto label = arg.getLabel().str().str();
auto argExpr = arg.getExpr();
if (auto defaultArgument = dyn_cast<DefaultArgumentExpr>(argExpr)) {
auto *decl = defaultArgument->getParamDecl();
if (decl->hasDefaultExpr()) {
argExpr = decl->getTypeCheckedDefaultExpr();
}
}
parameters.push_back({label, argExpr->getType(),
extractCompileTimeValue(argExpr, declContext)});
}
}
return {attr, parameters};
}
static AttrValueVector
extractPropertyWrapperAttrValues(VarDecl *propertyDecl) {
AttrValueVector customAttrValues;
for (auto *propertyWrapper : propertyDecl->getAttachedPropertyWrappers())
customAttrValues.push_back(
extractAttributeValue(propertyWrapper, propertyDecl->getDeclContext()));
return customAttrValues;
}
static ConstValueTypePropertyInfo
extractTypePropertyInfo(VarDecl *propertyDecl) {
std::optional<AttrValueVector> propertyWrapperValues;
if (propertyDecl->hasAttachedPropertyWrapper())
propertyWrapperValues = extractPropertyWrapperAttrValues(propertyDecl);
if (const auto binding = propertyDecl->getParentPatternBinding()) {
if (const auto originalInit = binding->getInit(0)) {
return {propertyDecl,
extractCompileTimeValue(originalInit,
propertyDecl->getInnermostDeclContext()),
propertyWrapperValues};
}
}
if (auto accessorDecl = propertyDecl->getAccessor(AccessorKind::Get)) {
if (auto body = accessorDecl->getTypecheckedBody()) {
auto node = body->getFirstElement();
if (auto *stmt = node.dyn_cast<Stmt *>()) {
if (stmt->getKind() == StmtKind::Return) {
return {
propertyDecl,
extractCompileTimeValue(cast<ReturnStmt>(stmt)->getResult(),
accessorDecl->getInnermostDeclContext()),
propertyWrapperValues};
}
}
}
}
return {propertyDecl, std::make_shared<RuntimeValue>()};
}
std::optional<std::vector<EnumElementDeclValue>>
extractEnumCases(NominalTypeDecl *Decl) {
if (Decl->getKind() == DeclKind::Enum) {
std::vector<EnumElementDeclValue> Elements;
for (EnumCaseDecl *ECD : cast<EnumDecl>(Decl)->getAllCases()) {
for (EnumElementDecl *EED : ECD->getElements()) {
std::string Name = EED->getNameStr().str();
std::optional<std::string> RawValue =
extractRawLiteral(EED->getRawValueExpr());
std::vector<EnumElementParameterValue> Parameters;
if (const ParameterList *Params = EED->getParameterList()) {
for (const ParamDecl *Parameter : Params->getArray()) {
std::optional<std::string> Label =
Parameter->getParameterName().empty()
? std::nullopt
: std::optional<std::string>(
Parameter->getParameterName().str().str());
Parameters.push_back({Label, Parameter->getInterfaceType()});
}
}
if (Parameters.empty()) {
Elements.push_back({Name, RawValue, std::nullopt});
} else {
Elements.push_back({Name, RawValue, Parameters});
}
}
}
return Elements;
}
return std::nullopt;
}
ConstValueTypeInfo ConstantValueInfoRequest::evaluate(
Evaluator &Evaluator, NominalTypeDecl *Decl,
llvm::PointerUnion<const SourceFile *, ModuleDecl *> extractionScope)
const {
auto shouldExtract = [&](DeclContext *decl) {
if (auto SF = extractionScope.dyn_cast<const SourceFile *>())
return decl->getOutermostParentSourceFile() == SF;
return decl->getParentModule() == extractionScope.get<ModuleDecl *>();
};
std::vector<ConstValueTypePropertyInfo> Properties;
std::optional<std::vector<EnumElementDeclValue>> EnumCases;
// Use 'getStoredProperties' to get lowered lazy and wrapped properties.
// @_objcImplementation extensions might contain stored properties.
auto StoredProperties = Decl->getStoredProperties();
std::unordered_set<VarDecl *> StoredPropertiesSet(StoredProperties.begin(),
StoredProperties.end());
for (auto Property : StoredProperties) {
if (shouldExtract(Property->getDeclContext())) {
Properties.push_back(extractTypePropertyInfo(Property));
}
}
auto extract = [&](class Decl *Member) {
// Ignore plain stored properties collected above,
// instead gather up remaining static and computed properties.
if (auto *VD = dyn_cast<VarDecl>(Member))
if (!StoredPropertiesSet.count(VD))
Properties.push_back(extractTypePropertyInfo(VD));
};
if (shouldExtract(Decl)) {
for (auto Member : Decl->getAllMembers()) {
extract(Member);
}
EnumCases = extractEnumCases(Decl);
}
for (auto Extension: Decl->getExtensions()) {
if (shouldExtract(Extension)) {
for (auto Member : Extension->getAllMembers()) {
extract(Member);
}
}
}
return ConstValueTypeInfo{Decl, Properties, EnumCases};
}
std::vector<ConstValueTypeInfo>
gatherConstValuesForModule(const std::unordered_set<std::string> &Protocols,
ModuleDecl *Module) {
std::vector<ConstValueTypeInfo> Result;
std::vector<NominalTypeDecl *> ConformanceDecls;
NominalTypeConformanceCollector ConformanceCollector(Protocols,
ConformanceDecls);
Module->walk(ConformanceCollector);
// Visit macro expanded extensions
for (auto *FU : Module->getFiles())
if (auto *synthesizedSF = FU->getSynthesizedFile())
for (auto D : synthesizedSF->getTopLevelDecls())
if (isa<ExtensionDecl>(D))
D->walk(ConformanceCollector);
for (auto *CD : ConformanceDecls)
Result.emplace_back(evaluateOrDefault(CD->getASTContext().evaluator,
ConstantValueInfoRequest{CD, Module},
{}));
return Result;
}
std::vector<ConstValueTypeInfo>
gatherConstValuesForPrimary(const std::unordered_set<std::string> &Protocols,
const SourceFile *SF) {
std::vector<ConstValueTypeInfo> Result;
std::vector<NominalTypeDecl *> ConformanceDecls;
NominalTypeConformanceCollector ConformanceCollector(Protocols,
ConformanceDecls);
for (auto D : SF->getTopLevelDecls())
D->walk(ConformanceCollector);
// Visit macro expanded extensions
if (auto *synthesizedSF = SF->getSynthesizedFile())
for (auto D : synthesizedSF->getTopLevelDecls())
if (isa<ExtensionDecl>(D))
D->walk(ConformanceCollector);
for (auto *CD : ConformanceDecls)
Result.emplace_back(evaluateOrDefault(
CD->getASTContext().evaluator, ConstantValueInfoRequest{CD, SF}, {}));
return Result;
}
void writeLocationInformation(llvm::json::OStream &JSON, SourceLoc Loc,
const ASTContext &ctx) {
if (Loc.isInvalid())
return;
JSON.attribute("file", ctx.SourceMgr.getDisplayNameForLoc(Loc));
JSON.attribute("line",
ctx.SourceMgr.getPresumedLineAndColumnForLoc(Loc).first);
}
// Take BuilderValue, which is a representation of a result builder
// and write the values
void writeBuilderValue(llvm::json::OStream &JSON, BuilderValue *Value);
void writeValue(llvm::json::OStream &JSON,
std::shared_ptr<CompileTimeValue> Value) {
auto value = Value.get();
switch (value->getKind()) {
case CompileTimeValue::ValueKind::RawLiteral: {
JSON.attribute("valueKind", "RawLiteral");
JSON.attribute("value", cast<RawLiteralValue>(value)->getValue());
break;
}
case CompileTimeValue::ValueKind::NilLiteral: {
JSON.attribute("valueKind", "NilLiteral");
break;
}
case CompileTimeValue::ValueKind::InitCall: {
auto initCallValue = cast<InitCallValue>(value);
JSON.attribute("valueKind", "InitCall");
JSON.attributeObject("value", [&]() {
JSON.attribute("type",
toFullyQualifiedTypeNameString(initCallValue->getType()));
JSON.attributeArray("arguments", [&] {
for (auto FP : initCallValue->getParameters()) {
JSON.object([&] {
JSON.attribute("label", FP.Label);
JSON.attribute("type", toFullyQualifiedTypeNameString(FP.Type));
writeValue(JSON, FP.Value);
});
}
});
});
break;
}
case CompileTimeValue::ValueKind::Tuple: {
auto tupleValue = cast<TupleValue>(value);
JSON.attribute("valueKind", "Tuple");
JSON.attributeArray("value", [&] {
for (auto TV : tupleValue->getElements()) {
JSON.object([&] {
if (auto Label = TV.Label) {
JSON.attribute("label", Label);
}
JSON.attribute("type", toFullyQualifiedTypeNameString(TV.Type));
writeValue(JSON, TV.Value);
});
}
});
break;
}
case CompileTimeValue::ValueKind::Builder: {
auto builderValue = cast<BuilderValue>(value);
writeBuilderValue(JSON, builderValue);
break;
}
case CompileTimeValue::ValueKind::Dictionary: {
JSON.attribute("valueKind", "Dictionary");
JSON.attributeArray("value", [&] {
for (auto tupleValue : cast<DictionaryValue>(value)->getElements()) {
auto tupleElements = tupleValue.get()->getElements();
JSON.object([&] {
JSON.attributeObject(
"key", [&] { writeValue(JSON, tupleElements[0].Value); });
JSON.attributeObject(
"value", [&] { writeValue(JSON, tupleElements[1].Value); });
});
}
});
break;
}
case CompileTimeValue::ValueKind::Array: {
auto arrayValue = cast<ArrayValue>(value);
JSON.attribute("valueKind", "Array");
JSON.attributeArray("value", [&] {
for (auto CTP : arrayValue->getElements()) {
JSON.object([&] { writeValue(JSON, CTP); });
}
});
break;
}
case CompileTimeValue::ValueKind::Enum: {
auto enumValue = cast<EnumValue>(value);
JSON.attribute("valueKind", "Enum");
JSON.attributeObject("value", [&]() {
JSON.attribute("name", enumValue->getIdentifier());
if (enumValue->getParameters().has_value()) {
auto params = enumValue->getParameters().value();
JSON.attributeArray("arguments", [&] {
for (auto FP : params) {
JSON.object([&] {
JSON.attribute("label", FP.Label);
JSON.attribute("type", toFullyQualifiedTypeNameString(FP.Type));
writeValue(JSON, FP.Value);
});
}
});
}
});
break;
}
case CompileTimeValue::ValueKind::Type: {
auto typeValue = cast<TypeValue>(value);
Type type = typeValue->getType();
JSON.attribute("valueKind", "Type");
JSON.attributeObject("value", [&]() {
JSON.attribute("type",
toFullyQualifiedTypeNameString(type));
JSON.attribute("mangledName",
toMangledTypeNameString(type));
});
break;
}
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", FP.Label);
JSON.attribute("type", toFullyQualifiedTypeNameString(FP.Type));
writeValue(JSON, FP.Value);
});
}
});
}
});
break;
}
case CompileTimeValue::ValueKind::StaticFunctionCall: {
auto staticFunctionCallValue = cast<StaticFunctionCallValue>(value);
JSON.attribute("valueKind", "StaticFunctionCall");
JSON.attributeObject("value", [&]() {
JSON.attribute("type", toFullyQualifiedTypeNameString(
staticFunctionCallValue->getType()));
JSON.attribute("memberLabel", staticFunctionCallValue->getLabel());
JSON.attributeArray("arguments", [&] {
for (auto FP : staticFunctionCallValue->getParameters()) {
JSON.object([&] {
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;
}
case CompileTimeValue::ValueKind::Runtime: {
JSON.attribute("valueKind", "Runtime");
break;
}
}
}
void writeAttributeInfo(llvm::json::OStream &JSON,
const CustomAttrValue &AttrVal,
const ASTContext &ctx) {
JSON.object([&] {
JSON.attribute("type",
toFullyQualifiedTypeNameString(AttrVal.Attr->getType()));
writeLocationInformation(JSON, AttrVal.Attr->getLocation(), ctx);
JSON.attributeArray("arguments", [&] {
for (auto FP : AttrVal.Parameters) {
JSON.object([&] {
JSON.attribute("label", FP.Label);
JSON.attribute("type", toFullyQualifiedTypeNameString(FP.Type));
writeValue(JSON, FP.Value);
});
}
});
});
}
void writePropertyWrapperAttributes(
llvm::json::OStream &JSON, std::optional<AttrValueVector> PropertyWrappers,
const ASTContext &ctx) {
if (!PropertyWrappers.has_value()) {
return;
}
JSON.attributeArray("propertyWrappers", [&] {
for (auto PW : PropertyWrappers.value())
writeAttributeInfo(JSON, PW, ctx);
});
}
void writeEnumCases(
llvm::json::OStream &JSON,
std::optional<std::vector<EnumElementDeclValue>> EnumElements) {
if (!EnumElements.has_value()) {
return;
}
JSON.attributeArray("cases", [&] {
for (const auto &Case : EnumElements.value()) {
JSON.object([&] {
JSON.attribute("name", Case.Name);
if (Case.RawValue.has_value()) {
JSON.attribute("rawValue", Case.RawValue.value());
}
if (Case.Parameters.has_value()) {
JSON.attributeArray("parameters", [&] {
for (const auto &Parameter : Case.Parameters.value()) {
JSON.object([&] {
if (auto Label = Parameter.Label) {
JSON.attribute("label", Label);
}
JSON.attribute("type",
toFullyQualifiedTypeNameString(Parameter.Type));
});
}
});
}
});
}
});
}
std::optional<std::shared_ptr<CompileTimeValue>>
getResultBuilderElementFromASTNode(const ASTNode node) {
if (auto *D = node.dyn_cast<Decl *>()) {
if (auto *patternBinding = dyn_cast<PatternBindingDecl>(D)) {
if (auto originalInit = patternBinding->getOriginalInit(0)) {
return extractCompileTimeValue(
originalInit, patternBinding->getInnermostDeclContext());
}
}
}
return std::nullopt;
}
BuilderValue::ConditionalMember
getConditionalMemberFromIfStmt(const IfStmt *ifStmt,
const DeclContext *declContext) {
std::vector<BuilderValue::ConditionalMember::AvailabilitySpec>
AvailabilitySpecs;
std::vector<std::shared_ptr<BuilderValue::BuilderMember>> IfElements;
std::vector<std::shared_ptr<BuilderValue::BuilderMember>> ElseElements;
if (auto thenBraceStmt = ifStmt->getThenStmt()) {
for (auto elem : thenBraceStmt->getElements()) {
if (auto memberElement = getResultBuilderElementFromASTNode(elem)) {
IfElements.push_back(std::make_shared<BuilderValue::SingleMember>(
memberElement.value()));
}
}
}
if (auto elseStmt = ifStmt->getElseStmt()) {
if (auto *elseIfStmt = dyn_cast<IfStmt>(elseStmt)) {
ElseElements.push_back(std::make_shared<BuilderValue::ConditionalMember>(
getConditionalMemberFromIfStmt(elseIfStmt, declContext)));
} else if (auto *elseBraceStmt = dyn_cast<BraceStmt>(elseStmt)) {
for (auto elem : elseBraceStmt->getElements()) {
if (auto memberElement = getResultBuilderElementFromASTNode(elem)) {
ElseElements.push_back(std::make_shared<BuilderValue::SingleMember>(
memberElement.value()));
}
}
}
}
BuilderValue::MemberKind memberKind = BuilderValue::Either;
if (ElseElements.size() == 0) {
memberKind = BuilderValue::Optional;
}
for (auto elt : ifStmt->getCond()) {
if (elt.getKind() == StmtConditionElement::CK_Availability) {
for (auto spec :
elt.getAvailability()->getSemanticAvailabilitySpecs(declContext)) {
if (spec.getDomain().isPlatform()) {
AvailabilitySpecs.push_back(
BuilderValue::ConditionalMember::AvailabilitySpec(
spec.getDomain(), spec.getVersion()));
}
}
memberKind = BuilderValue::LimitedAvailability;
break;
}
}
if (AvailabilitySpecs.empty()) {
return BuilderValue::ConditionalMember(memberKind, IfElements,
ElseElements);
}
return BuilderValue::ConditionalMember(memberKind, AvailabilitySpecs,
IfElements, ElseElements);
}
BuilderValue::ArrayMember
getBuildArrayMemberFromForEachStmt(const ForEachStmt *forEachStmt) {
std::vector<std::shared_ptr<BuilderValue::BuilderMember>> MemberElements;
if (auto braceStmt = forEachStmt->getBody()) {
for (auto elem : braceStmt->getElements()) {
if (auto memberElement = getResultBuilderElementFromASTNode(elem)) {
MemberElements.push_back(std::make_shared<BuilderValue::SingleMember>(
memberElement.value()));
}
}
}
return BuilderValue::ArrayMember(MemberElements);
}
std::vector<std::shared_ptr<BuilderValue::BuilderMember>>
getResultBuilderMembersFromBraceStmt(BraceStmt *braceStmt,
const DeclContext *declContext) {
std::vector<std::shared_ptr<BuilderValue::BuilderMember>>
ResultBuilderMembers;
for (auto elem : braceStmt->getElements()) {
if (auto resultBuilderElement = getResultBuilderElementFromASTNode(elem)) {
ResultBuilderMembers.push_back(
std::make_shared<BuilderValue::SingleMember>(
resultBuilderElement.value()));
} else if (auto *stmt = elem.dyn_cast<Stmt *>()) {
if (auto *ifStmt = dyn_cast<IfStmt>(stmt)) {
ResultBuilderMembers.push_back(
std::make_shared<BuilderValue::ConditionalMember>(
getConditionalMemberFromIfStmt(ifStmt, declContext)));
} else if (auto *doStmt = dyn_cast<DoStmt>(stmt)) {
if (auto body = doStmt->getBody()) {
for (auto elem : body->getElements()) {
if (auto *stmt = elem.dyn_cast<Stmt *>()) {
if (auto *forEachStmt = dyn_cast<ForEachStmt>(stmt)) {
ResultBuilderMembers.push_back(
std::make_shared<BuilderValue::ArrayMember>(
getBuildArrayMemberFromForEachStmt(forEachStmt)));
}
}
}
}
}
}
}
return ResultBuilderMembers;
}
std::shared_ptr<BuilderValue>
createBuilderCompileTimeValue(CustomAttr *AttachedResultBuilder,
const swift::VarDecl *VarDecl) {
std::vector<std::shared_ptr<BuilderValue::BuilderMember>>
ResultBuilderMembers;
if (!VarDecl->getAllAccessors().empty()) {
if (auto accessor = VarDecl->getAllAccessors()[0]) {
if (auto braceStmt = accessor->getTypecheckedBody()) {
ResultBuilderMembers = getResultBuilderMembersFromBraceStmt(
braceStmt, accessor->getDeclContext());
}
}
}
return std::make_shared<BuilderValue>(AttachedResultBuilder,
ResultBuilderMembers);
}
void writeSingleBuilderMemberElement(
llvm::json::OStream &JSON, std::shared_ptr<CompileTimeValue> Element) {
switch (Element.get()->getKind()) {
case CompileTimeValue::ValueKind::StaticFunctionCall: {
auto staticFunctionCallValue = cast<StaticFunctionCallValue>(Element.get());
if (staticFunctionCallValue->getLabel() == "buildExpression") {
for (auto FP : staticFunctionCallValue->getParameters()) {
writeValue(JSON, FP.Value);
}
}
break;
}
default: {
writeValue(JSON, Element);
break;
}
}
}
void writeBuilderMember(
llvm::json::OStream &JSON,
std::shared_ptr<BuilderValue::BuilderMember> BuilderMember) {
auto Member = BuilderMember.get();
switch (Member->getKind()) {
case BuilderValue::Expression: {
auto member = cast<BuilderValue::SingleMember>(Member);
JSON.attributeObject("element", [&] {
writeSingleBuilderMemberElement(JSON, member->getElement());
});
break;
}
case BuilderValue::Array: {
auto member = cast<BuilderValue::ArrayMember>(Member);
JSON.attributeArray("elements", [&] {
for (auto elem : member->getElements()) {
JSON.object([&] { writeBuilderMember(JSON, elem); });
}
});
break;
}
default: {
auto member = cast<BuilderValue::ConditionalMember>(Member);
if (auto availabilitySpecs = member->getAvailabilitySpecs()) {
JSON.attributeArray("availabilityAttributes", [&] {
for (auto elem : *availabilitySpecs) {
JSON.object([&] {
JSON.attribute(
"platform",
platformString(elem.getDomain().getPlatformKind()).str());
JSON.attribute("minVersion", elem.getVersion().getAsString());
});
}
});
}
JSON.attributeArray("ifElements", [&] {
for (auto elem : member->getIfElements()) {
JSON.object([&] { writeBuilderMember(JSON, elem); });
}
});
JSON.attributeArray("elseElements", [&] {
for (auto elem : member->getElseElements()) {
JSON.object([&] { writeBuilderMember(JSON, elem); });
}
});
break;
}
}
}
void writeBuilderValue(llvm::json::OStream &JSON, BuilderValue *Value) {
JSON.attribute("valueKind", "Builder");
JSON.attributeObject("value", [&] {
if (auto resultBuilderType = Value->getResultBuilderType()) {
JSON.attribute("type", toFullyQualifiedTypeNameString(
resultBuilderType.value()->getType()));
} else {
JSON.attribute("type", "");
}
JSON.attributeArray("members", [&] {
for (auto member : Value->getMembers()) {
JSON.object([&] {
switch (member->getKind()) {
case BuilderValue::Expression:
JSON.attribute("kind", "buildExpression");
break;
case BuilderValue::Either:
JSON.attribute("kind", "buildEither");
break;
case BuilderValue::Optional:
JSON.attribute("kind", "buildOptional");
break;
case BuilderValue::LimitedAvailability:
JSON.attribute("kind", "buildLimitedAvailability");
break;
case BuilderValue::Array:
JSON.attribute("kind", "buildArray");
break;
case BuilderValue::Unknown:
JSON.attribute("kind", "Unknown");
break;
}
writeBuilderMember(JSON, member);
});
}
});
});
}
std::optional<std::shared_ptr<BuilderValue>>
extractBuilderValueIfExists(const swift::NominalTypeDecl *TypeDecl,
const swift::VarDecl *VarDecl) {
if (auto *attr = VarDecl->getAttachedResultBuilder()) {
return createBuilderCompileTimeValue(attr, VarDecl);
}
for (ProtocolDecl *Decl :
TypeDecl->getLocalProtocols(ConformanceLookupKind::All)) {
// FIXME(noncopyable_generics): Should these be included?
if (Decl->getInvertibleProtocolKind())
continue;
for (auto Member : Decl->getMembers()) {
if (auto *VD = dyn_cast<swift::VarDecl>(Member)) {
if (VD->getName() != VarDecl->getName())
continue;
if (auto *attr = VD->getAttachedResultBuilder()) {
return createBuilderCompileTimeValue(attr, VarDecl);
}
}
}
}
return std::nullopt;
;
}
void writeAvailabilityAttributes(llvm::json::OStream &JSON, const Decl &decl) {
auto attrs = decl.getSemanticAvailableAttrs();
if (attrs.empty())
return;
JSON.attributeArray("availabilityAttributes", [&] {
for (auto attr : attrs) {
JSON.object([&] {
auto domainName = attr.getDomain().getNameForAttributePrinting();
if (!domainName.empty())
JSON.attribute("platform", domainName);
if (!attr.getMessage().empty())
JSON.attribute("message", attr.getMessage());
if (!attr.getRename().empty())
JSON.attribute("rename", attr.getRename());
if (attr.getIntroduced().has_value())
JSON.attribute("introducedVersion",
attr.getIntroduced().value().getAsString());
if (attr.getDeprecated().has_value())
JSON.attribute("deprecatedVersion",
attr.getDeprecated().value().getAsString());
if (attr.getObsoleted().has_value())
JSON.attribute("obsoletedVersion",
attr.getObsoleted().value().getAsString());
JSON.attribute("isUnavailable", attr.isUnconditionallyUnavailable());
JSON.attribute("isDeprecated", attr.isUnconditionallyDeprecated());
});
}
});
}
void writeSubstitutedOpaqueTypeAliasDetails(
llvm::json::OStream &JSON, const OpaqueTypeArchetypeType &OpaqueTy) {
auto Signature = OpaqueTy.getDecl()->getOpaqueInterfaceGenericSignature();
JSON.attributeArray("opaqueTypeProtocolRequirements", [&] {
for (const auto Requirement : Signature.getRequirements()) {
// Ignore requirements whose subject type is that of the owner decl
if (!Requirement.getFirstType()->isEqual(OpaqueTy.getInterfaceType()))
continue;
if (Requirement.getKind() != RequirementKind::Conformance)
continue;
// FIXME(noncopyable_generics): Should these be included?
if (Requirement.getProtocolDecl()->getInvertibleProtocolKind())
continue;
JSON.value(
toFullyQualifiedProtocolNameString(*Requirement.getProtocolDecl()));
}
});
JSON.attributeArray("opaqueTypeSameTypeRequirements", [&] {
for (const auto Requirement : Signature.getRequirements()) {
if (Requirement.getKind() == RequirementKind::SameType) {
auto TypeAliasType = Requirement.getFirstType();
auto TypeWitness = Requirement.getSecondType();
JSON.object([&] {
auto TypeAliasName = toFullyQualifiedTypeNameString(TypeAliasType);
if (auto DependentMemberTy =
TypeAliasType->getAs<DependentMemberType>())
if (const auto *Assoc = DependentMemberTy->getAssocType())
TypeAliasName =
toFullyQualifiedProtocolNameString(*Assoc->getProtocol()) +
"." + DependentMemberTy->getName().str().str();
JSON.attribute("typeAliasName", TypeAliasName);
JSON.attribute("substitutedTypeName",
toFullyQualifiedTypeNameString(TypeWitness));
JSON.attribute("substitutedMangledTypeName",
toMangledTypeNameString(TypeWitness));
});
}
}
});
}
void writeAssociatedTypeAliases(llvm::json::OStream &JSON,
const NominalTypeDecl &NomTypeDecl) {
JSON.attributeArray("associatedTypeAliases", [&] {
for (auto &Conformance : NomTypeDecl.getAllConformances()) {
Conformance->forEachTypeWitness(
[&](AssociatedTypeDecl *assoc, Type type, TypeDecl *typeDecl) {
JSON.object([&] {
JSON.attribute("typeAliasName", assoc->getName().str().str());
JSON.attribute("substitutedTypeName",
toFullyQualifiedTypeNameString(type));
JSON.attribute("substitutedMangledTypeName",
toMangledTypeNameString(type));
if (auto OpaqueTy = dyn_cast<OpaqueTypeArchetypeType>(type)) {
writeSubstitutedOpaqueTypeAliasDetails(JSON, *OpaqueTy);
}
});
return false;
});
}
});
}
void writeProperties(llvm::json::OStream &JSON,
const ConstValueTypeInfo &TypeInfo,
const NominalTypeDecl &NomTypeDecl) {
JSON.attributeArray("properties", [&] {
for (const auto &PropertyInfo : TypeInfo.Properties) {
JSON.object([&] {
const auto *decl = PropertyInfo.VarDecl;
std::shared_ptr<CompileTimeValue> value = PropertyInfo.Value;
JSON.attribute("label", decl->getName().str().str());
JSON.attribute("type", toFullyQualifiedTypeNameString(
decl->getInterfaceType()));
JSON.attribute("mangledTypeName", "n/a - deprecated");
JSON.attribute("isStatic", decl->isStatic() ? "true" : "false");
JSON.attribute("isComputed", !decl->hasStorage() ? "true" : "false");
writeLocationInformation(JSON, decl->getLoc(),
decl->getDeclContext()->getASTContext());
if (value.get()->getKind() == CompileTimeValue::ValueKind::Runtime) {
// Extract result builder information only if the variable has not
// used a different kind of initializer
if (auto builderValue =
extractBuilderValueIfExists(&NomTypeDecl, decl)) {
value = builderValue.value();
}
}
writeValue(JSON, value);
writePropertyWrapperAttributes(JSON, PropertyInfo.PropertyWrappers,
decl->getASTContext());
writeAvailabilityAttributes(JSON, *decl);
});
}
});
}
void writeConformances(llvm::json::OStream &JSON,
const NominalTypeDecl &NomTypeDecl) {
JSON.attributeArray("conformances", [&] {
for (auto *Conformance : NomTypeDecl.getAllConformances()) {
auto Proto = Conformance->getProtocol();
// FIXME(noncopyable_generics): Should these be included?
if (Proto->getInvertibleProtocolKind())
continue;
JSON.value(toFullyQualifiedProtocolNameString(*Proto));
}
});
}
void writeAllConformances(llvm::json::OStream &JSON,
const NominalTypeDecl &NomTypeDecl) {
JSON.attributeArray("allConformances", [&] {
for (auto *Conformance : NomTypeDecl.getAllConformances()) {
auto Proto = Conformance->getProtocol();
// FIXME(noncopyable_generics): Should these be included?
if (Proto->getInvertibleProtocolKind())
continue;
JSON.object([&] {
JSON.attribute("protocolName",
toFullyQualifiedProtocolNameString(*Proto));
JSON.attribute(
"conformanceDefiningModule",
Conformance->getDeclContext()->getParentModule()->getName().str());
});
}
});
}
void writeTypeName(llvm::json::OStream &JSON, const TypeDecl &TypeDecl) {
JSON.attribute("typeName",
toFullyQualifiedTypeNameString(
TypeDecl.getDeclaredInterfaceType()));
JSON.attribute("mangledTypeName",
toMangledTypeNameString(TypeDecl.getDeclaredInterfaceType()));
}
void writeNominalTypeKind(llvm::json::OStream &JSON,
const NominalTypeDecl &NomTypeDecl) {
JSON.attribute(
"kind",
NomTypeDecl.getDescriptiveKindName(NomTypeDecl.getDescriptiveKind())
.str());
}
bool writeAsJSONToFile(const std::vector<ConstValueTypeInfo> &ConstValueInfos,
llvm::raw_ostream &OS) {
llvm::json::OStream JSON(OS, 2);
JSON.array([&] {
for (const auto &TypeInfo : ConstValueInfos) {
assert(isa<NominalTypeDecl>(TypeInfo.TypeDecl) &&
"Expected Nominal Type Decl for a conformance");
const auto *NomTypeDecl = cast<NominalTypeDecl>(TypeInfo.TypeDecl);
const auto SourceLoc =
extractNearestSourceLoc(NomTypeDecl->getInnermostDeclContext());
const auto &Ctx = NomTypeDecl->getInnermostDeclContext()->getASTContext();
JSON.object([&] {
writeTypeName(JSON, *NomTypeDecl);
writeNominalTypeKind(JSON, *NomTypeDecl);
writeLocationInformation(JSON, SourceLoc, Ctx);
writeConformances(JSON, *NomTypeDecl);
// "conformances" will be removed once all clients move to
// "allConformances"
writeAllConformances(JSON, *NomTypeDecl);
writeAssociatedTypeAliases(JSON, *NomTypeDecl);
writeProperties(JSON, TypeInfo, *NomTypeDecl);
writeEnumCases(JSON, TypeInfo.EnumElements);
writeAvailabilityAttributes(JSON, *NomTypeDecl);
});
}
});
JSON.flush();
return false;
}
} // namespace swift
#define SWIFT_TYPEID_ZONE ConstExtract
#define SWIFT_TYPEID_HEADER "swift/ConstExtract/ConstExtractTypeIDZone.def"
#include "swift/Basic/ImplementTypeIDZone.h"
#undef SWIFT_TYPEID_ZONE
#undef SWIFT_TYPEID_HEADER
// Define request evaluation functions for each of the name lookup requests.
static AbstractRequestFunction *constExtractRequestFunctions[] = {
#define SWIFT_REQUEST(Zone, Name, Sig, Caching, LocOptions) \
reinterpret_cast<AbstractRequestFunction *>(&Name::evaluateRequest),
#include "swift/ConstExtract/ConstExtractTypeIDZone.def"
#undef SWIFT_REQUEST
};
void swift::registerConstExtractRequestFunctions(Evaluator &evaluator) {
evaluator.registerRequestFunctions(Zone::ConstExtract,
constExtractRequestFunctions);
}