blob: 03eda9fc3c64f78bb3bf70f13da043e9e7dcf150 [file] [log] [blame]
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "compiler.h"
#include "parser.h" // only for generateChildId()
#include <kj/mutex.h>
#include <kj/arena.h>
#include <kj/vector.h>
#include <kj/debug.h>
#include <capnp/message.h>
#include <map>
#include <set>
#include <unordered_map>
#include "node-translator.h"
namespace capnp {
namespace compiler {
typedef std::unordered_map<uint64_t, Orphan<schema::Node::SourceInfo::Reader>> SourceInfoMap;
class Compiler::Alias {
public:
Alias(CompiledModule& module, Node& parent, const Expression::Reader& targetName)
: module(module), parent(parent), targetName(targetName) {}
kj::Maybe<Resolver::ResolveResult> compile();
private:
CompiledModule& module;
Node& parent;
Expression::Reader targetName;
kj::Maybe<Resolver::ResolveResult> target;
Orphan<schema::Brand> brandOrphan;
bool initialized = false;
};
class Compiler::Node final: public Resolver {
// Passes through four states:
// - Stub: On initial construction, the Node is just a placeholder object. Its ID has been
// determined, and it is placed in its parent's member table as well as the compiler's
// nodes-by-ID table.
// - Expanded: Nodes have been constructed for all of this Node's nested children. This happens
// the first time a lookup is performed for one of those children.
// - Bootstrap: A NodeTranslator has been built and advanced to the bootstrap phase.
// - Finished: A final Schema object has been constructed.
public:
explicit Node(CompiledModule& module);
// Create a root node representing the given file. May
Node(Node& parent, const Declaration::Reader& declaration);
// Create a child node.
Node(kj::StringPtr name, Declaration::Which kind,
List<Declaration::BrandParameter>::Reader genericParams);
// Create a dummy node representing a built-in declaration, like "Int32" or "true".
uint64_t getId() { return id; }
uint getParameterCount() { return genericParamCount; }
Declaration::Which getKind() { return kind; }
kj::Maybe<Schema> getBootstrapSchema();
kj::Maybe<schema::Node::Reader> getFinalSchema();
void loadFinalSchema(const SchemaLoader& loader);
void traverse(uint eagerness, std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo);
// Get the final schema for this node, and also possibly traverse the node's children and
// dependencies to ensure that they are loaded, depending on the mode.
void addError(kj::StringPtr error);
// Report an error on this Node.
// implements Resolver ---------------------------------------------
kj::Maybe<ResolveResult> resolve(kj::StringPtr name) override;
kj::Maybe<ResolveResult> resolveMember(kj::StringPtr name) override;
ResolvedDecl resolveBuiltin(Declaration::Which which) override;
ResolvedDecl resolveId(uint64_t id) override;
kj::Maybe<ResolvedDecl> getParent() override;
ResolvedDecl getTopScope() override;
kj::Maybe<Schema> resolveBootstrapSchema(
uint64_t id, schema::Brand::Reader brand) override;
kj::Maybe<schema::Node::Reader> resolveFinalSchema(uint64_t id) override;
kj::Maybe<ResolvedDecl> resolveImport(kj::StringPtr name) override;
kj::Maybe<kj::Array<const byte>> readEmbed(kj::StringPtr name) override;
kj::Maybe<Type> resolveBootstrapType(schema::Type::Reader type, Schema scope) override;
private:
CompiledModule* module; // null iff isBuiltin is true
kj::Maybe<Node&> parent;
Declaration::Reader declaration;
// AST of the declaration parsed from the schema file. May become invalid once the content
// state has reached FINISHED.
uint64_t id;
// The ID of this node, either taken from the AST or computed based on the parent. Or, a dummy
// value, if duplicates were detected.
kj::StringPtr displayName;
// Fully-qualified display name for this node. For files, this is just the file name, otherwise
// it is "filename:Path.To.Decl".
Declaration::Which kind;
// Kind of node.
uint genericParamCount;
// Number of generic parameters.
bool isBuiltin;
// Whether this is a bulit-in declaration, like "Int32" or "true".
uint32_t startByte;
uint32_t endByte;
// Start and end byte for reporting general errors.
struct Content {
inline Content(): state(STUB) {}
enum State {
STUB,
EXPANDED,
BOOTSTRAP,
FINISHED
};
State state;
// Indicates which fields below are valid.
inline bool stateHasReached(State minimumState) {
return state >= minimumState;
}
inline void advanceState(State newState) {
state = newState;
}
// EXPANDED ------------------------------------
typedef std::multimap<kj::StringPtr, kj::Own<Node>> NestedNodesMap;
NestedNodesMap nestedNodes;
kj::Vector<Node*> orderedNestedNodes;
// multimap in case of duplicate member names -- we still want to compile them, even if it's an
// error.
typedef std::multimap<kj::StringPtr, kj::Own<Alias>> AliasMap;
AliasMap aliases;
// The "using" declarations. These are just links to nodes elsewhere.
// BOOTSTRAP -----------------------------------
NodeTranslator* translator;
// Node translator, allocated in the bootstrap arena.
kj::Maybe<Schema> bootstrapSchema;
// The schema built in the bootstrap loader. Null if the bootstrap loader threw an exception.
// FINISHED ------------------------------------
kj::Maybe<schema::Node::Reader> finalSchema;
// The completed schema, ready to load into the real schema loader.
kj::Array<schema::Node::Reader> auxSchemas;
// Schemas for all auxiliary nodes built by the NodeTranslator.
kj::Array<schema::Node::SourceInfo::Reader> sourceInfo;
// All source info structs as built by the NodeTranslator.
};
Content guardedContent; // Read using getContent() only!
bool inGetContent = false; // True while getContent() is running; detects cycles.
kj::Maybe<schema::Node::Reader> loadedFinalSchema;
// Copy of `finalSchema` as loaded into the final schema loader. This doesn't go away if the
// workspace is destroyed.
// ---------------------------------------------
static uint64_t generateId(uint64_t parentId, kj::StringPtr declName,
Declaration::Id::Reader declId);
// Extract the ID from the declaration, or if it has none, generate one based on the name and
// parent ID.
static kj::StringPtr joinDisplayName(kj::Arena& arena, Node& parent, kj::StringPtr declName);
// Join the parent's display name with the child's unqualified name to construct the child's
// display name.
kj::Maybe<Content&> getContent(Content::State minimumState);
// Advances the content to at least the given state and returns it. Returns null if getContent()
// is being called recursively and the given state has not yet been reached, as this indicates
// that the declaration recursively depends on itself.
void traverseNodeDependencies(const schema::Node::Reader& schemaNode, uint eagerness,
std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo);
void traverseType(const schema::Type::Reader& type, uint eagerness,
std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo);
void traverseBrand(const schema::Brand::Reader& brand, uint eagerness,
std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo);
void traverseAnnotations(const List<schema::Annotation>::Reader& annotations, uint eagerness,
std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo);
void traverseDependency(uint64_t depId, uint eagerness,
std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo,
bool ignoreIfNotFound = false);
// Helpers for traverse().
};
class Compiler::CompiledModule {
public:
CompiledModule(Compiler::Impl& compiler, Module& parserModule);
Compiler::Impl& getCompiler() { return compiler; }
ErrorReporter& getErrorReporter() { return parserModule; }
ParsedFile::Reader getParsedFile() { return content.getReader(); }
Node& getRootNode() { return rootNode; }
kj::StringPtr getSourceName() { return parserModule.getSourceName(); }
kj::Maybe<CompiledModule&> importRelative(kj::StringPtr importPath);
kj::Maybe<kj::Array<const byte>> embedRelative(kj::StringPtr importPath);
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
getFileImportTable(Orphanage orphanage);
private:
Compiler::Impl& compiler;
Module& parserModule;
MallocMessageBuilder contentArena;
Orphan<ParsedFile> content;
Node rootNode;
};
class Compiler::Impl: public SchemaLoader::LazyLoadCallback {
public:
explicit Impl(AnnotationFlag annotationFlag);
virtual ~Impl() noexcept(false);
uint64_t add(Module& module);
kj::Maybe<uint64_t> lookup(uint64_t parent, kj::StringPtr childName);
kj::Maybe<schema::Node::SourceInfo::Reader> getSourceInfo(uint64_t id);
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
getFileImportTable(Module& module, Orphanage orphanage);
Orphan<List<schema::Node::SourceInfo>> getAllSourceInfo(Orphanage orphanage);
void eagerlyCompile(uint64_t id, uint eagerness, const SchemaLoader& loader);
CompiledModule& addInternal(Module& parsedModule);
struct Workspace {
// Scratch space where stuff can be allocated while working. The Workspace is available
// whenever nodes are actively being compiled, then is destroyed once control exits the
// compiler. Note that since nodes are compiled lazily, a new Workspace may have to be
// constructed in order to compile more nodes later.
MallocMessageBuilder message;
Orphanage orphanage;
// Orphanage for allocating temporary Cap'n Proto objects.
kj::Arena arena;
// Arena for allocating temporary native objects. Note that objects in `arena` may contain
// pointers into `message` that will be manipulated on destruction, so `arena` must be declared
// after `message`.
SchemaLoader bootstrapLoader;
// Loader used to load bootstrap schemas. The bootstrap schema nodes are similar to the final
// versions except that any value expressions which depend on knowledge of other types (e.g.
// default values for struct fields) are left unevaluated (the values in the schema are empty).
// These bootstrap schemas can then be plugged into the dynamic API and used to evaluate these
// remaining values.
inline explicit Workspace(const SchemaLoader::LazyLoadCallback& loaderCallback)
: orphanage(message.getOrphanage()),
bootstrapLoader(loaderCallback) {}
};
kj::Arena& getNodeArena() { return nodeArena; }
// Arena where nodes and other permanent objects should be allocated.
Workspace& getWorkspace() { return workspace; }
// Temporary workspace that can be used to construct bootstrap objects.
inline bool shouldCompileAnnotations() {
return annotationFlag == AnnotationFlag::COMPILE_ANNOTATIONS;
}
void clearWorkspace();
// Reset the temporary workspace.
uint64_t addNode(uint64_t desiredId, Node& node);
// Add the given node to the by-ID map under the given ID. If another node with the same ID
// already exists, choose a new one arbitrarily and use that instead. Return the ID that was
// finally used.
kj::Maybe<Node&> findNode(uint64_t id);
kj::Maybe<Node&> lookupBuiltin(kj::StringPtr name);
Node& getBuiltin(Declaration::Which which);
void load(const SchemaLoader& loader, uint64_t id) const override;
// SchemaLoader callback for the bootstrap loader.
void loadFinal(const SchemaLoader& loader, uint64_t id);
// Called from the SchemaLoader callback for the final loader.
private:
AnnotationFlag annotationFlag;
kj::Arena nodeArena;
// Arena used to allocate nodes and other permanent objects.
std::unordered_map<Module*, kj::Own<CompiledModule>> modules;
// Map of parser modules to compiler modules.
Workspace workspace;
// The temporary workspace. This field must be declared after `modules` because objects
// allocated in the workspace may hold references to the compiled modules in `modules`.
std::unordered_map<uint64_t, Node*> nodesById;
// Map of nodes by ID.
std::unordered_map<uint64_t, schema::Node::SourceInfo::Reader> sourceInfoById;
// Map of SourceInfos by ID, including SourceInfos for groups and param sturcts (which are not
// listed in nodesById).
std::map<kj::StringPtr, kj::Own<Node>> builtinDecls;
std::map<Declaration::Which, Node*> builtinDeclsByKind;
// Map of built-in declarations, like "Int32" and "List", which make up the global scope.
uint64_t nextBogusId = 1000;
// Counter for assigning bogus IDs to nodes whose real ID is a duplicate.
};
// =======================================================================================
kj::Maybe<Resolver::ResolveResult> Compiler::Alias::compile() {
if (!initialized) {
initialized = true;
auto& workspace = module.getCompiler().getWorkspace();
brandOrphan = workspace.orphanage.newOrphan<schema::Brand>();
// If the Workspace is destroyed, revert the alias to the uninitialized state, because the
// orphan we created is no longer valid in this case.
workspace.arena.copy(kj::defer([this]() {
initialized = false;
brandOrphan = Orphan<schema::Brand>();
}));
target = NodeTranslator::compileDecl(
parent.getId(), parent.getParameterCount(), parent,
module.getErrorReporter(), targetName, brandOrphan.get());
}
return target;
}
// =======================================================================================
Compiler::Node::Node(CompiledModule& module)
: module(&module),
parent(nullptr),
declaration(module.getParsedFile().getRoot()),
id(generateId(0, declaration.getName().getValue(), declaration.getId())),
displayName(module.getSourceName()),
kind(declaration.which()),
genericParamCount(declaration.getParameters().size()),
isBuiltin(false) {
auto name = declaration.getName();
if (name.getValue().size() > 0) {
startByte = name.getStartByte();
endByte = name.getEndByte();
} else {
startByte = declaration.getStartByte();
endByte = declaration.getEndByte();
}
id = module.getCompiler().addNode(id, *this);
}
Compiler::Node::Node(Node& parent, const Declaration::Reader& declaration)
: module(parent.module),
parent(parent),
declaration(declaration),
id(generateId(parent.id, declaration.getName().getValue(), declaration.getId())),
displayName(joinDisplayName(parent.module->getCompiler().getNodeArena(),
parent, declaration.getName().getValue())),
kind(declaration.which()),
genericParamCount(declaration.getParameters().size()),
isBuiltin(false) {
auto name = declaration.getName();
if (name.getValue().size() > 0) {
startByte = name.getStartByte();
endByte = name.getEndByte();
} else {
startByte = declaration.getStartByte();
endByte = declaration.getEndByte();
}
id = module->getCompiler().addNode(id, *this);
}
Compiler::Node::Node(kj::StringPtr name, Declaration::Which kind,
List<Declaration::BrandParameter>::Reader genericParams)
: module(nullptr),
parent(nullptr),
// It's helpful if these have unique IDs. Real type IDs can't be under 2^31 anyway.
id(1000 + static_cast<uint>(kind)),
displayName(name),
kind(kind),
genericParamCount(genericParams.size()),
isBuiltin(true),
startByte(0),
endByte(0) {}
uint64_t Compiler::Node::generateId(uint64_t parentId, kj::StringPtr declName,
Declaration::Id::Reader declId) {
if (declId.isUid()) {
return declId.getUid().getValue();
}
return generateChildId(parentId, declName);
}
kj::StringPtr Compiler::Node::joinDisplayName(
kj::Arena& arena, Node& parent, kj::StringPtr declName) {
kj::ArrayPtr<char> result = arena.allocateArray<char>(
parent.displayName.size() + declName.size() + 2);
size_t separatorPos = parent.displayName.size();
memcpy(result.begin(), parent.displayName.begin(), separatorPos);
result[separatorPos] = parent.parent == nullptr ? ':' : '.';
memcpy(result.begin() + separatorPos + 1, declName.begin(), declName.size());
result[result.size() - 1] = '\0';
return kj::StringPtr(result.begin(), result.size() - 1);
}
kj::Maybe<Compiler::Node::Content&> Compiler::Node::getContent(Content::State minimumState) {
KJ_REQUIRE(!isBuiltin, "illegal method call for built-in declaration");
auto& content = guardedContent;
if (content.stateHasReached(minimumState)) {
return content;
}
if (inGetContent) {
addError("Declaration recursively depends on itself.");
return nullptr;
}
inGetContent = true;
KJ_DEFER(inGetContent = false);
switch (content.state) {
case Content::STUB: {
if (minimumState <= Content::STUB) break;
// Expand the child nodes.
auto& arena = module->getCompiler().getNodeArena();
for (auto nestedDecl: declaration.getNestedDecls()) {
switch (nestedDecl.which()) {
case Declaration::FILE:
case Declaration::CONST:
case Declaration::ANNOTATION:
case Declaration::ENUM:
case Declaration::STRUCT:
case Declaration::INTERFACE: {
kj::Own<Node> subNode = arena.allocateOwn<Node>(*this, nestedDecl);
kj::StringPtr name = nestedDecl.getName().getValue();
content.orderedNestedNodes.add(subNode);
content.nestedNodes.insert(std::make_pair(name, kj::mv(subNode)));
break;
}
case Declaration::USING: {
kj::Own<Alias> alias = arena.allocateOwn<Alias>(
*module, *this, nestedDecl.getUsing().getTarget());
kj::StringPtr name = nestedDecl.getName().getValue();
content.aliases.insert(std::make_pair(name, kj::mv(alias)));
break;
}
case Declaration::ENUMERANT:
case Declaration::FIELD:
case Declaration::UNION:
case Declaration::GROUP:
case Declaration::METHOD:
case Declaration::NAKED_ID:
case Declaration::NAKED_ANNOTATION:
// Not a node. Skip.
break;
default:
KJ_FAIL_ASSERT("unknown declaration type", nestedDecl);
break;
}
}
content.advanceState(Content::EXPANDED);
} KJ_FALLTHROUGH;
case Content::EXPANDED: {
if (minimumState <= Content::EXPANDED) break;
// Construct the NodeTranslator.
auto& workspace = module->getCompiler().getWorkspace();
auto schemaNode = workspace.orphanage.newOrphan<schema::Node>();
auto builder = schemaNode.get();
builder.setId(id);
builder.setDisplayName(displayName);
// TODO(cleanup): Would be better if we could remember the prefix length from before we
// added this decl's name to the end.
KJ_IF_MAYBE(lastDot, displayName.findLast('.')) {
builder.setDisplayNamePrefixLength(*lastDot + 1);
}
KJ_IF_MAYBE(lastColon, displayName.findLast(':')) {
if (*lastColon > builder.getDisplayNamePrefixLength()) {
builder.setDisplayNamePrefixLength(*lastColon + 1);
}
}
KJ_IF_MAYBE(p, parent) {
builder.setScopeId(p->id);
}
auto nestedNodes = builder.initNestedNodes(content.orderedNestedNodes.size());
auto nestedIter = nestedNodes.begin();
for (auto node: content.orderedNestedNodes) {
nestedIter->setName(node->declaration.getName().getValue());
nestedIter->setId(node->id);
++nestedIter;
}
content.translator = &workspace.arena.allocate<NodeTranslator>(
*this, module->getErrorReporter(), declaration, kj::mv(schemaNode),
module->getCompiler().shouldCompileAnnotations());
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&](){
auto nodeSet = content.translator->getBootstrapNode();
for (auto& auxNode: nodeSet.auxNodes) {
workspace.bootstrapLoader.loadOnce(auxNode);
}
content.bootstrapSchema = workspace.bootstrapLoader.loadOnce(nodeSet.node);
})) {
content.bootstrapSchema = nullptr;
// Only bother to report validation failures if we think we haven't seen any errors.
// Otherwise we assume that the errors caused the validation failure.
if (!module->getErrorReporter().hadErrors()) {
addError(kj::str("Internal compiler bug: Bootstrap schema failed validation:\n",
*exception));
}
}
// If the Workspace is destroyed, revert the node to the EXPANDED state, because the
// NodeTranslator is no longer valid in this case.
workspace.arena.copy(kj::defer([&content]() {
content.bootstrapSchema = nullptr;
if (content.state > Content::EXPANDED) {
content.state = Content::EXPANDED;
}
}));
content.advanceState(Content::BOOTSTRAP);
} KJ_FALLTHROUGH;
case Content::BOOTSTRAP: {
if (minimumState <= Content::BOOTSTRAP) break;
// Create the final schema.
NodeTranslator::NodeSet nodeSet;
if (content.bootstrapSchema == nullptr) {
// Must have failed in an earlier stage.
KJ_ASSERT(module->getErrorReporter().hadErrors());
nodeSet = content.translator->getBootstrapNode();
} else {
nodeSet = content.translator->finish(
module->getCompiler().getWorkspace().bootstrapLoader.getUnbound(id));
}
content.finalSchema = nodeSet.node;
content.auxSchemas = kj::mv(nodeSet.auxNodes);
content.sourceInfo = kj::mv(nodeSet.sourceInfo);
content.advanceState(Content::FINISHED);
} KJ_FALLTHROUGH;
case Content::FINISHED:
break;
}
return content;
}
kj::Maybe<Schema> Compiler::Node::getBootstrapSchema() {
KJ_IF_MAYBE(schema, loadedFinalSchema) {
// We don't need to rebuild the bootstrap schema if we already have a final schema.
return module->getCompiler().getWorkspace().bootstrapLoader.loadOnce(*schema);
} else KJ_IF_MAYBE(content, getContent(Content::BOOTSTRAP)) {
if (content->state == Content::FINISHED && content->bootstrapSchema == nullptr) {
// The bootstrap schema was discarded. Copy it from the final schema.
// (We can't just return the final schema because using it could trigger schema loader
// callbacks that would deadlock.)
KJ_IF_MAYBE(finalSchema, content->finalSchema) {
return module->getCompiler().getWorkspace().bootstrapLoader.loadOnce(*finalSchema);
} else {
return nullptr;
}
} else {
return content->bootstrapSchema;
}
} else {
return nullptr;
}
}
kj::Maybe<schema::Node::Reader> Compiler::Node::getFinalSchema() {
KJ_IF_MAYBE(schema, loadedFinalSchema) {
return *schema;
} else KJ_IF_MAYBE(content, getContent(Content::FINISHED)) {
return content->finalSchema;
} else {
return nullptr;
}
}
void Compiler::Node::loadFinalSchema(const SchemaLoader& loader) {
KJ_IF_MAYBE(content, getContent(Content::FINISHED)) {
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&](){
KJ_IF_MAYBE(finalSchema, content->finalSchema) {
KJ_MAP(auxSchema, content->auxSchemas) {
return loader.loadOnce(auxSchema);
};
loadedFinalSchema = loader.loadOnce(*finalSchema).getProto();
}
})) {
// Schema validation threw an exception.
// Don't try loading this again.
content->finalSchema = nullptr;
// Only bother to report validation failures if we think we haven't seen any errors.
// Otherwise we assume that the errors caused the validation failure.
if (!module->getErrorReporter().hadErrors()) {
addError(kj::str("Internal compiler bug: Schema failed validation:\n", *exception));
}
}
}
}
void Compiler::Node::traverse(uint eagerness, std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo) {
uint& slot = seen[this];
if ((slot & eagerness) == eagerness) {
// We've already covered this node.
return;
}
slot |= eagerness;
KJ_IF_MAYBE(content, getContent(Content::FINISHED)) {
loadFinalSchema(finalLoader);
KJ_IF_MAYBE(schema, getFinalSchema()) {
if (eagerness / DEPENDENCIES != 0) {
// For traversing dependencies, discard the bits lower than DEPENDENCIES and replace
// them with the bits above DEPENDENCIES shifted over.
uint newEagerness = (eagerness & ~(DEPENDENCIES - 1)) | (eagerness / DEPENDENCIES);
traverseNodeDependencies(*schema, newEagerness, seen, finalLoader, sourceInfo);
for (auto& aux: content->auxSchemas) {
traverseNodeDependencies(aux, newEagerness, seen, finalLoader, sourceInfo);
}
}
}
sourceInfo.addAll(content->sourceInfo);
}
if (eagerness & PARENTS) {
KJ_IF_MAYBE(p, parent) {
p->traverse(eagerness, seen, finalLoader, sourceInfo);
}
}
if (eagerness & CHILDREN) {
KJ_IF_MAYBE(content, getContent(Content::EXPANDED)) {
for (auto& child: content->orderedNestedNodes) {
child->traverse(eagerness, seen, finalLoader, sourceInfo);
}
// Also traverse `using` declarations.
for (auto& child: content->aliases) {
child.second->compile();
}
}
}
}
void Compiler::Node::traverseNodeDependencies(
const schema::Node::Reader& schemaNode, uint eagerness,
std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo) {
switch (schemaNode.which()) {
case schema::Node::STRUCT:
for (auto field: schemaNode.getStruct().getFields()) {
switch (field.which()) {
case schema::Field::SLOT:
traverseType(field.getSlot().getType(), eagerness, seen, finalLoader, sourceInfo);
break;
case schema::Field::GROUP:
// Aux node will be scanned later.
break;
}
traverseAnnotations(field.getAnnotations(), eagerness, seen, finalLoader, sourceInfo);
}
break;
case schema::Node::ENUM:
for (auto enumerant: schemaNode.getEnum().getEnumerants()) {
traverseAnnotations(enumerant.getAnnotations(), eagerness, seen, finalLoader, sourceInfo);
}
break;
case schema::Node::INTERFACE: {
auto interface = schemaNode.getInterface();
for (auto superclass: interface.getSuperclasses()) {
uint64_t superclassId = superclass.getId();
if (superclassId != 0) { // if zero, we reported an error earlier
traverseDependency(superclassId, eagerness, seen, finalLoader, sourceInfo);
}
traverseBrand(superclass.getBrand(), eagerness, seen, finalLoader, sourceInfo);
}
for (auto method: interface.getMethods()) {
traverseDependency(
method.getParamStructType(), eagerness, seen, finalLoader, sourceInfo, true);
traverseBrand(method.getParamBrand(), eagerness, seen, finalLoader, sourceInfo);
traverseDependency(
method.getResultStructType(), eagerness, seen, finalLoader, sourceInfo, true);
traverseBrand(method.getResultBrand(), eagerness, seen, finalLoader, sourceInfo);
traverseAnnotations(method.getAnnotations(), eagerness, seen, finalLoader, sourceInfo);
}
break;
}
case schema::Node::CONST:
traverseType(schemaNode.getConst().getType(), eagerness, seen, finalLoader, sourceInfo);
break;
case schema::Node::ANNOTATION:
traverseType(schemaNode.getAnnotation().getType(), eagerness, seen, finalLoader, sourceInfo);
break;
default:
break;
}
traverseAnnotations(schemaNode.getAnnotations(), eagerness, seen, finalLoader, sourceInfo);
}
void Compiler::Node::traverseType(const schema::Type::Reader& type, uint eagerness,
std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo) {
uint64_t id = 0;
schema::Brand::Reader brand;
switch (type.which()) {
case schema::Type::STRUCT:
id = type.getStruct().getTypeId();
brand = type.getStruct().getBrand();
break;
case schema::Type::ENUM:
id = type.getEnum().getTypeId();
brand = type.getEnum().getBrand();
break;
case schema::Type::INTERFACE:
id = type.getInterface().getTypeId();
brand = type.getInterface().getBrand();
break;
case schema::Type::LIST:
traverseType(type.getList().getElementType(), eagerness, seen, finalLoader, sourceInfo);
return;
default:
return;
}
traverseDependency(id, eagerness, seen, finalLoader, sourceInfo);
traverseBrand(brand, eagerness, seen, finalLoader, sourceInfo);
}
void Compiler::Node::traverseBrand(
const schema::Brand::Reader& brand, uint eagerness,
std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo) {
for (auto scope: brand.getScopes()) {
switch (scope.which()) {
case schema::Brand::Scope::BIND:
for (auto binding: scope.getBind()) {
switch (binding.which()) {
case schema::Brand::Binding::UNBOUND:
break;
case schema::Brand::Binding::TYPE:
traverseType(binding.getType(), eagerness, seen, finalLoader, sourceInfo);
break;
}
}
break;
case schema::Brand::Scope::INHERIT:
break;
}
}
}
void Compiler::Node::traverseDependency(uint64_t depId, uint eagerness,
std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo,
bool ignoreIfNotFound) {
KJ_IF_MAYBE(node, module->getCompiler().findNode(depId)) {
node->traverse(eagerness, seen, finalLoader, sourceInfo);
} else if (!ignoreIfNotFound) {
KJ_FAIL_ASSERT("Dependency ID not present in compiler?", depId);
}
}
void Compiler::Node::traverseAnnotations(const List<schema::Annotation>::Reader& annotations,
uint eagerness,
std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader,
kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo) {
for (auto annotation: annotations) {
KJ_IF_MAYBE(node, module->getCompiler().findNode(annotation.getId())) {
node->traverse(eagerness, seen, finalLoader, sourceInfo);
}
}
}
void Compiler::Node::addError(kj::StringPtr error) {
module->getErrorReporter().addError(startByte, endByte, error);
}
kj::Maybe<Resolver::ResolveResult>
Compiler::Node::resolve(kj::StringPtr name) {
// Check members.
KJ_IF_MAYBE(member, resolveMember(name)) {
return *member;
}
// Check parameters.
// TODO(perf): Maintain a map?
auto params = declaration.getParameters();
for (uint i: kj::indices(params)) {
if (params[i].getName() == name) {
ResolveResult result;
result.init<ResolvedParameter>(ResolvedParameter {id, i});
return result;
}
}
// Check parent scope.
KJ_IF_MAYBE(p, parent) {
return p->resolve(name);
} else KJ_IF_MAYBE(b, module->getCompiler().lookupBuiltin(name)) {
ResolveResult result;
result.init<ResolvedDecl>(ResolvedDecl { b->id, b->genericParamCount, 0, b->kind, b, nullptr });
return result;
} else {
return nullptr;
}
}
kj::Maybe<Resolver::ResolveResult>
Compiler::Node::resolveMember(kj::StringPtr name) {
if (isBuiltin) return nullptr;
KJ_IF_MAYBE(content, getContent(Content::EXPANDED)) {
{
auto iter = content->nestedNodes.find(name);
if (iter != content->nestedNodes.end()) {
Node* node = iter->second;
ResolveResult result;
result.init<ResolvedDecl>(ResolvedDecl {
node->id, node->genericParamCount, id, node->kind, node, nullptr });
return result;
}
}
{
auto iter = content->aliases.find(name);
if (iter != content->aliases.end()) {
return iter->second->compile();
}
}
}
return nullptr;
}
Resolver::ResolvedDecl Compiler::Node::resolveBuiltin(Declaration::Which which) {
auto& b = module->getCompiler().getBuiltin(which);
return { b.id, b.genericParamCount, 0, b.kind, &b, nullptr };
}
Resolver::ResolvedDecl Compiler::Node::resolveId(uint64_t id) {
auto& n = KJ_ASSERT_NONNULL(module->getCompiler().findNode(id));
uint64_t parentId = n.parent.map([](Node& n) { return n.id; }).orDefault(0);
return { n.id, n.genericParamCount, parentId, n.kind, &n, nullptr };
}
kj::Maybe<Resolver::ResolvedDecl> Compiler::Node::getParent() {
return parent.map([](Node& parent) {
uint64_t scopeId = parent.parent.map([](Node& gp) { return gp.id; }).orDefault(0);
return ResolvedDecl { parent.id, parent.genericParamCount, scopeId, parent.kind, &parent, nullptr };
});
}
Resolver::ResolvedDecl Compiler::Node::getTopScope() {
Node& node = module->getRootNode();
return ResolvedDecl { node.id, 0, 0, node.kind, &node, nullptr };
}
kj::Maybe<Schema> Compiler::Node::resolveBootstrapSchema(
uint64_t id, schema::Brand::Reader brand) {
KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
// Make sure the bootstrap schema is loaded into the SchemaLoader.
if (node->getBootstrapSchema() == nullptr) {
return nullptr;
}
// Now we actually invoke get() to evaluate the brand.
return module->getCompiler().getWorkspace().bootstrapLoader.get(id, brand);
} else {
KJ_FAIL_REQUIRE("Tried to get schema for ID we haven't seen before.");
}
}
kj::Maybe<schema::Node::Reader> Compiler::Node::resolveFinalSchema(uint64_t id) {
KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
return node->getFinalSchema();
} else {
KJ_FAIL_REQUIRE("Tried to get schema for ID we haven't seen before.");
}
}
kj::Maybe<Resolver::ResolvedDecl>
Compiler::Node::resolveImport(kj::StringPtr name) {
KJ_IF_MAYBE(m, module->importRelative(name)) {
Node& root = m->getRootNode();
return ResolvedDecl { root.id, 0, 0, root.kind, &root, nullptr };
} else {
return nullptr;
}
}
kj::Maybe<kj::Array<const byte>> Compiler::Node::readEmbed(kj::StringPtr name) {
return module->embedRelative(name);
}
kj::Maybe<Type> Compiler::Node::resolveBootstrapType(schema::Type::Reader type, Schema scope) {
// TODO(someday): Arguably should return null if the type or its dependencies are placeholders.
kj::Maybe<Type> result;
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]() {
result = module->getCompiler().getWorkspace().bootstrapLoader.getType(type, scope);
})) {
result = nullptr;
if (!module->getErrorReporter().hadErrors()) {
addError(kj::str("Internal compiler bug: Bootstrap schema failed to load:\n",
*exception));
}
}
return result;
}
// =======================================================================================
Compiler::CompiledModule::CompiledModule(Compiler::Impl& compiler, Module& parserModule)
: compiler(compiler), parserModule(parserModule),
content(parserModule.loadContent(contentArena.getOrphanage())),
rootNode(*this) {}
kj::Maybe<Compiler::CompiledModule&> Compiler::CompiledModule::importRelative(
kj::StringPtr importPath) {
return parserModule.importRelative(importPath).map(
[this](Module& module) -> Compiler::CompiledModule& {
return compiler.addInternal(module);
});
}
kj::Maybe<kj::Array<const byte>> Compiler::CompiledModule::embedRelative(kj::StringPtr embedPath) {
return parserModule.embedRelative(embedPath);
}
static void findImports(Expression::Reader exp, std::set<kj::StringPtr>& output) {
switch (exp.which()) {
case Expression::UNKNOWN:
case Expression::POSITIVE_INT:
case Expression::NEGATIVE_INT:
case Expression::FLOAT:
case Expression::STRING:
case Expression::BINARY:
case Expression::RELATIVE_NAME:
case Expression::ABSOLUTE_NAME:
case Expression::EMBED:
break;
case Expression::IMPORT:
output.insert(exp.getImport().getValue());
break;
case Expression::LIST:
for (auto element: exp.getList()) {
findImports(element, output);
}
break;
case Expression::TUPLE:
for (auto element: exp.getTuple()) {
findImports(element.getValue(), output);
}
break;
case Expression::APPLICATION: {
auto app = exp.getApplication();
findImports(app.getFunction(), output);
for (auto param: app.getParams()) {
findImports(param.getValue(), output);
}
break;
}
case Expression::MEMBER: {
findImports(exp.getMember().getParent(), output);
break;
}
}
}
static void findImports(Declaration::ParamList::Reader paramList, std::set<kj::StringPtr>& output) {
switch (paramList.which()) {
case Declaration::ParamList::NAMED_LIST:
for (auto param: paramList.getNamedList()) {
findImports(param.getType(), output);
for (auto ann: param.getAnnotations()) {
findImports(ann.getName(), output);
}
}
break;
case Declaration::ParamList::TYPE:
findImports(paramList.getType(), output);
break;
case Declaration::ParamList::STREAM:
output.insert("/capnp/stream.capnp");
break;
}
}
static void findImports(Declaration::Reader decl, std::set<kj::StringPtr>& output) {
switch (decl.which()) {
case Declaration::USING:
findImports(decl.getUsing().getTarget(), output);
break;
case Declaration::CONST:
findImports(decl.getConst().getType(), output);
break;
case Declaration::FIELD:
findImports(decl.getField().getType(), output);
break;
case Declaration::INTERFACE:
for (auto superclass: decl.getInterface().getSuperclasses()) {
findImports(superclass, output);
}
break;
case Declaration::METHOD: {
auto method = decl.getMethod();
findImports(method.getParams(), output);
if (method.getResults().isExplicit()) {
findImports(method.getResults().getExplicit(), output);
}
break;
}
default:
break;
}
for (auto ann: decl.getAnnotations()) {
findImports(ann.getName(), output);
}
for (auto nested: decl.getNestedDecls()) {
findImports(nested, output);
}
}
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
Compiler::CompiledModule::getFileImportTable(Orphanage orphanage) {
// Build a table of imports for CodeGeneratorRequest.RequestedFile.imports. Note that we only
// care about type imports, not constant value imports, since constant values (including default
// values) are already embedded in full in the schema. In other words, we only need the imports
// that would need to be #included in the generated code.
std::set<kj::StringPtr> importNames;
findImports(content.getReader().getRoot(), importNames);
auto result = orphanage.newOrphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>(
importNames.size());
auto builder = result.get();
uint i = 0;
for (auto name: importNames) {
// We presumably ran this import before, so it shouldn't throw now.
auto entry = builder[i++];
entry.setId(KJ_ASSERT_NONNULL(importRelative(name)).rootNode.getId());
entry.setName(name);
}
return result;
}
// =======================================================================================
Compiler::Impl::Impl(AnnotationFlag annotationFlag)
: annotationFlag(annotationFlag), workspace(*this) {
// Reflectively interpret the members of Declaration.body. Any member prefixed by "builtin"
// defines a builtin declaration visible in the global scope.
StructSchema declSchema = Schema::from<Declaration>();
for (auto field: declSchema.getFields()) {
auto fieldProto = field.getProto();
if (fieldProto.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT) {
auto name = fieldProto.getName();
if (name.startsWith("builtin")) {
kj::StringPtr symbolName = name.slice(strlen("builtin"));
List<Declaration::BrandParameter>::Reader params;
for (auto annotation: fieldProto.getAnnotations()) {
if (annotation.getId() == 0x94099c3f9eb32d6bull) {
params = annotation.getValue().getList().getAs<List<Declaration::BrandParameter>>();
break;
}
}
Declaration::Which which =
static_cast<Declaration::Which>(fieldProto.getDiscriminantValue());
kj::Own<Node> newNode = nodeArena.allocateOwn<Node>(symbolName, which, params);
builtinDeclsByKind[which] = newNode;
builtinDecls[symbolName] = kj::mv(newNode);
}
}
}
}
Compiler::Impl::~Impl() noexcept(false) {}
void Compiler::Impl::clearWorkspace() {
// Make sure we reconstruct the workspace even if destroying it throws an exception.
KJ_DEFER(kj::ctor(workspace, *this));
kj::dtor(workspace);
}
Compiler::CompiledModule& Compiler::Impl::addInternal(Module& parsedModule) {
kj::Own<CompiledModule>& slot = modules[&parsedModule];
if (slot.get() == nullptr) {
slot = kj::heap<CompiledModule>(*this, parsedModule);
}
return *slot;
}
uint64_t Compiler::Impl::addNode(uint64_t desiredId, Node& node) {
for (;;) {
auto insertResult = nodesById.insert(std::make_pair(desiredId, &node));
if (insertResult.second) {
return desiredId;
}
// Only report an error if this ID is not bogus. Actual IDs specified in the original source
// code are required to have the upper bit set. Anything else must have been manufactured
// at some point to cover up an error.
if (desiredId & (1ull << 63)) {
node.addError(kj::str("Duplicate ID @0x", kj::hex(desiredId), "."));
insertResult.first->second->addError(
kj::str("ID @0x", kj::hex(desiredId), " originally used here."));
}
// Assign a new bogus ID.
desiredId = nextBogusId++;
}
}
kj::Maybe<Compiler::Node&> Compiler::Impl::findNode(uint64_t id) {
auto iter = nodesById.find(id);
if (iter == nodesById.end()) {
return nullptr;
} else {
return *iter->second;
}
}
kj::Maybe<Compiler::Node&> Compiler::Impl::lookupBuiltin(kj::StringPtr name) {
auto iter = builtinDecls.find(name);
if (iter == builtinDecls.end()) {
return nullptr;
} else {
return *iter->second;
}
}
Compiler::Node& Compiler::Impl::getBuiltin(Declaration::Which which) {
auto iter = builtinDeclsByKind.find(which);
KJ_REQUIRE(iter != builtinDeclsByKind.end(), "invalid builtin", (uint)which);
return *iter->second;
}
kj::Maybe<uint64_t> Compiler::Impl::lookup(uint64_t parent, kj::StringPtr childName) {
// Looking up members does not use the workspace, so we don't need to lock it.
KJ_IF_MAYBE(parentNode, findNode(parent)) {
KJ_IF_MAYBE(child, parentNode->resolveMember(childName)) {
if (child->is<Resolver::ResolvedDecl>()) {
return child->get<Resolver::ResolvedDecl>().id;
} else {
// An alias. We don't support looking up aliases with this method.
return nullptr;
}
} else {
return nullptr;
}
} else {
KJ_FAIL_REQUIRE("lookup()s parameter 'parent' must be a known ID.", parent);
}
}
kj::Maybe<schema::Node::SourceInfo::Reader> Compiler::Impl::getSourceInfo(uint64_t id) {
auto iter = sourceInfoById.find(id);
if (iter == sourceInfoById.end()) {
return nullptr;
} else {
return iter->second;
}
}
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
Compiler::Impl::getFileImportTable(Module& module, Orphanage orphanage) {
return addInternal(module).getFileImportTable(orphanage);
}
Orphan<List<schema::Node::SourceInfo>> Compiler::Impl::getAllSourceInfo(Orphanage orphanage) {
auto result = orphanage.newOrphan<List<schema::Node::SourceInfo>>(sourceInfoById.size());
auto builder = result.get();
size_t i = 0;
for (auto& entry: sourceInfoById) {
builder.setWithCaveats(i++, entry.second);
}
return result;
}
void Compiler::Impl::eagerlyCompile(uint64_t id, uint eagerness,
const SchemaLoader& finalLoader) {
KJ_IF_MAYBE(node, findNode(id)) {
std::unordered_map<Node*, uint> seen;
kj::Vector<schema::Node::SourceInfo::Reader> sourceInfos;
node->traverse(eagerness, seen, finalLoader, sourceInfos);
// Copy the SourceInfo structures into permanent space so that they aren't invalidated when
// clearWorkspace() is called.
for (auto& sourceInfo: sourceInfos) {
auto words = nodeArena.allocateArray<word>(sourceInfo.totalSize().wordCount + 1);
memset(words.begin(), 0, words.asBytes().size());
copyToUnchecked(sourceInfo, words);
sourceInfoById.insert(std::make_pair(sourceInfo.getId(),
readMessageUnchecked<schema::Node::SourceInfo>(words.begin())));
}
} else {
KJ_FAIL_REQUIRE("id did not come from this Compiler.", id);
}
}
void Compiler::Impl::load(const SchemaLoader& loader, uint64_t id) const {
// We know that this load() is only called from the bootstrap loader which is already protected
// by our mutex, so we can drop thread-safety.
auto& self = const_cast<Compiler::Impl&>(*this);
KJ_IF_MAYBE(node, self.findNode(id)) {
node->getBootstrapSchema();
}
}
void Compiler::Impl::loadFinal(const SchemaLoader& loader, uint64_t id) {
KJ_IF_MAYBE(node, findNode(id)) {
node->loadFinalSchema(loader);
}
}
// =======================================================================================
Compiler::Compiler(AnnotationFlag annotationFlag)
: impl(kj::heap<Impl>(annotationFlag)),
loader(*this) {}
Compiler::~Compiler() noexcept(false) {}
Compiler::ModuleScope Compiler::add(Module& module) const {
Node& root = impl.lockExclusive()->get()->addInternal(module).getRootNode();
return ModuleScope(*this, root.getId(), root);
}
kj::Maybe<uint64_t> Compiler::lookup(uint64_t parent, kj::StringPtr childName) const {
return impl.lockExclusive()->get()->lookup(parent, childName);
}
kj::Maybe<schema::Node::SourceInfo::Reader> Compiler::getSourceInfo(uint64_t id) const {
return impl.lockExclusive()->get()->getSourceInfo(id);
}
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
Compiler::getFileImportTable(Module& module, Orphanage orphanage) const {
return impl.lockExclusive()->get()->getFileImportTable(module, orphanage);
}
Orphan<List<schema::Node::SourceInfo>> Compiler::getAllSourceInfo(Orphanage orphanage) const {
return impl.lockExclusive()->get()->getAllSourceInfo(orphanage);
}
void Compiler::eagerlyCompile(uint64_t id, uint eagerness) const {
impl.lockExclusive()->get()->eagerlyCompile(id, eagerness, loader);
}
void Compiler::clearWorkspace() const {
impl.lockExclusive()->get()->clearWorkspace();
}
void Compiler::load(const SchemaLoader& loader, uint64_t id) const {
impl.lockExclusive()->get()->loadFinal(loader, id);
}
// -----------------------------------------------------------------------------
class Compiler::ErrorIgnorer: public ErrorReporter {
public:
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override {}
bool hadErrors() override { return false; }
static ErrorIgnorer instance;
};
Compiler::ErrorIgnorer Compiler::ErrorIgnorer::instance;
kj::Maybe<Type> Compiler::CompiledType::getSchema() {
capnp::word scratch[32];
memset(&scratch, 0, sizeof(scratch));
capnp::MallocMessageBuilder message(scratch);
auto builder = message.getRoot<schema::Type>();
{
auto lock = compiler.impl.lockShared();
decl.get(lock).compileAsType(ErrorIgnorer::instance, builder);
}
// No need to pass `scope` as second parameter since CompiledType always represents a type
// expression evaluated free-standing, not in any scope.
return compiler.loader.getType(builder.asReader());
}
Compiler::CompiledType Compiler::CompiledType::clone() {
kj::ExternalMutexGuarded<BrandedDecl> newDecl;
{
auto lock = compiler.impl.lockExclusive();
newDecl.set(lock, kj::cp(decl.get(lock)));
}
return CompiledType(compiler, kj::mv(newDecl));
}
kj::Maybe<Compiler::CompiledType> Compiler::CompiledType::getMember(kj::StringPtr name) {
kj::ExternalMutexGuarded<BrandedDecl> newDecl;
bool found = false;
{
auto lock = compiler.impl.lockShared();
KJ_IF_MAYBE(member, decl.get(lock).getMember(name, {})) {
newDecl.set(lock, kj::mv(*member));
found = true;
}
}
if (found) {
return CompiledType(compiler, kj::mv(newDecl));
} else {
return nullptr;
}
}
kj::Maybe<Compiler::CompiledType> Compiler::CompiledType::applyBrand(
kj::Array<CompiledType> arguments) {
kj::ExternalMutexGuarded<BrandedDecl> newDecl;
bool found = false;
{
auto lock = compiler.impl.lockShared();
auto args = KJ_MAP(arg, arguments) { return kj::mv(arg.decl.get(lock)); };
KJ_IF_MAYBE(member, decl.get(lock).applyParams(kj::mv(args), {})) {
newDecl.set(lock, kj::mv(*member));
found = true;
}
}
if (found) {
return CompiledType(compiler, kj::mv(newDecl));
} else {
return nullptr;
}
}
Compiler::CompiledType Compiler::ModuleScope::getRoot() {
kj::ExternalMutexGuarded<BrandedDecl> newDecl;
{
auto lock = compiler.impl.lockExclusive();
auto brandScope = kj::refcounted<BrandScope>(ErrorIgnorer::instance, node.getId(), 0, node);
Resolver::ResolvedDecl decl { node.getId(), 0, 0, node.getKind(), &node, nullptr };
newDecl.set(lock, BrandedDecl(kj::mv(decl), kj::mv(brandScope), {}));
}
return CompiledType(compiler, kj::mv(newDecl));
}
kj::Maybe<Compiler::CompiledType> Compiler::ModuleScope::evalType(
Expression::Reader expression, ErrorReporter& errorReporter) {
kj::ExternalMutexGuarded<BrandedDecl> newDecl;
bool found = false;
{
auto lock = compiler.impl.lockExclusive();
auto brandScope = kj::refcounted<BrandScope>(errorReporter, node.getId(), 0, node);
KJ_IF_MAYBE(result, brandScope->compileDeclExpression(
expression, node, ImplicitParams::none())) {
newDecl.set(lock, kj::mv(*result));
found = true;
};
}
if (found) {
return CompiledType(compiler, kj::mv(newDecl));
} else {
return nullptr;
}
}
} // namespace compiler
} // namespace capnp