Commit b92a28a6 authored by Kenton Varda's avatar Kenton Varda

Compiler nearing completion. Mostly just needs a driver.

parent 34e70d5a
......@@ -23,6 +23,7 @@
#include "lexer.h"
#include "parser.h"
#include "compiler.h"
#include <capnp/pretty-print.h>
#include <kj/vector.h>
#include <kj/io.h>
......@@ -31,8 +32,22 @@
#include "../message.h"
#include <iostream>
class CerrErrorReporter: public capnp::compiler::ErrorReporter {
class DummyModule: public capnp::compiler::Module {
public:
capnp::compiler::ParsedFile::Reader content;
kj::StringPtr getLocalName() const {
return "(stdin)";
}
kj::StringPtr getSourceName() const {
return "(stdin)";
}
capnp::Orphan<capnp::compiler::ParsedFile> loadContent(capnp::Orphanage orphanage) const {
return orphanage.newOrphanCopy(content);
}
kj::Maybe<const Module&> importRelative(kj::StringPtr importPath) const {
return nullptr;
}
void addError(uint32_t startByte, uint32_t endByte, kj::StringPtr message) const override {
std::cerr << "input:" << startByte << "-" << endByte << ": " << message.cStr() << std::endl;
}
......@@ -41,18 +56,23 @@ public:
int main(int argc, char* argv[]) {
// Eventually this will be capnpc. For now it's just a dummy program that tests parsing.
kj::Vector<char> input;
char buffer[4096];
for (;;) {
ssize_t n;
KJ_SYSCALL(n = read(STDIN_FILENO, buffer, sizeof(buffer)));
if (n == 0) {
break;
}
input.addAll(buffer, buffer + n);
}
// kj::Vector<char> input;
// char buffer[4096];
// for (;;) {
// ssize_t n;
// KJ_SYSCALL(n = read(STDIN_FILENO, buffer, sizeof(buffer)));
// if (n == 0) {
// break;
// }
// input.addAll(buffer, buffer + n);
// }
kj::StringPtr input =
"@0x8e001c75f6ff54c8;\n"
"struct Foo { bar @0 :Int32 = 123; baz @1 :Text; }\n"
"struct Qux { foo @0 :List(Int32) = [12, 34]; }";
CerrErrorReporter errorReporter;
DummyModule module;
std::cout << "=========================================================================\n"
<< "lex\n"
......@@ -61,7 +81,7 @@ int main(int argc, char* argv[]) {
capnp::MallocMessageBuilder lexerArena;
auto lexedFile = lexerArena.initRoot<capnp::compiler::LexedStatements>();
capnp::compiler::lex(input, lexedFile, errorReporter);
capnp::compiler::lex(input, lexedFile, module);
std::cout << capnp::prettyPrint(lexedFile).cStr() << std::endl;
std::cout << "=========================================================================\n"
......@@ -71,8 +91,21 @@ int main(int argc, char* argv[]) {
capnp::MallocMessageBuilder parserArena;
auto parsedFile = parserArena.initRoot<capnp::compiler::ParsedFile>();
capnp::compiler::parseFile(lexedFile.getStatements(), parsedFile, errorReporter);
capnp::compiler::parseFile(lexedFile.getStatements(), parsedFile, module);
std::cout << capnp::prettyPrint(parsedFile).cStr() << std::endl;
std::cout << "=========================================================================\n"
<< "compile\n"
<< "========================================================================="
<< std::endl;
module.content = parsedFile.asReader();
capnp::compiler::Compiler compiler;
compiler.add(module, capnp::compiler::Compiler::EAGER);
for (auto schema: compiler.getLoader().getAllLoaded()) {
std::cout << capnp::prettyPrint(schema.getProto()).cStr() << std::endl;
}
return 0;
}
......@@ -68,8 +68,8 @@ public:
Node(kj::StringPtr name, Declaration::Body::Which kind);
// Create a dummy node representing a built-in declaration, like "Int32" or "true".
uint64_t getId() { return id; }
Declaration::Body::Which getKind() { return kind; }
uint64_t getId() const { return id; }
Declaration::Body::Which getKind() const { return kind; }
kj::Maybe<const Node&> lookupMember(kj::StringPtr name) const;
// Find a direct member of this node with the given name.
......@@ -81,16 +81,20 @@ public:
kj::Maybe<const Node&> lookup(const DeclName::Reader& name) const;
// Resolve an arbitrary DeclName to a Node.
Schema getBootstrapOrFinalSchema() const;
Schema getFinalSchema() const;
kj::Maybe<Schema> getBootstrapOrFinalSchema() const;
kj::Maybe<Schema> getFinalSchema() const;
void traverse(Compiler::Mode mode) const;
// Get the final schema for this node, and also possibly traverse the node's children and
// dependencies to ensure that they are loaded, depending on the mode.
void addError(kj::StringPtr error) const;
// Report an error on this Node.
// implements NodeTranslator::Resolver -----------------------------
kj::Maybe<ResolvedName> resolve(const DeclName::Reader& name) const override;
Schema resolveMaybeBootstrapSchema(uint64_t id) const override;
Schema resolveFinalSchema(uint64_t id) const override;
kj::Maybe<Schema> resolveMaybeBootstrapSchema(uint64_t id) const override;
kj::Maybe<Schema> resolveFinalSchema(uint64_t id) const override;
private:
const CompiledModule* module; // null iff isBuiltin is true
......@@ -153,13 +157,14 @@ private:
NodeTranslator* translator;
// Node translator, allocated in the bootstrap arena.
Schema bootstrapSchema;
// The schema built in the bootstrap loader.
kj::Maybe<Schema> bootstrapSchema;
// The schema built in the bootstrap loader. Null if the bootstrap loader threw an exception.
// FINISHED ------------------------------------
Schema finalSchema;
// The complete schema as loaded by the compiler's main SchemaLoader.
kj::Maybe<Schema> finalSchema;
// The complete schema as loaded by the compiler's main SchemaLoader. Null if the final
// loader threw an exception.
};
kj::MutexGuarded<Content> content;
......@@ -188,7 +193,7 @@ public:
const Compiler::Impl& getCompiler() const { return compiler; }
const ErrorReporter& getErrorReporter() const { return parserModule; }
const ParsedFile::Reader& getParsedFile() const { return content; }
ParsedFile::Reader getParsedFile() const { return content.getReader(); }
const Node& getRootNode() const { return rootNode; }
kj::StringPtr getSourceName() const { return parserModule.getSourceName(); }
......@@ -198,14 +203,16 @@ private:
const Compiler::Impl& compiler;
const Module& parserModule;
MallocMessageBuilder contentArena;
ParsedFile::Reader content;
Orphan<ParsedFile> content;
Node rootNode;
};
class Compiler::Impl: public SchemaLoader::LazyLoadCallback {
public:
Impl();
virtual ~Impl();
uint64_t add(Module& module, Mode mode) const;
const CompiledModule& add(const Module& parsedModule) const;
struct Workspace {
......@@ -214,18 +221,23 @@ public:
// compiler. Note that since nodes are compiled lazily, a new Workspace may have to be
// constructed in order to compile more nodes later.
kj::Arena arena;
// Arena for allocating temporary native objects.
MallocMessageBuilder message;
Orphanage orphanage;
// Orphanage for allocating temporary Cap'n Proto objects.
kj::Arena arena;
// Arena for allocating temporary native objects. Note that objects in `arena` may contain
// pointers into `message` that will be manipulated on destruction, so `arena` must be declared
// after `message`.
SchemaLoader bootstrapLoader;
// Loader used to load bootstrap schemas. The bootstrap schema nodes are similar to the final
// versions except that any value expressions which depend on knowledge of other types (e.g.
// default values for struct fields) are left unevaluated (the values in the schema are empty).
// These bootstrap schemas can then be plugged into the dynamic API and used to evaluate these
// remaining values.
inline Workspace(): orphanage(message.getOrphanage()) {}
};
const kj::Arena& getNodeArena() const { return nodeArena; }
......@@ -261,10 +273,7 @@ private:
// the pointer to point at said stack workspace. The rest of the compiler can assume that this
// Workspace is active.
uint workspaceRefcount = 0;
// Count of threads that have entered the compiler.
typedef std::unordered_map<Module*, kj::Own<CompiledModule>> ModuleMap;
typedef std::unordered_map<const Module*, kj::Own<CompiledModule>> ModuleMap;
kj::MutexGuarded<ModuleMap> modules;
// Map of parser modules to compiler modules.
......@@ -377,7 +386,7 @@ kj::StringPtr Compiler::Node::joinDisplayName(
result[separatorPos] = parent.parent == nullptr ? ':' : '.';
memcpy(result.begin() + separatorPos + 1, declName.begin(), declName.size());
result[result.size() - 1] = '\0';
return kj::StringPtr(declName.begin(), declName.size() - 1);
return kj::StringPtr(result.begin(), result.size() - 1);
}
const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimumState) const {
......@@ -459,8 +468,14 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
locked->translator = &workspace.arena.allocate<NodeTranslator>(
*this, module->getErrorReporter(), declaration, kj::mv(schemaNode));
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&](){
locked->bootstrapSchema = workspace.bootstrapLoader.loadOnce(
locked->translator->getBootstrapNode());
})) {
locked->bootstrapSchema = nullptr;
addError(kj::str("Internal compiler bug: Bootstrap schema failed validation: ",
exception->getDescription()));
}
// 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
......@@ -480,8 +495,14 @@ const Compiler::Node::Content& Compiler::Node::getContent(Content::State minimum
if (minimumState <= Content::BOOTSTRAP) break;
// Create the final schema.
KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&](){
locked->finalSchema = module->getCompiler().getFinalLoader().loadOnce(
locked->translator->finish());
})) {
locked->finalSchema = nullptr;
addError(kj::str("Internal compiler bug: Schema failed validation: ",
exception->getDescription()));
}
locked->advanceState(Content::FINISHED);
// no break
......@@ -584,19 +605,30 @@ kj::Maybe<const Compiler::Node&> Compiler::Node::lookup(const DeclName::Reader&
return *node;
}
Schema Compiler::Node::getBootstrapOrFinalSchema() const {
kj::Maybe<Schema> Compiler::Node::getBootstrapOrFinalSchema() const {
auto& content = getContent(Content::BOOTSTRAP);
if (__atomic_load_n(&content.state, __ATOMIC_ACQUIRE) == Content::FINISHED) {
if (__atomic_load_n(&content.state, __ATOMIC_ACQUIRE) == Content::FINISHED &&
content.finalSchema != nullptr) {
return content.finalSchema;
} else {
return content.bootstrapSchema;
}
}
Schema Compiler::Node::getFinalSchema() const {
kj::Maybe<Schema> Compiler::Node::getFinalSchema() const {
return getContent(Content::FINISHED).finalSchema;
}
void Compiler::Node::traverse(Compiler::Mode mode) const {
getFinalSchema();
if (mode == EAGER) {
for (auto& child: getContent(Content::EXPANDED).nestedNodes) {
child.second->traverse(mode);
}
}
}
void Compiler::Node::addError(kj::StringPtr error) const {
module->getErrorReporter().addError(startByte, endByte, error);
}
......@@ -608,7 +640,7 @@ kj::Maybe<NodeTranslator::Resolver::ResolvedName> Compiler::Node::resolve(
});
}
Schema Compiler::Node::resolveMaybeBootstrapSchema(uint64_t id) const {
kj::Maybe<Schema> Compiler::Node::resolveMaybeBootstrapSchema(uint64_t id) const {
KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
return node->getBootstrapOrFinalSchema();
} else {
......@@ -616,7 +648,7 @@ Schema Compiler::Node::resolveMaybeBootstrapSchema(uint64_t id) const {
}
}
Schema Compiler::Node::resolveFinalSchema(uint64_t id) const {
kj::Maybe<Schema> Compiler::Node::resolveFinalSchema(uint64_t id) const {
KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
return node->getFinalSchema();
} else {
......@@ -657,6 +689,19 @@ Compiler::Impl::Impl(): finalLoader(*this), workspace(nullptr) {
}
}
Compiler::Impl::~Impl() {}
const Compiler::CompiledModule& Compiler::Impl::add(const Module& parsedModule) const {
auto locked = modules.lockExclusive();
kj::Own<CompiledModule>& slot = (*locked)[&parsedModule];
if (slot.get() == nullptr) {
slot = kj::heap<CompiledModule>(*this, parsedModule);
}
return *slot;
}
uint64_t Compiler::Impl::addNode(uint64_t desiredId, Node& node) const {
auto lock = nodesById.lockExclusive();
for (;;) {
......@@ -698,6 +743,19 @@ kj::Maybe<const Compiler::Node&> Compiler::Impl::lookupBuiltin(kj::StringPtr nam
}
}
uint64_t Compiler::Impl::add(Module& module, Mode mode) const {
Impl::Workspace workspace;
auto lock = this->workspace.lockExclusive();
*lock = &workspace;
KJ_DEFER(*lock = nullptr);
auto& node = add(module).getRootNode();
if (mode != LAZY) {
node.traverse(mode);
}
return node.getId();
}
void Compiler::Impl::load(const SchemaLoader& loader, uint64_t id) const {
KJ_IF_MAYBE(node, findNode(id)) {
if (&loader == &finalLoader) {
......@@ -713,5 +771,18 @@ void Compiler::Impl::load(const SchemaLoader& loader, uint64_t id) const {
}
}
// =======================================================================================
Compiler::Compiler(): impl(kj::heap<Impl>()) {}
Compiler::~Compiler() {}
uint64_t Compiler::add(Module& module, Mode mode) const {
return impl->add(module, mode);
}
const SchemaLoader& Compiler::getLoader() const {
return impl->getFinalLoader();
}
} // namespace compiler
} // namespace capnp
......@@ -55,7 +55,7 @@ class Compiler {
// Cross-links separate modules (schema files) and translates them into schema nodes.
public:
explicit Compiler();
Compiler();
~Compiler();
KJ_DISALLOW_COPY(Compiler);
......@@ -76,8 +76,11 @@ public:
// use EAGER mode.
};
Schema add(Module& module, Mode mode) const;
// Add a module to the Compiler, returning its root Schema object.
uint64_t add(Module& module, Mode mode) const;
// Add a module to the Compiler, returning the module's file ID. The ID can then be used to
// look up the schema in the SchemaLoader returned by `getLoader()`. However, if there were any
// errors while compiling (reported via `module.addError()`), then the SchemaLoader may behave as
// if the node doesn't exist, or may return an invalid partial Schema.
const SchemaLoader& getLoader() const;
// Get a SchemaLoader backed by this compiler. Schema nodes will be lazily constructed as you
......
......@@ -74,7 +74,9 @@ public:
// remove it from the holes, and return its offset (as a multiple of its size). If there
// is no such space, returns zero (no hole can be at offset zero, as explained above).
if (holes[lgSize] != 0) {
if (lgSize >= KJ_ARRAY_SIZE(holes)) {
return nullptr;
} else if (holes[lgSize] != 0) {
UIntType result = holes[lgSize];
holes[lgSize] = 0;
return result;
......@@ -549,7 +551,7 @@ public:
}
};
Top& getTop();
Top& getTop() { return top; }
private:
Top top;
......@@ -835,7 +837,7 @@ public:
schema::StructNode::Builder builder) {
// Build the member-info-by-ordinal map.
MemberInfo root(layout.getTop());
traverseGroup(members, root);
traverseTopOrGroup(members, root);
// Init the root.
root.memberSchemas = builder.initMembers(root.childCount);
......@@ -872,6 +874,9 @@ public:
translator.compileDefaultDefaultValue(typeBuilder, fieldBuilder.initDefaultValue());
break;
}
} else {
translator.compileDefaultDefaultValue(typeBuilder, fieldBuilder.initDefaultValue());
}
int lgSize = -1;
switch (typeBuilder.getBody().which()) {
......@@ -906,7 +911,6 @@ public:
} else {
fieldBuilder.setOffset(member.fieldScope->addData(lgSize));
}
}
targetsFlagName = "targetsField";
break;
......@@ -1107,13 +1111,17 @@ private:
}
uint traverseGroup(List<Declaration>::Reader members, MemberInfo& parent) {
uint minOrdinal = std::numeric_limits<uint>::max();
uint codeOrder = 0;
if (members.size() < 2) {
errorReporter.addErrorOn(parent.decl, "Group must have at least two members.");
}
return traverseTopOrGroup(members, parent);
}
uint traverseTopOrGroup(List<Declaration>::Reader members, MemberInfo& parent) {
uint minOrdinal = std::numeric_limits<uint>::max();
uint codeOrder = 0;
for (auto member: members) {
uint ordinal = 0;
MemberInfo* memberInfo = nullptr;
......@@ -1122,6 +1130,7 @@ private:
case Declaration::Body::FIELD_DECL: {
memberInfo = &arena.allocate<MemberInfo>(
parent, codeOrder++, member, *parent.fieldScope);
ordinal = member.getId().getOrdinal().getValue();
break;
}
......@@ -1258,13 +1267,14 @@ bool NodeTranslator::compileType(TypeExpression::Reader source, schema::Type::Bu
if (source.getParams().size() != 0) {
errorReporter.addErrorOn(source, kj::str(
"'", declNameString(name), "' does not accept parameters."));
}
return false;
}
}
return true;
} else {
target.getBody().setVoidType();
return false;
}
}
......@@ -1305,63 +1315,59 @@ public:
: type(FIELD), structBuilder(structBuilder), member(member) {}
DynamicSlot(DynamicList::Builder listBuilder, uint index)
: type(ELEMENT), listBuilder(listBuilder), index(index) {}
DynamicSlot(DynamicStruct::Builder structBuilder, StructSchema::Member member,
DynamicSlot(DynamicUnion::Builder unionBuilder, StructSchema::Member unionMember)
: type(UNION_MEMBER), unionBuilder(unionBuilder), unionMember(unionMember) {}
DynamicSlot(DynamicUnion::Builder unionBuilder, StructSchema::Member unionMember,
StructSchema structMemberSchema)
: type(STRUCT_OBJECT_FIELD), structBuilder(structBuilder), member(member),
: type(STRUCT_OBJECT_UNION_MEMBER), unionBuilder(unionBuilder), unionMember(unionMember),
structMemberSchema(structMemberSchema) {}
DynamicSlot(DynamicStruct::Builder structBuilder, StructSchema::Member member,
DynamicSlot(DynamicUnion::Builder unionBuilder, StructSchema::Member unionMember,
ListSchema listMemberSchema)
: type(LIST_OBJECT_FIELD), structBuilder(structBuilder), member(member),
: type(LIST_OBJECT_UNION_MEMBER), unionBuilder(unionBuilder), unionMember(unionMember),
listMemberSchema(listMemberSchema) {}
DynamicSlot(DynamicUnion::Builder unionBuilder, StructSchema::Member unionMember)
: type(UNION_MEMBER), unionBuilder(unionBuilder), unionMember(unionMember) {}
bool wasSet = false;
DynamicStruct::Builder initStruct() {
wasSet = true;
switch (type) {
case FIELD: return structBuilder.init(member).as<DynamicStruct>();
case ELEMENT: return listBuilder[index].as<DynamicStruct>();
case STRUCT_OBJECT_FIELD: return structBuilder.initObject(member, structMemberSchema);
case LIST_OBJECT_FIELD: KJ_FAIL_REQUIRE("Value type mismatch.");
case UNION_MEMBER: return unionBuilder.init(unionMember).as<DynamicStruct>();
case STRUCT_OBJECT_UNION_MEMBER:
return unionBuilder.initObject(unionMember, structMemberSchema);
case LIST_OBJECT_UNION_MEMBER: KJ_FAIL_REQUIRE("Type mismatch.");
}
KJ_FAIL_ASSERT("can't get here");
}
DynamicList::Builder initList(uint size) {
wasSet = true;
switch (type) {
case FIELD: return structBuilder.init(member, size).as<DynamicList>();
case ELEMENT: return listBuilder.init(index, size).as<DynamicList>();
case STRUCT_OBJECT_FIELD: KJ_FAIL_REQUIRE("Value type mismatch.");
case LIST_OBJECT_FIELD: return structBuilder.initObject(member, listMemberSchema, size);
case UNION_MEMBER: return unionBuilder.init(unionMember, size).as<DynamicList>();
case STRUCT_OBJECT_UNION_MEMBER: KJ_FAIL_REQUIRE("Type mismatch.");
case LIST_OBJECT_UNION_MEMBER:
return unionBuilder.initObject(unionMember, listMemberSchema, size);
}
KJ_FAIL_ASSERT("can't get here");
}
DynamicUnion::Builder getUnion() {
wasSet = true;
switch (type) {
case FIELD: return structBuilder.get(member).as<DynamicUnion>();
case ELEMENT: KJ_FAIL_REQUIRE("Value type mismatch.");
case STRUCT_OBJECT_FIELD: KJ_FAIL_REQUIRE("Value type mismatch.");
case LIST_OBJECT_FIELD: KJ_FAIL_REQUIRE("Value type mismatch.");
case ELEMENT: KJ_FAIL_REQUIRE("Type mismatch.");
case UNION_MEMBER: return unionBuilder.init(unionMember).as<DynamicUnion>();
case STRUCT_OBJECT_UNION_MEMBER: KJ_FAIL_REQUIRE("Type mismatch.");
case LIST_OBJECT_UNION_MEMBER: KJ_FAIL_REQUIRE("Type mismatch.");
}
KJ_FAIL_ASSERT("can't get here");
}
void set(DynamicValue::Reader value) {
wasSet = true;
switch (type) {
case FIELD: return structBuilder.set(member, value);
case ELEMENT: return listBuilder.set(index, value);
case STRUCT_OBJECT_FIELD: return structBuilder.set(member, value);
case LIST_OBJECT_FIELD: return structBuilder.set(member, value);
case UNION_MEMBER: return unionBuilder.set(unionMember, value);
case STRUCT_OBJECT_UNION_MEMBER: return unionBuilder.set(unionMember, value);
case LIST_OBJECT_UNION_MEMBER: return unionBuilder.set(unionMember, value);
}
KJ_FAIL_ASSERT("can't get here");
}
......@@ -1379,16 +1385,16 @@ public:
}
return nullptr;
}
case STRUCT_OBJECT_FIELD: return nullptr;
case LIST_OBJECT_FIELD: return nullptr;
case UNION_MEMBER: return enumIdForMember(unionMember);
case STRUCT_OBJECT_UNION_MEMBER: return nullptr;
case LIST_OBJECT_UNION_MEMBER: return nullptr;
}
KJ_FAIL_ASSERT("can't get here");
}
private:
enum Type {
FIELD, ELEMENT, STRUCT_OBJECT_FIELD, LIST_OBJECT_FIELD, UNION_MEMBER
FIELD, ELEMENT, UNION_MEMBER, STRUCT_OBJECT_UNION_MEMBER, LIST_OBJECT_UNION_MEMBER
};
Type type;
......@@ -1396,10 +1402,6 @@ private:
struct {
DynamicStruct::Builder structBuilder;
StructSchema::Member member;
union {
StructSchema structMemberSchema;
ListSchema listMemberSchema;
};
};
struct {
DynamicList::Builder listBuilder;
......@@ -1408,6 +1410,10 @@ private:
struct {
DynamicUnion::Builder unionBuilder;
StructSchema::Member unionMember;
union {
StructSchema structMemberSchema;
ListSchema listMemberSchema;
};
};
};
......@@ -1451,6 +1457,10 @@ static kj::StringPtr getValueUnionMemberNameFor(schema::Type::Body::Which type)
void NodeTranslator::compileBootstrapValue(ValueExpression::Reader source,
schema::Type::Reader type,
schema::Value::Builder target) {
// Start by filling in a default default value so that if for whatever reason we don't end up
// initializing the value, this won't cause schema validation to fail.
compileDefaultDefaultValue(type, target);
switch (type.getBody().which()) {
case schema::Type::Body::LIST_TYPE:
case schema::Type::Body::STRUCT_TYPE:
......@@ -1471,11 +1481,24 @@ void NodeTranslator::compileValue(ValueExpression::Reader source, schema::Type::
auto valueUnion = toDynamic(target).get("body").as<DynamicUnion>();
auto member = valueUnion.getSchema().getMemberByName(
getValueUnionMemberNameFor(type.getBody().which()));
switch (type.getBody().which()) {
case schema::Type::Body::LIST_TYPE:
KJ_IF_MAYBE(listSchema, makeListSchemaOf(type.getBody().getListType())) {
DynamicSlot slot(valueUnion, member, *listSchema);
compileValue(source, slot, isBootstrap);
}
break;
case schema::Type::Body::STRUCT_TYPE:
KJ_IF_MAYBE(structSchema, resolver.resolveMaybeBootstrapSchema(
type.getBody().getStructType())) {
DynamicSlot slot(valueUnion, member, structSchema->asStruct());
compileValue(source, slot, isBootstrap);
}
break;
default:
DynamicSlot slot(valueUnion, member);
compileValue(source, slot, isBootstrap);
if (!slot.wasSet) {
// An error should have been reported already. Initialize to a reasonable default.
compileDefaultDefaultValue(type, target);
break;
}
}
......@@ -1487,8 +1510,7 @@ void NodeTranslator::compileValue(ValueExpression::Reader src, DynamicSlot& dst,
// way to test for type compatibility without throwing.
KJ_IF_MAYBE(exception, kj::runCatchingExceptions(
[&]() { compileValueInner(src, dst, isBootstrap); })) {
errorReporter.addErrorOn(src, exception->getDescription());
dst.wasSet = false;
errorReporter.addErrorOn(src, "Type mismatch.");
}
}
......@@ -1499,32 +1521,45 @@ void NodeTranslator::compileValueInner(
auto name = src.getBody().getName();
bool isBare = name.getBase().which() == DeclName::Base::RELATIVE_NAME &&
name.getMemberPath().size() == 0;
bool wasSet = false;
if (isBare) {
// The name is just a bare identifier. It may be a literal value or an enumerant.
kj::StringPtr id = name.getBase().getRelativeName().getValue();
KJ_IF_MAYBE(enumId, dst.getEnumType()) {
auto enumSchema = resolver.resolveMaybeBootstrapSchema(*enumId).asEnum();
KJ_IF_MAYBE(enumerant, enumSchema.findEnumerantByName(id)) {
KJ_IF_MAYBE(enumSchema, resolver.resolveMaybeBootstrapSchema(*enumId)) {
KJ_IF_MAYBE(enumerant, enumSchema->asEnum().findEnumerantByName(id)) {
dst.set(DynamicEnum(*enumerant));
wasSet = true;
}
} else {
// Enum type is broken. We don't want to report a redundant error here, so just assume
// we would have found a matching enumerant.
dst.set(kj::implicitCast<uint16_t>(0));
wasSet = true;
}
} else {
// Interpret known constant values.
if (id == "void") {
dst.set(Void::VOID);
wasSet = true;
} else if (id == "true") {
dst.set(true);
wasSet = true;
} else if (id == "false") {
dst.set(false);
wasSet = true;
} else if (id == "nan") {
dst.set(std::numeric_limits<double>::quiet_NaN());
wasSet = true;
} else if (id == "inf") {
dst.set(std::numeric_limits<double>::infinity());
wasSet = true;
}
}
}
if (!dst.wasSet) {
if (!wasSet) {
// Haven't resolved the name yet. Try looking up a constant.
KJ_IF_MAYBE(constValue, readConstant(src.getBody().getName(), isBootstrap, src)) {
dst.set(*constValue);
......@@ -1618,7 +1653,7 @@ void NodeTranslator::copyValue(schema::Value::Reader src, schema::Type::Reader s
KJ_IF_MAYBE(exception, kj::runCatchingExceptions(
[&]() { dstBody.set(dstFieldName, srcBody.get()); })) {
// Exception caught, therefore the types are not compatible.
errorReporter.addErrorOn(errorLocation, exception->getDescription());
errorReporter.addErrorOn(errorLocation, "Type mismatch.");
}
} else {
KJ_FAIL_ASSERT("Didn't recognize schema::Value::Body type?");
......@@ -1641,10 +1676,11 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
//
// We need to be very careful not to query this Schema's dependencies because if it is
// a final schema then this query could trigger a lazy load which would deadlock.
Schema constSchema = isBootstrap ?
kj::Maybe<Schema> maybeConstSchema = isBootstrap ?
resolver.resolveMaybeBootstrapSchema(resolved->id) :
resolver.resolveFinalSchema(resolved->id);
auto constReader = constSchema.getProto().getBody().getConstNode();
KJ_IF_MAYBE(constSchema, maybeConstSchema) {
auto constReader = constSchema->getProto().getBody().getConstNode();
auto constValue = toDynamic(constReader.getValue()).get("body").as<DynamicUnion>().get();
if (constValue.getType() == DynamicValue::OBJECT) {
......@@ -1653,11 +1689,21 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
auto constType = constReader.getType();
switch (constType.getBody().which()) {
case schema::Type::Body::STRUCT_TYPE:
constValue = objValue.as(resolver.resolveMaybeBootstrapSchema(
constType.getBody().getStructType()).asStruct());
KJ_IF_MAYBE(structSchema, resolver.resolveMaybeBootstrapSchema(
constType.getBody().getStructType())) {
constValue = objValue.as(structSchema->asStruct());
} else {
// The struct's schema is broken for reasons already reported.
return nullptr;
}
break;
case schema::Type::Body::LIST_TYPE:
constValue = objValue.as(makeListSchemaOf(constType.getBody().getListType()));
KJ_IF_MAYBE(listSchema, makeListSchemaOf(constType.getBody().getListType())) {
constValue = objValue.as(*listSchema);
} else {
// The list's schema is broken for reasons already reported.
return nullptr;
}
break;
case schema::Type::Body::OBJECT_TYPE:
// Fine as-is.
......@@ -1673,8 +1719,9 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
// A fully unqualified identifier looks like it might refer to a constant visible in the
// current scope, but if that's really what the user wanted, we want them to use a
// qualified name to make it more obvious. Report an error.
Schema scope = resolver.resolveMaybeBootstrapSchema(constSchema.getProto().getScopeId());
auto scopeReader = scope.getProto();
KJ_IF_MAYBE(scope, resolver.resolveMaybeBootstrapSchema(
constSchema->getProto().getScopeId())) {
auto scopeReader = scope->getProto();
kj::StringPtr parent;
if (scopeReader.getBody().which() == schema::Node::Body::FILE_NODE) {
parent = "";
......@@ -1688,25 +1735,46 @@ kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant(
declNameString(name), "' with '", parent, ".", id,
"', if that's what you intended."));
}
}
return constValue;
} else {
// The target is a constant, but the constant's schema is broken for reasons already reported.
return nullptr;
}
} else {
// Lookup will have reported an error.
return nullptr;
}
}
ListSchema NodeTranslator::makeListSchemaOf(schema::Type::Reader elementType) {
kj::Maybe<ListSchema> NodeTranslator::makeListSchemaOf(schema::Type::Reader elementType) {
auto body = elementType.getBody();
switch (body.which()) {
case schema::Type::Body::ENUM_TYPE:
return ListSchema::of(resolver.resolveMaybeBootstrapSchema(body.getEnumType()).asEnum());
KJ_IF_MAYBE(enumSchema, resolver.resolveMaybeBootstrapSchema(body.getEnumType())) {
return ListSchema::of(enumSchema->asEnum());
} else {
return nullptr;
}
case schema::Type::Body::STRUCT_TYPE:
return ListSchema::of(resolver.resolveMaybeBootstrapSchema(body.getStructType()).asStruct());
KJ_IF_MAYBE(structSchema, resolver.resolveMaybeBootstrapSchema(body.getStructType())) {
return ListSchema::of(structSchema->asStruct());
} else {
return nullptr;
}
case schema::Type::Body::INTERFACE_TYPE:
return ListSchema::of(resolver.resolveMaybeBootstrapSchema(body.getInterfaceType())
.asInterface());
KJ_IF_MAYBE(interfaceSchema, resolver.resolveMaybeBootstrapSchema(body.getInterfaceType())) {
return ListSchema::of(interfaceSchema->asInterface());
} else {
return nullptr;
}
case schema::Type::Body::LIST_TYPE:
return ListSchema::of(makeListSchemaOf(body.getListType()));
KJ_IF_MAYBE(listSchema, makeListSchemaOf(body.getListType())) {
return ListSchema::of(*listSchema);
} else {
return nullptr;
}
default:
return ListSchema::of(body.which());
}
......@@ -1727,6 +1795,10 @@ Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications(
for (uint i = 0; i < annotations.size(); i++) {
Declaration::AnnotationApplication::Reader annotation = annotations[i];
schema::Annotation::Builder annotationBuilder = builder[i];
// Set the annotation's value to void in case we fail to produce something better below.
annotationBuilder.initValue().getBody().setVoidValue();
auto name = annotation.getName();
KJ_IF_MAYBE(decl, resolver.resolve(name)) {
if (decl->kind != Declaration::Body::ANNOTATION_DECL) {
......@@ -1734,8 +1806,8 @@ Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications(
"'", declNameString(name), "' is not an annotation."));
} else {
annotationBuilder.setId(decl->id);
auto node = resolver.resolveMaybeBootstrapSchema(decl->id).getProto()
.getBody().getAnnotationNode();
KJ_IF_MAYBE(annotationSchema, resolver.resolveMaybeBootstrapSchema(decl->id)) {
auto node = annotationSchema->getProto().getBody().getAnnotationNode();
if (!toDynamic(node).get(targetsFlagName).as<bool>()) {
errorReporter.addErrorOn(name, kj::str(
"'", declNameString(name), "' cannot be applied to this kind of declaration."));
......@@ -1747,20 +1819,20 @@ Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications(
case Declaration::AnnotationApplication::Value::NONE:
// No value, i.e. void.
if (node.getType().getBody().which() == schema::Type::Body::VOID_TYPE) {
annotationBuilder.initValue().getBody().setVoidValue();
annotationBuilder.getValue().getBody().setVoidValue();
} else {
errorReporter.addErrorOn(name, kj::str(
"'", declNameString(name), "' requires a value."));
compileDefaultDefaultValue(node.getType(), annotationBuilder.initValue());
compileDefaultDefaultValue(node.getType(), annotationBuilder.getValue());
}
break;
case Declaration::AnnotationApplication::Value::EXPRESSION:
compileBootstrapValue(value.getExpression(), node.getType(),
annotationBuilder.initValue());
annotationBuilder.getValue());
break;
}
}
}
}
}
......
......@@ -54,15 +54,17 @@ public:
// Look up the given name, relative to this node, and return basic information about the
// target.
virtual Schema resolveMaybeBootstrapSchema(uint64_t id) const = 0;
virtual kj::Maybe<Schema> resolveMaybeBootstrapSchema(uint64_t id) const = 0;
// Get the schema for the given ID. Returning either a bootstrap schema or a final schema
// is acceptable. Throws an exception if the id is not one that was found by calling resolve()
// or by traversing other schemas.
// or by 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.
virtual Schema resolveFinalSchema(uint64_t id) const = 0;
virtual kj::Maybe<Schema> resolveFinalSchema(uint64_t id) const = 0;
// Get the final schema for the given ID. A bootstrap schema is not acceptable. Throws an
// exception if the id is not one that was found by calling resolve() or by traversing other
// schemas.
// schemas. Returns null if the ID is recognized, but the corresponding schema node failed to
// be built for reasons that were already reported.
};
NodeTranslator(const Resolver& resolver, const ErrorReporter& errorReporter,
......@@ -162,10 +164,12 @@ private:
kj::Maybe<DynamicValue::Reader> readConstant(DeclName::Reader name, bool isBootstrap,
ValueExpression::Reader errorLocation);
// Get the value of the given constant.
// Get the value of the given constant. May return null if some error occurs, which will already
// have been reported.
ListSchema makeListSchemaOf(schema::Type::Reader elementType);
// Construct a list schema representing a list of elements of the given type.
kj::Maybe<ListSchema> makeListSchemaOf(schema::Type::Reader elementType);
// Construct a list schema representing a list of elements of the given type. May return null if
// some error occurs, which will already have been reported.
Orphan<List<schema::Annotation>> compileAnnotationApplications(
List<Declaration::AnnotationApplication>::Reader annotations,
......
......@@ -1585,7 +1585,7 @@ struct WireHelpers {
}
static void adopt(SegmentBuilder* segment, WirePointer* ref, OrphanBuilder&& value) {
KJ_REQUIRE(value.segment->getArena() == segment->getArena(),
KJ_REQUIRE(value.segment == nullptr || value.segment->getArena() == segment->getArena(),
"Adopted object must live in the same message.");
if (!ref->isNull()) {
......
......@@ -733,7 +733,7 @@ public:
KJ_DISALLOW_COPY(Deferred);
// This move constructor is usually optimized away by the compiler.
inline Deferred(Deferred&& other): func(kj::mv(other.func)) {
inline Deferred(Deferred&& other): func(kj::mv(other.func)), canceled(false) {
other.canceled = true;
}
private:
......
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