Commit e422fc1d authored by Kenton Varda's avatar Kenton Varda

Simplify mutex usage in compiler code.

parent e1fdba61
...@@ -298,7 +298,7 @@ public: ...@@ -298,7 +298,7 @@ public:
} }
private: private:
kj::Maybe<const Module&> loadModule(kj::StringPtr file) { kj::Maybe<Module&> loadModule(kj::StringPtr file) {
size_t longestPrefix = 0; size_t longestPrefix = 0;
for (auto& prefix: sourcePrefixes) { for (auto& prefix: sourcePrefixes) {
...@@ -1218,12 +1218,12 @@ private: ...@@ -1218,12 +1218,12 @@ private:
kj::ArrayPtr<const char> content) kj::ArrayPtr<const char> content)
: globalReporter(globalReporter), lineBreaks(content) {} : globalReporter(globalReporter), lineBreaks(content) {}
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const override { void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override {
globalReporter.addError("<stdin>", lineBreaks.toSourcePos(startByte), globalReporter.addError("<stdin>", lineBreaks.toSourcePos(startByte),
lineBreaks.toSourcePos(endByte), message); lineBreaks.toSourcePos(endByte), message);
} }
bool hadErrors() const override { bool hadErrors() override {
return globalReporter.hadErrors(); return globalReporter.hadErrors();
} }
...@@ -1234,7 +1234,7 @@ private: ...@@ -1234,7 +1234,7 @@ private:
class ValueResolverGlue final: public ValueTranslator::Resolver { class ValueResolverGlue final: public ValueTranslator::Resolver {
public: public:
ValueResolverGlue(const SchemaLoader& loader, const ErrorReporter& errorReporter) ValueResolverGlue(const SchemaLoader& loader, ErrorReporter& errorReporter)
: loader(loader), errorReporter(errorReporter) {} : loader(loader), errorReporter(errorReporter) {}
kj::Maybe<Schema> resolveType(uint64_t id) { kj::Maybe<Schema> resolveType(uint64_t id) {
...@@ -1266,14 +1266,14 @@ private: ...@@ -1266,14 +1266,14 @@ private:
private: private:
const SchemaLoader& loader; const SchemaLoader& loader;
const ErrorReporter& errorReporter; ErrorReporter& errorReporter;
}; };
public: public:
// ===================================================================================== // =====================================================================================
void addError(kj::StringPtr file, SourcePos start, SourcePos end, void addError(kj::StringPtr file, SourcePos start, SourcePos end,
kj::StringPtr message) const override { kj::StringPtr message) override {
kj::String wholeMessage; kj::String wholeMessage;
if (end.line == start.line) { if (end.line == start.line) {
if (end.column == start.column) { if (end.column == start.column) {
...@@ -1289,11 +1289,11 @@ public: ...@@ -1289,11 +1289,11 @@ public:
} }
context.error(wholeMessage); context.error(wholeMessage);
__atomic_store_n(&hadErrors_, true, __ATOMIC_RELAXED); hadErrors_ = true;
} }
bool hadErrors() const override { bool hadErrors() override {
return __atomic_load_n(&hadErrors_, __ATOMIC_RELAXED); return hadErrors_;
} }
private: private:
...@@ -1326,7 +1326,7 @@ private: ...@@ -1326,7 +1326,7 @@ private:
struct SourceFile { struct SourceFile {
uint64_t id; uint64_t id;
kj::StringPtr name; kj::StringPtr name;
const Module* module; Module* module;
}; };
kj::Vector<SourceFile> sourceFiles; kj::Vector<SourceFile> sourceFiles;
...@@ -1337,7 +1337,7 @@ private: ...@@ -1337,7 +1337,7 @@ private:
}; };
kj::Vector<OutputDirective> outputs; kj::Vector<OutputDirective> outputs;
mutable bool hadErrors_ = false; bool hadErrors_ = false;
}; };
} // namespace compiler } // namespace compiler
......
...@@ -39,15 +39,15 @@ namespace compiler { ...@@ -39,15 +39,15 @@ namespace compiler {
class Compiler::Alias { class Compiler::Alias {
public: public:
Alias(const Node& parent, const DeclName::Reader& targetName) Alias(Node& parent, const DeclName::Reader& targetName)
: parent(parent), targetName(targetName) {} : parent(parent), targetName(targetName) {}
kj::Maybe<const Node&> getTarget() const; kj::Maybe<Node&> getTarget();
private: private:
const Node& parent; Node& parent;
DeclName::Reader targetName; DeclName::Reader targetName;
kj::Lazy<kj::Maybe<const Node&>> target; kj::Lazy<kj::Maybe<Node&>> target;
}; };
class Compiler::Node: public NodeTranslator::Resolver { class Compiler::Node: public NodeTranslator::Resolver {
...@@ -64,44 +64,46 @@ public: ...@@ -64,44 +64,46 @@ public:
explicit Node(CompiledModule& module); explicit Node(CompiledModule& module);
// Create a root node representing the given file. May // Create a root node representing the given file. May
Node(const Node& parent, const Declaration::Reader& declaration); Node(Node& parent, const Declaration::Reader& declaration);
// Create a child node. // Create a child node.
Node(kj::StringPtr name, Declaration::Which kind); Node(kj::StringPtr name, Declaration::Which kind);
// Create a dummy node representing a built-in declaration, like "Int32" or "true". // Create a dummy node representing a built-in declaration, like "Int32" or "true".
uint64_t getId() const { return id; } uint64_t getId() { return id; }
Declaration::Which getKind() const { return kind; } Declaration::Which getKind() { return kind; }
kj::Maybe<const Node&> lookupMember(kj::StringPtr name) const; kj::Maybe<Node&> lookupMember(kj::StringPtr name);
// Find a direct member of this node with the given name. // Find a direct member of this node with the given name.
kj::Maybe<const Node&> lookupLexical(kj::StringPtr name) const; kj::Maybe<Node&> lookupLexical(kj::StringPtr name);
// Look up the given name first as a member of this Node, then in its parent, and so on, until // Look up the given name first as a member of this Node, then in its parent, and so on, until
// it is found or there are no more parents to search. // it is found or there are no more parents to search.
kj::Maybe<const Node&> lookup(const DeclName::Reader& name) const; kj::Maybe<Node&> lookup(const DeclName::Reader& name);
// Resolve an arbitrary DeclName to a Node. // Resolve an arbitrary DeclName to a Node.
kj::Maybe<Schema> getBootstrapSchema() const; kj::Maybe<Schema> getBootstrapSchema();
kj::Maybe<schema::Node::Reader> getFinalSchema() const; kj::Maybe<schema::Node::Reader> getFinalSchema();
void loadFinalSchema(const SchemaLoader& loader);
void traverse(uint eagerness, std::unordered_map<const Node*, uint>& seen) const; void traverse(uint eagerness, std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader);
// Get the final schema for this node, and also possibly traverse the node's children and // 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. // dependencies to ensure that they are loaded, depending on the mode.
void addError(kj::StringPtr error) const; void addError(kj::StringPtr error);
// Report an error on this Node. // Report an error on this Node.
// implements NodeTranslator::Resolver ----------------------------- // implements NodeTranslator::Resolver -----------------------------
kj::Maybe<ResolvedName> resolve(const DeclName::Reader& name) const override; kj::Maybe<ResolvedName> resolve(const DeclName::Reader& name) override;
kj::Maybe<Schema> resolveBootstrapSchema(uint64_t id) const override; kj::Maybe<Schema> resolveBootstrapSchema(uint64_t id) override;
kj::Maybe<schema::Node::Reader> resolveFinalSchema(uint64_t id) const override; kj::Maybe<schema::Node::Reader> resolveFinalSchema(uint64_t id) override;
kj::Maybe<uint64_t> resolveImport(kj::StringPtr name) const override; kj::Maybe<uint64_t> resolveImport(kj::StringPtr name) override;
private: private:
const CompiledModule* module; // null iff isBuiltin is true CompiledModule* module; // null iff isBuiltin is true
kj::Maybe<const Node&> parent; kj::Maybe<Node&> parent;
Declaration::Reader declaration; Declaration::Reader declaration;
// AST of the declaration parsed from the schema file. May become invalid once the content // AST of the declaration parsed from the schema file. May become invalid once the content
...@@ -135,13 +137,13 @@ private: ...@@ -135,13 +137,13 @@ private:
FINISHED FINISHED
}; };
State state; State state;
// Indicates which fields below are valid. Must update with atomic-release semantics. // Indicates which fields below are valid.
inline bool stateHasReached(State minimumState) const { inline bool stateHasReached(State minimumState) {
return __atomic_load_n(&state, __ATOMIC_ACQUIRE) >= minimumState; return state >= minimumState;
} }
inline void advanceState(State newState) { inline void advanceState(State newState) {
__atomic_store_n(&state, newState, __ATOMIC_RELEASE); state = newState;
} }
// EXPANDED ------------------------------------ // EXPANDED ------------------------------------
...@@ -166,15 +168,15 @@ private: ...@@ -166,15 +168,15 @@ private:
// FINISHED ------------------------------------ // FINISHED ------------------------------------
kj::Maybe<Schema> finalSchema; kj::Maybe<schema::Node::Reader> finalSchema;
// The complete schema as loaded by the compiler's main SchemaLoader. Null if the final // The completed schema, ready to load into the real schema loader.
// loader threw an exception.
kj::Array<Schema> auxSchemas; kj::Array<schema::Node::Reader> auxSchemas;
// Schemas for all auxiliary nodes built by the NodeTranslator. // Schemas for all auxiliary nodes built by the NodeTranslator.
}; };
kj::MutexGuarded<Content> content; Content guardedContent; // Read using getContent() only!
bool inGetContent = false; // True while getContent() is running; detects cycles.
// --------------------------------------------- // ---------------------------------------------
...@@ -183,46 +185,51 @@ private: ...@@ -183,46 +185,51 @@ private:
// Extract the ID from the declaration, or if it has none, generate one based on the name and // Extract the ID from the declaration, or if it has none, generate one based on the name and
// parent ID. // parent ID.
static kj::StringPtr joinDisplayName(const kj::Arena& arena, const Node& parent, static kj::StringPtr joinDisplayName(const kj::Arena& arena, Node& parent,
kj::StringPtr declName); kj::StringPtr declName);
// Join the parent's display name with the child's unqualified name to construct the child's // Join the parent's display name with the child's unqualified name to construct the child's
// display name. // display name.
const Content& getContent(Content::State minimumState) const; kj::Maybe<Content&> getContent(Content::State minimumState);
// Advances the content to at least the given state and returns it. Does not lock if the content // Advances the content to at least the given state and returns it. Returns null if getContent()
// is already at or past the given state. // 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, void traverseNodeDependencies(const schema::Node::Reader& schemaNode, uint eagerness,
std::unordered_map<const Node*, uint>& seen) const; std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader);
void traverseType(const schema::Type::Reader& type, uint eagerness, void traverseType(const schema::Type::Reader& type, uint eagerness,
std::unordered_map<const Node*, uint>& seen) const; std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader);
void traverseAnnotations(const List<schema::Annotation>::Reader& annotations, uint eagerness, void traverseAnnotations(const List<schema::Annotation>::Reader& annotations, uint eagerness,
std::unordered_map<const Node*, uint>& seen) const; std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader);
void traverseDependency(uint64_t depId, uint eagerness, void traverseDependency(uint64_t depId, uint eagerness,
std::unordered_map<const Node*, uint>& seen, std::unordered_map<Node*, uint>& seen,
bool ignoreIfNotFound = false) const; const SchemaLoader& finalLoader,
bool ignoreIfNotFound = false);
// Helpers for traverse(). // Helpers for traverse().
}; };
class Compiler::CompiledModule { class Compiler::CompiledModule {
public: public:
CompiledModule(const Compiler::Impl& compiler, const Module& parserModule); CompiledModule(Compiler::Impl& compiler, Module& parserModule);
const Compiler::Impl& getCompiler() const { return compiler; } Compiler::Impl& getCompiler() { return compiler; }
const ErrorReporter& getErrorReporter() const { return parserModule; } ErrorReporter& getErrorReporter() { return parserModule; }
ParsedFile::Reader getParsedFile() const { return content.getReader(); } ParsedFile::Reader getParsedFile() { return content.getReader(); }
const Node& getRootNode() const { return rootNode; } Node& getRootNode() { return rootNode; }
kj::StringPtr getSourceName() const { return parserModule.getSourceName(); } kj::StringPtr getSourceName() { return parserModule.getSourceName(); }
kj::Maybe<const CompiledModule&> importRelative(kj::StringPtr importPath) const; kj::Maybe<CompiledModule&> importRelative(kj::StringPtr importPath);
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>> Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
getFileImportTable(Orphanage orphanage) const; getFileImportTable(Orphanage orphanage);
private: private:
const Compiler::Impl& compiler; Compiler::Impl& compiler;
const Module& parserModule; Module& parserModule;
MallocMessageBuilder contentArena; MallocMessageBuilder contentArena;
Orphan<ParsedFile> content; Orphan<ParsedFile> content;
Node rootNode; Node rootNode;
...@@ -233,12 +240,12 @@ public: ...@@ -233,12 +240,12 @@ public:
explicit Impl(AnnotationFlag annotationFlag); explicit Impl(AnnotationFlag annotationFlag);
virtual ~Impl() noexcept(false); virtual ~Impl() noexcept(false);
uint64_t add(const Module& module) const; uint64_t add(Module& module);
kj::Maybe<uint64_t> lookup(uint64_t parent, kj::StringPtr childName) const; kj::Maybe<uint64_t> lookup(uint64_t parent, kj::StringPtr childName);
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>> Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
getFileImportTable(const Module& module, Orphanage orphanage) const; getFileImportTable(Module& module, Orphanage orphanage);
void eagerlyCompile(uint64_t id, uint eagerness) const; void eagerlyCompile(uint64_t id, uint eagerness, const SchemaLoader& loader);
const CompiledModule& addInternal(const Module& parsedModule) const; CompiledModule& addInternal(Module& parsedModule);
struct Workspace { struct Workspace {
// Scratch space where stuff can be allocated while working. The Workspace is available // Scratch space where stuff can be allocated while working. The Workspace is available
...@@ -267,33 +274,33 @@ public: ...@@ -267,33 +274,33 @@ public:
bootstrapLoader(loaderCallback) {} bootstrapLoader(loaderCallback) {}
}; };
const kj::Arena& getNodeArena() const { return nodeArena; } const kj::Arena& getNodeArena() { return nodeArena; }
// Arena where nodes and other permanent objects should be allocated. // Arena where nodes and other permanent objects should be allocated.
const SchemaLoader& getFinalLoader() const { return finalLoader; } const Workspace& getWorkspace() { return workspace; }
// Schema loader containing final versions of schemas. // Temporary workspace that can be used to construct bootstrap objects.
const Workspace& getWorkspace() const { return workspace.getAlreadyLockedShared(); } inline bool shouldCompileAnnotations() {
// Temporary workspace that can be used to construct bootstrap objects. We assume that the
// caller already holds the workspace lock somewhere up the stack.
inline bool shouldCompileAnnotations() const {
return annotationFlag == AnnotationFlag::COMPILE_ANNOTATIONS; return annotationFlag == AnnotationFlag::COMPILE_ANNOTATIONS;
} }
void clearWorkspace(); void clearWorkspace();
// Reset the temporary workspace. // Reset the temporary workspace.
uint64_t addNode(uint64_t desiredId, Node& node) const; 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 // 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 // already exists, choose a new one arbitrarily and use that instead. Return the ID that was
// finally used. // finally used.
kj::Maybe<const Node&> findNode(uint64_t id) const; kj::Maybe<Node&> findNode(uint64_t id);
kj::Maybe<const Node&> lookupBuiltin(kj::StringPtr name) const; kj::Maybe<Node&> lookupBuiltin(kj::StringPtr name);
void load(const SchemaLoader& loader, uint64_t id) const override; 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: private:
AnnotationFlag annotationFlag; AnnotationFlag annotationFlag;
...@@ -301,33 +308,26 @@ private: ...@@ -301,33 +308,26 @@ private:
kj::Arena nodeArena; kj::Arena nodeArena;
// Arena used to allocate nodes and other permanent objects. // Arena used to allocate nodes and other permanent objects.
SchemaLoader finalLoader; Workspace workspace;
// The loader where we put final output of the compiler.
kj::MutexGuarded<Workspace> workspace;
// The temporary workspace. // The temporary workspace.
typedef std::unordered_map<const Module*, kj::Own<CompiledModule>> ModuleMap; std::unordered_map<Module*, kj::Own<CompiledModule>> modules;
kj::MutexGuarded<ModuleMap> modules;
// Map of parser modules to compiler modules. // Map of parser modules to compiler modules.
typedef std::unordered_map<uint64_t, const Node*> NodeMap; std::unordered_map<uint64_t, Node*> nodesById;
kj::MutexGuarded<NodeMap> nodesById;
// Map of nodes by ID. // Map of nodes by ID.
std::map<kj::StringPtr, kj::Own<Node>> builtinDecls; std::map<kj::StringPtr, kj::Own<Node>> builtinDecls;
// Map of built-in declarations, like "Int32" and "List", which make up the global scope. // Map of built-in declarations, like "Int32" and "List", which make up the global scope.
mutable uint32_t nextBogusId = 1000; uint64_t nextBogusId = 1000;
// Counter for assigning bogus IDs to nodes whose real ID is a duplicate. 32-bit so that we // Counter for assigning bogus IDs to nodes whose real ID is a duplicate.
// can atomically increment it on 32-bit machines. It will never overflow since that would
// require compiling at least 2^32 nodes in one process.
}; };
// ======================================================================================= // =======================================================================================
kj::Maybe<const Compiler::Node&> Compiler::Alias::getTarget() const { kj::Maybe<Compiler::Node&> Compiler::Alias::getTarget() {
return target.get([this](kj::SpaceFor<kj::Maybe<const Node&>>& space) { return target.get([this](kj::SpaceFor<kj::Maybe<Node&>>& space) {
return space.construct(parent.lookup(targetName)); return space.construct(parent.lookup(targetName));
}); });
} }
...@@ -354,7 +354,7 @@ Compiler::Node::Node(CompiledModule& module) ...@@ -354,7 +354,7 @@ Compiler::Node::Node(CompiledModule& module)
id = module.getCompiler().addNode(id, *this); id = module.getCompiler().addNode(id, *this);
} }
Compiler::Node::Node(const Node& parent, const Declaration::Reader& declaration) Compiler::Node::Node(Node& parent, const Declaration::Reader& declaration)
: module(parent.module), : module(parent.module),
parent(parent), parent(parent),
declaration(declaration), declaration(declaration),
...@@ -395,7 +395,7 @@ uint64_t Compiler::Node::generateId(uint64_t parentId, kj::StringPtr declName, ...@@ -395,7 +395,7 @@ uint64_t Compiler::Node::generateId(uint64_t parentId, kj::StringPtr declName,
} }
kj::StringPtr Compiler::Node::joinDisplayName( kj::StringPtr Compiler::Node::joinDisplayName(
const kj::Arena& arena, const Node& parent, kj::StringPtr declName) { const kj::Arena& arena, Node& parent, kj::StringPtr declName) {
kj::ArrayPtr<char> result = arena.allocateArray<char>( kj::ArrayPtr<char> result = arena.allocateArray<char>(
parent.displayName.size() + declName.size() + 2); parent.displayName.size() + declName.size() + 2);
...@@ -407,16 +407,24 @@ kj::StringPtr Compiler::Node::joinDisplayName( ...@@ -407,16 +407,24 @@ kj::StringPtr Compiler::Node::joinDisplayName(
return kj::StringPtr(result.begin(), result.size() - 1); return kj::StringPtr(result.begin(), result.size() - 1);
} }
const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimumState) const { kj::Maybe<Compiler::Node::Content&> Compiler::Node::getContent(Content::State minimumState) {
KJ_REQUIRE(!isBuiltin, "illegal method call for built-in declaration"); KJ_REQUIRE(!isBuiltin, "illegal method call for built-in declaration");
if (content.getWithoutLock().stateHasReached(minimumState)) { auto& content = guardedContent;
return content.getWithoutLock();
if (content.stateHasReached(minimumState)) {
return content;
}
if (inGetContent) {
addError("Declaration recursively depends on itself.");
return nullptr;
} }
auto locked = content.lockExclusive(); inGetContent = true;
KJ_DEFER(inGetContent = false);
switch (locked->state) { switch (content.state) {
case Content::STUB: { case Content::STUB: {
if (minimumState <= Content::STUB) break; if (minimumState <= Content::STUB) break;
...@@ -433,8 +441,8 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum ...@@ -433,8 +441,8 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
case Declaration::INTERFACE: { case Declaration::INTERFACE: {
kj::Own<Node> subNode = arena.allocateOwn<Node>(*this, nestedDecl); kj::Own<Node> subNode = arena.allocateOwn<Node>(*this, nestedDecl);
kj::StringPtr name = nestedDecl.getName().getValue(); kj::StringPtr name = nestedDecl.getName().getValue();
locked->orderedNestedNodes.add(subNode); content.orderedNestedNodes.add(subNode);
locked->nestedNodes.insert(std::make_pair(name, kj::mv(subNode))); content.nestedNodes.insert(std::make_pair(name, kj::mv(subNode)));
break; break;
} }
...@@ -442,7 +450,7 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum ...@@ -442,7 +450,7 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
kj::Own<Alias> alias = arena.allocateOwn<Alias>( kj::Own<Alias> alias = arena.allocateOwn<Alias>(
*this, nestedDecl.getUsing().getTarget()); *this, nestedDecl.getUsing().getTarget());
kj::StringPtr name = nestedDecl.getName().getValue(); kj::StringPtr name = nestedDecl.getName().getValue();
locked->aliases.insert(std::make_pair(name, kj::mv(alias))); content.aliases.insert(std::make_pair(name, kj::mv(alias)));
break; break;
} }
case Declaration::ENUMERANT: case Declaration::ENUMERANT:
...@@ -460,7 +468,7 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum ...@@ -460,7 +468,7 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
} }
} }
locked->advanceState(Content::EXPANDED); content.advanceState(Content::EXPANDED);
// no break // no break
} }
...@@ -478,25 +486,25 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum ...@@ -478,25 +486,25 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
builder.setScopeId(p->id); builder.setScopeId(p->id);
} }
auto nestedNodes = builder.initNestedNodes(locked->orderedNestedNodes.size()); auto nestedNodes = builder.initNestedNodes(content.orderedNestedNodes.size());
auto nestedIter = nestedNodes.begin(); auto nestedIter = nestedNodes.begin();
for (auto node: locked->orderedNestedNodes) { for (auto node: content.orderedNestedNodes) {
nestedIter->setName(node->declaration.getName().getValue()); nestedIter->setName(node->declaration.getName().getValue());
nestedIter->setId(node->id); nestedIter->setId(node->id);
++nestedIter; ++nestedIter;
} }
locked->translator = &workspace.arena.allocate<NodeTranslator>( content.translator = &workspace.arena.allocate<NodeTranslator>(
*this, module->getErrorReporter(), declaration, kj::mv(schemaNode), *this, module->getErrorReporter(), declaration, kj::mv(schemaNode),
module->getCompiler().shouldCompileAnnotations()); module->getCompiler().shouldCompileAnnotations());
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&](){ KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&](){
auto nodeSet = locked->translator->getBootstrapNode(); auto nodeSet = content.translator->getBootstrapNode();
for (auto& auxNode: nodeSet.auxNodes) { for (auto& auxNode: nodeSet.auxNodes) {
workspace.bootstrapLoader.loadOnce(auxNode); workspace.bootstrapLoader.loadOnce(auxNode);
} }
locked->bootstrapSchema = workspace.bootstrapLoader.loadOnce(nodeSet.node); content.bootstrapSchema = workspace.bootstrapLoader.loadOnce(nodeSet.node);
})) { })) {
locked->bootstrapSchema = nullptr; content.bootstrapSchema = nullptr;
// Only bother to report validation failures if we think we haven't seen any errors. // 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. // Otherwise we assume that the errors caused the validation failure.
if (!module->getErrorReporter().hadErrors()) { if (!module->getErrorReporter().hadErrors()) {
...@@ -508,15 +516,14 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum ...@@ -508,15 +516,14 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
// If the Workspace is destroyed while this Node is still in the BOOTSTRAP state, // If the Workspace is destroyed while this Node is still in the BOOTSTRAP state,
// revert it to the EXPANDED state, because the NodeTranslator is no longer valid in this // revert it to the EXPANDED state, because the NodeTranslator is no longer valid in this
// case. // case.
Content* contentPtr = locked.get(); workspace.arena.copy(kj::defer([&content]() {
workspace.arena.copy(kj::defer([contentPtr]() { content.bootstrapSchema = nullptr;
contentPtr->bootstrapSchema = nullptr; if (content.state == Content::BOOTSTRAP) {
if (contentPtr->state == Content::BOOTSTRAP) { content.state = Content::EXPANDED;
contentPtr->state = Content::EXPANDED;
} }
})); }));
locked->advanceState(Content::BOOTSTRAP); content.advanceState(Content::BOOTSTRAP);
// no break // no break
} }
...@@ -524,24 +531,11 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum ...@@ -524,24 +531,11 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
if (minimumState <= Content::BOOTSTRAP) break; if (minimumState <= Content::BOOTSTRAP) break;
// Create the final schema. // Create the final schema.
auto nodeSet = locked->translator->finish(); auto nodeSet = content.translator->finish();
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&](){ content.finalSchema = nodeSet.node;
locked->auxSchemas = KJ_MAP(auxNode, nodeSet.auxNodes) { content.auxSchemas = kj::mv(nodeSet.auxNodes);
return module->getCompiler().getFinalLoader().loadOnce(auxNode);
};
locked->finalSchema = module->getCompiler().getFinalLoader().loadOnce(nodeSet.node);
})) {
locked->finalSchema = nullptr;
// Only bother to report validation failures if we think we haven't seen any errors. content.advanceState(Content::FINISHED);
// 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));
}
}
locked->advanceState(Content::FINISHED);
// no break // no break
} }
...@@ -549,29 +543,30 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum ...@@ -549,29 +543,30 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
break; break;
} }
return *locked; return content;
} }
kj::Maybe<const Compiler::Node&> Compiler::Node::lookupMember(kj::StringPtr name) const { kj::Maybe<Compiler::Node&> Compiler::Node::lookupMember(kj::StringPtr name) {
if (isBuiltin) return nullptr; if (isBuiltin) return nullptr;
auto& content = getContent(Content::EXPANDED); KJ_IF_MAYBE(content, getContent(Content::EXPANDED)) {
{ {
auto iter = content.nestedNodes.find(name); auto iter = content->nestedNodes.find(name);
if (iter != content.nestedNodes.end()) { if (iter != content->nestedNodes.end()) {
return *iter->second; return *iter->second;
} }
} }
{ {
auto iter = content.aliases.find(name); auto iter = content->aliases.find(name);
if (iter != content.aliases.end()) { if (iter != content->aliases.end()) {
return iter->second->getTarget(); return iter->second->getTarget();
} }
} }
}
return nullptr; return nullptr;
} }
kj::Maybe<const Compiler::Node&> Compiler::Node::lookupLexical(kj::StringPtr name) const { kj::Maybe<Compiler::Node&> Compiler::Node::lookupLexical(kj::StringPtr name) {
KJ_REQUIRE(!isBuiltin, "illegal method call for built-in declaration"); KJ_REQUIRE(!isBuiltin, "illegal method call for built-in declaration");
auto result = lookupMember(name); auto result = lookupMember(name);
...@@ -585,10 +580,10 @@ kj::Maybe<const Compiler::Node&> Compiler::Node::lookupLexical(kj::StringPtr nam ...@@ -585,10 +580,10 @@ kj::Maybe<const Compiler::Node&> Compiler::Node::lookupLexical(kj::StringPtr nam
return result; return result;
} }
kj::Maybe<const Compiler::Node&> Compiler::Node::lookup(const DeclName::Reader& name) const { kj::Maybe<Compiler::Node&> Compiler::Node::lookup(const DeclName::Reader& name) {
KJ_REQUIRE(!isBuiltin, "illegal method call for built-in declaration"); KJ_REQUIRE(!isBuiltin, "illegal method call for built-in declaration");
const Node* node = nullptr; Node* node = nullptr;
auto base = name.getBase(); auto base = name.getBase();
switch (base.which()) { switch (base.which()) {
...@@ -642,29 +637,55 @@ kj::Maybe<const Compiler::Node&> Compiler::Node::lookup(const DeclName::Reader& ...@@ -642,29 +637,55 @@ kj::Maybe<const Compiler::Node&> Compiler::Node::lookup(const DeclName::Reader&
return *node; return *node;
} }
kj::Maybe<Schema> Compiler::Node::getBootstrapSchema() const { kj::Maybe<Schema> Compiler::Node::getBootstrapSchema() {
auto& content = getContent(Content::BOOTSTRAP); KJ_IF_MAYBE(content, getContent(Content::BOOTSTRAP)) {
if (content->state == Content::FINISHED && content->bootstrapSchema == nullptr) {
if (__atomic_load_n(&content.state, __ATOMIC_ACQUIRE) == Content::FINISHED &&
content.bootstrapSchema == nullptr) {
// The bootstrap schema was discarded. Copy it from the final schema. // 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 // (We can't just return the final schema because using it could trigger schema loader
// callbacks that would deadlock.) // callbacks that would deadlock.)
KJ_IF_MAYBE(finalSchema, content.finalSchema) { KJ_IF_MAYBE(finalSchema, content->finalSchema) {
return module->getCompiler().getWorkspace().bootstrapLoader.loadOnce(finalSchema->getProto()); return module->getCompiler().getWorkspace().bootstrapLoader.loadOnce(*finalSchema);
} else { } else {
return nullptr; return nullptr;
} }
} else { } else {
return content.bootstrapSchema; return content->bootstrapSchema;
}
} else {
return nullptr;
}
}
kj::Maybe<schema::Node::Reader> Compiler::Node::getFinalSchema() {
KJ_IF_MAYBE(content, getContent(Content::FINISHED)) {
return content->finalSchema;
} else {
return nullptr;
} }
} }
kj::Maybe<schema::Node::Reader> Compiler::Node::getFinalSchema() const { void Compiler::Node::loadFinalSchema(const SchemaLoader& loader) {
return getContent(Content::FINISHED).finalSchema.map( KJ_IF_MAYBE(content, getContent(Content::FINISHED)) {
[](const Schema& schema) { return schema.getProto(); }); KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&](){
KJ_IF_MAYBE(finalSchema, content->finalSchema) {
KJ_MAP(auxSchema, content->auxSchemas) {
return loader.loadOnce(auxSchema);
};
loader.loadOnce(*finalSchema);
}
})) {
// 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<const Node*, uint>& seen) const { void Compiler::Node::traverse(uint eagerness, std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader) {
uint& slot = seen[this]; uint& slot = seen[this];
if ((slot & eagerness) == eagerness) { if ((slot & eagerness) == eagerness) {
// We've already covered this node. // We've already covered this node.
...@@ -672,54 +693,61 @@ void Compiler::Node::traverse(uint eagerness, std::unordered_map<const Node*, ui ...@@ -672,54 +693,61 @@ void Compiler::Node::traverse(uint eagerness, std::unordered_map<const Node*, ui
} }
slot |= eagerness; slot |= eagerness;
KJ_IF_MAYBE(content, getContent(Content::FINISHED)) {
loadFinalSchema(finalLoader);
KJ_IF_MAYBE(schema, getFinalSchema()) { KJ_IF_MAYBE(schema, getFinalSchema()) {
if (eagerness / DEPENDENCIES != 0) { if (eagerness / DEPENDENCIES != 0) {
// For traversing dependencies, discard the bits lower than DEPENDENCIES and replace // For traversing dependencies, discard the bits lower than DEPENDENCIES and replace
// them with the bits above DEPENDENCIES shifted over. // them with the bits above DEPENDENCIES shifted over.
uint newEagerness = (eagerness & ~(DEPENDENCIES - 1)) | (eagerness / DEPENDENCIES); uint newEagerness = (eagerness & ~(DEPENDENCIES - 1)) | (eagerness / DEPENDENCIES);
traverseNodeDependencies(*schema, newEagerness, seen); traverseNodeDependencies(*schema, newEagerness, seen, finalLoader);
for (auto& aux: getContent(Content::FINISHED).auxSchemas) { for (auto& aux: content->auxSchemas) {
traverseNodeDependencies(aux.getProto(), newEagerness, seen); traverseNodeDependencies(aux, newEagerness, seen, finalLoader);
}
} }
} }
} }
if (eagerness & PARENTS) { if (eagerness & PARENTS) {
KJ_IF_MAYBE(p, parent) { KJ_IF_MAYBE(p, parent) {
p->traverse(eagerness, seen); p->traverse(eagerness, seen, finalLoader);
} }
} }
if (eagerness & CHILDREN) { if (eagerness & CHILDREN) {
for (auto& child: getContent(Content::EXPANDED).orderedNestedNodes) { KJ_IF_MAYBE(content, getContent(Content::EXPANDED)) {
child->traverse(eagerness, seen); for (auto& child: content->orderedNestedNodes) {
child->traverse(eagerness, seen, finalLoader);
}
} }
} }
} }
void Compiler::Node::traverseNodeDependencies( void Compiler::Node::traverseNodeDependencies(
const schema::Node::Reader& schemaNode, uint eagerness, const schema::Node::Reader& schemaNode, uint eagerness,
std::unordered_map<const Node*, uint>& seen) const { std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader) {
switch (schemaNode.which()) { switch (schemaNode.which()) {
case schema::Node::STRUCT: case schema::Node::STRUCT:
for (auto field: schemaNode.getStruct().getFields()) { for (auto field: schemaNode.getStruct().getFields()) {
switch (field.which()) { switch (field.which()) {
case schema::Field::SLOT: case schema::Field::SLOT:
traverseType(field.getSlot().getType(), eagerness, seen); traverseType(field.getSlot().getType(), eagerness, seen, finalLoader);
break; break;
case schema::Field::GROUP: case schema::Field::GROUP:
// Aux node will be scanned later. // Aux node will be scanned later.
break; break;
} }
traverseAnnotations(field.getAnnotations(), eagerness, seen); traverseAnnotations(field.getAnnotations(), eagerness, seen, finalLoader);
} }
break; break;
case schema::Node::ENUM: case schema::Node::ENUM:
for (auto enumerant: schemaNode.getEnum().getEnumerants()) { for (auto enumerant: schemaNode.getEnum().getEnumerants()) {
traverseAnnotations(enumerant.getAnnotations(), eagerness, seen); traverseAnnotations(enumerant.getAnnotations(), eagerness, seen, finalLoader);
} }
break; break;
...@@ -727,13 +755,13 @@ void Compiler::Node::traverseNodeDependencies( ...@@ -727,13 +755,13 @@ void Compiler::Node::traverseNodeDependencies(
auto interface = schemaNode.getInterface(); auto interface = schemaNode.getInterface();
for (auto extend: interface.getExtends()) { for (auto extend: interface.getExtends()) {
if (extend != 0) { // if zero, we reported an error earlier if (extend != 0) { // if zero, we reported an error earlier
traverseDependency(extend, eagerness, seen); traverseDependency(extend, eagerness, seen, finalLoader);
} }
} }
for (auto method: interface.getMethods()) { for (auto method: interface.getMethods()) {
traverseDependency(method.getParamStructType(), eagerness, seen, true); traverseDependency(method.getParamStructType(), eagerness, seen, finalLoader, true);
traverseDependency(method.getResultStructType(), eagerness, seen, true); traverseDependency(method.getResultStructType(), eagerness, seen, finalLoader, true);
traverseAnnotations(method.getAnnotations(), eagerness, seen); traverseAnnotations(method.getAnnotations(), eagerness, seen, finalLoader);
} }
break; break;
} }
...@@ -742,11 +770,12 @@ void Compiler::Node::traverseNodeDependencies( ...@@ -742,11 +770,12 @@ void Compiler::Node::traverseNodeDependencies(
break; break;
} }
traverseAnnotations(schemaNode.getAnnotations(), eagerness, seen); traverseAnnotations(schemaNode.getAnnotations(), eagerness, seen, finalLoader);
} }
void Compiler::Node::traverseType(const schema::Type::Reader& type, uint eagerness, void Compiler::Node::traverseType(const schema::Type::Reader& type, uint eagerness,
std::unordered_map<const Node*, uint>& seen) const { std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader) {
uint64_t id = 0; uint64_t id = 0;
switch (type.which()) { switch (type.which()) {
case schema::Type::STRUCT: case schema::Type::STRUCT:
...@@ -759,20 +788,21 @@ void Compiler::Node::traverseType(const schema::Type::Reader& type, uint eagerne ...@@ -759,20 +788,21 @@ void Compiler::Node::traverseType(const schema::Type::Reader& type, uint eagerne
id = type.getInterface().getTypeId(); id = type.getInterface().getTypeId();
break; break;
case schema::Type::LIST: case schema::Type::LIST:
traverseType(type.getList().getElementType(), eagerness, seen); traverseType(type.getList().getElementType(), eagerness, seen, finalLoader);
return; return;
default: default:
return; return;
} }
traverseDependency(id, eagerness, seen); traverseDependency(id, eagerness, seen, finalLoader);
} }
void Compiler::Node::traverseDependency(uint64_t depId, uint eagerness, void Compiler::Node::traverseDependency(uint64_t depId, uint eagerness,
std::unordered_map<const Node*, uint>& seen, std::unordered_map<Node*, uint>& seen,
bool ignoreIfNotFound) const { const SchemaLoader& finalLoader,
bool ignoreIfNotFound) {
KJ_IF_MAYBE(node, module->getCompiler().findNode(depId)) { KJ_IF_MAYBE(node, module->getCompiler().findNode(depId)) {
node->traverse(eagerness, seen); node->traverse(eagerness, seen, finalLoader);
} else if (!ignoreIfNotFound) { } else if (!ignoreIfNotFound) {
KJ_FAIL_ASSERT("Dependency ID not present in compiler?", depId); KJ_FAIL_ASSERT("Dependency ID not present in compiler?", depId);
} }
...@@ -780,27 +810,28 @@ void Compiler::Node::traverseDependency(uint64_t depId, uint eagerness, ...@@ -780,27 +810,28 @@ void Compiler::Node::traverseDependency(uint64_t depId, uint eagerness,
void Compiler::Node::traverseAnnotations(const List<schema::Annotation>::Reader& annotations, void Compiler::Node::traverseAnnotations(const List<schema::Annotation>::Reader& annotations,
uint eagerness, uint eagerness,
std::unordered_map<const Node*, uint>& seen) const { std::unordered_map<Node*, uint>& seen,
const SchemaLoader& finalLoader) {
for (auto annotation: annotations) { for (auto annotation: annotations) {
KJ_IF_MAYBE(node, module->getCompiler().findNode(annotation.getId())) { KJ_IF_MAYBE(node, module->getCompiler().findNode(annotation.getId())) {
node->traverse(eagerness, seen); node->traverse(eagerness, seen, finalLoader);
} }
} }
} }
void Compiler::Node::addError(kj::StringPtr error) const { void Compiler::Node::addError(kj::StringPtr error) {
module->getErrorReporter().addError(startByte, endByte, error); module->getErrorReporter().addError(startByte, endByte, error);
} }
kj::Maybe<NodeTranslator::Resolver::ResolvedName> Compiler::Node::resolve( kj::Maybe<NodeTranslator::Resolver::ResolvedName> Compiler::Node::resolve(
const DeclName::Reader& name) const { const DeclName::Reader& name) {
return lookup(name).map([](const Node& node) { return lookup(name).map([](Node& node) {
return ResolvedName { node.id, node.kind }; return ResolvedName { node.id, node.kind };
}); });
} }
kj::Maybe<Schema> Compiler::Node::resolveBootstrapSchema(uint64_t id) const { kj::Maybe<Schema> Compiler::Node::resolveBootstrapSchema(uint64_t id) {
KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) { KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
return node->getBootstrapSchema(); return node->getBootstrapSchema();
} else { } else {
...@@ -808,7 +839,7 @@ kj::Maybe<Schema> Compiler::Node::resolveBootstrapSchema(uint64_t id) const { ...@@ -808,7 +839,7 @@ kj::Maybe<Schema> Compiler::Node::resolveBootstrapSchema(uint64_t id) const {
} }
} }
kj::Maybe<schema::Node::Reader> Compiler::Node::resolveFinalSchema(uint64_t id) const { kj::Maybe<schema::Node::Reader> Compiler::Node::resolveFinalSchema(uint64_t id) {
KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) { KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
return node->getFinalSchema(); return node->getFinalSchema();
} else { } else {
...@@ -816,7 +847,7 @@ kj::Maybe<schema::Node::Reader> Compiler::Node::resolveFinalSchema(uint64_t id) ...@@ -816,7 +847,7 @@ kj::Maybe<schema::Node::Reader> Compiler::Node::resolveFinalSchema(uint64_t id)
} }
} }
kj::Maybe<uint64_t> Compiler::Node::resolveImport(kj::StringPtr name) const { kj::Maybe<uint64_t> Compiler::Node::resolveImport(kj::StringPtr name) {
KJ_IF_MAYBE(m, module->importRelative(name)) { KJ_IF_MAYBE(m, module->importRelative(name)) {
return m->getRootNode().getId(); return m->getRootNode().getId();
} else { } else {
...@@ -826,16 +857,15 @@ kj::Maybe<uint64_t> Compiler::Node::resolveImport(kj::StringPtr name) const { ...@@ -826,16 +857,15 @@ kj::Maybe<uint64_t> Compiler::Node::resolveImport(kj::StringPtr name) const {
// ======================================================================================= // =======================================================================================
Compiler::CompiledModule::CompiledModule( Compiler::CompiledModule::CompiledModule(Compiler::Impl& compiler, Module& parserModule)
const Compiler::Impl& compiler, const Module& parserModule)
: compiler(compiler), parserModule(parserModule), : compiler(compiler), parserModule(parserModule),
content(parserModule.loadContent(contentArena.getOrphanage())), content(parserModule.loadContent(contentArena.getOrphanage())),
rootNode(*this) {} rootNode(*this) {}
kj::Maybe<const Compiler::CompiledModule&> Compiler::CompiledModule::importRelative( kj::Maybe<Compiler::CompiledModule&> Compiler::CompiledModule::importRelative(
kj::StringPtr importPath) const { kj::StringPtr importPath) {
return parserModule.importRelative(importPath).map( return parserModule.importRelative(importPath).map(
[this](const Module& module) -> const Compiler::CompiledModule& { [this](Module& module) -> Compiler::CompiledModule& {
return compiler.addInternal(module); return compiler.addInternal(module);
}); });
} }
...@@ -913,7 +943,7 @@ static void findImports(Declaration::Reader decl, std::set<kj::StringPtr>& outpu ...@@ -913,7 +943,7 @@ static void findImports(Declaration::Reader decl, std::set<kj::StringPtr>& outpu
} }
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>> Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
Compiler::CompiledModule::getFileImportTable(Orphanage orphanage) const { Compiler::CompiledModule::getFileImportTable(Orphanage orphanage) {
std::set<kj::StringPtr> importNames; std::set<kj::StringPtr> importNames;
findImports(content.getReader().getRoot(), importNames); findImports(content.getReader().getRoot(), importNames);
...@@ -935,7 +965,7 @@ Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>> ...@@ -935,7 +965,7 @@ Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
// ======================================================================================= // =======================================================================================
Compiler::Impl::Impl(AnnotationFlag annotationFlag) Compiler::Impl::Impl(AnnotationFlag annotationFlag)
: annotationFlag(annotationFlag), finalLoader(*this), workspace(*this) { : annotationFlag(annotationFlag), workspace(*this) {
// Reflectively interpret the members of Declaration.body. Any member prefixed by "builtin" // Reflectively interpret the members of Declaration.body. Any member prefixed by "builtin"
// defines a builtin declaration visible in the global scope. // defines a builtin declaration visible in the global scope.
...@@ -956,17 +986,13 @@ Compiler::Impl::Impl(AnnotationFlag annotationFlag) ...@@ -956,17 +986,13 @@ Compiler::Impl::Impl(AnnotationFlag annotationFlag)
Compiler::Impl::~Impl() noexcept(false) {} Compiler::Impl::~Impl() noexcept(false) {}
void Compiler::Impl::clearWorkspace() { void Compiler::Impl::clearWorkspace() {
auto lock = workspace.lockExclusive();
// Make sure we reconstruct the workspace even if destroying it throws an exception. // Make sure we reconstruct the workspace even if destroying it throws an exception.
KJ_DEFER(kj::ctor(*lock, *this)); KJ_DEFER(kj::ctor(workspace, *this));
kj::dtor(*lock); kj::dtor(workspace);
} }
const Compiler::CompiledModule& Compiler::Impl::addInternal(const Module& parsedModule) const { Compiler::CompiledModule& Compiler::Impl::addInternal(Module& parsedModule) {
auto locked = modules.lockExclusive(); kj::Own<CompiledModule>& slot = modules[&parsedModule];
kj::Own<CompiledModule>& slot = (*locked)[&parsedModule];
if (slot.get() == nullptr) { if (slot.get() == nullptr) {
slot = kj::heap<CompiledModule>(*this, parsedModule); slot = kj::heap<CompiledModule>(*this, parsedModule);
} }
...@@ -974,10 +1000,9 @@ const Compiler::CompiledModule& Compiler::Impl::addInternal(const Module& parsed ...@@ -974,10 +1000,9 @@ const Compiler::CompiledModule& Compiler::Impl::addInternal(const Module& parsed
return *slot; return *slot;
} }
uint64_t Compiler::Impl::addNode(uint64_t desiredId, Node& node) const { uint64_t Compiler::Impl::addNode(uint64_t desiredId, Node& node) {
auto lock = nodesById.lockExclusive();
for (;;) { for (;;) {
auto insertResult = lock->insert(std::make_pair(desiredId, &node)); auto insertResult = nodesById.insert(std::make_pair(desiredId, &node));
if (insertResult.second) { if (insertResult.second) {
return desiredId; return desiredId;
} }
...@@ -992,21 +1017,20 @@ uint64_t Compiler::Impl::addNode(uint64_t desiredId, Node& node) const { ...@@ -992,21 +1017,20 @@ uint64_t Compiler::Impl::addNode(uint64_t desiredId, Node& node) const {
} }
// Assign a new bogus ID. // Assign a new bogus ID.
desiredId = __atomic_fetch_add(&nextBogusId, 1, __ATOMIC_RELAXED); desiredId = nextBogusId++;
} }
} }
kj::Maybe<const Compiler::Node&> Compiler::Impl::findNode(uint64_t id) const { kj::Maybe<Compiler::Node&> Compiler::Impl::findNode(uint64_t id) {
auto lock = nodesById.lockShared(); auto iter = nodesById.find(id);
auto iter = lock->find(id); if (iter == nodesById.end()) {
if (iter == lock->end()) {
return nullptr; return nullptr;
} else { } else {
return *iter->second; return *iter->second;
} }
} }
kj::Maybe<const Compiler::Node&> Compiler::Impl::lookupBuiltin(kj::StringPtr name) const { kj::Maybe<Compiler::Node&> Compiler::Impl::lookupBuiltin(kj::StringPtr name) {
auto iter = builtinDecls.find(name); auto iter = builtinDecls.find(name);
if (iter == builtinDecls.end()) { if (iter == builtinDecls.end()) {
return nullptr; return nullptr;
...@@ -1015,11 +1039,11 @@ kj::Maybe<const Compiler::Node&> Compiler::Impl::lookupBuiltin(kj::StringPtr nam ...@@ -1015,11 +1039,11 @@ kj::Maybe<const Compiler::Node&> Compiler::Impl::lookupBuiltin(kj::StringPtr nam
} }
} }
uint64_t Compiler::Impl::add(const Module& module) const { uint64_t Compiler::Impl::add(Module& module) {
return addInternal(module).getRootNode().getId(); return addInternal(module).getRootNode().getId();
} }
kj::Maybe<uint64_t> Compiler::Impl::lookup(uint64_t parent, kj::StringPtr childName) const { 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. // 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(parentNode, findNode(parent)) {
KJ_IF_MAYBE(child, parentNode->lookupMember(childName)) { KJ_IF_MAYBE(child, parentNode->lookupMember(childName)) {
...@@ -1033,61 +1057,75 @@ kj::Maybe<uint64_t> Compiler::Impl::lookup(uint64_t parent, kj::StringPtr childN ...@@ -1033,61 +1057,75 @@ kj::Maybe<uint64_t> Compiler::Impl::lookup(uint64_t parent, kj::StringPtr childN
} }
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>> Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
Compiler::Impl::getFileImportTable(const Module& module, Orphanage orphanage) const { Compiler::Impl::getFileImportTable(Module& module, Orphanage orphanage) {
return addInternal(module).getFileImportTable(orphanage); return addInternal(module).getFileImportTable(orphanage);
} }
void Compiler::Impl::eagerlyCompile(uint64_t id, uint eagerness) const { void Compiler::Impl::eagerlyCompile(uint64_t id, uint eagerness,
const SchemaLoader& finalLoader) {
KJ_IF_MAYBE(node, findNode(id)) { KJ_IF_MAYBE(node, findNode(id)) {
auto lock = this->workspace.lockShared(); std::unordered_map<Node*, uint> seen;
std::unordered_map<const Node*, uint> seen; node->traverse(eagerness, seen, finalLoader);
node->traverse(eagerness, seen);
} else { } else {
KJ_FAIL_REQUIRE("id did not come from this Compiler.", id); KJ_FAIL_REQUIRE("id did not come from this Compiler.", id);
} }
} }
void Compiler::Impl::load(const SchemaLoader& loader, uint64_t id) const { void Compiler::Impl::load(const SchemaLoader& loader, uint64_t id) const {
KJ_IF_MAYBE(node, findNode(id)) { // We know that this load() is only called from the bootstrap loader which is already protected
if (&loader == &finalLoader) { // by our mutex, so we can drop thread-safety.
auto lock = this->workspace.lockShared(); auto& self = const_cast<Compiler::Impl&>(*this);
node->getFinalSchema();
} else { KJ_IF_MAYBE(node, self.findNode(id)) {
// Must be the bootstrap loader. Workspace should already be locked.
this->workspace.getAlreadyLockedShared();
node->getBootstrapSchema(); node->getBootstrapSchema();
} }
}
void Compiler::Impl::loadFinal(const SchemaLoader& loader, uint64_t id) {
KJ_IF_MAYBE(node, findNode(id)) {
KJ_IF_MAYBE(schema, node->getFinalSchema()) {
loader.loadOnce(*schema);
}
// Schema loads can happen lazily while using the dynamic API. The caller doesn't necessarily
// know that the compiler was invoked and so won't know to clear the workspace. Probably, if
// we don't clear the workspace, it will waste memory for the lifetime of the process, whereas
// if we do clear it, we're only imposing a little more work at startup time / the first time
// each type is used.
clearWorkspace();
} }
} }
// ======================================================================================= // =======================================================================================
Compiler::Compiler(AnnotationFlag annotationFlag): impl(kj::heap<Impl>(annotationFlag)) {} Compiler::Compiler(AnnotationFlag annotationFlag)
: impl(kj::heap<Impl>(annotationFlag)),
loader(*this) {}
Compiler::~Compiler() noexcept(false) {} Compiler::~Compiler() noexcept(false) {}
uint64_t Compiler::add(const Module& module) const { uint64_t Compiler::add(Module& module) const {
return impl->add(module); return impl.lockExclusive()->get()->add(module);
} }
kj::Maybe<uint64_t> Compiler::lookup(uint64_t parent, kj::StringPtr childName) const { kj::Maybe<uint64_t> Compiler::lookup(uint64_t parent, kj::StringPtr childName) const {
return impl->lookup(parent, childName); return impl.lockExclusive()->get()->lookup(parent, childName);
} }
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>> Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
Compiler::getFileImportTable(const Module& module, Orphanage orphanage) const { Compiler::getFileImportTable(Module& module, Orphanage orphanage) const {
return impl->getFileImportTable(module, orphanage); return impl.lockExclusive()->get()->getFileImportTable(module, orphanage);
} }
void Compiler::eagerlyCompile(uint64_t id, uint eagerness) const { void Compiler::eagerlyCompile(uint64_t id, uint eagerness) const {
return impl->eagerlyCompile(id, eagerness); impl.lockExclusive()->get()->eagerlyCompile(id, eagerness, loader);
} }
const SchemaLoader& Compiler::getLoader() const { void Compiler::clearWorkspace() const {
return impl->getFinalLoader(); impl.lockExclusive()->get()->clearWorkspace();
} }
void Compiler::clearWorkspace() { void Compiler::load(const SchemaLoader& loader, uint64_t id) const {
impl->clearWorkspace(); impl.lockExclusive()->get()->loadFinal(loader, id);
} }
} // namespace compiler } // namespace compiler
......
...@@ -34,21 +34,23 @@ namespace compiler { ...@@ -34,21 +34,23 @@ namespace compiler {
class Module: public ErrorReporter { class Module: public ErrorReporter {
public: public:
virtual kj::StringPtr getSourceName() const = 0; virtual kj::StringPtr getSourceName() = 0;
// The name of the module file relative to the source tree. Used to decide where to output // The name of the module file relative to the source tree. Used to decide where to output
// generated code and to form the `displayName` in the schema. // generated code and to form the `displayName` in the schema.
virtual Orphan<ParsedFile> loadContent(Orphanage orphanage) const = 0; virtual Orphan<ParsedFile> loadContent(Orphanage orphanage) = 0;
// Loads the module content, using the given orphanage to allocate objects if necessary. // Loads the module content, using the given orphanage to allocate objects if necessary.
virtual kj::Maybe<const Module&> importRelative(kj::StringPtr importPath) const = 0; virtual kj::Maybe<Module&> importRelative(kj::StringPtr importPath) = 0;
// Find another module, relative to this one. Importing the same logical module twice should // Find another module, relative to this one. Importing the same logical module twice should
// produce the exact same object, comparable by identity. These objects are owned by some // produce the exact same object, comparable by identity. These objects are owned by some
// outside pool that outlives the Compiler instance. // outside pool that outlives the Compiler instance.
}; };
class Compiler { class Compiler: private SchemaLoader::LazyLoadCallback {
// Cross-links separate modules (schema files) and translates them into schema nodes. // Cross-links separate modules (schema files) and translates them into schema nodes.
//
// This class is thread-safe, hence all its methods are const.
public: public:
enum AnnotationFlag { enum AnnotationFlag {
...@@ -71,7 +73,7 @@ public: ...@@ -71,7 +73,7 @@ public:
~Compiler() noexcept(false); ~Compiler() noexcept(false);
KJ_DISALLOW_COPY(Compiler); KJ_DISALLOW_COPY(Compiler);
uint64_t add(const Module& module) const; uint64_t add(Module& module) const;
// Add a module to the Compiler, returning the module's file ID. The ID can then be looked up in // Add a module to the Compiler, returning the module's file ID. The ID can then be looked up in
// the `SchemaLoader` returned by `getLoader()`. However, the SchemaLoader may behave as if the // the `SchemaLoader` returned by `getLoader()`. However, the SchemaLoader may behave as if the
// schema node doesn't exist if any compilation errors occur (reported via the module's // schema node doesn't exist if any compilation errors occur (reported via the module's
...@@ -85,7 +87,7 @@ public: ...@@ -85,7 +87,7 @@ public:
// given name. Neither the parent nor the child schema node is actually compiled. // given name. Neither the parent nor the child schema node is actually compiled.
Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>> Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
getFileImportTable(const Module& module, Orphanage orphanage) const; getFileImportTable(Module& module, Orphanage orphanage) const;
// Build the import table for the CodeGeneratorRequest for the given module. // Build the import table for the CodeGeneratorRequest for the given module.
enum Eagerness: uint32_t { enum Eagerness: uint32_t {
...@@ -159,11 +161,11 @@ public: ...@@ -159,11 +161,11 @@ public:
// If this returns and no errors have been reported, then it is guaranteed that the compiled // If this returns and no errors have been reported, then it is guaranteed that the compiled
// nodes can be found in the SchemaLoader returned by `getLoader()`. // nodes can be found in the SchemaLoader returned by `getLoader()`.
const SchemaLoader& getLoader() const; const SchemaLoader& getLoader() const { return loader; }
// Get a SchemaLoader backed by this compiler. Schema nodes will be lazily constructed as you // Get a SchemaLoader backed by this compiler. Schema nodes will be lazily constructed as you
// traverse them using this loader. // traverse them using this loader.
void clearWorkspace(); void clearWorkspace() const;
// The compiler builds a lot of temporary tables and data structures while it works. It's // The compiler builds a lot of temporary tables and data structures while it works. It's
// useful to keep these around if more work is expected (especially if you are using lazy // useful to keep these around if more work is expected (especially if you are using lazy
// compilation and plan to look up Schema nodes that haven't already been seen), but once // compilation and plan to look up Schema nodes that haven't already been seen), but once
...@@ -174,11 +176,14 @@ public: ...@@ -174,11 +176,14 @@ public:
private: private:
class Impl; class Impl;
kj::Own<Impl> impl; kj::MutexGuarded<kj::Own<Impl>> impl;
SchemaLoader loader;
class CompiledModule; class CompiledModule;
class Node; class Node;
class Alias; class Alias;
void load(const SchemaLoader& loader, uint64_t id) const override;
}; };
} // namespace compiler } // namespace compiler
......
...@@ -36,20 +36,20 @@ class ErrorReporter { ...@@ -36,20 +36,20 @@ class ErrorReporter {
// Callback for reporting errors within a particular file. // Callback for reporting errors within a particular file.
public: public:
virtual void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const = 0; virtual void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) = 0;
// Report an error at the given location in the input text. `startByte` and `endByte` indicate // Report an error at the given location in the input text. `startByte` and `endByte` indicate
// the span of text that is erroneous. They may be equal, in which case the parser was only // the span of text that is erroneous. They may be equal, in which case the parser was only
// able to identify where the error begins, not where it ends. // able to identify where the error begins, not where it ends.
template <typename T> template <typename T>
inline void addErrorOn(T&& decl, kj::StringPtr message) const { inline void addErrorOn(T&& decl, kj::StringPtr message) {
// Works for any `T` that defines `getStartByte()` and `getEndByte()` methods, which many // Works for any `T` that defines `getStartByte()` and `getEndByte()` methods, which many
// of the Cap'n Proto types defined in `grammar.capnp` do. // of the Cap'n Proto types defined in `grammar.capnp` do.
addError(decl.getStartByte(), decl.getEndByte(), message); addError(decl.getStartByte(), decl.getEndByte(), message);
} }
virtual bool hadErrors() const = 0; virtual bool hadErrors() = 0;
// Return true if any errors have been reported, globally. The main use case for this callback // Return true if any errors have been reported, globally. The main use case for this callback
// is to inhibit the reporting of errors which may have been caused by previous errors, or to // is to inhibit the reporting of errors which may have been caused by previous errors, or to
// allow the compiler to bail out entirely if it gets confused and thinks this could be because // allow the compiler to bail out entirely if it gets confused and thinks this could be because
...@@ -67,10 +67,10 @@ public: ...@@ -67,10 +67,10 @@ public:
}; };
virtual void addError(kj::StringPtr file, SourcePos start, SourcePos end, virtual void addError(kj::StringPtr file, SourcePos start, SourcePos end,
kj::StringPtr message) const = 0; kj::StringPtr message) = 0;
// Report an error at the given location in the given file. // Report an error at the given location in the given file.
virtual bool hadErrors() const = 0; virtual bool hadErrors() = 0;
// Return true if any errors have been reported, globally. The main use case for this callback // Return true if any errors have been reported, globally. The main use case for this callback
// is to inhibit the reporting of errors which may have been caused by previous errors, or to // is to inhibit the reporting of errors which may have been caused by previous errors, or to
// allow the compiler to bail out entirely if it gets confused and thinks this could be because // allow the compiler to bail out entirely if it gets confused and thinks this could be because
......
...@@ -632,18 +632,18 @@ class ModuleImpl final: public Module { ...@@ -632,18 +632,18 @@ class ModuleImpl final: public Module {
public: public:
explicit ModuleImpl(ParsedFile::Reader content): content(content) {} explicit ModuleImpl(ParsedFile::Reader content): content(content) {}
kj::StringPtr getSourceName() const override { return "evolving-schema.capnp"; } kj::StringPtr getSourceName() override { return "evolving-schema.capnp"; }
Orphan<ParsedFile> loadContent(Orphanage orphanage) const override { Orphan<ParsedFile> loadContent(Orphanage orphanage) override {
return orphanage.newOrphanCopy(content); return orphanage.newOrphanCopy(content);
} }
kj::Maybe<const Module&> importRelative(kj::StringPtr importPath) const override { kj::Maybe<Module&> importRelative(kj::StringPtr importPath) override {
return nullptr; return nullptr;
} }
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const override { void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override {
KJ_FAIL_ASSERT("Unexpected parse error.", startByte, endByte, message); KJ_FAIL_ASSERT("Unexpected parse error.", startByte, endByte, message);
} }
bool hadErrors() const override { bool hadErrors() override {
return false; return false;
} }
......
...@@ -31,11 +31,11 @@ namespace { ...@@ -31,11 +31,11 @@ namespace {
class TestFailingErrorReporter: public ErrorReporter { class TestFailingErrorReporter: public ErrorReporter {
public: public:
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const override { void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override {
ADD_FAILURE() << "Parse failed: (" << startByte << "-" << endByte << ") " << message.cStr(); ADD_FAILURE() << "Parse failed: (" << startByte << "-" << endByte << ") " << message.cStr();
} }
bool hadErrors() const override { bool hadErrors() override {
// Not used by lexer. // Not used by lexer.
return false; return false;
} }
......
...@@ -31,7 +31,7 @@ namespace compiler { ...@@ -31,7 +31,7 @@ namespace compiler {
namespace p = kj::parse; namespace p = kj::parse;
bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result, bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
const ErrorReporter& errorReporter) { ErrorReporter& errorReporter) {
Lexer lexer(Orphanage::getForMessageContaining(result), errorReporter); Lexer lexer(Orphanage::getForMessageContaining(result), errorReporter);
auto parser = p::sequence(lexer.getParsers().statementSequence, p::endOfInput); auto parser = p::sequence(lexer.getParsers().statementSequence, p::endOfInput);
...@@ -53,7 +53,7 @@ bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result, ...@@ -53,7 +53,7 @@ bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
} }
bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result, bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result,
const ErrorReporter& errorReporter) { ErrorReporter& errorReporter) {
Lexer lexer(Orphanage::getForMessageContaining(result), errorReporter); Lexer lexer(Orphanage::getForMessageContaining(result), errorReporter);
auto parser = p::sequence(lexer.getParsers().tokenSequence, p::endOfInput); auto parser = p::sequence(lexer.getParsers().tokenSequence, p::endOfInput);
...@@ -138,7 +138,7 @@ constexpr auto docComment = p::optional(p::sequence( ...@@ -138,7 +138,7 @@ constexpr auto docComment = p::optional(p::sequence(
} // namespace } // namespace
Lexer::Lexer(Orphanage orphanageParam, const ErrorReporter& errorReporterParam) Lexer::Lexer(Orphanage orphanageParam, ErrorReporter& errorReporterParam)
: orphanage(orphanageParam) { : orphanage(orphanageParam) {
// Note that because passing an lvalue to a parser constructor uses it by-referencee, it's safe // Note that because passing an lvalue to a parser constructor uses it by-referencee, it's safe
......
...@@ -33,9 +33,8 @@ namespace capnp { ...@@ -33,9 +33,8 @@ namespace capnp {
namespace compiler { namespace compiler {
bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result, bool lex(kj::ArrayPtr<const char> input, LexedStatements::Builder result,
const ErrorReporter& errorReporter); ErrorReporter& errorReporter);
bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result, bool lex(kj::ArrayPtr<const char> input, LexedTokens::Builder result, ErrorReporter& errorReporter);
const ErrorReporter& errorReporter);
// Lex the given source code, placing the results in `result`. Returns true if there // Lex the given source code, placing the results in `result`. Returns true if there
// were no errors, false if there were. Even when errors are present, the file may have partial // were no errors, false if there were. Even when errors are present, the file may have partial
// content which can be fed into later stages of parsing in order to find more errors. // content which can be fed into later stages of parsing in order to find more errors.
...@@ -49,7 +48,7 @@ class Lexer { ...@@ -49,7 +48,7 @@ class Lexer {
// into your own parsers. // into your own parsers.
public: public:
Lexer(Orphanage orphanage, const ErrorReporter& errorReporter); Lexer(Orphanage orphanage, ErrorReporter& errorReporter);
// `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is // `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is
// a pointer to the beginning of the input, used to compute byte offsets. // a pointer to the beginning of the input, used to compute byte offsets.
......
...@@ -198,41 +198,40 @@ kj::String catPath(kj::StringPtr base, kj::StringPtr add) { ...@@ -198,41 +198,40 @@ kj::String catPath(kj::StringPtr base, kj::StringPtr add) {
class ModuleLoader::Impl { class ModuleLoader::Impl {
public: public:
Impl(const GlobalErrorReporter& errorReporter): errorReporter(errorReporter) {} Impl(GlobalErrorReporter& errorReporter): errorReporter(errorReporter) {}
void addImportPath(kj::String path) { void addImportPath(kj::String path) {
searchPath.add(kj::heapString(kj::mv(path))); searchPath.add(kj::heapString(kj::mv(path)));
} }
kj::Maybe<const Module&> loadModule(kj::StringPtr localName, kj::StringPtr sourceName) const; kj::Maybe<Module&> loadModule(kj::StringPtr localName, kj::StringPtr sourceName);
kj::Maybe<const Module&> loadModuleFromSearchPath(kj::StringPtr sourceName) const; kj::Maybe<Module&> loadModuleFromSearchPath(kj::StringPtr sourceName);
const GlobalErrorReporter& getErrorReporter() const { return errorReporter; } GlobalErrorReporter& getErrorReporter() { return errorReporter; }
private: private:
const GlobalErrorReporter& errorReporter; GlobalErrorReporter& errorReporter;
kj::Vector<kj::String> searchPath; kj::Vector<kj::String> searchPath;
kj::MutexGuarded<std::map<kj::StringPtr, kj::Own<Module>>> modules; kj::MutexGuarded<std::map<kj::StringPtr, kj::Own<Module>>> modules;
}; };
class ModuleLoader::ModuleImpl final: public Module { class ModuleLoader::ModuleImpl final: public Module {
public: public:
ModuleImpl(const ModuleLoader::Impl& loader, kj::String localName, kj::String sourceName) ModuleImpl(ModuleLoader::Impl& loader, kj::String localName, kj::String sourceName)
: loader(loader), localName(kj::mv(localName)), sourceName(kj::mv(sourceName)) {} : loader(loader), localName(kj::mv(localName)), sourceName(kj::mv(sourceName)) {}
kj::StringPtr getLocalName() const { kj::StringPtr getLocalName() {
return localName; return localName;
} }
kj::StringPtr getSourceName() const override { kj::StringPtr getSourceName() override {
return sourceName; return sourceName;
} }
Orphan<ParsedFile> loadContent(Orphanage orphanage) const override { Orphan<ParsedFile> loadContent(Orphanage orphanage) override {
kj::Array<const char> content = mmapForRead(localName); kj::Array<const char> content = mmapForRead(localName);
lineBreaks.get([&](kj::SpaceFor<LineBreakTable>& space) { lineBreaks = nullptr; // In case loadContent() is called multiple times.
return space.construct(content); lineBreaks = lineBreaksSpace.construct(content);
});
MallocMessageBuilder lexedBuilder; MallocMessageBuilder lexedBuilder;
auto statements = lexedBuilder.initRoot<LexedStatements>(); auto statements = lexedBuilder.initRoot<LexedStatements>();
...@@ -243,7 +242,7 @@ public: ...@@ -243,7 +242,7 @@ public:
return parsed; return parsed;
} }
kj::Maybe<const Module&> importRelative(kj::StringPtr importPath) const override { kj::Maybe<Module&> importRelative(kj::StringPtr importPath) override {
if (importPath.size() > 0 && importPath[0] == '/') { if (importPath.size() > 0 && importPath[0] == '/') {
return loader.loadModuleFromSearchPath(importPath.slice(1)); return loader.loadModuleFromSearchPath(importPath.slice(1));
} else { } else {
...@@ -251,32 +250,31 @@ public: ...@@ -251,32 +250,31 @@ public:
} }
} }
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const override { void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override {
auto& lines = lineBreaks.get( auto& lines = *KJ_REQUIRE_NONNULL(lineBreaks,
[](kj::SpaceFor<LineBreakTable>& space) -> kj::Own<LineBreakTable> { "Can't report errors until loadContent() is called.");
KJ_FAIL_REQUIRE("Can't report errors until loadContent() is called.");
});
loader.getErrorReporter().addError( loader.getErrorReporter().addError(
localName, lines.toSourcePos(startByte), lines.toSourcePos(endByte), message); localName, lines.toSourcePos(startByte), lines.toSourcePos(endByte), message);
} }
bool hadErrors() const override { bool hadErrors() override {
return loader.getErrorReporter().hadErrors(); return loader.getErrorReporter().hadErrors();
} }
private: private:
const ModuleLoader::Impl& loader; ModuleLoader::Impl& loader;
kj::String localName; kj::String localName;
kj::String sourceName; kj::String sourceName;
kj::Lazy<LineBreakTable> lineBreaks; kj::SpaceFor<LineBreakTable> lineBreaksSpace;
kj::Maybe<kj::Own<LineBreakTable>> lineBreaks;
}; };
// ======================================================================================= // =======================================================================================
kj::Maybe<const Module&> ModuleLoader::Impl::loadModule( kj::Maybe<Module&> ModuleLoader::Impl::loadModule(
kj::StringPtr localName, kj::StringPtr sourceName) const { kj::StringPtr localName, kj::StringPtr sourceName) {
kj::String canonicalLocalName = canonicalizePath(localName); kj::String canonicalLocalName = canonicalizePath(localName);
kj::String canonicalSourceName = canonicalizePath(sourceName); kj::String canonicalSourceName = canonicalizePath(sourceName);
...@@ -300,8 +298,7 @@ kj::Maybe<const Module&> ModuleLoader::Impl::loadModule( ...@@ -300,8 +298,7 @@ kj::Maybe<const Module&> ModuleLoader::Impl::loadModule(
return result; return result;
} }
kj::Maybe<const Module&> ModuleLoader::Impl::loadModuleFromSearchPath( kj::Maybe<Module&> ModuleLoader::Impl::loadModuleFromSearchPath(kj::StringPtr sourceName) {
kj::StringPtr sourceName) const {
for (auto& search: searchPath) { for (auto& search: searchPath) {
kj::String candidate = kj::str(search, "/", sourceName); kj::String candidate = kj::str(search, "/", sourceName);
char* end = canonicalizePath(candidate.begin() + (candidate[0] == '/')); char* end = canonicalizePath(candidate.begin() + (candidate[0] == '/'));
...@@ -316,14 +313,13 @@ kj::Maybe<const Module&> ModuleLoader::Impl::loadModuleFromSearchPath( ...@@ -316,14 +313,13 @@ kj::Maybe<const Module&> ModuleLoader::Impl::loadModuleFromSearchPath(
// ======================================================================================= // =======================================================================================
ModuleLoader::ModuleLoader(const GlobalErrorReporter& errorReporter) ModuleLoader::ModuleLoader(GlobalErrorReporter& errorReporter)
: impl(kj::heap<Impl>(errorReporter)) {} : impl(kj::heap<Impl>(errorReporter)) {}
ModuleLoader::~ModuleLoader() noexcept(false) {} ModuleLoader::~ModuleLoader() noexcept(false) {}
void ModuleLoader::addImportPath(kj::String path) { impl->addImportPath(kj::mv(path)); } void ModuleLoader::addImportPath(kj::String path) { impl->addImportPath(kj::mv(path)); }
kj::Maybe<const Module&> ModuleLoader::loadModule( kj::Maybe<Module&> ModuleLoader::loadModule(kj::StringPtr localName, kj::StringPtr sourceName) {
kj::StringPtr localName, kj::StringPtr sourceName) const {
return impl->loadModule(localName, sourceName); return impl->loadModule(localName, sourceName);
} }
......
...@@ -35,7 +35,7 @@ namespace compiler { ...@@ -35,7 +35,7 @@ namespace compiler {
class ModuleLoader { class ModuleLoader {
public: public:
explicit ModuleLoader(const GlobalErrorReporter& errorReporter); explicit ModuleLoader(GlobalErrorReporter& errorReporter);
// Create a ModuleLoader that reports error messages to the given reporter. // Create a ModuleLoader that reports error messages to the given reporter.
KJ_DISALLOW_COPY(ModuleLoader); KJ_DISALLOW_COPY(ModuleLoader);
...@@ -45,7 +45,7 @@ public: ...@@ -45,7 +45,7 @@ public:
void addImportPath(kj::String path); void addImportPath(kj::String path);
// Add a directory to the list of paths that is searched for imports that start with a '/'. // Add a directory to the list of paths that is searched for imports that start with a '/'.
kj::Maybe<const Module&> loadModule(kj::StringPtr localName, kj::StringPtr sourceName) const; kj::Maybe<Module&> loadModule(kj::StringPtr localName, kj::StringPtr sourceName);
// Tries to load the module with the given filename. `localName` is the path to the file on // Tries to load the module with the given filename. `localName` is the path to the file on
// disk (as you'd pass to open(2)), and `sourceName` is the canonical name it should be given // disk (as you'd pass to open(2)), and `sourceName` is the canonical name it should be given
// in the schema (this is used e.g. to decide output file locations). Often, these are the same. // in the schema (this is used e.g. to decide output file locations). Often, these are the same.
......
...@@ -524,7 +524,7 @@ private: ...@@ -524,7 +524,7 @@ private:
// ======================================================================================= // =======================================================================================
NodeTranslator::NodeTranslator( NodeTranslator::NodeTranslator(
const Resolver& resolver, const ErrorReporter& errorReporter, Resolver& resolver, ErrorReporter& errorReporter,
const Declaration::Reader& decl, Orphan<schema::Node> wipNodeParam, const Declaration::Reader& decl, Orphan<schema::Node> wipNodeParam,
bool compileAnnotations) bool compileAnnotations)
: resolver(resolver), errorReporter(errorReporter), : resolver(resolver), errorReporter(errorReporter),
...@@ -561,12 +561,12 @@ NodeTranslator::NodeSet NodeTranslator::finish() { ...@@ -561,12 +561,12 @@ NodeTranslator::NodeSet NodeTranslator::finish() {
class NodeTranslator::DuplicateNameDetector { class NodeTranslator::DuplicateNameDetector {
public: public:
inline explicit DuplicateNameDetector(const ErrorReporter& errorReporter) inline explicit DuplicateNameDetector(ErrorReporter& errorReporter)
: errorReporter(errorReporter) {} : errorReporter(errorReporter) {}
void check(List<Declaration>::Reader nestedDecls, Declaration::Which parentKind); void check(List<Declaration>::Reader nestedDecls, Declaration::Which parentKind);
private: private:
const ErrorReporter& errorReporter; ErrorReporter& errorReporter;
std::map<kj::StringPtr, LocatedText::Reader> names; std::map<kj::StringPtr, LocatedText::Reader> names;
}; };
...@@ -757,7 +757,7 @@ void NodeTranslator::compileAnnotation(Declaration::Annotation::Reader decl, ...@@ -757,7 +757,7 @@ void NodeTranslator::compileAnnotation(Declaration::Annotation::Reader decl,
class NodeTranslator::DuplicateOrdinalDetector { class NodeTranslator::DuplicateOrdinalDetector {
public: public:
DuplicateOrdinalDetector(const ErrorReporter& errorReporter): errorReporter(errorReporter) {} DuplicateOrdinalDetector(ErrorReporter& errorReporter): errorReporter(errorReporter) {}
void check(LocatedInteger::Reader ordinal) { void check(LocatedInteger::Reader ordinal) {
if (ordinal.getValue() < expectedOrdinal) { if (ordinal.getValue() < expectedOrdinal) {
...@@ -780,7 +780,7 @@ public: ...@@ -780,7 +780,7 @@ public:
} }
private: private:
const ErrorReporter& errorReporter; ErrorReporter& errorReporter;
uint expectedOrdinal = 0; uint expectedOrdinal = 0;
kj::Maybe<LocatedInteger::Reader> lastOrdinalLocation; kj::Maybe<LocatedInteger::Reader> lastOrdinalLocation;
}; };
...@@ -842,7 +842,7 @@ public: ...@@ -842,7 +842,7 @@ public:
private: private:
NodeTranslator& translator; NodeTranslator& translator;
const ErrorReporter& errorReporter; ErrorReporter& errorReporter;
StructLayout layout; StructLayout layout;
kj::Arena arena; kj::Arena arena;
......
...@@ -49,11 +49,11 @@ public: ...@@ -49,11 +49,11 @@ public:
Declaration::Which kind; Declaration::Which kind;
}; };
virtual kj::Maybe<ResolvedName> resolve(const DeclName::Reader& name) const = 0; virtual kj::Maybe<ResolvedName> resolve(const DeclName::Reader& name) = 0;
// Look up the given name, relative to this node, and return basic information about the // Look up the given name, relative to this node, and return basic information about the
// target. // target.
virtual kj::Maybe<Schema> resolveBootstrapSchema(uint64_t id) const = 0; virtual kj::Maybe<Schema> resolveBootstrapSchema(uint64_t id) = 0;
// Get the schema for the given ID. If a schema is returned, it must be safe to traverse its // Get the schema for the given ID. If a schema is returned, it must be safe to traverse its
// dependencies using Schema::getDependency(). A schema that is only at the bootstrap stage // dependencies using Schema::getDependency(). A schema that is only at the bootstrap stage
// is acceptable. // is acceptable.
...@@ -62,7 +62,7 @@ public: ...@@ -62,7 +62,7 @@ public:
// traversing other schemas. Returns null if the ID is recognized, but the corresponding // traversing other schemas. Returns null if the ID is recognized, but the corresponding
// schema node failed to be built for reasons that were already reported. // schema node failed to be built for reasons that were already reported.
virtual kj::Maybe<schema::Node::Reader> resolveFinalSchema(uint64_t id) const = 0; virtual kj::Maybe<schema::Node::Reader> resolveFinalSchema(uint64_t id) = 0;
// Get the final schema for the given ID. A bootstrap schema is not acceptable. A raw // Get the final schema for the given ID. A bootstrap schema is not acceptable. A raw
// node reader is returned rather than a Schema object because using a Schema object built // node reader is returned rather than a Schema object because using a Schema object built
// by the final schema loader could trigger lazy initialization of dependencies which could // by the final schema loader could trigger lazy initialization of dependencies which could
...@@ -72,11 +72,11 @@ public: ...@@ -72,11 +72,11 @@ public:
// traversing other schemas. Returns null if the ID is recognized, but the corresponding // traversing other schemas. Returns null if the ID is recognized, but the corresponding
// schema node failed to be built for reasons that were already reported. // schema node failed to be built for reasons that were already reported.
virtual kj::Maybe<uint64_t> resolveImport(kj::StringPtr name) const = 0; virtual kj::Maybe<uint64_t> resolveImport(kj::StringPtr name) = 0;
// Get the ID of an imported file given the import path. // Get the ID of an imported file given the import path.
}; };
NodeTranslator(const Resolver& resolver, const ErrorReporter& errorReporter, NodeTranslator(Resolver& resolver, ErrorReporter& errorReporter,
const Declaration::Reader& decl, Orphan<schema::Node> wipNode, const Declaration::Reader& decl, Orphan<schema::Node> wipNode,
bool compileAnnotations); bool compileAnnotations);
// Construct a NodeTranslator to translate the given declaration. The wipNode starts out with // Construct a NodeTranslator to translate the given declaration. The wipNode starts out with
...@@ -107,8 +107,8 @@ public: ...@@ -107,8 +107,8 @@ public:
// bootstrap node) and return it. // bootstrap node) and return it.
private: private:
const Resolver& resolver; Resolver& resolver;
const ErrorReporter& errorReporter; ErrorReporter& errorReporter;
Orphanage orphanage; Orphanage orphanage;
bool compileAnnotations; bool compileAnnotations;
...@@ -195,7 +195,7 @@ public: ...@@ -195,7 +195,7 @@ public:
virtual kj::Maybe<DynamicValue::Reader> resolveConstant(DeclName::Reader name) = 0; virtual kj::Maybe<DynamicValue::Reader> resolveConstant(DeclName::Reader name) = 0;
}; };
ValueTranslator(Resolver& resolver, const ErrorReporter& errorReporter, Orphanage orphanage) ValueTranslator(Resolver& resolver, ErrorReporter& errorReporter, Orphanage orphanage)
: resolver(resolver), errorReporter(errorReporter), orphanage(orphanage) {} : resolver(resolver), errorReporter(errorReporter), orphanage(orphanage) {}
kj::Maybe<Orphan<DynamicValue>> compileValue( kj::Maybe<Orphan<DynamicValue>> compileValue(
...@@ -203,7 +203,7 @@ public: ...@@ -203,7 +203,7 @@ public:
private: private:
Resolver& resolver; Resolver& resolver;
const ErrorReporter& errorReporter; ErrorReporter& errorReporter;
Orphanage orphanage; Orphanage orphanage;
Orphan<DynamicValue> compileValueInner(ValueExpression::Reader src, schema::Type::Reader type); Orphan<DynamicValue> compileValueInner(ValueExpression::Reader src, schema::Type::Reader type);
......
...@@ -122,7 +122,7 @@ uint64_t generateMethodParamsId(uint64_t parentId, uint16_t methodOrdinal, bool ...@@ -122,7 +122,7 @@ uint64_t generateMethodParamsId(uint64_t parentId, uint16_t methodOrdinal, bool
} }
void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result, void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result,
const ErrorReporter& errorReporter) { ErrorReporter& errorReporter) {
CapnpParser parser(Orphanage::getForMessageContaining(result), errorReporter); CapnpParser parser(Orphanage::getForMessageContaining(result), errorReporter);
kj::Vector<Orphan<Declaration>> decls(statements.size()); kj::Vector<Orphan<Declaration>> decls(statements.size());
...@@ -272,7 +272,7 @@ class ParseListItems { ...@@ -272,7 +272,7 @@ class ParseListItems {
// Transformer that parses all items in the input token sequence list using the given parser. // Transformer that parses all items in the input token sequence list using the given parser.
public: public:
constexpr ParseListItems(ItemParser&& itemParser, const ErrorReporter& errorReporter) constexpr ParseListItems(ItemParser&& itemParser, ErrorReporter& errorReporter)
: itemParser(p::sequence(kj::fwd<ItemParser>(itemParser), p::endOfInput)), : itemParser(p::sequence(kj::fwd<ItemParser>(itemParser), p::endOfInput)),
errorReporter(errorReporter) {} errorReporter(errorReporter) {}
...@@ -310,12 +310,11 @@ public: ...@@ -310,12 +310,11 @@ public:
private: private:
decltype(p::sequence(kj::instance<ItemParser>(), p::endOfInput)) itemParser; decltype(p::sequence(kj::instance<ItemParser>(), p::endOfInput)) itemParser;
const ErrorReporter& errorReporter; ErrorReporter& errorReporter;
}; };
template <typename ItemParser> template <typename ItemParser>
constexpr auto parenthesizedList(ItemParser&& itemParser, constexpr auto parenthesizedList(ItemParser&& itemParser, ErrorReporter& errorReporter) -> decltype(
const ErrorReporter& errorReporter) -> decltype(
transform(rawParenthesizedList, ParseListItems<ItemParser>( transform(rawParenthesizedList, ParseListItems<ItemParser>(
kj::fwd<ItemParser>(itemParser), errorReporter))) { kj::fwd<ItemParser>(itemParser), errorReporter))) {
return transform(rawParenthesizedList, ParseListItems<ItemParser>( return transform(rawParenthesizedList, ParseListItems<ItemParser>(
...@@ -323,8 +322,7 @@ constexpr auto parenthesizedList(ItemParser&& itemParser, ...@@ -323,8 +322,7 @@ constexpr auto parenthesizedList(ItemParser&& itemParser,
} }
template <typename ItemParser> template <typename ItemParser>
constexpr auto bracketedList(ItemParser&& itemParser, constexpr auto bracketedList(ItemParser&& itemParser, ErrorReporter& errorReporter) -> decltype(
const ErrorReporter& errorReporter) -> decltype(
transform(rawBracketedList, ParseListItems<ItemParser>( transform(rawBracketedList, ParseListItems<ItemParser>(
kj::fwd<ItemParser>(itemParser), errorReporter))) { kj::fwd<ItemParser>(itemParser), errorReporter))) {
return transform(rawBracketedList, ParseListItems<ItemParser>( return transform(rawBracketedList, ParseListItems<ItemParser>(
...@@ -384,7 +382,7 @@ void initLocation(kj::parse::Span<List<Token>::Reader::Iterator> location, ...@@ -384,7 +382,7 @@ void initLocation(kj::parse::Span<List<Token>::Reader::Iterator> location,
// ======================================================================================= // =======================================================================================
CapnpParser::CapnpParser(Orphanage orphanageParam, const ErrorReporter& errorReporterParam) CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterParam)
: orphanage(orphanageParam), errorReporter(errorReporterParam) { : orphanage(orphanageParam), errorReporter(errorReporterParam) {
parsers.declName = arena.copy(p::transformWithLocation( parsers.declName = arena.copy(p::transformWithLocation(
p::sequence( p::sequence(
......
...@@ -34,7 +34,7 @@ namespace capnp { ...@@ -34,7 +34,7 @@ namespace capnp {
namespace compiler { namespace compiler {
void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result, void parseFile(List<Statement>::Reader statements, ParsedFile::Builder result,
const ErrorReporter& errorReporter); ErrorReporter& errorReporter);
// Parse a list of statements to build a ParsedFile. // Parse a list of statements to build a ParsedFile.
// //
// If any errors are reported, then the output is not usable. However, it may be passed on through // If any errors are reported, then the output is not usable. However, it may be passed on through
...@@ -59,7 +59,7 @@ class CapnpParser { ...@@ -59,7 +59,7 @@ class CapnpParser {
// them into your own parsers. // them into your own parsers.
public: public:
CapnpParser(Orphanage orphanage, const ErrorReporter& errorReporter); CapnpParser(Orphanage orphanage, ErrorReporter& errorReporter);
// `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is // `orphanage` is used to allocate Cap'n Proto message objects in the result. `inputStart` is
// a pointer to the beginning of the input, used to compute byte offsets. // a pointer to the beginning of the input, used to compute byte offsets.
...@@ -141,7 +141,7 @@ public: ...@@ -141,7 +141,7 @@ public:
private: private:
Orphanage orphanage; Orphanage orphanage;
const ErrorReporter& errorReporter; ErrorReporter& errorReporter;
kj::Arena arena; kj::Arena arena;
Parsers parsers; Parsers parsers;
}; };
......
...@@ -72,11 +72,11 @@ public: ...@@ -72,11 +72,11 @@ public:
ModuleImpl(const SchemaParser& parser, kj::Own<const SchemaFile>&& file) ModuleImpl(const SchemaParser& parser, kj::Own<const SchemaFile>&& file)
: parser(parser), file(kj::mv(file)) {} : parser(parser), file(kj::mv(file)) {}
kj::StringPtr getSourceName() const override { kj::StringPtr getSourceName() override {
return file->getDisplayName(); return file->getDisplayName();
} }
Orphan<compiler::ParsedFile> loadContent(Orphanage orphanage) const override { Orphan<compiler::ParsedFile> loadContent(Orphanage orphanage) override {
kj::Array<const char> content = file->readContent(); kj::Array<const char> content = file->readContent();
lineBreaks.get([&](kj::SpaceFor<kj::Vector<uint>>& space) { lineBreaks.get([&](kj::SpaceFor<kj::Vector<uint>>& space) {
...@@ -99,7 +99,7 @@ public: ...@@ -99,7 +99,7 @@ public:
return parsed; return parsed;
} }
kj::Maybe<const Module&> importRelative(kj::StringPtr importPath) const override { kj::Maybe<Module&> importRelative(kj::StringPtr importPath) override {
KJ_IF_MAYBE(importedFile, file->import(importPath)) { KJ_IF_MAYBE(importedFile, file->import(importPath)) {
return parser.getModuleImpl(kj::mv(*importedFile)); return parser.getModuleImpl(kj::mv(*importedFile));
} else { } else {
...@@ -107,7 +107,7 @@ public: ...@@ -107,7 +107,7 @@ public:
} }
} }
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const override { void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) override {
auto& lines = lineBreaks.get( auto& lines = lineBreaks.get(
[](kj::SpaceFor<kj::Vector<uint>>& space) { [](kj::SpaceFor<kj::Vector<uint>>& space) {
KJ_FAIL_REQUIRE("Can't report errors until loadContent() is called."); KJ_FAIL_REQUIRE("Can't report errors until loadContent() is called.");
...@@ -129,7 +129,7 @@ public: ...@@ -129,7 +129,7 @@ public:
__atomic_store_n(&parser.hadErrors, true, __ATOMIC_RELAXED); __atomic_store_n(&parser.hadErrors, true, __ATOMIC_RELAXED);
} }
bool hadErrors() const override { bool hadErrors() override {
return __atomic_load_n(&parser.hadErrors, __ATOMIC_RELAXED); return __atomic_load_n(&parser.hadErrors, __ATOMIC_RELAXED);
} }
...@@ -162,7 +162,7 @@ struct SchemaFileEq { ...@@ -162,7 +162,7 @@ struct SchemaFileEq {
struct SchemaParser::Impl { struct SchemaParser::Impl {
typedef std::unordered_map< typedef std::unordered_map<
const SchemaFile*, kj::Own<const ModuleImpl>, SchemaFileHash, SchemaFileEq> FileMap; const SchemaFile*, kj::Own<ModuleImpl>, SchemaFileHash, SchemaFileEq> FileMap;
kj::MutexGuarded<FileMap> fileMap; kj::MutexGuarded<FileMap> fileMap;
compiler::Compiler compiler; compiler::Compiler compiler;
}; };
...@@ -172,11 +172,11 @@ SchemaParser::~SchemaParser() noexcept(false) {} ...@@ -172,11 +172,11 @@ SchemaParser::~SchemaParser() noexcept(false) {}
ParsedSchema SchemaParser::parseDiskFile( ParsedSchema SchemaParser::parseDiskFile(
kj::StringPtr displayName, kj::StringPtr diskPath, kj::StringPtr displayName, kj::StringPtr diskPath,
kj::ArrayPtr<const kj::StringPtr> importPath) { kj::ArrayPtr<const kj::StringPtr> importPath) const {
return parseFile(SchemaFile::newDiskFile(displayName, diskPath, importPath)); return parseFile(SchemaFile::newDiskFile(displayName, diskPath, importPath));
} }
ParsedSchema SchemaParser::parseFile(kj::Own<SchemaFile>&& file) { ParsedSchema SchemaParser::parseFile(kj::Own<SchemaFile>&& file) const {
KJ_DEFER(impl->compiler.clearWorkspace()); KJ_DEFER(impl->compiler.clearWorkspace());
uint64_t id = impl->compiler.add(getModuleImpl(kj::mv(file))); uint64_t id = impl->compiler.add(getModuleImpl(kj::mv(file)));
impl->compiler.eagerlyCompile(id, impl->compiler.eagerlyCompile(id,
...@@ -185,7 +185,7 @@ ParsedSchema SchemaParser::parseFile(kj::Own<SchemaFile>&& file) { ...@@ -185,7 +185,7 @@ ParsedSchema SchemaParser::parseFile(kj::Own<SchemaFile>&& file) {
return ParsedSchema(impl->compiler.getLoader().get(id), *this); return ParsedSchema(impl->compiler.getLoader().get(id), *this);
} }
const SchemaParser::ModuleImpl& SchemaParser::getModuleImpl(kj::Own<SchemaFile>&& file) const { SchemaParser::ModuleImpl& SchemaParser::getModuleImpl(kj::Own<SchemaFile>&& file) const {
auto lock = impl->fileMap.lockExclusive(); auto lock = impl->fileMap.lockExclusive();
auto insertResult = lock->insert(std::make_pair(file.get(), kj::Own<ModuleImpl>())); auto insertResult = lock->insert(std::make_pair(file.get(), kj::Own<ModuleImpl>()));
...@@ -196,14 +196,14 @@ const SchemaParser::ModuleImpl& SchemaParser::getModuleImpl(kj::Own<SchemaFile>& ...@@ -196,14 +196,14 @@ const SchemaParser::ModuleImpl& SchemaParser::getModuleImpl(kj::Own<SchemaFile>&
return *insertResult.first->second; return *insertResult.first->second;
} }
kj::Maybe<ParsedSchema> ParsedSchema::findNested(kj::StringPtr name) { kj::Maybe<ParsedSchema> ParsedSchema::findNested(kj::StringPtr name) const {
return parser->impl->compiler.lookup(getProto().getId(), name).map( return parser->impl->compiler.lookup(getProto().getId(), name).map(
[this](uint64_t childId) { [this](uint64_t childId) {
return ParsedSchema(parser->impl->compiler.getLoader().get(childId), *parser); return ParsedSchema(parser->impl->compiler.getLoader().get(childId), *parser);
}); });
} }
ParsedSchema ParsedSchema::getNested(kj::StringPtr nestedName) { ParsedSchema ParsedSchema::getNested(kj::StringPtr nestedName) const {
KJ_IF_MAYBE(nested, findNested(nestedName)) { KJ_IF_MAYBE(nested, findNested(nestedName)) {
return *nested; return *nested;
} else { } else {
......
...@@ -34,13 +34,15 @@ class SchemaFile; ...@@ -34,13 +34,15 @@ class SchemaFile;
class SchemaParser { class SchemaParser {
// Parses `.capnp` files to produce `Schema` objects. // Parses `.capnp` files to produce `Schema` objects.
//
// This class is thread-safe, hence all its methods are const.
public: public:
SchemaParser(); SchemaParser();
~SchemaParser() noexcept(false); ~SchemaParser() noexcept(false);
ParsedSchema parseDiskFile(kj::StringPtr displayName, kj::StringPtr diskPath, ParsedSchema parseDiskFile(kj::StringPtr displayName, kj::StringPtr diskPath,
kj::ArrayPtr<const kj::StringPtr> importPath); kj::ArrayPtr<const kj::StringPtr> importPath) const;
// Parse a file located on disk. Throws an exception if the file dosen't exist. // Parse a file located on disk. Throws an exception if the file dosen't exist.
// //
// Parameters: // Parameters:
...@@ -61,7 +63,7 @@ public: ...@@ -61,7 +63,7 @@ public:
// anything in the imported file -- only the imported types which are actually used are // anything in the imported file -- only the imported types which are actually used are
// "dependencies". // "dependencies".
ParsedSchema parseFile(kj::Own<SchemaFile>&& file); ParsedSchema parseFile(kj::Own<SchemaFile>&& file) const;
// Advanced interface for parsing a file that may or may not be located in any global namespace. // Advanced interface for parsing a file that may or may not be located in any global namespace.
// Most users will prefer `parseDiskFile()`. // Most users will prefer `parseDiskFile()`.
// //
...@@ -79,7 +81,7 @@ private: ...@@ -79,7 +81,7 @@ private:
kj::Own<Impl> impl; kj::Own<Impl> impl;
mutable bool hadErrors = false; mutable bool hadErrors = false;
const ModuleImpl& getModuleImpl(kj::Own<SchemaFile>&& file) const; ModuleImpl& getModuleImpl(kj::Own<SchemaFile>&& file) const;
friend class ParsedSchema; friend class ParsedSchema;
}; };
...@@ -91,11 +93,11 @@ class ParsedSchema: public Schema { ...@@ -91,11 +93,11 @@ class ParsedSchema: public Schema {
public: public:
inline ParsedSchema(): parser(nullptr) {} inline ParsedSchema(): parser(nullptr) {}
kj::Maybe<ParsedSchema> findNested(kj::StringPtr name); kj::Maybe<ParsedSchema> findNested(kj::StringPtr name) const;
// Gets the nested node with the given name, or returns null if there is no such nested // Gets the nested node with the given name, or returns null if there is no such nested
// declaration. // declaration.
ParsedSchema getNested(kj::StringPtr name); ParsedSchema getNested(kj::StringPtr name) const;
// Gets the nested node with the given name, or throws an exception if there is no such nested // Gets the nested node with the given name, or throws an exception if there is no such nested
// declaration. // declaration.
......
...@@ -137,3 +137,5 @@ enum DupEnumerants { ...@@ -137,3 +137,5 @@ enum DupEnumerants {
dupNumber1 @2; dupNumber1 @2;
dupNumber2 @2; dupNumber2 @2;
} }
const recursive: UInt32 = .recursive;
...@@ -48,3 +48,4 @@ file:136:3-10: error: 'dupName' is already defined in this scope. ...@@ -48,3 +48,4 @@ file:136:3-10: error: 'dupName' is already defined in this scope.
file:135:3-10: error: 'dupName' previously defined here. file:135:3-10: error: 'dupName' previously defined here.
file:138:15-16: error: Duplicate ordinal number. file:138:15-16: error: Duplicate ordinal number.
file:137:15-16: error: Ordinal @2 originally used here. file:137:15-16: error: Ordinal @2 originally used here.
file:141:7-16: error: Declaration recursively depends on itself.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment