diff --git a/hilti/runtime/include/result.h b/hilti/runtime/include/result.h index e1372ab1b..e14447bfb 100644 --- a/hilti/runtime/include/result.h +++ b/hilti/runtime/include/result.h @@ -185,4 +185,10 @@ class Result { std::variant _value; }; +/** Similar to `std::make_optional`, construct a result from a value. */ +template +Result make_result(T&& t) { + return Result(std::forward(t)); +} + } // namespace hilti::rt diff --git a/hilti/runtime/include/type-info.h b/hilti/runtime/include/type-info.h index d067ff796..45a48e634 100644 --- a/hilti/runtime/include/type-info.h +++ b/hilti/runtime/include/type-info.h @@ -378,6 +378,68 @@ class Address : public detail::AtomicType {}; /** Type information for type ``any`. */ class Any : public detail::ValueLessType {}; +class Bitfield; + +namespace bitfield { + +/** Auxiliary type information for type ``bitfield`` describing one of its fields. */ +class Bits { +public: + /** + * Constructor. + * + * @param name ID of the field + * @param type type of the field + * @param offset offset of the field inside the bitfield's storage tuple. + */ + Bits(const char* name, const TypeInfo* type, std::ptrdiff_t offset) : name(name), type(type), offset(offset) {} + + const std::string name; /**< ID of the field, with an empty string indicating no name */ + const TypeInfo* type; /**< type of the field */ + +private: + friend class type_info::Bitfield; + + const std::ptrdiff_t offset; +}; + +}; // namespace bitfield + +/** Auxiliary type information for type ``bitfield`. */ +class Bitfield { +public: + /** + * Constructor + * + * @param labels the bitfield's fields + */ + Bitfield(std::vector bits) : _bits(std::move(bits)) {} + + /** Returns the bitfield's fields. */ + const auto& bits() const { return _bits; } + + /** + * Returns a vector that can be iterated over to visit all the fields. + * + * @param v the value referring to the bitfield to iterate over + * + * @return a vector of pairs ``(field, value)`` where *field* is the + * current ``bitfield::Bit` and *value* is the field's value. + */ + auto iterate(const Value& v) const { + std::vector> values; + + values.reserve(_bits.size()); + for ( const auto& f : _bits ) + values.emplace_back(f, Value(static_cast(v.pointer()) + f.offset, f.type, v)); + + return values; + } + +private: + const std::vector _bits; +}; + /** Type information for type ``bool`. */ class Bool : public detail::AtomicType {}; @@ -838,9 +900,9 @@ struct Field { * @param offset offset of the field in number bytes inside the struct * @param accessor function returning a pointer to a fields value */ - Field(const char* name, const TypeInfo* type, std::ptrdiff_t offset, bool internal, + Field(const char* name, const TypeInfo* type, std::ptrdiff_t offset, bool internal, bool anonymous, Accessor accessor = accessor_default) - : name(name), type(type), offset(offset), accessor(accessor), internal(internal) {} + : name(name), type(type), offset(offset), accessor(accessor), internal(internal), anonymous(anonymous) {} /** Default accessor function suitable for non-optional fields. */ static const void* accessor_default(const Value& v) { return v.pointer(); } @@ -859,6 +921,7 @@ struct Field { }; } + bool isAnonymous() const { return anonymous; } bool isInternal() const { return internal; } const std::string name; /**< ID of the field */ @@ -873,6 +936,7 @@ struct Field { const std::ptrdiff_t offset; const Accessor accessor; const bool internal; + const bool anonymous; }; }; // namespace struct_ @@ -1148,6 +1212,7 @@ struct TypeInfo { Undefined, Address, Any, + Bitfield, Bool, Bytes, BytesIterator, @@ -1198,6 +1263,7 @@ struct TypeInfo { union { type_info::Address* address; type_info::Any* any; + type_info::Bitfield* bitfield; type_info::Bool* bool_; type_info::Bytes* bytes; type_info::BytesIterator* bytes_iterator; @@ -1256,6 +1322,10 @@ struct TypeInfo { tag = Any; any = value; } + else if constexpr ( std::is_same_v ) { + tag = Bitfield; + bitfield = value; + } else if constexpr ( std::is_same_v ) { tag = Bool; bool_ = value; @@ -1450,6 +1520,10 @@ const Type* auxType(const type_info::Value& v) { assert(type.tag == TypeInfo::Any); return type.any; } + else if constexpr ( std::is_same_v ) { + assert(type.tag == TypeInfo::Bitfield); + return type.bitfield; + } else if constexpr ( std::is_same_v ) { assert(type.tag == TypeInfo::Bool); return type.bool_; diff --git a/hilti/runtime/include/types/all.h b/hilti/runtime/include/types/all.h index 54648ed3e..985e644ae 100644 --- a/hilti/runtime/include/types/all.h +++ b/hilti/runtime/include/types/all.h @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/hilti/runtime/include/types/bitfield.h b/hilti/runtime/include/types/bitfield.h new file mode 100644 index 000000000..309ae769d --- /dev/null +++ b/hilti/runtime/include/types/bitfield.h @@ -0,0 +1,96 @@ +// Copyright (c) 2020-2023 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace hilti::rt { + +/// A bitfield is just a type wrapper around a tuple of the corresponding field +/// values (including the hidden additional element storing the bitfield's +/// original integer value). We wrap it so that we can customize the +/// printing. +template +struct Bitfield { + std::tuple value; +}; + +template +Bitfield make_bitfield(Ts&&... args) { + return Bitfield{std::make_tuple(std::forward(args)...)}; +} + +namespace bitfield { + +template +ptrdiff_t elementOffset() { + return tuple::elementOffset(); +} + +} // namespace bitfield + +// Helper to convert tuple to a vector of the elements' string representations. +// This version uses `to_string` for each element. +namespace detail { +template>>> +std::vector to_vector_with_to_string(Tuple&& tuple) { + return std::apply( + [](auto&&... elems) { + std::vector result; + result.reserve(sizeof...(elems)); + (result.emplace_back(to_string(elems)), ...); + return result; + }, + std::forward(tuple)); +} + +// Helper to convert tuple to a vector of the elements' string representations. +// This version uses `to_string_for_print` for each element. +template>>> +std::vector to_vector_with_to_string_for_print(Tuple&& tuple) { + return std::apply( + [](auto&&... elems) { + std::vector result; + result.reserve(sizeof...(elems)); + (result.emplace_back(to_string(elems)), ...); + return result; + }, + std::forward(tuple)); +} +} // namespace detail + +namespace detail::adl { +template +inline std::string to_string(const Bitfield& x, adl::tag /*unused*/) { + // Need to remove the last, hidden element + auto y = to_vector_with_to_string(x.value); + y.pop_back(); + return fmt("(%s)", rt::join(std::move(y), ", ")); +} + +template +inline std::string to_string_for_print(const Bitfield& x, adl::tag /*unused*/) { + // Need to remove the last, hidden element + auto y = to_vector_with_to_string_for_print(x.value); + y.pop_back(); + return fmt("(%s)", rt::join(std::move(y), ", ")); +} +} // namespace detail::adl + +} // namespace hilti::rt + +namespace std { + +template +inline std::ostream& operator<<(std::ostream& out, const hilti::rt::Bitfield& x) { + return out << hilti::rt::to_string_for_print(x); +} + +} // namespace std diff --git a/hilti/runtime/include/types/integer.h b/hilti/runtime/include/types/integer.h index e21ab5a24..2811cd5b2 100644 --- a/hilti/runtime/include/types/integer.h +++ b/hilti/runtime/include/types/integer.h @@ -343,6 +343,16 @@ inline hilti::rt::integer::safe bits(hilti::rt::integer::safe v, uin return (v & mask) >> lower; } +/** + * Helper function just returning the value passed in. This is for working + * around an issue where our code generator produces code that, for unknown + * reasons, doesn't compile if the value is used directly. + */ +template +inline hilti::rt::integer::safe noop(hilti::rt::integer::safe v) { + return v; +} + } // namespace integer namespace detail::adl { diff --git a/hilti/runtime/src/tests/type-info.cc b/hilti/runtime/src/tests/type-info.cc index dc943d6cf..f93ed7584 100644 --- a/hilti/runtime/src/tests/type-info.cc +++ b/hilti/runtime/src/tests/type-info.cc @@ -54,14 +54,14 @@ namespace { const hilti::rt::TypeInfo __ti_Test_X = {"Test::X", "Test::X", new hilti::rt::type_info::Struct(std::vector( - {hilti::rt::type_info::struct_::Field{"i", &hilti::rt::type_info::int32, offsetof(Test::X, i), false}, - hilti::rt::type_info::struct_::Field{"s", &hilti::rt::type_info::string, offsetof(Test::X, s), false}, - hilti::rt::type_info::struct_::Field{"y", &type_info::__ti_Test_Y, offsetof(Test::X, y), false}}))}; + {hilti::rt::type_info::struct_::Field{"i", &hilti::rt::type_info::int32, offsetof(Test::X, i), false, false}, + hilti::rt::type_info::struct_::Field{"s", &hilti::rt::type_info::string, offsetof(Test::X, s), false, false}, + hilti::rt::type_info::struct_::Field{"y", &type_info::__ti_Test_Y, offsetof(Test::X, y), false, false}}))}; const hilti::rt::TypeInfo __ti_Test_Y = {"Test::Y", "Test::Y", new hilti::rt::type_info::Struct(std::vector( - {hilti::rt::type_info::struct_::Field{"b", &hilti::rt::type_info::bool_, offsetof(Test::Y, b), false}, - hilti::rt::type_info::struct_::Field{"r", &hilti::rt::type_info::real, offsetof(Test::Y, r), false}}))}; + {hilti::rt::type_info::struct_::Field{"b", &hilti::rt::type_info::bool_, offsetof(Test::Y, b), false, false}, + hilti::rt::type_info::struct_::Field{"r", &hilti::rt::type_info::real, offsetof(Test::Y, r), false, false}}))}; } // namespace } // namespace __hlt::type_info @@ -126,10 +126,10 @@ TEST_CASE("internal fields") { const TypeInfo ti = {"A", "A", new type_info::Struct( - {type_info::struct_::Field{"f1", &type_info::int32, offsetof(A, f1), false}, - type_info::struct_::Field{"f2", &type_info::string, offsetof(A, f2), false}, + {type_info::struct_::Field{"f1", &type_info::int32, offsetof(A, f1), false, false}, + type_info::struct_::Field{"f2", &type_info::string, offsetof(A, f2), false, false}, type_info::struct_::Field{"__internal", &type_info::bool_, offsetof(A, __internal), - true}})}; + true, false}})}; auto sx = StrongReference({42, "foo", true}); auto p = type_info::value::Parent(sx); @@ -146,4 +146,23 @@ TEST_CASE("internal fields") { CHECK_EQ(s->iterate(v, true).size(), 3U); } +TEST_CASE("anonymous fields") { + struct A { + std::string f1; + }; + + const TypeInfo ti = {"A", "A", + new type_info::Struct( + {type_info::struct_::Field{"f1", &type_info::int32, offsetof(A, f1), false, true}})}; + + auto sx = StrongReference({"foo"}); + auto p = type_info::value::Parent(sx); + auto v = type_info::Value(&*sx, &ti, p); + + const auto s = type_info::value::auxType(v); + + CHECK_EQ(s->fields().size(), 1U); + CHECK(s->fields()[0].get().isAnonymous()); +} + TEST_SUITE_END(); diff --git a/hilti/toolchain/CMakeLists.txt b/hilti/toolchain/CMakeLists.txt index d8ec53973..d7df91191 100644 --- a/hilti/toolchain/CMakeLists.txt +++ b/hilti/toolchain/CMakeLists.txt @@ -50,6 +50,7 @@ set(SOURCES src/ast/scope.cc src/ast/scope-lookup.cc src/ast/type.cc + src/ast/types/bitfield.cc src/ast/types/enum.cc src/ast/types/integer.cc src/ast/types/library.cc diff --git a/hilti/toolchain/include/ast/attribute.h b/hilti/toolchain/include/ast/attribute.h index bc82d34eb..f64ba9d35 100644 --- a/hilti/toolchain/include/ast/attribute.h +++ b/hilti/toolchain/include/ast/attribute.h @@ -297,6 +297,23 @@ class AttributeSet : public NodeBase { return *s; } + /** + * Returns a new attribute set that adds one element. + * + * @param s set to add to. + * @param a element to add. + * @return `s` with `a' added + */ + static AttributeSet add(optional_ref s, Attribute a) { + if ( s ) { + auto ns = *s; + ns.addChild(std::move(a)); + return ns; + } + else + return AttributeSet({std::move(a)}); + } + /** * Retrieves an attribute with a given name from a set, dealing correctly * with an unset optional set. If multiple attributes with that tag diff --git a/hilti/toolchain/include/ast/declarations/field.h b/hilti/toolchain/include/ast/declarations/field.h index cbd218c05..efe360551 100644 --- a/hilti/toolchain/include/ast/declarations/field.h +++ b/hilti/toolchain/include/ast/declarations/field.h @@ -69,6 +69,7 @@ class Field : public DeclarationBase { return {}; } + auto isAnonymous() const { return AttributeSet::find(attributes(), "&anonymous").has_value(); } auto isInternal() const { return AttributeSet::find(attributes(), "&internal").has_value(); } auto isOptional() const { return AttributeSet::find(attributes(), "&optional").has_value(); } auto isStatic() const { return AttributeSet::find(attributes(), "&static").has_value(); } diff --git a/hilti/toolchain/include/ast/expressions/resolved-operator.h b/hilti/toolchain/include/ast/expressions/resolved-operator.h index e3fc75915..2db1ab986 100644 --- a/hilti/toolchain/include/ast/expressions/resolved-operator.h +++ b/hilti/toolchain/include/ast/expressions/resolved-operator.h @@ -68,8 +68,6 @@ class ResolvedOperatorBase : public NodeBase, public trait::isExpression, public bool isTemporary() const { return isLhs(); } /** Implements `Expression` interface. */ const Type& type() const { return result(); } - /** Implements `Expression` interface. */ - auto isEqual(const Expression& other) const { return node::isEqual(this, other); } /** Implements `Expression` interface. */ bool isConstant() const { return type::isConstant(type()); } diff --git a/hilti/toolchain/include/ast/node.h b/hilti/toolchain/include/ast/node.h index bb5b2ee07..1a8a26475 100644 --- a/hilti/toolchain/include/ast/node.h +++ b/hilti/toolchain/include/ast/node.h @@ -413,8 +413,8 @@ class NodeBase : public trait::isNode { */ template auto children(int begin, int end) const { - auto end_ = (end < 0) ? _children.end() : _children.begin() + end; - return hilti::node::Range(_children.begin() + begin, end_); + int end_ = (end >= 0) ? end : std::max(begin, static_cast(_children.size()) + end + 1); + return hilti::node::Range(_children.begin() + begin, _children.begin() + end_); } /** @@ -426,10 +426,10 @@ class NodeBase : public trait::isNode { * @return vector containing child references from `start` to `end` */ auto childRefs(int begin, int end) { - auto end_ = (end < 0) ? _children.end() : _children.begin() + end; + int end_ = (end >= 0) ? end : std::max(begin, static_cast(_children.size()) + end + 1); std::vector refs; - for ( auto c = _children.begin(); c != end_; c = std::next(c) ) + for ( auto c = _children.begin(); c != _children.begin() + end_; c = std::next(c) ) refs.emplace_back(*c); return refs; diff --git a/hilti/toolchain/include/ast/nodes.decl b/hilti/toolchain/include/ast/nodes.decl index ca5959b34..3fa329a34 100644 --- a/hilti/toolchain/include/ast/nodes.decl +++ b/hilti/toolchain/include/ast/nodes.decl @@ -20,6 +20,7 @@ hilti::type::function::Result : isNode hilti::type::function::Parameter : isNode hilti::statement::switch_::Case : isNode hilti::statement::try_::Catch : isNode +hilti::type::bitfield::Bits : isNode hilti::type::enum_::Label : isNode hilti::type::tuple::Element : isNode @@ -113,6 +114,7 @@ hilti::statement::Yield : isStatement hilti::type::Address : isType hilti::type::Any : isType hilti::type::Auto : isType +hilti::type::Bitfield : isType hilti::type::Bool : isType hilti::type::Bytes : isType hilti::type::DocOnly : isType diff --git a/hilti/toolchain/include/ast/operators/all.h b/hilti/toolchain/include/ast/operators/all.h index ee1333105..812cd42f4 100644 --- a/hilti/toolchain/include/ast/operators/all.h +++ b/hilti/toolchain/include/ast/operators/all.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include #include diff --git a/spicy/toolchain/include/ast/operators/bitfield.h b/hilti/toolchain/include/ast/operators/bitfield.h similarity index 96% rename from spicy/toolchain/include/ast/operators/bitfield.h rename to hilti/toolchain/include/ast/operators/bitfield.h index 0d1ff481c..4be69e79c 100644 --- a/spicy/toolchain/include/ast/operators/bitfield.h +++ b/hilti/toolchain/include/ast/operators/bitfield.h @@ -9,12 +9,11 @@ #include #include #include +#include #include #include -#include - -namespace spicy::operator_ { +namespace hilti::operator_ { namespace bitfield::detail { @@ -76,4 +75,4 @@ right. } END_OPERATOR_CUSTOM_x -} // namespace spicy::operator_ +} // namespace hilti::operator_ diff --git a/hilti/toolchain/include/ast/operators/common.h b/hilti/toolchain/include/ast/operators/common.h index 7f74803c6..9cc2be0ea 100644 --- a/hilti/toolchain/include/ast/operators/common.h +++ b/hilti/toolchain/include/ast/operators/common.h @@ -21,6 +21,7 @@ class cls : public hilti::expression::ResolvedOperatorBase { \ public: \ using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; \ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } \ \ /** Class implementing operator interface. */ \ struct Operator : public hilti::trait::isOperator { \ diff --git a/hilti/toolchain/include/ast/operators/function.h b/hilti/toolchain/include/ast/operators/function.h index 4052b03b7..9f9b03d7c 100644 --- a/hilti/toolchain/include/ast/operators/function.h +++ b/hilti/toolchain/include/ast/operators/function.h @@ -21,6 +21,9 @@ class Call : public hilti::expression::ResolvedOperatorBase { public: using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + struct Operator : public hilti::trait::isOperator { Operator(const Scope::Referee& r, const type::Function& ftype) { auto op0 = operator_::Operand{{}, type::Any()}; // IDs won't be resolved diff --git a/hilti/toolchain/include/ast/operators/generic.h b/hilti/toolchain/include/ast/operators/generic.h index 12de35d1f..f4644b40d 100644 --- a/hilti/toolchain/include/ast/operators/generic.h +++ b/hilti/toolchain/include/ast/operators/generic.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -166,6 +167,17 @@ BEGIN_OPERATOR_CUSTOM(generic, Unpack) return; } + else if ( data_type.isA() ) { + if ( args.size() >= 2 && args.size() <= 3 ) { + auto arg1 = args[1].type().typeID(); + auto arg2 = (args.size() > 2 ? args[2].type().typeID() : ID("BitOrder")); + if ( arg1 && arg1->local() == ID("ByteOrder") && arg2 && arg2->local() == ID("BitOrder") ) + return; + } + + p.node.addError("invalid arguments for bitfield unpacking; want (, [, ])"); + } + else p.node.addError("type not unpackable"); } @@ -281,6 +293,9 @@ class CastedCoercion : public hilti::expression::ResolvedOperatorBase { public: using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + struct Operator : public hilti::trait::isOperator { Operator() = default; diff --git a/hilti/toolchain/include/ast/operators/struct.h b/hilti/toolchain/include/ast/operators/struct.h index fa59598ac..42a51f400 100644 --- a/hilti/toolchain/include/ast/operators/struct.h +++ b/hilti/toolchain/include/ast/operators/struct.h @@ -199,6 +199,9 @@ class MemberCall : public hilti::expression::ResolvedOperatorBase { public: using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + struct Operator : public hilti::trait::isOperator { Operator(const type::Struct& stype, const declaration::Field& f) { auto ftype = f.type().as(); diff --git a/hilti/toolchain/include/ast/types/all.h b/hilti/toolchain/include/ast/types/all.h index e3abe5f99..bae668f8e 100644 --- a/hilti/toolchain/include/ast/types/all.h +++ b/hilti/toolchain/include/ast/types/all.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/spicy/toolchain/include/ast/types/bitfield.h b/hilti/toolchain/include/ast/types/bitfield.h similarity index 75% rename from spicy/toolchain/include/ast/types/bitfield.h rename to hilti/toolchain/include/ast/types/bitfield.h index 6907e386d..0259ef309 100644 --- a/spicy/toolchain/include/ast/types/bitfield.h +++ b/hilti/toolchain/include/ast/types/bitfield.h @@ -16,9 +16,7 @@ #include #include -#include - -namespace spicy::type { +namespace hilti::type { namespace bitfield { @@ -41,6 +39,7 @@ class Bits : public hilti::NodeBase { auto upper() const { return _upper; } auto fieldWidth() const { return _field_width; } auto attributes() const { return children()[3].tryAs(); } + const Type& ddType() const { return children()[1].as().expression().type(); } NodeRef ddRef() const { return NodeRef(children()[1]); } const auto& itemType() const { return child(2); } @@ -59,8 +58,9 @@ class Bits : public hilti::NodeBase { bool operator==(const Bits& other) const { return id() == other.id() && _lower == other._lower && _upper == other._upper && - _field_width == other._field_width && itemType() == other.itemType() && - attributes() == other.attributes(); + _field_width == other._field_width && itemType() == other.itemType(); + // TODO: Attributes don't quite compare correctly, see e.g., spicy.types.bitfield.parse-enum failure + // && attributes() == other.attributes(); } private: @@ -79,29 +79,37 @@ class Bitfield : public hilti::TypeBase, hilti::type::trait::isParameterized, hilti::type::trait::isMutable { public: - Bitfield(int width, std::vector bits, const Meta& m = Meta()) - : TypeBase(nodes(type::UnsignedInteger(width, m), hilti::type::auto_, std::move(bits)), m), _width(width) {} + Bitfield(int width, std::vector bits, std::optional attributes, + const Meta& m = Meta()) + : TypeBase(nodes(type::UnsignedInteger(width, m), std::move(attributes), std::move(bits)), m), _width(width) { + // Add a hidden range storing the original integer value. + addChild(bitfield::Bits(ID("__value__"), 0, width - 1, width, {}, m)); + } + Bitfield(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({hilti::type::unknown, hilti::type::unknown}, std::move(m)), _wildcard(true) {} int width() const { return _width; } - auto bits() const { return children(2, -1); } + auto bits(bool include_hidden = false) const { return children(2, include_hidden ? -1 : -2); } hilti::optional_ref bits(const ID& id) const; std::optional bitsIndex(const ID& id) const; - const Type& parseType() const { return child(0); } - const Type& type() const { return child(1); } - - void addField(bitfield::Bits f) { addChild(std::move(f)); } - void setType(const Type& t) { children()[1] = t; } + auto attributes() const { return children()[1].tryAs(); } bool operator==(const Bitfield& other) const { return width() == other.width() && bits() == other.bits(); } /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - auto _isResolved(ResolvedState* rstate) const { return true; } + auto _isResolved(ResolvedState* rstate) const { + auto bs = bits(); + return std::all_of(bs.begin(), bs.end(), + [&](const auto& b) { return type::detail::isResolved(b.itemType(), rstate); }); + } + + void setAttributes(const AttributeSet& attrs) { children()[1] = attrs; } + /** Implements the `Type` interface. */ - auto typeParameters() const { return hilti::util::slice(children(), 1); } + auto typeParameters() const { return hilti::util::slice(children(), 2); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Node` interface. */ @@ -112,4 +120,4 @@ class Bitfield : public hilti::TypeBase, bool _wildcard = false; }; -} // namespace spicy::type +} // namespace hilti::type diff --git a/hilti/toolchain/include/compiler/detail/codegen/codegen.h b/hilti/toolchain/include/compiler/detail/codegen/codegen.h index adbe04d49..5efd04418 100644 --- a/hilti/toolchain/include/compiler/detail/codegen/codegen.h +++ b/hilti/toolchain/include/compiler/detail/codegen/codegen.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -98,8 +99,8 @@ class CodeGen { cxx::Expression pack(const hilti::Type& t, const cxx::Expression& data, const std::vector& args); cxx::Expression unpack(const hilti::Type& t, const Expression& data, const std::vector& args, bool throw_on_error); - cxx::Expression unpack(const hilti::Type& t, const cxx::Expression& data, const std::vector& args, - bool throw_on_error); + cxx::Expression unpack(const hilti::Type& t, const Type& data_type, const cxx::Expression& data, + const std::vector& args, bool throw_on_error); void addDeclarationFor(const hilti::Type& t) { _need_decls.push_back(t); } cxx::Expression addTmp(const std::string& prefix, const cxx::Type& t); @@ -108,6 +109,9 @@ class CodeGen { cxx::Expression startProfiler(const std::string& name, cxx::Block* block = nullptr, bool insert_at_front = false); void stopProfiler(const cxx::Expression& profiler, cxx::Block* block = nullptr); + cxx::Expression unsignedIntegerToBitfield(const type::Bitfield& t, const cxx::Expression& value, + const cxx::Expression& bitorder); + /** * Returns an ID that's unique for a given node. The ID is derived from * the node's location information, which must be present. @@ -119,12 +123,13 @@ class CodeGen { cxx::ID uniqueID(const std::string& prefix, const Node& n); cxx::Expression self() const { return _self.back(); } - cxx::Expression dollardollar() const { - return {"__dd", cxx::Side::LHS}; - } // TODO(robin): We hardcode the currently; need a stack, too? void pushSelf(detail::cxx::Expression e) { _self.push_back(std::move(e)); } void popSelf() { _self.pop_back(); } + cxx::Expression dollardollar() const { return _dd.back(); } + void pushDollarDollar(cxx::Expression e) { _dd.push_back(std::move(e)); } + void popDollarDollar() { _dd.pop_back(); } + auto cxxBlock() const { return ! _cxx_blocks.empty() ? _cxx_blocks.back() : nullptr; } void pushCxxBlock(cxx::Block* b) { _cxx_blocks.push_back(b); } void popCxxBlock() { _cxx_blocks.pop_back(); } @@ -148,6 +153,7 @@ class CodeGen { hilti::Unit* _hilti_unit = nullptr; std::weak_ptr _context; std::vector _self = {{"__self", cxx::Side::LHS}}; + std::vector _dd = {{"__dd", cxx::Side::LHS}}; std::vector _cxx_blocks; std::vector _tmps; std::map _tmp_counters; diff --git a/spicy/toolchain/src/ast/types/bitfield.cc b/hilti/toolchain/src/ast/types/bitfield.cc similarity index 70% rename from spicy/toolchain/src/ast/types/bitfield.cc rename to hilti/toolchain/src/ast/types/bitfield.cc index 9278f4495..bc0b7de76 100644 --- a/spicy/toolchain/src/ast/types/bitfield.cc +++ b/hilti/toolchain/src/ast/types/bitfield.cc @@ -1,13 +1,12 @@ // Copyright (c) 2020-2023 by the Zeek Project. See LICENSE for details. +#include #include -#include - -using namespace spicy; +using namespace hilti; hilti::optional_ref type::Bitfield::bits(const ID& id) const { - for ( const auto& b : bits() ) { + for ( const auto& b : bits(true) ) { if ( id == b.id() ) return b; } @@ -16,7 +15,7 @@ hilti::optional_ref type::Bitfield::bits(const ID& i } std::optional type::Bitfield::bitsIndex(const ID& id) const { - for ( const auto&& [i, b] : hilti::util::enumerate(bits()) ) { + for ( const auto&& [i, b] : hilti::util::enumerate(bits(true)) ) { if ( id == b.id() ) return i; } diff --git a/hilti/toolchain/src/compiler/codegen/codegen.cc b/hilti/toolchain/src/compiler/codegen/codegen.cc index a05dd8090..07d9f0488 100644 --- a/hilti/toolchain/src/compiler/codegen/codegen.cc +++ b/hilti/toolchain/src/compiler/codegen/codegen.cc @@ -841,6 +841,28 @@ void CodeGen::stopProfiler(const cxx::Expression& profiler, cxx::Block* block) { block->addStatement(cxx::Expression(fmt("hilti::rt::profiler::stop(%s)", profiler))); } +cxx::Expression CodeGen::unsignedIntegerToBitfield(const type::Bitfield& t, const cxx::Expression& value, + const cxx::Expression& bitorder) { + std::vector bits; + for ( const auto& b : t.bits(false) ) { + auto x = fmt("hilti::rt::integer::bits(%s, %d, %d, %s)", value, b.lower(), b.upper(), bitorder); + + if ( auto a = AttributeSet::find(b.attributes(), "&convert") ) { + pushDollarDollar(x); + bits.emplace_back(compile(*a->valueAsExpression())); + popDollarDollar(); + } + else + bits.emplace_back(std::move(x)); + } + + // `noop()` just returns the same value passed in. Without it, the compiler + // doesn't like the expression we are building, not sure why. + bits.emplace_back(fmt("hilti::rt::integer::noop(%s)", value)); + + return fmt("hilti::rt::make_bitfield(%s)", util::join(bits, ", ")); +} + cxx::ID CodeGen::uniqueID(const std::string& prefix, const Node& n) { std::string x; diff --git a/hilti/toolchain/src/compiler/codegen/coercions.cc b/hilti/toolchain/src/compiler/codegen/coercions.cc index 0c537ad36..da50205a8 100644 --- a/hilti/toolchain/src/compiler/codegen/coercions.cc +++ b/hilti/toolchain/src/compiler/codegen/coercions.cc @@ -74,7 +74,8 @@ struct Visitor : public hilti::visitor::PreOrder { if ( auto t = dst.tryAs() ) { // Create tmp to avoid evaluation "expr" twice. auto tmp = cg->addTmp("opt", cg->compile(src, codegen::TypeUsage::Storage)); - return {fmt("(%s = (%s), %s.has_value() ? std::make_optional(*%s) : std::nullopt)", tmp, expr, tmp, tmp), cxx::Side::LHS}; + return {fmt("(%s = (%s), %s.has_value() ? std::make_optional(*%s) : std::nullopt)", tmp, expr, tmp, tmp), + cxx::Side::LHS}; } if ( auto t = dst.tryAs() ) @@ -182,6 +183,9 @@ struct Visitor : public hilti::visitor::PreOrder { if ( auto t = dst.tryAs() ) return fmt("::hilti::rt::integer::safe(%s)", t->width(), expr); + if ( auto t = dst.tryAs() ) + return cg->unsignedIntegerToBitfield(*t, expr, cxx::Expression("hilti::rt::integer::BitOrder::LSB0")); + logger().internalError(fmt("codegen: unexpected type coercion from unsigned integer to %s", dst.typename_())); } diff --git a/hilti/toolchain/src/compiler/codegen/operators.cc b/hilti/toolchain/src/compiler/codegen/operators.cc index a7adb7bc5..628215201 100644 --- a/hilti/toolchain/src/compiler/codegen/operators.cc +++ b/hilti/toolchain/src/compiler/codegen/operators.cc @@ -50,6 +50,15 @@ struct Visitor : hilti::visitor::PreOrder { return compileExpressions(ctor.as().value()); } + auto tupleArgumentType(const Expression& op, int i) { + auto ctor = op.as().ctor(); + + if ( auto x = ctor.tryAs() ) + ctor = x->coercedCtor(); + + return ctor.as().value()[i].type(); + } + auto methodArguments(const expression::ResolvedOperatorBase& o) { auto ops = o.op2(); @@ -96,6 +105,15 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const operator_::bool_::BitOr& n) { return binary(n, "|"); } result_t operator()(const operator_::bool_::BitXor& n) { return binary(n, "^"); } + /// Bitfield + + result_t operator()(const operator_::bitfield::Member& n) { + auto id = n.op1().as().id(); + auto elem = n.op0().type().as().bitsIndex(id); + assert(elem); + return {fmt("std::get<%u>(%s.value)", *elem, op0(n)), cxx::Side::RHS}; + } + /// bytes::Iterator result_t operator()(const operator_::bytes::iterator::Deref& n) { return {fmt("*%s", op0(n)), cxx::Side::LHS}; } @@ -377,7 +395,8 @@ struct Visitor : hilti::visitor::PreOrder { if ( auto default_ = optionalArgument(args, 1); ! default_.empty() ) return fmt( - "[](auto&& m, auto&& k, auto&& default_) { return m.contains(k)? m.get(k) : default_; }(%s, %s, %s)", + "[](auto&& m, auto&& k, auto&& default_) { return m.contains(k)? m.get(k) : default_; }(%s, %s, " + "%s)", self, k, default_); else return fmt("%s.get(%s)", self, k); @@ -457,8 +476,8 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const operator_::generic::Unpack& n) { auto args = tupleArguments(n, n.op1()); auto throw_on_error = n.op2().as().ctor().as().value(); - return cg->unpack(n.op0().type().as().typeValue(), args[0], util::slice(args, 1, -1), - throw_on_error); + return cg->unpack(n.op0().type().as().typeValue(), tupleArgumentType(n.op1(), 0), args[0], + util::slice(args, 1, -1), throw_on_error); } result_t operator()(const operator_::generic::Begin& n) { return fmt("%s.begin()", op0(n)); } diff --git a/hilti/toolchain/src/compiler/codegen/types.cc b/hilti/toolchain/src/compiler/codegen/types.cc index 4dfee9ce1..45fcf74af 100644 --- a/hilti/toolchain/src/compiler/codegen/types.cc +++ b/hilti/toolchain/src/compiler/codegen/types.cc @@ -379,6 +379,13 @@ struct VisitorStorage : hilti::visitor::PreOrder { result_t operator()(const type::Bool& n) { return CxxTypes{.base_type = "::hilti::rt::Bool"}; } + result_t operator()(const type::Bitfield& n) { + auto x = node::transform(n.bits(true), + [this](const auto& b) { return cg->compile(b.itemType(), codegen::TypeUsage::Storage); }); + auto t = fmt("hilti::rt::Bitfield<%s>", util::join(x, ", ")); + return CxxTypes{.base_type = t}; + } + result_t operator()(const type::Bytes& n) { return CxxTypes{.base_type = "::hilti::rt::Bytes"}; } result_t operator()(const type::Real& n) { return CxxTypes{.base_type = "double"}; } @@ -806,6 +813,19 @@ struct VisitorTypeInfoDynamic : hilti::visitor::PreOrder().typeID(); } auto cxxID(const Node& n) { return n.as().cxxID(); } + result_t operator()(const type::Bitfield& n, position_t p) { + std::vector elems; + auto ttype = cg->compile(p.node.as(), codegen::TypeUsage::Storage); + + for ( const auto&& [i, b] : util::enumerate(n.bits()) ) + elems.push_back( + fmt("::hilti::rt::type_info::bitfield::Bits{ \"%s\", %s, hilti::rt::bitfield::elementOffset<%s, %d>() }", + b.id(), cg->typeInfo(b.itemType()), ttype, i)); + + return fmt("::hilti::rt::type_info::Bitfield(std::vector<::hilti::rt::type_info::bitfield::Bits>({%s}))", + util::join(elems, ", ")); + } + result_t operator()(const type::Enum& n) { std::vector labels; @@ -881,8 +901,8 @@ struct VisitorTypeInfoDynamic : hilti::visitor::PreOrdertypeInfo(f.type()), cxx_type_id, cxx::ID(f.id()), f.isInternal(), + fields.push_back(fmt("::hilti::rt::type_info::struct_::Field{ \"%s\", %s, offsetof(%s, %s), %s, %s%s }", + cxx::ID(f.id()), cg->typeInfo(f.type()), cxx_type_id, cxx::ID(f.id()), f.isInternal(), f.isAnonymous(), accessor)); } @@ -1079,7 +1099,7 @@ const CxxTypeInfo& CodeGen::_getOrCreateTypeInfo(const hilti::Type& t) { // Prefer the bare type name as the display value. display << *t.typeID(); else - hilti::print(display, t); + hilti::print(display, t, true); if ( display.str().empty() ) logger().internalError(fmt("codegen: type %s does not have a display rendering for type information", diff --git a/hilti/toolchain/src/compiler/codegen/unpack.cc b/hilti/toolchain/src/compiler/codegen/unpack.cc index 3efa3089d..677a29b65 100644 --- a/hilti/toolchain/src/compiler/codegen/unpack.cc +++ b/hilti/toolchain/src/compiler/codegen/unpack.cc @@ -19,10 +19,11 @@ namespace { struct Visitor : hilti::visitor::PreOrder { enum class Kind { Pack, Unpack }; - Visitor(CodeGen* cg, Kind kind, cxx::Expression data, const std::vector& args) - : cg(cg), kind(kind), data(std::move(data)), args(args) {} + Visitor(CodeGen* cg, Kind kind, Type data_type, cxx::Expression data, const std::vector& args) + : cg(cg), kind(kind), data_type(std::move(data_type)), data(std::move(data)), args(args) {} CodeGen* cg; Kind kind; + Type data_type; cxx::Expression data; const std::vector& args; @@ -44,6 +45,24 @@ struct Visitor : hilti::visitor::PreOrder { util::cannot_be_reached(); } + result_t operator()(const type::Bitfield& n, position_t p) { + assert(kind == Kind::Unpack); // packing not supported (yet?) + + auto bitorder = cxx::Expression("hilti::rt::integer::BitOrder::LSB0"); + if ( args.size() > 1 ) + bitorder = args[1]; + + auto unpacked = + cg->addTmp("x", cg->compile(type::Result(type::Tuple({type::UnsignedInteger(n.width()), data_type})), + codegen::TypeUsage::Storage)); + auto unpack_uint = + fmt("%s = ::hilti::rt::integer::unpack(%s, %s)", unpacked, n.width(), data, args[0]); + + auto bf_value = cg->unsignedIntegerToBitfield(n, fmt("std::get<0>(*%s)", unpacked), bitorder); + return fmt("(%s, hilti::rt::make_result(std::make_tuple(%s, std::get<1>(*%s))))", unpack_uint, bf_value, + unpacked); + } + result_t operator()(const type::UnsignedInteger& n) { return fmt("::hilti::rt::integer::%s(%s, %s)", kindToString(), n.width(), data, args[0]); } @@ -61,7 +80,7 @@ struct Visitor : hilti::visitor::PreOrder { cxx::Expression CodeGen::pack(const Expression& data, const std::vector& args) { auto cxx_args = util::transform(args, [&](const auto& e) { return compile(e, false); }); - if ( auto x = Visitor(this, Visitor::Kind::Pack, compile(data), cxx_args).dispatch(data.type()) ) + if ( auto x = Visitor(this, Visitor::Kind::Pack, data.type(), compile(data), cxx_args).dispatch(data.type()) ) return cxx::Expression(*x); logger().internalError("pack failed to compile", data.type()); @@ -69,15 +88,16 @@ cxx::Expression CodeGen::pack(const Expression& data, const std::vector& args) { - if ( auto x = Visitor(this, Visitor::Kind::Pack, data, args).dispatch(t) ) + if ( auto x = Visitor(this, Visitor::Kind::Pack, type::Bytes(), data, args).dispatch(t) ) return cxx::Expression(*x); logger().internalError("pack failed to compile", t); } -cxx::Expression CodeGen::unpack(const hilti::Type& t, const Expression& data, const std::vector& args, bool throw_on_error) { +cxx::Expression CodeGen::unpack(const hilti::Type& t, const Expression& data, const std::vector& args, + bool throw_on_error) { auto cxx_args = util::transform(args, [&](const auto& e) { return compile(e, false); }); - if ( auto x = Visitor(this, Visitor::Kind::Unpack, compile(data), cxx_args).dispatch(t) ) { + if ( auto x = Visitor(this, Visitor::Kind::Unpack, data.type(), compile(data), cxx_args).dispatch(t) ) { if ( throw_on_error ) return cxx::Expression(util::fmt("%s.valueOrThrow()", *x)); else @@ -87,9 +107,9 @@ cxx::Expression CodeGen::unpack(const hilti::Type& t, const Expression& data, co logger().internalError("unpack failed to compile", t); } -cxx::Expression CodeGen::unpack(const hilti::Type& t, const cxx::Expression& data, +cxx::Expression CodeGen::unpack(const hilti::Type& t, const Type& data_type, const cxx::Expression& data, const std::vector& args, bool throw_on_error) { - if ( auto x = Visitor(this, Visitor::Kind::Unpack, data, args).dispatch(t) ) { + if ( auto x = Visitor(this, Visitor::Kind::Unpack, data_type, data, args).dispatch(t) ) { if ( throw_on_error ) return cxx::Expression(util::fmt("%s.valueOrThrow<::hilti::rt::InvalidValue>()", *x)); else diff --git a/hilti/toolchain/src/compiler/coercion.cc b/hilti/toolchain/src/compiler/coercion.cc index 3df265de0..b21a8fc7f 100644 --- a/hilti/toolchain/src/compiler/coercion.cc +++ b/hilti/toolchain/src/compiler/coercion.cc @@ -221,6 +221,12 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) return ctor::Bool(c.value() != 0, c.meta()); + if ( auto t = dst.tryAs(); t && c.value() >= 0 ) { + auto u = static_cast(c.value()); + if ( auto [umin, umax] = util::unsigned_integer_range(t->width()); u >= umin && u <= umax ) + return ctor::UnsignedInteger(u, t->width(), c.meta()); + } + return {}; } @@ -271,6 +277,12 @@ struct VisitorCtor : public visitor::PreOrder, VisitorCtor> return ctor::Real(static_cast(c.value())); } + if ( auto t = dst.tryAs() ) { + uint64_t u = c.value(); + if ( auto [umin, umax] = util::unsigned_integer_range(t->width()); u >= umin && u <= umax ) + return ctor::UnsignedInteger(u, t->width(), c.meta()); + } + return {}; } @@ -532,6 +544,11 @@ struct VisitorType : public visitor::PreOrder, VisitorType> return dst; } + if ( auto t = dst.tryAs() ) { + if ( src.width() <= t->width() ) + return dst; + } + return {}; } @@ -775,7 +792,8 @@ Result>> hilti::coerceOperands(const nod auto oat = operator_::type(op.type, exprs, node::Range(transformed.begin(), transformed.end())); if ( ! oat ) { - HILTI_DEBUG(logging::debug::Operator, util::fmt(" [param %d] could not look up operand type -> failure", i)); + HILTI_DEBUG(logging::debug::Operator, + util::fmt(" [param %d] could not look up operand type -> failure", i)); return result::Error("could not look up operand type"); } @@ -904,7 +922,7 @@ static CoercedExpression _coerceExpression(const Expression& e, const Type& src, if ( style & CoercionStyle::OperandMatching ) { // Don't allow a constant value to match a non-constant operand. - if ( e_is_const && !dst_is_const && dst_is_mut ) + if ( e_is_const && ! dst_is_const && dst_is_mut ) RETURN(result::Error()); } } @@ -1019,7 +1037,8 @@ CoercedExpression hilti::coerceExpression(const Expression& e, const Type& src, } // Public version going through all plugins. -CoercedExpression hilti::coerceExpression(const Expression& e, const Type& dst, bitmask style, bool lhs) { +CoercedExpression hilti::coerceExpression(const Expression& e, const Type& dst, bitmask style, + bool lhs) { return coerceExpression(e, e.type(), dst, style, lhs); } diff --git a/hilti/toolchain/src/compiler/parser/parser.yy b/hilti/toolchain/src/compiler/parser/parser.yy index ba0730d2f..fa7fe81e4 100644 --- a/hilti/toolchain/src/compiler/parser/parser.yy +++ b/hilti/toolchain/src/compiler/parser/parser.yy @@ -34,7 +34,7 @@ namespace hilti { namespace detail { class Parser; } } %glr-parser %expect 113 -%expect-rr 207 +%expect-rr 209 %{ @@ -67,6 +67,8 @@ static hilti::Type viewForType(hilti::Type t, hilti::Meta m) { #define __loc__ toMeta(yylhs.location) +static int _field_width = 0; + %} %token IDENT "identifier" @@ -98,6 +100,7 @@ static hilti::Type viewForType(hilti::Type t, hilti::Meta m) { %token AUTO "auto" %token AT "at" %token BEGIN_ "begin" +%token BITFIELD "bitfield" %token BOOL "bool" %token BREAK "break" %token BYTES "bytes" @@ -223,7 +226,7 @@ static hilti::Type viewForType(hilti::Type t, hilti::Meta m) { %type local_id scoped_id dotted_id function_id scoped_function_id %type local_decl local_init_decl global_decl type_decl import_decl constant_decl function_decl global_scope_decl property_decl struct_field union_field %type > struct_fields union_fields opt_union_fields -%type base_type_no_attrs base_type type function_type tuple_type struct_type enum_type union_type +%type base_type_no_attrs base_type type function_type tuple_type struct_type enum_type union_type bitfield_type %type ctor tuple struct_ list regexp map set %type expr tuple_elem tuple_expr member_expr ctor_expr expr_0 expr_1 expr_2 expr_3 expr_4 expr_5 expr_6 expr_7 expr_8 expr_9 expr_a expr_b expr_c expr_d expr_e expr_f expr_g %type > opt_tuple_elems1 opt_tuple_elems2 exprs opt_exprs opt_type_arguments @@ -249,6 +252,8 @@ static hilti::Type viewForType(hilti::Type t, hilti::Meta m) { %type map_elem %type enum_label %type > enum_labels +%type > bitfield_bits opt_bitfield_bits +%type bitfield_bits_spec %type > re_patterns %type re_pattern_constant %type switch_case @@ -594,6 +599,7 @@ base_type_no_attrs | struct_type { $$ = std::move($1); } | union_type { $$ = std::move($1); } | enum_type { $$ = std::move($1); } + | bitfield_type { $$ = std::move($1); } ; base_type : base_type_no_attrs opt_type_flags @@ -678,6 +684,26 @@ enum_label : local_id { $$ = hilti::type::enum_::Labe | local_id '=' CUINTEGER { $$ = hilti::type::enum_::Label(std::move($1), $3, __loc__); } ; +bitfield_type : BITFIELD '(' CUINTEGER ')' + { _field_width = $3; } + '{' opt_bitfield_bits '}' + { $$ = hilti::type::Bitfield($3, $7, {}, __loc__); } + +opt_bitfield_bits + : bitfield_bits + { $$ = std::move($1); } + | /* empty */ { $$ = std::vector(); } + +bitfield_bits + : bitfield_bits bitfield_bits_spec + { $$ = std::move($1); $$.push_back(std::move($2)); } + | bitfield_bits_spec { $$ = std::vector(); $$.push_back(std::move($1)); } + +bitfield_bits_spec + : local_id ':' CUINTEGER DOTDOT CUINTEGER opt_attributes ';' + { $$ = hilti::type::bitfield::Bits(std::move($1), $3, $5, _field_width, std::move($6), __loc__); } + | local_id ':' CUINTEGER opt_attributes ';' + { $$ = hilti::type::bitfield::Bits(std::move($1), $3, $3, _field_width, std::move($4), __loc__); } /* Expressions */ expr : expr_0 { $$ = std::move($1); } @@ -783,6 +809,7 @@ expr_f : ctor { $$ = hilti::expression::Ctor( expr_g : '(' expr ')' { $$ = hilti::expression::Grouping(std::move($2)); } | scoped_id { $$ = hilti::expression::UnresolvedID(std::move($1), __loc__); } + | DOLLARDOLLAR { $$ = hilti::expression::UnresolvedID(std::move("__dd"), __loc__);} | SCOPE { $$ = hilti::expression::Keyword(hilti::expression::keyword::Kind::Scope, __loc__); } diff --git a/hilti/toolchain/src/compiler/parser/scanner.ll b/hilti/toolchain/src/compiler/parser/scanner.ll index 4e65513db..749cb9af2 100644 --- a/hilti/toolchain/src/compiler/parser/scanner.ll +++ b/hilti/toolchain/src/compiler/parser/scanner.ll @@ -56,8 +56,8 @@ hexit [0-9a-fA-F] hexs {hexit}+ E ([Ee][+-]?{digits}) P ([Pp][+-]?{digits}) -decfloat {digits}{E}|{digit}*\.{digits}{E}?|{digits}\.{digit}*{E}? -hexfloat 0[xX]({hexit}+{P}|{hexit}*\.{hexit}+{P}?|{hexit}+\.{hexit}*{P}?) +decfloat {digits}{E}|{digit}*\.{digits}{E}?|{digits}\.{digits}{E}? +hexfloat 0[xX]({hexit}+{P}|{hexit}*\.{hexit}+{P}?|{hexit}+\.{hexs}{P}?) id [a-zA-Z_]|[a-zA-Z_][a-zA-Z_0-9-]*[a-zA-Z_0-9]|[$][$] property %[a-zA-Z_][a-zA-Z_0-9-]* string \"(\\.|[^\\"])*\" @@ -84,6 +84,7 @@ assert return token::ASSERT; assert-exception return token::ASSERT_EXCEPTION; auto return token::AUTO; begin return token::BEGIN_; +bitfield return token::BITFIELD; bool return token::BOOL; break return token::BREAK; bytes return token::BYTES; diff --git a/hilti/toolchain/src/compiler/visitors/printer.cc b/hilti/toolchain/src/compiler/visitors/printer.cc index 180282807..06929bf6a 100644 --- a/hilti/toolchain/src/compiler/visitors/printer.cc +++ b/hilti/toolchain/src/compiler/visitors/printer.cc @@ -797,6 +797,38 @@ struct Visitor : visitor::PreOrder { void operator()(const type::Bool& n) { out << const_(n) << "bool"; } + void operator()(const type::bitfield::Bits& n) { + out << " " << n.id() << ": "; + + if ( n.lower() == n.upper() ) + out << fmt("%d", n.lower()); + else + out << fmt("%d..%d", n.lower(), n.upper()); + + if ( n.attributes() ) + out << ' ' << *n.attributes(); + + out << ";" << out.newline(); + } + + void operator()(const type::Bitfield& n, position_t p) { + if ( ! out.isExpandSubsequentType() ) { + if ( auto id = p.node.as().typeID() ) { + out << *id; + return; + } + } + + out.setExpandSubsequentType(false); + + out << const_(n) << fmt("bitfield(%d) {", n.width()) << out.newline(); + + for ( const auto& f : n.bits() ) + out << f; + + out << "}"; + } + void operator()(const type::Bytes& n) { out << const_(n) << "bytes"; } void operator()(const type::enum_::Label& n) { out << n.id() << " = " << n.value(); } diff --git a/hilti/toolchain/src/compiler/visitors/resolver.cc b/hilti/toolchain/src/compiler/visitors/resolver.cc index 237031d39..08ad48204 100644 --- a/hilti/toolchain/src/compiler/visitors/resolver.cc +++ b/hilti/toolchain/src/compiler/visitors/resolver.cc @@ -474,6 +474,23 @@ struct Visitor : public visitor::PostOrder { } } + void operator()(const type::bitfield::Bits& b, position_t p) { + if ( type::isResolved(b.itemType()) ) + return; + + Type t = b.ddType(); + + if ( auto a = AttributeSet::find(b.attributes(), "&convert") ) { + t = a->valueAsExpression()->get().type(); + if ( ! type::isResolved(t) ) + return; + } + + logChange(p.node, t); + p.node.as().setItemType(t); + modified = true; + } + void operator()(const type::Enum& m, position_t p) { if ( type::isResolved(p.node.as()) ) return; diff --git a/hilti/toolchain/src/compiler/visitors/scope-builder.cc b/hilti/toolchain/src/compiler/visitors/scope-builder.cc index 75aade833..48ebd8457 100644 --- a/hilti/toolchain/src/compiler/visitors/scope-builder.cc +++ b/hilti/toolchain/src/compiler/visitors/scope-builder.cc @@ -155,6 +155,11 @@ struct Visitor : public visitor::PostOrder { p.node.scope()->insert(std::move(x)); } + void operator()(const type::bitfield::Bits& b, position_t p) { + if ( auto d = b.ddRef() ) + p.node.scope()->insert(std::move(d)); + } + void operator()(const type::Enum& m, position_t p) { if ( ! p.parent().isA() ) return; diff --git a/hilti/toolchain/src/compiler/visitors/validator.cc b/hilti/toolchain/src/compiler/visitors/validator.cc index be3ee2f9e..ecd719e87 100644 --- a/hilti/toolchain/src/compiler/visitors/validator.cc +++ b/hilti/toolchain/src/compiler/visitors/validator.cc @@ -430,6 +430,21 @@ struct VisitorPost : public hilti::visitor::PreOrder, public error("automatic type has not been resolved", p, node::ErrorPriority::Low); } + void operator()(const type::Bitfield& b, position_t p) { + const auto width = b.width(); + + for ( const auto& bit : b.bits() ) { + const auto lower = bit.lower(); + const auto upper = bit.upper(); + + if ( lower > upper ) + error("lower limit needs to be lower than upper limit", p); + + if ( upper >= width ) + error("upper limit is beyond the width of the bitfield", p); + } + } + void operator()(const type::Enum& n, position_t p) { std::unordered_set seen; diff --git a/spicy/lib/spicy_rt.hlt b/spicy/lib/spicy_rt.hlt index d34888ec3..8721a6214 100644 --- a/spicy/lib/spicy_rt.hlt +++ b/spicy/lib/spicy_rt.hlt @@ -68,6 +68,7 @@ public type Parser = struct { vector ports; } &cxxname="spicy::rt::Parser"; +public type BitOrder = enum { LSB0, MSB0 } &cxxname="hilti::rt::integer::BitOrder"; public type Direction = enum { Originator, Responder, Both } &cxxname="spicy::rt::Direction"; public type FindDirection = __library_type("hilti::rt::stream::Direction"); public type MIMEType = __library_type("spicy::rt::MIMEType"); @@ -87,10 +88,6 @@ declare public optional> unit_find(iterator begin_, ite declare public void backtrack() &cxxname="spicy::rt::detail::backtrack" &have_prototype; -public type BitOrder = enum { LSB0, MSB0 } &cxxname="hilti::rt::integer::BitOrder"; - # TODO: Should accept BitOrder instead of enum<*> here. -declare public uint<*> extractBits(uint<*> v, uint<64> lower, uint<64> upper, enum<*> order) &cxxname="hilti::rt::integer::bits" &have_prototype; - declare public void initializeParsedUnit(inout ParsedUnit punit, any unit, TypeInfo ti) &cxxname="spicy::rt::ParsedUnit::initialize" &have_prototype; } diff --git a/spicy/toolchain/CMakeLists.txt b/spicy/toolchain/CMakeLists.txt index 76560d996..2de2b735b 100644 --- a/spicy/toolchain/CMakeLists.txt +++ b/spicy/toolchain/CMakeLists.txt @@ -42,7 +42,6 @@ autogen_dispatchers( set(SOURCES_COMPILER src/ast/hook.cc src/ast/types.cc - src/ast/types/bitfield.cc src/ast/types/unit.cc src/ast/types/unit-items/field.cc src/ast/types/unit-items/switch.cc diff --git a/spicy/toolchain/bin/spicy-dump/printer-json.cc b/spicy/toolchain/bin/spicy-dump/printer-json.cc index 9c895cb74..b848d3f8a 100644 --- a/spicy/toolchain/bin/spicy-dump/printer-json.cc +++ b/spicy/toolchain/bin/spicy-dump/printer-json.cc @@ -22,6 +22,14 @@ nlohmann::json JSONPrinter::convert(const hilti::rt::type_info::Value& v) { case TypeInfo::Undefined: throw RuntimeError("unhandled type"); case TypeInfo::Address: return type.address->get(v); case TypeInfo::Any: return ""; + case TypeInfo::Bitfield: { + auto j = json::object(); + + for ( const auto& i : type.bitfield->iterate(v) ) + j[i.first.name] = convert(i.second); + + return j; + } case TypeInfo::Bool: return type.bool_->get(v); case TypeInfo::Bytes: return to_string_for_print(type.bytes->get(v)); case TypeInfo::BytesIterator: return to_string(type.bytes_iterator->get(v)); @@ -91,12 +99,22 @@ nlohmann::json JSONPrinter::convert(const hilti::rt::type_info::Value& v) { // Field not set. continue; + if ( f.type->tag == TypeInfo::Bitfield && f.isAnonymous() ) { + // Special case anonymous bitfield: map field to into current array. + for ( const auto& [b, val] : f.type->bitfield->iterate(y) ) + j[b.name] = convert(val); + + continue; + } + j[f.name] = convert(y); } - auto offsets = json::array(); + auto offsets = json::object(); const auto& __offsets = spicy::rt::get_offsets_for_unit(*struct_, v); if ( _options.include_offsets && __offsets ) { + auto fields = struct_->fields(); + for ( const auto&& [index, offset] : enumerate(*__offsets) ) { auto o = json::object(); @@ -106,8 +124,16 @@ nlohmann::json JSONPrinter::convert(const hilti::rt::type_info::Value& v) { o["end"] = end->Ref(); } - offsets.push_back(std::move(o)); + const auto& field = fields[index].get(); + if ( field.type->tag == TypeInfo::Bitfield && field.isAnonymous() ) { + // Special case anonymous bitfield: add offsets for all its items + for ( const auto& b : field.type->bitfield->bits() ) + offsets[b.name] = o; + } + else + offsets[field.name] = std::move(o); } + j["__offsets"] = offsets; } diff --git a/spicy/toolchain/bin/spicy-dump/printer-text.cc b/spicy/toolchain/bin/spicy-dump/printer-text.cc index 11b8bef34..248329e03 100644 --- a/spicy/toolchain/bin/spicy-dump/printer-text.cc +++ b/spicy/toolchain/bin/spicy-dump/printer-text.cc @@ -15,6 +15,32 @@ void TextPrinter::print(const type_info::Value& v) { case TypeInfo::Undefined: throw RuntimeError("unhandled type"); case TypeInfo::Address: out() << type.address->get(v); break; case TypeInfo::Any: out() << ""; break; + case TypeInfo::Bitfield: { + auto first = true; + + out() << "{\n"; + + indent([&]() { + outputIndent(); + + for ( auto [key, value] : type.bitfield->iterate(v) ) { + if ( ! first ) { + out() << '\n'; + outputIndent(); + } + else + first = false; + + out() << key.name << ": "; + print(value); + } + }); + + out() << '\n'; + outputIndent(); + out() << '}'; + break; + } case TypeInfo::Bool: out() << (type.bool_->get(v) ? "True" : "False"); break; case TypeInfo::Bytes: out() << to_string_for_print(type.bytes->get(v)); break; case TypeInfo::BytesIterator: out() << to_string(type.bytes_iterator->get(v)); break; @@ -107,31 +133,32 @@ void TextPrinter::print(const type_info::Value& v) { const auto* x = type.struct_; - const auto& offsets = spicy::rt::get_offsets_for_unit(*x, v); - bool empty = true; uint64_t index = 0; indent([&]() { for ( const auto& [f, y] : x->iterate(v) ) { if ( y ) { - out() << '\n'; - outputIndent(); - out() << f.name << ": "; - print(y); - - const auto& offset = offsets && offsets->size() > index ? offsets->at(index) : std::nullopt; - - if ( _options.include_offsets && offset ) { - const auto& start = std::get<0>(*offset); - const auto& end = std::get<1>(*offset); + if ( f.type->tag == TypeInfo::Bitfield && f.isAnonymous() ) { + // Special case anonymous bitfield: print at top level. + for ( const auto& [b, val] : f.type->bitfield->iterate(y) ) { + out() << '\n'; + outputIndent(); + + out() << b.name << ": "; + print(val); + printOffsets(*x, v, index); + } + } + else { + out() << '\n'; + outputIndent(); - out() << " [" << start << ", "; + if ( ! f.isAnonymous() ) + out() << f.name; - if ( end ) - out() << *end; - else - out() << "-"; - out() << "]"; + out() << ": "; + print(y); + printOffsets(*x, v, index); } empty = false; @@ -221,3 +248,21 @@ void TextPrinter::print(const type_info::Value& v) { } } } + +void TextPrinter::printOffsets(const type_info::Struct& ti, const type_info::Value& v, uint64_t index) { + const auto& offsets = spicy::rt::get_offsets_for_unit(ti, v); + const auto& offset = offsets && offsets->size() > index ? offsets->at(index) : std::nullopt; + + if ( _options.include_offsets && offset ) { + const auto& start = std::get<0>(*offset); + const auto& end = std::get<1>(*offset); + + out() << " [" << start << ", "; + + if ( end ) + out() << *end; + else + out() << "-"; + out() << "]"; + } +} diff --git a/spicy/toolchain/bin/spicy-dump/printer-text.h b/spicy/toolchain/bin/spicy-dump/printer-text.h index 98f851958..066c25cb6 100644 --- a/spicy/toolchain/bin/spicy-dump/printer-text.h +++ b/spicy/toolchain/bin/spicy-dump/printer-text.h @@ -33,6 +33,9 @@ class TextPrinter { // Return output stream. std::ostream& out() { return _output; } + // Append rendering of offsets to current output line. + void printOffsets(const hilti::rt::type_info::Struct& ti, const hilti::rt::type_info::Value& v, uint64_t index); + // Insert current indentation into output stream. void outputIndent() { out() << std::string(static_cast::size_type>(_level) * 2, ' '); } diff --git a/spicy/toolchain/include/ast/nodes.decl b/spicy/toolchain/include/ast/nodes.decl index d3b7cd3e2..243ed6049 100644 --- a/spicy/toolchain/include/ast/nodes.decl +++ b/spicy/toolchain/include/ast/nodes.decl @@ -2,7 +2,6 @@ trait spicy::type::unit::Item isUnitItem spicy::Hook : isNode -spicy::type::bitfield::Bits : isNode spicy::type::unit::Item : isNode spicy::type::unit::item::switch_::Case : isNode @@ -10,7 +9,6 @@ spicy::ctor::Unit : isCtor spicy::declaration::UnitHook : isDeclaration -spicy::type::Bitfield : isType spicy::type::Sink : isType spicy::type::Unit : isType diff --git a/spicy/toolchain/include/ast/operators/all.h b/spicy/toolchain/include/ast/operators/all.h index fffa7f54a..8630d6616 100644 --- a/spicy/toolchain/include/ast/operators/all.h +++ b/spicy/toolchain/include/ast/operators/all.h @@ -2,6 +2,5 @@ #pragma once -#include #include #include diff --git a/spicy/toolchain/include/ast/operators/unit.h b/spicy/toolchain/include/ast/operators/unit.h index eb967ec97..891fb047f 100644 --- a/spicy/toolchain/include/ast/operators/unit.h +++ b/spicy/toolchain/include/ast/operators/unit.h @@ -204,6 +204,9 @@ class MemberCall : public hilti::expression::ResolvedOperatorBase { public: using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + struct Operator : public hilti::trait::isOperator { Operator(const type::Unit& stype, const type::unit::item::Field& f) : _field(f) { auto ftype = f.itemType().as(); diff --git a/spicy/toolchain/include/ast/types/all.h b/spicy/toolchain/include/ast/types/all.h index 3bf8edcb3..74762d62e 100644 --- a/spicy/toolchain/include/ast/types/all.h +++ b/spicy/toolchain/include/ast/types/all.h @@ -2,7 +2,6 @@ #pragma once -#include #include #include #include diff --git a/spicy/toolchain/include/ast/types/unit-items/field.h b/spicy/toolchain/include/ast/types/unit-items/field.h index 3992f971c..5c2258dea 100644 --- a/spicy/toolchain/include/ast/types/unit-items/field.h +++ b/spicy/toolchain/include/ast/types/unit-items/field.h @@ -25,9 +25,9 @@ class Field : public hilti::NodeBase, public hilti::node::WithDocString, public std::optional repeat, const std::vector& sinks, std::optional attrs = {}, std::optional cond = {}, const std::vector& hooks = {}, Meta m = Meta()) - : NodeBase(nodes((id ? id : _uniquer.get("anon")), hilti::type::pruneWalk(std::move(type)), hilti::type::auto_, - hilti::node::none, hilti::type::auto_, node::none, std::move(repeat), std::move(attrs), - std::move(cond), args, sinks, hooks), + : NodeBase(nodes((id ? id : _uniquer.get("__anon")), hilti::type::pruneWalk(std::move(type)), + hilti::type::auto_, hilti::node::none, hilti::type::auto_, node::none, std::move(repeat), + std::move(attrs), std::move(cond), args, sinks, hooks), std::move(m)), _is_forwarding(false), _is_transient(false), diff --git a/spicy/toolchain/src/ast/types/unit-items/field.cc b/spicy/toolchain/src/ast/types/unit-items/field.cc index 7b4dcaf7c..e23579722 100644 --- a/spicy/toolchain/src/ast/types/unit-items/field.cc +++ b/spicy/toolchain/src/ast/types/unit-items/field.cc @@ -3,11 +3,11 @@ #include #include +#include #include #include #include -#include #include using namespace spicy; diff --git a/spicy/toolchain/src/compiler/codegen/codegen.cc b/spicy/toolchain/src/compiler/codegen/codegen.cc index 9800c00c3..9c03dda38 100644 --- a/spicy/toolchain/src/compiler/codegen/codegen.cc +++ b/spicy/toolchain/src/compiler/codegen/codegen.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -161,13 +162,6 @@ struct VisitorPass2 : public hilti::visitor::PreOrder { * replaceNode(&p, hilti::expression::UnresolvedOperator(n.operator_().kind(), n.operands(), p.node.meta())); * } */ - result_t operator()(const operator_::bitfield::Member& n, position_t p) { - const auto& id = n.op1().as().id(); - auto idx = n.op0().type().as().bitsIndex(id); - assert(idx); - auto x = builder::index(n.op0(), *idx, n.meta()); - replaceNode(&p, std::move(x)); - } result_t operator()(const operator_::unit::Unset& n, position_t p) { const auto& id = n.op1().as().id(); diff --git a/spicy/toolchain/src/compiler/codegen/parser-builder.cc b/spicy/toolchain/src/compiler/codegen/parser-builder.cc index 012072be5..7bd9d99b9 100644 --- a/spicy/toolchain/src/compiler/codegen/parser-builder.cc +++ b/spicy/toolchain/src/compiler/codegen/parser-builder.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,6 @@ #include #include -#include #include #include #include @@ -499,7 +499,7 @@ struct ProductionVisitor builder()->addAssign(destination(), builder::default_(field->itemType())); } - else if ( field->isAnonymous() || field->isSkip() ) { + else if ( (field->isAnonymous() || field->isSkip()) && ! field->itemType().isA() ) { // We won't have a field to store the value in, create a temporary. auto dst = builder()->addTmp(fmt("transient_%s", field->id()), field->itemType()); pushDestination(dst); diff --git a/spicy/toolchain/src/compiler/codegen/parsers/types.cc b/spicy/toolchain/src/compiler/codegen/parsers/types.cc index 6f1b311c6..23c1e99df 100644 --- a/spicy/toolchain/src/compiler/codegen/parsers/types.cc +++ b/spicy/toolchain/src/compiler/codegen/parsers/types.cc @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -49,8 +50,8 @@ struct Visitor : public hilti::visitor::PreOrder { return builder()->addTmp("x", t); } - Expression performUnpack(const Expression& target, const Type& t, int len, const std::vector& unpack_args, - const Meta& m, bool is_try) { + Expression performUnpack(const Expression& target, const Type& t, int len, + const std::vector& unpack_args, const Meta& m, bool is_try) { if ( ! is_try ) { auto error_msg = fmt("expecting %d bytes for unpacking value", len); pb->waitForInput(builder::integer(len), error_msg, m); @@ -115,45 +116,30 @@ struct Visitor : public hilti::visitor::PreOrder { is_try); } - result_t operator()(const spicy::type::Bitfield& t) { - const auto& itype = t.parseType(); - auto value = builder()->addTmp("bitfield", itype); - performUnpack(value, itype, t.width() / 8, {state().cur, fieldByteOrder()}, t.meta(), is_try); + result_t operator()(const hilti::type::Bitfield& t, position_t p) { + std::optional bitorder = builder::id("hilti::BitOrder::LSB0"); - builder()->addDebugMsg("spicy", fmt("%s = %%s", meta.field()->id()), {value}); - builder()->addDebugIndent("spicy"); - - std::vector extracted_bits; + if ( auto attrs = t.attributes() ) { + if ( auto a = AttributeSet::find(*attrs, "&bit-order") ) + bitorder = *a->valueAsExpression(); + } - for ( const auto& b : t.bits() ) { - auto bit_order = builder::id("spicy_rt::BitOrder::LSB0"); + auto target = destination(t); + performUnpack(target, t, t.width() / 8, {state().cur, fieldByteOrder(), *bitorder}, t.meta(), is_try); - if ( const auto& a = AttributeSet::find(meta.field()->attributes(), "&bit-order") ) - bit_order = *a->valueAsExpression(); - else if ( const auto& p = state().unit.get().propertyItem("%bit-order") ) - bit_order = *p->expression(); + if ( pb->options().debug ) { + // Print all the bit ranges individually so that we can include + // their IDs, which the standard tuple output wouldn't show. + builder()->addDebugMsg("spicy", fmt("%s = %%s", meta.field()->id()), + {builder::member(target, "__value__")}); - auto x = - builder()->addTmp("bits", itype, - builder::call("spicy_rt::extractBits", {value, builder::integer(b.lower()), - builder::integer(b.upper()), bit_order})); + builder()->addDebugIndent("spicy"); + for ( const auto& bits : t.bits() ) + builder()->addDebugMsg("spicy", fmt("%s = %%s", bits.id()), {builder::member(target, bits.id())}); - if ( auto a = AttributeSet::find(b.attributes(), "&convert") ) { - auto converted = builder()->addTmp(ID("converted"), b.itemType()); - auto block = builder()->addBlock(); - block->addLocal(ID("__dd"), itype, x); - block->addAssign(converted, *a->valueAsExpression()); - x = converted; - } - - extracted_bits.push_back(x); - builder()->addDebugMsg("spicy", fmt("%s = %%s", b.id()), {x}); + builder()->addDebugDedent("spicy"); } - builder()->addDebugDedent("spicy"); - - auto target = destination(t.type()); - builder()->addAssign(target, builder::tuple(extracted_bits)); return target; } @@ -172,9 +158,7 @@ struct Visitor : public hilti::visitor::PreOrder { return performUnpack(destination(t), t, t.width() / 8, {state().cur, fieldByteOrder()}, t.meta(), is_try); } - result_t operator()(const hilti::type::Void& t) { - return hilti::expression::Void(); - } + result_t operator()(const hilti::type::Void& t) { return hilti::expression::Void(); } result_t operator()(const hilti::type::Bytes& t) { auto chunked_attr = AttributeSet::find(meta.field()->attributes(), "&chunked"); diff --git a/spicy/toolchain/src/compiler/codegen/unit-builder.cc b/spicy/toolchain/src/compiler/codegen/unit-builder.cc index ec269206e..a10d51d11 100644 --- a/spicy/toolchain/src/compiler/codegen/unit-builder.cc +++ b/spicy/toolchain/src/compiler/codegen/unit-builder.cc @@ -41,7 +41,11 @@ struct FieldBuilder : public hilti::visitor::PreOrder { if ( auto x = AttributeSet::find(f.attributes(), "&default") ) attrs = AttributeSet::add(attrs, *x); - if ( f.isAnonymous() || f.isSkip() || f.parseType().isA() ) + if ( f.isAnonymous() ) + attrs = AttributeSet::add(attrs, Attribute("&anonymous")); + + if ( (f.isAnonymous() || f.isSkip() || f.parseType().isA()) && + ! f.itemType().isA() ) // This field will never make it into the C++ struct. We still // carry it around though as that makes type inference easier at // times, and also can improve error messages. diff --git a/spicy/toolchain/src/compiler/parser/parser.yy b/spicy/toolchain/src/compiler/parser/parser.yy index 6e25f9562..bf8d14ad5 100644 --- a/spicy/toolchain/src/compiler/parser/parser.yy +++ b/spicy/toolchain/src/compiler/parser/parser.yy @@ -689,7 +689,7 @@ enum_label : local_id { $$ = hilti::type::enum_::Labe bitfield_type : BITFIELD '(' CUINTEGER ')' { _field_width = $3; } '{' opt_bitfield_bits '}' - { $$ = spicy::type::Bitfield($3, $7, __loc__); } + { $$ = spicy::type::Bitfield($3, $7, {}, __loc__); } opt_bitfield_bits : bitfield_bits diff --git a/spicy/toolchain/src/compiler/visitors/normalizer.cc b/spicy/toolchain/src/compiler/visitors/normalizer.cc index a876cd425..088b96302 100644 --- a/spicy/toolchain/src/compiler/visitors/normalizer.cc +++ b/spicy/toolchain/src/compiler/visitors/normalizer.cc @@ -227,6 +227,45 @@ struct Visitor : public hilti::visitor::PostOrder { } } + void operator()(const operator_::unit::MemberNonConst& o, position_t p) { + auto unit = o.op0().type().tryAs(); + auto id = o.op1().tryAs()->id(); + if ( unit && id && ! unit->itemByName(id) ) { + // See if we we got an anonymous bitfield with a member of that + // name. If so, rewrite the access to transparently to refer to the + // member through the field's internal name. + bool found_field = false; + for ( const auto& f : unit->items() ) { + if ( ! f.isAnonymous() ) + continue; + + auto t = f.itemType().tryAs(); + if ( ! t ) + continue; + + auto bits = t->bits(id); + if ( ! bits ) + continue; + + if ( found_field ) + // Ambiguous access, the validator will catch that. + return; + + auto access_field = + operator_::unit::MemberNonConst::Operator().instantiate({o.op0(), + hilti::expression::Member(f.id())}, + o.meta()); + auto access_bits = + bitfield::Member::Operator().instantiate({std::move(access_field), o.op1()}, o.meta()); + + logChange(p.node, access_bits); + p.node = access_bits; + modified = true; + found_field = true; + } + } + } + void operator()(const type::Unit& u, position_t p) { if ( ! p.node.as().typeID() ) return; @@ -269,6 +308,30 @@ struct Visitor : public hilti::visitor::PostOrder { } } } + + void operator()(const type::Bitfield& bf, position_t p) { + if ( auto field = p.parent().tryAs() ) { + // Transfer any "&bitorder" attribute over to the type. + if ( auto a = AttributeSet::find(field->attributes(), "&bit-order"); + a && ! AttributeSet::find(bf.attributes(), "&bit-order") ) { + auto new_attrs = AttributeSet::add(bf.attributes(), *a); + logChange(p.node, "transfer &bitorder attribute"); + p.node.as().setAttributes(new_attrs); + modified = true; + } + } + + if ( auto decl = p.parent().tryAs() ) { + // Transfer any "&bitorder" attribute over to the type. + if ( auto a = AttributeSet::find(decl->attributes(), "&bit-order"); + a && ! AttributeSet::find(bf.attributes(), "&bit-order") ) { + auto new_attrs = AttributeSet::add(bf.attributes(), *a); + logChange(p.node, "transfer &bitorder attribute"); + p.node.as().setAttributes(new_attrs); + modified = true; + } + } + } }; } // anonymous namespace diff --git a/spicy/toolchain/src/compiler/visitors/printer.cc b/spicy/toolchain/src/compiler/visitors/printer.cc index 60b155299..158bcb9e6 100644 --- a/spicy/toolchain/src/compiler/visitors/printer.cc +++ b/spicy/toolchain/src/compiler/visitors/printer.cc @@ -9,7 +9,6 @@ #include using namespace spicy; -using hilti::util::fmt; namespace { @@ -18,38 +17,6 @@ struct Visitor : hilti::visitor::PreOrder { auto const_(const Type& t) { return (out.isCompact() && hilti::type::isConstant(t)) ? "const " : ""; } - void operator()(const type::bitfield::Bits& n) { - out << " " << n.id() << ": "; - - if ( n.lower() == n.upper() ) - out << fmt("%u", n.lower()); - else - out << fmt("%u..%d", n.lower(), n.upper()); - - if ( n.attributes() ) - out << ' ' << *n.attributes(); - - out << ";" << out.newline(); - } - - void operator()(const type::Bitfield& n, position_t p) { - if ( ! out.isExpandSubsequentType() ) { - if ( auto id = p.node.as().typeID() ) { - out << *id; - return; - } - } - - out.setExpandSubsequentType(false); - - out << const_(n) << fmt("bitfield(%d) {\n", n.width()); - - for ( const auto& f : n.bits() ) - out << f; - - out << "}"; - } - void operator()(const type::Sink& /* n */) { out << "sink"; } void operator()(const type::Unit& n) { diff --git a/spicy/toolchain/src/compiler/visitors/resolver.cc b/spicy/toolchain/src/compiler/visitors/resolver.cc index 730820231..833e4a220 100644 --- a/spicy/toolchain/src/compiler/visitors/resolver.cc +++ b/spicy/toolchain/src/compiler/visitors/resolver.cc @@ -58,20 +58,8 @@ enum class FieldType { // Visitor determining a unit field type. struct FieldTypeVisitor : public hilti::visitor::PreOrder { explicit FieldTypeVisitor(FieldType ft) : ft(ft) {} - FieldType ft; - result_t operator()(const type::Bitfield& t) { - switch ( ft ) { - case FieldType::DDType: - case FieldType::ItemType: return t.type(); - - case FieldType::ParseType: return t; - }; - - hilti::util::cannot_be_reached(); - } - result_t operator()(const hilti::type::RegExp& /* t */) { return hilti::type::Bytes(); } }; @@ -170,43 +158,6 @@ struct Visitor : public hilti::visitor::PreOrder { } } - void operator()(const type::bitfield::Bits& b, position_t p) { - if ( type::isResolved(b.itemType()) ) - return; - - Type t = b.ddType(); - - if ( auto a = AttributeSet::find(b.attributes(), "&convert") ) { - t = a->valueAsExpression()->get().type(); - if ( ! type::isResolved(t) ) - return; - } - - logChange(p.node, t, "item type"); - p.node.as().setItemType(t); - modified = true; - } - - void operator()(const type::Bitfield& b, position_t p) { - if ( type::isResolved(b.type()) ) - return; - - std::vector elems; - - for ( const auto& bit : b.bits() ) { - if ( ! type::isResolved(bit.itemType()) ) - return; - - elems.emplace_back(bit.id(), bit.itemType()); - } - - Type t = type::Tuple(std::move(elems), b.meta()); - assert(type::isResolved(t)); - logChange(p.node, t); - p.node.as().setType(t); - modified = true; - } - void operator()(const type::unit::item::Field& f, position_t p) { if ( ! type::isResolved(f.parseType()) ) { if ( auto t = _fieldType(f, f.originalType(), FieldType::ParseType, f.isContainer(), f.meta()) ) { diff --git a/spicy/toolchain/src/compiler/visitors/scope-builder.cc b/spicy/toolchain/src/compiler/visitors/scope-builder.cc index 30454c958..051aea1d0 100644 --- a/spicy/toolchain/src/compiler/visitors/scope-builder.cc +++ b/spicy/toolchain/src/compiler/visitors/scope-builder.cc @@ -26,11 +26,6 @@ struct Visitor : public hilti::visitor::PostOrder { p.node.scope()->insert(std::move(x)); } - void operator()(const type::bitfield::Bits& b, position_t p) { - if ( auto d = b.ddRef() ) - p.node.scope()->insert(std::move(d)); - } - void operator()(const type::unit::item::Field& f, position_t p) { if ( auto d = f.ddRef() ) p.node.scope()->insert(std::move(d)); diff --git a/spicy/toolchain/src/compiler/visitors/validator.cc b/spicy/toolchain/src/compiler/visitors/validator.cc index 4fe750216..f0f5dffdb 100644 --- a/spicy/toolchain/src/compiler/visitors/validator.cc +++ b/spicy/toolchain/src/compiler/visitors/validator.cc @@ -636,6 +636,29 @@ struct VisitorPost : public hilti::visitor::PreOrder, public if ( u.propertyItem("%synchronize-at") && u.propertyItem("%synchronize-after") ) error("unit cannot specify both %synchronize-at and %synchronize-after", p); + + + // Ensure that the items of anonymous bitfields do not lead to ambiguities. + std::set seen_bits; + + for ( const auto& f : u.items() ) { + if ( ! f.isAnonymous() ) + continue; + + auto t = f.itemType().tryAs(); + if ( ! t ) + continue; + + for ( const auto& b : t->bits() ) { + if ( u.itemByName(b.id()) ) + error(fmt("bitfield item '%s' shadows unit field", b.id()), p); + + if ( seen_bits.find(b.id()) != seen_bits.end() ) + error(fmt("bitfield item name '%s' appears in multiple anonymous bitfields", b.id()), p); + + seen_bits.insert(b.id()); + } + } } void operator()(const hilti::operator_::value_reference::Equal& o, position_t p) { @@ -934,21 +957,6 @@ struct VisitorPost : public hilti::visitor::PreOrder, public if ( auto x = n.op0().type().tryAs(); x && ! x->isFilter() ) error("unit type cannot be a filter, %filter missing", p); } - - void operator()(const spicy::type::Bitfield& b, position_t p) { - const auto width = b.width(); - - for ( const auto& bit : b.bits() ) { - const auto lower = bit.lower(); - const auto upper = bit.upper(); - - if ( lower > upper ) - error("lower limit needs to be lower than upper limit", p); - - if ( upper >= width ) - error("upper limit is beyond the width of the bitfield", p); - } - } }; } // anonymous namespace diff --git a/tests/Baseline/hilti.types.bitfield.convert/output b/tests/Baseline/hilti.types.bitfield.convert/output new file mode 100644 index 000000000..49d861c74 --- /dev/null +++ b/tests/Baseline/hilti.types.bitfield.convert/output @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/tests/Baseline/hilti.types.bitfield.ops/output b/tests/Baseline/hilti.types.bitfield.ops/output new file mode 100644 index 000000000..5cb47b88b --- /dev/null +++ b/tests/Baseline/hilti.types.bitfield.ops/output @@ -0,0 +1,2 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +(31, 3) diff --git a/tests/Baseline/hilti.types.bitfield.unpack/output b/tests/Baseline/hilti.types.bitfield.unpack/output new file mode 100644 index 000000000..49d861c74 --- /dev/null +++ b/tests/Baseline/hilti.types.bitfield.unpack/output @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/tests/Baseline/spicy.optimization.default-parser-functions/log b/tests/Baseline/spicy.optimization.default-parser-functions/log index ef59a28c0..85777d638 100644 --- a/tests/Baseline/spicy.optimization.default-parser-functions/log +++ b/tests/Baseline/spicy.optimization.default-parser-functions/log @@ -92,7 +92,6 @@ [debug/optimizer] removing declaration for unused function spicy_rt::backtrack [debug/optimizer] removing declaration for unused function spicy_rt::confirm [debug/optimizer] removing declaration for unused function spicy_rt::createContext -[debug/optimizer] removing declaration for unused function spicy_rt::extractBits [debug/optimizer] removing declaration for unused function spicy_rt::filter_connect [debug/optimizer] removing declaration for unused function spicy_rt::filter_disconnect [debug/optimizer] removing declaration for unused function spicy_rt::filter_forward diff --git a/tests/Baseline/spicy.optimization.feature_requirements/log b/tests/Baseline/spicy.optimization.feature_requirements/log index 76a776520..131a3b0e9 100644 --- a/tests/Baseline/spicy.optimization.feature_requirements/log +++ b/tests/Baseline/spicy.optimization.feature_requirements/log @@ -199,7 +199,6 @@ [debug/optimizer] removing declaration for unused function spicy_rt::backtrack [debug/optimizer] removing declaration for unused function spicy_rt::confirm [debug/optimizer] removing declaration for unused function spicy_rt::createContext -[debug/optimizer] removing declaration for unused function spicy_rt::extractBits [debug/optimizer] removing declaration for unused function spicy_rt::filter_forward [debug/optimizer] removing declaration for unused function spicy_rt::printParserState [debug/optimizer] removing declaration for unused function spicy_rt::reject diff --git a/tests/Baseline/spicy.optimization.unused-functions/log b/tests/Baseline/spicy.optimization.unused-functions/log index 1d173feb8..7ab221b5d 100644 --- a/tests/Baseline/spicy.optimization.unused-functions/log +++ b/tests/Baseline/spicy.optimization.unused-functions/log @@ -147,7 +147,6 @@ [debug/optimizer] removing declaration for unused function spicy_rt::backtrack [debug/optimizer] removing declaration for unused function spicy_rt::confirm [debug/optimizer] removing declaration for unused function spicy_rt::createContext -[debug/optimizer] removing declaration for unused function spicy_rt::extractBits [debug/optimizer] removing declaration for unused function spicy_rt::filter_connect [debug/optimizer] removing declaration for unused function spicy_rt::filter_disconnect [debug/optimizer] removing declaration for unused function spicy_rt::filter_forward diff --git a/tests/Baseline/spicy.optimization.unused-functions/noopt.hlt b/tests/Baseline/spicy.optimization.unused-functions/noopt.hlt index a7d5959eb..cd125f0d2 100644 --- a/tests/Baseline/spicy.optimization.unused-functions/noopt.hlt +++ b/tests/Baseline/spicy.optimization.unused-functions/noopt.hlt @@ -83,7 +83,7 @@ type C = struct { method tuple, int<64>, iterator, optional> __parse_foo_C_stage2(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error); } &on-heap; public type D = struct { - value_ref anon &optional &no-emit; + value_ref anon &optional &anonymous &no-emit; optional> __begin &internal &needed-by-feature="uses_random_access"; optional> __position &internal &needed-by-feature="uses_random_access"; optional> __position_update &internal &needed-by-feature="uses_random_access"; diff --git a/tests/Baseline/spicy.optimization.unused-functions/opt.hlt b/tests/Baseline/spicy.optimization.unused-functions/opt.hlt index 336f69cf6..bcbd14f0c 100644 --- a/tests/Baseline/spicy.optimization.unused-functions/opt.hlt +++ b/tests/Baseline/spicy.optimization.unused-functions/opt.hlt @@ -19,7 +19,7 @@ type C = struct { method tuple, int<64>, iterator, optional> __parse_foo_C_stage2(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error); } &on-heap; public type D = struct { - value_ref anon &optional &no-emit; + value_ref anon &optional &anonymous &no-emit; spicy_rt::Parser __parser &static &internal &needed-by-feature="supports_filters" &always-emit; optional __error &always-emit &internal; method tuple, int<64>, iterator, optional> __parse_stage1(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error); diff --git a/tests/Baseline/spicy.optimization.unused-types/log b/tests/Baseline/spicy.optimization.unused-types/log index c060fc291..be99d8e47 100644 --- a/tests/Baseline/spicy.optimization.unused-types/log +++ b/tests/Baseline/spicy.optimization.unused-types/log @@ -256,7 +256,6 @@ [debug/optimizer] removing declaration for unused function spicy_rt::backtrack [debug/optimizer] removing declaration for unused function spicy_rt::confirm [debug/optimizer] removing declaration for unused function spicy_rt::createContext -[debug/optimizer] removing declaration for unused function spicy_rt::extractBits [debug/optimizer] removing declaration for unused function spicy_rt::filter_connect [debug/optimizer] removing declaration for unused function spicy_rt::filter_disconnect [debug/optimizer] removing declaration for unused function spicy_rt::filter_forward diff --git a/tests/Baseline/spicy.optimization.unused-types/noopt.hlt b/tests/Baseline/spicy.optimization.unused-types/noopt.hlt index ca0226cbd..3f2b0d3dd 100644 --- a/tests/Baseline/spicy.optimization.unused-types/noopt.hlt +++ b/tests/Baseline/spicy.optimization.unused-types/noopt.hlt @@ -109,7 +109,7 @@ type Priv3 = struct { method tuple, int<64>, iterator, optional> __parse_foo_Priv3_stage2(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error); } &on-heap; type Priv4 = struct { - value_ref anon &optional &no-emit; + value_ref anon &optional &anonymous &no-emit; value_ref x &optional; optional> __begin &internal &needed-by-feature="uses_random_access"; optional> __position &internal &needed-by-feature="uses_random_access"; @@ -190,7 +190,7 @@ type Priv6 = struct { method tuple, int<64>, iterator, optional> __parse_foo_Priv6_stage2(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error); } &on-heap; public type Pub3 = struct { - value_ref anon_2 &optional &no-emit; + value_ref anon_2 &optional &anonymous &no-emit; value_ref x &optional; optional> __begin &internal &needed-by-feature="uses_random_access"; optional> __position &internal &needed-by-feature="uses_random_access"; diff --git a/tests/Baseline/spicy.optimization.unused-types/opt.hlt b/tests/Baseline/spicy.optimization.unused-types/opt.hlt index 022c706f4..6ff14e0aa 100644 --- a/tests/Baseline/spicy.optimization.unused-types/opt.hlt +++ b/tests/Baseline/spicy.optimization.unused-types/opt.hlt @@ -24,7 +24,7 @@ type Priv6 = struct { method tuple, int<64>, iterator, optional> __parse_foo_Priv6_stage2(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error); } &on-heap; public type Pub3 = struct { - value_ref anon_2 &optional &no-emit; + value_ref anon_2 &optional &anonymous &no-emit; value_ref x &optional; spicy_rt::Parser __parser &static &internal &needed-by-feature="supports_filters" &always-emit; optional __error &always-emit &internal; diff --git a/tests/Baseline/spicy.tools.spicy-dump-bitfields/output-json b/tests/Baseline/spicy.tools.spicy-dump-bitfields/output-json new file mode 100644 index 000000000..9d04176be --- /dev/null +++ b/tests/Baseline/spicy.tools.spicy-dump-bitfields/output-json @@ -0,0 +1,2 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +{"__offsets":{"a1":{"end":2,"start":1},"a2":{"end":2,"start":1},"b":{"end":3,"start":2},"x":{"end":1,"start":0}},"a1":2,"a2":2,"b":{"b1":3,"b2":3},"x":1} diff --git a/tests/Baseline/spicy.tools.spicy-dump-bitfields/output-text b/tests/Baseline/spicy.tools.spicy-dump-bitfields/output-text new file mode 100644 index 000000000..12a841c2e --- /dev/null +++ b/tests/Baseline/spicy.tools.spicy-dump-bitfields/output-text @@ -0,0 +1,10 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +Test::X { + x: 1 [0, 1] + a1: 2 [1, 2] + a2: 2 [1, 2] + b: { + b1: 3 + b2: 3 + } [2, 3] +} diff --git a/tests/Baseline/spicy.tools.spicy-dump/output-json b/tests/Baseline/spicy.tools.spicy-dump/output-json index 309466ae7..b12f379aa 100644 --- a/tests/Baseline/spicy.tools.spicy-dump/output-json +++ b/tests/Baseline/spicy.tools.spicy-dump/output-json @@ -1,2 +1,2 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -{"__offsets":[],"ad":"1.2.3.4","an":"","bi":"","bo":true,"by":"123","en":"B","i16":-16,"i32":-32,"i64":-64,"i8":-8,"in_":42.0,"ma":[[1,"A"],[2,"B"]],"mai":[1,"A"],"op1":null,"op2":"optional","po":{"port":42,"protocol":"TCP"},"re":3.14,"rx":"/abc/","se":["A","B","C"],"sei":"A","st":"ABC","sti":"","str":"string","stv":"ABC","ti":1.0,"tu":[1,"2",true],"u":{"__offsets":[],"b":100},"ui16":16,"ui32":32,"ui64":64,"ui8":8,"ve":["a","b","c"],"vei":"a"} +{"__offsets":{},"ad":"1.2.3.4","an":"","bf":{"a":3,"b":3,"c":3},"bi":"","bo":true,"by":"123","en":"B","i16":-16,"i32":-32,"i64":-64,"i8":-8,"in_":42.0,"ma":[[1,"A"],[2,"B"]],"mai":[1,"A"],"op1":null,"op2":"optional","po":{"port":42,"protocol":"TCP"},"re":3.14,"rx":"/abc/","se":["A","B","C"],"sei":"A","st":"ABC","sti":"","str":"string","stv":"ABC","ti":1.0,"tu":[1,"2",true],"u":{"__offsets":{},"b":100},"ui16":16,"ui32":32,"ui64":64,"ui8":8,"ve":["a","b","c"],"vei":"a"} diff --git a/tests/Baseline/spicy.tools.spicy-dump/output-json2 b/tests/Baseline/spicy.tools.spicy-dump/output-json2 index f04df8bff..fccd577e9 100644 --- a/tests/Baseline/spicy.tools.spicy-dump/output-json2 +++ b/tests/Baseline/spicy.tools.spicy-dump/output-json2 @@ -1,2 +1,2 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -{"__offsets":[{"end":3,"start":0}],"f":{"__offsets":[{"end":2,"start":0},{"end":3,"start":2}],"x":258,"y":3}} +{"__offsets":{"f":{"end":3,"start":0}},"f":{"__offsets":{"x":{"end":2,"start":0},"y":{"end":3,"start":2}},"x":258,"y":3}} diff --git a/tests/Baseline/spicy.tools.spicy-dump/output-text b/tests/Baseline/spicy.tools.spicy-dump/output-text index 7f4106075..95f3edee2 100644 --- a/tests/Baseline/spicy.tools.spicy-dump/output-text +++ b/tests/Baseline/spicy.tools.spicy-dump/output-text @@ -5,6 +5,11 @@ Test::TestTypes { bo: True by: 123 bi: + bf: { + a: 3 + b: 3 + c: 3 + } en: B in_: 42.000000s ma: {1: A, 2: B} diff --git a/tests/Baseline/spicy.types.bitfield.anonymous-fail-2/output b/tests/Baseline/spicy.types.bitfield.anonymous-fail-2/output new file mode 100644 index 000000000..d11863527 --- /dev/null +++ b/tests/Baseline/spicy.types.bitfield.anonymous-fail-2/output @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[error] <...>/anonymous-fail.spicy:4:12-13:2: bitfield item name 'x' appears in multiple anonymous bitfields +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.bitfield.anonymous-fail/output b/tests/Baseline/spicy.types.bitfield.anonymous-fail/output new file mode 100644 index 000000000..8606d939d --- /dev/null +++ b/tests/Baseline/spicy.types.bitfield.anonymous-fail/output @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[error] <...>/anonymous-fail.spicy:7:12-12:2: bitfield item 'x' shadows unit field +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.bitfield.anonymous-field/output b/tests/Baseline/spicy.types.bitfield.anonymous-field/output new file mode 100644 index 000000000..2636cf834 --- /dev/null +++ b/tests/Baseline/spicy.types.bitfield.anonymous-field/output @@ -0,0 +1,6 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +15, 15 +Test::Foo { + x: 15 + y: 15 +} diff --git a/tests/Baseline/spicy.types.bitfield.parse-and-convert/output b/tests/Baseline/spicy.types.bitfield.parse-and-convert/output new file mode 100644 index 000000000..8ffd55c1b --- /dev/null +++ b/tests/Baseline/spicy.types.bitfield.parse-and-convert/output @@ -0,0 +1,2 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[$f=3] diff --git a/tests/Baseline/spicy.types.bitfield.parse-bitorder/output b/tests/Baseline/spicy.types.bitfield.parse-bitorder/output index 832fb7868..8d60f5303 100644 --- a/tests/Baseline/spicy.types.bitfield.parse-bitorder/output +++ b/tests/Baseline/spicy.types.bitfield.parse-bitorder/output @@ -2,3 +2,4 @@ 0, 2 1, 0 1, 0 +0, 2 diff --git a/tests/Baseline/spicy.types.bitfield.typedef/output b/tests/Baseline/spicy.types.bitfield.typedef/output new file mode 100644 index 000000000..bc3f17a9a --- /dev/null +++ b/tests/Baseline/spicy.types.bitfield.typedef/output @@ -0,0 +1,6 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +foo::Y { + x: { + a: 31 + } +} diff --git a/tests/Baseline/spicy.types.function.cxxname-normalization/output b/tests/Baseline/spicy.types.function.cxxname-normalization/output index b769095fe..9d3e5dedf 100644 --- a/tests/Baseline/spicy.types.function.cxxname-normalization/output +++ b/tests/Baseline/spicy.types.function.cxxname-normalization/output @@ -17,20 +17,19 @@ [debug/normalizer] &cxxname="spicy::rt::detail::createContext" -> &cxxname="::spicy::rt::detail::createContext" (<...>/spicy_rt.hlt:53:64) [debug/normalizer] &cxxname="spicy::rt::detail::setContext" -> &cxxname="::spicy::rt::detail::setContext" (<...>/spicy_rt.hlt:54:88) [debug/normalizer] &cxxname="spicy::rt::Parser" -> &cxxname="::spicy::rt::Parser" (<...>/spicy_rt.hlt:69:3) -[debug/normalizer] &cxxname="spicy::rt::Direction" -> &cxxname="::spicy::rt::Direction" (<...>/spicy_rt.hlt:71:62) -[debug/normalizer] &cxxname="spicy::rt::detail::registerParser" -> &cxxname="::spicy::rt::detail::registerParser" (<...>/spicy_rt.hlt:76:96) -[debug/normalizer] &cxxname="spicy::rt::detail::printParserState" -> &cxxname="::spicy::rt::detail::printParserState" (<...>/spicy_rt.hlt:77:214) -[debug/normalizer] &cxxname="spicy::rt::detail::waitForInputOrEod" -> &cxxname="::spicy::rt::detail::waitForInputOrEod" (<...>/spicy_rt.hlt:79:122) -[debug/normalizer] &cxxname="spicy::rt::detail::waitForInputOrEod" -> &cxxname="::spicy::rt::detail::waitForInputOrEod" (<...>/spicy_rt.hlt:80:134) -[debug/normalizer] &cxxname="spicy::rt::detail::waitForInput" -> &cxxname="::spicy::rt::detail::waitForInput" (<...>/spicy_rt.hlt:81:152) -[debug/normalizer] &cxxname="spicy::rt::detail::waitForInput" -> &cxxname="::spicy::rt::detail::waitForInput" (<...>/spicy_rt.hlt:82:158) -[debug/normalizer] &cxxname="spicy::rt::detail::waitForEod" -> &cxxname="::spicy::rt::detail::waitForEod" (<...>/spicy_rt.hlt:83:115) -[debug/normalizer] &cxxname="spicy::rt::detail::atEod" -> &cxxname="::spicy::rt::detail::atEod" (<...>/spicy_rt.hlt:84:110) -[debug/normalizer] &cxxname="spicy::rt::detail::unitFind" -> &cxxname="::spicy::rt::detail::unitFind" (<...>/spicy_rt.hlt:86:164) -[debug/normalizer] &cxxname="spicy::rt::detail::backtrack" -> &cxxname="::spicy::rt::detail::backtrack" (<...>/spicy_rt.hlt:88:33) -[debug/normalizer] &cxxname="hilti::rt::integer::BitOrder" -> &cxxname="::hilti::rt::integer::BitOrder" (<...>/spicy_rt.hlt:90:44) -[debug/normalizer] &cxxname="hilti::rt::integer::bits" -> &cxxname="::hilti::rt::integer::bits" (<...>/spicy_rt.hlt:92:94) -[debug/normalizer] &cxxname="spicy::rt::ParsedUnit::initialize" -> &cxxname="::spicy::rt::ParsedUnit::initialize" (<...>/spicy_rt.hlt:94:89) +[debug/normalizer] &cxxname="hilti::rt::integer::BitOrder" -> &cxxname="::hilti::rt::integer::BitOrder" (<...>/spicy_rt.hlt:71:44) +[debug/normalizer] &cxxname="spicy::rt::Direction" -> &cxxname="::spicy::rt::Direction" (<...>/spicy_rt.hlt:72:62) +[debug/normalizer] &cxxname="spicy::rt::detail::registerParser" -> &cxxname="::spicy::rt::detail::registerParser" (<...>/spicy_rt.hlt:77:96) +[debug/normalizer] &cxxname="spicy::rt::detail::printParserState" -> &cxxname="::spicy::rt::detail::printParserState" (<...>/spicy_rt.hlt:78:214) +[debug/normalizer] &cxxname="spicy::rt::detail::waitForInputOrEod" -> &cxxname="::spicy::rt::detail::waitForInputOrEod" (<...>/spicy_rt.hlt:80:122) +[debug/normalizer] &cxxname="spicy::rt::detail::waitForInputOrEod" -> &cxxname="::spicy::rt::detail::waitForInputOrEod" (<...>/spicy_rt.hlt:81:134) +[debug/normalizer] &cxxname="spicy::rt::detail::waitForInput" -> &cxxname="::spicy::rt::detail::waitForInput" (<...>/spicy_rt.hlt:82:152) +[debug/normalizer] &cxxname="spicy::rt::detail::waitForInput" -> &cxxname="::spicy::rt::detail::waitForInput" (<...>/spicy_rt.hlt:83:158) +[debug/normalizer] &cxxname="spicy::rt::detail::waitForEod" -> &cxxname="::spicy::rt::detail::waitForEod" (<...>/spicy_rt.hlt:84:115) +[debug/normalizer] &cxxname="spicy::rt::detail::atEod" -> &cxxname="::spicy::rt::detail::atEod" (<...>/spicy_rt.hlt:85:110) +[debug/normalizer] &cxxname="spicy::rt::detail::unitFind" -> &cxxname="::spicy::rt::detail::unitFind" (<...>/spicy_rt.hlt:87:164) +[debug/normalizer] &cxxname="spicy::rt::detail::backtrack" -> &cxxname="::spicy::rt::detail::backtrack" (<...>/spicy_rt.hlt:89:33) +[debug/normalizer] &cxxname="spicy::rt::ParsedUnit::initialize" -> &cxxname="::spicy::rt::ParsedUnit::initialize" (<...>/spicy_rt.hlt:91:89) [debug/normalizer] &cxxname="hilti::rt::integer::BitOrder" -> &cxxname="::hilti::rt::integer::BitOrder" (<...>/hilti.hlt:5:44) [debug/normalizer] &cxxname="hilti::rt::ByteOrder" -> &cxxname="::hilti::rt::ByteOrder" (<...>/hilti.hlt:6:61) [debug/normalizer] &cxxname="hilti::rt::bytes::Side" -> &cxxname="::hilti::rt::bytes::Side" (<...>/hilti.hlt:7:47) diff --git a/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output b/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output index 2e4450c7a..9584d6a88 100644 --- a/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output +++ b/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output @@ -97,6 +97,10 @@ [debug/ast-declarations] - Field "description" (spicy_rt::Parser::description) [debug/ast-declarations] - Field "mime_types" (spicy_rt::Parser::mime_types) [debug/ast-declarations] - Field "ports" (spicy_rt::Parser::ports) +[debug/ast-declarations] - Type "BitOrder" (spicy_rt::BitOrder) +[debug/ast-declarations] - Constant "LSB0" (spicy_rt::BitOrder::LSB0) +[debug/ast-declarations] - Constant "MSB0" (spicy_rt::BitOrder::MSB0) +[debug/ast-declarations] - Constant "Undef" (spicy_rt::BitOrder::Undef) [debug/ast-declarations] - Type "Direction" (spicy_rt::Direction) [debug/ast-declarations] - Constant "Originator" (spicy_rt::Direction::Originator) [debug/ast-declarations] - Constant "Responder" (spicy_rt::Direction::Responder) @@ -155,15 +159,6 @@ [debug/ast-declarations] - Parameter "needle" (spicy_rt::unit_find::needle) [debug/ast-declarations] - Parameter "dir" (spicy_rt::unit_find::dir) [debug/ast-declarations] - Function "backtrack" (spicy_rt::backtrack) -[debug/ast-declarations] - Type "BitOrder" (spicy_rt::BitOrder) -[debug/ast-declarations] - Constant "LSB0" (spicy_rt::BitOrder::LSB0) -[debug/ast-declarations] - Constant "MSB0" (spicy_rt::BitOrder::MSB0) -[debug/ast-declarations] - Constant "Undef" (spicy_rt::BitOrder::Undef) -[debug/ast-declarations] - Function "extractBits" (spicy_rt::extractBits) -[debug/ast-declarations] - Parameter "v" (spicy_rt::extractBits::v) -[debug/ast-declarations] - Parameter "lower" (spicy_rt::extractBits::lower) -[debug/ast-declarations] - Parameter "upper" (spicy_rt::extractBits::upper) -[debug/ast-declarations] - Parameter "order" (spicy_rt::extractBits::order) [debug/ast-declarations] - Function "initializeParsedUnit" (spicy_rt::initializeParsedUnit) [debug/ast-declarations] - Parameter "punit" (spicy_rt::initializeParsedUnit::punit) [debug/ast-declarations] - Parameter "unit" (spicy_rt::initializeParsedUnit::unit) @@ -770,6 +765,10 @@ [debug/ast-declarations] - Field "description" (spicy_rt::Parser::description) [debug/ast-declarations] - Field "mime_types" (spicy_rt::Parser::mime_types) [debug/ast-declarations] - Field "ports" (spicy_rt::Parser::ports) +[debug/ast-declarations] - Type "BitOrder" (spicy_rt::BitOrder) +[debug/ast-declarations] - Constant "LSB0" (spicy_rt::BitOrder::LSB0) +[debug/ast-declarations] - Constant "MSB0" (spicy_rt::BitOrder::MSB0) +[debug/ast-declarations] - Constant "Undef" (spicy_rt::BitOrder::Undef) [debug/ast-declarations] - Type "Direction" (spicy_rt::Direction) [debug/ast-declarations] - Constant "Originator" (spicy_rt::Direction::Originator) [debug/ast-declarations] - Constant "Responder" (spicy_rt::Direction::Responder) @@ -828,15 +827,6 @@ [debug/ast-declarations] - Parameter "needle" (spicy_rt::unit_find::needle) [debug/ast-declarations] - Parameter "dir" (spicy_rt::unit_find::dir) [debug/ast-declarations] - Function "backtrack" (spicy_rt::backtrack) -[debug/ast-declarations] - Type "BitOrder" (spicy_rt::BitOrder) -[debug/ast-declarations] - Constant "LSB0" (spicy_rt::BitOrder::LSB0) -[debug/ast-declarations] - Constant "MSB0" (spicy_rt::BitOrder::MSB0) -[debug/ast-declarations] - Constant "Undef" (spicy_rt::BitOrder::Undef) -[debug/ast-declarations] - Function "extractBits" (spicy_rt::extractBits) -[debug/ast-declarations] - Parameter "v" (spicy_rt::extractBits::v) -[debug/ast-declarations] - Parameter "lower" (spicy_rt::extractBits::lower) -[debug/ast-declarations] - Parameter "upper" (spicy_rt::extractBits::upper) -[debug/ast-declarations] - Parameter "order" (spicy_rt::extractBits::order) [debug/ast-declarations] - Function "initializeParsedUnit" (spicy_rt::initializeParsedUnit) [debug/ast-declarations] - Parameter "punit" (spicy_rt::initializeParsedUnit::punit) [debug/ast-declarations] - Parameter "unit" (spicy_rt::initializeParsedUnit::unit) diff --git a/tests/Baseline/spicy.types.unit.canonical-ids/output b/tests/Baseline/spicy.types.unit.canonical-ids/output index a50882464..b84b19bb2 100644 --- a/tests/Baseline/spicy.types.unit.canonical-ids/output +++ b/tests/Baseline/spicy.types.unit.canonical-ids/output @@ -94,6 +94,10 @@ [debug/ast-declarations] - Field "description" (spicy_rt::Parser::description) [debug/ast-declarations] - Field "mime_types" (spicy_rt::Parser::mime_types) [debug/ast-declarations] - Field "ports" (spicy_rt::Parser::ports) +[debug/ast-declarations] - Type "BitOrder" (spicy_rt::BitOrder) +[debug/ast-declarations] - Constant "LSB0" (spicy_rt::BitOrder::LSB0) +[debug/ast-declarations] - Constant "MSB0" (spicy_rt::BitOrder::MSB0) +[debug/ast-declarations] - Constant "Undef" (spicy_rt::BitOrder::Undef) [debug/ast-declarations] - Type "Direction" (spicy_rt::Direction) [debug/ast-declarations] - Constant "Originator" (spicy_rt::Direction::Originator) [debug/ast-declarations] - Constant "Responder" (spicy_rt::Direction::Responder) @@ -152,15 +156,6 @@ [debug/ast-declarations] - Parameter "needle" (spicy_rt::unit_find::needle) [debug/ast-declarations] - Parameter "dir" (spicy_rt::unit_find::dir) [debug/ast-declarations] - Function "backtrack" (spicy_rt::backtrack) -[debug/ast-declarations] - Type "BitOrder" (spicy_rt::BitOrder) -[debug/ast-declarations] - Constant "LSB0" (spicy_rt::BitOrder::LSB0) -[debug/ast-declarations] - Constant "MSB0" (spicy_rt::BitOrder::MSB0) -[debug/ast-declarations] - Constant "Undef" (spicy_rt::BitOrder::Undef) -[debug/ast-declarations] - Function "extractBits" (spicy_rt::extractBits) -[debug/ast-declarations] - Parameter "v" (spicy_rt::extractBits::v) -[debug/ast-declarations] - Parameter "lower" (spicy_rt::extractBits::lower) -[debug/ast-declarations] - Parameter "upper" (spicy_rt::extractBits::upper) -[debug/ast-declarations] - Parameter "order" (spicy_rt::extractBits::order) [debug/ast-declarations] - Function "initializeParsedUnit" (spicy_rt::initializeParsedUnit) [debug/ast-declarations] - Parameter "punit" (spicy_rt::initializeParsedUnit::punit) [debug/ast-declarations] - Parameter "unit" (spicy_rt::initializeParsedUnit::unit) @@ -1116,6 +1111,10 @@ [debug/ast-declarations] - Field "description" (spicy_rt::Parser::description) [debug/ast-declarations] - Field "mime_types" (spicy_rt::Parser::mime_types) [debug/ast-declarations] - Field "ports" (spicy_rt::Parser::ports) +[debug/ast-declarations] - Type "BitOrder" (spicy_rt::BitOrder) +[debug/ast-declarations] - Constant "LSB0" (spicy_rt::BitOrder::LSB0) +[debug/ast-declarations] - Constant "MSB0" (spicy_rt::BitOrder::MSB0) +[debug/ast-declarations] - Constant "Undef" (spicy_rt::BitOrder::Undef) [debug/ast-declarations] - Type "Direction" (spicy_rt::Direction) [debug/ast-declarations] - Constant "Originator" (spicy_rt::Direction::Originator) [debug/ast-declarations] - Constant "Responder" (spicy_rt::Direction::Responder) @@ -1174,15 +1173,6 @@ [debug/ast-declarations] - Parameter "needle" (spicy_rt::unit_find::needle) [debug/ast-declarations] - Parameter "dir" (spicy_rt::unit_find::dir) [debug/ast-declarations] - Function "backtrack" (spicy_rt::backtrack) -[debug/ast-declarations] - Type "BitOrder" (spicy_rt::BitOrder) -[debug/ast-declarations] - Constant "LSB0" (spicy_rt::BitOrder::LSB0) -[debug/ast-declarations] - Constant "MSB0" (spicy_rt::BitOrder::MSB0) -[debug/ast-declarations] - Constant "Undef" (spicy_rt::BitOrder::Undef) -[debug/ast-declarations] - Function "extractBits" (spicy_rt::extractBits) -[debug/ast-declarations] - Parameter "v" (spicy_rt::extractBits::v) -[debug/ast-declarations] - Parameter "lower" (spicy_rt::extractBits::lower) -[debug/ast-declarations] - Parameter "upper" (spicy_rt::extractBits::upper) -[debug/ast-declarations] - Parameter "order" (spicy_rt::extractBits::order) [debug/ast-declarations] - Function "initializeParsedUnit" (spicy_rt::initializeParsedUnit) [debug/ast-declarations] - Parameter "punit" (spicy_rt::initializeParsedUnit::punit) [debug/ast-declarations] - Parameter "unit" (spicy_rt::initializeParsedUnit::unit) diff --git a/tests/Baseline/spicy.types.unit.skip-field/skips.txt b/tests/Baseline/spicy.types.unit.skip-field/skips.txt index adb6605e1..3f348d0fc 100644 --- a/tests/Baseline/spicy.types.unit.skip-field/skips.txt +++ b/tests/Baseline/spicy.types.unit.skip-field/skips.txt @@ -1,7 +1,7 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. - # Begin parsing production: Skip: anon_6 -> skip: anon - # Begin parsing production: Skip: anon_2_2 -> skip: anon_2 - # Begin parsing production: Skip: anon_3_2 -> skip: anon_3 - # Begin parsing production: Skip: anon_4_2 -> skip: anon_4 - # Begin parsing production: Skip: anon_5_2 -> skip: anon_5 + # Begin parsing production: Skip: _anon_6 -> skip: _anon + # Begin parsing production: Skip: _anon_2_2 -> skip: _anon_2 + # Begin parsing production: Skip: _anon_3_2 -> skip: _anon_3 + # Begin parsing production: Skip: _anon_4_2 -> skip: _anon_4 + # Begin parsing production: Skip: _anon_5_2 -> skip: _anon_5 # Begin parsing production: Skip: eod_2 -> skip: eod diff --git a/tests/hilti/codegen/type-info.hlt b/tests/hilti/codegen/type-info.hlt index 63faecd68..1044bda98 100644 --- a/tests/hilti/codegen/type-info.hlt +++ b/tests/hilti/codegen/type-info.hlt @@ -1,4 +1,4 @@ -# @TEST-EXEC: spicy-build -d -S %INPUT type-info.cc && ./a.out +# @TEST-EXEC: spicy-build -v -d -S %INPUT type-info.cc && ./a.out module Test { @@ -23,9 +23,16 @@ type A_Union = union { int<64> i }; +type Bitfield = bitfield(32) { + a: 0..4 &convert="string"; + b: 1..2 &convert=(2 * $$); + c: 3..7; +}; + public type TestTypes = struct { addr ad; any an; + Bitfield bf; bool bo; bytes by; iterator bi; @@ -79,6 +86,7 @@ global Map = map(1: "foo-1", 2: "foo-2"); global Set = set("aaa", "bbb", "ccc"); global Bytes = b"bytes"; global Stream = stream(b"stream"); +global Bitfield bf = 255; public function tuple getThreeTypeInfos() { # Test different expressions for retrieving type information. @@ -101,6 +109,7 @@ public function tuple, strong_ref, TypeInfo> ma local TestTypes x = [ $ad = 1.2.3.4, $an = "any", + $bf = bf, $bo = True, $by = b"bytes", $bi = begin(Bytes), @@ -202,6 +211,7 @@ void __check_eq(const T& x, const U& y, std::string loc) { return; std::cerr << fmt("Failed comparison: %s == %s (%s)", x, y, loc) << std::endl; + abort(); errors++; } @@ -209,7 +219,7 @@ void __check_eq(const T& x, const U& y, std::string loc) { // "TypesInit". struct VisitorTypesInit { std::set seen; - static inline const int ExpectedVisitorsSeen = 40; // all (43) minus void and function and MatchState (which comes as struct) + static inline const int ExpectedVisitorsSeen = 41; // all (44) minus void and function and MatchState (which comes as struct) // Helper for checking content of a struct of type "S". All our instances // of "S" have the same values. @@ -246,6 +256,24 @@ struct VisitorTypesInit { SEEN(); break; } + case TypeInfo::Bitfield: { + SEEN(); + const auto* x = type.tuple; + auto s = x->iterate(v); + auto i = s.begin(); + CHECK_EQ(type_info::value::auxType(i->second)->get(i->second), "string"); + CHECK_EQ(i->first.name, "a"); + ++i; + CHECK_EQ(type_info::value::auxType>(i->second)->get(i->second), 6); + CHECK_EQ(i->first.name, "b"); + ++i; + CHECK_EQ(type_info::value::auxType>(i->second)->get(i->second), 31); + CHECK_EQ(i->first.name, "c"); + ++i; + CHECK(i == s.end()); + CHECK_EQ(x->elements().size(), 3); + break; + } case TypeInfo::Bool: { SEEN(); CHECK_EQ(type.bool_->get(v), true); diff --git a/tests/hilti/types/bitfield/convert.hlt b/tests/hilti/types/bitfield/convert.hlt new file mode 100644 index 000000000..ce28f3f0e --- /dev/null +++ b/tests/hilti/types/bitfield/convert.hlt @@ -0,0 +1,17 @@ +# @TEST-EXEC: ${HILTIC} -j %INPUT >output +# @TEST-EXEC: btest-diff output + +module Test { + +import hilti; + +type X = bitfield(32) { + a: 0..4 &convert="string"; + b: 1..2 &convert=(2 * $$); +}; + +global X x = 255; +assert x.a == "string"; +assert x.b == 6; + +} diff --git a/tests/hilti/types/bitfield/ops.hlt b/tests/hilti/types/bitfield/ops.hlt new file mode 100644 index 000000000..7cb7dfd3d --- /dev/null +++ b/tests/hilti/types/bitfield/ops.hlt @@ -0,0 +1,34 @@ +# @TEST-EXEC: ${HILTIC} -j %INPUT >output +# @TEST-EXEC: btest-diff output + +module Test { + +import hilti; + +type X = bitfield(32) { + a: 0..4; + b: 1..2; +}; + +global X x0; +global X x1; +global X x2; +global X x3; + +x0 = uint64(255); +x1 = uint32(255); +x2 = uint8(255); +x3 = int64(255); + +hilti::print(x0); + +assert x0.a == 31; +assert x0.b == 3; +assert x1.a == 31; +assert x1.b == 3; +assert x2.a == 31; +assert x2.b == 3; +assert x3.a == 31; +assert x3.b == 3; + +} diff --git a/tests/hilti/types/bitfield/unpack.hlt b/tests/hilti/types/bitfield/unpack.hlt new file mode 100644 index 000000000..32036fd0b --- /dev/null +++ b/tests/hilti/types/bitfield/unpack.hlt @@ -0,0 +1,62 @@ +# @TEST-EXEC: ${HILTIC} -j %INPUT >output +# @TEST-EXEC: btest-diff output + +module Foo { + +import hilti; + +type X = bitfield(16) { + a: 0..3; + b: 4..7; + c: 8..11; + d: 12..15; +}; + +global d = b"\x01\x00\xff"; + +global bytes r; +global X bf; + +(bf, r) = *unpack(d, hilti::ByteOrder::Little); +assert bf.a == 1; +assert bf.b == 0; +assert bf.c == 0; +assert bf.d == 0; +assert r == b"\xff"; + +(bf, r) = *unpack(d, hilti::ByteOrder::Little, hilti::BitOrder::LSB0); +assert bf.a == 1; +assert bf.b == 0; +assert bf.c == 0; +assert bf.d == 0; +assert r == b"\xff"; + +(bf, r) = *unpack(d, hilti::ByteOrder::Little, hilti::BitOrder::MSB0); +assert bf.a == 0; +assert bf.b == 0; +assert bf.c == 0; +assert bf.d == 1; +assert r == b"\xff"; + +(bf, r) = *unpack(d, hilti::ByteOrder::Big); +assert bf.a == 0; +assert bf.b == 0; +assert bf.c == 1; +assert bf.d == 0; +assert r == b"\xff"; + +(bf, r) = *unpack(d, hilti::ByteOrder::Big, hilti::BitOrder::LSB0); +assert bf.a == 0; +assert bf.b == 0; +assert bf.c == 1; +assert bf.d == 0; +assert r == b"\xff"; + +(bf, r) = *unpack(d, hilti::ByteOrder::Big, hilti::BitOrder::MSB0); +assert bf.a == 0; +assert bf.b == 1; +assert bf.c == 0; +assert bf.d == 0; +assert r == b"\xff"; + +} diff --git a/tests/hilti/types/real/coercion.hlt b/tests/hilti/types/real/coercion.hlt index 1dc1a5207..a015285f6 100644 --- a/tests/hilti/types/real/coercion.hlt +++ b/tests/hilti/types/real/coercion.hlt @@ -56,7 +56,7 @@ global uint<64> uinty = cast>(really); # Can't coerce real variable to # 355/113 approximates pi as 3.141592920353982520964564... -global real piish = 355./113.; +global real piish = 355.0/113.0; # real needs ~15 decimal digits for its double-precision assert piish == 3.1415929203539823; diff --git a/tests/hilti/types/real/inf_NaN.hlt b/tests/hilti/types/real/inf_NaN.hlt index b803636f8..0bc893036 100644 --- a/tests/hilti/types/real/inf_NaN.hlt +++ b/tests/hilti/types/real/inf_NaN.hlt @@ -6,10 +6,10 @@ module Foo { import hilti; global zero = 0e0; -global real minus_zero = -0.; -global real inf = 1./0.; -global real minus_inf = -1./0.; -global real nan = 0./0.; +global real minus_zero = -0.0; +global real inf = 1.0/0.0; +global real minus_inf = -1.0/0.0; +global real nan = 0.0/0.0; assert zero == minus_zero; assert inf == 3.14159/0e-999; diff --git a/tests/hilti/types/real/series.hlt b/tests/hilti/types/real/series.hlt index c1b1a4ce7..0e7aba3fa 100644 --- a/tests/hilti/types/real/series.hlt +++ b/tests/hilti/types/real/series.hlt @@ -44,7 +44,7 @@ function void run() { d3 = d1*1e3; hilti::print(d3); - d3 = d1+1.; + d3 = d1+1.0; hilti::print(d3); return; diff --git a/tests/hilti/types/real/sub.hlt b/tests/hilti/types/real/sub.hlt index bfcae0a9a..728ce0b8b 100644 --- a/tests/hilti/types/real/sub.hlt +++ b/tests/hilti/types/real/sub.hlt @@ -6,13 +6,13 @@ module Foo { import hilti; function void run() { - local real d = 15.0 - 5.; - assert d == 10.; + local real d = 15.0 - 5.0; + assert d == 10.0; hilti::print(d); d -= 2.5; hilti::print(d); assert d == 10.0 - 2.5; - assert d == 10.-2.5; + assert d == 10.0-2.5; assert d == 10e0+-25e-1; } diff --git a/tests/spicy/tools/spicy-dump-bitfields.spicy b/tests/spicy/tools/spicy-dump-bitfields.spicy new file mode 100644 index 000000000..091b2a032 --- /dev/null +++ b/tests/spicy/tools/spicy-dump-bitfields.spicy @@ -0,0 +1,13 @@ +# @TEST-EXEC: spicyc -j -Q -o test.hlto %INPUT +# @TEST-EXEC: printf '\001\002\003' | spicy-dump -Q test.hlto >output-text +# @TEST-EXEC: btest-diff output-text +# @TEST-EXEC: printf '\001\002\003' | spicy-dump -J -Q test.hlto >output-json +# @TEST-EXEC: btest-diff output-json + +module Test; + +public type X = unit { + x: uint8; + : bitfield(8) { a1: 0..7; a2: 0..7; }; + b: bitfield(8) { b1: 0..7; b2: 0..7; }; +}; diff --git a/tests/spicy/tools/spicy-dump.spicy b/tests/spicy/tools/spicy-dump.spicy index 505c3eed8..a03c00962 100644 --- a/tests/spicy/tools/spicy-dump.spicy +++ b/tests/spicy/tools/spicy-dump.spicy @@ -16,6 +16,8 @@ type Enum = enum { A = 1, B = 2, C = 4 }; type Unit = unit { var b: int32 = 100; }; +type Bitfield = bitfield(8) { a: 1..2; b: 3..4; c: 5..6; }; + global b: bytes = b"abc"; global m: map = map(1: "A", 2: "B"); global s: set = set("A", "B", "C"); @@ -29,6 +31,7 @@ public type TestTypes = unit { var bo: bool = True; var by: bytes = b"123"; var bi: iterator = begin(b); + var bf: Bitfield = 255; var en: Enum = Enum::B; var in_: interval = interval(42.0); var ma: map = map(1: "A", 2: "B"); diff --git a/tests/spicy/types/bitfield/anonymous-fail.spicy b/tests/spicy/types/bitfield/anonymous-fail.spicy new file mode 100644 index 000000000..3977fb088 --- /dev/null +++ b/tests/spicy/types/bitfield/anonymous-fail.spicy @@ -0,0 +1,27 @@ +# @TEST-EXEC-FAIL: spicyc -c %INPUT >output 2>&1 +# @TEST-EXEC: btest-diff output +# @TEST-DOC: Check that an anonymous bitfield's field name cannot shadow other fields. + +module Test; + +type Foo = unit { + x: uint8; + : bitfield(8) { + x: 0..3; + }; +}; + +# @TEST-START-NEXT + +module Test; + +type Bar = unit { + : bitfield(8) { + x: 0..3; + }; + : bitfield(8) { + x: 0..3; + }; + + on %done { print self.x; } +}; diff --git a/tests/spicy/types/bitfield/anonymous-field.spicy b/tests/spicy/types/bitfield/anonymous-field.spicy new file mode 100644 index 000000000..d0b788c32 --- /dev/null +++ b/tests/spicy/types/bitfield/anonymous-field.spicy @@ -0,0 +1,16 @@ +# @TEST-EXEC: printf '\377' | spicy-driver %INPUT >output +# @TEST-EXEC: printf '\377' | spicy-dump %INPUT >>output +# @TEST-EXEC: btest-diff output + +module Test; + +public type Foo = unit { + : bitfield(8) { + x: 0..3; + y: 4..7; + }; + + on %done { + print self.x, self.y; + } +}; diff --git a/tests/spicy/types/bitfield/parse-and-convert.spicy b/tests/spicy/types/bitfield/parse-and-convert.spicy new file mode 100644 index 000000000..04c768aaa --- /dev/null +++ b/tests/spicy/types/bitfield/parse-and-convert.spicy @@ -0,0 +1,14 @@ +# @TEST-EXEC: printf '\377\377\377\377' | spicy-driver %INPUT >output +# @TEST-EXEC: btest-diff output + +module Mini; + +public type test = unit { + f: bitfield(32) { + x1: 0; + x2: 1..2; + x3: 3..4; + } &convert=$$.x2; + + on %done { print self; } +}; diff --git a/tests/spicy/types/bitfield/parse-bitorder.spicy b/tests/spicy/types/bitfield/parse-bitorder.spicy index 41da26c57..c255ed4b8 100644 --- a/tests/spicy/types/bitfield/parse-bitorder.spicy +++ b/tests/spicy/types/bitfield/parse-bitorder.spicy @@ -1,10 +1,15 @@ -# @TEST-EXEC: printf '\100\001\100\001\100\001' | spicy-driver %INPUT >output +# @TEST-EXEC: printf '\100\001\100\001\100\001\100\001' | spicy-driver %INPUT >output # @TEST-EXEC: btest-diff output module Mini; import spicy; +type F4 = bitfield(16) { + x1: 0; + x2: 1..2; +} &bit-order=spicy::BitOrder::MSB0; + public type test = unit { f1: bitfield(16) { x1: 0; @@ -21,9 +26,12 @@ public type test = unit { x2: 1..2; }; + f4: F4; + on %done { print self.f1.x1, self.f1.x2; print self.f2.x1, self.f2.x2; print self.f3.x1, self.f3.x2; + print self.f4.x1, self.f4.x2; } }; diff --git a/tests/spicy/types/bitfield/typedef.spicy b/tests/spicy/types/bitfield/typedef.spicy new file mode 100644 index 000000000..60e242ccd --- /dev/null +++ b/tests/spicy/types/bitfield/typedef.spicy @@ -0,0 +1,12 @@ +# @TEST-EXEC: printf '\377' | spicy-dump %INPUT >output +# @TEST-EXEC: btest-diff output + +module foo; + +type X = bitfield(8) { + a: 0..4; +}; + +public type Y = unit { + x: X; +};