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
......
This diff is collapsed.
...@@ -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