Commit b0730966 authored by Kenton Varda's avatar Kenton Varda

More compiler WIP

parent 0da57538
...@@ -89,8 +89,8 @@ public: ...@@ -89,8 +89,8 @@ public:
// implements NodeTranslator::Resolver ----------------------------- // implements NodeTranslator::Resolver -----------------------------
kj::Maybe<ResolvedName> resolve(const DeclName::Reader& name) const override; kj::Maybe<ResolvedName> resolve(const DeclName::Reader& name) const override;
kj::Maybe<Schema> resolveMaybeBootstrapSchema(uint64_t id) const override; Schema resolveMaybeBootstrapSchema(uint64_t id) const override;
kj::Maybe<Schema> resolveFinalSchema(uint64_t id) const override; Schema resolveFinalSchema(uint64_t id) const override;
private: private:
const CompiledModule* module; // null iff isBuiltin is true const CompiledModule* module; // null iff isBuiltin is true
...@@ -608,14 +608,20 @@ kj::Maybe<NodeTranslator::Resolver::ResolvedName> Compiler::Node::resolve( ...@@ -608,14 +608,20 @@ kj::Maybe<NodeTranslator::Resolver::ResolvedName> Compiler::Node::resolve(
}); });
} }
kj::Maybe<Schema> Compiler::Node::resolveMaybeBootstrapSchema(uint64_t id) const { Schema Compiler::Node::resolveMaybeBootstrapSchema(uint64_t id) const {
return module->getCompiler().findNode(id).map( KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
[](const Node& node) { return node.getBootstrapOrFinalSchema(); }); return node->getBootstrapOrFinalSchema();
} else {
KJ_FAIL_REQUIRE("Tried to get schema for ID we haven't seen before.");
}
} }
kj::Maybe<Schema> Compiler::Node::resolveFinalSchema(uint64_t id) const { Schema Compiler::Node::resolveFinalSchema(uint64_t id) const {
return module->getCompiler().findNode(id).map( KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
[](const Node& node) { return node.getFinalSchema(); }); return node->getFinalSchema();
} else {
KJ_FAIL_REQUIRE("Tried to get schema for ID we haven't seen before.");
}
} }
// ======================================================================================= // =======================================================================================
...@@ -644,19 +650,7 @@ Compiler::Impl::Impl(): finalLoader(*this), workspace(nullptr) { ...@@ -644,19 +650,7 @@ Compiler::Impl::Impl(): finalLoader(*this), workspace(nullptr) {
for (auto member: declBodySchema.getMembers()) { for (auto member: declBodySchema.getMembers()) {
auto name = member.getProto().getName(); auto name = member.getProto().getName();
if (name.startsWith("builtin")) { if (name.startsWith("builtin")) {
kj::StringPtr symbolName; kj::StringPtr symbolName = name.slice(strlen("builtin"));
if (name.endsWith("Value")) {
// e.g. "builtinTrueValue" -- transform this to "true".
auto substr = name.slice(strlen("builtin"), name.size() - strlen("Value"));
kj::ArrayPtr<char> array = nodeArena.allocateArray<char>(substr.size() + 1);
memcpy(array.begin(), substr.begin(), substr.size());
array[substr.size()] = '\0';
array[0] += 'a' - 'A';
symbolName = kj::StringPtr(array.begin(), array.size() - 1);
} else {
// e.g. "builtinVoid" -- transform this to "Void".
symbolName = name.slice(strlen("builtin"));
}
builtinDecls[symbolName] = nodeArena.allocateOwn<Node>( builtinDecls[symbolName] = nodeArena.allocateOwn<Node>(
symbolName, static_cast<Declaration::Body::Which>(member.getIndex())); symbolName, static_cast<Declaration::Body::Which>(member.getIndex()));
} }
......
...@@ -95,7 +95,7 @@ struct ValueExpression { ...@@ -95,7 +95,7 @@ struct ValueExpression {
negativeInt @3 :UInt64; negativeInt @3 :UInt64;
float @4 :Float64; float @4 :Float64;
string @5 :Text; string @5 :Text;
identifier @6 :Text; name @6 :DeclName;
list @7 :List(ValueExpression); list @7 :List(ValueExpression);
structValue @8 :List(FieldAssignment); structValue @8 :List(FieldAssignment);
unionValue @9 :FieldAssignment; unionValue @9 :FieldAssignment;
...@@ -160,7 +160,7 @@ struct Declaration { ...@@ -160,7 +160,7 @@ struct Declaration {
# so that the compiler can handle symbol name lookups more uniformly. # so that the compiler can handle symbol name lookups more uniformly.
# #
# New union members added here will magically become visible in the global scope. # New union members added here will magically become visible in the global scope.
# "builtinFoo" becomes visible as "Foo", while "builtinFooValue" becomes visible as "foo". # E.g. "builtinFoo" becomes visible as "Foo".
builtinVoid @25 :Void; builtinVoid @25 :Void;
builtinBool @26 :Void; builtinBool @26 :Void;
builtinInt8 @27 :Void; builtinInt8 @27 :Void;
...@@ -177,10 +177,6 @@ struct Declaration { ...@@ -177,10 +177,6 @@ struct Declaration {
builtinData @38 :Void; builtinData @38 :Void;
builtinList @39 :Void; builtinList @39 :Void;
builtinObject @40 :Void; builtinObject @40 :Void;
builtinTrueValue @41 :Void;
builtinFalseValue @42 :Void;
builtinVoidValue @43 :Void;
} }
struct File {} struct File {}
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include <kj/arena.h> #include <kj/arena.h>
#include <set> #include <set>
#include <map> #include <map>
#include <limits.h> #include <limits>
namespace capnp { namespace capnp {
namespace compiler { namespace compiler {
...@@ -38,7 +38,7 @@ class NodeTranslator::StructLayout { ...@@ -38,7 +38,7 @@ class NodeTranslator::StructLayout {
public: public:
template <typename UIntType> template <typename UIntType>
struct HoleSet { struct HoleSet {
inline HoleSet() {} inline HoleSet(): holes{0, 0, 0, 0, 0, 0} {}
// Represents a set of "holes" within a segment of allocated space, up to one hole of each // Represents a set of "holes" within a segment of allocated space, up to one hole of each
// power-of-two size between 1 bit and 32 bits. // power-of-two size between 1 bit and 32 bits.
...@@ -62,7 +62,7 @@ public: ...@@ -62,7 +62,7 @@ public:
// a series of holes between N and 64 bits, as described in point (1). Thus, again, there // a series of holes between N and 64 bits, as described in point (1). Thus, again, there
// is still at most one hole of each size, and the largest hole is 32 bits. // is still at most one hole of each size, and the largest hole is 32 bits.
UIntType holes[6] = {0, 0, 0, 0, 0, 0}; UIntType holes[6];
// The offset of each hole as a multiple of its size. A value of zero indicates that no hole // The offset of each hole as a multiple of its size. A value of zero indicates that no hole
// exists. Notice that it is impossible for any actual hole to have an offset of zero, because // exists. Notice that it is impossible for any actual hole to have an offset of zero, because
// the first field allocated is always placed at the very beginning of the section. So either // the first field allocated is always placed at the very beginning of the section. So either
...@@ -138,7 +138,7 @@ public: ...@@ -138,7 +138,7 @@ public:
} }
} }
kj::Maybe<int> smallestAtLeast(uint size) { kj::Maybe<uint> smallestAtLeast(uint size) {
// Return the size of the smallest hole that is equal to or larger than the given size. // Return the size of the smallest hole that is equal to or larger than the given size.
for (uint i = size; i < KJ_ARRAY_SIZE(holes); i++) { for (uint i = size; i < KJ_ARRAY_SIZE(holes); i++) {
...@@ -148,6 +148,20 @@ public: ...@@ -148,6 +148,20 @@ public:
} }
return nullptr; return nullptr;
} }
uint getFirstWordUsed() {
// Computes the lg of the amount of space used in the first word of the section.
// If there is a 32-bit hole with a 32-bit offset, no more than the first 32 bits are used.
// If no more than the first 32 bits are used, and there is a 16-bit hole with a 16-bit
// offset, then no more than the first 16 bits are used. And so on.
for (uint i = KJ_ARRAY_SIZE(holes); i > 0; i--) {
if (holes[i - 1] != 1) {
return i;
}
}
return 0;
}
}; };
struct StructOrGroup { struct StructOrGroup {
...@@ -461,7 +475,7 @@ public: ...@@ -461,7 +475,7 @@ public:
KJ_DISALLOW_COPY(Group); KJ_DISALLOW_COPY(Group);
uint addData(uint lgSize) override { uint addData(uint lgSize) override {
uint bestSize = UINT_MAX; uint bestSize = std::numeric_limits<uint>::max();
kj::Maybe<uint> bestLocation = nullptr; kj::Maybe<uint> bestLocation = nullptr;
for (uint i = 0; i < parent.dataLocations.size(); i++) { for (uint i = 0; i < parent.dataLocations.size(); i++) {
...@@ -565,27 +579,35 @@ schema::Node::Reader NodeTranslator::finish() { ...@@ -565,27 +579,35 @@ schema::Node::Reader NodeTranslator::finish() {
void NodeTranslator::compileNode(Declaration::Reader decl, schema::Node::Builder builder) { void NodeTranslator::compileNode(Declaration::Reader decl, schema::Node::Builder builder) {
checkMembers(decl.getNestedDecls(), decl.getBody().which()); checkMembers(decl.getNestedDecls(), decl.getBody().which());
kj::StringPtr targetsFlagName;
switch (decl.getBody().which()) { switch (decl.getBody().which()) {
case Declaration::Body::FILE_DECL: case Declaration::Body::FILE_DECL:
compileFile(decl, builder.getBody().initFileNode()); compileFile(decl, builder.getBody().initFileNode());
targetsFlagName = "targetsFile";
break; break;
case Declaration::Body::CONST_DECL: case Declaration::Body::CONST_DECL:
compileConst(decl.getBody().getConstDecl(), builder.getBody().initConstNode()); compileConst(decl.getBody().getConstDecl(), builder.getBody().initConstNode());
targetsFlagName = "targetsConst";
break; break;
case Declaration::Body::ANNOTATION_DECL: case Declaration::Body::ANNOTATION_DECL:
compileAnnotation(decl.getBody().getAnnotationDecl(), builder.getBody().initAnnotationNode()); compileAnnotation(decl.getBody().getAnnotationDecl(), builder.getBody().initAnnotationNode());
targetsFlagName = "targetsAnnotation";
break; break;
case Declaration::Body::ENUM_DECL: case Declaration::Body::ENUM_DECL:
compileEnum(decl.getBody().getEnumDecl(), decl.getNestedDecls(), compileEnum(decl.getBody().getEnumDecl(), decl.getNestedDecls(),
builder.getBody().initEnumNode()); builder.getBody().initEnumNode());
targetsFlagName = "targetsEnum";
break; break;
case Declaration::Body::STRUCT_DECL: case Declaration::Body::STRUCT_DECL:
compileStruct(decl.getBody().getStructDecl(), decl.getNestedDecls(), compileStruct(decl.getBody().getStructDecl(), decl.getNestedDecls(),
builder.getBody().initStructNode()); builder.getBody().initStructNode());
targetsFlagName = "targetsStruct";
break; break;
case Declaration::Body::INTERFACE_DECL: case Declaration::Body::INTERFACE_DECL:
compileInterface(decl.getBody().getInterfaceDecl(), decl.getNestedDecls(), compileInterface(decl.getBody().getInterfaceDecl(), decl.getNestedDecls(),
builder.getBody().initInterfaceNode()); builder.getBody().initInterfaceNode());
targetsFlagName = "targetsInterface";
break; break;
default: default:
...@@ -593,7 +615,7 @@ void NodeTranslator::compileNode(Declaration::Reader decl, schema::Node::Builder ...@@ -593,7 +615,7 @@ void NodeTranslator::compileNode(Declaration::Reader decl, schema::Node::Builder
break; break;
} }
// TODO(now): annotations builder.adoptAnnotations(compileAnnotationApplications(decl.getAnnotations(), targetsFlagName));
} }
void NodeTranslator::checkMembers( void NodeTranslator::checkMembers(
...@@ -927,6 +949,34 @@ public: ...@@ -927,6 +949,34 @@ public:
KJ_FAIL_ASSERT("addDiscriminant() didn't set the offset?"); KJ_FAIL_ASSERT("addDiscriminant() didn't set the offset?");
} }
} }
// And fill in the sizes.
builder.setDataSectionWordSize(layout.getTop().dataWordCount);
builder.setPointerSectionSize(layout.getTop().pointerCount);
builder.setPreferredListEncoding(schema::ElementSize::INLINE_COMPOSITE);
if (layout.getTop().pointerCount == 0) {
if (layout.getTop().dataWordCount == 0) {
builder.setPreferredListEncoding(schema::ElementSize::EMPTY);
} else if (layout.getTop().dataWordCount == 1) {
KJ_IF_MAYBE(smallestHole, layout.getTop().holes.smallestAtLeast(0)) {
}
switch (layout.getTop().holes.getFirstWordUsed()) {
case 0: builder.setPreferredListEncoding(schema::ElementSize::BIT); break;
case 1:
case 2:
case 3: builder.setPreferredListEncoding(schema::ElementSize::BYTE); break;
case 4: builder.setPreferredListEncoding(schema::ElementSize::TWO_BYTES); break;
case 5: builder.setPreferredListEncoding(schema::ElementSize::FOUR_BYTES); break;
case 6: builder.setPreferredListEncoding(schema::ElementSize::EIGHT_BYTES); break;
default: KJ_FAIL_ASSERT("Expected 0, 1, 2, 3, 4, 5, or 6."); break;
}
}
} else if (layout.getTop().pointerCount == 1 &&
layout.getTop().dataWordCount == 0) {
builder.setPreferredListEncoding(schema::ElementSize::POINTER);
}
} }
private: private:
...@@ -1009,7 +1059,7 @@ private: ...@@ -1009,7 +1059,7 @@ private:
// Unions that need to have their discriminant offsets filled in after layout is complete. // Unions that need to have their discriminant offsets filled in after layout is complete.
uint traverseUnion(List<Declaration>::Reader members, MemberInfo& parent) { uint traverseUnion(List<Declaration>::Reader members, MemberInfo& parent) {
uint minOrdinal = UINT_MAX; uint minOrdinal = std::numeric_limits<uint>::max();
uint codeOrder = 0; uint codeOrder = 0;
if (members.size() < 2) { if (members.size() < 2) {
...@@ -1057,7 +1107,7 @@ private: ...@@ -1057,7 +1107,7 @@ private:
} }
uint traverseGroup(List<Declaration>::Reader members, MemberInfo& parent) { uint traverseGroup(List<Declaration>::Reader members, MemberInfo& parent) {
uint minOrdinal = UINT_MAX; uint minOrdinal = std::numeric_limits<uint>::max();
uint codeOrder = 0; uint codeOrder = 0;
if (members.size() < 2) { if (members.size() < 2) {
...@@ -1110,7 +1160,7 @@ private: ...@@ -1110,7 +1160,7 @@ private:
void NodeTranslator::compileStruct(Declaration::Struct::Reader decl, void NodeTranslator::compileStruct(Declaration::Struct::Reader decl,
List<Declaration>::Reader members, List<Declaration>::Reader members,
schema::StructNode::Builder builder) { schema::StructNode::Builder builder) {
StructTranslator(*this).translate(decl, members, builder);
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
...@@ -1118,13 +1168,354 @@ void NodeTranslator::compileStruct(Declaration::Struct::Reader decl, ...@@ -1118,13 +1168,354 @@ void NodeTranslator::compileStruct(Declaration::Struct::Reader decl,
void NodeTranslator::compileInterface(Declaration::Interface::Reader decl, void NodeTranslator::compileInterface(Declaration::Interface::Reader decl,
List<Declaration>::Reader members, List<Declaration>::Reader members,
schema::InterfaceNode::Builder builder) { schema::InterfaceNode::Builder builder) {
KJ_FAIL_ASSERT("TODO: compile interfaces");
}
// -------------------------------------------------------------------
static kj::String declNameString(DeclName::Reader name) {
kj::String prefix;
switch (name.getBase().which()) {
case DeclName::Base::RELATIVE_NAME:
prefix = kj::str(name.getBase().getRelativeName());
break;
case DeclName::Base::ABSOLUTE_NAME:
prefix = kj::str(".", name.getBase().getAbsoluteName());
break;
case DeclName::Base::IMPORT_NAME:
prefix = kj::str("import \"", name.getBase().getImportName(), "\"");
break;
}
if (name.getMemberPath().size() == 0) {
return prefix;
} else {
auto path = name.getMemberPath();
KJ_STACK_ARRAY(kj::StringPtr, parts, path.size(), 16, 16);
for (size_t i = 0; i < parts.size(); i++) {
parts[i] = path[i].getValue();
}
return kj::str(prefix, ".", kj::strArray(parts, "."));
}
}
bool NodeTranslator::compileType(TypeExpression::Reader source, schema::Type::Builder target) {
auto name = source.getName();
KJ_IF_MAYBE(base, resolver.resolve(name)) {
bool handledParams = false;
switch (base->kind) {
case Declaration::Body::ENUM_DECL: target.getBody().setEnumType(base->id); break;
case Declaration::Body::STRUCT_DECL: target.getBody().setStructType(base->id); break;
case Declaration::Body::INTERFACE_DECL: target.getBody().setInterfaceType(base->id); break;
case Declaration::Body::BUILTIN_LIST: {
auto params = source.getParams();
if (params.size() != 1) {
errorReporter.addErrorOn(source, "'List' requires exactly one parameter.");
return false;
}
if (!compileType(params[0], target.getBody().initListType())) {
return false;
}
handledParams = true;
break;
}
case Declaration::Body::BUILTIN_VOID: target.getBody().setVoidType(); break;
case Declaration::Body::BUILTIN_BOOL: target.getBody().setBoolType(); break;
case Declaration::Body::BUILTIN_INT8: target.getBody().setInt8Type(); break;
case Declaration::Body::BUILTIN_INT16: target.getBody().setInt16Type(); break;
case Declaration::Body::BUILTIN_INT32: target.getBody().setInt32Type(); break;
case Declaration::Body::BUILTIN_INT64: target.getBody().setInt64Type(); break;
case Declaration::Body::BUILTIN_U_INT8: target.getBody().setUint8Type(); break;
case Declaration::Body::BUILTIN_U_INT16: target.getBody().setUint16Type(); break;
case Declaration::Body::BUILTIN_U_INT32: target.getBody().setUint32Type(); break;
case Declaration::Body::BUILTIN_U_INT64: target.getBody().setUint64Type(); break;
case Declaration::Body::BUILTIN_FLOAT32: target.getBody().setFloat32Type(); break;
case Declaration::Body::BUILTIN_FLOAT64: target.getBody().setFloat64Type(); break;
case Declaration::Body::BUILTIN_TEXT: target.getBody().setTextType(); break;
case Declaration::Body::BUILTIN_DATA: target.getBody().setDataType(); break;
case Declaration::Body::BUILTIN_OBJECT: target.getBody().setObjectType(); break;
default:
errorReporter.addErrorOn(source, kj::str("'", declNameString(name), "' is not a type."));
return false;
}
if (!handledParams) {
if (source.getParams().size() != 0) {
errorReporter.addErrorOn(source, kj::str(
"'", declNameString(name), "' does not accept parameters."));
}
return false;
}
return true;
} else {
return false;
}
}
// -------------------------------------------------------------------
void NodeTranslator::compileDefaultDefaultValue(
schema::Type::Reader type, schema::Value::Builder target) {
switch (type.getBody().which()) {
case schema::Type::Body::VOID_TYPE: target.getBody().setVoidValue(); break;
case schema::Type::Body::BOOL_TYPE: target.getBody().setBoolValue(false); break;
case schema::Type::Body::INT8_TYPE: target.getBody().setInt8Value(0); break;
case schema::Type::Body::INT16_TYPE: target.getBody().setInt16Value(0); break;
case schema::Type::Body::INT32_TYPE: target.getBody().setInt32Value(0); break;
case schema::Type::Body::INT64_TYPE: target.getBody().setInt64Value(0); break;
case schema::Type::Body::UINT8_TYPE: target.getBody().setUint8Value(0); break;
case schema::Type::Body::UINT16_TYPE: target.getBody().setUint16Value(0); break;
case schema::Type::Body::UINT32_TYPE: target.getBody().setUint32Value(0); break;
case schema::Type::Body::UINT64_TYPE: target.getBody().setUint64Value(0); break;
case schema::Type::Body::FLOAT32_TYPE: target.getBody().setFloat32Value(0); break;
case schema::Type::Body::FLOAT64_TYPE: target.getBody().setFloat64Value(0); break;
case schema::Type::Body::TEXT_TYPE: target.getBody().initTextValue(0); break;
case schema::Type::Body::DATA_TYPE: target.getBody().initDataValue(0); break;
case schema::Type::Body::ENUM_TYPE: target.getBody().setEnumValue(0); break;
case schema::Type::Body::INTERFACE_TYPE: target.getBody().setInterfaceValue(); break;
// Bit of a hack: For "Object" types, we adopt a null orphan, which sets the field to null.
// TODO(cleanup): Create a cleaner way to do this.
case schema::Type::Body::STRUCT_TYPE: target.getBody().adoptStructValue(Orphan<Data>()); break;
case schema::Type::Body::LIST_TYPE: target.getBody().adoptListValue(Orphan<Data>()); break;
case schema::Type::Body::OBJECT_TYPE: target.getBody().adoptObjectValue(Orphan<Data>()); break;
}
} }
void NodeTranslator::compileBootstrapValue(ValueExpression::Reader source, void NodeTranslator::compileBootstrapValue(ValueExpression::Reader source,
schema::Type::Reader type, schema::Type::Reader type,
schema::Value::Builder target) { schema::Value::Builder target) {
switch (type.getBody().which()) {
case schema::Type::Body::LIST_TYPE:
case schema::Type::Body::OBJECT_TYPE:
case schema::Type::Body::STRUCT_TYPE:
case schema::Type::Body::INTERFACE_TYPE:
// Handle later.
unfinishedValues.add(UnfinishedValue { source, type, target });
return;
default:
break;
}
switch (source.getBody().which()) {
case ValueExpression::Body::NAME: {
auto name = source.getBody().getName();
bool isBare = name.getBase().which() == DeclName::Base::RELATIVE_NAME &&
name.getMemberPath().size() == 0;
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();
switch (type.getBody().which()) {
case schema::Type::Body::VOID_TYPE:
if (id == "void") {
target.getBody().setVoidValue();
return;
}
break;
case schema::Type::Body::BOOL_TYPE:
if (id == "true") {
target.getBody().setBoolValue(true);
return;
} else if (id == "false") {
target.getBody().setBoolValue(false);
return;
}
break;
case schema::Type::Body::FLOAT32_TYPE:
if (id == "nan") {
target.getBody().setFloat32Value(std::numeric_limits<float>::quiet_NaN());
return;
} else if (id == "inf") {
target.getBody().setFloat32Value(std::numeric_limits<float>::infinity());
return;
}
break;
case schema::Type::Body::FLOAT64_TYPE:
if (id == "nan") {
target.getBody().setFloat64Value(std::numeric_limits<double>::quiet_NaN());
return;
} else if (id == "inf") {
target.getBody().setFloat64Value(std::numeric_limits<double>::infinity());
return;
}
break;
case schema::Type::Body::ENUM_TYPE:
KJ_IF_MAYBE(enumerant, resolver.resolveMaybeBootstrapSchema(
type.getBody().getEnumType()).asEnum().findEnumerantByName(id)) {
target.getBody().setEnumValue(enumerant->getOrdinal());
return;
}
break;
default:
break;
}
}
// Haven't resolved the name yet. Try looking it up.
KJ_IF_MAYBE(resolved, resolver.resolve(source.getBody().getName())) {
if (resolved->kind != Declaration::Body::CONST_DECL) {
errorReporter.addErrorOn(source,
kj::str("'", declNameString(name), "' does not refer to a constant."));
compileDefaultDefaultValue(type, target);
break;
}
// We can get the bootstrap version of the constant here because if it has a
// non-bootstrap-time value then it's an error anyway.
Schema constSchema = resolver.resolveMaybeBootstrapSchema(resolved->id);
auto constReader = constSchema.getProto().getBody().getConstNode();
copyValue(constReader.getValue(), constReader.getType(), target, type, source);
if (isBare) {
// 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::StringPtr parent;
if (scopeReader.getBody().which() == schema::Node::Body::FILE_NODE) {
parent = "";
} else {
parent = scopeReader.getDisplayName().slice(scopeReader.getDisplayNamePrefixLength());
}
kj::StringPtr id = name.getBase().getRelativeName().getValue();
errorReporter.addErrorOn(source, kj::str(
"Constant names must be qualified to avoid confusion. Please replace '",
declNameString(name), "' with '", parent, ".", id,
"', if that's what you intended."));
}
}
break;
}
case ValueExpression::Body::POSITIVE_INT: {
uint64_t value = source.getBody().getPositiveInt();
uint64_t limit = std::numeric_limits<uint64_t>::max();
switch (type.getBody().which()) {
#define HANDLE_TYPE(discrim, name, type) \
case schema::Type::Body::discrim##_TYPE: \
limit = std::numeric_limits<type>::max(); \
target.getBody().set##name##Value(value); \
break;
HANDLE_TYPE(INT8 , Int8 , int8_t );
HANDLE_TYPE(INT16, Int16, int16_t);
HANDLE_TYPE(INT32, Int32, int32_t);
HANDLE_TYPE(INT64, Int64, int64_t);
HANDLE_TYPE(UINT8 , Uint8 , uint8_t );
HANDLE_TYPE(UINT16, Uint16, uint16_t);
HANDLE_TYPE(UINT32, Uint32, uint32_t);
HANDLE_TYPE(UINT64, Uint64, uint64_t);
#undef HANDLE_TYPE
case schema::Type::Body::FLOAT32_TYPE:
target.getBody().setFloat32Value(value);
break;
case schema::Type::Body::FLOAT64_TYPE:
target.getBody().setFloat64Value(value);
break;
default:
errorReporter.addErrorOn(source, "Type/value mismatch.");
compileDefaultDefaultValue(type, target);
break;
}
if (value > limit) {
errorReporter.addErrorOn(source, "Value out-of-range for type.");
}
break;
}
case ValueExpression::Body::NEGATIVE_INT: {
uint64_t value = source.getBody().getNegativeInt();
uint64_t limit = std::numeric_limits<uint64_t>::max();
switch (type.getBody().which()) {
#define HANDLE_TYPE(discrim, name, type) \
case schema::Type::Body::discrim##_TYPE: \
limit = kj::implicitCast<uint64_t>(std::numeric_limits<type>::max()) + 1; \
target.getBody().set##name##Value(-value); \
break;
HANDLE_TYPE(INT8 , Int8 , int8_t );
HANDLE_TYPE(INT16, Int16, int16_t);
HANDLE_TYPE(INT32, Int32, int32_t);
HANDLE_TYPE(INT64, Int64, int64_t);
#undef HANDLE_TYPE
case schema::Type::Body::FLOAT32_TYPE:
target.getBody().setFloat32Value(-kj::implicitCast<float>(value));
break;
case schema::Type::Body::FLOAT64_TYPE:
target.getBody().setFloat64Value(-kj::implicitCast<double>(value));
break;
default:
errorReporter.addErrorOn(source, "Type/value mismatch.");
compileDefaultDefaultValue(type, target);
break;
}
if (value > limit) {
errorReporter.addErrorOn(source, "Value out-of-range for type.");
}
break;
}
case ValueExpression::Body::FLOAT: {
switch (type.getBody().which()) {
case schema::Type::Body::FLOAT32_TYPE:
target.getBody().setFloat32Value(source.getBody().getFloat());
break;
case schema::Type::Body::FLOAT64_TYPE:
target.getBody().setFloat64Value(source.getBody().getFloat());
break;
default:
errorReporter.addErrorOn(source, "Type/value mismatch.");
compileDefaultDefaultValue(type, target);
break;
}
break;
}
case ValueExpression::Body::STRING: {
switch (type.getBody().which()) {
case schema::Type::Body::TEXT_TYPE:
target.getBody().setTextValue(source.getBody().getString());
break;
case schema::Type::Body::DATA_TYPE: {
auto str = source.getBody().getString();
target.getBody().setDataValue(
kj::arrayPtr(reinterpret_cast<const byte*>(str.begin()), str.size()));
break;
}
default:
errorReporter.addErrorOn(source, "Type/value mismatch.");
compileDefaultDefaultValue(type, target);
break;
}
}
case ValueExpression::Body::LIST:
case ValueExpression::Body::STRUCT_VALUE:
case ValueExpression::Body::UNION_VALUE:
// If the type matched, these cases should have been handled earlier.
errorReporter.addErrorOn(source, "Type/value mismatch.");
compileDefaultDefaultValue(type, target);
break;
case ValueExpression::Body::UNKNOWN:
// Ignore earlier error.
compileDefaultDefaultValue(type, target);
break;
}
} }
void NodeTranslator::compileFinalValue(ValueExpression::Reader source, void NodeTranslator::compileFinalValue(ValueExpression::Reader source,
...@@ -1132,6 +1523,50 @@ void NodeTranslator::compileFinalValue(ValueExpression::Reader source, ...@@ -1132,6 +1523,50 @@ void NodeTranslator::compileFinalValue(ValueExpression::Reader source,
} }
void NodeTranslator::copyValue(schema::Value::Reader src, schema::Type::Reader srcType,
schema::Value::Builder dst, schema::Type::Reader dstType,
ValueExpression::Reader errorLocation) {
DynamicUnion::Reader srcBody = DynamicStruct::Reader(src).get("body").as<DynamicUnion>();
DynamicUnion::Builder dstBody = DynamicStruct::Builder(dst).get("body").as<DynamicUnion>();
kj::StringPtr dstFieldName;
switch (dstType.getBody().which()) {
case schema::Type::Body::VOID_TYPE: dstFieldName = "voidValue"; break;
case schema::Type::Body::BOOL_TYPE: dstFieldName = "boolValue"; break;
case schema::Type::Body::INT8_TYPE: dstFieldName = "int8Value"; break;
case schema::Type::Body::INT16_TYPE: dstFieldName = "int16Value"; break;
case schema::Type::Body::INT32_TYPE: dstFieldName = "int32Value"; break;
case schema::Type::Body::INT64_TYPE: dstFieldName = "int64Value"; break;
case schema::Type::Body::UINT8_TYPE: dstFieldName = "uint8Value"; break;
case schema::Type::Body::UINT16_TYPE: dstFieldName = "uint16Value"; break;
case schema::Type::Body::UINT32_TYPE: dstFieldName = "uint32Value"; break;
case schema::Type::Body::UINT64_TYPE: dstFieldName = "uint64Value"; break;
case schema::Type::Body::FLOAT32_TYPE: dstFieldName = "float32Value"; break;
case schema::Type::Body::FLOAT64_TYPE: dstFieldName = "float64Value"; break;
case schema::Type::Body::TEXT_TYPE: dstFieldName = "textValue"; break;
case schema::Type::Body::DATA_TYPE: dstFieldName = "dataValue"; break;
case schema::Type::Body::LIST_TYPE: dstFieldName = "listValue"; break;
case schema::Type::Body::ENUM_TYPE: dstFieldName = "enumValue"; break;
case schema::Type::Body::STRUCT_TYPE: dstFieldName = "structValue"; break;
case schema::Type::Body::INTERFACE_TYPE: dstFieldName = "interfaceValue"; break;
case schema::Type::Body::OBJECT_TYPE: dstFieldName = "objectValue"; break;
}
KJ_IF_MAYBE(which, srcBody.which()) {
// Setting a value via the dynamic API implements the implicit conversions that we want, with
// bounds checking that we want. It throws an exception on failure, but that exception is a
// recoverable one, so even if exceptions are disabled, we should be able to catch it. So,
// let's do that rather than try to re-implement all that logic here.
KJ_IF_MAYBE(exception, kj::runCatchingExceptions(
[&]() { dstBody.set(dstFieldName, srcBody.get()); })) {
// Exception caught, therefore the types are not compatible.
errorReporter.addErrorOn(errorLocation, exception->getDescription());
}
} else {
KJ_FAIL_ASSERT("Didn't recognize schema::Value::Body type?");
}
}
Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications( Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications(
List<Declaration::AnnotationApplication>::Reader annotations, List<Declaration::AnnotationApplication>::Reader annotations,
kj::StringPtr targetsFlagName) { kj::StringPtr targetsFlagName) {
......
...@@ -54,12 +54,15 @@ public: ...@@ -54,12 +54,15 @@ public:
// 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> resolveMaybeBootstrapSchema(uint64_t id) const = 0; virtual Schema resolveMaybeBootstrapSchema(uint64_t id) const = 0;
// Get the schema for the given ID. Returning either a bootstrap schema or a final schema // Get the schema for the given ID. Returning either a bootstrap schema or a final schema
// is acceptable. // is acceptable. Throws an exception if the id is not one that was found by calling resolve()
// or by traversing other schemas.
virtual kj::Maybe<Schema> resolveFinalSchema(uint64_t id) const = 0; virtual Schema resolveFinalSchema(uint64_t id) const = 0;
// Get the final schema for the given ID. A bootstrap schema is not acceptable. // 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.
}; };
NodeTranslator(const Resolver& resolver, const ErrorReporter& errorReporter, NodeTranslator(const Resolver& resolver, const ErrorReporter& errorReporter,
...@@ -143,6 +146,12 @@ private: ...@@ -143,6 +146,12 @@ private:
schema::Type::Reader type, schema::Value::Builder target); schema::Type::Reader type, schema::Value::Builder target);
// Compile a previously-unfinished value. See `unfinishedValues`. // Compile a previously-unfinished value. See `unfinishedValues`.
void copyValue(schema::Value::Reader src, schema::Type::Reader srcType,
schema::Value::Builder dst, schema::Type::Reader dstType,
ValueExpression::Reader errorLocation);
// Copy a value from one schema to another, possibly coercing the type if compatible, or
// reporting an error otherwise.
Orphan<List<schema::Annotation>> compileAnnotationApplications( Orphan<List<schema::Annotation>> compileAnnotationApplications(
List<Declaration::AnnotationApplication>::Reader annotations, List<Declaration::AnnotationApplication>::Reader annotations,
kj::StringPtr targetsFlagName); kj::StringPtr targetsFlagName);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits>
namespace capnp { namespace capnp {
namespace compiler { namespace compiler {
...@@ -299,6 +300,15 @@ inline Declaration::Builder initMemberDecl( ...@@ -299,6 +300,15 @@ inline Declaration::Builder initMemberDecl(
return builder; return builder;
} }
template <typename BuilderType>
void initLocation(kj::parse::Span<List<Token>::Reader::Iterator> location,
BuilderType builder) {
if (location.begin() < location.end()) {
builder.setStartByte(location.begin()->getStartByte());
builder.setEndByte((location.end() - 1)->getEndByte());
}
}
} // namespace } // namespace
// ======================================================================================= // =======================================================================================
...@@ -336,10 +346,11 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP ...@@ -336,10 +346,11 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP
return kj::mv(result); return kj::mv(result);
})); }));
parsers.typeExpression = arena.copy(p::transform( parsers.typeExpression = arena.copy(p::transformWithLocation(
p::sequence(parsers.declName, p::optional( p::sequence(parsers.declName, p::optional(
parenthesizedList(parsers.typeExpression, errorReporter))), parenthesizedList(parsers.typeExpression, errorReporter))),
[this](Orphan<DeclName>&& name, [this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Orphan<DeclName>&& name,
kj::Maybe<Located<kj::Array<kj::Maybe<Orphan<TypeExpression>>>>>&& params) kj::Maybe<Located<kj::Array<kj::Maybe<Orphan<TypeExpression>>>>>&& params)
-> Orphan<TypeExpression> { -> Orphan<TypeExpression> {
auto result = orphanage.newOrphan<TypeExpression>(); auto result = orphanage.newOrphan<TypeExpression>();
...@@ -356,6 +367,7 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP ...@@ -356,6 +367,7 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP
} }
} }
} }
initLocation(location, builder);
return result; return result;
})); }));
...@@ -451,6 +463,15 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP ...@@ -451,6 +463,15 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP
value.copyLocationTo(builder); value.copyLocationTo(builder);
return result; return result;
}), }),
p::transformWithLocation(p::sequence(op("-"), keyword("inf")),
[this](kj::parse::Span<List<Token>::Reader::Iterator> location)
-> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get();
builder.getBody().setFloat(-std::numeric_limits<double>::infinity());
initLocation(location, builder);
return result;
}),
p::transform(stringLiteral, p::transform(stringLiteral,
[this](Located<Text::Reader>&& value) -> Orphan<ValueExpression> { [this](Located<Text::Reader>&& value) -> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>(); auto result = orphanage.newOrphan<ValueExpression>();
...@@ -474,12 +495,13 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP ...@@ -474,12 +495,13 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP
return result; return result;
}), }),
p::transform(identifier, p::transformWithLocation(parsers.declName,
[this](Located<Text::Reader>&& value) -> Orphan<ValueExpression> { [this](kj::parse::Span<List<Token>::Reader::Iterator> location,
Orphan<DeclName>&& value) -> Orphan<ValueExpression> {
auto result = orphanage.newOrphan<ValueExpression>(); auto result = orphanage.newOrphan<ValueExpression>();
auto builder = result.get(); auto builder = result.get();
builder.getBody().setIdentifier(value.value); builder.getBody().adoptName(kj::mv(value));
value.copyLocationTo(builder); initLocation(location, builder);
return result; return result;
}), }),
p::transform(bracketedList(parsers.valueExpression, errorReporter), p::transform(bracketedList(parsers.valueExpression, errorReporter),
...@@ -518,10 +540,9 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP ...@@ -518,10 +540,9 @@ CapnpParser::CapnpParser(Orphanage orphanageParam, ErrorReporter& errorReporterP
}) })
)); ));
parsers.annotation = arena.copy(p::transformWithLocation( parsers.annotation = arena.copy(p::transform(
p::sequence(op("$"), parsers.declName, p::optional(parsers.parenthesizedValueExpression)), p::sequence(op("$"), parsers.declName, p::optional(parsers.parenthesizedValueExpression)),
[this](kj::parse::Span<List<Token>::Reader::Iterator> location, [this](Orphan<DeclName>&& name, kj::Maybe<Orphan<ValueExpression>>&& value)
Orphan<DeclName>&& name, kj::Maybe<Orphan<ValueExpression>>&& value)
-> Orphan<Declaration::AnnotationApplication> { -> Orphan<Declaration::AnnotationApplication> {
auto result = orphanage.newOrphan<Declaration::AnnotationApplication>(); auto result = orphanage.newOrphan<Declaration::AnnotationApplication>();
auto builder = result.get(); auto builder = result.get();
......
...@@ -1408,7 +1408,7 @@ typeName DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \ ...@@ -1408,7 +1408,7 @@ typeName DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \
case FLOAT: \ case FLOAT: \
return ifFloat<typeName>(reader.floatValue); \ return ifFloat<typeName>(reader.floatValue); \
default: \ default: \
KJ_FAIL_REQUIRE("Type mismatch when using DynamicValue::Reader::as().") { \ KJ_FAIL_REQUIRE("Value type mismatch.") { \
return 0; \ return 0; \
} \ } \
} \ } \
...@@ -1422,7 +1422,7 @@ typeName DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \ ...@@ -1422,7 +1422,7 @@ typeName DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \
case FLOAT: \ case FLOAT: \
return ifFloat<typeName>(builder.floatValue); \ return ifFloat<typeName>(builder.floatValue); \
default: \ default: \
KJ_FAIL_REQUIRE("Type mismatch when using DynamicValue::Builder::as().") { \ KJ_FAIL_REQUIRE("Value type mismatch.") { \
return 0; \ return 0; \
} \ } \
} \ } \
...@@ -1443,15 +1443,13 @@ HANDLE_NUMERIC_TYPE(double, kj::implicitCast, kj::implicitCast, kj::implicitCast ...@@ -1443,15 +1443,13 @@ HANDLE_NUMERIC_TYPE(double, kj::implicitCast, kj::implicitCast, kj::implicitCast
#define HANDLE_TYPE(name, discrim, typeName) \ #define HANDLE_TYPE(name, discrim, typeName) \
ReaderFor<typeName> DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \ ReaderFor<typeName> DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \
KJ_REQUIRE(reader.type == discrim, \ KJ_REQUIRE(reader.type == discrim, "Value type mismatch.") { \
"Type mismatch when using DynamicValue::Reader::as().") { \
return ReaderFor<typeName>(); \ return ReaderFor<typeName>(); \
} \ } \
return reader.name##Value; \ return reader.name##Value; \
} \ } \
BuilderFor<typeName> DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \ BuilderFor<typeName> DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \
KJ_REQUIRE(builder.type == discrim, \ KJ_REQUIRE(builder.type == discrim, "Value type mismatch.") { \
"Type mismatch when using DynamicValue::Builder::as().") { \
return BuilderFor<typeName>(); \ return BuilderFor<typeName>(); \
} \ } \
return builder.name##Value; \ return builder.name##Value; \
...@@ -1472,13 +1470,13 @@ HANDLE_TYPE(union, UNION, DynamicUnion) ...@@ -1472,13 +1470,13 @@ HANDLE_TYPE(union, UNION, DynamicUnion)
// As in the header, HANDLE_TYPE(void, VOID, Void) crashes GCC 4.7. // As in the header, HANDLE_TYPE(void, VOID, Void) crashes GCC 4.7.
Void DynamicValue::Reader::AsImpl<Void>::apply(const Reader& reader) { Void DynamicValue::Reader::AsImpl<Void>::apply(const Reader& reader) {
KJ_REQUIRE(reader.type == VOID, "Type mismatch when using DynamicValue::Reader::as().") { KJ_REQUIRE(reader.type == VOID, "Value type mismatch.") {
return Void(); return Void();
} }
return reader.voidValue; return reader.voidValue;
} }
Void DynamicValue::Builder::AsImpl<Void>::apply(Builder& builder) { Void DynamicValue::Builder::AsImpl<Void>::apply(Builder& builder) {
KJ_REQUIRE(builder.type == VOID, "Type mismatch when using DynamicValue::Builder::as().") { KJ_REQUIRE(builder.type == VOID, "Value type mismatch.") {
return Void(); return Void();
} }
return builder.voidValue; return builder.voidValue;
......
...@@ -40,6 +40,10 @@ struct Node { ...@@ -40,6 +40,10 @@ struct Node {
# #
# (On Zooko's triangle, this is the node's nickname.) # (On Zooko's triangle, this is the node's nickname.)
displayNamePrefixLength @12 :UInt32;
# If you want a shorter version of `displayName` (just naming this node, without its surrounding
# scope), chop off this many characters from the beginning of `displayName`.
scopeId @2 :Id = 0; scopeId @2 :Id = 0;
# ID of the lexical parent node. Typically, the scope node will have a NestedNode pointing back # ID of the lexical parent node. Typically, the scope node will have a NestedNode pointing back
# at this node, but robust code should avoid relying on this. `scopeId` is zero if the node has # at this node, but robust code should avoid relying on this. `scopeId` is zero if the node has
...@@ -109,6 +113,7 @@ struct Value { ...@@ -109,6 +113,7 @@ struct Value {
body @0 union { body @0 union {
# Note ordinals 1 and 10 are intentionally swapped to improve union layout. # Note ordinals 1 and 10 are intentionally swapped to improve union layout.
# TODO: Make it 2 and 10 that are swapped instead so that voidValue is still default?
voidValue @10 :Void; voidValue @10 :Void;
boolValue @2 :Bool; boolValue @2 :Bool;
int8Value @3 :Int8; int8Value @3 :Int8;
......
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