diff --git a/CHANGES b/CHANGES index 5c4ab2148..0d012bf01 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ + +1.3.0-dev.101 | 2021-09-21 12:10:25 +0200 + + * Major internal overhaul of the AST infrastructure. (Robin Sommer, + Corelight) + 1.3.0-dev.100 | 2021-09-17 13:18:50 +0200 * Fix lints in rst files. (Benjamin Bannier, Corelight) diff --git a/VERSION b/VERSION index 0604c8638..70a12094d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.0-dev.100 +1.3.0-dev.101 diff --git a/doc/autogen/spicy-types.spicy b/doc/autogen/spicy-types.spicy index 78075bd2a..9f247821d 100644 --- a/doc/autogen/spicy-types.spicy +++ b/doc/autogen/spicy-types.spicy @@ -6,7 +6,7 @@ Specifies an address' IP family. .. spicy-code:: - type AddressFamily = { + type AddressFamily = enum { IPv4, # IP4 address IPv6 # IPv6 address }; @@ -25,7 +25,7 @@ Specifies the bit order for individual bit ranges inside a bitfield. .. spicy-code:: - type BitOrder = { + type BitOrder = enum { LSB0, # bits are interpreted as lowest-significant-bit coming first MSB0 # bits are interpreted as most-significant-bit coming first }; @@ -38,13 +38,26 @@ Specifies byte order for data operations. .. spicy-code:: - type ByteOrder = { + type ByteOrder = enum { Little, # data is in little-endian byte order Big, # data is in big-endian byte order Network, # data is in network byte order (same a big endian) Host # data is in byte order of the host we are executing on }; +.. _spicy_charset: + +.. rubric:: ``spicy::Charset`` + +Specifies the character set for bytes encoding/decoding. + +.. spicy-code:: + + type Charset = enum { + ASCII, + UTF8 + }; + .. _spicy_matchstate: .. rubric:: ``spicy::MatchState`` @@ -59,7 +72,7 @@ Specifies a transport-layer protocol. .. spicy-code:: - type Protocol = { + type Protocol = enum { TCP, UDP, ICMP @@ -73,7 +86,7 @@ Specifies the type of a real value. .. spicy-code:: - type RealType = { + type RealType = enum { IEEE754_Single, # single precision in IEEE754 format IEEE754_Double # double precision in IEEE754 format }; @@ -86,7 +99,7 @@ Specifies the policy for a sink's reassembler when encountering overlapping data .. spicy-code:: - type ReassemblerPolicy = { + type ReassemblerPolicy = enum { First # take the original data & discard the new data }; @@ -98,7 +111,7 @@ Specifies a side an operation should operate on. .. spicy-code:: - type Side = { + type Side = enum { Left, # operate on left side Right, # operate on right side Both # operate on both sides @@ -112,7 +125,7 @@ Specifies direction of a search. .. spicy-code:: - type Direction = { + type Direction = enum { Forward, # search forward Backward, # search backward }; diff --git a/doc/autogen/types/bytes.rst b/doc/autogen/types/bytes.rst index 2e4b4eb42..9abe97a31 100644 --- a/doc/autogen/types/bytes.rst +++ b/doc/autogen/types/bytes.rst @@ -164,7 +164,7 @@ Returns the number of bytes the value contains. -.. spicy:operator:: bytes::Sum const~bytes t:bytes op:+ t:bytes +.. spicy:operator:: bytes::Sum bytes t:bytes op:+ t:bytes Returns the concatenation of two bytes values. diff --git a/doc/autogen/types/sink.rst b/doc/autogen/types/sink.rst index 4da63899c..90ecdb919 100644 --- a/doc/autogen/types/sink.rst +++ b/doc/autogen/types/sink.rst @@ -32,7 +32,7 @@ sink. If data has already been written when a filter is added, an error is triggered. -.. spicy:method:: sink::connect_mime_type sink connect_mime_type False void (inout mt: bytes) +.. spicy:method:: sink::connect_mime_type sink connect_mime_type False void (mt: bytes) Connects parsing units to a sink for all parsers that support a given MIME type. All subsequent write operations to the sink will pass their diff --git a/doc/autogen/types/tuple.rst b/doc/autogen/types/tuple.rst index ab5657d6a..fa31f1d31 100644 --- a/doc/autogen/types/tuple.rst +++ b/doc/autogen/types/tuple.rst @@ -1,5 +1,9 @@ .. rubric:: Operators +.. spicy:operator:: tuple::CustomAssign t:(x,~...,~y) = t: + + Assigns element-wise to the left-hand-side tuple + .. spicy:operator:: tuple::Equal bool t:tuple op:== t:tuple Compares two tuples element-wise. diff --git a/doc/autogen/types/unit.rst b/doc/autogen/types/unit.rst index ac7fe5165..7e872b8fc 100644 --- a/doc/autogen/types/unit.rst +++ b/doc/autogen/types/unit.rst @@ -91,3 +91,29 @@ Usage of this method requires the unit to be declared with the ``%random-access`` property. +.. rubric:: Operators + +.. spicy:operator:: unit::HasMember bool t:unit op:?. t: + + Returns true if the unit's field has a value assigned (not counting + any ``&default``). + +.. spicy:operator:: unit::Member t:unit op:. t: + + Retrieves the value of a unit's field. If the field does not have a + value assigned, it returns its ``&default`` expression if that has + been defined; otherwise it triggers an exception. + +.. spicy:operator:: unit::TryMember t:unit op:.? t: + + Retrieves the value of a unit's field. If the field does not have a + value assigned, it returns its ``&default`` expression if that has + been defined; otherwise it signals a special non-error exception to + the host application (which will normally still lead to aborting + execution, similar to the standard dereference operator, unless the + host application specifically handles this exception differently). + +.. spicy:operator:: unit::Unset void unset t:unit. + + Clears an optional field. + diff --git a/doc/programming/language/types.rst b/doc/programming/language/types.rst index 310e1222a..b8cc233f8 100644 --- a/doc/programming/language/types.rst +++ b/doc/programming/language/types.rst @@ -503,36 +503,6 @@ Unit .. include:: /autogen/types/unit.rst -.. These are copied and adapted from the corresponding struct -.. operators. We have to hardcode them as we currently have no -.. way to pull the struct operators over into the unit type -.. automatically. - -.. rubric:: Operators - -.. spicy:operator:: unit::HasMember bool t:unit op:?. t: - - Returns true if the unit's field has a value assigned (not counting - any ``&default``). - -.. spicy:operator:: unit::Member t:unit op:. t: - - Retrieves the value of a unit's field. If the field does not yet have - a value assigned, it returns its ``&default`` expression if that has - been defined; otherwise it triggers an exception. - -.. spicy:operator:: unit::TryMember t:unit op:.? t: - - Retrieves the value of a unit's field. If the field does not yet have - a value assigned, it returns its ``&default`` expression if that has - been defined. Otherwise it triggers an exception, unless used in a - context that specifically allows for that situation (such as, - inside the Zeek plugin's `evt` files). - -.. spicy:operator:: unit::Unset void unset t:unit. - - Resets a field back to its original uninitialized state. - .. _type_vector: Vector diff --git a/doc/programming/parsing.rst b/doc/programming/parsing.rst index 6dd6f17da..0a2c32536 100644 --- a/doc/programming/parsing.rst +++ b/doc/programming/parsing.rst @@ -491,9 +491,10 @@ The most commonly used hooks are: ``on { ... }`` (field hook) Executes just after the given unit field has been parsed. The - parsed value is accessible through the ``$$`` identifier. It will - also have been assigned to the field already, potentially with any - relevant type conversion applied (see :ref:`attribute_convert`). + parsed value is accessible through the ``$$``, potentially with + any relevant type conversion applied (see + :ref:`attribute_convert`). The same will also have been assigned + to the field already. .. _foreach: diff --git a/doc/scripts/autogen-spicy-lib b/doc/scripts/autogen-spicy-lib index f4fd9a705..83d412142 100755 --- a/doc/scripts/autogen-spicy-lib +++ b/doc/scripts/autogen-spicy-lib @@ -25,7 +25,19 @@ awk -v "target=$1" -v "ns=$2" ' printf(".. rubric:: ``%s::%s``\n\n", ns, label); printf("%s\n\n", comment); printf(".. spicy-code::\n\n"); - printf(" type %s = {\n", $3); + printf(" type %s = enum {\n", $3); + } + + comment = ""; + next; + } + + # Struct + /public type .* = struct { *$/ { + if ( target == "types" ) { + printf(".. _spicy_%s:\n\n", tolower($3)); + printf(".. rubric:: ``%s::%s``\n\n", ns, $3); + printf("%s\n\n", comment); } comment = ""; diff --git a/doc/scripts/spicy-doc-to-rst b/doc/scripts/spicy-doc-to-rst index 218373d95..937dcb852 100755 --- a/doc/scripts/spicy-doc-to-rst +++ b/doc/scripts/spicy-doc-to-rst @@ -76,6 +76,11 @@ Operators = { TypedType.sub("\\1", op.operands[1].rst( in_operator=True, markup=False)), op.operands[0].rst(in_operator=True, markup=False)), + "CustomAssign": lambda op: "{} = {}".format( + op.operands[0].rst(in_operator=True), + op.operands[1].rst(in_operator=True) + ), + "Delete": lambda op: "delete {}[{}]".format( op.operands[0].rst(in_operator=True), op.operands[1].rst(in_operator=True, markup=False) @@ -137,8 +142,8 @@ NamespaceMappings = { } TypeMappings = { - "hilti::rt::regexp::MatchState": "spicy::MatchState", - "hilti::rt::bytes::Side": "spicy::Side", + "::hilti::rt::regexp::MatchState": "spicy::MatchState", + "::hilti::rt::bytes::Side": "spicy::Side", } LibraryType = re.compile(r'__library_type\("(.*)"\)') diff --git a/hilti/lib/hilti.hlt b/hilti/lib/hilti.hlt index 9c6be43c7..d8a9d27b6 100644 --- a/hilti/lib/hilti.hlt +++ b/hilti/lib/hilti.hlt @@ -1,38 +1,38 @@ module hilti { -public type BitOrder = enum { LSB0, MSB0 } &cxxname="hilti::rt::integer::BitOrder"; -public type ByteOrder = enum { Little, Big, Network, Host } &cxxname="hilti::rt::ByteOrder"; -public type Side = enum { Left, Right, Both } &cxxname="hilti::rt::bytes::Side"; -public type AddressFamily = enum { IPv4, IPv6 } &cxxname="hilti::rt::AddressFamily"; -public type RealType = enum { IEEE754_Single, IEEE754_Double } &cxxname="hilti::rt::real::Type"; -public type Protocol = enum { TCP, UDP, ICMP } &cxxname="hilti::rt::Protocol"; -public type Charset = enum { ASCII, UTF8} &cxxname="hilti::rt::bytes::Charset"; +public type BitOrder = enum { LSB0, MSB0 } &cxxname="::hilti::rt::integer::BitOrder"; +public type ByteOrder = enum { Little, Big, Network, Host } &cxxname="::hilti::rt::ByteOrder"; +public type Side = enum { Left, Right, Both } &cxxname="::hilti::rt::bytes::Side"; +public type AddressFamily = enum { IPv4, IPv6 } &cxxname="::hilti::rt::AddressFamily"; +public type RealType = enum { IEEE754_Single, IEEE754_Double } &cxxname="::hilti::rt::real::Type"; +public type Protocol = enum { TCP, UDP, ICMP } &cxxname="::hilti::rt::Protocol"; +public type Charset = enum { ASCII, UTF8 } &cxxname="::hilti::rt::bytes::Charset"; public type Captures = vector; public type MatchState = struct { method Captures captures(stream data); -} &cxxname="hilti::rt::regexp::MatchState"; +} &cxxname="::hilti::rt::regexp::MatchState"; -declare public void print(any obj, bool newline = True) &cxxname="hilti::rt::print" &have_prototype; -declare public void printValues(tuple<*> t, bool newline = True) &cxxname="hilti::rt::printValues" &have_prototype; +declare public void print(any obj, bool newline = True) &cxxname="::hilti::rt::print" &have_prototype; +declare public void printValues(tuple<*> t, bool newline = True) &cxxname="::hilti::rt::printValues" &have_prototype; declare public void debug(string dbg_stream, any obj) &cxxname="HILTI_RT_DEBUG" &have_prototype; -declare public void debugIndent(string dbg_stream) &cxxname="hilti::rt::debug::indent" &have_prototype; -declare public void debugDedent(string dbg_stream) &cxxname="hilti::rt::debug::dedent" &have_prototype; +declare public void debugIndent(string dbg_stream) &cxxname="::hilti::rt::debug::indent" &have_prototype; +declare public void debugDedent(string dbg_stream) &cxxname="::hilti::rt::debug::dedent" &have_prototype; -declare public time current_time() &cxxname="hilti::rt::time::current_time" &have_prototype; -declare public time mktime(uint<64> y, uint<64> m, uint<64> d, uint<64> H, uint<64> M, uint<64> S) &cxxname="hilti::rt::time::mktime" &have_prototype; +declare public time current_time() &cxxname="::hilti::rt::time::current_time" &have_prototype; +declare public time mktime(uint<64> y, uint<64> m, uint<64> d, uint<64> H, uint<64> M, uint<64> S) &cxxname="::hilti::rt::time::mktime" &have_prototype; -declare public void abort() &cxxname="hilti::rt::abort_with_backtrace" &have_prototype; +declare public void abort() &cxxname="::hilti::rt::abort_with_backtrace" &have_prototype; declare public string linker_scope() &cxxname="hilti::rt::linker_scope" &have_prototype; # Base type for all exceptions. -public type Exception = exception &cxxname="hilti::rt::Exception"; +public type Exception = exception &cxxname="::hilti::rt::Exception"; # Base type for all exception generated by the runtime system. Catching # this allows to continue after operations triggering runtime errors. -public type RuntimeError = exception &cxxname="hilti::rt::RuntimeError"; +public type RuntimeError = exception &cxxname="::hilti::rt::RuntimeError"; } diff --git a/hilti/runtime/include/types/reference.h b/hilti/runtime/include/types/reference.h index f4fff1e40..659695aa1 100644 --- a/hilti/runtime/include/types/reference.h +++ b/hilti/runtime/include/types/reference.h @@ -172,6 +172,13 @@ class ValueReference { */ T* operator->() { return _safeGet(); } + /** + * Implicitly converts to the contained type. + * + * @throws NullReference if the instance does not refer to a valid value + */ + operator const T&() const { return *_safeGet(); } + /** * Compares the values of two references. * diff --git a/hilti/runtime/src/init.cc b/hilti/runtime/src/init.cc index 0ff4b0a76..c8a116598 100644 --- a/hilti/runtime/src/init.cc +++ b/hilti/runtime/src/init.cc @@ -1,7 +1,5 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "hilti/rt/init.h" - #include #include @@ -11,6 +9,7 @@ #include #include #include +#include #include using namespace hilti::rt; diff --git a/hilti/runtime/src/library.cc b/hilti/runtime/src/library.cc index f533fbbba..f0d91b953 100644 --- a/hilti/runtime/src/library.cc +++ b/hilti/runtime/src/library.cc @@ -1,7 +1,5 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "hilti/rt/library.h" - #include #include @@ -10,6 +8,7 @@ #include #include #include +#include #include using namespace hilti::rt; @@ -103,7 +102,7 @@ hilti::rt::Result hilti::rt::Library::symbol(std::string_view name) const auto* symbol = ::dlsym(_handle, name.data()); - if ( auto error = ::dlerror() ) + if ( ::dlerror() ) return result::Error(fmt("symbol '%s' not found", name)); return symbol; diff --git a/hilti/runtime/src/logging.cc b/hilti/runtime/src/logging.cc index 38ea5b88a..4d7b7d68f 100644 --- a/hilti/runtime/src/logging.cc +++ b/hilti/runtime/src/logging.cc @@ -1,8 +1,7 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "hilti/rt/logging.h" - #include +#include #include using namespace hilti::rt; diff --git a/hilti/runtime/src/type-info.cc b/hilti/runtime/src/type-info.cc index c6f32bd1f..3bc06733a 100644 --- a/hilti/runtime/src/type-info.cc +++ b/hilti/runtime/src/type-info.cc @@ -1,7 +1,5 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "hilti/rt/type-info.h" - #include #include diff --git a/hilti/runtime/src/types/bytes.cc b/hilti/runtime/src/types/bytes.cc index c1fd2ad51..43ff5fbcc 100644 --- a/hilti/runtime/src/types/bytes.cc +++ b/hilti/runtime/src/types/bytes.cc @@ -1,7 +1,6 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "hilti/rt/types/bytes.h" - +#include #include #include #include diff --git a/hilti/runtime/src/types/port.cc b/hilti/runtime/src/types/port.cc index d4d5ef59c..a1aa14ed3 100644 --- a/hilti/runtime/src/types/port.cc +++ b/hilti/runtime/src/types/port.cc @@ -1,7 +1,6 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "hilti/rt/types/port.h" - +#include #include using namespace hilti::rt; diff --git a/hilti/runtime/src/types/regexp.cc b/hilti/runtime/src/types/regexp.cc index bc9b07e09..bcf05de52 100644 --- a/hilti/runtime/src/types/regexp.cc +++ b/hilti/runtime/src/types/regexp.cc @@ -3,10 +3,9 @@ // Note: We don't run clang-tidy on this file. The use of the JRX's C // interface triggers all kinds of warnings. -#include "hilti/rt/types/regexp.h" - #include +#include #include extern "C" { diff --git a/hilti/runtime/src/types/string.cc b/hilti/runtime/src/types/string.cc index 56d7bf5c3..a4e75a3a6 100644 --- a/hilti/runtime/src/types/string.cc +++ b/hilti/runtime/src/types/string.cc @@ -1,10 +1,9 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "hilti/rt/types/string.h" - #include #include +#include using namespace hilti::rt; diff --git a/hilti/toolchain/CMakeLists.txt b/hilti/toolchain/CMakeLists.txt index 723cf2033..9b61b17da 100644 --- a/hilti/toolchain/CMakeLists.txt +++ b/hilti/toolchain/CMakeLists.txt @@ -43,6 +43,7 @@ autogen_dispatchers(SOURCES_TYPE_ERASED ${AUTOGEN_H}/__dispatchers.h set(SOURCES src/ast/builder/builder.cc src/ast/builder/type.cc + src/ast/declarations/imported-module.cc src/ast/expression.cc src/ast/expressions src/ast/expressions/id.cc @@ -85,13 +86,10 @@ set(SOURCES src/compiler/plugin.cc src/compiler/unit.cc src/compiler/visitors/coercer.cc - src/compiler/visitors/apply-coercions.cc - src/compiler/visitors/coercer.cc - src/compiler/visitors/importer.cc + src/compiler/visitors/normalizer.cc src/compiler/visitors/printer.cc src/compiler/visitors/renderer.cc - src/compiler/visitors/id-resolver.cc - src/compiler/visitors/operator-resolver.cc + src/compiler/visitors/resolver.cc src/compiler/visitors/scope-builder.cc src/compiler/visitors/validator.cc src/global.cc diff --git a/hilti/toolchain/include/ast/attribute.h b/hilti/toolchain/include/ast/attribute.h index 43b80b27b..83a652851 100644 --- a/hilti/toolchain/include/ast/attribute.h +++ b/hilti/toolchain/include/ast/attribute.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -57,7 +58,7 @@ class Attribute : public NodeBase { * * @exception `std::out_of_range` if the attribute does not have an argument */ - const Node& value() const { return childs().at(0); } + const Node& value() const { return childs()[0]; } /** * Returns the attributes argument as type `T`. `T` must be either an @@ -70,44 +71,40 @@ class Attribute : public NodeBase { * @return the argument, or an error if the argument could not be interpreted as type `T` * @exception `std::out_of_range` if the attribute does not have an argument */ - template - Result valueAs() const { - if constexpr ( std::is_same::value ) { - if ( ! hasValue() ) - return result::Error(hilti::util::fmt("attribute '%s' requires an expression", _tag)); - - if ( auto e = value().tryAs() ) - return *e; + Result> valueAsExpression() const { + if ( ! hasValue() ) + return result::Error(hilti::util::fmt("attribute '%s' requires an expression", _tag)); + if ( ! value().isA() ) return result::Error(hilti::util::fmt("value for attribute '%s' must be an expression", _tag)); - } - if constexpr ( std::is_same::value ) { - if ( ! hasValue() ) - return result::Error(hilti::util::fmt("attribute '%s' requires a string", _tag)); + return {value().as()}; + } - if ( auto e = value().tryAs() ) - if ( auto s = e->ctor().tryAs() ) - return s->value(); - return result::Error(hilti::util::fmt("value for attribute '%s' must be a string", _tag)); - } + Result valueAsString() const { + if ( ! hasValue() ) + return result::Error(hilti::util::fmt("attribute '%s' requires a string", _tag)); + + if ( auto e = value().tryAs() ) + if ( auto s = e->ctor().tryAs() ) + return s->value(); - if constexpr ( std::is_same::value ) { - if ( ! hasValue() ) - return result::Error(hilti::util::fmt("attribute '%s' requires an integer", _tag)); + return result::Error(hilti::util::fmt("value for attribute '%s' must be a string", _tag)); + } - if ( auto e = value().tryAs() ) { - if ( auto s = e->ctor().tryAs() ) - return s->value(); + Result valueAsInteger() const { + if ( ! hasValue() ) + return result::Error(hilti::util::fmt("attribute '%s' requires an integer", _tag)); - if ( auto s = e->ctor().tryAs() ) - return static_cast(s->value()); - } + if ( auto e = value().tryAs() ) { + if ( auto s = e->ctor().tryAs() ) + return s->value(); - return result::Error(hilti::util::fmt("value for attribute '%s' must be an integer", _tag)); + if ( auto s = e->ctor().tryAs() ) + return static_cast(s->value()); } - logger().internalError(hilti::util::fmt("unsupported attribute value type requested (%s)", typeid(T).name())); + return result::Error(hilti::util::fmt("value for attribute '%s' must be an integer", _tag)); } /** Implements the `Node` interface. */ @@ -117,33 +114,22 @@ class Attribute : public NodeBase { if ( _tag != other._tag ) return false; - if ( auto x = valueAs() ) { - auto y = other.valueAs(); + if ( auto x = valueAsExpression() ) { + auto y = other.valueAsExpression(); return y && *x == *y; } - if ( auto x = valueAs() ) { - auto y = other.valueAs(); + else if ( auto x = valueAsString() ) { + auto y = other.valueAsString(); return y && *x == *y; } - return false; - } - - /** - * Sets or replaces an attributes's associated argument. - * - * @param the node to assign as the attribute's argument - * @return a new attribute with the value changed - */ - static Attribute setValue(const Attribute& a, Node n) { - auto x = Attribute(a); - if ( a.childs().empty() ) - x.childs().emplace_back(std::move(n)); - else - x.childs()[0] = std::move(n); + else if ( auto x = valueAsInteger() ) { + auto y = other.valueAsInteger(); + return y && *x == *y; + } - return x; + return false; } private: @@ -174,12 +160,7 @@ class AttributeSet : public NodeBase { AttributeSet(Meta m = Meta()) : NodeBase({}, std::move(m)) {} /** Returns the set's attributes. */ - const auto& attributes() const { - if ( _cache.attributes.empty() ) - _cache.attributes = childs(0, -1); - - return _cache.attributes; - } + auto attributes() const { return childs(0, -1); } /** * Retrieves an attribute with a given name from the set. If multiple @@ -187,8 +168,8 @@ class AttributeSet : public NodeBase { * * @return attribute if found */ - std::optional find(std::string_view tag) const { - for ( auto& a : attributes() ) + hilti::optional_ref find(std::string_view tag) const { + for ( const auto& a : attributes() ) if ( a.tag() == tag ) return a; @@ -200,12 +181,12 @@ class AttributeSet : public NodeBase { * * @return all attributes with matching name */ - std::vector findAll(std::string_view tag) const { - std::vector result; + auto findAll(std::string_view tag) const { + hilti::node::Set result; - for ( auto& a : attributes() ) + for ( const auto& a : attributes() ) if ( a.tag() == tag ) - result.push_back(a); + result.insert(a); return result; } @@ -216,16 +197,19 @@ class AttributeSet : public NodeBase { * * @return A successful return value if either the coercion succeeded * (then the result's value is true), or nothing was to be done (then the - * result's value is false); a failures if a coercionk would have been + * result's value is false); a failures if a coercion would have been * necessary, but failed. */ Result coerceValueTo(const std::string& tag, const Type& dst) { + if ( ! type::isResolved(dst) ) + return false; + for ( auto& n : childs() ) { auto a = n.as(); if ( a.tag() != tag ) continue; - if ( auto e = a.valueAs() ) { + if ( auto e = a.valueAsExpression() ) { auto ne = coerceExpression(*e, dst); if ( ! ne.coerced ) return result::Error("cannot coerce attribute value"); @@ -292,7 +276,8 @@ class AttributeSet : public NodeBase { * @param attrs set to inspect * @return attribute if found */ - static std::optional find(const std::optional& attrs, std::string_view tag) { + static hilti::optional_ref find(const hilti::optional_ref& attrs, + std::string_view tag) { if ( attrs ) return attrs->find(tag); else @@ -306,7 +291,22 @@ class AttributeSet : public NodeBase { * @param attrs set to inspect * @return all attributes with matching name */ - static std::vector findAll(const std::optional& attrs, std::string_view tag) { + static hilti::node::Set findAll(const std::optional& attrs, std::string_view tag) { + if ( attrs ) + return attrs->findAll(tag); + else + return {}; + } + + /** + * Retrieves all attribute with a given name from a set, dealing correctly + * with an unset optional set. + * + * @param attrs set to inspect + * @return all attributes with matching name + */ + static hilti::node::Set findAll(const hilti::optional_ref& attrs, + std::string_view tag) { if ( attrs ) return attrs->findAll(tag); else @@ -326,11 +326,6 @@ class AttributeSet : public NodeBase { else return false; } - - void clearCache() { _cache.attributes.clear(); } - -private: - mutable struct { std::vector attributes; } _cache; }; /** diff --git a/hilti/toolchain/include/ast/builder/builder.h b/hilti/toolchain/include/ast/builder/builder.h index 2a0107ac3..1113c5bcb 100644 --- a/hilti/toolchain/include/ast/builder/builder.h +++ b/hilti/toolchain/include/ast/builder/builder.h @@ -21,7 +21,7 @@ namespace hilti::builder { class Builder { public: - Builder(std::shared_ptr context) + Builder(std::weak_ptr context) : _context(std::move(context)), _our_block(statement::Block()), _block(*_our_block) {} Statement block() { @@ -29,6 +29,8 @@ class Builder { return *_our_block; } + auto context() const { return _context.lock(); } + Expression addTmp(const std::string& prefix, const Expression& init); Expression addTmp(const std::string& prefix, const Type& t, const std::vector& args = {}); Expression addTmp(const std::string& prefix, const Type& t, const Expression& init); @@ -197,8 +199,8 @@ class Builder { return SwitchProxy(this, lastStatement()); } - auto addSwitch(const statement::Declaration& init, Expression cond, Meta m = Meta()) { - _block._add(statement::Switch(init.declaration(), std::move(cond), {}, std::move(m))); + auto addSwitch(const statement::Declaration& cond, Meta m = Meta()) { + _block._add(statement::Switch(cond.declaration(), {}, std::move(m))); return SwitchProxy(this, lastStatement()); } @@ -239,7 +241,7 @@ class Builder { private: friend class SwitchProxy; - Builder(std::shared_ptr context, Statement& s) // NOLINT + Builder(std::weak_ptr context, Statement& s) // NOLINT : _context(std::move(context)), _block(s.as()) {} template @@ -251,7 +253,7 @@ class Builder { return std::shared_ptr(new Builder(_context, n.template as())); } - std::shared_ptr _context; + std::weak_ptr _context; std::optional _our_block; statement::Block& _block; diff --git a/hilti/toolchain/include/ast/builder/declaration.h b/hilti/toolchain/include/ast/builder/declaration.h index caf32618c..abba57349 100644 --- a/hilti/toolchain/include/ast/builder/declaration.h +++ b/hilti/toolchain/include/ast/builder/declaration.h @@ -19,14 +19,18 @@ inline auto import(std::string module, const Meta& m = Meta()) { return declaration::ImportedModule(hilti::ID(std::move(module), m), std::string(".hlt"), m); } -inline auto import(std::string module, std::vector search_dirs, const Meta& m = Meta()) { - return declaration::ImportedModule(hilti::ID(std::move(module), m), std::string(".hlt"), {}, std::move(search_dirs), - m); +inline auto import(std::string module, const std::string& parse_extension, const Meta& m = Meta()) { + return declaration::ImportedModule(hilti::ID(std::move(module), m), parse_extension, m); } -inline auto import(std::string module, std::optional search_scope, +inline auto import(std::string module, const std::string& parse_extension, std::vector search_dirs, const Meta& m = Meta()) { - return declaration::ImportedModule(hilti::ID(std::move(module), m), std::string(".hlt"), std::move(search_scope), + return declaration::ImportedModule(hilti::ID(std::move(module), m), parse_extension, {}, std::move(search_dirs), m); +} + +inline auto import(std::string module, const std::string& parse_extension, std::optional search_scope, + std::vector search_dirs, const Meta& m = Meta()) { + return declaration::ImportedModule(hilti::ID(std::move(module), m), parse_extension, std::move(search_scope), std::move(search_dirs), m); } diff --git a/hilti/toolchain/include/ast/builder/expression.h b/hilti/toolchain/include/ast/builder/expression.h index eb9043da6..361dfc5b3 100644 --- a/hilti/toolchain/include/ast/builder/expression.h +++ b/hilti/toolchain/include/ast/builder/expression.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include namespace hilti::builder { @@ -46,6 +46,11 @@ inline Expression default_(Type t, std::vector type_args, const Meta return expression::Ctor(ctor::Default(std::move(t), std::move(type_args), m), m); } +inline Expression default_(Type t, hilti::node::Range type_args, const Meta& m = Meta()) { + return expression::Ctor(ctor::Default(std::move(t), type_args.copy(), m), m); +} + + inline Expression exception(Type t, std::string msg, const Meta& m = Meta()) { return expression::Ctor(ctor::Exception(std::move(t), builder::string(std::move(msg)), m), m); } @@ -211,10 +216,27 @@ inline Expression member(Expression self, std::string id_, const Meta& m = Meta( {std::move(self), expression::Member(ID(std::move(id_)), m)}, m); } -inline Expression memberCall(Expression self, std::string id_, std::vector v, const Meta& m = Meta()) { +inline Expression hasMember(Expression self, std::string id_, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::HasMember, + {std::move(self), expression::Member(ID(std::move(id_)), m)}, m); +} + +inline Expression tryMember(Expression self, std::string id_, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::TryMember, + {std::move(self), expression::Member(ID(std::move(id_)), m)}, m); +} + +inline Expression memberCall(Expression self, std::string id_, std::vector args, const Meta& m = Meta()) { + return expression::UnresolvedOperator(operator_::Kind::MemberCall, + {std::move(self), expression::Member(ID(std::move(id_)), m), + tuple(std::move(args), m)}, + m); +} + +inline Expression memberCall(Expression self, std::string id_, ctor::Tuple args, const Meta& m = Meta()) { return expression::UnresolvedOperator(operator_::Kind::MemberCall, {std::move(self), expression::Member(ID(std::move(id_)), m), - tuple(std::move(v), m)}, + expression::Ctor(std::move(args))}, m); } @@ -264,15 +286,16 @@ inline Expression incrementPrefix(Expression op, const Meta& m = Meta()) { inline Expression new_(Type t, const Meta& m = Meta()) { return expression::UnresolvedOperator(operator_::Kind::New, - {expression::Type_(std::move(t), m), - hilti::expression::Ctor(hilti::ctor::Tuple({}, m))}, + std::vector{expression::Type_(std::move(t), m), + hilti::expression::Ctor(hilti::ctor::Tuple({}, m))}, m); } inline Expression new_(Type t, std::vector args, const Meta& m = Meta()) { return expression::UnresolvedOperator(operator_::Kind::New, - {expression::Type_(std::move(t), m), - hilti::expression::Ctor(hilti::ctor::Tuple(std::move(args), m))}, + std::vector{expression::Type_(std::move(t), m), + hilti::expression::Ctor( + hilti::ctor::Tuple(std::move(args), m))}, m); } @@ -294,18 +317,6 @@ inline Expression typeinfo(Type t, Meta m = Meta()) { inline Expression typeinfo(Expression e, Meta m = Meta()) { return expression::TypeInfo(std::move(e), std::move(m)); } -inline Expression self(NodeRef d, Meta m = Meta()) { - return expression::Keyword(hilti::expression::keyword::Kind::Self, std::move(d), std::move(m)); -} - -inline Expression dollardollar(Meta m = Meta()) { - return expression::Keyword(hilti::expression::keyword::Kind::DollarDollar, std::move(m)); -} - -inline Expression dollardollar(Type t, Meta m = Meta()) { - return expression::Keyword(hilti::expression::keyword::Kind::DollarDollar, std::move(t), std::move(m)); -} - inline Expression assign(Expression target, Expression src, Meta m = Meta()) { return expression::Assign(std::move(target), std::move(src), std::move(m)); } @@ -324,27 +335,9 @@ inline Expression max(const Expression& e1, const Expression& e2, const Meta& m return ternary(lowerEqual(e1, e2, m), e2, e1, m); } -inline Expression type_wrapped(Expression e, const Meta& m = Meta()) { - return expression::TypeWrapped(std::move(e), m); -} - -inline Expression type_wrapped(Expression e, Type t, const Meta& m = Meta()) { - return expression::TypeWrapped(std::move(e), std::move(t), m); -} - -inline Expression expect_type(Expression e, Type expected, const Meta& m = Meta()) { - return expression::TypeWrapped(e, std::move(expected), expression::TypeWrapped::ValidateTypeMatch(), - m ? std::move(m) : e.meta()); -} - -// Forces interpreting a given expression as a value of a __library_type. -inline Expression library_type_value(Expression e, ID library_type, const Meta& m = Meta()) { - return expression::TypeWrapped(e, hilti::type::UnresolvedID(std::move(library_type), m), m); -} - inline auto port(Expression port, Expression protocol, const Meta& m = Meta()) { return hilti::expression::BuiltinFunction( - "port", "hilti::rt::Port", hilti::type::Port(), + "port", "::hilti::rt::Port", hilti::type::Port(), hilti::builder::parameters(hilti::builder::parameter(hilti::ID("port"), hilti::type::UnsignedInteger(16)), hilti::builder::parameter(hilti::ID("protocol"), hilti::builder::typeByID("hilti::Protocol"))), diff --git a/hilti/toolchain/include/ast/builder/type.h b/hilti/toolchain/include/ast/builder/type.h index b2d4e972b..758024032 100644 --- a/hilti/toolchain/include/ast/builder/type.h +++ b/hilti/toolchain/include/ast/builder/type.h @@ -4,7 +4,7 @@ #include #include -#include +#include namespace hilti::builder { diff --git a/hilti/toolchain/include/ast/ctor.api b/hilti/toolchain/include/ast/ctor.api index 7a4417415..0346233df 100644 --- a/hilti/toolchain/include/ast/ctor.api +++ b/hilti/toolchain/include/ast/ctor.api @@ -3,7 +3,7 @@ /** Interface for HILTI constructors. */ class Ctor(trait::isCtor) : trait::isNode { /** Returns the HILTI `Type` of the constructor's value. */ - Type type() const; + const Type& type() const; /** * Returns true if the constructor's value is a value that will never @@ -42,11 +42,5 @@ class Ctor(trait::isCtor) : trait::isNode { void setMeta(Meta m); /** Implements the `Node` interface. */ - const NodeRef& originalNode() const; - - /** Implements the `Node` interface. */ - void setOriginalNode(const NodeRef& n); - - /** Implements the `Node` interface. */ - void clearCache(); + bool pruneWalk() const; }; diff --git a/hilti/toolchain/include/ast/ctors/address.h b/hilti/toolchain/include/ast/ctors/address.h index f7ec90a94..1072ed364 100644 --- a/hilti/toolchain/include/ast/ctors/address.h +++ b/hilti/toolchain/include/ast/ctors/address.h @@ -17,14 +17,14 @@ class Address : public NodeBase, public hilti::trait::isCtor { public: using Value = hilti::rt::Address; - Address(const Value& addr, Meta m = Meta()) : NodeBase(std::move(m)), _address(addr) {} + Address(const Value& addr, Meta m = Meta()) : NodeBase(nodes(type::Address(m)), m), _address(addr) {} const auto& value() const { return _address; } bool operator==(const Address& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Address(meta()); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ @@ -33,6 +33,7 @@ class Address : public NodeBase, public hilti::trait::isCtor { auto isTemporary() const { return true; } /** Implements `Ctor` interface. */ auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + /** Implements `Node` interface. */ auto properties() const { return node::Properties{{"address", to_string(_address)}}; } diff --git a/hilti/toolchain/include/ast/ctors/all.h b/hilti/toolchain/include/ast/ctors/all.h index 932a6947e..76cc7adc4 100644 --- a/hilti/toolchain/include/ast/ctors/all.h +++ b/hilti/toolchain/include/ast/ctors/all.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/hilti/toolchain/include/ast/ctors/bool.h b/hilti/toolchain/include/ast/ctors/bool.h index 8528bbfb8..31dfd8ba7 100644 --- a/hilti/toolchain/include/ast/ctors/bool.h +++ b/hilti/toolchain/include/ast/ctors/bool.h @@ -13,14 +13,14 @@ namespace ctor { /** AST node for a boolean constructor. */ class Bool : public NodeBase, public hilti::trait::isCtor { public: - Bool(bool v, Meta m = Meta()) : NodeBase(std::move(m)), _value(v) {} + Bool(bool v, Meta m = Meta()) : NodeBase(nodes(type::Bool(m)), m), _value(v) {} auto value() const { return _value; } bool operator==(const Bool& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Bool(); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/bytes.h b/hilti/toolchain/include/ast/ctors/bytes.h index 5bdbcd8bd..d178783f3 100644 --- a/hilti/toolchain/include/ast/ctors/bytes.h +++ b/hilti/toolchain/include/ast/ctors/bytes.h @@ -14,14 +14,14 @@ namespace ctor { /** AST node for a bytes constructor. */ class Bytes : public NodeBase, public hilti::trait::isCtor { public: - Bytes(std::string v, Meta m = Meta()) : NodeBase(std::move(m)), _value(std::move(v)) {} + Bytes(std::string v, Meta m = Meta()) : NodeBase(nodes(type::Bytes(m)), m), _value(std::move(v)) {} auto value() const { return _value; } bool operator==(const Bytes& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Bytes(); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return false; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/coerced.h b/hilti/toolchain/include/ast/ctors/coerced.h index 3249c240e..7676d4fd4 100644 --- a/hilti/toolchain/include/ast/ctors/coerced.h +++ b/hilti/toolchain/include/ast/ctors/coerced.h @@ -23,7 +23,7 @@ class Coerced : public NodeBase, public hilti::trait::isCtor { } /** Implements `Ctor` interface. */ - Type type() const { return type::effectiveType(coercedCtor().type()); } + const Type& type() const { return coercedCtor().type(); } /** Implements `Ctor` interface. */ bool isConstant() const { return coercedCtor().isConstant(); } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/default.h b/hilti/toolchain/include/ast/ctors/default.h index 85def5390..cd0a5d155 100644 --- a/hilti/toolchain/include/ast/ctors/default.h +++ b/hilti/toolchain/include/ast/ctors/default.h @@ -27,10 +27,17 @@ class Default : public NodeBase, public hilti::trait::isCtor { auto typeArguments() const { return childs(1, -1); } + void setTypeArguments(std::vector args) { + auto& c = childs(); + c.erase(c.begin() + 1, c.end()); + for ( auto&& a : args ) + c.emplace_back(std::move(a)); + } + bool operator==(const Default& other) const { return type() == other.type(); } /** Implements `Ctor` interface. */ - Type type() const { return type::effectiveType(child(0)); } + const Type& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ @@ -39,25 +46,8 @@ class Default : public NodeBase, public hilti::trait::isCtor { auto isTemporary() const { return true; } /** Implements `Ctor` interface. */ auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } - /** Implements `Node` interface. */ auto properties() const { return node::Properties{}; } - - /** - * Returns a new local default constructor with the type argument expressions replaced. - * - * @param d original declaration - * @param i new init expresssion - * @return new declaration that's equal to original one but with the init expression replaced - */ - static Ctor setTypeArguments(const Default& d, std::vector args) { - auto x = Ctor(d)._clone().as(); - x.childs() = x.childs(0, 1); - for ( auto&& a : args ) - x.childs().emplace_back(std::move(a)); - - return std::move(x); - } }; } // namespace ctor diff --git a/hilti/toolchain/include/ast/ctors/enum.h b/hilti/toolchain/include/ast/ctors/enum.h index 2e52421da..0e424b060 100644 --- a/hilti/toolchain/include/ast/ctors/enum.h +++ b/hilti/toolchain/include/ast/ctors/enum.h @@ -15,19 +15,15 @@ namespace ctor { /** AST node for an enum constructor. */ class Enum : public NodeBase, public hilti::trait::isCtor { public: - Enum(type::enum_::Label v, type::Enum t, Meta m = Meta()) : NodeBase({std::move(v), std::move(t)}, std::move(m)) {} - Enum(type::enum_::Label v, NodeRef td, Meta m = Meta()) - : NodeBase({std::move(v), node::none}, std::move(m)), _type_decl(std::move(td)) {} + Enum(type::enum_::Label v, Meta m = Meta()) : NodeBase(nodes(std::move(v)), std::move(m)) {} const auto& value() const { return childs()[0].as(); } bool operator==(const Enum& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { - return type::effectiveType(_type_decl ? type::effectiveType((*_type_decl)->as().type()) : - childs()[1].as()); - } + const auto& type() const { return value().enumType(); } + /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ @@ -36,12 +32,8 @@ class Enum : public NodeBase, public hilti::trait::isCtor { auto isTemporary() const { return true; } /** Implements `Ctor` interface. */ auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } - /** Implements `Node` interface. */ auto properties() const { return node::Properties{}; } - -private: - std::optional _type_decl; }; } // namespace ctor diff --git a/hilti/toolchain/include/ast/ctors/error.h b/hilti/toolchain/include/ast/ctors/error.h index 15108c5b1..590e8ae87 100644 --- a/hilti/toolchain/include/ast/ctors/error.h +++ b/hilti/toolchain/include/ast/ctors/error.h @@ -14,14 +14,14 @@ namespace ctor { /** AST node for an error constructor. */ class Error : public NodeBase, public hilti::trait::isCtor { public: - Error(std::string v, Meta m = Meta()) : NodeBase(std::move(m)), _value(std::move(v)) {} + Error(std::string v, Meta m = Meta()) : NodeBase(nodes(type::Error(m)), m), _value(std::move(v)) {} auto value() const { return _value; } bool operator==(const Error& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Error(); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/exception.h b/hilti/toolchain/include/ast/ctors/exception.h index e92c2756e..0f020970d 100644 --- a/hilti/toolchain/include/ast/ctors/exception.h +++ b/hilti/toolchain/include/ast/ctors/exception.h @@ -21,7 +21,7 @@ class Exception : public NodeBase, public hilti::trait::isCtor { bool operator==(const Exception& other) const { return type() == other.type() && value() == other.value(); } /** Implements `Ctor` interface. */ - Type type() const { return type::effectiveType(child(0)); } + const Type& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/integer.h b/hilti/toolchain/include/ast/ctors/integer.h index db35db2f8..bcb6af17a 100644 --- a/hilti/toolchain/include/ast/ctors/integer.h +++ b/hilti/toolchain/include/ast/ctors/integer.h @@ -17,7 +17,7 @@ namespace detail { template class IntegerBase : public NodeBase, public hilti::trait::isCtor { public: - IntegerBase(T v, int w, const Meta& m = Meta()) : NodeBase(m), _value(v), _width(w) {} + IntegerBase(T v, int w, const Meta& m = Meta()) : NodeBase(nodes(S(w, m)), m), _value(v), _width(w) {} auto value() const { return _value; } auto width() const { return _width; } @@ -29,7 +29,7 @@ class IntegerBase : public NodeBase, public hilti::trait::isCtor { /** Implements `Ctor` interface. */ auto isTemporary() const { return true; } /** Implements `Ctor` interface. */ - auto type() const { return S(_width); } + const auto& type() const { return child(0); } /** Implements `Node` interface. */ auto properties() const { return node::Properties{{"value", _value}, {"width", _width}}; } diff --git a/hilti/toolchain/include/ast/ctors/interval.h b/hilti/toolchain/include/ast/ctors/interval.h index d4c417be2..4f02a9e39 100644 --- a/hilti/toolchain/include/ast/ctors/interval.h +++ b/hilti/toolchain/include/ast/ctors/interval.h @@ -17,14 +17,14 @@ class Interval : public NodeBase, public hilti::trait::isCtor { public: using Value = hilti::rt::Interval; - Interval(const Value& interval, Meta m = Meta()) : NodeBase(std::move(m)), _interval(interval) {} + Interval(const Value& interval, Meta m = Meta()) : NodeBase(nodes(type::Interval(m)), m), _interval(interval) {} const auto& value() const { return _interval; } bool operator==(const Interval& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Interval(meta()); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/library.h b/hilti/toolchain/include/ast/ctors/library.h new file mode 100644 index 000000000..f0ea5f885 --- /dev/null +++ b/hilti/toolchain/include/ast/ctors/library.h @@ -0,0 +1,46 @@ +// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include + +namespace hilti { +namespace ctor { + +/** + * AST node for a constructor of an instance of a library type. Because we + * don't know more about the internal representation of the library type, we + * represent the value through a ctor of another, known type. The code + * generator must ensure that coercion operates correctly for the final C++ + * code. + **/ +class Library : public NodeBase, public hilti::trait::isCtor { +public: + Library(Ctor ctor, Type lib_type, Meta m = Meta()) + : NodeBase({std::move(ctor), std::move(lib_type)}, std::move(m)) {} + + const auto& value() const { return child(0); } + + bool operator==(const Library& other) const { return value() == other.value() && type() == other.type(); } + + /** Implements `Ctor` interface. */ + const Type& type() const { return child(1); } + /** Implements `Ctor` interface. */ + bool isConstant() const { return value().isConstant(); } + /** Implements `Ctor` interface. */ + auto isLhs() const { return false; } + /** Implements `Ctor` interface. */ + auto isTemporary() const { return true; } + /** Implements `Ctor` interface. */ + auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } + + /** Implements `Node` interface. */ + auto properties() const { return node::Properties{}; } +}; + +} // namespace ctor +} // namespace hilti diff --git a/hilti/toolchain/include/ast/ctors/list.h b/hilti/toolchain/include/ast/ctors/list.h index 06807c244..e2b2fc584 100644 --- a/hilti/toolchain/include/ast/ctors/list.h +++ b/hilti/toolchain/include/ast/ctors/list.h @@ -8,8 +8,9 @@ #include #include #include +#include +#include #include -#include namespace hilti { namespace ctor { @@ -17,29 +18,28 @@ namespace ctor { /** AST node for a List constructor. */ class List : public NodeBase, public hilti::trait::isCtor { public: - List(std::vector e, Meta m = Meta()) : NodeBase(nodes(node::none, e), m) {} - List(Type t, std::vector e, Meta m = Meta()) - : NodeBase(nodes(std::move(t), std::move(e)), std::move(m)) {} - - auto elementType() const { - if ( auto t = childs()[0].tryAs() ) - return type::effectiveType(*t); - else { - if ( childs().size() < 2 ) - return type::unknown; - - return childs()[1].as().type(); - } - } + List(std::vector e, Meta m = Meta()) + : NodeBase(nodes(type::List(e.size() ? Type(type::auto_) : Type(type::Bool())), e), m) { + } // Bool is just an arbitrary place-holder type for empty values + List(Type t, std::vector e, Meta m = Meta()) : NodeBase(nodes(type::List(t, m), std::move(e)), m) {} + const auto& elementType() const { return childs()[0].as().elementType(); } auto value() const { return childs(1, -1); } + void setElementType(Type t) { childs()[0] = type::List(std::move(t), meta()); } + + void setValue(std::vector elems) { + childs().erase(childs().begin() + 1, childs().end()); + for ( auto&& e : elems ) + childs().push_back(e); + } + bool operator==(const List& other) const { return elementType() == other.elementType() && value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::List(elementType(), meta()); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return false; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/map.h b/hilti/toolchain/include/ast/ctors/map.h index 9dc5bfa65..3d7ae3aa4 100644 --- a/hilti/toolchain/include/ast/ctors/map.h +++ b/hilti/toolchain/include/ast/ctors/map.h @@ -9,57 +9,70 @@ #include #include #include +#include +#include #include -#include namespace hilti { namespace ctor { +namespace map { +/** AST node for a map element constructor. */ +class Element : public NodeBase { +public: + Element(Expression k, Expression v, Meta m = Meta()) : NodeBase(nodes(std::move(k), std::move(v)), std::move(m)) {} + Element(Meta m = Meta()) : NodeBase(nodes(node::none, node::none), std::move(m)) {} + + const auto& key() const { return child(0); } + const auto& value() const { return child(1); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Element& other) const { return key() == other.key() && value() == other.value(); } +}; + +inline Node to_node(Element f) { return Node(std::move(f)); } +} // namespace map + /** AST node for a map constructor. */ class Map : public NodeBase, public hilti::trait::isCtor { public: - using Element = std::pair; - Map(const std::vector& e, const Meta& m = Meta()) - : NodeBase(nodes(node::none, node::none, _flatten(e)), m) {} - Map(Type key, Type value, const std::vector& e, Meta m = Meta()) - : NodeBase(nodes(std::move(key), std::move(value), _flatten(e)), std::move(m)) {} - - auto keyType() const { - if ( auto t = childs()[0].tryAs() ) - return type::effectiveType(*t); - else { - if ( childs().size() < 2 ) - return type::unknown; - - return childs()[2].as().type(); - } - } + Map(const std::vector& e, const Meta& m = Meta()) + : NodeBase(nodes(e.size() ? Type(type::auto_) : Type(type::Bool()), std::move(e)), m) {} + Map(Type key, Type value, const std::vector& e, Meta m = Meta()) + : NodeBase(nodes(type::Map(key, value, m), std::move(e)), m) {} - auto elementType() const { - if ( auto t = childs()[1].tryAs() ) - return type::effectiveType(*t); - else { - if ( childs().size() < 3 ) - return type::unknown; + const auto& keyType() const { + if ( auto t = childs()[0].tryAs() ) + return t->keyType(); + else + return childs()[0].as(); + } - return childs()[3].as().type(); - } + const auto& valueType() const { + if ( auto t = childs()[0].tryAs() ) + return t->valueType(); + else + return childs()[0].as(); } - auto value() const { - auto exprs = childs(2, -1); - std::vector elems; - for ( auto&& i = exprs.begin(); i != exprs.end(); i += 2 ) - elems.emplace_back(std::make_pair(std::move(*i), std::move(*(i + 1)))); - return elems; + auto value() const { return childs(1, -1); } + + void setElementType(Type k, Type v) { childs()[0] = type::Map(std::move(k), std::move(v), meta()); } + + void setValue(std::vector elems) { + childs().erase(childs().begin() + 1, childs().end()); + for ( auto&& e : elems ) + childs().push_back(e); } bool operator==(const Map& other) const { - return keyType() == other.keyType() && elementType() == other.elementType() && value() == other.value(); + return keyType() == other.keyType() && valueType() == other.valueType() && value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Map(keyType(), elementType(), meta()); } + const auto& type() const { return childs()[0].as(); } /** Implements `Ctor` interface. */ bool isConstant() const { return false; } /** Implements `Ctor` interface. */ @@ -70,17 +83,6 @@ class Map : public NodeBase, public hilti::trait::isCtor { auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } /** Implements `Node` interface. */ auto properties() const { return node::Properties{}; } - -private: - std::vector _flatten(const std::vector& elems) { - std::vector exprs; - for ( auto&& e : elems ) { - exprs.emplace_back(e.first); - exprs.emplace_back(e.second); - } - - return exprs; - } }; } // namespace ctor diff --git a/hilti/toolchain/include/ast/ctors/network.h b/hilti/toolchain/include/ast/ctors/network.h index f95f72639..70f8e7597 100644 --- a/hilti/toolchain/include/ast/ctors/network.h +++ b/hilti/toolchain/include/ast/ctors/network.h @@ -17,14 +17,14 @@ class Network : public NodeBase, public hilti::trait::isCtor { public: using Value = hilti::rt::Network; - Network(const Value& network, Meta m = Meta()) : NodeBase(std::move(m)), _network(network) {} + Network(const Value& network, Meta m = Meta()) : NodeBase(nodes(type::Network(m)), m), _network(network) {} const auto& value() const { return _network; } bool operator==(const Network& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Network(meta()); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/null.h b/hilti/toolchain/include/ast/ctors/null.h index 6a166a7cd..ba7ff253c 100644 --- a/hilti/toolchain/include/ast/ctors/null.h +++ b/hilti/toolchain/include/ast/ctors/null.h @@ -13,12 +13,12 @@ namespace ctor { /** AST node for a null constructor. */ class Null : public NodeBase, public hilti::trait::isCtor { public: - Null(Meta m = Meta()) : NodeBase(std::move(m)) {} + Null(Meta m = Meta()) : NodeBase(nodes(type::Null(m)), m) {} bool operator==(const Null& /* other */) const { return true; } /** Implements `Ctor` interface. */ - auto type() const { return type::Null(); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/optional.h b/hilti/toolchain/include/ast/ctors/optional.h index 08627b8bb..eafdb76e7 100644 --- a/hilti/toolchain/include/ast/ctors/optional.h +++ b/hilti/toolchain/include/ast/ctors/optional.h @@ -7,6 +7,7 @@ #include #include #include +#include #include namespace hilti { @@ -16,24 +17,20 @@ namespace ctor { class Optional : public NodeBase, public hilti::trait::isCtor { public: /** Constructs a set value. */ - Optional(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + Optional(Expression e, Meta m = Meta()) : NodeBase(nodes(type::Optional(type::auto_), e), m) {} /** Constructs an unset value of type `t`. */ - Optional(Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)) {} + Optional(Type t, Meta m = Meta()) : NodeBase(nodes(type::Optional(t, m), node::none), m) {} - std::optional value() const { return childs()[0].tryAs(); } + const Type& dereferencedType() const { return childs()[0].as().dereferencedType(); } + hilti::optional_ref value() const { return childs()[1].tryAs(); } - Type dereferencedType() const { - if ( auto x = childs()[0].tryAs() ) - return x->type(); - - return type::effectiveType(child(0)); - } + void setDereferencedType(Type x) { childs()[0] = type::Optional(std::move(x)); } bool operator==(const Optional& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - Type type() const { return type::Optional(dereferencedType(), meta()); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { diff --git a/hilti/toolchain/include/ast/ctors/port.h b/hilti/toolchain/include/ast/ctors/port.h index a362d5415..363746abc 100644 --- a/hilti/toolchain/include/ast/ctors/port.h +++ b/hilti/toolchain/include/ast/ctors/port.h @@ -17,14 +17,14 @@ class Port : public NodeBase, public hilti::trait::isCtor { public: using Value = hilti::rt::Port; - Port(const Value& port, Meta m = Meta()) : NodeBase(std::move(m)), _port(port) {} + Port(const Value& port, Meta m = Meta()) : NodeBase(nodes(type::Port(m)), m), _port(port) {} const auto& value() const { return _port; } bool operator==(const Port& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Port(meta()); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/real.h b/hilti/toolchain/include/ast/ctors/real.h index 3468766a3..b57188345 100644 --- a/hilti/toolchain/include/ast/ctors/real.h +++ b/hilti/toolchain/include/ast/ctors/real.h @@ -13,14 +13,14 @@ namespace ctor { /** AST node for a double precision floating-point constructor. */ class Real : public NodeBase, public hilti::trait::isCtor { public: - Real(double v, Meta m = Meta()) : NodeBase(std::move(m)), _value(v) {} + Real(double v, Meta m = Meta()) : NodeBase(nodes(type::Real(m)), m), _value(v) {} auto value() const { return _value; } bool operator==(const Real& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Real(); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/reference.h b/hilti/toolchain/include/ast/ctors/reference.h index dfd59f3f0..7f379ff56 100644 --- a/hilti/toolchain/include/ast/ctors/reference.h +++ b/hilti/toolchain/include/ast/ctors/reference.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include namespace hilti { @@ -16,14 +18,14 @@ namespace ctor { class StrongReference : public NodeBase, public hilti::trait::isCtor { public: /** Constructs a null value of type `t`. */ - StrongReference(Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)) {} + StrongReference(Type t, Meta m = Meta()) : NodeBase(nodes(t, type::StrongReference(t, m)), m) {} - Type dereferencedType() const { return type::effectiveType(child(0)); } + const Type& dereferencedType() const { return child(0); } bool operator==(const StrongReference& other) const { return dereferencedType() == other.dereferencedType(); } /** Implements `Ctor` interface. */ - Type type() const { return type::StrongReference(dereferencedType(), meta()); } + const Type& type() const { return child(1); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } @@ -43,14 +45,14 @@ class StrongReference : public NodeBase, public hilti::trait::isCtor { class WeakReference : public NodeBase, public hilti::trait::isCtor { public: /** Constructs a null value of type `t`. */ - WeakReference(Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)) {} + WeakReference(Type t, Meta m = Meta()) : NodeBase(nodes(t, type::WeakReference(t, m)), m) {} - Type dereferencedType() const { return type::effectiveType(child(0)); } + const Type& dereferencedType() const { return child(0); } bool operator==(const WeakReference& other) const { return dereferencedType() == other.dereferencedType(); } /** Implements `Ctor` interface. */ - Type type() const { return type::WeakReference(dereferencedType(), meta()); } + const Type& type() const { return child(1); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } @@ -70,15 +72,18 @@ class WeakReference : public NodeBase, public hilti::trait::isCtor { class ValueReference : public NodeBase, public hilti::trait::isCtor { public: /** Constructs a reference value of type `t`. */ - ValueReference(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + ValueReference(Expression e, Meta m = Meta()) + : NodeBase(nodes(type::ValueReference(type::auto_, m), e), std::move(m)) {} - const Expression& expression() const { return child(0); } - Type dereferencedType() const { return child(0).type(); } + const Type& dereferencedType() const { return child(0).dereferencedType(); } + const Expression& expression() const { return child(1); } + + void setDereferencedType(Type x) { childs()[0] = type::ValueReference(std::move(x)); } bool operator==(const ValueReference& other) const { return dereferencedType() == other.dereferencedType(); } /** Implements `Ctor` interface. */ - Type type() const { return type::ValueReference(dereferencedType(), meta()); } + const Type& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/regexp.h b/hilti/toolchain/include/ast/ctors/regexp.h index 791b66092..9ad2ebc40 100644 --- a/hilti/toolchain/include/ast/ctors/regexp.h +++ b/hilti/toolchain/include/ast/ctors/regexp.h @@ -18,9 +18,9 @@ namespace ctor { class RegExp : public NodeBase, public hilti::trait::isCtor { public: RegExp(std::vector p, std::optional attrs = {}, Meta m = Meta()) - : NodeBase(nodes(std::move(attrs)), std::move(m)), _patterns(std::move(p)) {} + : NodeBase(nodes(type::RegExp(m), std::move(attrs)), std::move(m)), _patterns(std::move(p)) {} - auto attributes() const { return childs()[0].tryReferenceAs(); } + auto attributes() const { return childs()[1].tryAs(); } const auto& value() const { return _patterns; } /** @@ -31,7 +31,7 @@ class RegExp : public NodeBase, public hilti::trait::isCtor { bool operator==(const RegExp& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::RegExp(meta()); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/result.h b/hilti/toolchain/include/ast/ctors/result.h index 357138357..c79ea6ce9 100644 --- a/hilti/toolchain/include/ast/ctors/result.h +++ b/hilti/toolchain/include/ast/ctors/result.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -17,42 +18,32 @@ namespace ctor { /** AST node for a constructor for a result value. */ class Result : public NodeBase, public hilti::trait::isCtor { public: - Result(Expression v, Meta m = Meta()) : NodeBase({std::move(v)}, std::move(m)) {} + Result(Expression v, Meta m = Meta()) : NodeBase(nodes(type::Result(type::auto_), std::move(v)), m) {} - std::optional value() const { - auto e = child(0); + hilti::optional_ref value() const { + const auto& e = child(1); if ( e.type() != type::Error() ) - return std::move(e); + return e; return {}; } - std::optional error() const { - auto e = child(0); + hilti::optional_ref error() const { + const auto& e = child(1); if ( e.type() == type::Error() ) - return std::move(e); + return e; return {}; } - std::optional dereferencedType() const { - if ( auto x = value() ) - return x->type(); + const Type& dereferencedType() const { return childs()[0].as().dereferencedType(); } - return {}; - } - - bool operator==(const Result& other) const { return value() == other.value() && error() == other.error(); } + void setDereferencedType(Type x) { childs()[0] = type::Result(std::move(x)); } /** Implements `Ctor` interface. */ - Type type() const { - if ( auto v = value() ) - return type::Result(v->type(), meta()); - - return type::Result(type::Any(), meta()); - } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { @@ -62,6 +53,8 @@ class Result : public NodeBase, public hilti::trait::isCtor { return true; } + bool operator==(const Result& other) const { return value() == other.value() && error() == other.error(); } + /** Implements `Ctor` interface. */ auto isLhs() const { return false; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/set.h b/hilti/toolchain/include/ast/ctors/set.h index 2d81043be..95f8dfc9d 100644 --- a/hilti/toolchain/include/ast/ctors/set.h +++ b/hilti/toolchain/include/ast/ctors/set.h @@ -8,8 +8,9 @@ #include #include #include +#include +#include #include -#include namespace hilti { namespace ctor { @@ -17,27 +18,26 @@ namespace ctor { /** AST node for a set constructor. */ class Set : public NodeBase, public hilti::trait::isCtor { public: - Set(const std::vector& e, const Meta& m = Meta()) : NodeBase(nodes(node::none, e), m) {} - Set(Type t, std::vector e, Meta m = Meta()) - : NodeBase(nodes(std::move(t), std::move(e)), std::move(m)) {} - - auto elementType() const { - if ( auto t = childs()[0].tryAs() ) - return type::effectiveType(*t); - else { - if ( childs().size() < 2 ) - return type::unknown; - - return childs()[1].as().type(); - } - } + Set(std::vector e, Meta m = Meta()) + : NodeBase(nodes(type::Set(e.size() ? Type(type::auto_) : Type(type::Bool())), e), m) { + } // Bool is just an arbitrary place-holder type for empty values + Set(Type t, std::vector e, Meta m = Meta()) : NodeBase(nodes(type::Set(t, m), std::move(e)), m) {} + const auto& elementType() const { return childs()[0].as().elementType(); } auto value() const { return childs(1, -1); } + void setElementType(Type t) { childs()[0] = type::Set(std::move(t), meta()); } + + void setValue(std::vector elems) { + childs().erase(childs().begin() + 1, childs().end()); + for ( auto&& e : elems ) + childs().push_back(e); + } + bool operator==(const Set& other) const { return elementType() == other.elementType() && value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Set(elementType(), meta()); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return false; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/stream.h b/hilti/toolchain/include/ast/ctors/stream.h index e944d679a..cb1c95322 100644 --- a/hilti/toolchain/include/ast/ctors/stream.h +++ b/hilti/toolchain/include/ast/ctors/stream.h @@ -14,14 +14,14 @@ namespace ctor { /** AST node for a stream constructor. */ class Stream : public NodeBase, public hilti::trait::isCtor { public: - Stream(std::string v, Meta m = Meta()) : NodeBase(std::move(m)), _value(std::move(v)) {} + Stream(std::string v, Meta m = Meta()) : NodeBase(nodes(type::Stream(m)), m), _value(std::move(v)) {} auto value() const { return _value; } bool operator==(const Stream& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Stream(); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return false; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/string.h b/hilti/toolchain/include/ast/ctors/string.h index 747f077c4..f78b734b5 100644 --- a/hilti/toolchain/include/ast/ctors/string.h +++ b/hilti/toolchain/include/ast/ctors/string.h @@ -14,14 +14,14 @@ namespace ctor { /** AST node for a string constructor. */ class String : public NodeBase, public hilti::trait::isCtor { public: - String(std::string v, Meta m = Meta()) : NodeBase(std::move(m)), _value(std::move(v)) {} + String(std::string v, Meta m = Meta()) : NodeBase(nodes(type::String(m)), m), _value(std::move(v)) {} auto value() const { return _value; } bool operator==(const String& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::String(); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/struct.h b/hilti/toolchain/include/ast/ctors/struct.h index f3af4512a..d04df217c 100644 --- a/hilti/toolchain/include/ast/ctors/struct.h +++ b/hilti/toolchain/include/ast/ctors/struct.h @@ -9,55 +9,59 @@ #include #include #include +#include #include namespace hilti { namespace ctor { namespace struct_ { -/** A struct field initialization in the form of key/value. */ -using Field = std::pair; +/** AST node for a struct field constructor. */ +class Field : public NodeBase { +public: + Field(ID id, Expression e, Meta m = Meta()) : NodeBase(nodes(std::move(id), std::move(e)), std::move(m)) {} + Field(Meta m = Meta()) : NodeBase(nodes(node::none, node::none), std::move(m)) {} + + const auto& id() const { return child(0); } + const auto& expression() const { return child(1); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Field& other) const { return id() == other.id() && expression() == other.expression(); } +}; + +inline Node to_node(Field f) { return Node(std::move(f)); } } // namespace struct_ /** AST node for a struct constructor. */ class Struct : public NodeBase, public hilti::trait::isCtor { public: - Struct(std::vector f, Meta m = Meta()) - : NodeBase(nodes(type::unknown, std::move(f)), std::move(m)) {} + Struct(std::vector f, Meta m = Meta()) : NodeBase(nodes(type::auto_, std::move(f)), std::move(m)) {} Struct(std::vector f, Type t, Meta m = Meta()) : NodeBase(nodes(std::move(t), std::move(f)), std::move(m)) {} - /** Returns all field IDs that the constructors initialized. */ - auto ids() const { return childsOfType(); } - - /** Returns all field values that the constructors initializes. */ - auto values() const { return childsOfType(); } - /** Returns all fields that the constructors initialized. */ - auto fields() const { return util::zip2(ids(), values()); } + auto fields() const { return childs(1, -1); } + + auto stype() const { return child(0); } /** Returns a field initialized by the constructor by its ID. */ - std::optional field(const ID& id) const { - for ( auto f : fields() ) { - if ( f.first == id ) + hilti::optional_ref field(const ID& id) const { + for ( const auto& f : fields() ) { + if ( f.id() == id ) return f; } return {}; } - bool operator==(const Struct& other) const { return ids() == other.ids() && values() == other.values(); } + void setType(type::Struct x) { childs()[0] = std::move(x); } + + bool operator==(const Struct& other) const { return fields() == other.fields(); } /** Implements `Ctor` interface. */ - Type type() const { - if ( auto t = childs()[0].as(); ! t.isA() ) - return type::effectiveType(t); - - auto f = util::transform(fields(), [](auto& x) { - return type::struct_::Field(x.first, x.second.type(), {}, x.first.meta()); - }); - return type::Struct(f, meta()); - } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } diff --git a/hilti/toolchain/include/ast/ctors/time.h b/hilti/toolchain/include/ast/ctors/time.h index 923543c81..d25e7bf55 100644 --- a/hilti/toolchain/include/ast/ctors/time.h +++ b/hilti/toolchain/include/ast/ctors/time.h @@ -17,14 +17,14 @@ class Time : public NodeBase, public hilti::trait::isCtor { public: using Value = hilti::rt::Time; - Time(const Value& time, Meta m = Meta()) : NodeBase(std::move(m)), _time(time) {} + Time(const Value& time, Meta m = Meta()) : NodeBase(nodes(type::Time(m)), m), _time(time) {} const auto& value() const { return _time; } bool operator==(const Time& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Time(meta()); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/ctors/tuple.h b/hilti/toolchain/include/ast/ctors/tuple.h index 61185a382..906b7f3b7 100644 --- a/hilti/toolchain/include/ast/ctors/tuple.h +++ b/hilti/toolchain/include/ast/ctors/tuple.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -17,29 +18,16 @@ namespace ctor { /** AST node for a tuple constructor. */ class Tuple : public NodeBase, public hilti::trait::isCtor { public: - Tuple(std::vector v, Meta m = Meta()) : NodeBase(nodes(std::move(v)), std::move(m)) {} + Tuple(std::vector v, Meta m = Meta()) : NodeBase(nodes(_inferType(v), v), std::move(m)) {} - auto value() const { return childs(0, -1); } + auto value() const { return childs(1, -1); } + + void setElementTypes(std::vector t) { childs()[0] = Type(type::Tuple(std::move(t), meta())); } bool operator==(const Tuple& other) const { return value() == other.value(); } /** Implements `Ctor` interface. */ - Type type() const { - auto v1 = value(); - auto v2 = std::vector{}; - bool is_unknown = false; - std::transform(v1.begin(), v1.end(), std::back_inserter(v2), [&is_unknown](const Expression& e) { - if ( e.type() == type::unknown ) - is_unknown = true; - - return e.type(); - }); - - if ( is_unknown ) - return type::unknown; - else - return type::Tuple(v2, meta()); - } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } @@ -63,6 +51,20 @@ class Tuple : public NodeBase, public hilti::trait::isCtor { auto isEqual(const Ctor& other) const { return node::isEqual(this, other); } /** Implements `Node` interface. */ auto properties() const { return node::Properties{}; } + +private: + Type _inferType(const std::vector& exprs) { + for ( const auto& e : exprs ) { + if ( ! expression::isResolved(e) ) + return type::auto_; + } + + std::vector types; + for ( const auto& e : exprs ) + types.push_back(e.type()); + + return type::Tuple(std::move(types)); + } }; } // namespace ctor diff --git a/hilti/toolchain/include/ast/ctors/union.h b/hilti/toolchain/include/ast/ctors/union.h index f1f33fa95..b68aec3da 100644 --- a/hilti/toolchain/include/ast/ctors/union.h +++ b/hilti/toolchain/include/ast/ctors/union.h @@ -19,12 +19,12 @@ class Union : public NodeBase, public hilti::trait::isCtor { : NodeBase(nodes(std::move(unit_type), std::move(value)), std::move(m)) {} /** Returns the value to initialize the unit with. */ - Expression value() const { return child(1); } + const Expression& value() const { return child(1); } bool operator==(const Union& other) const { return type() == other.type() && value() == other.value(); } /** Implements `Ctor` interface. */ - Type type() const { return child(0); } + const Type& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return true; } diff --git a/hilti/toolchain/include/ast/ctors/vector.h b/hilti/toolchain/include/ast/ctors/vector.h index 08903e815..d7f63d8aa 100644 --- a/hilti/toolchain/include/ast/ctors/vector.h +++ b/hilti/toolchain/include/ast/ctors/vector.h @@ -8,7 +8,8 @@ #include #include #include -#include +#include +#include #include namespace hilti { @@ -17,29 +18,28 @@ namespace ctor { /** AST node for a vector constructor. */ class Vector : public NodeBase, public hilti::trait::isCtor { public: - Vector(const std::vector& e, const Meta& m = Meta()) : NodeBase(nodes(node::none, e), m) {} - Vector(Type t, std::vector e, Meta m = Meta()) - : NodeBase(nodes(std::move(t), std::move(e)), std::move(m)) {} - - auto elementType() const { - if ( auto t = childs()[0].tryAs() ) - return type::effectiveType(*t); - else { - if ( childs().size() < 2 ) - return type::unknown; - - return childs()[1].as().type(); - } - } + Vector(std::vector e, Meta m = Meta()) + : NodeBase(nodes(type::Vector(e.size() ? Type(type::auto_) : Type(type::Bool())), e), m) { + } // Bool is just an arbitrary place-holder type for empty values + Vector(Type t, std::vector e, Meta m = Meta()) : NodeBase(nodes(type::Vector(t, m), std::move(e)), m) {} + const auto& elementType() const { return childs()[0].as().elementType(); } auto value() const { return childs(1, -1); } + void setElementType(Type t) { childs()[0] = type::Vector(std::move(t), meta()); } + + void setValue(std::vector elems) { + childs().erase(childs().begin() + 1, childs().end()); + for ( auto&& e : elems ) + childs().push_back(e); + } + bool operator==(const Vector& other) const { return elementType() == other.elementType() && value() == other.value(); } /** Implements `Ctor` interface. */ - auto type() const { return type::Vector(elementType(), meta()); } + const auto& type() const { return child(0); } /** Implements `Ctor` interface. */ bool isConstant() const { return false; } /** Implements `Ctor` interface. */ diff --git a/hilti/toolchain/include/ast/declaration.api b/hilti/toolchain/include/ast/declaration.api index f462a2370..881f1ed65 100644 --- a/hilti/toolchain/include/ast/declaration.api +++ b/hilti/toolchain/include/ast/declaration.api @@ -14,13 +14,29 @@ class Declaration(trait::isDeclaration) : trait::isNode { bool isConstant() const; /** - * Returns a user-friendly name for the type of object the declararion + * Returns the canonical ID associated with the declaration. Canonical IDs + * are automatically computed during AST processing and guaranteed to be + * globally unique and stable across runs. + */ + const ID& canonicalID() const; + + /** + * Associates a canonical ID with the declaration. To be called from AST + * processing. + */ + void setCanonicalID(ID id); + + /** + * Returns a user-friendly name for the type of object the declaration * refers to. This is used in error messages. */ std::string displayName() const; - /** Returns true if the declaration is equivalent to another one in HILTI semantics. */ - bool isEqual(const Declaration& other) const; + /** + * Returns true if the declaration is equivalent to another one in HILTI + * semantics. + */ + bool isEqual(const hilti::Declaration& other) const; /** Implements the `Node` interface. */ hilti::node::Properties properties() const; @@ -38,11 +54,5 @@ class Declaration(trait::isDeclaration) : trait::isNode { void setMeta(Meta m); /** Implements the `Node` interface. */ - const NodeRef& originalNode() const; - - /** Implements the `Node` interface. */ - void setOriginalNode(const NodeRef& n); - - /** Implements the `Node` interface. */ - void clearCache(); + bool pruneWalk() const; }; diff --git a/hilti/toolchain/include/ast/declaration.h b/hilti/toolchain/include/ast/declaration.h index ff59af492..2521433c2 100644 --- a/hilti/toolchain/include/ast/declaration.h +++ b/hilti/toolchain/include/ast/declaration.h @@ -1,7 +1,7 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. #pragma once +#include #include #include @@ -28,7 +28,7 @@ enum class Linkage { namespace detail { constexpr util::enum_::Value linkages[] = { - {Linkage::Struct, "method"}, {Linkage::Public, "public"}, {Linkage::Private, "private"}, + {Linkage::Struct, "struct"}, {Linkage::Public, "public"}, {Linkage::Private, "private"}, {Linkage::Init, "init"}, {Linkage::PreInit, "preinit"}, }; } // namespace detail @@ -46,8 +46,14 @@ constexpr auto from_string(const std::string_view& s) { return util::enum_::from } // namespace linkage namespace detail { - #include +} +} // namespace declaration + +class Declaration : public declaration::detail::Declaration { +public: + using declaration::detail::Declaration::Declaration; +}; /** Creates an AST node representing a `Declaration`. */ inline Node to_node(Declaration t) { return Node(std::move(t)); } @@ -65,16 +71,30 @@ inline bool operator==(const Declaration& x, const Declaration& y) { inline bool operator!=(const Declaration& d1, const Declaration& d2) { return ! (d1 == d2); } -} // namespace detail -} // namespace declaration - -using Declaration = declaration::detail::Declaration; -using declaration::detail::to_node; - +namespace declaration { /** Constructs an AST node from any class implementing the `Declaration` interface. */ template::value>* = nullptr> inline Node to_node(T t) { return Node(Declaration(std::move(t))); } +} // namespace declaration + +/** + * Base class for classes implementing the `Declaration` interface. This class + * provides implementations for some interface methods shared that are shared + * by all declarations. + */ +class DeclarationBase : public NodeBase, public hilti::trait::isDeclaration { +public: + using NodeBase::NodeBase; + + /** Implements the `Declaration` interface. */ + const ID& canonicalID() const { return _id; } + /** Implements the `Declaration` interface. */ + void setCanonicalID(ID id) { _id = std::move(id); } + +private: + ID _id; +}; } // namespace hilti diff --git a/hilti/toolchain/include/ast/declarations/all.h b/hilti/toolchain/include/ast/declarations/all.h index dc4ae44f8..75ca1be07 100644 --- a/hilti/toolchain/include/ast/declarations/all.h +++ b/hilti/toolchain/include/ast/declarations/all.h @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include #include diff --git a/hilti/toolchain/include/ast/declarations/constant.h b/hilti/toolchain/include/ast/declarations/constant.h index cf4192ca7..60b3763cc 100644 --- a/hilti/toolchain/include/ast/declarations/constant.h +++ b/hilti/toolchain/include/ast/declarations/constant.h @@ -8,34 +8,31 @@ #include #include #include -#include +#include namespace hilti { namespace declaration { /** AST node for a declaration of a constant. */ -class Constant : public NodeBase, public hilti::trait::isDeclaration { +class Constant : public DeclarationBase { public: Constant(ID id, ::hilti::Type type, hilti::Expression value = {}, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(type), std::move(value)), std::move(m)), _linkage(linkage) {} + : DeclarationBase(nodes(std::move(id), std::move(type), std::move(value)), std::move(m)), _linkage(linkage) {} Constant(ID id, hilti::Expression value, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase({std::move(id), node::none, std::move(value)}, std::move(m)), _linkage(linkage) {} + : DeclarationBase(nodes(std::move(id), node::none, std::move(value)), std::move(m)), _linkage(linkage) {} const auto& value() const { return child(2); } - ::hilti::Type type() const { - if ( auto t = childs()[1].tryAs<::hilti::Type>(); t && *t != type::unknown ) - return type::effectiveType(std::move(*t)); - - return value().type(); + const auto& type() const { + if ( auto t = childs()[1].tryAs() ) + return *t; + else + return value().type(); } - /** - * Returns true if the type is not explicitly specified. - */ - auto hasAutomaticType() const { return ! childs()[1].isA<::hilti::Type>(); } + void setValue(hilti::Expression i) { childs()[2] = std::move(i); } bool operator==(const Constant& other) const { return id() == other.id() && value() == other.value(); } @@ -53,19 +50,6 @@ class Constant : public NodeBase, public hilti::trait::isDeclaration { /** Implements `Node` interface. */ auto properties() const { return node::Properties{{"linkage", to_string(_linkage)}}; } - /** - * Returns a new global variable declaration with the init expression replaced. - * - * @param d original declaration - * @param b new init expresssion - * @return new declaration that's equal to original one but with the init expression replaced - */ - static Declaration setValue(const Constant& d, const hilti::Expression& i) { - auto x = Declaration(d)._clone().as(); - x.childs()[2] = i; - return std::move(x); - } - private: Linkage _linkage; }; diff --git a/hilti/toolchain/include/ast/declarations/expression.h b/hilti/toolchain/include/ast/declarations/expression.h index b6e31e9cc..d1b84fa50 100644 --- a/hilti/toolchain/include/ast/declarations/expression.h +++ b/hilti/toolchain/include/ast/declarations/expression.h @@ -14,16 +14,16 @@ namespace hilti { namespace declaration { /** AST node for a declaration of an expression. */ -class Expression : public NodeBase, public hilti::trait::isDeclaration { +class Expression : public DeclarationBase { public: Expression(ID id, hilti::Expression e, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(e), node::none), std::move(m)), _linkage(linkage) {} + : DeclarationBase(nodes(std::move(id), std::move(e), node::none), std::move(m)), _linkage(linkage) {} Expression(ID id, hilti::Expression e, std::optional attrs, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(e), std::move(attrs)), std::move(m)), _linkage(linkage) {} + : DeclarationBase(nodes(std::move(id), std::move(e), std::move(attrs)), std::move(m)), _linkage(linkage) {} const auto& expression() const { return child(1); } - auto attributes() const { return childs()[2].tryReferenceAs(); } + auto attributes() const { return childs()[2].tryAs(); } bool operator==(const Expression& other) const { return id() == other.id() && expression() == other.expression(); } diff --git a/hilti/toolchain/include/ast/declarations/field.h b/hilti/toolchain/include/ast/declarations/field.h new file mode 100644 index 000000000..84276fa19 --- /dev/null +++ b/hilti/toolchain/include/ast/declarations/field.h @@ -0,0 +1,112 @@ + +// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace hilti { +namespace declaration { + +/** AST node for a struct/union field. */ +class Field : public DeclarationBase { +public: + Field() : DeclarationBase({ID(""), type::unknown, node::none, node::none}, Meta()) {} + Field(ID id, hilti::Type t, std::optional attrs = {}, Meta m = Meta()) + : DeclarationBase(nodes(std::move(id), std::move(t), std::move(attrs), node::none), std::move(m)) {} + Field(ID id, ::hilti::function::CallingConvention cc, type::Function ft, std::optional attrs = {}, + Meta m = Meta()) + : DeclarationBase(nodes(std::move(id), std::move(ft), std::move(attrs), node::none), std::move(m)), _cc(cc) {} + Field(ID id, hilti::Function inline_func, std::optional attrs = {}, Meta m = Meta()) + : DeclarationBase(nodes(std::move(id), node::none, std::move(attrs), std::move(inline_func)), std::move(m)), + _cc(inline_func.callingConvention()) {} + + const auto& id() const { return child(0); } + + auto callingConvention() const { return _cc; } + auto inlineFunction() const { return childs()[3].tryAs(); } + auto attributes() const { return childs()[2].tryAs(); } + bool isResolved(type::ResolvedState* rstate) const { + if ( childs()[1].isA() ) + return true; + + if ( auto func = inlineFunction() ) + return type::detail::isResolved(func->type(), rstate); + else + return type::detail::isResolved(child(1), rstate); + } + + const hilti::Type& type() const { + if ( const auto& func = inlineFunction() ) + return func->type(); + else + return child(1); + } + + NodeRef typeRef() { + if ( inlineFunction() ) + return childs()[3].as().typeRef(); + else + return NodeRef(childs()[1]); + } + + hilti::optional_ref default_() const { + if ( auto a = AttributeSet::find(attributes(), "&default") ) { + if ( auto x = a->valueAsExpression() ) + return x->get(); + else + return {}; + } + + return {}; + } + + 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(); } + auto isNoEmit() const { return AttributeSet::find(attributes(), "&no-emit").has_value(); } + + /** Internal method for use by builder API only. */ + auto& _typeNode() { + if ( auto func = inlineFunction() ) + return const_cast<::hilti::Function&>(*func)._typeNode(); + else + return childs()[1]; + } + + void setAttributes(AttributeSet attrs) { childs()[2] = std::move(attrs); } + + bool operator==(const Field& other) const { + return id() == other.id() && type() == other.type() && attributes() == other.attributes() && _cc == other._cc; + } + + /** Implements the `Declaration` interface. */ + declaration::Linkage linkage() const { return declaration::Linkage::Struct; } + + /** Implements the `Declaration` interface. */ + bool isConstant() const { return false; } + + /** Implements the `Declaration` interface. */ + std::string displayName() const { return "struct field"; } + + /** Implements the `Declaration` interface. */ + auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{{"cc", to_string(_cc)}}; } + +private: + ::hilti::function::CallingConvention _cc = ::hilti::function::CallingConvention::Standard; +}; // namespace struct_ + +} // namespace declaration +} // namespace hilti diff --git a/hilti/toolchain/include/ast/declarations/forward.h b/hilti/toolchain/include/ast/declarations/forward.h deleted file mode 100644 index 2499d22de..000000000 --- a/hilti/toolchain/include/ast/declarations/forward.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#pragma once - -#include -#include - -#include -#include -#include - -namespace hilti { -namespace declaration { - -/** - * AST node for a declaration that forwards all methods to another one. This - * is useful to bind to nodes with declarations that may later be replaced. - * Note that this is not meant to be used as the original definition of a - * declaration itself; the code generator won't emit any corresponding - * declaration for it. - */ -class Forward : public NodeBase, public hilti::trait::isDeclaration { -public: - using Callback = std::function; - - Forward(Callback cb, Meta m = Meta()) : NodeBase(std::move(m)), _cb(std::move(cb)) {} - - auto callback() const { return _cb; } - - bool operator==(const Forward& other) const { return _cb() == other._cb(); } - - /** Implements `Declaration` interface. */ - bool isConstant() const { return _cb().isConstant(); } - /** Implements `Declaration` interface. */ - ID id() const { return _cb().id(); } - /** Implements `Declaration` interface. */ - Linkage linkage() const { return _cb().linkage(); } - /** Implements `Declaration` interface. */ - std::string displayName() const { return _cb().displayName() + " (forwarded)"; } - /** Implements `Declaration` interface. */ - auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } - - /** Implements `Node` interface. */ - auto properties() const { return node::Properties{{}}; } - -private: - Callback _cb; -}; - -} // namespace declaration -} // namespace hilti diff --git a/hilti/toolchain/include/ast/declarations/function.h b/hilti/toolchain/include/ast/declarations/function.h index 5d4f2160e..7990d433c 100644 --- a/hilti/toolchain/include/ast/declarations/function.h +++ b/hilti/toolchain/include/ast/declarations/function.h @@ -5,27 +5,58 @@ #include #include +#include #include #include #include +#include namespace hilti { namespace declaration { /** AST node for a declaration of an function. */ -class Function : public NodeBase, public hilti::trait::isDeclaration { +class Function : public DeclarationBase { public: Function(::hilti::Function function, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase({std::move(function)}, std::move(m)), _linkage(linkage) {} + : DeclarationBase(nodes(std::move(function)), std::move(m)), _linkage(linkage) {} - const auto& function() const { return child<::hilti::Function>(0); } + const ::hilti::Function& function() const { return child<::hilti::Function>(0); } + + /** + * Returns the parent declaration associated with the function, if any. For + * methods, this will the declaration of the corresponding struct type. + */ + hilti::optional_ref parent() const { + if ( _parent ) + return _parent->as(); + else + return {}; + } + + /** + * If the parent declaration associated with the function refers to a valid + * struct type, returns that type. + */ + hilti::optional_ref parentStructType() const { + if ( ! _parent ) + return {}; + + return _parent->as().type().tryAs(); + } + + void setFunction(::hilti::Function f) { childs()[0] = std::move(f); } + void setLinkage(Linkage x) { _linkage = x; } + void setParentRef(NodeRef p) { + assert(p && p->isA()); + _parent = std::move(p); + } bool operator==(const Function& other) const { return id() == other.id() && function() == other.function(); } /** Implements `Declaration` interface. */ bool isConstant() const { return true; } /** Implements `Declaration` interface. */ - ID id() const { return function().id(); } + const ID& id() const { return function().id(); } /** Implements `Declaration` interface. */ Linkage linkage() const { return _linkage; } /** Implements `Declaration` interface. */ @@ -34,23 +65,13 @@ class Function : public NodeBase, public hilti::trait::isDeclaration { auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } /** Implements `Node` interface. */ - auto properties() const { return node::Properties{{"linkage", to_string(_linkage)}}; } - - /** - * Returns a new function declaration with the body replaced. - * - * @param d original declaration - * @param b new body - * @return new declaration that's equal to original one but with the body replaced - */ - static Declaration setBody(const Function& d, const Statement& b) { - auto x = Declaration(d)._clone().as(); - x.childs()[0] = hilti::Function::setBody(x.childs()[0].as<::hilti::Function>(), b); - return x; + auto properties() const { + return node::Properties{{"linkage", to_string(_linkage)}, {"parent_type", _parent.renderedRid()}}; } private: Linkage _linkage; + NodeRef _parent; }; } // namespace declaration diff --git a/hilti/toolchain/include/ast/declarations/global-variable.h b/hilti/toolchain/include/ast/declarations/global-variable.h index 2d04b69d0..849ce3b68 100644 --- a/hilti/toolchain/include/ast/declarations/global-variable.h +++ b/hilti/toolchain/include/ast/declarations/global-variable.h @@ -10,48 +10,50 @@ #include #include #include -#include +#include namespace hilti { namespace declaration { /** AST node for a declaration of global variable. */ -class GlobalVariable : public NodeBase, public hilti::trait::isDeclaration { +class GlobalVariable : public DeclarationBase { public: - GlobalVariable(ID id, ::hilti::Type type, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(type), node::none), std::move(m)), _linkage(linkage) {} - GlobalVariable(ID id, ::hilti::Type type, std::optional init = {}, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(type), std::move(init)), std::move(m)), _linkage(linkage) {} + : DeclarationBase(nodes(std::move(id), std::move(type), std::move(init)), std::move(m)), _linkage(linkage) {} + + GlobalVariable(ID id, ::hilti::Type type, Linkage linkage = Linkage::Private, Meta m = Meta()) + : DeclarationBase(nodes(std::move(id), std::move(type), node::none), std::move(m)), _linkage(linkage) {} + + GlobalVariable(ID id, hilti::Expression init, Linkage linkage = Linkage::Private, Meta m = Meta()) + : DeclarationBase(nodes(std::move(id), node::none, std::move(init)), std::move(m)), _linkage(linkage) {} GlobalVariable(ID id, ::hilti::Type type, std::vector args, std::optional init = {}, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(type), std::move(init), std::move(args)), std::move(m)), + : DeclarationBase(nodes(std::move(id), std::move(type), std::move(init), std::move(args)), std::move(m)), _linkage(linkage) {} - GlobalVariable(ID id, hilti::Expression init, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase(nodes(std::move(id), node::none, std::move(init)), std::move(m)), _linkage(linkage) {} - - auto init() const { return childs()[2].tryReferenceAs(); } - auto typeArguments() const { return childs(3, -1); } + auto init() const { return childs()[2].tryAs(); } - ::hilti::Type type() const { - if ( auto t = childs()[1].tryAs<::hilti::Type>(); t && *t != type::unknown ) - return type::effectiveType(std::move(*t)); + const auto& type() const { + if ( auto t = childs()[1].tryAs() ) + return *t; + else { + assert(init()); + return init()->type(); + } + } - if ( auto i = init() ) - return i->type(); + auto typeArguments() const { return childs(3, -1); } - return type::unknown; + void setInit(hilti::Expression i) { childs()[2] = std::move(i); } + void setTypeArguments(std::vector args) { + auto& c = childs(); + c.erase(c.begin() + 3, c.end()); + for ( auto&& a : args ) + c.emplace_back(std::move(a)); } - /** - * Returns true if this is an `auto` variable, i.e., the type is derived - * from the initialization expression. - */ - auto hasAutomaticType() const { return ! childs()[1].isA<::hilti::Type>(); } - bool operator==(const GlobalVariable& other) const { return id() == other.id() && type() == other.type() && init() == other.init(); } @@ -70,52 +72,6 @@ class GlobalVariable : public NodeBase, public hilti::trait::isDeclaration { /** Implements `Node` interface. */ auto properties() const { return node::Properties{{"linkage", to_string(_linkage)}}; } - /** - * Returns a new global variable declaration with its type replaced. - * - * @param d original declaration - * @param b new type - * @return new declaration that's equal to original one but with the type replaced - */ - static Declaration setType(const GlobalVariable& d, std::optional t) { - auto x = Declaration(d)._clone().as(); - if ( t ) - x.childs()[1] = *t; - else - x.childs()[1] = node::none; - - return std::move(x); - } - - /** - * Returns a new global variable declaration with the init expression replaced. - * - * @param d original declaration - * @param b new init expresssion - * @return new declaration that's equal to original one but with the init expression replaced - */ - static Declaration setInit(const GlobalVariable& d, const hilti::Expression& i) { - auto x = Declaration(d)._clone().as(); - x.childs()[2] = i; - return std::move(x); - } - - /** - * Returns a new global variable declaration with the type argument expressions replaced. - * - * @param d original declaration - * @param i new init expresssion - * @return new declaration that's equal to original one but with the init expression replaced - */ - static Declaration setTypeArguments(const GlobalVariable& d, std::vector args) { - auto x = Declaration(d)._clone().as(); - x.childs() = x.childs(0, 3); - for ( auto&& a : args ) - x.childs().emplace_back(std::move(a)); - - return std::move(x); - } - private: Linkage _linkage; }; diff --git a/hilti/toolchain/include/ast/declarations/imported-module.h b/hilti/toolchain/include/ast/declarations/imported-module.h index 0b56118bd..5719436ef 100644 --- a/hilti/toolchain/include/ast/declarations/imported-module.h +++ b/hilti/toolchain/include/ast/declarations/imported-module.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include #include @@ -15,39 +16,51 @@ #include namespace hilti { + +class Unit; + namespace declaration { -/** AST node for a declaration of imported module. */ -class ImportedModule : public NodeBase, public hilti::trait::isDeclaration { +/** + * AST node for a declaration of imported module. + * + * We associate an explicit "parse extension" with an imported module that + * specifies which plugin is to parse the code into an AST. Note that this does + * *not* specify the semantics of the resulting AST. The imported AST will + * always be processed by the same plugin that is in charge of the declaration + * itself as well. This separation allows, for example, to import a piece of + * HILTI source code into a Spicy AST. + */ +class ImportedModule : public DeclarationBase { public: - ImportedModule(ID id, const std::string& search_extension, Meta m = Meta()) - : NodeBase({std::move(id)}, std::move(m)), _extension(search_extension) {} + ImportedModule(ID id, const std::string& parse_extension, Meta m = Meta()) + : DeclarationBase({std::move(id)}, std::move(m)), _parse_extension(parse_extension) {} - ImportedModule(ID id, const std::string& search_extension, std::optional search_scope, Meta m = Meta()) - : NodeBase({std::move(id)}, std::move(m)), _extension(search_extension), _scope(std::move(search_scope)) {} + ImportedModule(ID id, const std::string& parse_extension, std::optional search_scope, Meta m = Meta()) + : DeclarationBase({std::move(id)}, std::move(m)), + _parse_extension(parse_extension), + _scope(std::move(search_scope)) {} - ImportedModule(ID id, const std::string& search_extension, std::optional search_scope, + ImportedModule(ID id, const std::string& parse_extension, std::optional search_scope, std::vector search_dirs, Meta m = Meta()) - : NodeBase({std::move(id)}, std::move(m)), - _extension(search_extension), + : DeclarationBase({std::move(id)}, std::move(m)), + _parse_extension(parse_extension), _scope(std::move(search_scope)), _dirs(std::move(search_dirs)) {} ImportedModule(ID id, hilti::rt::filesystem::path path, Meta m = Meta()) - : NodeBase({std::move(id)}, std::move(m)), _path(std::move(path)) {} + : DeclarationBase({std::move(id)}, std::move(m)), _parse_extension(path.extension()), _path(path) {} - Result module() const { - if ( _module ) - return _module->template as(); + hilti::rt::filesystem::path parseExtension() const { return _parse_extension; } - return result::Error("module reference not initialized yet"); - } - - auto extension() const { return _extension; } auto path() const { return _path; } auto scope() const { return _scope; } + auto unit() const { return _unit.lock(); } const auto& searchDirectories() const { return _dirs; } + /** Sets both extensions to the same value. */ + void setUnit(std::shared_ptr unit) { _unit = std::move(unit); } + bool operator==(const ImportedModule& other) const { return id() == other.id(); } /** Implements `Declaration` interface. */ @@ -62,28 +75,11 @@ class ImportedModule : public NodeBase, public hilti::trait::isDeclaration { auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } /** Implements `Node` interface. */ - auto properties() const { - return node::Properties{{"extension", _extension.native()}, - {"path", _path.native()}, - {"scope", (_scope ? _scope->str() : std::string("-"))}}; - } - - /** - * Returns a new imported module declaration with the module reference replaced. - * - * @param d original declaration - * @param n new module reference - * @return new declaration that's equal to original one but with the module reference replaced - */ - static Declaration setModule(const ImportedModule& d, NodeRef n) { - auto x = Declaration(d)._clone().as(); - x._module = std::move(n); - return x; - } + node::Properties properties() const; private: - NodeRef _module; - hilti::rt::filesystem::path _extension; + std::weak_ptr _unit; + hilti::rt::filesystem::path _parse_extension; hilti::rt::filesystem::path _path; std::optional _scope; std::vector _dirs; diff --git a/hilti/toolchain/include/ast/declarations/local-variable.h b/hilti/toolchain/include/ast/declarations/local-variable.h index 6f7a0ec93..eb07b4a16 100644 --- a/hilti/toolchain/include/ast/declarations/local-variable.h +++ b/hilti/toolchain/include/ast/declarations/local-variable.h @@ -10,45 +10,54 @@ #include #include #include -#include +#include namespace hilti { namespace declaration { /** AST node for a declaration of a local variable. */ -class LocalVariable : public NodeBase, public hilti::trait::isDeclaration { +class LocalVariable : public DeclarationBase { public: - LocalVariable(ID id, ::hilti::Type type, std::optional init = {}, bool const_ = false, + LocalVariable(ID id, ::hilti::Type type, std::optional init, bool const_ = false, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(type), std::move(init)), std::move(m)), _const(const_) {} + : DeclarationBase(nodes(std::move(id), std::move(type), std::move(init)), std::move(m)), _const(const_) {} + + LocalVariable(ID id, ::hilti::Type type, bool const_ = false, Meta m = Meta()) + : DeclarationBase(nodes(std::move(id), std::move(type), node::none), std::move(m)), _const(const_) {} + + LocalVariable(ID id, hilti::Expression init, bool const_ = false, Meta m = Meta()) + : DeclarationBase(nodes(std::move(id), node::none, std::move(init)), std::move(m)), _const(const_) {} LocalVariable(ID id, ::hilti::Type type, std::vector args, std::optional init = {}, bool const_ = false, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(type), std::move(init), std::move(args)), std::move(m)), + : DeclarationBase(nodes(std::move(id), std::move(type), std::move(init), std::move(args)), std::move(m)), _const(const_) {} - LocalVariable(ID id, hilti::Expression init, bool const_ = false, Meta m = Meta()) - : NodeBase(nodes(std::move(id), node::none, std::move(init)), std::move(m)), _const(const_) {} + LocalVariable(ID id, bool const_ = false, Meta m = Meta()) + : DeclarationBase(nodes(std::move(id), type::auto_, node::none), std::move(m)), _const(const_) {} - auto init() const { return childs()[2].tryReferenceAs(); } - auto typeArguments() const { return childs(3, -1); } + auto init() const { return childs()[2].tryAs(); } - ::hilti::Type type() const { - if ( auto t = childs()[1].tryAs<::hilti::Type>(); t && *t != type::unknown ) - return type::effectiveType(std::move(*t)); + const auto& type() const { + if ( auto t = childs()[1].tryAs() ) + return *t; + else { + assert(init()); + return init()->type(); + } + } - if ( auto i = init() ) - return i->type(); + auto typeArguments() const { return childs(3, -1); } - return type::unknown; + void setInit(hilti::Expression i) { childs()[2] = std::move(i); } + void setType(hilti::Type t) { childs()[1] = std::move(t); } + void setTypeArguments(std::vector args) { + auto& c = childs(); + c.erase(c.begin() + 3, c.end()); + for ( auto&& a : args ) + c.emplace_back(std::move(a)); } - /** - * Returns true if this is an `auto` variable, i.e., the type is derived - * from the initialization expression. - */ - auto hasAutomaticType() const { return ! childs()[1].isA<::hilti::Type>(); } - bool operator==(const LocalVariable& other) const { return id() == other.id() && type() == other.type() && init() == other.init(); } @@ -67,57 +76,6 @@ class LocalVariable : public NodeBase, public hilti::trait::isDeclaration { /** Implements `Node` interface. */ auto properties() const { return node::Properties{{"const", _const}}; } - /** - * Returns a new local variable declaration with the type replaced. - * - * @param d original declaration - * @param b new type - * @return new declaration that's equal to original one but with the type replaced - */ - static Declaration setType(const LocalVariable& d, std::optional t) { - auto x = Declaration(d)._clone().as(); - if ( t ) - x.childs()[1] = *t; - else - x.childs()[1] = node::none; - - return std::move(x); - } - - /** - * Returns a new local variable declaration with the init expression replaced. - * - * @param d original declaration - * @param i new init expresssion - * @return new declaration that's equal to original one but with the init expression replaced - */ - static Declaration setInit(const LocalVariable& d, std::optional i) { - auto x = Declaration(d)._clone().as(); - if ( i ) - x.childs()[2] = *i; - else - x.childs()[2] = node::none; - - return std::move(x); - } - - /** - * Returns a new local variable declaration with the type argument expressions replaced. - * - * @param d original declaration - * @param i new init expresssion - * @return new declaration that's equal to original one but with the init expression replaced - */ - static Declaration setTypeArguments(const LocalVariable& d, std::vector args) { - auto x = Declaration(d)._clone().as(); - // We keep the first 3 children, which are ID, type and init expression. - x.childs() = x.childs(0, 3); - for ( auto&& a : args ) - x.childs().emplace_back(std::move(a)); - - return std::move(x); - } - private: bool _const; }; diff --git a/hilti/toolchain/include/ast/declarations/module.h b/hilti/toolchain/include/ast/declarations/module.h index fccf29bb2..0552b28e2 100644 --- a/hilti/toolchain/include/ast/declarations/module.h +++ b/hilti/toolchain/include/ast/declarations/module.h @@ -10,8 +10,8 @@ #include #include #include +#include #include -#include #include #include @@ -19,18 +19,18 @@ namespace hilti { namespace declaration { /** AST node for an AST's top-level module declaration. */ -class Module : public NodeBase, public hilti::trait::isDeclaration { +class Module : public DeclarationBase { public: /** * Constructor. * * @param root reference to root node of module's AST; must be a ``Module`` node. */ - Module(NodeRef root, Meta m = Meta()) : NodeBase(std::move(m)), _root(std::move(root)) { + Module(NodeRef root, Meta m = Meta()) : DeclarationBase(std::move(m)), _root(std::move(root)) { assert(_root && _root->isA()); } - Node& root() const { return *_root; } + const Node& root() const { return *_root; } bool operator==(const Module& other) const { return id() == other.id(); } diff --git a/hilti/toolchain/include/ast/declarations/parameter.h b/hilti/toolchain/include/ast/declarations/parameter.h index 47ade6370..613cf44e2 100644 --- a/hilti/toolchain/include/ast/declarations/parameter.h +++ b/hilti/toolchain/include/ast/declarations/parameter.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace hilti { namespace declaration { @@ -43,27 +44,35 @@ constexpr auto from_string(const std::string_view& s) { return util::enum_::from } // namespace parameter /** AST node for a declaration of a function parameter. */ -class Parameter : public NodeBase, public hilti::trait::isDeclaration { +class Parameter : public DeclarationBase { public: Parameter(ID id, hilti::Type type, parameter::Kind kind, std::optional default_, std::optional attrs, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(type), std::move(default_), std::move(attrs)), std::move(m)), + : DeclarationBase(nodes(std::move(id), type::nonConstant(std::move(type)), std::move(default_), + std::move(attrs)), + std::move(m)), _kind(kind) {} Parameter(ID id, hilti::Type type, parameter::Kind kind, std::optional default_, - bool is_struct_param, std::optional attrs, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(type), std::move(default_), std::move(attrs)), std::move(m)), + bool is_type_param, std::optional attrs, Meta m = Meta()) + : DeclarationBase(nodes(std::move(id), type::nonConstant(std::move(type)), std::move(default_), + std::move(attrs)), + std::move(m)), _kind(kind), - _is_struct_param(is_struct_param) {} + _is_type_param(is_type_param) {} - Parameter() : NodeBase({node::none, node::none, node::none}, Meta()) {} + Parameter() : DeclarationBase({node::none, type::unknown, node::none, node::none}, Meta()) {} - auto type() const { return type::effectiveType(child(1)); } - - auto attributes() const { return childs()[3].tryReferenceAs(); } - auto default_() const { return childs()[2].tryReferenceAs(); } + auto attributes() const { return childs()[3].tryAs(); } + auto default_() const { return childs()[2].tryAs(); } auto kind() const { return _kind; } - auto isStructParameter() const { return _is_struct_param; } + const auto& type() const { return child(1); } + auto isTypeParameter() const { return _is_type_param; } + auto isResolved(type::ResolvedState* rstate) const { return type::detail::isResolved(type(), rstate); } + + void setDefault(hilti::Expression e) { childs()[2] = std::move(e); } + void setIsTypeParameter() { _is_type_param = true; } + void setType(hilti::Type t) { childs()[1] = std::move(t); } bool operator==(const Parameter& other) const { return id() == other.id() && type() == other.type() && kind() == other.kind() && default_() == other.default_(); @@ -81,54 +90,11 @@ class Parameter : public NodeBase, public hilti::trait::isDeclaration { auto isEqual(const Declaration& other) const { return node::isEqual(this, other); } /** Implements `Node` interface. */ - auto properties() const { - return node::Properties{{"kind", to_string(_kind)}, {"is_struct_param", _is_struct_param}}; - } - - /** - * Returns a new parameter declaration with its type replaced. - * - * @param d original declaration - * @param b new type - * @return new declaration that's equal to original one but with the type replaced - */ - static Declaration setType(const Parameter& d, std::optional t) { - auto x = Declaration(d)._clone().as(); - if ( t ) - x.childs()[1] = *t; - else - x.childs()[1] = node::none; - - return x; - } - - /** - * Returns a new parameter declaration with the default expression replaced. - * - * @param d original declaration - * @param e new default expresssion - * @return new declaration that's equal to original one but with the default expression replaced - */ - static Declaration setDefault(const Parameter& d, const hilti::Expression& e) { - auto x = Declaration(d)._clone().as(); - x.childs()[2] = e; - return x; - } - - /** - * Returns a new parameter declaration with the is-struct-parameter option set. - * @param d original declaration - * @return new declaration that's equal to original one but with the flag set - */ - static Declaration setIsStructParameter(const Parameter& d) { - auto x = Declaration(d)._clone().as(); - x._is_struct_param = true; - return x; - } + auto properties() const { return node::Properties{{"kind", to_string(_kind)}, {"is_type_param", _is_type_param}}; } private: parameter::Kind _kind = parameter::Kind::Unknown; - bool _is_struct_param = false; + bool _is_type_param = false; }; /** Returns true if two parameters are different only by name of their ID. */ diff --git a/hilti/toolchain/include/ast/declarations/property.h b/hilti/toolchain/include/ast/declarations/property.h index d42e5d3d4..79bab0239 100644 --- a/hilti/toolchain/include/ast/declarations/property.h +++ b/hilti/toolchain/include/ast/declarations/property.h @@ -13,14 +13,14 @@ namespace hilti { namespace declaration { /** AST node for a declaration of a module property. */ -class Property : public NodeBase, public hilti::trait::isDeclaration { +class Property : public DeclarationBase { public: - Property(ID id, Meta m = Meta()) : NodeBase(nodes(std::move(id), node::none), std::move(m)) {} + Property(ID id, Meta m = Meta()) : DeclarationBase(nodes(std::move(id), node::none), std::move(m)) {} Property(ID id, hilti::Expression attr, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(attr)), std::move(m)) {} + : DeclarationBase(nodes(std::move(id), std::move(attr)), std::move(m)) {} - auto expression() const { return childs()[1].tryReferenceAs(); } + auto expression() const { return childs()[1].tryAs(); } bool operator==(const Property& other) const { return id() == other.id() && expression() == other.expression(); } diff --git a/hilti/toolchain/include/ast/declarations/type.h b/hilti/toolchain/include/ast/declarations/type.h index d5ead6858..1ab752eca 100644 --- a/hilti/toolchain/include/ast/declarations/type.h +++ b/hilti/toolchain/include/ast/declarations/type.h @@ -13,24 +13,24 @@ namespace hilti { namespace declaration { /** AST node for a type declaration. */ -class Type : public NodeBase, public hilti::trait::isDeclaration { +class Type : public DeclarationBase { public: Type(ID id, ::hilti::Type type, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase({std::move(id), std::move(type), node::none}, std::move(m)), _linkage(linkage) {} + : DeclarationBase({std::move(id), std::move(type), node::none}, std::move(m)), _linkage(linkage) {} Type(ID id, ::hilti::Type type, std::optional attrs, Linkage linkage = Linkage::Private, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(type), std::move(attrs)), std::move(m)), _linkage(linkage) {} + : DeclarationBase(nodes(std::move(id), std::move(type), std::move(attrs)), std::move(m)), _linkage(linkage) {} - auto type() const { return type::effectiveType(child(1)); } - auto attributes() const { return childs()[2].tryReferenceAs(); } + const auto& type() const { return child(1); } + NodeRef typeRef() const { return NodeRef(childs()[1]); } + auto attributes() const { return childs()[2].tryAs(); } bool isOnHeap() const { - if ( type::isOnHeap(type()) ) - return true; - - auto x = attributes(); - return x && x->find("&on-heap"); + if ( auto x = attributes() ) + return x->find("&on-heap").has_value(); + else + return false; } /** Shortcut to `type::typeID()` for the declared type. */ @@ -39,10 +39,15 @@ class Type : public NodeBase, public hilti::trait::isDeclaration { /** Shortcut to `type::cxxID()` for the declared type. */ auto cxxID() const { return childs()[1].as().cxxID(); } + /** Shortcut to `type::resolvedID()` for the declared type. */ + auto resolvedID() const { return childs()[1].as().resolvedID(); } + + void setType(::hilti::Type t) { childs()[1] = std::move(t); } + bool operator==(const Type& other) const { return id() == other.id() && type() == other.type(); } /** Internal method for use by builder API only. */ - auto& _typeNode() { return childs()[1]; } + // auto& _typeNode() { return childs()[1]; } /** Implements `Declaration` interface. */ bool isConstant() const { return true; } @@ -58,19 +63,6 @@ class Type : public NodeBase, public hilti::trait::isDeclaration { /** Implements `Node` interface. */ auto properties() const { return node::Properties{{"linkage", to_string(_linkage)}}; } - /** - * Returns a new type declaration with the resolving type replaced. - * - * @param d original declaration - * @param t new type - * @return new declaration that's equal to original one but with the resolving type replaced - */ - static Type setType(const Type& d, const ::hilti::Type& t) { - auto x = Declaration(d)._clone().as(); - x.childs()[1] = t; - return x; - } - private: Linkage _linkage; }; diff --git a/hilti/toolchain/include/ast/detail/operator-registry.h b/hilti/toolchain/include/ast/detail/operator-registry.h index aa9cf2b93..42102b27c 100644 --- a/hilti/toolchain/include/ast/detail/operator-registry.h +++ b/hilti/toolchain/include/ast/detail/operator-registry.h @@ -21,15 +21,10 @@ class Registry { using OperatorMap = std::map>; /** Returns a map of all available operators. */ - const OperatorMap& all() const { return _operators; } + const auto& all() const { return _operators; } /** Returns a map of all available operators. */ - const std::vector& allOfKind(Kind kind) const { - if ( auto x = _operators.find(kind); x != _operators.end() ) - return x->second; - - logger().internalError("unregistered operator requested in allOfKind()"); - } + const auto& allOfKind(Kind kind) const { return _operators.at(kind); } /** Registers an Operator as available. */ void register_(Kind kind, Operator info) { _operators[kind].push_back(std::move(info)); } @@ -64,7 +59,7 @@ class Register { Register(Kind k, Operator c) { Registry::singleton().register_(k, std::move(c)); } }; -inline auto registry() { return Registry::singleton(); } +inline const auto& registry() { return Registry::singleton(); } } // namespace operator_ } // namespace hilti diff --git a/hilti/toolchain/include/ast/expression.api b/hilti/toolchain/include/ast/expression.api index 439c80e84..8cccb065e 100644 --- a/hilti/toolchain/include/ast/expression.api +++ b/hilti/toolchain/include/ast/expression.api @@ -3,7 +3,7 @@ /** Interface for HILTI expressions. */ class Expression(hilti::trait::isExpression) : hilti::trait::isNode { /** Returns the expressions's HILTI type when evaluated. */ - hilti::Type type() const; + const hilti::Type& type() const; /** * Returns true if the constructor's value is a value that will never @@ -39,11 +39,5 @@ class Expression(hilti::trait::isExpression) : hilti::trait::isNode { void setMeta(Meta m); /** Implements the `Node` interface. */ - const NodeRef& originalNode() const; - - /** Implements the `Node` interface. */ - void setOriginalNode(const NodeRef& n); - - /** Implements the `Node` interface. */ - void clearCache(); + bool pruneWalk() const; }; diff --git a/hilti/toolchain/include/ast/expression.h b/hilti/toolchain/include/ast/expression.h index 48e842d60..a6bb68da5 100644 --- a/hilti/toolchain/include/ast/expression.h +++ b/hilti/toolchain/include/ast/expression.h @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -38,6 +39,61 @@ inline bool operator!=(const Expression& e1, const Expression& e2) { return ! (e } // namespace detail +/** + * Returns true if the type of an expression has been resolved. + * + * @param e expression to check + * @param rstate internal parameter, leave unset + */ +inline bool isResolved(const detail::Expression& e, type::ResolvedState* rstate = nullptr) { + return type::detail::isResolved(e.type(), rstate); +} + +/** + * Returns true if the types of all expressions in a vector have been resolved. + * + * @param exprs expressions expressions to check + * @param rstate internal parameter, leave unset + */ +inline bool isResolved(const std::vector& exprs, type::ResolvedState* rstate = nullptr) { + for ( const auto& e : exprs ) { + if ( ! type::detail::isResolved(e.type(), rstate) ) + return false; + } + + return true; +} + +/** + * Returns true if the types of all expressions in a range have been resolved. + * + * @param exprs expressions expressions to check + * @param rstate internal parameter, leave unset + */ +inline bool isResolved(const hilti::node::Range& exprs, type::ResolvedState* rstate = nullptr) { + for ( const auto& e : exprs ) { + if ( ! type::detail::isResolved(e.type(), rstate) ) + return false; + } + + return true; +} + +/** + * Returns true if the types of all expressions in a set have been resolved. + * + * @param exprs expressions expressions to check + * @param rstate internal parameter, leave unset + */ +inline bool isResolved(const hilti::node::Set& exprs, type::ResolvedState* rstate = nullptr) { + for ( const auto& e : exprs ) { + if ( ! type::detail::isResolved(e.type(), rstate) ) + return false; + } + + return true; +} + } // namespace expression using Expression = expression::detail::Expression; diff --git a/hilti/toolchain/include/ast/expressions/assign.h b/hilti/toolchain/include/ast/expressions/assign.h index 6e8070cec..1c9af6fce 100644 --- a/hilti/toolchain/include/ast/expressions/assign.h +++ b/hilti/toolchain/include/ast/expressions/assign.h @@ -15,8 +15,11 @@ class Assign : public NodeBase, public trait::isExpression { Assign(Expression target, Expression src, Meta m = Meta()) : NodeBase({std::move(target), std::move(src)}, std::move(m)) {} - const auto& target() const { return child(0); } const auto& source() const { return child(1); } + const auto& target() const { return child(0); } + + void setSource(hilti::Expression c) { childs()[1] = std::move(c); } + void setTarget(hilti::Expression c) { childs()[0] = std::move(c); } bool operator==(const Assign& other) const { return target() == other.target() && source() == other.source(); } @@ -25,7 +28,7 @@ class Assign : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return isLhs(); } /** Implements `Expression` interface. */ - auto type() const { return type::effectiveType(target().type()); } + const auto& type() const { return target().type(); } /** Implements `Expression` interface. */ auto isConstant() const { return false; } /** Implements `Expression` interface. */ @@ -33,32 +36,6 @@ class Assign : public NodeBase, public trait::isExpression { /** Implements `Node` interface. */ auto properties() const { return node::Properties{}; } - - /** - * Returns a new assign expression with the target expression replaced. - * - * @param d original expression - * @param t new target expresssion - * @return new expression that's equal to original one but with the target expression replaced - */ - static Expression setTarget(const Assign& e, const Expression& t) { - auto x = Expression(e)._clone().as(); - x.childs()[0] = t; - return x; - } - - /** - * Returns a new assign expression with the source expression replaced. - * - * @param d original expression - * @param t new source expresssion - * @return new expression that's equal to original one but with the source expression replaced - */ - static Expression setSource(const Assign& e, const Expression& s) { - auto x = Expression(e)._clone().as(); - x.childs()[1] = s; - return x; - } }; } // namespace expression diff --git a/hilti/toolchain/include/ast/expressions/builtin-function.h b/hilti/toolchain/include/ast/expressions/builtin-function.h index 7070282e3..fe6f9000c 100644 --- a/hilti/toolchain/include/ast/expressions/builtin-function.h +++ b/hilti/toolchain/include/ast/expressions/builtin-function.h @@ -31,48 +31,16 @@ class BuiltinFunction : public NodeBase, public trait::isExpression { _cxxname(std::move(cxxname)), _num_parameters(parameters.size()) {} - /** Implements `Expression` interface. */ - auto type() const { return child(0); }; - - /** Implements `Expression` interface. */ - bool isConstant() const { return false; } - - /** Implements `Expression` interface. */ - auto isEqual(const Expression& other) const { return node::isEqual(this, other); } - - /** Implements `Expression` interface. */ - auto isLhs() const { return false; } - - /** Implements `Expression` interface. */ - auto isTemporary() const { return true; } - - /** Implements `Expression` interface. */ - auto properties() const { return node::Properties{{"name", _name}}; } - - const auto& name() const { return _name; } - - const auto& cxxname() const { return _cxxname; } - auto arguments() const { return childs(_num_parameters + 1, -1); } - const auto parameters() const { return childs(1, _num_parameters); } + const auto& cxxname() const { return _cxxname; } + const auto& name() const { return _name; } - /** - * Returns a new builtin function node with the arguments replaced. - * - * @param d original builtin function - * @param args new arguments - * @return new builtin function now that is equal to the original one but with the arguments replaced - */ - static BuiltinFunction setArguments(const BuiltinFunction& d, std::vector args) { - auto x = Expression(d)._clone().as(); - - x.childs().clear(); + void setArguments(std::vector args) { + childs().clear(); for ( auto& a : args ) - x.childs().emplace_back(std::move(a)); - - return x; + childs().emplace_back(std::move(a)); } friend bool operator==(const BuiltinFunction& lhs, const BuiltinFunction& rhs) { @@ -80,6 +48,19 @@ class BuiltinFunction : public NodeBase, public trait::isExpression { lhs.arguments() == rhs.arguments(); } + /** Implements `Expression` interface. */ + const Type& type() const { return child(0); }; + /** Implements `Expression` interface. */ + bool isConstant() const { return false; } + /** Implements `Expression` interface. */ + auto isEqual(const Expression& other) const { return node::isEqual(this, other); } + /** Implements `Expression` interface. */ + auto isLhs() const { return false; } + /** Implements `Expression` interface. */ + auto isTemporary() const { return true; } + /** Implements `Expression` interface. */ + auto properties() const { return node::Properties{{"name", _name}}; } + private: std::string _name; std::string _cxxname; diff --git a/hilti/toolchain/include/ast/expressions/coerced.h b/hilti/toolchain/include/ast/expressions/coerced.h index 5b66284cd..daaa5334c 100644 --- a/hilti/toolchain/include/ast/expressions/coerced.h +++ b/hilti/toolchain/include/ast/expressions/coerced.h @@ -12,7 +12,8 @@ namespace expression { /** AST node for an expression that's being coerced from one type to another. */ class Coerced : public NodeBase, public trait::isExpression { public: - Coerced(Expression e, Type t, Meta m = Meta()) : NodeBase({std::move(e), std::move(t)}, std::move(m)) {} + Coerced(Expression e, Type t, Meta m = Meta()) + : NodeBase({std::move(e), type::nonConstant(std::move(t))}, std::move(m)) {} const auto& expression() const { return child(0); } @@ -23,7 +24,7 @@ class Coerced : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return true; } /** Implements `Expression` interface. */ - Type type() const { return type::nonConstant(type::effectiveType(child(1))); } + const Type& type() const { return child(1); } /** Implements `Expression` interface. */ auto isConstant() const { return expression().isConstant(); } /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/expressions/ctor.h b/hilti/toolchain/include/ast/expressions/ctor.h index 8299a5784..833ad6a4a 100644 --- a/hilti/toolchain/include/ast/expressions/ctor.h +++ b/hilti/toolchain/include/ast/expressions/ctor.h @@ -24,7 +24,7 @@ class Ctor : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return ctor().isTemporary(); } /** Implements `Expression` interface. */ - auto type() const { return type::effectiveType(ctor().type()); } + const auto& type() const { return ctor().type(); } /** Implements `Expression` interface. */ auto isConstant() const { return ctor().isConstant(); } /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/expressions/deferred.h b/hilti/toolchain/include/ast/expressions/deferred.h index be1c063b6..c947e9575 100644 --- a/hilti/toolchain/include/ast/expressions/deferred.h +++ b/hilti/toolchain/include/ast/expressions/deferred.h @@ -5,6 +5,7 @@ #include #include +#include #include namespace hilti { @@ -18,23 +19,30 @@ namespace expression { */ class Deferred : public NodeBase, public trait::isExpression { public: - Deferred(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + Deferred(Expression e, Meta m = Meta()) : NodeBase(nodes(std::move(e), type::auto_), std::move(m)) {} Deferred(Expression e, bool catch_exception, Meta m = Meta()) - : NodeBase({std::move(e)}, std::move(m)), _catch_exception(catch_exception) {} + : NodeBase(nodes(e, type::auto_), m), _catch_exception(catch_exception) {} const auto& expression() const { return child(0); } bool catchException() const { return _catch_exception; } - bool operator==(const Deferred& other) const { return expression() == other.expression(); } + void setType(Type t) { + if ( _catch_exception ) + childs()[1] = type::Result(std::move(t)); + else + childs()[1] = std::move(t); + } + + bool operator==(const Deferred& other) const { + return expression() == other.expression() && _catch_exception == other._catch_exception; + } /** Implements `Expression` interface. */ bool isLhs() const { return false; } /** Implements `Expression` interface. */ bool isTemporary() const { return true; } /** Implements `Expression` interface. */ - auto type() const { - return _catch_exception ? Type(type::Result(expression().type(), meta())) : expression().type(); - } + const auto& type() const { return child(1); } /** Implements `Expression` interface. */ auto isConstant() const { return expression().isConstant(); } /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/expressions/grouping.h b/hilti/toolchain/include/ast/expressions/grouping.h index 3a65267e1..7aa08e9e7 100644 --- a/hilti/toolchain/include/ast/expressions/grouping.h +++ b/hilti/toolchain/include/ast/expressions/grouping.h @@ -5,6 +5,7 @@ #include #include +#include namespace hilti { namespace expression { @@ -12,7 +13,7 @@ namespace expression { /** AST node for grouping another expression inside parentheses. */ class Grouping : public NodeBase, public trait::isExpression { public: - Grouping(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + Grouping(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, m) {} const auto& expression() const { return child(0); } @@ -23,7 +24,7 @@ class Grouping : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return expression().isTemporary(); } /** Implements `Expression` interface. */ - auto type() const { return type::effectiveType(expression().type()); } + const auto& type() const { return expression().type(); } /** Implements `Expression` interface. */ auto isConstant() const { return expression().isConstant(); } /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/expressions/id.h b/hilti/toolchain/include/ast/expressions/id.h index 325052683..cfcd9a35c 100644 --- a/hilti/toolchain/include/ast/expressions/id.h +++ b/hilti/toolchain/include/ast/expressions/id.h @@ -7,9 +7,9 @@ #include #include #include +#include #include -#include -#include +#include #include namespace hilti { @@ -18,18 +18,11 @@ namespace expression { /** AST node for a expression representing a resolved ID. */ class ResolvedID : public NodeBase, hilti::trait::isExpression { public: - ResolvedID(ID id, NodeRef r, Meta m = Meta()) : NodeBase({std::move(id)}, std::move(m)), _node(std::move(r)) { - assert(_node && _node->isA()); - } + ResolvedID(ID id, NodeRef d, Meta m = Meta()) : NodeBase(nodes(std::move(id)), m), _d(std::move(d)) {} const auto& id() const { return child(0); } - const auto& declaration() const { - assert(_node); - return _node->as(); - } - - bool isValid() const { return static_cast(_node); } - auto rid() const { return _node.rid(); } + const auto& declaration() const { return _d->as(); } + const auto& declarationRef() const { return _d; } bool operator==(const ResolvedID& other) const { return id() == other.id() && declaration() == other.declaration(); @@ -40,25 +33,23 @@ class ResolvedID : public NodeBase, hilti::trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return false; } /** Implements `Expression` interface. */ - Type type() const; + const Type& type() const; /** Implements `Expression` interface. */ bool isConstant() const; /** Implements `Expression` interface. */ auto isEqual(const Expression& other) const { return node::isEqual(this, other); } /** Implements `Node` interface. */ - auto properties() const { - return _node ? node::Properties{{"resolved", _node.renderedRid()}} : node::Properties{{}}; - } + auto properties() const { return node::Properties{{"rid", _d.rid()}}; } private: - NodeRef _node; + NodeRef _d; }; /** AST node for a expression representing an unresolved ID. */ class UnresolvedID : public NodeBase, hilti::trait::isExpression { public: - UnresolvedID(ID id, Meta m = Meta()) : NodeBase({std::move(id)}, std::move(m)) {} + UnresolvedID(ID id, Meta m = Meta()) : NodeBase(nodes(std::move(id), type::auto_), std::move(m)) {} const auto& id() const { return child(0); } @@ -67,7 +58,7 @@ class UnresolvedID : public NodeBase, hilti::trait::isExpression { // Expression interface. bool isLhs() const { return true; } bool isTemporary() const { return false; } - Type type() const { return type::unknown; } + const Type& type() const { return child(1); } auto isConstant() const { return false; } auto isEqual(const Expression& other) const { return node::isEqual(this, other); } diff --git a/hilti/toolchain/include/ast/expressions/keyword.h b/hilti/toolchain/include/ast/expressions/keyword.h index 4064f55d2..0cbfef901 100644 --- a/hilti/toolchain/include/ast/expressions/keyword.h +++ b/hilti/toolchain/include/ast/expressions/keyword.h @@ -4,7 +4,10 @@ #include +#include +#include #include +#include #include #include @@ -34,16 +37,10 @@ constexpr auto to_string(Kind m) { return util::enum_::to_string(m, detail::kind /** AST node for an expression representing a reservered keyword. */ class Keyword : public NodeBase, public hilti::trait::isExpression { public: - Keyword(keyword::Kind kind, Meta m = Meta()) : NodeBase({type::unknown}, std::move(m)), _kind(kind) {} - Keyword(keyword::Kind kind, Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)), _kind(kind) {} - Keyword(keyword::Kind kind, NodeRef d, Meta m = Meta()) - : NodeBase({node::none}, std::move(m)), _kind(kind), _decl(d) {} + Keyword(keyword::Kind kind, Meta m = Meta()) : NodeBase(nodes(type::auto_), std::move(m)), _kind(kind) {} + Keyword(keyword::Kind kind, Type t, Meta m = Meta()) : NodeBase(nodes(std::move(t)), std::move(m)), _kind(kind) {} keyword::Kind kind() const { return _kind; } - bool hasType() const { return _decl.has_value() || ! childs()[0].isA(); } - - /** Returns true if the keyword expression has been resolved to a known type. */ - bool isSet() const { return (! childs()[0].isA()) || _decl.has_value(); } bool operator==(const Keyword& other) const { return _kind == other._kind && type() == other.type(); } @@ -52,41 +49,25 @@ class Keyword : public NodeBase, public hilti::trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return false; } /** Implements `Expression` interface. */ - Type type() const { - auto t = type::effectiveType(_decl ? (**_decl).as().type() : childs()[0].as()); - - if ( _kind == keyword::Kind::Self ) - t = type::removeFlags(t, type::Flag::Constant); - - return t; - } + const Type& type() const { return childs()[0].as(); } /** Implements `Expression` interface. */ auto isConstant() const { return false; } - /** Implements `Expression` interface. */ auto isEqual(const Expression& other) const { return node::isEqual(this, other); } /** Implements `Node` interface. */ auto properties() const { return node::Properties{{"kind", to_string(_kind)}}; } - /** - * Returns a new keyword expression with the resulting type replaced. - * - * @param d original expression - * @param t new type - * @return new expression that's equal to original one but with the resulting type replaced - */ - static Expression setType(const Keyword& e, const Type& t) { - auto x = Expression(e)._clone().as(); - x.childs()[0] = t; - x._decl = {}; - return x; + /** Helper to create `$$` a declaration of a given type. */ + static Declaration createDollarDollarDeclaration(Type t) { + Expression kw = hilti::expression::Keyword(hilti::expression::keyword::Kind::DollarDollar, + hilti::type::pruneWalk(std::move(t))); + return hilti::declaration::Expression("__dd", std::move(kw), hilti::declaration::Linkage::Private); } private: keyword::Kind _kind; - std::optional _decl; }; } // namespace expression diff --git a/hilti/toolchain/include/ast/expressions/list-comprehension.h b/hilti/toolchain/include/ast/expressions/list-comprehension.h index 6ebb16ed5..e97ec9b8f 100644 --- a/hilti/toolchain/include/ast/expressions/list-comprehension.h +++ b/hilti/toolchain/include/ast/expressions/list-comprehension.h @@ -5,8 +5,8 @@ #include #include +#include #include -#include #include #include @@ -17,15 +17,16 @@ namespace expression { class ListComprehension : public NodeBase, public trait::isExpression { public: ListComprehension(Expression input, Expression output, ID id, std::optional cond, Meta m = Meta()) - : NodeBase(nodes(std::move(input), std::move(output), std::move(id), std::move(cond), type::unknown), - std::move(m)) { - _computeTypes(); - } + : NodeBase(nodes(std::move(input), std::move(output), + declaration::LocalVariable(std::move(id), type::auto_, true, id.meta()), std::move(cond), + type::List(type::auto_, m)), + std::move(m)) {} const auto& input() const { return child(0); } const auto& output() const { return child(1); } - const auto& id() const { return child(2); } - auto condition() const { return childs()[3].tryReferenceAs(); } + const auto& local() const { return child(2); } + auto localRef() const { return NodeRef(childs()[2]); } + auto condition() const { return childs()[3].tryAs(); } /** * Returns the output expressions's scope. Note that the scope is shared @@ -33,8 +34,11 @@ class ListComprehension : public NodeBase, public trait::isExpression { */ IntrusivePtr scope() const { return childs()[1].scope(); } + void setLocalType(Type t) { childs()[2].as().setType(std::move(t)); } + void setElementType(const Type x) { childs()[4] = type::List(std::move(x)); } + bool operator==(const ListComprehension& other) const { - return input() == other.input() && output() == other.output() && id() == other.id() && + return input() == other.input() && output() == other.output() && local() == other.local() && condition() == other.condition(); } @@ -43,8 +47,7 @@ class ListComprehension : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return true; } /** Implements `Expression` interface. */ - Type type() const { return childs()[4].as(); } - Node& typeNode() { return childs()[4]; } + const Type& type() const { return childs()[4].as(); } /** Implements `Expression` interface. */ auto isConstant() const { return input().isConstant(); } @@ -53,9 +56,6 @@ class ListComprehension : public NodeBase, public trait::isExpression { /** Implements `Node` interface. */ auto properties() const { return node::Properties{}; } - -private: - void _computeTypes() {} }; } // namespace expression diff --git a/hilti/toolchain/include/ast/expressions/logical-and.h b/hilti/toolchain/include/ast/expressions/logical-and.h index 9596d7add..85acfbfca 100644 --- a/hilti/toolchain/include/ast/expressions/logical-and.h +++ b/hilti/toolchain/include/ast/expressions/logical-and.h @@ -14,11 +14,14 @@ namespace expression { class LogicalAnd : public NodeBase, public trait::isExpression { public: LogicalAnd(Expression op0, Expression op1, Meta m = Meta()) - : NodeBase({std::move(op0), std::move(op1)}, std::move(m)) {} + : NodeBase(nodes(std::move(op0), std::move(op1), type::Bool(m)), m) {} const auto& op0() const { return child(0); } const auto& op1() const { return child(1); } + void setOp0(const Expression& op) { childs()[0] = std::move(op); } + void setOp1(const Expression& op) { childs()[1] = std::move(op); } + bool operator==(const LogicalAnd& other) const { return op0() == other.op0() && op1() == other.op1(); } /** Implements `Expression` interface. */ @@ -26,7 +29,7 @@ class LogicalAnd : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return true; } /** Implements `Expression` interface. */ - auto type() const { return type::Bool(); } + const auto& type() const { return child(2); } /** Implements `Expression` interface. */ auto isConstant() const { return op0().isConstant() && op1().isConstant(); } /** Implements `Expression` interface. */ @@ -34,32 +37,6 @@ class LogicalAnd : public NodeBase, public trait::isExpression { /** Implements `Node` interface. */ auto properties() const { return node::Properties{}; } - - /** - * Returns a new "and" expression with the first operand expression replaced. - * - * @param e original expression - * @param op new operand expresssion - * @return new expression that's equal to original one but with the operand replaced - */ - static Expression setOp0(const LogicalAnd& e, const Expression& op) { - auto x = Expression(e)._clone().as(); - x.childs()[0] = op; - return x; - } - - /** - * Returns a new "and" expression with the second operand expression replaced. - * - * @param e original expression - * @param op new operand expresssion - * @return new expression that's equal to original one but with the operand replaced - */ - static Expression setOp1(const LogicalAnd& e, const Expression& op) { - auto x = Expression(e)._clone().as(); - x.childs()[1] = op; - return x; - } }; } // namespace expression diff --git a/hilti/toolchain/include/ast/expressions/logical-not.h b/hilti/toolchain/include/ast/expressions/logical-not.h index 343ce64d8..389856d89 100644 --- a/hilti/toolchain/include/ast/expressions/logical-not.h +++ b/hilti/toolchain/include/ast/expressions/logical-not.h @@ -13,10 +13,12 @@ namespace expression { /** AST node for a logical "not" expression. */ class LogicalNot : public NodeBase, public trait::isExpression { public: - LogicalNot(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + LogicalNot(Expression e, Meta m = Meta()) : NodeBase(nodes(std::move(e), type::Bool(m)), m) {} const auto& expression() const { return child(0); } + void setExpression(const Expression op) { childs()[0] = std::move(op); } + bool operator==(const LogicalNot& other) const { return expression() == other.expression(); } /** Implements `Expression` interface. */ @@ -24,7 +26,7 @@ class LogicalNot : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return true; } /** Implements `Expression` interface. */ - auto type() const { return type::Bool(); } + const auto& type() const { return child(1); } /** Implements `Expression` interface. */ auto isConstant() const { return expression().isConstant(); } /** Implements `Expression` interface. */ @@ -32,19 +34,6 @@ class LogicalNot : public NodeBase, public trait::isExpression { /** Implements `Node` interface. */ auto properties() const { return node::Properties{}; } - - /** - * Returns a new "not" expression with the operand expression replaced. - * - * @param e original expression - * @param op new operand expresssion - * @return new expression that's equal to original one but with the operand replaced - */ - static Expression setExpression(const LogicalNot& e, const Expression& op) { - auto x = Expression(e)._clone().as(); - x.childs()[0] = op; - return x; - } }; } // namespace expression diff --git a/hilti/toolchain/include/ast/expressions/logical-or.h b/hilti/toolchain/include/ast/expressions/logical-or.h index 17a306f29..abd42c291 100644 --- a/hilti/toolchain/include/ast/expressions/logical-or.h +++ b/hilti/toolchain/include/ast/expressions/logical-or.h @@ -14,11 +14,14 @@ namespace expression { class LogicalOr : public NodeBase, public trait::isExpression { public: LogicalOr(Expression op0, Expression op1, Meta m = Meta()) - : NodeBase({std::move(op0), std::move(op1)}, std::move(m)) {} + : NodeBase(nodes(std::move(op0), std::move(op1), type::Bool(m)), m) {} const auto& op0() const { return child(0); } const auto& op1() const { return child(1); } + void setOp0(const Expression& op) { childs()[0] = std::move(op); } + void setOp1(const Expression& op) { childs()[1] = std::move(op); } + bool operator==(const LogicalOr& other) const { return op0() == other.op0() && op1() == other.op1(); } /** Implements `Expression` interface. */ @@ -26,7 +29,7 @@ class LogicalOr : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return true; } /** Implements `Expression` interface. */ - auto type() const { return type::Bool(); } + const auto& type() const { return child(2); } /** Implements `Expression` interface. */ auto isConstant() const { return op0().isConstant() && op1().isConstant(); } /** Implements `Expression` interface. */ @@ -34,32 +37,6 @@ class LogicalOr : public NodeBase, public trait::isExpression { /** Implements `Node` interface. */ auto properties() const { return node::Properties{}; } - - /** - * Returns a new "or" expression with the first operand expression replaced. - * - * @param e original expression - * @param op new operand expresssion - * @return new expression that's equal to original one but with the operand replaced - */ - static Expression setOp0(const LogicalOr& e, const Expression& op) { - auto x = Expression(e)._clone().as(); - x.childs()[0] = op; - return x; - } - - /** - * Returns a new "or" expression with the second operand expression replaced. - * - * @param e original expression - * @param op new operand expresssion - * @return new expression that's equal to original one but with the operand replaced - */ - static Expression setOp1(const LogicalOr& e, const Expression& op) { - auto x = Expression(e)._clone().as(); - x.childs()[1] = op; - return x; - } }; } // namespace expression diff --git a/hilti/toolchain/include/ast/expressions/member.h b/hilti/toolchain/include/ast/expressions/member.h index e8e5230e6..d0b75ab08 100644 --- a/hilti/toolchain/include/ast/expressions/member.h +++ b/hilti/toolchain/include/ast/expressions/member.h @@ -4,9 +4,11 @@ #include +#include #include +#include #include -#include +#include #include #include @@ -16,12 +18,10 @@ namespace expression { /** AST node for a member-access expression. */ class Member : public NodeBase, hilti::trait::isExpression { public: - Member(ID id, Meta m = Meta()) : NodeBase({id, Type(type::Member(std::move(id))), node::none}, std::move(m)) {} - Member(ID id, Type member_type, Meta m = Meta()) - : NodeBase({id, std::move(member_type), Type(type::Member(std::move(id)))}, std::move(m)) {} + Member(ID id, Meta m = Meta()) : NodeBase({id, Type(type::Member(std::move(id)))}, std::move(m)) {} + Member(ID id, Type member_type, Meta m = Meta()) : NodeBase({id, std::move(member_type)}, std::move(m)) {} const auto& id() const { return child(0); } - auto memberType() const { return type::effectiveOptionalType(childs()[1].tryAs()); } bool operator==(const Member& other) const { return id() == other.id() && type() == other.type(); } @@ -30,7 +30,7 @@ class Member : public NodeBase, hilti::trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return false; } /** Implements `Expression` interface. */ - Type type() const { return type::effectiveType(child(1)); } + const Type& type() const { return child(1); } /** Implements `Expression` interface. */ auto isConstant() const { return true; } /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/expressions/move.h b/hilti/toolchain/include/ast/expressions/move.h index 76dea1914..98cff128d 100644 --- a/hilti/toolchain/include/ast/expressions/move.h +++ b/hilti/toolchain/include/ast/expressions/move.h @@ -23,7 +23,7 @@ class Move : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return true; } /** Implements `Expression` interface. */ - auto type() const { return expression().type(); } + const auto& type() const { return expression().type(); } /** Implements `Expression` interface. */ auto isConstant() const { return expression().isConstant(); } /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/expressions/pending-coerced.h b/hilti/toolchain/include/ast/expressions/pending-coerced.h index 266c41122..0af1aa35d 100644 --- a/hilti/toolchain/include/ast/expressions/pending-coerced.h +++ b/hilti/toolchain/include/ast/expressions/pending-coerced.h @@ -28,7 +28,7 @@ class PendingCoerced : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return expression().isTemporary(); } /** Implements `Expression` interface. */ - Type type() const { return type::effectiveType(child(1)); } + const Type& type() const { return child(1); } /** Implements `Expression` interface. */ auto isConstant() const { return expression().isConstant(); } /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/expressions/resolved-operator.api b/hilti/toolchain/include/ast/expressions/resolved-operator.api index bef72df57..939a4bb70 100644 --- a/hilti/toolchain/include/ast/expressions/resolved-operator.api +++ b/hilti/toolchain/include/ast/expressions/resolved-operator.api @@ -6,28 +6,28 @@ class ResolvedOperator(trait::isResolvedOperator) : trait::isExpression { Operator operator_() const; /** Returns the type of the operator expressions's result. */ - hilti::Type result() const; + const hilti::Type& result() const; /** Returns the operator expression's operands. */ - std::vector operands() const; + hilti::node::Range operands() const; /** * Returns the operator expression's 1st operand. * @exception `std::out_of_range`` if the expression doesn't have that operand */ - Expression op0() const; + const Expression& op0() const; /** * Returns the operator expression's 2nd operand. * @exception `std::out_of_range`` if the expression doesn't have that operand */ - Expression op1() const; + const Expression& op1() const; /** * Returns the operator expression's 3rd operand * @exception `std::out_of_range`` if the expression doesn't have that operand */ - Expression op2() const; + const Expression& op2() const; /** Returns true if the operator expression has at least one operand. */ bool hasOp0(); @@ -52,7 +52,7 @@ class ResolvedOperator(trait::isResolvedOperator) : trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const; /** Implements the `Expression` interface. */ - hilti::Type type() const; + const hilti::Type& type() const; /** Implements the `Expression` interface. */ bool isEqual(const Expression& other) const; /** Implements the `Expression` interface. */ @@ -69,9 +69,5 @@ class ResolvedOperator(trait::isResolvedOperator) : trait::isExpression { /** Implements the `Node` interface. */ void setMeta(Meta m); /** Implements the `Node` interface. */ - const NodeRef& originalNode() const; - /** Implements the `Node` interface. */ - void setOriginalNode(const NodeRef& n); - /** Implements the `Node` interface. */ - void clearCache(); + bool pruneWalk() const; }; diff --git a/hilti/toolchain/include/ast/expressions/resolved-operator.h b/hilti/toolchain/include/ast/expressions/resolved-operator.h index a9ce111b1..50deeca9b 100644 --- a/hilti/toolchain/include/ast/expressions/resolved-operator.h +++ b/hilti/toolchain/include/ast/expressions/resolved-operator.h @@ -9,7 +9,7 @@ #include #include #include -#include +#include namespace hilti { @@ -32,21 +32,6 @@ inline std::ostream& operator<<(std::ostream& out, ResolvedOperator i) { return using ResolvedOperator = resolved_operator::detail::ResolvedOperator; using resolved_operator::detail::to_node; -namespace detail { - -// Generally we want to compute the result type of operators dynamically -// because updates to their child nodes may lead to changes. For unresolved -// IDs, however, we need to store the type in the AST for it get to resolved. -// This function implements that distinction. -inline Type type_to_store(Type t) { - if ( t.isA() ) - return t; - else - return type::unknown; -} - -} // namespace detail - /** * Base class for an AST node for an expression representing a resolved operator usage. * @@ -55,32 +40,17 @@ inline Type type_to_store(Type t) { class ResolvedOperatorBase : public NodeBase, public trait::isExpression, public trait::isResolvedOperator { public: ResolvedOperatorBase(const Operator& op, const std::vector& operands, Meta meta = Meta()) - : NodeBase(nodes(detail::type_to_store(op.result(operands)), operands), std::move(meta)), _operator(op) {} + : NodeBase(nodes(node::none, operands), std::move(meta)), _operator(op) { + // Must not have instantiated this before we know the result type. + childs()[0] = type::pruneWalk(op.result(childs(1, -1))); + } const auto& operator_() const { return _operator; } auto kind() const { return _operator.kind(); } // ResolvedOperator interface with common implementation. - const auto& operands() const { - if ( _cache.operands.empty() ) - _cache.operands = childs(1, -1); - - return _cache.operands; - } - - const auto& result() const { - if ( _cache.result ) - return *_cache.result; - - if ( ! childs()[0].isA() ) - _cache.result = child(0); - else - // If the result wasn't stored at instantiation time, try again. - _cache.result = _operator.result(operands()); - - return *_cache.result; - } - + auto operands() const { return childs(1, -1); } + const auto& result() const { return child(0); } const auto& op0() const { return child(1); } const auto& op1() const { return child(2); } const auto& op2() const { return child(3); } @@ -100,7 +70,7 @@ class ResolvedOperatorBase : public NodeBase, public trait::isExpression, public /** Implements `Expression` interface. */ bool isTemporary() const { return isLhs(); } /** Implements `Expression` interface. */ - auto type() const { return type::effectiveType(result()); } + const Type& type() const { return result(); } /** Implements `Expression` interface. */ auto isEqual(const Expression& other) const { return node::isEqual(this, other); } @@ -109,19 +79,9 @@ class ResolvedOperatorBase : public NodeBase, public trait::isExpression, public /** Implements `Node` interface. */ auto properties() const { return node::Properties{{"kind", to_string(_operator.kind())}}; } - /** Implements `Node` interface. */ - void clearCache() { - _cache.result.reset(); - _cache.operands.clear(); - } private: ::hilti::operator_::detail::Operator _operator; - - mutable struct { - std::optional result; - std::vector operands; - } _cache; }; namespace resolved_operator { diff --git a/hilti/toolchain/include/ast/expressions/ternary.h b/hilti/toolchain/include/ast/expressions/ternary.h index 22b7920a2..2bbc4295d 100644 --- a/hilti/toolchain/include/ast/expressions/ternary.h +++ b/hilti/toolchain/include/ast/expressions/ternary.h @@ -28,7 +28,7 @@ class Ternary : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return true_().isTemporary() || false_().isTemporary(); } /** Implements `Expression` interface. */ - Type type() const { + const Type& type() const { return true_().type(); } // TODO(robin): Currentluy we enforce both having the same type; we might need to coerce to target type though /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/expressions/type-wrapped.h b/hilti/toolchain/include/ast/expressions/type-wrapped.h index ad6a39499..3026365db 100644 --- a/hilti/toolchain/include/ast/expressions/type-wrapped.h +++ b/hilti/toolchain/include/ast/expressions/type-wrapped.h @@ -5,53 +5,16 @@ #include #include -#include namespace hilti { namespace expression { -/** - * AST node for an expression wrapped into another which does not have a - * known type yet, for example because IDs are stil unresolved. With a - * "normal" expression, calling `type()` would yield an unusable type. This - * expression instead returns a place-holder type that's derived on one of - * two ways: - * - * 1. If the fully resolved type of the expression is actually known - * a-priori, it can be jsut passed into the constructor and will then - * always be returned, independent of the inner expression's type - * itself. - * - * 2. If no explicit type is given, `type()` returns a proxy type that - * evaluates the expression's type on demand once requested (but not, - * crucially, immediately). So once the expression is fully resolved, - * this will yield its correct type. In the meantime, the proxy can be - * passed around like any other type. - * - * In case 1, one can in addition require that the expression's eventual - * fully-resolved type matches the type that was specified. If it doesn't the - * validator will then reject the code. - * - */ +/** AST node for an expression wrapped to have a specific type. */ class TypeWrapped : public NodeBase, public trait::isExpression { public: - struct ValidateTypeMatch {}; - - TypeWrapped(Expression e, Meta m = Meta()) : NodeBase(nodes(std::move(e), node::none), std::move(m)) {} - TypeWrapped(Expression e, Type t, Meta m = Meta()) : NodeBase(nodes(std::move(e), std::move(t)), std::move(m)) {} - TypeWrapped(Expression e, Type t, ValidateTypeMatch _, Meta m = Meta()) - : NodeBase(nodes(std::move(e), std::move(t)), std::move(m)), _validate_type_match(true) {} - - TypeWrapped(Expression e, NodeRef t, Meta m = Meta()) - : NodeBase(nodes(std::move(e)), std::move(m)), _type_node_ref(std::move(t)) {} - - TypeWrapped(Expression e, NodeRef t, ValidateTypeMatch _, Meta m = Meta()) - : NodeBase(nodes(std::move(e)), std::move(m)), _validate_type_match(true), _type_node_ref(std::move(t)) {} - const auto& expression() const { return child(0); } - bool validateTypeMatch() const { return _validate_type_match; } bool operator==(const TypeWrapped& other) const { return expression() == other.expression() && type() == other.type(); @@ -62,21 +25,7 @@ class TypeWrapped : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return expression().isTemporary(); } /** Implements `Expression` interface. */ - Type type() const { - if ( _type_node_ref ) - return _type_node_ref->template as(); - - if ( auto t = childs()[1].tryAs() ) { - if ( t->template isA() ) - // Don't call effectiveType() here, we want to keep - // evaluation pending. - return *t; - - return type::effectiveType(*t); - } - - return type::Computed(expression(), meta()); - } + const Type& type() const { return childs()[1].as(); } /** Implements `Expression` interface. */ auto isConstant() const { return expression().isConstant(); } @@ -84,11 +33,7 @@ class TypeWrapped : public NodeBase, public trait::isExpression { auto isEqual(const Expression& other) const { return node::isEqual(this, other); } /** Implements `Node` interface. */ - auto properties() const { return node::Properties{{"validate_type_match", _validate_type_match}}; } - -private: - bool _validate_type_match = false; - NodeRef _type_node_ref; + auto properties() const { return node::Properties{}; } }; } // namespace expression diff --git a/hilti/toolchain/include/ast/expressions/type.h b/hilti/toolchain/include/ast/expressions/type.h index 4f5cc3995..322d3b96b 100644 --- a/hilti/toolchain/include/ast/expressions/type.h +++ b/hilti/toolchain/include/ast/expressions/type.h @@ -13,9 +13,9 @@ namespace expression { /** AST node for a type expression. */ class Type_ : public NodeBase, public trait::isExpression { public: - Type_(Type t, Meta m = Meta()) : NodeBase({std::move(t)}, std::move(m)) {} + Type_(Type t, Meta m = Meta()) : NodeBase(nodes(type::Type_(std::move(t), m)), m) {} - auto typeValue() const { return type::effectiveType(child(0)); } + const auto& typeValue() const { return child(0).typeValue(); } bool operator==(const Type_& other) const { return typeValue() == other.typeValue(); } @@ -24,7 +24,7 @@ class Type_ : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return true; } /** Implements `Expression` interface. */ - auto type() const { return type::Type_(child(0)); } + const auto& type() const { return child(0); } /** Implements `Expression` interface. */ auto isConstant() const { return true; } /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/expressions/typeinfo.h b/hilti/toolchain/include/ast/expressions/typeinfo.h index ea93ea871..7107271f3 100644 --- a/hilti/toolchain/include/ast/expressions/typeinfo.h +++ b/hilti/toolchain/include/ast/expressions/typeinfo.h @@ -13,7 +13,8 @@ namespace expression { /** AST node for a "move" expression. */ class TypeInfo : public NodeBase, public trait::isExpression { public: - TypeInfo(Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} + TypeInfo(Expression e, Meta m = Meta()) + : NodeBase(nodes(std::move(e), Type(type::Library("const hilti::rt::TypeInfo*"))), std::move(m)) {} const auto& expression() const { return child(0); } @@ -24,7 +25,7 @@ class TypeInfo : public NodeBase, public trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return true; } /** Implements `Expression` interface. */ - auto type() const { return type::Library("const hilti::rt::TypeInfo*"); } + const auto& type() const { return child(1); } /** Implements `Expression` interface. */ auto isConstant() const { return true; } /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/expressions/unresolved-operator.h b/hilti/toolchain/include/ast/expressions/unresolved-operator.h index e9176a3ef..56762e8a9 100644 --- a/hilti/toolchain/include/ast/expressions/unresolved-operator.h +++ b/hilti/toolchain/include/ast/expressions/unresolved-operator.h @@ -7,7 +7,7 @@ #include #include -#include +#include namespace hilti { namespace expression { @@ -16,30 +16,37 @@ namespace expression { class UnresolvedOperator : public NodeBase, public trait::isExpression { public: UnresolvedOperator(operator_::Kind op, std::vector operands, Meta meta = Meta()) - : NodeBase(nodes(std::move(operands)), std::move(meta)), _kind(op) {} + : NodeBase(nodes(type::auto_, std::move(operands)), std::move(meta)), _kind(op) {} + + UnresolvedOperator(operator_::Kind op, hilti::node::Range operands, Meta meta = Meta()) + : NodeBase(nodes(type::auto_, std::move(operands)), std::move(meta)), _kind(op) {} auto kind() const { return _kind; } - /** Implements interfave for use with `OverloadRegistry`. */ - const auto& operands() const { - if ( _cache.operands.empty() ) - _cache.operands = childs(0, -1); + bool areOperandsResolved() const { + for ( auto op : childs(1, -1) ) { + if ( ! type::isResolved(op.type()) ) + return false; + } - return _cache.operands; + return true; } bool operator==(const UnresolvedOperator& other) const { return kind() == other.kind() && operands() == other.operands(); } - /** Implements `Expression` interface. */ + /** Implements interfave for use with `OverloadRegistry`. */ + hilti::node::Range operands() const { return childs(1, -1); } // Dummy implementations as the node will be rejected in validation anyway. + + /** Implements `Expression` interface. */ bool isLhs() const { return false; } /** Implements `Expression` interface. */ bool isTemporary() const { return false; } /** Implements `Expression` interface. */ - auto type() const { return type::unknown; } + const auto& type() const { return child(0); } /** Implements `Expression` interface. */ auto isConstant() const { return false; } /** Implements `Expression` interface. */ @@ -48,12 +55,8 @@ class UnresolvedOperator : public NodeBase, public trait::isExpression { /** Implements `Node` interface. */ auto properties() const { return node::Properties{{"kind", to_string(_kind)}}; } - void clearCache() { _cache.operands.clear(); } - private: operator_::Kind _kind; - - mutable struct { std::vector operands; } _cache; }; } // namespace expression diff --git a/hilti/toolchain/include/ast/expressions/void.h b/hilti/toolchain/include/ast/expressions/void.h index 5ffd072a6..082466d0a 100644 --- a/hilti/toolchain/include/ast/expressions/void.h +++ b/hilti/toolchain/include/ast/expressions/void.h @@ -13,7 +13,7 @@ namespace expression { /** AST node for a void expression. */ class Void : public NodeBase, public hilti::trait::isExpression { public: - Void(Meta m = Meta()) : NodeBase(std::move(m)) {} + Void(Meta m = Meta()) : NodeBase(nodes(type::void_), m) {} bool operator==(const Void& /* other */) const { return true; } @@ -22,7 +22,7 @@ class Void : public NodeBase, public hilti::trait::isExpression { /** Implements `Expression` interface. */ bool isTemporary() const { return true; } /** Implements `Expression` interface. */ - auto type() const { return type::Void(); } + const auto& type() const { return child(0); } /** Implements `Expression` interface. */ auto isConstant() const { return true; } /** Implements `Expression` interface. */ diff --git a/hilti/toolchain/include/ast/function.h b/hilti/toolchain/include/ast/function.h index 965672b1f..ec133820e 100644 --- a/hilti/toolchain/include/ast/function.h +++ b/hilti/toolchain/include/ast/function.h @@ -51,9 +51,11 @@ class Function : public NodeBase { Function() : NodeBase(nodes(node::none, node::none, node::none, node::none), Meta()) {} const auto& id() const { return child(0); } - auto type() const { return type::effectiveType(child(1)).as(); } - auto body() const { return childs()[2].tryReferenceAs(); } - auto attributes() const { return childs()[3].tryReferenceAs(); } + const auto& type() const { return child(1).as(); } + NodeRef typeRef() { return NodeRef(childs()[1]); } + const auto& ftype() const { return child(1).as(); } + auto body() const { return childs()[2].tryAs(); } + auto attributes() const { return childs()[3].tryAs(); } auto callingConvention() const { return _cc; } bool isStatic() const { return AttributeSet::find(attributes(), "&static").has_value(); } @@ -62,22 +64,16 @@ class Function : public NodeBase { attributes() == other.attributes() && callingConvention() == other.callingConvention(); } + void setBody(Statement b) { childs()[2] = std::move(b); } + void setID(ID id) { childs()[0] = std::move(id); } + void setResultType(Type t) { childs()[1].as().setResultType(std::move(t)); } + + /** Internal method for use by builder API only. */ + Node& _typeNode() { return childs()[1]; } + /** Implements the `Node` interface. */ auto properties() const { return node::Properties{{"cc", to_string(_cc)}}; } - /** - * Returns a new function with the body replaced. - * - * @param d original function - * @param b new body - * @return new function that's equal to original one but with the body replaced - */ - static Function setBody(const Function& d, const Statement& b) { - auto x = Function(d); - x.childs()[2] = b; - return x; - } - private: function::CallingConvention _cc = function::CallingConvention::Standard; }; diff --git a/hilti/toolchain/include/ast/module.h b/hilti/toolchain/include/ast/module.h index c022e3fce..c7c734219 100644 --- a/hilti/toolchain/include/ast/module.h +++ b/hilti/toolchain/include/ast/module.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include @@ -19,7 +20,7 @@ namespace hilti { /** AST node representing a HILTI module. */ class Module : public NodeBase { public: - Module(ID id = {}, Meta m = Meta()) : NodeBase({std::move(id), statement::Block({}, m)}, std::move(m)) {} + Module(ID id, Meta m = Meta()) : NodeBase({std::move(id), statement::Block({}, m)}, std::move(m)) {} Module(ID id, std::vector decls, Meta m = Meta()) : NodeBase(nodes(std::move(id), statement::Block({}, m), std::move(decls)), std::move(m)) {} @@ -27,17 +28,12 @@ class Module : public NodeBase { Module(ID id, std::vector decls, std::vector stmts, const Meta& m = Meta()) : NodeBase(nodes(std::move(id), statement::Block(std::move(stmts), m), std::move(decls)), m) {} + Module() : NodeBase(nodes(ID(), statement::Block({}, Meta())), Meta()) {} + const auto& id() const { return child(0); } const auto& statements() const { return child(1); } - const auto& declarations() const { - if ( _cache.declarations.empty() ) - _cache.declarations = childsOfType(); - - return _cache.declarations; - } - - const auto& preserved() const { return _preserved; } - auto& preserved() { return _preserved; } + auto declarations() const { return childsOfType(); } + auto declarationRefs() const { return childRefsOfType(); } bool isEmpty() const { // We always have an ID and a block as childs. @@ -57,15 +53,15 @@ class Module : public NodeBase { * * @param id name of the property to return */ - Result moduleProperty(const ID& id) const; + hilti::optional_ref moduleProperty(const ID& id) const; /** * Returns all of module's property declarations of a given name. If * there's no property declaration of that ID, return an empty container. * - * @param id name of the property to return + * @param id name of the property to return; leave unset for returning all */ - std::vector moduleProperties(const ID& id) const; + hilti::node::Set moduleProperties(const std::optional& id) const; /** * Adds a declaration to the module. It will be appended to the current @@ -102,25 +98,28 @@ class Module : public NodeBase { void add(Expression e) { add(statement::Expression(std::move(e))); } /** - * Saves a node along with the module, but outside of the actual AST. - * This allows keeping references to the node valid while not making the - * node itself part of the AST. That's especially useful when - * transforming nodes from one representation to another, but wanting to - * retain a link to the original one through `Node::setOriginalNode()`. + * Saves a node along with the module, but outside of the actual AST. This + * allows keeping references to the node's sub-AST valid while not making + * the node itself part of the AST. That's especially useful when + * transforming nodes from one representation to another while other parts + * of the AST are still refering to the original once. * - * @return reference to the preserved node + * @param n node to retain with all of its children + * @returns a reference to the internally stored node */ - NodeRef preserve(Node n); + NodeRef preserve(const Node& n) { + _preserved.push_back(n); + return NodeRef(_preserved.back()); + } + + /** Destroys any nodes retained previously through `preserve()`. */ + void destroyPreservedNodes(); /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } - void clearCache() { _cache.declarations.clear(); } - private: std::vector _preserved; - - mutable struct { std::vector declarations; } _cache; }; /** Creates an AST node representing a `Module`. */ diff --git a/hilti/toolchain/include/ast/node_ref.h b/hilti/toolchain/include/ast/node-ref.h similarity index 85% rename from hilti/toolchain/include/ast/node_ref.h rename to hilti/toolchain/include/ast/node-ref.h index e885064da..69590e2e1 100644 --- a/hilti/toolchain/include/ast/node_ref.h +++ b/hilti/toolchain/include/ast/node-ref.h @@ -19,8 +19,8 @@ namespace detail { // Control block for refering to nodes. class Control : public hilti::intrusive_ptr::ManagedObject { public: - Control(Node* n) : _node(n), _rid(++_rid_counter) {} - Node* _node; + Control(const Node* n) : _node(n), _rid(++_rid_counter) {} + const Node* _node; uint64_t _rid; static uint64_t _rid_counter; @@ -43,7 +43,7 @@ struct Invalid : std::runtime_error { */ class NodeRef { public: - explicit NodeRef(Node& n); // NOLINT + explicit NodeRef(const Node& n); explicit NodeRef(const NodeRef& other) = default; NodeRef(NodeRef&& other) = default; NodeRef& operator=(const NodeRef& other) = default; @@ -60,12 +60,12 @@ class NodeRef { uint64_t rid() const { return _control ? _control->_rid : 0; } /** - * Returns a string version of the referenced node's unique control ID, - * or 0 if the instance isn't referencing anything. + * Returns a string version of the referenced node's unique control ID, or + * `???` if the instance isn't referencing anything. * * @note This is primarily for internal usage. */ - std::string renderedRid() const { return rid() ? util::fmt("%%%" PRIu64, rid()) : "%???"; }; + std::string renderedRid() const { return _control && _control->_node ? util::fmt("%%%" PRIu64, rid()) : "%???"; }; /** * Returns a pointer to the the referenced node. @@ -80,7 +80,7 @@ class NodeRef { * * @exception Invalid if the node does not exist anymore */ - Node& operator*() const { return *_node(); } + const Node& operator*() const { return *_node(); } operator const Node&() const { return *_node(); } @@ -88,7 +88,7 @@ class NodeRef { explicit operator bool() const { return _control && _control->_node; } private: - Node* _node() const; + const Node* _node() const; IntrusivePtr _control; }; diff --git a/hilti/toolchain/include/ast/node.api b/hilti/toolchain/include/ast/node.api index a8a49db88..c45b30821 100644 --- a/hilti/toolchain/include/ast/node.api +++ b/hilti/toolchain/include/ast/node.api @@ -17,16 +17,6 @@ class Node(trait::isNode) { /** Sets the node's meta data. */ void setMeta(Meta m); - /** - * Returns an original, pre-transformation node associated with the node - * if such has been set. - */ - const NodeRef& originalNode() const; - - /** Associates an original, pre-transformation node with this node. */ - void setOriginalNode(const NodeRef& n); - - /** Clears any dynamically computed information that the node might have cached for reuse. */ - void clearCache(); - + /** Returns true if AST walking is not to visit the nodes children. */ + bool pruneWalk() const; }; diff --git a/hilti/toolchain/include/ast/node.h b/hilti/toolchain/include/ast/node.h index b5a73817d..707c84737 100644 --- a/hilti/toolchain/include/ast/node.h +++ b/hilti/toolchain/include/ast/node.h @@ -2,7 +2,9 @@ #pragma once +#include #include +#include #include #include #include @@ -10,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +24,7 @@ class isNode {}; } // namespace hilti #include -#include +#include #include #include #include @@ -29,9 +32,15 @@ class isNode {}; namespace hilti { class Node; -class NodeRef; namespace node { + +template +class Range; + +template +class Set; + namespace detail { /** Value of a node property, stored as part of `Properties`. */ @@ -57,10 +66,17 @@ inline std::string to_string(PropertyValue v) { /** Importance of reporting an error, relative to others. */ enum class ErrorPriority { - Normal, /**< Normal priority error that will always be reported. */ - Low /**< Low priority error that will be reported only if no normal priority ones have been found. */ + High = 3, /**< high priority error that will always be reported */ + Normal = 2, /**< normal priority error that will be reported if there are no higher priority ones */ + Low = 1, /**< low priority error that will be reported if there are no higher priority ones */ + NoError = 0 /**< place-holder for comparision if no error was encountered */ }; +inline bool operator<(ErrorPriority x, ErrorPriority y) { + return static_cast>(x) < + static_cast>(y); +} + /** Error information associated with nodes. */ struct Error { std::string message; /**< main error message to report */ @@ -113,8 +129,6 @@ class Node final : public node::detail::Node { Node() = delete; - explicit Node(IntrusivePtr data) : node::detail::Node(std::move(data)) {} - ~Node() final { if ( _control_ptr ) _control_ptr->_node = nullptr; @@ -150,14 +164,18 @@ class Node final : public node::detail::Node { } /** - * Resets the node's scope to point to another one. Nodes + * Resets the node's scope to point to another one. */ void setScope(IntrusivePtr new_scope) { _scope = std::move(new_scope); } /** Clears out the current scope. */ void clearScope() { - if ( _scope ) - _scope->clear(); + if ( ! _scope ) + return; + + // Don't just clear the items because the scope might be shared with + // other nodes. + _scope = make_intrusive(); } /** Returns any error messages associated with the node. */ @@ -189,6 +207,19 @@ class Node final : public node::detail::Node { addError(std::move(msg), location(), std::move(context)); } + /** + * Associate an error message with the node. The error's location will be + * that of the current node. + * + * @param msg error message to report + * @param priority importance of showing the error + * @param context further lines of context to show along with error + * + */ + void addError(std::string msg, node::ErrorPriority priority, std::vector context = {}) { + addError(std::move(msg), location(), priority, std::move(context)); + } + /** * Associate an error message with the node. The error will have normal * priority. @@ -222,6 +253,12 @@ class Node final : public node::detail::Node { _errors->push_back(std::move(error)); } + /** + * Recursively clears all child nodes and then deletes them from this node. + * This helps to break reference cycles. + */ + void destroyChilds(); + /** * Returns an internal string representation of the node and all its * children. @@ -232,16 +269,23 @@ class Node final : public node::detail::Node { std::string render(bool include_location = true) const; /** - * Returns a HILTI source code representation of the node and all its + * Print out a HILTI source code representation of the node and all its * children. If the node is not the root of an AST, it's not guaranteed * that the result will form valid HILTI source code (but it can still be * used, e.g., in error messages). * + * @param out output stream * @param compact create a one-line representation * */ void print(std::ostream& out, bool compact = false) const; + /** + * Returns a HILTI source code representation of the node and all its + * children. This can be called from inside a debugger. + */ + std::string print() const; + /** Convenience method to return the meta data's location information. */ const Location& location() const { return meta().location(); } @@ -256,11 +300,7 @@ class Node final : public node::detail::Node { } /** Renders the node as HILTI source code. */ - operator std::string() const { - std::stringstream buf; - print(buf, true); - return buf.str(); - } + operator std::string() const { return print(); } /** * Replaces the node with another one. Existing `NodeRef` pointing to @@ -296,16 +336,16 @@ class Node final : public node::detail::Node { private: friend class NodeRef; - // Returns (and potentially) created the control block for this node that - // NodeRef uses to maintain links to it. - IntrusivePtr _control() { + // Returns (and potentially creates) the control block for this node that + // `NodeRef` uses to maintain links to it. + IntrusivePtr _control() const { if ( ! _control_ptr ) _control_ptr = make_intrusive(this); return _control_ptr; } - IntrusivePtr _control_ptr = nullptr; + mutable IntrusivePtr _control_ptr = nullptr; mutable IntrusivePtr _scope = nullptr; std::unique_ptr> _errors = nullptr; }; @@ -367,65 +407,50 @@ class NodeBase : public trait::isNode { * @tparam T type that the child nodes are assumed to (and must) have * @param begin index of first child to include; a negative index counts Python-style from end of list * @param end index of one beyond last child to include; a negative index counts Python-style from end of list - * @return childs from `start` to `end` + * @return range containing childs from `start` to `end` */ template - std::vector childs(int begin, int end) const { - std::vector n; + auto childs(int begin, int end) const { + auto end_ = (end < 0) ? _childs.end() : _childs.begin() + end; + return hilti::node::Range(_childs.begin() + begin, end_); + } - if ( end < 0 ) - end = _childs.size(); + /** + * Returns a references to a subrange of children. The indices correspond + * to the order children were passed into the constructor and/or added. + * + * @param begin index of first child to include; a negative index counts Python-style from end of list + * @param end index of one beyond last child to include; a negative index counts Python-style from end of list + * @return vector containing child references from `start` to `end` + */ + auto childRefs(int begin, int end) { + auto end_ = (end < 0) ? _childs.end() : _childs.begin() + end; - for ( auto i = begin; i < end; i++ ) - n.emplace_back(_childs[i].as()); + std::vector refs; + for ( auto c = _childs.begin(); c != end_; c = std::next(c) ) + refs.push_back(NodeRef(*c)); - return n; + return refs; } /** - * Returns a subset of children by type. + * Returns a subset of children selected by their type. * * @tparam T type of children to return - * @return all childs that have type `T` + * @return set of all childs that have type `T` */ template - std::vector childsOfType() const { - std::vector n; - for ( auto& c : _childs ) { - if ( auto x = c.tryAs() ) - n.emplace_back(*x); - } - - return n; - } + hilti::node::Set childsOfType() const; /** - * Returns a subset of children `Node` references, selected by type. + * Returns a vector of references to a subset of children selected by their + * type. * * @tparam T type of children to return - * @return all childs that have type `T` + * @return set of all childs that have type `T` */ template - auto nodesOfType() const { - std::vector> n; - for ( const auto& c : _childs ) { - if ( c.isA() ) - n.emplace_back(c); - } - - return n; - } - - template - auto nodesOfType() { - std::vector> n; - for ( auto& c : _childs ) { - if ( c.isA() ) - n.emplace_back(c); - } - - return n; - } + std::vector childRefsOfType() const; /** * Adds a child node. It will be appended to the end of the current list @@ -442,7 +467,7 @@ class NodeBase : public trait::isNode { } /** Implements the `Node` interface. */ - auto& childs() const { return _childs; } + const auto& childs() const { return _childs; } /** Implements the `Node` interface. */ auto& childs() { return _childs; } /** Implements the `Node` interface. */ @@ -450,11 +475,7 @@ class NodeBase : public trait::isNode { /** Implements the `Node` interface. */ void setMeta(Meta m) { _meta = std::move(m); } /** Implements the `Node` interface. */ - const NodeRef& originalNode() const { return _orig; } - /** Implements the `Node` interface. */ - void setOriginalNode(const NodeRef& n) { _orig = n; } - /** Implements the `Node` interface. */ - void clearCache() {} + bool pruneWalk() const { return false; } private: std::vector<::hilti::Node> _childs; @@ -483,6 +504,263 @@ class None : public NodeBase, public util::type_erasure::trait::Singleton { /** Singleton. */ extern const Node none; +/** + * A constant iterator over a range of nodes (`node::Range`). Internally, this + * wrap around a vector iterator, and is adapted from + * https://www.artificialworlds.net/blog/2017/05/12/c-iterator-wrapperadaptor-example. + */ +template +class RangeIterator { + using BaseIterator = std::vector::const_iterator; + +public: + using value_type = BaseIterator::value_type; + using difference_type = BaseIterator::difference_type; + using pointer = BaseIterator::pointer; + using reference = BaseIterator::reference; + using iterator_category = BaseIterator::iterator_category; + + explicit RangeIterator(BaseIterator i) : _iter(std::move(i)) {} + RangeIterator(const RangeIterator& other) = default; + RangeIterator(RangeIterator&& other) = default; + RangeIterator() {} + ~RangeIterator() = default; + + const Node& node() const { return *_iter; } + + RangeIterator& operator=(const RangeIterator& other) = default; + RangeIterator& operator=(RangeIterator&& other) = default; + const T& operator*() const { return value(); } + const T* operator->() const { return &value(); } + bool operator==(const RangeIterator& other) const { return _iter == other._iter; } + bool operator!=(const RangeIterator& other) const { return ! (*this == other); } + + RangeIterator operator++(int) { + auto x = RangeIterator(_iter); + ++_iter; + return x; + } + + RangeIterator& operator++() { + ++_iter; + return *this; + } + + RangeIterator& operator+=(difference_type i) { + _iter += i; + return *this; + } + + RangeIterator& operator-=(difference_type i) { + _iter -= i; + return *this; + } + + difference_type operator-(const RangeIterator& other) const { return _iter - other._iter; } + RangeIterator operator-(difference_type i) const { return RangeIterator(_iter - i); } + RangeIterator operator+(difference_type i) const { return RangeIterator(_iter + i); } + +private: + const T& value() const { return (*_iter).template as>(); } + + BaseIterator _iter; +}; + +/** + * A range of AST nodes, defined by start and end into an existing vector of + * nodes. The range creates a view that can be iterated over, yielding a + * reference to each node in turn. + */ +template +class Range { +public: + using iterator = RangeIterator; + using const_iterator = RangeIterator; + + explicit Range() {} + Range(std::vector::const_iterator begin, std::vector::const_iterator end) + : _begin(std::move(begin)), _end(std::move(end)) {} + + explicit Range(const std::vector& nodes) : Range(nodes.begin(), nodes.end()) {} + + Range(const Range& other) = default; + Range(Range&& other) = default; + ~Range() = default; + + auto begin() const { return const_iterator(_begin); } + auto end() const { return const_iterator(_end); } + size_t size() const { return static_cast(_end - _begin); } + const T& front() const { return *_begin; } + bool empty() const { return _begin == _end; } + + /** + * Returns a new vector containing copies of all nodes that the range + * includes. + **/ + std::vector copy() const { + std::vector x; + for ( auto i = _begin; i != _end; i++ ) + x.push_back(*i); + + return x; + } + + const T& operator[](size_t i) const { + assert(i < std::distance(_begin, _end)); + return *(_begin + i); + } + + bool operator==(const Range& other) const { + if ( this == &other ) + return true; + + if ( size() != other.size() ) + return false; + + auto x = _begin; + auto y = other._begin; + while ( x != _end ) { + if ( ! (*x++ == *y++) ) + return false; + } + + return true; + } + + Range& operator=(const Range& other) = default; + Range& operator=(Range&& other) = default; + +private: + RangeIterator _begin; + RangeIterator _end; +}; + +/** + * A constant iterator over a set of nodes (`node::Set`). The content of the + * set is sorted by the order that nodes were added. Internally, this wraps + * around a iterator over a vector of node references, and is adapted from + * https://www.artificialworlds.net/blog/2017/05/12/c-iterator-wrapperadaptor-example. + */ +template +class SetIterator { + using BaseIterator = typename std::vector>::const_iterator; + +public: + // Previously provided by std::iterator + using value_type = T; + using difference_type = typename BaseIterator::difference_type; + using pointer = typename BaseIterator::pointer; + using reference = typename BaseIterator::reference; + using iterator_category = typename BaseIterator::iterator_category; + + explicit SetIterator(BaseIterator i) : _iter(std::move(i)) {} + SetIterator(const SetIterator& other) = default; + SetIterator(SetIterator&& other) = default; + SetIterator() {} + ~SetIterator() = default; + + const Node& node() const { return *_iter; } + + SetIterator& operator=(const SetIterator& other) = default; + SetIterator& operator=(SetIterator&& other) = default; + const T& operator*() const { return value(); } + const T* operator->() const { return &value(); } + bool operator==(const SetIterator& other) const { return _iter == other._iter; } + bool operator!=(const SetIterator& other) const { return ! (*this == other); } + + SetIterator operator++(int) { + auto x = SetIterator(_iter); + ++_iter; + return x; + } + + SetIterator& operator++() { + ++_iter; + return *this; + } + + SetIterator& operator+=(difference_type i) { + _iter += i; + return *this; + } + + SetIterator& operator-=(difference_type i) { + _iter -= i; + return *this; + } + + difference_type operator-(const SetIterator& other) const { return _iter - other._iter; } + SetIterator operator-(difference_type i) const { return SetIterator(_iter - i); } + SetIterator operator+(difference_type i) const { return SetIterator(_iter + i); } + +private: + const T& value() const { return ((*_iter).get()); } + + BaseIterator _iter; +}; + +/** + * A set of AST nodes. The set creates a view of nodes that can be iterated + * over, yielding a reference to each node in turn. In contrast to `Range`, a + * set can include nodes that are not all part of a continous slice inside a + * vector. + */ +template +class Set { +public: + using iterator = SetIterator; + using const_iterator = SetIterator; + + Set() {} + Set(const Set& other) = default; + Set(Set&& other) = default; + ~Set() = default; + + auto begin() const { return const_iterator(_set.begin()); } + auto end() const { return const_iterator(_set.end()); } + size_t size() const { return _set.size(); } + bool empty() const { return _set.empty(); } + void insert(const T& t) { _set.push_back(t); } + + /** + * Returns a new vector containing copies of all nodes that the range + * includes. + **/ + std::vector copy() const { + std::vector x; + for ( auto i = begin(); i != end(); i++ ) + x.push_back(*i); + + return x; + } + + const T& operator[](size_t i) const { return *(begin() + i); } + + bool operator==(const Set& other) const { + if ( this == &other ) + return true; + + if ( size() != other.size() ) + return false; + + auto x = begin(); + auto y = other.begin(); + while ( x != end() ) { + if ( ! (*x++ == *y++) ) + return false; + } + + return true; + } + + Set& operator=(const Set& other) = default; + Set& operator=(Set&& other) = default; + +private: + std::vector> _set; +}; + + } // namespace node inline const Node& to_node(const node::None& /* n */) { return node::none; } @@ -515,6 +793,7 @@ std::vector nodes(std::vector t) { v.reserve(t.size()); for ( const auto& i : t ) v.emplace_back(std::move(i)); + return v; } @@ -527,34 +806,21 @@ std::vector nodes(std::list t) { std::vector v; for ( const auto& i : t ) v.emplace_back(std::move(i)); + return v; } /** - * Creates `Node` instances for a set of objects all implementing the `Node` - * interface. + * Copies a ramge of nodes over into a vector. Note that as with all copies of + * node, this performs shallow copying. */ template -std::vector nodes(std::set t) { +std::vector nodes(hilti::node::Range t) { std::vector v; v.reserve(t.size()); for ( const auto& i : t ) v.emplace_back(std::move(i)); - return v; -} -/** - * Creates `Node` instances for a vector of pairs of objects all implementing - * the `Node` interface. The pair will be flattened in the result. - */ -template -std::vector nodes(std::vector> t) { - std::vector v; - v.reserve(t.size() * 2); - for ( const auto& i : t ) { - v.emplace_back(std::move(i.first)); - v.emplace_back(std::move(i.second)); - } return v; } @@ -582,12 +848,73 @@ std::vector nodes(T t, Ts... ts) { namespace node { template bool isEqual(const T* this_, const Other& other) { - if ( auto o = other.template tryAs() ) + if ( const auto o = other.template tryAs() ) return *this_ == *o; return false; } +/** + * Filters a node vector through a boolean predicate, returning a set + * containing the matching ones. + */ +template +auto filter(const hilti::node::Range& x, F f) { + hilti::node::Set y; + for ( const auto& i : x ) { + if ( f(i) ) + y.push_back(i); + } + + return y; +} + +/** + * Filters a node set through a boolean predicate, returning a new set + * containing the matching ones. + */ +template +auto filter(const hilti::node::Set& x, F f) { + hilti::node::Set y; + for ( const auto& i : x ) { + if ( f(i) ) + y.insert(i); + } + + return y; +} + +/** + * Applies a function to each element of a node range, returning a new vector + * with the results. + */ +template +auto transform(const hilti::node::Range& x, F f) { + using Y = typename std::result_of::type; + std::vector y; + y.reserve(x.size()); + for ( const auto& i : x ) + y.push_back(f(i)); + + return y; +} + +/** + * Applies a function to each element of a node set, returning a new vector of + * with the results. + */ +template +auto transform(const hilti::node::Set& x, F f) { + using Y = typename std::result_of::type; + std::vector y; + y.reserve(x.size()); + for ( const auto& i : x ) + y.push_back(f(i)); + + return y; +} + + } // namespace node /** Renders a node as HILTI source code. */ @@ -596,34 +923,44 @@ inline std::ostream& operator<<(std::ostream& out, const Node& n) { return out; } -namespace node { -namespace detail { -// Backend to NodeBase::flattenedChilds. template -void flattenedChilds(const Node& n, std::vector* dst) { - for ( const auto& c : n.childs() ) { - if ( auto t = c.tryAs() ) - dst->push_back(*t); +hilti::node::Set NodeBase::childsOfType() const { + typename hilti::node::Set n; + for ( auto c = _childs.begin(); c != _childs.end(); c = std::next(c) ) { + if ( auto t = c->tryAs() ) + n.insert(*t); + } - flattenedChilds(c, dst); + return n; +} + +template +std::vector NodeBase::childRefsOfType() const { + typename std::vector n; + for ( auto c = _childs.begin(); c != _childs.end(); c = std::next(c) ) { + if ( c->isA() ) + n.push_back(NodeRef(*c)); } + + return n; } +namespace node { +namespace detail { +// Backend to NodeBase::flattenedChilds. +void flattenedChilds(const hilti::Node& n, node::Set* dst); } // namespace detail /** * Returns a list of all childs of specific type, descending recursively * to find instance anywhere below this node. */ -template -std::vector flattenedChilds(const Node& n) { - std::vector dst; - detail::flattenedChilds(n, &dst); +inline node::Set flattenedChilds(const Node& n) { + node::Set dst; + detail::flattenedChilds(n, &dst); return dst; } } // namespace node } // namespace hilti - -extern hilti::node::Properties operator+(const hilti::node::Properties& p1, const hilti::node::Properties& p2); diff --git a/hilti/toolchain/include/ast/nodes.decl b/hilti/toolchain/include/ast/nodes.decl index 943d03a89..dd74b32f6 100644 --- a/hilti/toolchain/include/ast/nodes.decl +++ b/hilti/toolchain/include/ast/nodes.decl @@ -1,4 +1,3 @@ - trait hilti::Declaration isDeclaration trait hilti::Node isNode trait hilti::Type isType @@ -19,11 +18,10 @@ hilti::Statement : isNode hilti::Type : isNode hilti::type::function::Result : isNode hilti::type::function::Parameter : isNode -hilti::type::union_::Field : isNode -hilti::type::struct_::Field : isNode hilti::statement::switch_::Case : isNode hilti::statement::try_::Catch : isNode hilti::type::enum_::Label : isNode +hilti::type::tuple::Element : isNode hilti::ctor::Address : isCtor hilti::ctor::Bool : isCtor @@ -35,7 +33,9 @@ hilti::ctor::Error : isCtor hilti::ctor::Exception : isCtor hilti::ctor::Interval : isCtor hilti::ctor::List : isCtor +hilti::ctor::Library : isCtor hilti::ctor::Map : isCtor +hilti::ctor::map::Element : isNode hilti::ctor::Network : isCtor hilti::ctor::Null : isCtor hilti::ctor::Optional : isCtor @@ -59,7 +59,7 @@ hilti::ctor::ValueReference : isCtor hilti::declaration::Constant : isDeclaration hilti::declaration::Expression : isDeclaration -hilti::declaration::Forward : isDeclaration +hilti::declaration::Field : isDeclaration hilti::declaration::Function : isDeclaration hilti::declaration::GlobalVariable : isDeclaration hilti::declaration::ImportedModule : isDeclaration @@ -114,7 +114,6 @@ hilti::type::Any : isType hilti::type::Auto : isType hilti::type::Bool : isType hilti::type::Bytes : isType -hilti::type::Computed : isType hilti::type::DocOnly : isType hilti::type::Enum : isType hilti::type::Error : isType @@ -133,7 +132,6 @@ hilti::type::Port : isType hilti::type::Real : isType hilti::type::StrongReference : isType hilti::type::RegExp : isType -hilti::type::ResolvedID : isType hilti::type::Result : isType hilti::type::Set : isType hilti::type::SignedInteger : isType diff --git a/hilti/toolchain/include/ast/operator.api b/hilti/toolchain/include/ast/operator.api index 26562bd54..424b1c3bf 100644 --- a/hilti/toolchain/include/ast/operator.api +++ b/hilti/toolchain/include/ast/operator.api @@ -48,7 +48,7 @@ class Operator(trait::isOperator) { * @return type when evaluated * */ - Type result(const std::vector& ops) const; + Type result(const hilti::node::Range& ops) const; /** True if operator's result can be assigned to. */ bool isLhs() const; diff --git a/hilti/toolchain/include/ast/operator.h b/hilti/toolchain/include/ast/operator.h index ca00f10d8..a5c6f6a50 100644 --- a/hilti/toolchain/include/ast/operator.h +++ b/hilti/toolchain/include/ast/operator.h @@ -47,21 +47,33 @@ namespace operator_ { using position_t = visitor::Position; using const_position_t = visitor::Position; -using OperandType = - std::variant(const std::vector&, const std::vector&)>>; -inline std::optional type(const OperandType& t, const std::vector& orig_ops, - const std::vector& resolved_ops) { - if ( const auto& f = std::get_if< - std::function(const std::vector&, const std::vector&)>>(&t) ) +using OperandType = std::variant(const hilti::node::Range&, + const hilti::node::Range&)>>; + +inline std::optional type(const OperandType& t, const hilti::node::Range& orig_ops, + const hilti::node::Range& resolved_ops) { + if ( const auto& f = std::get_if(const hilti::node::Range&, + const hilti::node::Range&)>>(&t) ) return (*f)(orig_ops, resolved_ops); return std::get(t); } +inline std::optional type(const OperandType& t, const hilti::node::Range& orig_ops, + const std::vector& resolved_ops) { + auto resolved_ops_as_nodes = hilti::nodes(resolved_ops); + return type(t, orig_ops, hilti::node::Range(resolved_ops_as_nodes)); +} + +inline std::optional type(const OperandType& t, const std::vector& orig_ops, + const std::vector& resolved_ops) { + auto orig_ops_as_nodes = hilti::nodes(orig_ops); + return type(t, hilti::node::Range(orig_ops_as_nodes), resolved_ops); +} + inline auto operandType(unsigned int op, const char* doc = "") { - return [=](const std::vector& /* orig_ops */, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& /* orig_ops */, + const hilti::node::Range& resolved_ops) -> std::optional { if ( resolved_ops.empty() ) return type::DocOnly(doc); @@ -74,8 +86,8 @@ inline auto operandType(unsigned int op, const char* doc = "") { } inline auto elementType(unsigned int op, const char* doc = "", bool infer_const = true) { - return [=](const std::vector& /* orig_ops */, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& /* orig_ops */, + const hilti::node::Range& resolved_ops) -> std::optional { if ( resolved_ops.empty() ) return type::DocOnly(doc); @@ -93,8 +105,8 @@ inline auto elementType(unsigned int op, const char* doc = "", } inline auto constantElementType(unsigned int op, const char* doc = "") { - return [=](const std::vector& /* orig_ops */, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& /* orig_ops */, + const hilti::node::Range& resolved_ops) -> std::optional { if ( resolved_ops.empty() ) return type::DocOnly(doc); @@ -110,8 +122,8 @@ inline auto constantElementType(unsigned int op, const char* doc = "& /* orig_ops */, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& /* orig_ops */, + const hilti::node::Range& resolved_ops) -> std::optional { if ( resolved_ops.empty() ) return type::DocOnly(doc); @@ -127,8 +139,8 @@ inline auto iteratorType(unsigned int op, bool const_, const char* doc = "& /* orig_ops */, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& /* orig_ops */, + const hilti::node::Range& resolved_ops) -> std::optional { if ( resolved_ops.empty() ) return type::DocOnly(doc); @@ -139,7 +151,11 @@ inline auto dereferencedType(unsigned int op, const char* doc = "") { - return [=](const std::vector& /* orig_ops */, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& /* orig_ops */, + const hilti::node::Range& resolved_ops) -> std::optional { if ( resolved_ops.empty() ) return type::DocOnly(doc); @@ -161,8 +177,8 @@ inline auto sameTypeAs(unsigned int op, const char* doc = "") { } inline auto typedType(unsigned int op, const char* doc = "") { - return [=](const std::vector& /* orig_ops */, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& /* orig_ops */, + const hilti::node::Range& resolved_ops) -> std::optional { if ( resolved_ops.empty() ) return type::DocOnly(doc); @@ -170,7 +186,7 @@ inline auto typedType(unsigned int op, const char* doc = "") { logger().internalError(util::fmt("typedType(): index %d out of range, only %" PRIu64 " ops available", op, resolved_ops.size())); - return type::effectiveType(resolved_ops[op].type().as().typeValue()); + return resolved_ops[op].type().as().typeValue(); }; } @@ -240,6 +256,7 @@ enum class Kind { BitXor, Call, Cast, + CustomAssign, DecrPostfix, DecrPrefix, Delete, @@ -297,6 +314,7 @@ constexpr auto isCommutative(Kind k) { case Kind::Begin: case Kind::Call: case Kind::Cast: + case Kind::CustomAssign: case Kind::DecrPostfix: case Kind::DecrPrefix: case Kind::Delete: @@ -339,53 +357,30 @@ constexpr auto isCommutative(Kind k) { } namespace detail { -constexpr util::enum_::Value kinds[] = {{Kind::Add, "add"}, - {Kind::Begin, "begin"}, - {Kind::BitAnd, "&"}, - {Kind::BitOr, "|"}, - {Kind::BitXor, "^"}, - {Kind::Call, "call"}, - {Kind::Cast, "cast"}, - {Kind::DecrPostfix, "--"}, - {Kind::DecrPrefix, "--"}, - {Kind::Delete, "delete"}, - {Kind::Deref, "*"}, - {Kind::Division, "/"}, - {Kind::DivisionAssign, "/="}, - {Kind::Equal, "=="}, - {Kind::End, "end"}, - {Kind::Greater, ">"}, - {Kind::GreaterEqual, ">="}, - {Kind::HasMember, "?."}, - {Kind::In, "in"}, - {Kind::IncrPostfix, "++"}, - {Kind::IncrPrefix, "++"}, - {Kind::Index, "index"}, - {Kind::IndexAssign, "index_assign"}, - {Kind::Lower, "<"}, - {Kind::LowerEqual, "<="}, - {Kind::Member, "."}, - {Kind::MemberCall, "method call"}, - {Kind::Negate, "~"}, - {Kind::New, "new"}, - {Kind::Difference, "-"}, - {Kind::DifferenceAssign, "-="}, - {Kind::Modulo, "%"}, - {Kind::Multiple, "*"}, - {Kind::MultipleAssign, "*="}, - {Kind::Sum, "+"}, - {Kind::SumAssign, "+="}, - {Kind::Power, "**"}, - {Kind::ShiftLeft, "<<"}, - {Kind::ShiftRight, ">>"}, - {Kind::SignNeg, "-"}, - {Kind::SignPos, "+"}, - {Kind::Size, "size"}, - {Kind::TryMember, ".?"}, - {Kind::Unequal, "!="}, - {Kind::Unknown, ""}, - {Kind::Unpack, "unpack"}, - {Kind::Unset, "unset"}}; +constexpr util::enum_::Value kinds[] = {{Kind::Add, "add"}, {Kind::Begin, "begin"}, + {Kind::BitAnd, "&"}, {Kind::BitOr, "|"}, + {Kind::BitXor, "^"}, {Kind::Call, "call"}, + {Kind::Cast, "cast"}, {Kind::CustomAssign, "="}, + {Kind::DecrPostfix, "--"}, {Kind::DecrPrefix, "--"}, + {Kind::Delete, "delete"}, {Kind::Deref, "*"}, + {Kind::Division, "/"}, {Kind::DivisionAssign, "/="}, + {Kind::Equal, "=="}, {Kind::End, "end"}, + {Kind::Greater, ">"}, {Kind::GreaterEqual, ">="}, + {Kind::HasMember, "?."}, {Kind::In, "in"}, + {Kind::IncrPostfix, "++"}, {Kind::IncrPrefix, "++"}, + {Kind::Index, "index"}, {Kind::IndexAssign, "index_assign"}, + {Kind::Lower, "<"}, {Kind::LowerEqual, "<="}, + {Kind::Member, "."}, {Kind::MemberCall, "method call"}, + {Kind::Negate, "~"}, {Kind::New, "new"}, + {Kind::Difference, "-"}, {Kind::DifferenceAssign, "-="}, + {Kind::Modulo, "%"}, {Kind::Multiple, "*"}, + {Kind::MultipleAssign, "*="}, {Kind::Sum, "+"}, + {Kind::SumAssign, "+="}, {Kind::Power, "**"}, + {Kind::ShiftLeft, "<<"}, {Kind::ShiftRight, ">>"}, + {Kind::SignNeg, "-"}, {Kind::SignPos, "+"}, + {Kind::Size, "size"}, {Kind::TryMember, ".?"}, + {Kind::Unequal, "!="}, {Kind::Unknown, ""}, + {Kind::Unpack, "unpack"}, {Kind::Unset, "unset"}}; } // namespace detail /** diff --git a/hilti/toolchain/include/ast/operators/address.h b/hilti/toolchain/include/ast/operators/address.h index 7de81351d..f61f5e773 100644 --- a/hilti/toolchain/include/ast/operators/address.h +++ b/hilti/toolchain/include/ast/operators/address.h @@ -5,7 +5,6 @@ #include #include #include -#include namespace hilti { namespace operator_ { diff --git a/hilti/toolchain/include/ast/operators/bytes.h b/hilti/toolchain/include/ast/operators/bytes.h index f7f39d505..fa20d31c4 100644 --- a/hilti/toolchain/include/ast/operators/bytes.h +++ b/hilti/toolchain/include/ast/operators/bytes.h @@ -194,7 +194,7 @@ BEGIN_METHOD(bytes, Strip) .result = type::Bytes(), .id = "strip", .args = {{.id = "side", - .type = type::constant(type::Library("hilti::rt::bytes::Side")), + .type = type::constant(type::Library("::hilti::rt::bytes::Side")), .optional = true}, {.id = "set", .type = type::constant(type::Bytes()), .optional = true}}, .doc = R"( diff --git a/hilti/toolchain/include/ast/operators/common.h b/hilti/toolchain/include/ast/operators/common.h index 8a643a268..9120bd850 100644 --- a/hilti/toolchain/include/ast/operators/common.h +++ b/hilti/toolchain/include/ast/operators/common.h @@ -54,7 +54,7 @@ private: \ std::string doc() const { return signature().doc; } \ \ - hilti::Type result(const std::vector& ops) const { \ + hilti::Type result(const hilti::node::Range& ops) const { \ return *hilti::operator_::type(signature().result, ops, ops); \ } \ \ @@ -210,8 +210,8 @@ private: #define END_METHOD \ __END_METHOD \ \ - hilti::Type result(const std::vector& ops) const { \ - return *hilti::operator_::type(signature().result, ops, ops); \ + hilti::Type result(const hilti::node::Range& ops) const { \ + return *hilti::operator_::type(signature().result, hilti::node::Range(ops), ops); \ } \ \ bool isLhs() const { return false; } \ @@ -238,13 +238,12 @@ private: #define END_CTOR \ std::vector operands() const { \ - return {{.type = type::constant(hilti::type::Type_(ctorType()))}, \ - {.type = hilti::type::OperandList(signature().args)}}; \ + return {{.type = hilti::type::Type_(ctorType())}, {.type = hilti::type::OperandList(signature().args)}}; \ } \ \ std::string doc() const { return signature().doc; } \ \ - hilti::Type result(const std::vector& ops) const { \ + hilti::Type result(const hilti::node::Range& ops) const { \ if ( ops.size() ) \ return ops[0].type().as().typeValue(); \ \ diff --git a/hilti/toolchain/include/ast/operators/function.h b/hilti/toolchain/include/ast/operators/function.h index 18850eb96..a5f8b6e3e 100644 --- a/hilti/toolchain/include/ast/operators/function.h +++ b/hilti/toolchain/include/ast/operators/function.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -24,7 +25,7 @@ class Call : public hilti::expression::ResolvedOperatorBase { struct Operator : public hilti::trait::isOperator { Operator(const Scope::Referee& r, const type::Function& ftype) { auto op0 = operator_::Operand{.type = type::Any()}; // IDs won't be resolved - auto op1 = operator_::Operand{.type = ftype.operands()}; + auto op1 = operator_::Operand{.type = type::OperandList::fromParameters(ftype.parameters())}; _referee = r; _operands = {op0, op1}; _result = ftype.result().type(); @@ -32,7 +33,7 @@ class Call : public hilti::expression::ResolvedOperatorBase { static operator_::Kind kind() { return operator_::Kind::Call; } std::vector operands() const { return _operands; } - Type result(const std::vector& /* ops */) const { return _result; } + Type result(const hilti::node::Range& /* ops */) const { return _result; } bool isLhs() const { return false; } void validate(const expression::ResolvedOperator& /* i */, operator_::position_t /* p */) const {} std::string doc() const { return ""; } diff --git a/hilti/toolchain/include/ast/operators/generic.h b/hilti/toolchain/include/ast/operators/generic.h index fda407d2a..4f50550da 100644 --- a/hilti/toolchain/include/ast/operators/generic.h +++ b/hilti/toolchain/include/ast/operators/generic.h @@ -19,11 +19,11 @@ namespace hilti { namespace operator_ { BEGIN_OPERATOR_CUSTOM(generic, Unpack) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); - auto data_type = ops[1].type().as().types()[0]; + const auto& data_type = ops[1].type().as().elements()[0].type(); return type::Result(type::Tuple({ops[0].type().as().typeValue(), data_type}, ops[0].meta())); } @@ -34,7 +34,7 @@ BEGIN_OPERATOR_CUSTOM(generic, Unpack) } void validate(const expression::ResolvedOperator& i, operator_::position_t p) const { - auto data_type = i.op1().type().template as().types()[0]; + const auto& data_type = i.op1().type().template as().elements()[0].type(); if ( ! (data_type.isA() || data_type.isA()) ) p.node.addError("unpack() can be used only with bytes or a stream view as input"); @@ -44,7 +44,7 @@ BEGIN_OPERATOR_CUSTOM(generic, Unpack) END_OPERATOR_CUSTOM BEGIN_OPERATOR_CUSTOM(generic, Begin) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); @@ -68,7 +68,7 @@ BEGIN_OPERATOR_CUSTOM(generic, Begin) END_OPERATOR_CUSTOM BEGIN_OPERATOR_CUSTOM(generic, End) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); @@ -92,7 +92,7 @@ BEGIN_OPERATOR_CUSTOM(generic, End) END_OPERATOR_CUSTOM BEGIN_OPERATOR_CUSTOM(generic, New) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly("strong_ref"); @@ -150,7 +150,9 @@ class CastedCoercion : public hilti::expression::ResolvedOperatorBase { static operator_::Kind kind() { return operator_::Kind::Cast; } std::vector operands() const { return {}; } // Won't participate in overload resolution - Type result(const std::vector& ops) const { return ops[1].as().typeValue(); } + Type result(const hilti::node::Range& ops) const { + return ops[1].as().typeValue(); + } bool isLhs() const { return false; } void validate(const expression::ResolvedOperator& /* i */, operator_::position_t /* p */) const {} std::string doc() const { return ""; } diff --git a/hilti/toolchain/include/ast/operators/map.h b/hilti/toolchain/include/ast/operators/map.h index 5cb662d19..07afd0ece 100644 --- a/hilti/toolchain/include/ast/operators/map.h +++ b/hilti/toolchain/include/ast/operators/map.h @@ -17,8 +17,8 @@ namespace operator_ { namespace detail { static inline auto constantKeyType(unsigned int op, const char* doc = "") { - return [=](const std::vector& /* orig_ops */, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& /* orig_ops */, + const hilti::node::Range& resolved_ops) -> std::optional { if ( resolved_ops.empty() ) return type::DocOnly(doc); @@ -57,7 +57,7 @@ STANDARD_OPERATOR_2(map, Unequal, type::Bool(), type::constant(type::Map(type::W operator_::sameTypeAs(0, "map<*>"), "Compares two maps element-wise."); STANDARD_OPERATOR_2(map, In, type::Bool(), type::Any(), type::constant(type::Map(type::Wildcard())), "Returns true if an element is part of the map."); -STANDARD_OPERATOR_2(map, Delete, type::Void(), type::Map(type::Wildcard()), detail::constantKeyType(0, "element"), +STANDARD_OPERATOR_2(map, Delete, type::void_, type::Map(type::Wildcard()), detail::constantKeyType(0, "element"), "Removes an element from the map.") STANDARD_OPERATOR_2x(map, IndexConst, Index, operator_::constantElementType(0), @@ -68,7 +68,7 @@ STANDARD_OPERATOR_2x_lhs(map, IndexNonConst, Index, operator_::elementType(0), t "Returns the map's element for the given key. The key must exist, otherwise the operation " "will throw a runtime error."); -STANDARD_OPERATOR_3(map, IndexAssign, type::Void(), type::Map(type::Wildcard()), type::Any(), type::Any(), +STANDARD_OPERATOR_3(map, IndexAssign, type::void_, type::Map(type::Wildcard()), type::Any(), type::Any(), "Updates the map value for a given key. If the key does not exist a new element is inserted."); BEGIN_METHOD(map, Get) @@ -88,7 +88,7 @@ END_METHOD BEGIN_METHOD(map, Clear) auto signature() const { return Signature{.self = type::Map(type::Wildcard()), - .result = type::Void(), + .result = type::void_, .id = "clear", .args = {}, .doc = R"( diff --git a/hilti/toolchain/include/ast/operators/regexp.h b/hilti/toolchain/include/ast/operators/regexp.h index a56e25f3c..1718fd40f 100644 --- a/hilti/toolchain/include/ast/operators/regexp.h +++ b/hilti/toolchain/include/ast/operators/regexp.h @@ -89,7 +89,7 @@ END_METHOD BEGIN_METHOD(regexp_match_state, AdvanceBytes) auto signature() const { - return Signature{.self = type::Library("hilti::rt::regexp::MatchState"), + return Signature{.self = type::Library("::hilti::rt::regexp::MatchState"), .result = type::Tuple({type::SignedInteger(32), type::stream::View()}), .id = "advance", .args = {{.id = "data", .type = type::constant(type::Bytes())}, @@ -110,7 +110,7 @@ END_METHOD BEGIN_METHOD(regexp_match_state, AdvanceView) auto signature() const { - return Signature{.self = type::Library("hilti::rt::regexp::MatchState"), + return Signature{.self = type::Library("::hilti::rt::regexp::MatchState"), .result = type::Tuple({type::SignedInteger(32), type::stream::View()}), .id = "advance", .args = {{.id = "data", .type = type::constant(type::stream::View())}}, diff --git a/hilti/toolchain/include/ast/operators/set.h b/hilti/toolchain/include/ast/operators/set.h index e6de2bd1a..49dbe981a 100644 --- a/hilti/toolchain/include/ast/operators/set.h +++ b/hilti/toolchain/include/ast/operators/set.h @@ -36,15 +36,15 @@ STANDARD_OPERATOR_2(set, Unequal, type::Bool(), type::constant(type::Set(type::W operator_::sameTypeAs(0, "set<*>"), "Compares two sets element-wise."); STANDARD_OPERATOR_2(set, In, type::Bool(), type::Any(), type::constant(type::Set(type::Wildcard())), "Returns true if an element is part of the set."); -STANDARD_OPERATOR_2(set, Add, type::Void(), type::Set(type::Wildcard()), operator_::constantElementType(0, "element"), +STANDARD_OPERATOR_2(set, Add, type::void_, type::Set(type::Wildcard()), operator_::constantElementType(0, "element"), "Adds an element to the set.") -STANDARD_OPERATOR_2(set, Delete, type::Void(), type::Set(type::Wildcard()), - operator_::constantElementType(0, "element"), "Removes an element from the set.") +STANDARD_OPERATOR_2(set, Delete, type::void_, type::Set(type::Wildcard()), operator_::constantElementType(0, "element"), + "Removes an element from the set.") BEGIN_METHOD(set, Clear) auto signature() const { return Signature{.self = type::Set(type::Wildcard()), - .result = type::Void(), + .result = type::void_, .id = "clear", .args = {}, .doc = R"( diff --git a/hilti/toolchain/include/ast/operators/signed-integer.h b/hilti/toolchain/include/ast/operators/signed-integer.h index 3cb437c3f..68e2fb330 100644 --- a/hilti/toolchain/include/ast/operators/signed-integer.h +++ b/hilti/toolchain/include/ast/operators/signed-integer.h @@ -18,8 +18,8 @@ namespace operator_ { namespace detail { inline static auto widestTypeSigned() { - return [=](const std::vector& orig_ops, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& orig_ops, + const hilti::node::Range& resolved_ops) -> std::optional { if ( orig_ops.empty() && resolved_ops.empty() ) return type::DocOnly("int<*>"); diff --git a/hilti/toolchain/include/ast/operators/stream.h b/hilti/toolchain/include/ast/operators/stream.h index 308e8e141..037d261df 100644 --- a/hilti/toolchain/include/ast/operators/stream.h +++ b/hilti/toolchain/include/ast/operators/stream.h @@ -249,7 +249,7 @@ STANDARD_OPERATOR_2x(stream, SumAssignBytes, SumAssign, type::Stream(), type::St BEGIN_METHOD(stream, Freeze) auto signature() const { - return Signature{.self = type::Stream(), .result = type::Void(), .id = "freeze", .args = {}, .doc = R"( + return Signature{.self = type::Stream(), .result = type::void_, .id = "freeze", .args = {}, .doc = R"( Freezes the stream value. Once frozen, one cannot append any more data to a frozen stream value (unless it gets unfrozen first). If the value is already frozen, the operation does not change anything. @@ -259,7 +259,7 @@ END_METHOD BEGIN_METHOD(stream, Unfreeze) auto signature() const { - return Signature{.self = type::Stream(), .result = type::Void(), .id = "unfreeze", .args = {}, .doc = R"( + return Signature{.self = type::Stream(), .result = type::void_, .id = "unfreeze", .args = {}, .doc = R"( Unfreezes the stream value. A unfrozen stream value can be further modified. If the value is already unfrozen (which is the default), the operation does not change anything. @@ -294,7 +294,7 @@ END_METHOD BEGIN_METHOD(stream, Trim) auto signature() const { return Signature{.self = type::Stream(), - .result = type::Void(), + .result = type::void_, .id = "trim", .args = {{.id = "i", .type = type::stream::Iterator()}}, .doc = R"( diff --git a/hilti/toolchain/include/ast/operators/string.h b/hilti/toolchain/include/ast/operators/string.h index 49dcc2d3c..a188b6f8f 100644 --- a/hilti/toolchain/include/ast/operators/string.h +++ b/hilti/toolchain/include/ast/operators/string.h @@ -39,7 +39,7 @@ Converts the string into a binary representation encoded with the given characte END_METHOD BEGIN_OPERATOR_CUSTOM(string, Modulo) - Type result(const std::vector& /* ops */) const { return type::String(); } + Type result(const hilti::node::Range& /* ops */) const { return type::String(); } bool isLhs() const { return false; } diff --git a/hilti/toolchain/include/ast/operators/struct.h b/hilti/toolchain/include/ast/operators/struct.h index 184eadab9..fce3e602b 100644 --- a/hilti/toolchain/include/ast/operators/struct.h +++ b/hilti/toolchain/include/ast/operators/struct.h @@ -51,7 +51,7 @@ static inline Type itemType(const Expression& op0, const Expression& op1) { } // namespace struct_::detail BEGIN_OPERATOR_CUSTOM(struct_, Unset) - Type result(const std::vector& ops) const { return type::Void(); } + Type result(const hilti::node::Range& ops) const { return type::void_; } bool isLhs() const { return true; } @@ -72,11 +72,11 @@ Clears an optional field. END_OPERATOR_CUSTOM_x BEGIN_OPERATOR_CUSTOM_x(struct_, MemberNonConst, Member) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); - return detail::itemType(ops[0], ops[1]); + return type::nonConstant(detail::itemType(ops[0], ops[1]), true); } bool isLhs() const { return true; } @@ -100,11 +100,11 @@ triggers an exception. END_OPERATOR_CUSTOM_x BEGIN_OPERATOR_CUSTOM_x(struct_, MemberConst, Member) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); - return detail::itemType(ops[0], ops[1]); + return type::constant(detail::itemType(ops[0], ops[1])); } bool isLhs() const { return false; } @@ -128,7 +128,7 @@ triggers an exception. END_OPERATOR_CUSTOM_x BEGIN_OPERATOR_CUSTOM(struct_, TryMember) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); @@ -159,7 +159,7 @@ exception differently). END_OPERATOR_CUSTOM BEGIN_OPERATOR_CUSTOM(struct_, HasMember) - Type result(const std::vector& /* ops */) const { return type::Bool(); } + Type result(const hilti::node::Range& /* ops */) const { return type::Bool(); } bool isLhs() const { return false; } @@ -186,11 +186,11 @@ class MemberCall : public hilti::expression::ResolvedOperatorBase { using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; struct Operator : public hilti::trait::isOperator { - Operator(const type::Struct& stype, const type::struct_::Field& f) { + Operator(const type::Struct& stype, const declaration::Field& f) { auto ftype = f.type().as(); auto op0 = operator_::Operand{.type = stype}; auto op1 = operator_::Operand{.type = type::Member(f.id())}; - auto op2 = operator_::Operand{.type = ftype.operands()}; + auto op2 = operator_::Operand{.type = type::OperandList::fromParameters(ftype.parameters())}; _field = f; _operands = {op0, op1, op2}; _result = ftype.result().type(); @@ -198,7 +198,7 @@ class MemberCall : public hilti::expression::ResolvedOperatorBase { static operator_::Kind kind() { return operator_::Kind::MemberCall; } std::vector operands() const { return _operands; } - Type result(const std::vector& /* ops */) const { return _result; } + Type result(const hilti::node::Range& /* ops */) const { return _result; } bool isLhs() const { return false; } void validate(const expression::ResolvedOperator& /* i */, operator_::position_t p) const {} std::string doc() const { return ""; } @@ -215,7 +215,7 @@ class MemberCall : public hilti::expression::ResolvedOperatorBase { } private: - type::struct_::Field _field; + declaration::Field _field; std::vector _operands; Type _result; }; diff --git a/hilti/toolchain/include/ast/operators/tuple.h b/hilti/toolchain/include/ast/operators/tuple.h index e90dbb636..19257a6d5 100644 --- a/hilti/toolchain/include/ast/operators/tuple.h +++ b/hilti/toolchain/include/ast/operators/tuple.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -18,7 +19,7 @@ STANDARD_OPERATOR_2(tuple, Unequal, type::Bool(), type::constant(type::Tuple(typ operator_::sameTypeAs(0, "tuple<*>"), "Compares two tuples element-wise."); BEGIN_OPERATOR_CUSTOM(tuple, Index) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); @@ -33,12 +34,12 @@ BEGIN_OPERATOR_CUSTOM(tuple, Index) if ( ! i ) return type::unknown; - const auto& types = ops[0].type().as().types(); + const auto& elements = ops[0].type().as().elements(); - if ( types.size() <= i->value() ) + if ( static_cast(elements.size()) <= i->value() ) return type::unknown; - return types[i->value()]; + return elements[i->value()].type(); } bool isLhs() const { return true; } @@ -50,7 +51,8 @@ BEGIN_OPERATOR_CUSTOM(tuple, Index) void validate(const expression::ResolvedOperator& i, operator_::position_t p) const { if ( auto ec = i.op1().tryAs() ) if ( auto c = ec->ctor().tryAs() ) { - if ( c->value() < 0 || c->value() >= i.op0().type().as().types().size() ) + if ( c->value() < 0 || + c->value() >= static_cast(i.op0().type().as().elements().size()) ) p.node.addError("tuple index out of range"); return; @@ -65,7 +67,7 @@ BEGIN_OPERATOR_CUSTOM(tuple, Index) END_OPERATOR_CUSTOM BEGIN_OPERATOR_CUSTOM(tuple, Member) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); @@ -74,7 +76,7 @@ BEGIN_OPERATOR_CUSTOM(tuple, Member) if ( ! elem ) return type::unknown; - return elem->second; + return elem->second->type(); } bool isLhs() const { return true; } @@ -94,6 +96,56 @@ BEGIN_OPERATOR_CUSTOM(tuple, Member) std::string doc() const { return "Extracts the tuple element corresponding to the given ID."; } END_OPERATOR_CUSTOM_x +BEGIN_OPERATOR_CUSTOM(tuple, CustomAssign) + Type result(const hilti::node::Range& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return ops[0].type(); + } + + bool isLhs() const { return false; } + + std::vector operands() const { + // The operator gets instantiated only through the normalizer, but this is used for documentation. + return {{ + .type = type::Member(type::Wildcard()), + .doc = "(x, ..., y)", + }, + {.type = type::Tuple(type::Wildcard()), .doc = ""}}; + return {}; + } + + void validate(const expression::ResolvedOperator& i, operator_::position_t p) const { + auto lhs = i.operands()[0].as().ctor().as(); + auto lhs_type = lhs.type().as(); + auto rhs_type = i.operands()[1].type().tryAs(); + if ( ! rhs_type ) { + p.node.addError("rhs is not a tuple"); + return; + } + + if ( lhs_type.elements().size() != rhs_type->elements().size() ) { + p.node.addError("cannot assign tuples of different length"); + return; + } + + for ( auto i = 0u; i < lhs_type.elements().size(); i++ ) { + const auto& lhs_elem = lhs.value()[i]; + const auto& lhs_elem_type = lhs_type.elements()[i].type(); + const auto& rhs_elem_type = rhs_type->elements()[i].type(); + + if ( ! lhs_elem.isLhs() ) + p.node.addError(util::fmt("cannot assign to expression: %s", to_node(lhs_elem))); + + if ( ! type::sameExceptForConstness(lhs_elem_type, rhs_elem_type) ) + p.node.addError(util::fmt("type mismatch for element %d in assignment, expected type %s but got %s", i, + lhs_elem_type, rhs_elem_type)); + } + } + + std::string doc() const { return "Assigns element-wise to the left-hand-side tuple"; } +END_OPERATOR_CUSTOM_x } // namespace operator_ } // namespace hilti diff --git a/hilti/toolchain/include/ast/operators/union.h b/hilti/toolchain/include/ast/operators/union.h index 5709304f6..b0deffab5 100644 --- a/hilti/toolchain/include/ast/operators/union.h +++ b/hilti/toolchain/include/ast/operators/union.h @@ -45,7 +45,7 @@ static inline Type itemType(const Expression& op0, const Expression& op1) { // Returns the result type of a union method referenced by an operand. static inline Type methodResult(const Expression& /* op0 */, const Expression& op1) { - if ( auto f = memberExpression(op1).memberType()->template tryAs() ) + if ( auto f = memberExpression(op1).type().template tryAs() ) return f->result().type(); return type::unknown; @@ -59,7 +59,7 @@ STANDARD_OPERATOR_2(union_, Unequal, type::Bool(), type::constant(type::Union(ty operator_::sameTypeAs(0, "union<*>"), "Compares two unions element-wise."); BEGIN_OPERATOR_CUSTOM_x(union_, MemberConst, Member) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); @@ -86,7 +86,7 @@ this triggers an exception. END_OPERATOR_CUSTOM_x BEGIN_OPERATOR_CUSTOM_x(union_, MemberNonConst, Member) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); @@ -113,7 +113,7 @@ this triggers an exception unless the value is only being assigned to. END_OPERATOR_CUSTOM_x BEGIN_OPERATOR_CUSTOM(union_, HasMember) - Type result(const std::vector& /* ops */) const { return type::Bool(); } + Type result(const hilti::node::Range& /* ops */) const { return type::Bool(); } bool isLhs() const { return false; } diff --git a/hilti/toolchain/include/ast/operators/unsigned-integer.h b/hilti/toolchain/include/ast/operators/unsigned-integer.h index 99f0d7a3b..1cc343d89 100644 --- a/hilti/toolchain/include/ast/operators/unsigned-integer.h +++ b/hilti/toolchain/include/ast/operators/unsigned-integer.h @@ -16,8 +16,8 @@ namespace operator_ { namespace detail { inline static auto widestTypeUnsigned() { - return [=](const std::vector& orig_ops, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& orig_ops, + const hilti::node::Range& resolved_ops) -> std::optional { if ( orig_ops.empty() && resolved_ops.empty() ) return type::DocOnly("uint<*>"); diff --git a/hilti/toolchain/include/ast/operators/vector.h b/hilti/toolchain/include/ast/operators/vector.h index 41554a172..152716fa2 100644 --- a/hilti/toolchain/include/ast/operators/vector.h +++ b/hilti/toolchain/include/ast/operators/vector.h @@ -48,7 +48,7 @@ STANDARD_OPERATOR_2(vector, SumAssign, operator_::sameTypeAs(0, "vector<*>"), ty BEGIN_METHOD(vector, Assign) auto signature() const { return Signature{.self = type::Vector(type::Wildcard()), - .result = type::Void(), + .result = type::void_, .id = "assign", .args = {{.id = "i", .type = type::UnsignedInteger(64)}, {.id = "x", .type = type::Any()}}, .doc = R"( @@ -62,7 +62,7 @@ END_METHOD BEGIN_METHOD(vector, PushBack) auto signature() const { return Signature{.self = type::Vector(type::Wildcard()), - .result = type::Void(), + .result = type::void_, .id = "push_back", .args = {{.id = "x", .type = type::Any()}}, .doc = R"( @@ -74,7 +74,7 @@ END_METHOD BEGIN_METHOD(vector, PopBack) auto signature() const { return Signature{.self = type::Vector(type::Wildcard()), - .result = type::Void(), + .result = type::void_, .id = "pop_back", .args = {}, .doc = R"( @@ -113,7 +113,7 @@ END_METHOD BEGIN_METHOD(vector, Reserve) auto signature() const { return Signature{.self = type::Vector(type::Wildcard()), - .result = type::Void(), + .result = type::void_, .id = "reserve", .args = {{.id = "n", .type = type::constant(type::UnsignedInteger(64))}}, .doc = R"( @@ -127,7 +127,7 @@ END_METHOD BEGIN_METHOD(vector, Resize) auto signature() const { return Signature{.self = type::Vector(type::Wildcard()), - .result = type::Void(), + .result = type::void_, .id = "resize", .args = {{.id = "n", .type = type::constant(type::UnsignedInteger(64))}}, .doc = R"( diff --git a/hilti/toolchain/include/ast/scope-lookup.h b/hilti/toolchain/include/ast/scope-lookup.h index fbec290c6..71a114828 100644 --- a/hilti/toolchain/include/ast/scope-lookup.h +++ b/hilti/toolchain/include/ast/scope-lookup.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include diff --git a/hilti/toolchain/include/ast/scope.h b/hilti/toolchain/include/ast/scope.h index df71a3f1b..430be7e46 100644 --- a/hilti/toolchain/include/ast/scope.h +++ b/hilti/toolchain/include/ast/scope.h @@ -7,50 +7,37 @@ #include #include #include +#include #include -#include +#include #include namespace hilti { +class Declaration; class ID; /** - * Identifier scope. A scope maps identifiers to AST nodes (more precisely: - * to referneces to AST nodes). An identifier can be mapped to more than one - * node. + * Identifier scope. A scope maps identifiers to AST nodes (more precisely: to + * references to AST nodes). An identifier can be mapped to more than one node. */ class Scope : public intrusive_ptr::ManagedObject { public: Scope() = default; ~Scope() = default; - /** - * Inserts a new identifier mapping. If a mapping for the ID already - * exists, the new one is appended to it. - * - * @param id id to map - * @param n reference to the node that `id` is to be mapped to - */ - void insert(const ID& id, NodeRef n); - - /** - * Inserts a new identifier mapping. - * - * @param id id to map - * @param n node to map to; as a scope always maps to *references*, - * this takes ownership of the node and stores it internally - */ - void insert(const ID& id, Node&& n); + void insert(NodeRef&& n); + void insert(ID id, NodeRef&& n); + void insertNotFound(ID id); - /** Returns true if there's at least one mapping for an ID. */ + /** Returns if there's at least one mapping for an ID. */ bool has(const ID& id) const { return ! _findID(id).empty(); } /** Result typer for the lookup methods. */ struct Referee { NodeRef node; /**< node that ID maps to */ - std::string qualified; /**< qualified ID with full path used to find it */ + std::string qualified; /**< qualified ID with full path used to find it */ bool external{}; /**< true if found in a different (imported) module */ }; @@ -71,26 +58,6 @@ class Scope : public intrusive_ptr::ManagedObject { /** Returns all mappings of the scope. */ const auto& items() const { return _items; } - /** - * Copies the scope's mappings into another one. - */ - void copyInto(Scope* dst) const { - for ( const auto& i : _items ) - dst->_items.insert(i); - } - - /** - * Moves the scope's mappings into another one. The source scope will be - * empty afterwards. - */ - void moveInto(Scope* dst) { - // dst->_items.merge(std::move(_items)); // C++17, not supported by libc++ yet it seems - for ( const auto& i : _items ) - dst->_items.insert(i); - - _items.clear(); - } - /** * Prints out a debugging representation of the scope's content. * @@ -111,7 +78,6 @@ class Scope : public intrusive_ptr::ManagedObject { std::vector _findID(const Scope* scope, const ID& id, bool external = false) const; ItemMap _items; - std::vector> _nodes; // Nodes without other owners. }; } // namespace hilti diff --git a/hilti/toolchain/include/ast/statement.api b/hilti/toolchain/include/ast/statement.api index f34814191..38910bdb9 100644 --- a/hilti/toolchain/include/ast/statement.api +++ b/hilti/toolchain/include/ast/statement.api @@ -21,11 +21,5 @@ class Statement(trait::isStatement) : trait::isNode { void setMeta(Meta m); /** Implements the `Node` interface. */ - const NodeRef& originalNode() const; - - /** Implements the `Node` interface. */ - void setOriginalNode(const NodeRef& n); - - /** Implements the `Node` interface. */ - void clearCache(); + bool pruneWalk() const; }; diff --git a/hilti/toolchain/include/ast/statements/assert.h b/hilti/toolchain/include/ast/statements/assert.h index 56cc23e7c..47362e404 100644 --- a/hilti/toolchain/include/ast/statements/assert.h +++ b/hilti/toolchain/include/ast/statements/assert.h @@ -47,8 +47,10 @@ class Assert : public NodeBase, public hilti::trait::isStatement { bool expectsException() const { return _expects_exception; } const auto& expression() const { return child<::hilti::Expression>(0); } - auto exception() const { return type::effectiveOptionalType(childs()[1].tryAs()); } - auto message() const { return childs()[2].tryReferenceAs<::hilti::Expression>(); } + auto exception() const { return childs()[1].tryAs(); } + auto message() const { return childs()[2].tryAs<::hilti::Expression>(); } + + void setCondition(hilti::Expression c) { childs()[0] = std::move(c); } bool operator==(const Assert& other) const { return _expects_exception == other._expects_exception && expression() == other.expression() && @@ -61,19 +63,6 @@ class Assert : public NodeBase, public hilti::trait::isStatement { /** Implements the `Node` interface. */ auto properties() const { return node::Properties{{"expects-exception", _expects_exception}}; } - /** - * Returns a new `assert` statement with the expression replaced. - * - * @param e original statement - * @param c new expresssion - * @return new statement that's equal to original one but with the expression replaced - */ - static Statement setCondition(const Assert& e, const hilti::Expression& c) { - auto x = Statement(e)._clone().as(); - x.childs()[0] = c; - return x; - } - private: bool _expects_exception = false; }; diff --git a/hilti/toolchain/include/ast/statements/declaration.h b/hilti/toolchain/include/ast/statements/declaration.h index a6cd75512..cc302b5ae 100644 --- a/hilti/toolchain/include/ast/statements/declaration.h +++ b/hilti/toolchain/include/ast/statements/declaration.h @@ -16,6 +16,7 @@ class Declaration : public NodeBase, public hilti::trait::isStatement { Declaration(hilti::Declaration d, Meta m = Meta()) : NodeBase({std::move(d)}, std::move(m)) {} const auto& declaration() const { return child<::hilti::Declaration>(0); } + auto declarationRef() const { return NodeRef(childs()[0]); } bool operator==(const Declaration& other) const { return declaration() == other.declaration(); } diff --git a/hilti/toolchain/include/ast/statements/for.h b/hilti/toolchain/include/ast/statements/for.h index 458388c03..8ffecdf32 100644 --- a/hilti/toolchain/include/ast/statements/for.h +++ b/hilti/toolchain/include/ast/statements/for.h @@ -16,20 +16,18 @@ namespace statement { class For : public NodeBase, public hilti::trait::isStatement { public: For(hilti::ID id, hilti::Expression seq, Statement body, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(seq), std::move(body)), std::move(m)) {} + : NodeBase(nodes(declaration::LocalVariable(std::move(id), true, id.meta()), std::move(seq), std::move(body)), + std::move(m)) {} - const auto& id() const { return child(0); } + const auto& local() const { return child(0); } + auto localRef() const { return NodeRef(childs()[0]); } const auto& sequence() const { return child(1); } const auto& body() const { return child(2); } - /** - * Returns the body's scope. Note that the scope is shared among any - * copies of an instance. - */ - IntrusivePtr scope() const { return childs()[2].scope(); } + void setLocalType(Type t) { childs()[0].as().setType(std::move(t)); } bool operator==(const For& other) const { - return id() == other.id() && sequence() == other.sequence() && body() == other.body(); + return local() == other.local() && sequence() == other.sequence() && body() == other.body(); } /** Internal method for use by builder API only. */ diff --git a/hilti/toolchain/include/ast/statements/if.h b/hilti/toolchain/include/ast/statements/if.h index 4bd8d45ad..dcb78e328 100644 --- a/hilti/toolchain/include/ast/statements/if.h +++ b/hilti/toolchain/include/ast/statements/if.h @@ -15,8 +15,8 @@ namespace statement { /** AST node for a "if" statement. */ class If : public NodeBase, public hilti::trait::isStatement { public: - If(const hilti::Declaration& init, std::optional cond, Statement true_, - std::optional false_, Meta m = Meta()) + If(hilti::Declaration init, std::optional cond, Statement true_, std::optional false_, + Meta m = Meta()) : NodeBase(nodes(init, std::move(cond), std::move(true_), std::move(false_)), std::move(m)) { if ( ! init.isA() ) logger().internalError("initialization for 'if' must be a local declaration"); @@ -25,10 +25,16 @@ class If : public NodeBase, public hilti::trait::isStatement { If(hilti::Expression cond, Statement true_, std::optional false_, Meta m = Meta()) : NodeBase(nodes(node::none, std::move(cond), std::move(true_), std::move(false_)), std::move(m)) {} - auto init() const { return childs()[0].tryReferenceAs(); } - auto condition() const { return childs()[1].tryReferenceAs(); } + auto init() const { return childs()[0].tryAs(); } + auto initRef() const { + return childs()[0].isA() ? NodeRef(childs()[0]) : NodeRef(); + } + auto condition() const { return childs()[1].tryAs(); } const auto& true_() const { return child(2); } - auto false_() const { return childs()[3].tryReferenceAs(); } + auto false_() const { return childs()[3].tryAs(); } + + void setCondition(hilti::Expression e) { childs()[1] = std::move(e); } + void removeFalse() { childs()[3] = node::none; } bool operator==(const If& other) const { return init() == other.init() && condition() == other.condition() && true_() == other.true_() && @@ -46,44 +52,6 @@ class If : public NodeBase, public hilti::trait::isStatement { /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } - - /** - * Returns a new "if" statement with the init expression replaced. - * - * @param e original statement - * @param d new init expresssion - * @return new statement that's equal to original one but with the init expression replaced - */ - static Statement setInit(const If& e, const hilti::Declaration& d) { - auto x = Statement(e)._clone().as(); - x.childs()[0] = d; - return x; - } - - /** - * Returns a new "if" statement with the condition expression replaced. - * - * @param e original statement - * @param c new condition expresssion - * @return new statement that's equal to original one but with the condition replaced - */ - static Statement setCondition(const If& e, const hilti::Expression& c) { - auto x = Statement(e)._clone().as(); - x.childs()[1] = c; - return x; - } - - /** - * Returns a new "if" statement with the false branch removed. - * - * @param e original statement - * @return new statement that's equal to original one but with the false branch removed. - */ - static Statement removeElse(const If& e) { - auto x = Statement(e)._clone().as(); - x.childs()[3] = node::none; - return x; - } }; } // namespace statement diff --git a/hilti/toolchain/include/ast/statements/return.h b/hilti/toolchain/include/ast/statements/return.h index 09fa2e7f2..e723f8a5e 100644 --- a/hilti/toolchain/include/ast/statements/return.h +++ b/hilti/toolchain/include/ast/statements/return.h @@ -13,15 +13,12 @@ namespace statement { /** AST node for a "return" statement. */ class Return : public NodeBase, public hilti::trait::isStatement { public: - Return(Meta m = Meta()) : NodeBase({}, std::move(m)) {} + Return(Meta m = Meta()) : NodeBase({node::none}, std::move(m)) {} Return(hilti::Expression e, Meta m = Meta()) : NodeBase({std::move(e)}, std::move(m)) {} - std::optional expression() const { - if ( ! childs().empty() ) - return child<::hilti::Expression>(0); + auto expression() const { return childs()[0].tryAs(); } - return {}; - } + void setExpression(hilti::Expression c) { childs()[0] = std::move(c); } bool operator==(const Return& other) const { return expression() == other.expression(); } @@ -30,19 +27,6 @@ class Return : public NodeBase, public hilti::trait::isStatement { /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } - - /** - * Returns a new "return" statement with the expression replaced. - * - * @param e original statement - * @param c new expresssion - * @return new statement that's equal to original one but with the expression replaced - */ - static Statement setExpression(const Return& e, const hilti::Expression& c) { - auto x = Statement(e)._clone().as(); - x.childs()[0] = c; - return x; - } }; } // namespace statement diff --git a/hilti/toolchain/include/ast/statements/switch.h b/hilti/toolchain/include/ast/statements/switch.h index b2f23486d..d5558e175 100644 --- a/hilti/toolchain/include/ast/statements/switch.h +++ b/hilti/toolchain/include/ast/statements/switch.h @@ -55,22 +55,20 @@ class Case : public NodeBase { bool operator==(const Case& other) const { return expressions() == other.expressions() && body() == other.body(); } - /** - * Replaces a case's expresssions. - * - * @param c case to replace expressions in - * @param exprs new expressions - * @return new case that is a duplicate of *c* but has its expressions replaced with *expr* - */ - static Case setExpressions(Case c, std::vector exprs) { - c.childs() = hilti::nodes(c._bodyNode(), std::move(exprs)); - return c; - } - private: friend class hilti::statement::Switch; - void _addPreprocessedExpression(hilti::Expression e) { childs().emplace_back(std::move(e)); } + void _preprocessExpressions(const std::string& id) { + childs().erase(childs().begin() + _end_exprs, childs().end()); + childs().reserve(_end_exprs * 2); // avoid resizing/invalidation below on emplace + + for ( const auto& e : expressions() ) { + hilti::Expression n = + expression::UnresolvedOperator(operator_::Kind::Equal, {expression::UnresolvedID(ID(id)), e}, e.meta()); + + childs().emplace_back(std::move(n)); + } + } int _end_exprs{}; }; @@ -83,50 +81,38 @@ inline Node to_node(Case c) { return Node(std::move(c)); } class Switch : public NodeBase, public hilti::trait::isStatement { public: Switch(hilti::Expression cond, const std::vector& cases, Meta m = Meta()) - : NodeBase(nodes(node::none, std::move(cond), cases), std::move(m)) { - _preprocessCases("__x"); - } + : Switch(hilti::declaration::LocalVariable(hilti::ID("__x"), std::move(cond), true, m), std::move(cases), m) {} - Switch(const hilti::Declaration& init, hilti::Expression cond, const std::vector& cases, - Meta m = Meta()) - : NodeBase(nodes(init, std::move(cond), cases), std::move(m)) { - if ( ! init.isA() ) + Switch(const hilti::Declaration& cond, const std::vector& cases, Meta m = Meta()) + : NodeBase(nodes(cond, cases), std::move(m)) { + if ( ! cond.isA() ) logger().internalError("initialization for 'switch' must be a local declaration"); - _preprocessCases(init.id()); - } - - auto init() const { return childs()[0].tryReferenceAs(); } - const auto& expression() const { return childs()[1].as(); } - auto type() const { - if ( auto i = init() ) - return i->as().type(); - - return expression().type(); } - auto cases() const { - std::vector cases; - for ( auto& c : childs(2, -1) ) { - if ( ! c.isDefault() ) - cases.push_back(c); - } + const auto& condition() const { return childs()[0].as(); } + auto conditionRef() const { return NodeRef(childs()[0]); } + auto cases() const { return childs(1, -1); } - return cases; - } - - auto caseNodes() { return nodesOfType(); } - - std::optional default_() const { - for ( auto& c : childs(2, -1) ) { + hilti::optional_ref default_() const { + for ( const auto& c : childs(1, -1) ) { if ( c.isDefault() ) return c; } return {}; } + void preprocessCases() { + if ( _preprocessed ) + return; + + for ( auto c = childs().begin() + 1; c != childs().end(); c++ ) + c->as()._preprocessExpressions(condition().id()); + + _preprocessed = true; + } + bool operator==(const Switch& other) const { - return init() == other.init() && expression() == other.expression() && default_() == other.default_() && - cases() == other.cases(); + return condition() == other.condition() && default_() == other.default_() && cases() == other.cases(); } /** Internal method for use by builder API only. */ @@ -134,15 +120,10 @@ class Switch : public NodeBase, public hilti::trait::isStatement { /** Internal method for use by builder API only. */ void _addCase(switch_::Case case_) { - for ( const auto& c : case_.expressions() ) - case_._addPreprocessedExpression(expression::UnresolvedOperator(operator_::Kind::Equal, - {expression::UnresolvedID(ID(_id)), c}, - c.meta())); - addChild(std::move(case_)); + _preprocessed = false; } - /** Implements the `Statement` interface. */ auto isEqual(const Statement& other) const { return node::isEqual(this, other); } @@ -150,19 +131,7 @@ class Switch : public NodeBase, public hilti::trait::isStatement { auto properties() const { return node::Properties{}; } private: - void _preprocessCases(const std::string& id) { - _id = id; - - for ( uint64_t i = 2; i < childs().size(); i++ ) { - auto& case_ = childs()[i].as(); - for ( const auto& c : case_.expressions() ) - case_._addPreprocessedExpression(expression::UnresolvedOperator(operator_::Kind::Equal, - {expression::UnresolvedID(ID(id)), c}, - c.meta())); - } - } - - std::string _id; + bool _preprocessed = false; }; } // namespace statement diff --git a/hilti/toolchain/include/ast/statements/throw.h b/hilti/toolchain/include/ast/statements/throw.h index 06e5f3a71..30caa778c 100644 --- a/hilti/toolchain/include/ast/statements/throw.h +++ b/hilti/toolchain/include/ast/statements/throw.h @@ -16,7 +16,7 @@ class Throw : public NodeBase, public hilti::trait::isStatement { Throw(Meta m = Meta()) : NodeBase({node::none}, std::move(m)) {} Throw(hilti::Expression excpt, Meta m = Meta()) : NodeBase({std::move(excpt)}, std::move(m)) {} - auto expression() const { return childs()[0].tryReferenceAs(); } + auto expression() const { return childs()[0].tryAs(); } bool operator==(const Throw& other) const { return expression() == other.expression(); } diff --git a/hilti/toolchain/include/ast/statements/try.h b/hilti/toolchain/include/ast/statements/try.h index fbc61a015..93facd3c9 100644 --- a/hilti/toolchain/include/ast/statements/try.h +++ b/hilti/toolchain/include/ast/statements/try.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace hilti { namespace statement { @@ -27,14 +28,8 @@ class Catch : public NodeBase { } Catch() = default; - std::optional parameter() const { - auto d = childs()[0].tryAs(); - if ( d ) - return d->as(); - - return {}; - } - + auto parameter() const { return childs()[0].tryAs(); } + auto parameterRef() const { return childs()[0].isA() ? NodeRef(childs()[0]) : NodeRef(); } const auto& body() const { return child(1); } /** Internal method for use by builder API only. */ diff --git a/hilti/toolchain/include/ast/statements/while.h b/hilti/toolchain/include/ast/statements/while.h index daf6b2524..1553bdc9b 100644 --- a/hilti/toolchain/include/ast/statements/while.h +++ b/hilti/toolchain/include/ast/statements/while.h @@ -28,10 +28,16 @@ class While : public NodeBase, public hilti::trait::isStatement { While(hilti::Expression cond, Statement body, std::optional else_, Meta m = Meta()) : NodeBase(nodes(node::none, std::move(cond), std::move(body), std::move(else_)), std::move(m)) {} - auto init() const { return childs()[0].tryReferenceAs(); } - auto condition() const { return childs()[1].tryReferenceAs(); } + auto init() const { return childs()[0].tryAs(); } + auto initRef() const { + return childs()[0].isA() ? NodeRef(childs()[0]) : NodeRef(); + } + auto condition() const { return childs()[1].tryAs(); } const auto& body() const { return child(2); } - auto else_() const { return childs()[3].tryReferenceAs(); } + auto else_() const { return childs()[3].tryAs(); } + + void setCondition(hilti::Expression c) { childs()[1] = std::move(c); } + void setInit(hilti::Expression c) { childs()[0] = std::move(c); } bool operator==(const While& other) const { return init() == other.init() && condition() == other.condition() && body() == other.body() && @@ -49,32 +55,6 @@ class While : public NodeBase, public hilti::trait::isStatement { /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } - - /** - * Returns a new "while" statement with the init expression replaced. - * - * @param e original statement - * @param d new init expresssion - * @return new statement that's equal to original one but with the init expression replaced - */ - static Statement setInit(const While& e, const hilti::Declaration& d) { - auto x = Statement(e)._clone().as(); - x.childs()[0] = d; - return x; - } - - /** - * Returns a new "while" statement with the condition expression replaced. - * - * @param d original statement - * @param c new condition expresssion - * @return new statement that's equal to original one but with the condition replaced - */ - static Statement setCondition(const While& e, const hilti::Expression& c) { - auto x = Statement(e)._clone().as(); - x.childs()[1] = c; - return x; - } }; } // namespace statement diff --git a/hilti/toolchain/include/ast/type.api b/hilti/toolchain/include/ast/type.api index bf35640a2..4f5e5b250 100644 --- a/hilti/toolchain/include/ast/type.api +++ b/hilti/toolchain/include/ast/type.api @@ -3,7 +3,7 @@ /** Interface for HILTI types. */ class Type(hilti::trait::isType) : hilti::trait::isNode { /** Returns true if the type is equivalent to another HILTI type. */ - bool isEqual(const Type& other) const; + bool isEqual(const hilti::Type& other) const; /** * Returns any parameters associated with type. If a type is declared as @@ -19,29 +19,20 @@ class Type(hilti::trait::isType) : hilti::trait::isNode { */ bool isWildcard() const if hilti::type::trait::isParameterized else false; - /** Returns the set of flags associated with the type. */ - type::Flags flags() const; - - /** Returns true if a given type flag has been set. */ - bool hasFlag(type::Flag f) const; - /** Returns the type of an iterator for this type. */ - Type iteratorType(bool const_) const if hilti::type::trait::isIterable or hilti::type::trait::isView; + const hilti::Type& iteratorType(bool const_) const if hilti::type::trait::isIterable or hilti::type::trait::isView; /** Returns the type of an view for this type. */ - Type viewType() const if hilti::type::trait::isViewable; + const hilti::Type& viewType() const if hilti::type::trait::isViewable; /** Returns the type of elements the iterator traveres. */ - Type dereferencedType() const if hilti::type::trait::isDereferencable; + const hilti::Type& dereferencedType() const if hilti::type::trait::isDereferencable; /** Returns the type of elements the container stores. */ - Type elementType() const if hilti::type::trait::isIterable; + const hilti::Type& elementType() const if hilti::type::trait::isIterable; - /** - * Returns the fully deferenced type that this type corresponds to. This, - * for example, follows a type ID to the actual type it maps to. - */ - Type effectiveType() const if hilti::type::trait::hasDynamicType; + /** Returns any parameters the type expects. */ + hilti::node::Set parameters() const if hilti::type::trait::takesArguments; /** For internal use. Use ``type::isAllocable` instead. */ trait _isAllocable() from hilti::type::trait::isAllocable; @@ -70,32 +61,17 @@ class Type(hilti::trait::isType) : hilti::trait::isNode { /** For internal use. Use ``type::isMutable` instead. */ trait _isMutable() from hilti::type::trait::isMutable; - /** For internal use. Use ``type::hasDynamicInstead` instead. */ - trait _hasDynamicType() from hilti::type::trait::hasDynamicType; - /** For internal use. Use ``type::isRuntimeNonTrivial` instead. */ trait _isRuntimeNonTrivial() from hilti::type::trait::isRuntimeNonTrivial; - /** For internal use. Use ``type::isOnHeap` instead. */ - trait _isOnHeap() from hilti::type::trait::isOnHeap; - - /** For internal use. Use ``type::isConstant()` instead. */ - bool _isConstant() const; + /** For internal use. Use ``type::isResolved` instead. */ + bool _isResolved(type::ResolvedState* rstate) const; - /** For internal use. */ - type::detail::State& _state(); + /** Internal state managed by derived class. */ + member type::detail::State _state_; - /** - * Returns a HILTI ID associated with the type if any has been set. This - * method is implemented for all types in `TypeBase`. - */ - std::optional typeID() const; - - /** - * Returns a C++ ID associated with the type if any has been set. This - * method is implemented for all types in `TypeBase`. - */ - std::optional cxxID() const; + /** For internal use. Use ``type::takesArguments` instead. */ + trait _takesArguments() from hilti::type::trait::takesArguments; /** Implements the `Node` interface. */ hilti::node::Properties properties() const; @@ -113,11 +89,5 @@ class Type(hilti::trait::isType) : hilti::trait::isNode { void setMeta(Meta m); /** Implements the `Node` interface. */ - const NodeRef& originalNode() const; - - /** Implements the `Node` interface. */ - void setOriginalNode(const NodeRef& n); - - /** Implements the `Node` interface. */ - void clearCache(); + bool pruneWalk() const; }; diff --git a/hilti/toolchain/include/ast/type.h b/hilti/toolchain/include/ast/type.h index 755a06e4b..2da9149cc 100644 --- a/hilti/toolchain/include/ast/type.h +++ b/hilti/toolchain/include/ast/type.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include @@ -16,34 +17,52 @@ namespace trait { class isType : public isNode {}; } // namespace trait +class Type; + +namespace declaration { +class Parameter; +} + namespace type { +namespace function { +using Parameter = declaration::Parameter; +} + namespace trait { -class hasDynamicType {}; class isAllocable {}; class isDereferencable {}; class isIterable {}; class isIterator {}; class isMutable {}; -class isOnHeap {}; class isParameterized {}; class isReferenceType {}; class isRuntimeNonTrivial {}; class isView {}; class isViewable {}; class supportsWildcard {}; +class takesArguments {}; } // namespace trait +using ResolvedState = std::set; + /** Additional flags to associated with types. */ enum class Flag { /** Set to make the type `const`. */ Constant = (1U << 0U), + /** Set to make the type `non-const`. */ + NonConstant = (1U << 1U), + /** * Marks the type as having a top-level scope that does not derive scope content * from other nodes above it in the AST (except for truely global IDs). */ - NoInheritScope = (1U << 1U), + NoInheritScope = (1U << 2U), + + /** When walking over an AST, skip this node's children. This allows to + * break cycles. */ + PruneWalk = (1U << 3U), }; /** @@ -128,30 +147,24 @@ namespace detail { struct State { std::optional id; std::optional cxx; + std::optional resolved_id; type::Flags flags; }; #include - -/** Creates an AST node representing a `Type`. */ -inline Node to_node(Type t) { return Node(std::move(t)); } - -/** Renders a type as HILTI source code. */ -inline std::ostream& operator<<(std::ostream& out, Type t) { return out << to_node(std::move(t)); } - } // namespace detail -} // namespace type -using Type = type::detail::Type; +} // namespace type -/** - * Base class for classes implementing the `Type` interface. This class - * provides implementations for some interface methods shared that are shared - * by all types. - */ -class TypeBase : public NodeBase, public hilti::trait::isType { +class Type : public type::detail::Type { public: - using NodeBase::NodeBase; + using type::detail::Type::Type; + + std::optional resolvedID() const { return _state().resolved_id; } + + void setCxxID(ID id) { _state().cxx = std::move(id); } + void setTypeID(ID id) { _state().id = std::move(id); } + void addFlag(type::Flag f) { _state().flags += f; } /** Implements the `Type` interface. */ bool hasFlag(type::Flag f) const { return _state().flags.has(f); } @@ -167,12 +180,35 @@ class TypeBase : public NodeBase, public hilti::trait::isType { const type::detail::State& _state() const { return _state_; } /** Implements the `Type` interface. */ type::detail::State& _state() { return _state_; } + /** Implements the `Node` interface. */ + bool pruneWalk() const { return hasFlag(type::Flag::PruneWalk); } +}; -private: - type::detail::State _state_; +/** Creates an AST node representing a `Type`. */ +inline Node to_node(Type t) { return Node(std::move(t)); } + +/** Renders a type as HILTI source code. */ +inline std::ostream& operator<<(std::ostream& out, Type t) { return out << to_node(std::move(t)); } + +/** + * Base class for classes implementing the `Type` interface. This class + * provides implementations for some interface methods shared that are shared + * by all types. + */ +class TypeBase : public NodeBase, public hilti::trait::isType { +public: + using NodeBase::NodeBase; }; namespace type { +namespace detail { +extern void applyPruneWalk(hilti::Type& t); +} // namespace detail + +inline Type pruneWalk(Type t) { + detail::applyPruneWalk(t); + return t; +} /** * Copies an existing type, adding additional type flags. @@ -182,7 +218,7 @@ namespace type { * @return new type with the additional flags set */ inline hilti::Type addFlags(const Type& t, const type::Flags& flags) { - auto x = t._clone(); + auto x = Type(t); x._state().flags += flags; return x; } @@ -195,27 +231,11 @@ inline hilti::Type addFlags(const Type& t, const type::Flags& flags) { * @return new type with the flags removed */ inline hilti::Type removeFlags(const Type& t, const type::Flags& flags) { - auto x = t._clone(); + auto x = Type(t); x._state().flags -= flags; return x; } -/** - * Copies an existing type, marking the type as one that stores a - * constant/non-constant value. This has only an effect for mutable types. - * Non-mutable are always considered const. The default for for mutable * - * types is non-const. - * - * @param t original type - * @param const_ boolen indicating whether the new type should be const - * @return new type with the constness changed as requested - */ -inline hilti::Type setConstant(const Type& t, bool const_) { - auto x = t._clone(); - x._state().flags.set(type::Flag::Constant, const_); - return x; -} - /** * Copies an existing type, setting its C++ ID as emitted by the code generator. * @@ -224,7 +244,7 @@ inline hilti::Type setConstant(const Type& t, bool const_) { * @return new type with the C++ ID set accordindly */ inline hilti::Type setCxxID(const Type& t, ID id) { - auto x = t._clone(); + auto x = Type(t); x._state().cxx = std::move(id); return x; } @@ -237,7 +257,7 @@ inline hilti::Type setCxxID(const Type& t, ID id) { * @return new type with associateed type ID set accordindly */ inline hilti::Type setTypeID(const Type& t, ID id) { - auto x = t._clone(); + auto x = Type(t); x._state().id = std::move(id); return x; } @@ -248,57 +268,38 @@ inline hilti::Type setTypeID(const Type& t, ID id) { */ class Wildcard {}; -/** - * Fully deferences a type, returning the type it ultimately refers to. For - * most types, this will return them directly, but types with - * `trait::hasDynamicProcess` can customize the process (e.g., a resolved - * type ID will return the type the ID refers to. ) - */ -inline Type effectiveType(Type t) { return t._hasDynamicType() ? t.effectiveType() : std::move(t); } - -/** - * Like `effectiveType`, accepts an optional type. If not set, the returned - * type will likely remain unset. - */ -inline std::optional effectiveOptionalType(std::optional t) { - if ( t ) - return effectiveType(*t); - - return {}; -} - /** Returns true for HILTI types that can be used to instantiate variables. */ -inline bool isAllocable(const Type& t) { return effectiveType(t)._isAllocable(); } +inline bool isAllocable(const Type& t) { return t._isAllocable(); } /** Returns true for HILTI types that one can iterator over. */ -inline bool isDereferencable(const Type& t) { return effectiveType(t)._isDereferencable(); } +inline bool isDereferencable(const Type& t) { return t._isDereferencable(); } /** Returns true for HILTI types that one can iterator over. */ -inline bool isIterable(const Type& t) { return effectiveType(t)._isIterable(); } +inline bool isIterable(const Type& t) { return t._isIterable(); } /** Returns true for HILTI types that represent iterators. */ -inline bool isIterator(const Type& t) { return effectiveType(t)._isIterator(); } +inline bool isIterator(const Type& t) { return t._isIterator(); } /** Returns true for HILTI types that are parameterized with a set of type parameters. */ -inline bool isParameterized(const Type& t) { return effectiveType(t)._isParameterized(); } +inline bool isParameterized(const Type& t) { return t._isParameterized(); } /** Returns true for HILTI types that implement a reference to another type. */ -inline bool isReferenceType(const Type& t) { return effectiveType(t)._isReferenceType(); } +inline bool isReferenceType(const Type& t) { return t._isReferenceType(); } /** Returns true for HILTI types that can change their value. */ -inline bool isMutable(const Type& t) { return effectiveType(t)._isMutable(); } +inline bool isMutable(const Type& t) { return t._isMutable(); } /** Returns true for HILTI types that, when compiled, correspond to non-POD C++ types. */ -inline bool isRuntimeNonTrivial(const Type& t) { return effectiveType(t)._isRuntimeNonTrivial(); } +inline bool isRuntimeNonTrivial(const Type& t) { return t._isRuntimeNonTrivial(); } /** Returns true for HILTI types that represent iterators. */ -inline bool isView(const Type& t) { return effectiveType(t)._isView(); } +inline bool isView(const Type& t) { return t._isView(); } /** Returns true for HILTI types that one can create a view for. */ -inline bool isViewable(const Type& t) { return effectiveType(t)._isViewable(); } +inline bool isViewable(const Type& t) { return t._isViewable(); } -/** Returns true for HILTI types that are always to be placed on the heap. */ -inline bool isOnHeap(const Type& t) { return effectiveType(t)._isOnHeap(); } +/** Returns true for HILTI types that may receive type arguments on instantiations. */ +inline bool takesArguments(const Type& t) { return t._takesArguments(); } /** * Returns true if the type is marked constant. @@ -307,55 +308,92 @@ inline bool isOnHeap(const Type& t) { return effectiveType(t)._isOnHeap(); } * types. Ideally, this would always return true for non-mutable types, but * doing so breaks some coercion code currently. */ -inline bool isConstant(const Type& t) { return effectiveType(t).flags().has(type::Flag::Constant); } +inline bool isConstant(const Type& t) { + return t.flags().has(type::Flag::Constant) || (! isMutable(t) && ! t.flags().has(type::Flag::NonConstant)); +} /** Returns a `const` version of a type. */ -inline auto constant(const Type& t) { return setConstant(t, true); } +inline auto constant(Type t) { + t._state().flags -= type::Flag::NonConstant; + t._state().flags += type::Flag::Constant; + return t; +} + +/** + * Returns a not `const` version of a type. If `force` is true, then even + * immutable types are marked as non-const. This is usally not what one wants. + */ +inline auto nonConstant(Type t, bool force = false) { + t._state().flags -= type::Flag::Constant; -/** Returns a not `const` version of a type. */ -inline auto nonConstant(const Type& t) { return setConstant(t, false); } + if ( force ) + t._state().flags += type::Flag::NonConstant; -/** Sets the constness of type *t* to that of another type *from*. */ -inline auto transferConstness(const Type& t, const Type& from) { return setConstant(t, isConstant(from)); } + return t; +} namespace detail { -inline bool operator==(const Type& t1, const Type& t2) { +// Internal backends for the `isResolved()`. +extern bool isResolved(const hilti::Type& t, ResolvedState* rstate); + +inline bool isResolved(const std::optional& t, ResolvedState* rstate) { + return t.has_value() ? isResolved(*t, rstate) : true; +} + +inline bool isResolved(const std::optional& t, ResolvedState* rstate) { + return t.has_value() ? isResolved(*t, rstate) : true; +} +} // namespace detail + +/** Returns true if the type has been fully resolved, including all sub-types it may include. */ +extern bool isResolved(const Type& t); + +/** Returns true if the type has been fully resolved, including all sub-types it may include. */ +inline bool isResolved(const std::optional& t) { return t.has_value() ? isResolved(*t) : true; } + +/** Returns true if the type has been fully resolved, including all sub-types it may include. */ +inline bool isResolved(const std::optional& t) { return t.has_value() ? isResolved(*t) : true; } + +/** Returns true if two types are identical, ignoring for their constnesses. */ +inline bool sameExceptForConstness(const Type& t1, const Type& t2) { if ( &t1 == &t2 ) return true; - if ( type::isConstant(t1) != type::isConstant(t2) ) - return false; + if ( t1.typeID() && t2.typeID() ) + return *t1.typeID() == *t2.typeID(); if ( t1.cxxID() && t2.cxxID() ) - return t1.cxxID() == t2.cxxID(); - - if ( (t1.flags() - type::Flag::Constant) != (t2.flags() - type::Flag::Constant) ) - return false; + return *t1.cxxID() == *t2.cxxID(); - // Type comparision is not fully symmetric, it's good enough - // if one type believes it matches the other one. return t1.isEqual(t2) || t2.isEqual(t1); } -inline bool operator!=(const Type& t1, const Type& t2) { return ! (t1 == t2); } - -} // namespace detail +} // namespace type -/** - * Checks if a source type's constness suports promotion to a destination's - * constness. This ignores the types itself, it just looks at constness. - */ -inline bool isConstCompatible(const Type& src, const Type& dst) { - if ( type::isConstant(dst) ) +inline bool operator==(const Type& t1, const Type& t2) { + if ( &t1 == &t2 ) return true; - return ! type::isConstant(src); -} + if ( type::isMutable(t1) || type::isMutable(t2) ) { + if ( type::isConstant(t1) && ! type::isConstant(t2) ) + return false; -/** Returns true if two types are identical, ignoring for their constnesses. */ -inline bool sameExceptForConstness(const Type& t1, const Type& t2) { return t1.isEqual(t2) || t2.isEqual(t1); } + if ( type::isConstant(t2) && ! type::isConstant(t1) ) + return false; + } -} // namespace type + if ( t1.typeID() && t2.typeID() ) + return *t1.typeID() == *t2.typeID(); + + if ( t1.cxxID() && t2.cxxID() ) + return *t1.cxxID() == *t2.cxxID(); + + // Type comparision is not fully symmetric, it's good enough + // if one type believes it matches the other one. + return t1.isEqual(t2) || t2.isEqual(t1); +} + +inline bool operator!=(const Type& t1, const Type& t2) { return ! (t1 == t2); } /** Constructs an AST node from any class implementing the `Type` interface. */ template::value>* = nullptr> diff --git a/hilti/toolchain/include/ast/types/address.h b/hilti/toolchain/include/ast/types/address.h index 3f88bb542..e3215270a 100644 --- a/hilti/toolchain/include/ast/types/address.h +++ b/hilti/toolchain/include/ast/types/address.h @@ -18,6 +18,8 @@ class Address : public TypeBase, trait::isAllocable { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; diff --git a/hilti/toolchain/include/ast/types/all.h b/hilti/toolchain/include/ast/types/all.h index 17e6410f8..56a6fe6e7 100644 --- a/hilti/toolchain/include/ast/types/all.h +++ b/hilti/toolchain/include/ast/types/all.h @@ -7,13 +7,11 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include @@ -38,5 +36,6 @@ #include #include #include +#include #include #include diff --git a/hilti/toolchain/include/ast/types/any.h b/hilti/toolchain/include/ast/types/any.h index fa4289ca7..a5dd9db46 100644 --- a/hilti/toolchain/include/ast/types/any.h +++ b/hilti/toolchain/include/ast/types/any.h @@ -18,6 +18,8 @@ class Any : public TypeBase { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } diff --git a/hilti/toolchain/include/ast/types/auto.h b/hilti/toolchain/include/ast/types/auto.h index 992de54ef..a37820078 100644 --- a/hilti/toolchain/include/ast/types/auto.h +++ b/hilti/toolchain/include/ast/types/auto.h @@ -2,61 +2,37 @@ #pragma once -#include #include -#include #include -#include namespace hilti { namespace type { /** AST node for an "auto" type. */ -class Auto : public TypeBase, - trait::hasDynamicType, - type::trait::isParameterized, - type::trait::isViewable, - trait::isDereferencable, - trait::isIterable { +class Auto : public TypeBase, type::trait::isAllocable { public: - Auto(Meta m = Meta()) - : TypeBase(std::move(m)), - _type(std::make_shared>(std::make_shared(type::unknown))) {} - - const Type& type() const { return (*_type)->as(); } - - auto isSet() const { return ! (*_type)->isA(); } - - Node& typeNode() const { return **_type; } - - void linkTo(const Auto& other) { *_type = *other._type; } - - bool operator==(const Auto& other) const { return _type.get() == other._type.get(); } + bool operator==(const Auto& /* other */) const { return true; } /** Implements the `Type` interface. */ - bool isEqual(const Type& other) const { return type() == other; } + auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type effectiveType() const { - if ( isSet() ) - return type(); - else - return *this; // don't resolve yet - } - - std::vector typeParameters() const { return type().typeParameters(); } - bool isWildcard() const { return type().isWildcard(); } - Type iteratorType(bool const_) const { return type().iteratorType(const_); } - Type viewType() const { return type().viewType(); } - Type dereferencedType() const { return type().dereferencedType(); } - Type elementType() const { return type().elementType(); } - + auto _isResolved(ResolvedState* rstate) const { return false; } /** Implements the `Node` interface. */ - auto properties() const { return node::Properties{{"resolves-to", Node(**_type).typename_()}}; } + auto properties() const { return node::Properties{}; } + + /** + * Wrapper around constructor so that we can make it private. Don't use + * this, use the singleton `type::auto_` instead. + */ + static Auto create(Meta m = Meta()) { return Auto(std::move(m)); } private: - std::shared_ptr> _type; + Auto(Meta m = Meta()) : TypeBase(std::move(m)) {} }; +/** Singleton. */ +static const Type auto_ = Auto::create(Location("")); + } // namespace type } // namespace hilti diff --git a/hilti/toolchain/include/ast/types/bool.h b/hilti/toolchain/include/ast/types/bool.h index 22ee6716b..a6cd5c375 100644 --- a/hilti/toolchain/include/ast/types/bool.h +++ b/hilti/toolchain/include/ast/types/bool.h @@ -18,6 +18,8 @@ class Bool : public TypeBase, trait::isAllocable { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; diff --git a/hilti/toolchain/include/ast/types/bytes.h b/hilti/toolchain/include/ast/types/bytes.h index 29d6acaa9..317ebcc52 100644 --- a/hilti/toolchain/include/ast/types/bytes.h +++ b/hilti/toolchain/include/ast/types/bytes.h @@ -20,14 +20,16 @@ class Iterator : public TypeBase, trait::isMutable, trait::isRuntimeNonTrivial { public: - Iterator(Meta m = Meta()) : TypeBase(std::move(m)) {} + Iterator(Meta m = Meta()) : TypeBase(nodes(Type(type::UnsignedInteger(8))), std::move(m)) {} bool operator==(const Iterator& /* other */) const { return true; } /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type dereferencedType() const; + auto _isResolved(ResolvedState* rstate) const { return true; } + /** Implements the `Type` interface. */ + const Type& dereferencedType() const { return child(0); } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; @@ -37,30 +39,23 @@ class Iterator : public TypeBase, /** AST node for a bytes type. */ class Bytes : public TypeBase, trait::isAllocable, trait::isMutable, trait::isIterable, trait::isRuntimeNonTrivial { public: - Bytes(Meta m = Meta()) : TypeBase(std::move(m)) {} + Bytes(Meta m = Meta()) : TypeBase(nodes(Type(type::UnsignedInteger(8)), Type(bytes::Iterator(m))), m) {} bool operator==(const Bytes& /* other */) const { return true; } /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type elementType() const { return type::UnsignedInteger(8); } + auto _isResolved(ResolvedState* rstate) const { return true; } + /** Implements the `Type` interface. */ + const Type& elementType() const { return child(0); } /** Implements the `Type` interface. */ - Type iteratorType(bool /* const_ */) const { return bytes::Iterator(meta()); } + const Type& iteratorType(bool /* const */) const { return child(1); } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } - -private: - std::optional _etype; }; -namespace detail::bytes { -inline Node element_type = Node(type::UnsignedInteger(8, Location())); -} // namespace detail::bytes - -inline Type bytes::Iterator::dereferencedType() const { return type::UnsignedInteger(8); } - } // namespace type } // namespace hilti diff --git a/hilti/toolchain/include/ast/types/computed.h b/hilti/toolchain/include/ast/types/computed.h deleted file mode 100644 index 580e09d9b..000000000 --- a/hilti/toolchain/include/ast/types/computed.h +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace hilti { -namespace type { - -/** - * AST node for a type computed dynamically from another node that's - * potentially not resolved at first yet. This works either through a - * callback that executes at time the type is accessed, with access to the - * original node; or through an expression which's type at the time of access - * determined the result. - * - * @note This class gets a full set of traits, so that it can forward all - * method calls to the resulting type. - */ -class Computed : public TypeBase, - trait::hasDynamicType, - type::trait::isParameterized, - type::trait::isViewable, - trait::isDereferencable, - trait::isIterable { -public: - using Callback = std::function; - using Callback2 = std::function; - Computed(NodeRef r, Meta m = Meta()) : TypeBase(nodes(node::none), std::move(m)), _node(std::move(r)) {} - Computed(NodeRef r, Callback cb, Meta m = Meta()) - : TypeBase(nodes(node::none), std::move(m)), _node(std::move(r)), _callback(std::move(cb)) {} - Computed(Expression e, Meta m = Meta()) : TypeBase(nodes(std::move(e)), std::move(m)) {} - Computed(NodeRef r1, NodeRef r2, Callback2 cb, Meta m = Meta()) - : TypeBase(nodes(node::none), std::move(m)), - _node(std::move(r1)), - _node2(std::move(r2)), - _callback2(std::move(cb)) {} - Computed(Expression e, Callback cb, Meta m = Meta()) - : TypeBase(nodes(std::move(e)), std::move(m)), _callback(std::move(cb)) {} - Computed(Type t, Meta m = Meta()) : TypeBase(nodes(std::move(t)), std::move(m)) {} - Computed(Type t, Callback cb, Meta m = Meta()) - : TypeBase(nodes(std::move(t)), std::move(m)), _callback(std::move(cb)) {} - Computed(Type t, Node n, Callback2 cb2, Meta m = Meta()) - : TypeBase(nodes(std::move(t), std::move(n)), std::move(m)), _callback2(std::move(cb2)) {} - - Type type() const { - if ( _node ) { - if ( _callback ) - return type::effectiveType(_callback(*_node)); - else if ( _callback2 ) { - assert(_node2); - return type::effectiveType(_callback2(*_node, *_node2)); - } - else - return type::effectiveType(_node->template as()); - } - - if ( auto e = childs()[0].tryAs() ) { - if ( _callback ) - return type::effectiveType(_callback(const_cast(childs()[0]))); - else if ( _callback2 ) - return type::effectiveType(_callback2(const_cast(childs()[0]), const_cast(childs()[1]))); - - if ( ! _change_constness_to.has_value() ) - return e->type(); - - if ( *_change_constness_to ) - return type::constant(e->type()); - - return type::nonConstant(e->type()); - } - - if ( auto t = childs()[0].tryAs() ) { - if ( _callback ) - return type::effectiveType(_callback(const_cast(childs()[0]))); - else if ( _callback2 ) - return type::effectiveType(_callback2(const_cast(childs()[0]), const_cast(childs()[1]))); - else - return *t; - } - - return type::unknown; - } - - bool operator==(const Computed& other) const { return type() == other.type(); } - - /** Implements the `Type` interface. */ - bool isEqual(const Type& other) const { return type() == other; } - /** Implements the `Type` interface. */ - Type effectiveType() const { return type::effectiveType(type()); } - - std::vector typeParameters() const { return type().typeParameters(); } - bool isWildcard() const { return type().isWildcard(); } - Type iteratorType(bool const_) const { return type().iteratorType(const_); } - Type viewType() const { return type().viewType(); } - Type dereferencedType() const { return type().dereferencedType(); } - Type elementType() const { return type().elementType(); } - - /** Implements the `Node` interface. */ - auto properties() const { - node::Properties props; - if ( _node ) - props.insert({"resolved", _node.renderedRid()}); - - if ( _node2 ) - props.insert({"resolved2", _node2.renderedRid()}); - - props.insert({"rid", _node.renderedRid()}); - return props; - } - -private: - NodeRef _node; - NodeRef _node2; - Callback _callback; - Callback2 _callback2; - std::optional _change_constness_to; -}; - -} // namespace type -} // namespace hilti diff --git a/hilti/toolchain/include/ast/types/doc-only.h b/hilti/toolchain/include/ast/types/doc-only.h index 999e842d8..d29d65d1c 100644 --- a/hilti/toolchain/include/ast/types/doc-only.h +++ b/hilti/toolchain/include/ast/types/doc-only.h @@ -26,7 +26,8 @@ class DocOnly : public TypeBase { // Type interface. auto isEqual(const Type& other) const { return node::isEqual(this, other); } - + /** Implements the `Type` interface. */ + auto _isResolved(ResolvedState* rstate) const { return true; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } diff --git a/hilti/toolchain/include/ast/types/enum.h b/hilti/toolchain/include/ast/types/enum.h index ac2fd48e0..c8fde75d9 100644 --- a/hilti/toolchain/include/ast/types/enum.h +++ b/hilti/toolchain/include/ast/types/enum.h @@ -3,11 +3,17 @@ #pragma once #include +#include #include #include +#include +#include #include #include +#include +#include +#include namespace hilti { namespace type { @@ -17,18 +23,24 @@ namespace enum_ { class Label : public NodeBase, public util::type_erasure::trait::Singleton { public: Label() : NodeBase({ID("")}, Meta()) {} - Label(ID id, Meta m = Meta()) : NodeBase({std::move(id)}, std::move(m)) {} - Label(ID id, int v, Meta m = Meta()) : NodeBase({std::move(id)}, std::move(m)), _value(v) {} + Label(ID id, Meta m = Meta()) : NodeBase(nodes(std::move(id)), std::move(m)) {} + Label(ID id, int v, Meta m = Meta()) : NodeBase(nodes(std::move(id)), std::move(m)), _value(v) {} - const auto& id() const { return child(0); } + // Recreate from an existing label, but setting type. + Label(const Label& other, NodeRef enum_type) + : NodeBase(nodes(other.id()), other.meta()), _etype(std::move(enum_type)), _value(other._value) {} + + const ID& id() const { return child(0); } + const auto& enumType() const { return _etype ? _etype->as() : type::auto_; } auto value() const { return _value; } bool operator==(const Label& other) const { return id() == other.id() && value() == other.value(); } /** Implements the `Node` interface. */ - auto properties() const { return node::Properties{{"value", _value}}; } + auto properties() const { return node::Properties{{"value", _value}, {"etype", _etype.rid()}}; } private: + NodeRef _etype; int _value = -1; }; @@ -43,44 +55,37 @@ class Enum : public TypeBase, trait::isAllocable, trait::isParameterized { : TypeBase(nodes(_normalizeLabels(std::move(l))), std::move(m)) {} Enum(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(std::move(m)), _wildcard(true) {} - std::vector labels() const { return childs(0, -1); } + std::vector> labels() const; /** - * Returns the set of labels but makes sure to include each enumator - * value at most once. + * Filters a set of labels so that it includes each enumator value at most + * once. */ - std::vector uniqueLabels() const { - auto pred_gt = [](const enum_::Label& e1, const enum_::Label& e2) { return e1.value() > e2.value(); }; - auto pred_eq = [](const enum_::Label& e1, const enum_::Label& e2) { return e1.value() == e2.value(); }; - std::vector x = labels(); - std::sort(x.begin(), x.end(), pred_gt); - x.erase(std::unique(x.begin(), x.end(), pred_eq), x.end()); - return x; - } + std::vector> uniqueLabels() const; - std::optional label(const ID& id) const { - for ( auto l : labels() ) { - if ( l.id() == id ) - return l; + hilti::optional_ref label(const ID& id) const { + for ( const auto& l : labels() ) { + if ( l.get().id() == id ) + return l.get(); } return {}; } - bool operator==(const Enum& other) const { - if ( typeID() && other.typeID() ) - return *typeID() == *other.typeID(); + auto labelDeclarationRefs() { return childRefs(0, -1); } - return labels() == other.labels(); - } + bool operator==(const Enum& other) const { return childs(0, -1) == other.childs(0, -1); } /** 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 _initialized; } + /** Implements the `Type` interface. */ auto typeParameters() const { std::vector params; for ( auto&& c : uniqueLabels() ) - params.emplace_back(std::move(c)); + params.emplace_back(c.get()); + return params; } /** Implements the `Type` interface. */ @@ -89,10 +94,14 @@ class Enum : public TypeBase, trait::isAllocable, trait::isParameterized { /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } + /** Helper method for the resolver to link labels to their type. */ + static void initLabelTypes(Node* n); + private: - static std::vector _normalizeLabels(std::vector /*labels*/); + static std::vector _normalizeLabels(std::vector labels); bool _wildcard = false; + bool _initialized = false; }; } // namespace type diff --git a/hilti/toolchain/include/ast/types/error.h b/hilti/toolchain/include/ast/types/error.h index 5ea6684e6..a5b15f0f7 100644 --- a/hilti/toolchain/include/ast/types/error.h +++ b/hilti/toolchain/include/ast/types/error.h @@ -18,6 +18,8 @@ class Error : public TypeBase, trait::isAllocable { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } diff --git a/hilti/toolchain/include/ast/types/exception.h b/hilti/toolchain/include/ast/types/exception.h index b188275f9..35a5fe49f 100644 --- a/hilti/toolchain/include/ast/types/exception.h +++ b/hilti/toolchain/include/ast/types/exception.h @@ -16,19 +16,17 @@ class Exception : public TypeBase, trait::isAllocable, trait::isParameterized { Exception(Type base, Meta m = Meta()) : TypeBase({std::move(base)}, std::move(m)) {} Exception(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} - std::optional baseType() const { - auto t = childs()[0].tryAs(); - if ( t ) - return type::effectiveType(*t); - - return {}; - } + hilti::optional_ref baseType() const { return childs()[0].tryAs(); } bool operator==(const Exception& other) const { return baseType() == other.baseType(); } /** 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 baseType().has_value() ? type::detail::isResolved(baseType(), rstate) : true; + } + /** Implements the `Type` interface. */ auto typeParameters() const { return childs(); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } diff --git a/hilti/toolchain/include/ast/types/function.h b/hilti/toolchain/include/ast/types/function.h index 8febdfe3b..9374e990a 100644 --- a/hilti/toolchain/include/ast/types/function.h +++ b/hilti/toolchain/include/ast/types/function.h @@ -50,12 +50,14 @@ class Result : public NodeBase { Result() : NodeBase(nodes(node::none), Meta()) {} - auto type() const { return type::effectiveType(child(0)); } + const auto& type() const { return child(0); } - /** Implements the `Node` interface. */ - auto properties() const { return node::Properties{}; } + void setType(Type x) { childs()[0] = std::move(x); } bool operator==(const Result& other) const { return type() == other.type(); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } }; using Parameter = declaration::Parameter; @@ -69,23 +71,19 @@ using Kind = declaration::parameter::Kind; class Function : public TypeBase, trait::isParameterized { public: Function(Wildcard /*unused*/, Meta m = Meta()) - : TypeBase({function::Result(type::Error(m))}, std::move(m)), _wildcard(true) {} + : TypeBase(nodes(function::Result(type::Error(m))), std::move(m)), _wildcard(true) {} Function(function::Result result, const std::vector& params, function::Flavor flavor = function::Flavor::Standard, Meta m = Meta()) : TypeBase(nodes(std::move(result), util::transform(params, [](const auto& p) { return Declaration(p); })), std::move(m)), _flavor(flavor) {} - auto parameters() const { return childs(1, -1); } const auto& result() const { return child(0); } + auto parameters() const { return childs(1, -1); } + auto parameterRefs() const { return childRefsOfType(); } auto flavor() const { return _flavor; } - const auto& operands() const { - if ( ! _cache.operands ) - _cache.operands = type::OperandList::fromParameters(parameters()); - - return *_cache.operands; - } + void setResultType(Type t) { childs()[0].as().setType(std::move(t)); } bool operator==(const Function& other) const { return result() == other.result() && parameters() == other.parameters(); @@ -93,6 +91,26 @@ class Function : public TypeBase, trait::isParameterized { /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } + + /** Implements the `Type` interface. */ + auto _isResolved(ResolvedState* rstate) const { + if ( result().type().isA() ) + // We treat this as resolved because (1) it doesn't need to hold up + // other resolving, and (2) can lead to resolver dead-locks if we + // let it. + return true; + + if ( ! type::detail::isResolved(result().type(), rstate) ) + return false; + + for ( auto p = childs().begin() + 1; p != childs().end(); p++ ) { + if ( ! p->as().isResolved(rstate) ) + return false; + } + + return true; + } + /** Implements the `Type` interface. */ auto typeParameters() const { return childs(); } /** Implements the `Type` interface. */ @@ -101,13 +119,9 @@ class Function : public TypeBase, trait::isParameterized { /** Implements the `Node` interface. */ auto properties() const { return node::Properties{{"flavor", to_string(_flavor)}}; } - void clearCache() { _cache.operands.reset(); } - private: bool _wildcard = false; function::Flavor _flavor = function::Flavor::Standard; - - mutable struct { std::optional operands; } _cache; }; /** @@ -120,7 +134,7 @@ inline bool areEquivalent(const Function& f1, const Function& f2) { auto p1 = f1.parameters(); auto p2 = f2.parameters(); - return std::equal(begin(p1), end(p1), begin(p2), end(p2), + return std::equal(std::begin(p1), std::end(p1), std::begin(p2), std::end(p2), [](const auto& p1, const auto& p2) { return areEquivalent(p1, p2); }); } diff --git a/hilti/toolchain/include/ast/types/id.h b/hilti/toolchain/include/ast/types/id.h deleted file mode 100644 index 17f04c5dd..000000000 --- a/hilti/toolchain/include/ast/types/id.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#pragma once - -#include - -#include -#include -#include -#include -#include - -namespace hilti { -namespace type { - -/** AST node for a resolved type ID. */ -class ResolvedID : public TypeBase, trait::hasDynamicType { -public: - ResolvedID(::hilti::ID id, NodeRef r, Meta m = Meta()) - : TypeBase({std::move(id)}, std::move(m)), _node(std::move(std::move(r))) { - assert(_node && _node->isA()); - } - - const auto& id() const { return child<::hilti::ID>(0); } - auto declaration() const { - assert(_node); - return _node->as(); - } - auto type() const { - assert(_node); - return _node->as().type(); - } - bool isValid() const { return static_cast(_node); } - const NodeRef& ref() const { return _node; } - - bool operator==(const ResolvedID& other) const { return type() == other.type(); } - - /** Implements the `Type` interface. */ - bool isEqual(const Type& other) const { return type() == type::effectiveType(other); } - /** Implements the `Type` interface. */ - Type effectiveType() const { return type(); } - - /** Implements the `Node` interface. */ - auto properties() const { - return _node ? node::Properties{{"resolved", _node.renderedRid()}} : node::Properties{{}}; - } - -private: - NodeRef _node; -}; - -/** AST node for an unresolved type ID. */ -class UnresolvedID : public TypeBase { -public: - UnresolvedID(::hilti::ID id, Meta m = Meta()) : TypeBase({std::move(id)}, std::move(m)) {} - - const auto& id() const { return child<::hilti::ID>(0); } - - bool operator==(const UnresolvedID& other) const { return id() == other.id(); } - - // Type interface. - auto isEqual(const Type& other) const { return node::isEqual(this, other); } - - // Node interface. - auto properties() const { return node::Properties{}; } -}; - -} // namespace type -} // namespace hilti diff --git a/hilti/toolchain/include/ast/types/integer.h b/hilti/toolchain/include/ast/types/integer.h index cd8d6e1c7..e5cc400c1 100644 --- a/hilti/toolchain/include/ast/types/integer.h +++ b/hilti/toolchain/include/ast/types/integer.h @@ -24,7 +24,8 @@ class IntegerBase : public TypeBase, trait::isAllocable, trait::isParameterized /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } - + /** Implements the `Type` interface. */ + auto _isResolved(ResolvedState* rstate) const { return true; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{{"width", _width}}; } diff --git a/hilti/toolchain/include/ast/types/interval.h b/hilti/toolchain/include/ast/types/interval.h index c3eab67d8..d079c4fa5 100644 --- a/hilti/toolchain/include/ast/types/interval.h +++ b/hilti/toolchain/include/ast/types/interval.h @@ -18,6 +18,8 @@ class Interval : public TypeBase, trait::isAllocable { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; diff --git a/hilti/toolchain/include/ast/types/library.h b/hilti/toolchain/include/ast/types/library.h index 7322a883f..5b8522de9 100644 --- a/hilti/toolchain/include/ast/types/library.h +++ b/hilti/toolchain/include/ast/types/library.h @@ -32,6 +32,8 @@ class Library : public TypeBase, trait::isAllocable, trait::isMutable { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto _isResolved(ResolvedState* rstate) const { return true; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{{"cxx_name", _cxx_name}}; } diff --git a/hilti/toolchain/include/ast/types/list.h b/hilti/toolchain/include/ast/types/list.h index f47de48d0..dd7f635e1 100644 --- a/hilti/toolchain/include/ast/types/list.h +++ b/hilti/toolchain/include/ast/types/list.h @@ -21,29 +21,32 @@ class Iterator : public TypeBase, trait::isRuntimeNonTrivial, trait::isParameterized { public: - Iterator(Type ctype, Meta m = Meta()) : TypeBase({std::move(ctype)}, std::move(m)) {} - Iterator(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + Iterator(Type etype, bool const_, Meta m = Meta()) + : TypeBase(nodes(std::move(etype)), std::move(m)), _const(const_) {} + Iterator(Wildcard /*unused*/, bool const_ = true, Meta m = Meta()) + : TypeBase(nodes(type::unknown), std::move(m)), _wildcard(true), _const(const_) {} - /** Returns the type of the container the iterator is working on. */ - Type containerType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + /** Returns true if the container elements aren't modifiable. */ + bool isConstant() const { return _const; } /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type dereferencedType() const { - return (_wildcard || containerType().isWildcard()) ? type::unknown : containerType().elementType(); - } + auto _isResolved(ResolvedState* rstate) const { return type::detail::isResolved(dereferencedType(), rstate); } + /** Implements the `Type` interface. */ + const Type& dereferencedType() const { return child(0); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Type` interface. */ auto typeParameters() const { return childs(); } /** Implements the `Node` interface. */ - auto properties() const { return node::Properties{}; } + auto properties() const { return node::Properties{{"const", _const}}; } bool operator==(const Iterator& other) const { return dereferencedType() == other.dereferencedType(); } private: bool _wildcard = false; + bool _const = false; }; } // namespace list @@ -56,15 +59,22 @@ class List : public TypeBase, trait::isRuntimeNonTrivial, trait::isParameterized { public: - List(Type t, Meta m = Meta()) : TypeBase({std::move(t)}, std::move(m)) {} - List(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + List(Type t, Meta m = Meta()) : TypeBase(nodes(list::Iterator(t, true, m), list::Iterator(t, false, m)), m) {} + List(Wildcard /*unused*/, Meta m = Meta()) + : TypeBase(nodes(list::Iterator(Wildcard{}, true, m), list::Iterator(Wildcard{}, false, m)), m), + _wildcard(true) {} /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type elementType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + auto _isResolved(ResolvedState* rstate) const { + return type::detail::isResolved(iteratorType(true), rstate) && + type::detail::isResolved(iteratorType(false), rstate); + } + /** Implements the `Type` interface. */ + const Type& elementType() const { return child(0).dereferencedType(); } /** Implements the `Type` interface. */ - Type iteratorType(bool /* const_ */) const { return list::Iterator(*this, meta()); } + const Type& iteratorType(bool const_) const { return const_ ? child(0) : child(1); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Type` interface. */ diff --git a/hilti/toolchain/include/ast/types/map.h b/hilti/toolchain/include/ast/types/map.h index fa4b719d5..da042c23a 100644 --- a/hilti/toolchain/include/ast/types/map.h +++ b/hilti/toolchain/include/ast/types/map.h @@ -22,11 +22,24 @@ class Iterator : public TypeBase, trait::isRuntimeNonTrivial, trait::isParameterized { public: - Iterator(Type ctype, bool const_, Meta m = Meta()) : TypeBase({std::move(ctype)}, std::move(m)), _const(const_) {} - Iterator(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + Iterator(Type ktype, Type vtype, bool const_, Meta m = Meta()) + : TypeBase(nodes(type::Tuple({std::move(ktype), std::move(vtype)}, m)), m), _const(const_) {} + Iterator(Wildcard /*unused*/, bool const_ = true, Meta m = Meta()) + : TypeBase(nodes(type::unknown, type::unknown), std::move(m)), _wildcard(true), _const(const_) {} + + const Type& keyType() const { + if ( auto t = childs()[0].tryAs() ) + return t->elements()[0].type(); + else + return child(0); + } - /** Returns the type of the container the iterator is working on. */ - Type containerType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + const Type& valueType() const { + if ( auto t = childs()[0].tryAs() ) + return t->elements()[1].type(); + else + return child(0); + } /** Returns true if the container elements aren't modifiable. */ bool isConstant() const { return _const; } @@ -34,7 +47,9 @@ class Iterator : public TypeBase, /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type dereferencedType() const; + auto _isResolved(ResolvedState* rstate) const { return type::detail::isResolved(dereferencedType(), rstate); } + /** Implements the `Type` interface. */ + const Type& dereferencedType() const { return child(0); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Type` interface. */ @@ -42,7 +57,9 @@ class Iterator : public TypeBase, /** Implements the `Node` interface. */ auto properties() const { return node::Properties{{"const", _const}}; } - bool operator==(const Iterator& other) const { return dereferencedType() == other.dereferencedType(); } + bool operator==(const Iterator& other) const { + return keyType() == other.keyType() && valueType() == other.valueType(); + } private: bool _wildcard = false; @@ -59,17 +76,26 @@ class Map : public TypeBase, trait::isRuntimeNonTrivial, trait::isParameterized { public: - Map(Type key, Type value, Meta m = Meta()) : TypeBase({std::move(key), std::move(value)}, std::move(m)) {} - Map(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + Map(Type k, Type v, Meta m = Meta()) + : TypeBase(nodes(map::Iterator(k, v, true, m), map::Iterator(k, v, false, m)), m) {} + Map(Wildcard /*unused*/, Meta m = Meta()) + : TypeBase(nodes(map::Iterator(Wildcard{}, true, m), map::Iterator(Wildcard{}, false, m)), m), + _wildcard(true) {} - Type keyType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + const Type& keyType() const { return child(0).keyType(); } + const Type& valueType() const { return child(0).valueType(); } /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type elementType() const { return _wildcard ? type::unknown : type::effectiveType(child(1)); } + auto _isResolved(ResolvedState* rstate) const { + return type::detail::isResolved(iteratorType(true), rstate) && + type::detail::isResolved(iteratorType(false), rstate); + } + /** Implements the `Type` interface. */ + const Type& elementType() const { return valueType(); } /** Implements the `Type` interface. */ - Type iteratorType(bool const_) const { return map::Iterator(*this, const_, meta()); } + const Type& iteratorType(bool const_) const { return const_ ? child(0) : child(1); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Type` interface. */ @@ -77,22 +103,11 @@ class Map : public TypeBase, /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } - bool operator==(const Map& other) const { - return keyType() == other.keyType() && elementType() == other.elementType(); - } + bool operator==(const Map& other) const { return iteratorType(true) == other.iteratorType(true); } private: bool _wildcard = false; }; -namespace map { -inline Type Iterator::dereferencedType() const { - if ( _wildcard || containerType().isWildcard() ) - return type::unknown; - - return type::Tuple({containerType().as().keyType(), containerType().elementType()}); -} -} // namespace map - } // namespace type } // namespace hilti diff --git a/hilti/toolchain/include/ast/types/member.h b/hilti/toolchain/include/ast/types/member.h index ccfb8d761..12f111474 100644 --- a/hilti/toolchain/include/ast/types/member.h +++ b/hilti/toolchain/include/ast/types/member.h @@ -6,7 +6,7 @@ #include #include -#include +#include #include namespace hilti { @@ -25,6 +25,8 @@ class Member : public TypeBase, trait::isParameterized { /** 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; } + /** Implements the `Type` interface. */ auto typeParameters() const { return std::vector{id()}; } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } diff --git a/hilti/toolchain/include/ast/types/network.h b/hilti/toolchain/include/ast/types/network.h index 7b6ab105f..905466c71 100644 --- a/hilti/toolchain/include/ast/types/network.h +++ b/hilti/toolchain/include/ast/types/network.h @@ -18,6 +18,8 @@ class Network : public TypeBase, trait::isAllocable { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; diff --git a/hilti/toolchain/include/ast/types/null.h b/hilti/toolchain/include/ast/types/null.h index a14e5a08a..005b69ad9 100644 --- a/hilti/toolchain/include/ast/types/null.h +++ b/hilti/toolchain/include/ast/types/null.h @@ -18,6 +18,8 @@ class Null : public TypeBase { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } diff --git a/hilti/toolchain/include/ast/types/operand-list.h b/hilti/toolchain/include/ast/types/operand-list.h index 411cda9be..16e464950 100644 --- a/hilti/toolchain/include/ast/types/operand-list.h +++ b/hilti/toolchain/include/ast/types/operand-list.h @@ -26,17 +26,20 @@ class OperandList : public TypeBase { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } bool operator==(const OperandList& other) const { return operands() == other.operands(); } - static OperandList fromParameters(const std::vector& params) { + template + static OperandList fromParameters(const Container& params) { std::vector ops; for ( const auto& p : params ) { operator_::Operand op = {.id = p.id(), - .type = type::setConstant(p.type(), p.isConstant()), + .type = (p.isConstant() ? type::constant(p.type()) : p.type()), .optional = p.default_().has_value(), .default_ = p.default_()}; diff --git a/hilti/toolchain/include/ast/types/optional.h b/hilti/toolchain/include/ast/types/optional.h index 4f3e51740..b48204d63 100644 --- a/hilti/toolchain/include/ast/types/optional.h +++ b/hilti/toolchain/include/ast/types/optional.h @@ -5,6 +5,7 @@ #include #include +#include namespace hilti { namespace type { @@ -12,21 +13,18 @@ namespace type { /** AST node for an "optional" type. */ class Optional : public TypeBase, trait::isAllocable, trait::isParameterized, trait::isDereferencable { public: - Optional(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + Optional(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({type::unknown}, std::move(m)), _wildcard(true) {} Optional(Type ct, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) {} - Type dereferencedType() const { - if ( auto t = childs()[0].tryAs() ) - return *t; - - return type::unknown; - } + const Type& dereferencedType() const { return childs()[0].as(); } bool operator==(const Optional& other) const { return dereferencedType() == other.dereferencedType(); } /** 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 type::detail::isResolved(dereferencedType(), rstate); } + /** Implements the `Type` interface. */ auto typeParameters() const { return childs(); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } diff --git a/hilti/toolchain/include/ast/types/port.h b/hilti/toolchain/include/ast/types/port.h index f30a6d4e3..0a5ab6055 100644 --- a/hilti/toolchain/include/ast/types/port.h +++ b/hilti/toolchain/include/ast/types/port.h @@ -18,6 +18,8 @@ class Port : public TypeBase, trait::isAllocable { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; diff --git a/hilti/toolchain/include/ast/types/real.h b/hilti/toolchain/include/ast/types/real.h index ffe13d3bb..4d25920cb 100644 --- a/hilti/toolchain/include/ast/types/real.h +++ b/hilti/toolchain/include/ast/types/real.h @@ -18,6 +18,8 @@ class Real : public TypeBase, trait::isAllocable { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } diff --git a/hilti/toolchain/include/ast/types/reference.h b/hilti/toolchain/include/ast/types/reference.h index f8b8b666c..001fc9821 100644 --- a/hilti/toolchain/include/ast/types/reference.h +++ b/hilti/toolchain/include/ast/types/reference.h @@ -19,18 +19,15 @@ class StrongReference : public TypeBase, trait::isDereferencable, trait::isReferenceType { public: - StrongReference(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} - StrongReference(Type ct, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) {} - StrongReference(Type ct, bool treat_as_non_constant, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) { - if ( treat_as_non_constant ) - _state().flags -= type::Flag::Constant; - } - - Type dereferencedType() const { - if ( auto t = childs()[0].tryAs() ) - return type::effectiveType(*t); - - return type::unknown; + StrongReference(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({type::unknown}, std::move(m)), _wildcard(true) {} + StrongReference(Type ct, Meta m = Meta()) : TypeBase(nodes(std::move(ct)), std::move(m)) {} + StrongReference(NodeRef ct, Meta m = Meta()) : TypeBase(nodes(node::none), std::move(m)), _type(std::move(ct)) {} + + const Type& dereferencedType() const { + if ( _type ) + return _type->as(); + else + return childs()[0].as(); } bool operator==(const StrongReference& other) const { return dereferencedType() == other.dereferencedType(); } @@ -38,15 +35,18 @@ class StrongReference : public TypeBase, /** 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 type::detail::isResolved(dereferencedType(), rstate); } + /** Implements the `Type` interface. */ auto typeParameters() const { return childs(); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Node` interface. */ - auto properties() const { return node::Properties{}; } + auto properties() const { return node::Properties{{"type", _type.renderedRid()}}; } private: bool _wildcard = false; + NodeRef _type; }; /** AST node for a `weak_ref` type. */ @@ -56,21 +56,18 @@ class WeakReference : public TypeBase, trait::isDereferencable, trait::isReferenceType { public: - WeakReference(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + WeakReference(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({type::unknown}, std::move(m)), _wildcard(true) {} WeakReference(Type ct, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) {} - Type dereferencedType() const { - if ( auto t = childs()[0].tryAs() ) - return type::effectiveType(*t); - - return type::unknown; - } + const Type& dereferencedType() const { return childs()[0].as(); } bool operator==(const WeakReference& other) const { return dereferencedType() == other.dereferencedType(); } /** 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 type::detail::isResolved(dereferencedType(), rstate); } + /** Implements the `Type` interface. */ auto typeParameters() const { return childs(); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } @@ -89,14 +86,16 @@ class ValueReference : public TypeBase, trait::isDereferencable, trait::isReferenceType { public: - ValueReference(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} - ValueReference(Type ct, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) {} - - Type dereferencedType() const { - if ( auto t = childs()[0].tryAs() ) - return type::effectiveType(*t); - - return type::unknown; + ValueReference(Wildcard /*unused*/, Meta m = Meta()) + : TypeBase(nodes(type::unknown), std::move(m)), _wildcard(true) {} + ValueReference(Type ct, Meta m = Meta()) : TypeBase(nodes(std::move(ct)), std::move(m)) {} + ValueReference(NodeRef ct, Meta m = Meta()) : TypeBase(nodes(type::unknown), std::move(m)), _node(std::move(ct)) {} + + const Type& dereferencedType() const { + if ( _node ) + return _node->as(); + else + return childs()[0].as(); } bool operator==(const ValueReference& other) const { return dereferencedType() == other.dereferencedType(); } @@ -104,15 +103,18 @@ class ValueReference : public TypeBase, /** 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 type::detail::isResolved(dereferencedType(), rstate); } + /** Implements the `Type` interface. */ auto typeParameters() const { return childs(); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Node` interface. */ - auto properties() const { return node::Properties{}; } + auto properties() const { return node::Properties{{"rid", (_node ? _node->rid() : 0u)}}; } private: bool _wildcard = false; + NodeRef _node; }; } // namespace type diff --git a/hilti/toolchain/include/ast/types/regexp.h b/hilti/toolchain/include/ast/types/regexp.h index e9c5f21fa..4066f1341 100644 --- a/hilti/toolchain/include/ast/types/regexp.h +++ b/hilti/toolchain/include/ast/types/regexp.h @@ -18,6 +18,8 @@ class RegExp : public TypeBase, trait::isAllocable, trait::isRuntimeNonTrivial { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; diff --git a/hilti/toolchain/include/ast/types/result.h b/hilti/toolchain/include/ast/types/result.h index fe663d357..bfc233a04 100644 --- a/hilti/toolchain/include/ast/types/result.h +++ b/hilti/toolchain/include/ast/types/result.h @@ -5,6 +5,7 @@ #include #include +#include namespace hilti { namespace type { @@ -12,21 +13,18 @@ namespace type { /** AST node for a "result" type. */ class Result : public TypeBase, trait::isAllocable, trait::isParameterized, trait::isDereferencable { public: - Result(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + Result(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({type::unknown}, std::move(m)), _wildcard(true) {} Result(Type ct, Meta m = Meta()) : TypeBase({std::move(ct)}, std::move(m)) {} - Type dereferencedType() const { - if ( auto t = childs()[0].tryAs() ) - return *t; - - return type::unknown; - } + const Type& dereferencedType() const { return childs()[0].as(); } bool operator==(const Result& other) const { return dereferencedType() == other.dereferencedType(); } /** 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 type::detail::isResolved(dereferencedType(), rstate); } + /** Implements the `Type` interface. */ auto typeParameters() const { return childs(); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } diff --git a/hilti/toolchain/include/ast/types/set.h b/hilti/toolchain/include/ast/types/set.h index 9aac0c208..e4957a0b9 100644 --- a/hilti/toolchain/include/ast/types/set.h +++ b/hilti/toolchain/include/ast/types/set.h @@ -21,11 +21,10 @@ class Iterator : public TypeBase, trait::isRuntimeNonTrivial, trait::isParameterized { public: - Iterator(Type ctype, bool const_, Meta m = Meta()) : TypeBase({std::move(ctype)}, std::move(m)), _const(const_) {} - Iterator(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} - - /** Returns the type of the container the iterator is working on. */ - Type containerType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + Iterator(Type etype, bool const_, Meta m = Meta()) + : TypeBase(nodes(std::move(etype)), std::move(m)), _const(const_) {} + Iterator(Wildcard /*unused*/, bool const_ = true, Meta m = Meta()) + : TypeBase(nodes(type::unknown), std::move(m)), _wildcard(true), _const(const_) {} /** Returns true if the container elements aren't modifiable. */ bool isConstant() const { return _const; } @@ -33,9 +32,9 @@ class Iterator : public TypeBase, /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type dereferencedType() const { - return (_wildcard || containerType().isWildcard()) ? type::unknown : containerType().elementType(); - } + auto _isResolved(ResolvedState* rstate) const { return type::detail::isResolved(dereferencedType(), rstate); } + /** Implements the `Type` interface. */ + const Type& dereferencedType() const { return child(0); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Type` interface. */ @@ -60,15 +59,22 @@ class Set : public TypeBase, trait::isRuntimeNonTrivial, trait::isParameterized { public: - Set(Type t, Meta m = Meta()) : TypeBase({std::move(t)}, std::move(m)) {} - Set(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + Set(Type t, Meta m = Meta()) : TypeBase(nodes(set::Iterator(t, true, m), set::Iterator(t, false, m)), m) {} + Set(Wildcard /*unused*/, Meta m = Meta()) + : TypeBase(nodes(set::Iterator(Wildcard{}, true, m), set::Iterator(Wildcard{}, false, m)), m), + _wildcard(true) {} /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type elementType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + auto _isResolved(ResolvedState* rstate) const { + return type::detail::isResolved(iteratorType(true), rstate) && + type::detail::isResolved(iteratorType(false), rstate); + } + /** Implements the `Type` interface. */ + const Type& elementType() const { return child(0).dereferencedType(); } /** Implements the `Type` interface. */ - Type iteratorType(bool const_) const { return set::Iterator(*this, const_, meta()); } + const Type& iteratorType(bool const_) const { return const_ ? child(0) : child(1); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Type` interface. */ diff --git a/hilti/toolchain/include/ast/types/stream.h b/hilti/toolchain/include/ast/types/stream.h index 949ee26a8..bcd0a5d83 100644 --- a/hilti/toolchain/include/ast/types/stream.h +++ b/hilti/toolchain/include/ast/types/stream.h @@ -20,14 +20,16 @@ class Iterator : public TypeBase, trait::isMutable, trait::isRuntimeNonTrivial { public: - Iterator(Meta m = Meta()) : TypeBase(std::move(m)) {} + Iterator(Meta m = Meta()) : TypeBase(nodes(type::UnsignedInteger(8)), std::move(m)) {} bool operator==(const Iterator& /* other */) const { return true; } /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type dereferencedType() const; + auto _isResolved(ResolvedState* rstate) const { return true; } + /** Implements the `Type` interface. */ + const Type& dereferencedType() const { return child(0); } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; @@ -35,16 +37,18 @@ class Iterator : public TypeBase, /** AST node for a stream view type. */ class View : public TypeBase, trait::isView, trait::isIterable, trait::isAllocable, trait::isRuntimeNonTrivial { public: - View(Meta m = Meta()); + View(Meta m = Meta()) : TypeBase(nodes(stream::Iterator(m)), m) {} bool operator==(const View& /* other */) const { return true; } /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type elementType() const { return type::UnsignedInteger(8); } + auto _isResolved(ResolvedState* rstate) const { return true; } + /** Implements the `Type` interface. */ + const Type& elementType() const { return iteratorType(true).dereferencedType(); } /** Implements the `Type` interface. */ - Type iteratorType(bool /* const_ */) const { return stream::Iterator(meta()); } + const Type& iteratorType(bool /* const_ */) const { return child(0); } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; @@ -59,33 +63,28 @@ class Stream : public TypeBase, trait::isViewable, trait::isRuntimeNonTrivial { public: - Stream(Meta m = Meta()) : TypeBase(std::move(m)) {} + Stream(Meta m = Meta()) : TypeBase(nodes(stream::View(m)), m) {} bool operator==(const Stream& /* other */) const { return true; } /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type elementType() const { return type::UnsignedInteger(8); } - + auto _isResolved(ResolvedState* rstate) const { return true; } /** Implements the `Type` interface. */ - Type iteratorType(bool /* const_ */) const { return stream::Iterator(meta()); } + const Type& elementType() const { return iteratorType(true).dereferencedType(); } /** Implements the `Type` interface. */ - Type viewType() const { return stream::View(meta()); } + const Type& iteratorType(bool /* const_ */) const { return viewType().iteratorType(true); } + /** Implements the `Type` interface. */ + const Type& viewType() const { return child(0); } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } - -private: - std::optional _etype; }; namespace detail::stream { inline Node element_type = Node(type::UnsignedInteger(8, Location())); } // namespace detail::stream -inline Type stream::Iterator::dereferencedType() const { return type::UnsignedInteger(8); } -inline stream::View::View(Meta m) : TypeBase({type::Stream()}, std::move(m)) {} - } // namespace type } // namespace hilti diff --git a/hilti/toolchain/include/ast/types/string.h b/hilti/toolchain/include/ast/types/string.h index 23a42a2f3..40e98c80b 100644 --- a/hilti/toolchain/include/ast/types/string.h +++ b/hilti/toolchain/include/ast/types/string.h @@ -18,6 +18,8 @@ class String : public TypeBase, trait::isAllocable { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; diff --git a/hilti/toolchain/include/ast/types/struct.h b/hilti/toolchain/include/ast/types/struct.h index 9280d489d..10a8be20b 100644 --- a/hilti/toolchain/include/ast/types/struct.h +++ b/hilti/toolchain/include/ast/types/struct.h @@ -3,164 +3,63 @@ #pragma once #include +#include #include +#include #include #include #include +#include +#include +#include #include +#include +#include +#include #include #include +#include +#include #include #include +#include #include namespace hilti { namespace type { -namespace struct_ { -/** AST node for a struct field. */ -class Field : public NodeBase { -public: - Field() : NodeBase({ID(""), type::unknown, node::none, node::none}, Meta()) {} - Field(ID id, Type t, std::optional attrs = {}, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(t), node::none, std::move(attrs), node::none), std::move(m)) {} - Field(ID id, Type t, Type aux_type, std::optional attrs, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(t), std::move(aux_type), std::move(attrs), node::none), - std::move(m)) {} - Field(ID id, ::hilti::function::CallingConvention cc, type::Function ft, std::optional attrs = {}, - Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(ft), node::none, std::move(attrs), node::none), std::move(m)), - _cc(cc) {} - Field(ID id, hilti::Function inline_func, std::optional attrs = {}, Meta m = Meta()) - : NodeBase(nodes(std::move(id), node::none, node::none, std::move(attrs), std::move(inline_func)), - std::move(m)), - _cc(inline_func.callingConvention()) {} - - const auto& id() const { return child(0); } - - auto callingConvention() const { return _cc; } - auto inlineFunction() const { return childs()[4].tryReferenceAs(); } - auto attributes() const { return childs()[3].tryReferenceAs(); } - - Type type() const { - if ( ! _cache.type ) { - if ( auto func = inlineFunction() ) - _cache.type = func->type(); - else - _cache.type = type::effectiveType(child(1)); - } - - return *_cache.type; - } - - /** - * Returns the auxiliary type as passed into the corresponding - * constructor, if any. The auxiliary type isn't used for anything by - * HILTI itself, but it's as a node in aside the AST for use by external - * code. - */ - std::optional auxType() const { - if ( auto t = childs()[2].tryAs() ) - return type::effectiveType(*t); - else - return {}; - } - - std::optional default_() const { - if ( auto a = AttributeSet::find(attributes(), "&default") ) - return *a->valueAs(); - - return {}; - } - - 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(); } - auto isNoEmit() const { return AttributeSet::find(attributes(), "&no-emit").has_value(); } - - /** Internal method for use by builder API only. */ - auto& _typeNode() { return childs()[1]; } - - /** Implements the `Node` interface. */ - auto properties() const { return node::Properties{{"cc", to_string(_cc)}}; } - - bool operator==(const Field& other) const { - return id() == other.id() && type() == other.type() && attributes() == other.attributes() && _cc == other._cc; - } - - /** - * Copies an existing field but replaces its attributes. - * - * @param f original field - * @param attrs new attributes - * @return new field with attributes replaced - */ - static Field setAttributes(const Field& f, const AttributeSet& attrs) { - auto x = Field(f); - x.childs()[3] = attrs; - return x; - } - - void clearCache() { _cache.type.reset(); } - -private: - ::hilti::function::CallingConvention _cc = ::hilti::function::CallingConvention::Standard; - - mutable struct { std::optional type; } _cache; -}; // namespace struct_ - -inline Node to_node(Field f) { return Node(std::move(f)); } - -} // namespace struct_ - /** AST node for a struct type. */ -class Struct : public TypeBase, trait::isAllocable, trait::isParameterized, trait::isMutable { +class Struct : public TypeBase, trait::isAllocable, trait::isParameterized, trait::takesArguments, trait::isMutable { public: - Struct(std::vector fields, Meta m = Meta()) - : TypeBase(nodes(node::none, std::move(fields)), std::move(m)) { - _state().flags += type::Flag::NoInheritScope; - } + Struct(std::vector fields, Meta m = Meta()) : TypeBase(nodes(node::none, std::move(fields)), m) {} - Struct(std::vector params, std::vector fields, Meta m = Meta()) + Struct(std::vector params, std::vector fields, Meta m = Meta()) : TypeBase(nodes(node::none, std::move(fields), util::transform(params, - [](const auto& p) { - return type::function::Parameter::setIsStructParameter(p); + [](auto p) { + p.setIsTypeParameter(); + return Declaration(p); })), - std::move(m)) { - _state().flags += type::Flag::NoInheritScope; - } + std::move(m)) {} + + Struct(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(nodes(node::none), m), _wildcard(true) {} - Struct(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(nodes(node::none), std::move(m)), _wildcard(true) { - _state().flags += type::Flag::NoInheritScope; + NodeRef selfRef() const { + if ( childs()[0].isA() ) + return NodeRef(childs()[0]); + else + return {}; } auto hasFinalizer() const { return field("~finally").has_value(); } - auto parameters() const { return childsOfType(); } + auto parameterRefs() const { return childRefsOfType(); } - std::vector parameterNodes() { - std::vector params; - for ( auto& c : childs() ) { - if ( c.isA() ) - params.emplace_back(NodeRef(c)); - } - return params; - } - - auto fields() const { return childsOfType(); } + auto fields() const { return childsOfType(); } - auto types() const { - std::vector types; - for ( auto c = ++childs().begin(); c != childs().end(); c++ ) - types.push_back(c->as().type()); - - return types; - } - - std::optional field(const ID& id) const { - for ( auto f : fields() ) { + hilti::optional_ref field(const ID& id) const { + for ( const auto& f : fields() ) { if ( f.id() == id ) return f; } @@ -168,34 +67,45 @@ class Struct : public TypeBase, trait::isAllocable, trait::isParameterized, trai return {}; } - auto fields(const ID& id) const { - std::vector x; - + hilti::node::Set fields(const ID& id) const { + hilti::node::Set x; for ( const auto& f : fields() ) { if ( f.id() == id ) - x.push_back(f); + x.insert(f); } return x; } - bool operator==(const Struct& other) const { - if ( typeID() && other.typeID() ) - return *typeID() == *other.typeID(); - - return fields() == other.fields(); + void addField(Declaration f) { + assert(f.isA()); + addChild(std::move(f)); } - /** For internal use by the builder API only. */ - auto _fieldNodes() { return nodesOfType(); } + bool operator==(const Struct& other) const { return fields() == other.fields(); } /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto _isResolved(ResolvedState* rstate) const { + for ( const auto& c : childs() ) { + if ( auto f = c.tryAs() ) { + if ( ! f->isResolved(rstate) ) + return false; + } + else if ( auto p = c.tryAs() ) + if ( ! p->isResolved(rstate) ) + return false; + } + + return true; + } + /** Implements the `Type` interface. */ auto typeParameters() const { std::vector params; - for ( auto c = ++childs().begin(); c != childs().end(); c++ ) - params.emplace_back(c->as().type()); + for ( const auto& f : fields() ) + params.emplace_back(f.type()); return params; } /** Implements the `Type` interface. */ @@ -205,16 +115,17 @@ class Struct : public TypeBase, trait::isAllocable, trait::isParameterized, trai auto properties() const { return node::Properties{}; } /** - * Copies an existing type and adds a new field to the copy. - * - * @param s original type - * @param f field to add - * @return new typed with field added + * Given an existing node wrapping a struct type, updates the contained + * struct type to have its `self` declaration initialized. The struct + * type's constructor cannot do this because we need the `Node` shell for + * this. */ - static Struct addField(const Struct& s, struct_::Field f) { - auto x = Type(s)._clone().as(); - x.addChild(std::move(f)); - return x; + static void setSelf(Node* n) { + assert(n->isA()); + Expression self = + expression::Keyword(expression::keyword::Kind::Self, type::ValueReference(NodeRef(*n)), n->meta()); + Declaration d = declaration::Expression("self", std::move(self), declaration::Linkage::Private, n->meta()); + n->childs()[0] = std::move(d); } private: diff --git a/hilti/toolchain/include/ast/types/time.h b/hilti/toolchain/include/ast/types/time.h index 711e3070b..da4c9f462 100644 --- a/hilti/toolchain/include/ast/types/time.h +++ b/hilti/toolchain/include/ast/types/time.h @@ -18,6 +18,8 @@ class Time : public TypeBase, trait::isAllocable { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; diff --git a/hilti/toolchain/include/ast/types/tuple.h b/hilti/toolchain/include/ast/types/tuple.h index 3b38a2215..599e08baf 100644 --- a/hilti/toolchain/include/ast/types/tuple.h +++ b/hilti/toolchain/include/ast/types/tuple.h @@ -1,7 +1,8 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. +// Copyrights (c) 2020-2021 by the Zeek Project. See LICENSE for details. #pragma once +#include #include #include @@ -11,27 +12,58 @@ namespace hilti { namespace type { +namespace tuple { + +/** AST node for a tuple element. */ +class Element : public NodeBase { +public: + explicit Element(Type t, Meta m = Meta()) : NodeBase(nodes(node::none, std::move(t)), std::move(m)) {} + Element(ID id, Type t, Meta m = Meta()) + : NodeBase(nodes(id ? std::move(id) : node::none, std::move(t)), std::move(m)) {} + Element(Meta m = Meta()) : NodeBase(nodes(node::none, node::none), std::move(m)) {} + + auto id() const { return childs()[0].tryAs(); } + const auto& type() const { return child(1); } + + /** Implements the `Node` interface. */ + auto properties() const { return node::Properties{}; } + + bool operator==(const Element& other) const { return id() == other.id() && type() == other.type(); } +}; + +inline Node to_node(Element f) { return Node(std::move(f)); } + +} // namespace tuple + /** AST node for a tuple type. */ class Tuple : public TypeBase, trait::isAllocable, trait::isParameterized { public: - Tuple(std::vector t, Meta m = Meta()) : TypeBase(nodes(std::move(t)), std::move(m)) {} - Tuple(std::vector> t, Meta m = Meta()) : TypeBase(nodes(std::move(t)), std::move(m)) {} + Tuple(std::vector t, Meta m = Meta()) : TypeBase(nodes(_typesToElements(std::move(t))), std::move(m)) {} + Tuple(std::vector e, Meta m = Meta()) : TypeBase(nodes(std::move(e)), std::move(m)) {} Tuple(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(std::move(m)), _wildcard(true) {} - auto types() const { return childsOfType(); } - std::vector ids() const; - auto elements() const { return util::zip2(ids(), types()); } - std::optional> elementByID(const ID& id); + auto elements() const { return childs(0, -1); } + std::optional> elementByID(const ID& id) const; bool operator==(const Tuple& other) const { if ( _wildcard || other._wildcard ) return _wildcard && other._wildcard; - return types() == other.types() && ids() == other.ids(); + return elements() == other.elements(); } /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto _isResolved(ResolvedState* rstate) const { + for ( const auto& c : childs() ) { + if ( auto t = c.tryAs(); t && ! type::detail::isResolved(*t, rstate) ) + return false; + } + + return true; + } + /** Implements the `Type` interface. */ auto typeParameters() const { return childs(); } /** Implements the `Type` interface. */ @@ -41,6 +73,14 @@ class Tuple : public TypeBase, trait::isAllocable, trait::isParameterized { auto properties() const { return node::Properties{{"wildcard", _wildcard}}; } private: + std::vector _typesToElements(std::vector&& types) { + std::vector elements; + for ( auto&& t : types ) + elements.emplace_back(std::move(t), t.meta()); + + return elements; + } + bool _wildcard = false; }; diff --git a/hilti/toolchain/include/ast/types/type.h b/hilti/toolchain/include/ast/types/type.h index a359a2f9c..085ab5461 100644 --- a/hilti/toolchain/include/ast/types/type.h +++ b/hilti/toolchain/include/ast/types/type.h @@ -13,16 +13,18 @@ namespace type { /** AST node for a type representing a type value. */ class Type_ : public TypeBase, trait::isParameterized { public: - Type_(Type t, Meta m = Meta()) : TypeBase({std::move(t)}, std::move(m)) {} - Type_(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(std::move(m)), _wildcard(true) {} + Type_(Type t, Meta m = Meta()) : TypeBase(nodes(std::move(t)), std::move(m)) {} + Type_(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(nodes(type::Any()), std::move(m)), _wildcard(true) {} - auto typeValue() const { return _wildcard ? type::Any() : type::effectiveType(child(0)); } + const auto& typeValue() const { return child(0); } bool operator==(const Type_& other) const { return typeValue() == other.typeValue(); } /** 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 type::detail::isResolved(typeValue(), rstate); } + /** Implements the `Type` interface. */ auto typeParameters() const { return childs(); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } diff --git a/hilti/toolchain/include/ast/types/union.h b/hilti/toolchain/include/ast/types/union.h index 1aa7fff25..1c9cd2a51 100644 --- a/hilti/toolchain/include/ast/types/union.h +++ b/hilti/toolchain/include/ast/types/union.h @@ -2,10 +2,12 @@ #pragma once +#include #include #include #include +#include #include #include #include @@ -16,70 +18,17 @@ namespace hilti { namespace type { -namespace union_ { -/** AST node for a struct field. */ -class Field : public NodeBase { -public: - Field() : NodeBase({ID(""), type::unknown, node::none}, Meta()) {} - Field(ID id, Type t, std::optional attrs = {}, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(t), std::move(attrs)), std::move(m)) {} - - const auto& id() const { return child(0); } - auto type() const { return type::effectiveType(child(1)); } - auto attributes() const { return childs()[2].tryReferenceAs(); } - - /** Implements the `Node` interface. */ - auto properties() const { return node::Properties{}; } - - bool operator==(const Field& other) const { - return id() == other.id() && type() == other.type() && attributes() == other.attributes(); - } - - /** - * Copies an existing field but replaces its attributes. - * - * @param f original field - * @param attrs new attributes - * @return new field with attributes replaced - */ - static Field setAttributes(const Field& f, const AttributeSet& attrs) { - auto x = Field(f); - x.childs()[2] = attrs; - return x; - } -}; - -inline Node to_node(Field f) { return Node(std::move(f)); } - -} // namespace union_ - /** AST node for a struct type. */ class Union : public TypeBase, trait::isAllocable, trait::isParameterized, trait::isMutable { public: - Union(std::vector fields, Meta m = Meta()) + Union(std::vector fields, Meta m = Meta()) : TypeBase(nodes(node::none, std::move(fields)), std::move(m)) {} Union(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(nodes(node::none), std::move(m)), _wildcard(true) {} - auto fields() const { return childsOfType(); } - - auto types() const { - std::vector types; - for ( auto c = ++childs().begin(); c != childs().end(); c++ ) - types.push_back(c->as().type()); - - return types; - } - - auto ids() const { - std::vector ids; - for ( auto c = ++childs().begin(); c != childs().end(); c++ ) - ids.push_back(c->as().id()); - - return ids; - } + auto fields() const { return childsOfType(); } - std::optional field(const ID& id) const { - for ( auto f : fields() ) { + hilti::optional_ref field(const ID& id) const { + for ( const auto& f : fields() ) { if ( f.id() == id ) return f; } @@ -96,31 +45,26 @@ class Union : public TypeBase, trait::isAllocable, trait::isParameterized, trait return 0; } - auto fields(const ID& id) const { - std::vector x; + bool operator==(const Union& other) const { return fields() == other.fields(); } - for ( const auto& f : fields() ) { - if ( f.id() == id ) - x.push_back(f); - } - - return x; - } + /** Implements the `Type` interface. */ + auto isEqual(const Type& other) const { return node::isEqual(this, other); } - bool operator==(const Union& other) const { - if ( typeID() && other.typeID() ) - return *typeID() == *other.typeID(); + /** Implements the `Type` interface. */ + auto _isResolved(ResolvedState* rstate) const { + for ( auto c = ++childs().begin(); c != childs().end(); c++ ) { + if ( ! c->as().isResolved(rstate) ) + return false; + } - return fields() == other.fields(); + return true; } - /** Implements the `Type` interface. */ - auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ auto typeParameters() const { std::vector params; for ( auto c = ++childs().begin(); c != childs().end(); c++ ) - params.emplace_back(c->as().type()); + params.emplace_back(c->as().type()); return params; } /** Implements the `Type` interface. */ @@ -136,7 +80,7 @@ class Union : public TypeBase, trait::isAllocable, trait::isParameterized, trait * @param f field to add * @return new typed with field added */ - static Union addField(const Union& s, union_::Field f) { + static Union addField(const Union& s, declaration::Field f) { auto x = Type(s)._clone().as(); x.addChild(std::move(f)); return x; diff --git a/hilti/toolchain/include/ast/types/unknown.h b/hilti/toolchain/include/ast/types/unknown.h index 3745f241b..72f65a93f 100644 --- a/hilti/toolchain/include/ast/types/unknown.h +++ b/hilti/toolchain/include/ast/types/unknown.h @@ -10,12 +10,14 @@ namespace hilti { namespace type { /** AST node for an unknown place-holder type. */ -class Unknown : public TypeBase, public util::type_erasure::trait::Singleton { +class Unknown : public TypeBase, public type::trait::isAllocable, public util::type_erasure::trait::Singleton { public: bool operator==(const Unknown& /* other */) const { return true; } /** 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; } // sic! /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } diff --git a/hilti/toolchain/include/ast/types/unresolved-id.h b/hilti/toolchain/include/ast/types/unresolved-id.h new file mode 100644 index 000000000..4d50ef98b --- /dev/null +++ b/hilti/toolchain/include/ast/types/unresolved-id.h @@ -0,0 +1,35 @@ +// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace hilti { +namespace type { + +/** AST node for an unresolved type ID. */ +class UnresolvedID : public TypeBase { +public: + UnresolvedID(::hilti::ID id, Meta m = Meta()) : TypeBase({std::move(id)}, std::move(m)) {} + + const auto& id() const { return child<::hilti::ID>(0); } + + bool operator==(const UnresolvedID& other) const { return id() == other.id(); } + + // Type interface. + auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto _isResolved(ResolvedState* rstate) const { return false; } + + // Node interface. + auto properties() const { return node::Properties{}; } +}; + +} // namespace type +} // namespace hilti diff --git a/hilti/toolchain/include/ast/types/vector.h b/hilti/toolchain/include/ast/types/vector.h index cbd7c1542..62e76345e 100644 --- a/hilti/toolchain/include/ast/types/vector.h +++ b/hilti/toolchain/include/ast/types/vector.h @@ -21,11 +21,10 @@ class Iterator : public TypeBase, trait::isRuntimeNonTrivial, trait::isParameterized { public: - Iterator(Type ctype, bool const_, Meta m = Meta()) : TypeBase({std::move(ctype)}, std::move(m)), _const(const_) {} - Iterator(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} - - /** Returns the type of the container the iterator is working on. */ - Type containerType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + Iterator(Type etype, bool const_, Meta m = Meta()) + : TypeBase(nodes(std::move(etype)), std::move(m)), _const(const_) {} + Iterator(Wildcard /*unused*/, bool const_ = true, Meta m = Meta()) + : TypeBase(nodes(type::unknown), std::move(m)), _wildcard(true), _const(const_) {} /** Returns true if the container elements aren't modifiable. */ bool isConstant() const { return _const; } @@ -33,9 +32,9 @@ class Iterator : public TypeBase, /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type dereferencedType() const { - return (_wildcard || containerType().isWildcard()) ? type::unknown : containerType().elementType(); - } + auto _isResolved(ResolvedState* rstate) const { return type::detail::isResolved(dereferencedType(), rstate); } + /** Implements the `Type` interface. */ + const Type& dereferencedType() const { return child(0); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Type` interface. */ @@ -60,15 +59,22 @@ class Vector : public TypeBase, trait::isRuntimeNonTrivial, trait::isParameterized { public: - Vector(Type t, Meta m = Meta()) : TypeBase({std::move(t)}, std::move(m)) {} - Vector(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({node::none}, std::move(m)), _wildcard(true) {} + Vector(Type t, Meta m = Meta()) : TypeBase(nodes(vector::Iterator(t, true, m), vector::Iterator(t, false, m)), m) {} + Vector(Wildcard /*unused*/, Meta m = Meta()) + : TypeBase(nodes(vector::Iterator(Wildcard{}, true, m), vector::Iterator(Wildcard{}, false, m)), m), + _wildcard(true) {} /** Implements the `Type` interface. */ auto isEqual(const Type& other) const { return node::isEqual(this, other); } /** Implements the `Type` interface. */ - Type elementType() const { return _wildcard ? type::unknown : type::effectiveType(child(0)); } + auto _isResolved(ResolvedState* rstate) const { + return type::detail::isResolved(iteratorType(true), rstate) && + type::detail::isResolved(iteratorType(false), rstate); + } + /** Implements the `Type` interface. */ + const Type& elementType() const { return child(0).dereferencedType(); } /** Implements the `Type` interface. */ - Type iteratorType(bool const_) const { return vector::Iterator(*this, const_, meta()); } + const Type& iteratorType(bool const_) const { return const_ ? child(0) : child(1); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Type` interface. */ diff --git a/hilti/toolchain/include/ast/types/void.h b/hilti/toolchain/include/ast/types/void.h index 5df313445..47fd3796e 100644 --- a/hilti/toolchain/include/ast/types/void.h +++ b/hilti/toolchain/include/ast/types/void.h @@ -12,16 +12,27 @@ namespace type { /** AST node for a void type. */ class Void : public TypeBase { public: - Void(Meta m = Meta()) : TypeBase(std::move(m)) {} - bool operator==(const Void& /* other */) const { return true; } // Type interface. auto isEqual(const Type& other) const { return node::isEqual(this, other); } + /** Implements the `Type` interface. */ + auto _isResolved(ResolvedState* rstate) const { return true; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } + + /** + * Wrapper around constructor so that we can make it private. Don't use + * this, use the singleton `type::void_` instead. + */ + static Void create(Meta m = Meta()) { return Void(std::move(m)); } + +private: + Void(Meta m = Meta()) : TypeBase(std::move(m)) {} }; +/** Singleton. */ +static const Type void_ = Void::create(Location("")); } // namespace type } // namespace hilti diff --git a/hilti/toolchain/include/base/id-base.h b/hilti/toolchain/include/base/id-base.h index abf1fc8cf..d12fe93ab 100644 --- a/hilti/toolchain/include/base/id-base.h +++ b/hilti/toolchain/include/base/id-base.h @@ -55,8 +55,11 @@ class IDBase { /** Returns true if the ID's value has length zero. */ bool empty() const { return _id.empty(); } + /** Returns the number of namespace components (incl. the local ID0. */ + auto length() const { return util::split(_id, "::").size(); } + /** - * Returns a new ID containing just single component of the path;s of the + * Returns a new ID containing just single component of the path's of the * ID. Indices are zero-based and, if negative, counted from the end * Python-style. * diff --git a/hilti/toolchain/include/base/logger.h b/hilti/toolchain/include/base/logger.h index 4c9f193c5..ae3bcc6b1 100644 --- a/hilti/toolchain/include/base/logger.h +++ b/hilti/toolchain/include/base/logger.h @@ -191,6 +191,11 @@ class Logger { _debug_streams[dbg] -= 1; } + void debugSetIndent(const logging::DebugStream& dbg, int indent) { + if ( isEnabled(dbg) ) + _debug_streams[dbg] = indent; + } + int errors() const { return _errors; } int warnings() const { return _warnings; } diff --git a/hilti/toolchain/include/base/optional-ref.h b/hilti/toolchain/include/base/optional-ref.h index 6ad7a22ed..940e6467c 100644 --- a/hilti/toolchain/include/base/optional-ref.h +++ b/hilti/toolchain/include/base/optional-ref.h @@ -1,8 +1,8 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. // -// This was originally inspired by the version in -// https://github.com/Chlorie/clu, but it turned into pretty much a rewrite. (That -// code comes with an MIT license, FWIW). +// This was originally inspired by similar code part of +// https://github.com/Chlorie/clu, but it turned into pretty much a rewrite. +// (FWIW, that code comes with an MIT license.) #pragma once @@ -29,8 +29,9 @@ class optional_ref { optional_ref(std::nullopt_t) {} optional_ref(T& other) : _ptr(&other) {} optional_ref(T&& other) = delete; // to avoid easy mistakes + ~optional_ref() = default; - bool has_value() const { return _ptr; } + bool has_value() const { return _ptr != nullptr; } T& value() const { if ( ! _ptr ) diff --git a/hilti/toolchain/include/base/type_erase.h b/hilti/toolchain/include/base/type_erase.h index 34cb3f5ec..4c780804e 100644 --- a/hilti/toolchain/include/base/type_erase.h +++ b/hilti/toolchain/include/base/type_erase.h @@ -152,7 +152,6 @@ class ErasedBase : public trait::TypeErased { ErasedBase(T t, ConceptArgs&&... args) : _data(make_intrusive>(std::move(t), std::forward(args)...)) {} - explicit ErasedBase(IntrusivePtr data) : _data(std::move(data)) {} ErasedBase& operator=(IntrusivePtr data) { _data = std::move(data); ; @@ -217,16 +216,7 @@ class ErasedBase : public trait::TypeErased { /** Attempts to cast the contained object into a specified type. */ template - std::optional tryAs() const { - if ( auto p = _tryAs() ) - return *p; - - return {}; - } - - /** Attempts to cast the contained object into a specified type. */ - template - optional_ref tryReferenceAs() const { + optional_ref tryAs() const { if ( auto p = _tryAs() ) return *p; diff --git a/hilti/toolchain/include/base/util.h b/hilti/toolchain/include/base/util.h index 7682da9f0..e40744194 100644 --- a/hilti/toolchain/include/base/util.h +++ b/hilti/toolchain/include/base/util.h @@ -185,6 +185,7 @@ std::string join(const T& l, const std::string& delim = "") { for ( const auto& i : l ) { if ( not first ) result += delim; + result += std::string(i); first = false; } diff --git a/hilti/toolchain/include/base/visitor-types.h b/hilti/toolchain/include/base/visitor-types.h index 0926fb53a..d92869fe1 100644 --- a/hilti/toolchain/include/base/visitor-types.h +++ b/hilti/toolchain/include/base/visitor-types.h @@ -5,6 +5,8 @@ #include #include +#include + namespace hilti::visitor { /** Represents the location of a single node inside an AST during iteration. */ @@ -66,6 +68,17 @@ struct Position { return std::nullopt; } + + /** Returns a reference to the first parent that has a given type. */ + template + NodeRef findParentRef() const { + for ( auto i = path.rbegin() + 1; i != path.rend(); i++ ) { + if ( (**i).template isA() ) + return NodeRef(**i); + } + + return {}; + } }; } // namespace hilti::visitor diff --git a/hilti/toolchain/include/base/visitor.h b/hilti/toolchain/include/base/visitor.h index 9b678eb4a..5e489e7c1 100644 --- a/hilti/toolchain/include/base/visitor.h +++ b/hilti/toolchain/include/base/visitor.h @@ -142,13 +142,19 @@ class Iterator { p.child += 1; if ( p.child == -1 ) { - if constexpr ( order == Order::Pre ) + if ( order == Order::Pre || p.node.pruneWalk() ) return; next(); return; } + if ( p.node.pruneWalk() ) { + _path.pop_back(); + next(); + return; + } + assert(p.child >= 0); if ( p.child < static_cast(p.node.childs().size()) ) { diff --git a/hilti/toolchain/include/compiler/coercion.h b/hilti/toolchain/include/compiler/coercion.h index e3e41cf50..a99359798 100644 --- a/hilti/toolchain/include/compiler/coercion.h +++ b/hilti/toolchain/include/compiler/coercion.h @@ -55,12 +55,6 @@ enum class CoercionStyle { */ TryCoercion = (1U << 5U), - /** - * If the source expression's AST node has an original type associated - * with it, use that's type for coercion. - */ - PreferOriginalType = (1U << 6U), - /** Never allow any substantial type changes. */ DisallowTypeChanges = (1U << 7U), @@ -168,8 +162,7 @@ struct CoercedExpression { CoercedExpression(Type src, Expression coerced) : coerced(coerced), nexpr(coerced), - consider_type_changed(type::effectiveType(std::move(src)).typename_() != - type::effectiveType(coerced.type()).typename_()) {} + consider_type_changed(std::move(src).typename_() != coerced.type().typename_()) {} /** Represents an unsuccessful coercion. */ CoercedExpression() = default; @@ -242,7 +235,7 @@ CoercedExpression coerceExpression(const Expression& e, const Type& src_, const * available (missing expressions for optional operands without defaults will * remain left out). If unsuccessful, an error. */ -Result>> coerceOperands(const std::vector& exprs, +Result>> coerceOperands(const hilti::node::Range& exprs, const std::vector& operands, bitmask style); @@ -271,5 +264,11 @@ Result coerceCtor(Ctor c, const Type& dst, bitmask style = Result coerceType(const Type& src_, const Type& dst_, bitmask style = CoercionStyle::TryAllForAssignment); +namespace detail { +/** Implements the corresponding functionality for the default HILTI compiler plugin. */ +std::optional coerceCtor(Ctor c, const Type& dst, bitmask style); +/** Implements the corresponding functionality for the default HILTI compiler plugin. */ +std::optional coerceType(Type t, const Type& dst, bitmask style); +} // namespace detail } // namespace hilti diff --git a/hilti/toolchain/include/compiler/context.h b/hilti/toolchain/include/compiler/context.h index d1989e66d..aa2a8fe63 100644 --- a/hilti/toolchain/include/compiler/context.h +++ b/hilti/toolchain/include/compiler/context.h @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -16,12 +15,14 @@ #include #include +#include #include #include namespace hilti { class PluginRegistry; +class Unit; /** * Options controlling the compiler's code generation. @@ -102,13 +103,13 @@ namespace context { * meaning that the mapping from path to ID must be consisten throughout all * processing. */ -struct ModuleIndex { +struct CacheIndex { ID id; /**< module ID */ hilti::rt::filesystem::path path; /**< path to module's source code on disk; can be left empty if no file exists */ - ModuleIndex() = default; - ModuleIndex(ID id, const hilti::rt::filesystem::path& path) : id(std::move(id)), path(util::normalizePath(path)) {} - bool operator<(const ModuleIndex& other) const { return id < other.id; } + CacheIndex() = default; + CacheIndex(ID id, const hilti::rt::filesystem::path& path) : id(std::move(id)), path(util::normalizePath(path)) {} + bool operator<(const CacheIndex& other) const { return id < other.id; } }; /** @@ -117,18 +118,11 @@ struct ModuleIndex { * "final" is set, the information is assumed to correct and no longer * changing. */ -struct CachedModule { - ModuleIndex index; /**< ID and path of module */ - NodeRef node; /**< module's root AST node */ - bool requires_compilation = - false; /**< true if the module contains code that requires compilation itself (vs. modules that only declare - elements, but don't generate produce any code for linking) */ - std::optional> dependencies; /**< further modules imported by the processed one */ - - bool final = false; /**< once true, one can start relying on the other fields outside of AST processing */ - - CachedModule() = default; - CachedModule(ModuleIndex index, NodeRef node) : index(std::move(index)), node(std::move(node)) {} +struct CacheEntry { + std::shared_ptr unit; /**< cached unit */ + + CacheEntry() = default; + CacheEntry(std::shared_ptr unit) : unit(std::move(unit)) {} }; } // namespace context @@ -148,59 +142,55 @@ class Context { const Options& options() const { return _options; } /** - * Makes a new module known to the context, which will take ownershiup - * and cache it, along with further meta data. A module with the same ID - * or path must only be registered once, the method will abort otherwise. + * Caches a code unit inside the context. The cache uses a unit's `(ID, + * path)` tuple as the index. Any previously cached unit with the same + * index tuple will be replaced. * - * @param idx cache index for module - * @param module module to cache - * @param requires_compilation initial value for the corresponding `CachedModule` field; this may later be - * overridden if AST processing finds out more + * @param unit unit to cache * @return the meta data associated with the newly registered module */ - const context::CachedModule& registerModule(const context::ModuleIndex& idx, Node&& module, - bool requires_compilation); + void cacheUnit(std::shared_ptr unit); /** - * Updates the meta data associated with a previoysly cached module AST. + * Looks up a previously cached unit by its ID. * - * @param module module to cache; all the fields of the struct must have been filled out + * @param path path to look up a unit for + * @param extension a file extension expected for the unit, indicating its + * source language; a cached unit will only be returned if its extension + * matches + * @return the cache entry associated with the path if found */ - void updateModule(const context::CachedModule& module); + std::optional lookupUnit(const ID& id, const hilti::rt::filesystem::path& extension); /** - * Looks up a previously cached module AST. + * Looks up a previously cached unit by its path. It will only return a + * cached module if its extension matches that of the given path. * - * @param id ID that was used to cache the AST - * @return the meta data associated with the previously cached module, or not set if no module is associated with - * that ID + * @param path path to look up a unit for + * @return the cache entry associated with the path if found */ - std::optional lookupModule(const ID& id); + std::optional lookupUnit(const hilti::rt::filesystem::path& path, + std::optional ast_extension = {}); /** - * Looks up a previously cached module AST. - * - * @param path path that was used to cache the AST - * @return the meta data associated with the previously cached module, or not set if no module is associated with - * that path + * Returns all (direct & indirect) dependencies that a module imports. This + * information will be complete only once all AST have been fully resolved. */ - std::optional lookupModule(const hilti::rt::filesystem::path& path); + std::vector> lookupDependenciesForUnit(const ID& id, + const hilti::rt::filesystem::path& extension); /** - * Returns all (direct) dependencies that a module imports. This - * information may be correct yet, if `final` isn't set in the module - * meta data. + * Dumps the current state of the unit cache to a debug stream. * - * @param meta data for all dependencies + * @param stream debug stream to write to */ - std::vector lookupDependenciesForModule(const ID& id); + void dumpUnitCache(const hilti::logging::DebugStream& stream); private: Options _options; - std::vector, std::shared_ptr>> _modules; - std::unordered_map> _module_cache_by_id; - std::unordered_map> _module_cache_by_path; + std::unordered_map> _unit_cache_by_id; + std::unordered_map> _unit_cache_by_path; }; } // namespace hilti diff --git a/hilti/toolchain/include/compiler/detail/codegen/codegen.h b/hilti/toolchain/include/compiler/detail/codegen/codegen.h index f0a01e68b..65ace19c8 100644 --- a/hilti/toolchain/include/compiler/detail/codegen/codegen.h +++ b/hilti/toolchain/include/compiler/detail/codegen/codegen.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include #include @@ -64,8 +65,8 @@ class CodeGen { /** Entry point for generating additional cross-unit C++ code through HILTI's linker. */ Result linkUnits(const std::vector& mds); - std::shared_ptr context() const { return _context; } - const Options& options() const { return _context->options(); } + std::shared_ptr context() const { return _context.lock(); } + const Options& options() const { return context()->options(); } // These must be called only while a module is being compiled. std::optional typeDeclaration(const hilti::Type& t); @@ -79,8 +80,10 @@ class CodeGen { function::CallingConvention cc = function::CallingConvention::Standard, const std::optional& fattrs = {}, std::optional namespace_ = {}); - std::vector compileCallArguments(const std::vector& args, - const std::vector& params); + std::vector compileCallArguments(const hilti::node::Range& args, + const hilti::node::Set& params); + std::vector compileCallArguments(const hilti::node::Range& args, + const hilti::node::Range& params); std::optional typeDefaultValue(const hilti::Type& t); cxx::Expression typeInfo(const hilti::Type& t); @@ -127,7 +130,7 @@ class CodeGen { std::unique_ptr _cxx_unit; hilti::Unit* _hilti_unit = nullptr; - std::shared_ptr _context; + std::weak_ptr _context; std::vector _selfs = {"__self"}; std::vector _cxx_blocks; std::vector _tmps; diff --git a/hilti/toolchain/include/compiler/detail/cxx/unit.h b/hilti/toolchain/include/compiler/detail/cxx/unit.h index cb15992e3..d8bbad94c 100644 --- a/hilti/toolchain/include/compiler/detail/cxx/unit.h +++ b/hilti/toolchain/include/compiler/detail/cxx/unit.h @@ -105,7 +105,7 @@ class Unit { Result linkerMetaData() const; // only after finalize cxx::ID cxxNamespace() const; - std::shared_ptr context() const { return _context; } + std::shared_ptr context() const { return _context.lock(); } static std::pair> readLinkerMetaData(std::istream& input); @@ -119,7 +119,7 @@ class Unit { void _addHeader(Formatter& f); void _addModuleInitFunction(); - std::shared_ptr _context; + std::weak_ptr _context; cxx::ID _module_id; hilti::rt::filesystem::path _module_path; diff --git a/hilti/toolchain/include/compiler/detail/parser/driver.h b/hilti/toolchain/include/compiler/detail/parser/driver.h index 569d42dac..9497ffa5e 100644 --- a/hilti/toolchain/include/compiler/detail/parser/driver.h +++ b/hilti/toolchain/include/compiler/detail/parser/driver.h @@ -71,19 +71,17 @@ struct yystype_hilti { std::vector switch_cases; std::vector try_catches; - std::pair tuple_type_elem; - std::vector> tuple_type_elems; + hilti::type::tuple::Element tuple_type_elem; + std::vector tuple_type_elems; - hilti::type::struct_::Field struct_field; hilti::ctor::struct_::Field struct_elem; - std::vector struct_fields; std::vector struct_elems; - hilti::type::union_::Field union_field; - std::vector union_fields; + hilti::declaration::Field union_field; + std::vector union_fields; - hilti::ctor::Map::Element map_elem; - std::vector map_elems; + hilti::ctor::map::Element map_elem; + std::vector map_elems; hilti::type::enum_::Label enum_label; std::vector enum_labels; diff --git a/hilti/toolchain/include/compiler/detail/visitors.h b/hilti/toolchain/include/compiler/detail/visitors.h index 9be64da51..d90ea99eb 100644 --- a/hilti/toolchain/include/compiler/detail/visitors.h +++ b/hilti/toolchain/include/compiler/detail/visitors.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include #include @@ -26,8 +27,8 @@ class Stream; namespace detail { -/**Performs imports for an AST. */ -std::set importModules(const Node& root, Unit* unit); +/** Performs imports for an AST. */ +std::set importModules(const Node& root, Unit* unit); /** * Prints an AST as HILTI source code. This consults any installed plugin @@ -57,26 +58,17 @@ std::string renderOperatorInstance(const expression::ResolvedOperator& o); void renderNode(const Node& n, std::ostream& out, bool include_scopes = false); void renderNode(const Node& n, logging::DebugStream stream, bool include_scopes = false); -/** - * Clears any errors currentluy set in an AST. - */ -void clearErrors(Node* root); - -/** Implements the corresponding functionality for the default HILTI compiler plugin. */ -void buildScopes(const std::vector>& modules, Unit* unit); +namespace ast { /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -bool resolveIDs(Node* root, Unit* unit); +void buildScopes(const std::shared_ptr& context, Node* root, Unit* unit); /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -bool resolveOperators(Node* root, Unit* unit); +bool normalize(Node* root, Unit* unit); /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -std::optional coerceCtor(Ctor c, const Type& dst, bitmask style); +bool coerce(Node* root, Unit* unit); /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -std::optional coerceType(Type t, const Type& dst, bitmask style); +bool resolve(const std::shared_ptr& ctx, Node* root, Unit* unit); /** Implements the corresponding functionality for the default HILTI compiler plugin. */ -bool applyCoercions(Node* root, Unit* unit); -/** Implements the corresponding functionality for the default HILTI compiler plugin. */ -void validateAST(Node* root); - - +void validate(Node* root); +} // namespace ast } // namespace detail } // namespace hilti diff --git a/hilti/toolchain/include/compiler/driver.h b/hilti/toolchain/include/compiler/driver.h index daa332699..e20a89b92 100644 --- a/hilti/toolchain/include/compiler/driver.h +++ b/hilti/toolchain/include/compiler/driver.h @@ -1,4 +1,5 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. +// p #pragma once @@ -117,29 +118,28 @@ class Driver { Result parseOptions(int argc, char** argv); /** - * Schedules a HILTI module for compilation. The unit will take ownership - * and compile the module once `compile()` is called. If module of the same ID or - * path has been added previously, this will have no effect. + * Schedules a unit for compilation. The driver will compile the unit once + * `compile()` is called. If a module of the same ID or path has been added + * previously, this will have no further effect. * - * `hookNewASTPreCompilation()` hook will be called immediately for the - * new module. + * The `hookAddInput()` and `hookNewASTPreCompilation()` hooks will be + * called immediately for the new module. * - * @param m HILTI module to schedule for compilation - * @param path path associated with the module, if any + * @param u unit to schedule for compilation * @return set if successful; otherwise the result provides an error message */ - Result addInput(hilti::Module&& m, const hilti::rt::filesystem::path& path = ""); + Result addInput(std::shared_ptr u); /** - * Schedules a HILTI source file for compilation. The file will be parsed + * Schedules a source file for compilation. The file will be parsed * immediately, and then compiled later when `compile()` is called. If the - * same file/module has been added previously, this method will have no + * same source unit has been added previously, this method will have no * effect. * - * `hookNewASTPreCompilation()` hook will be called immediately for the - * new module after it has been parsed. + * The `hookAddInput()` and `hookNewASTPreCompilation()` hooks will be + * called immediately for the new module. * - * @param input source of HILTI module to compile + * @param path source code to compile * @return set if successful; otherwise the result provides an error message */ Result addInput(const hilti::rt::filesystem::path& path); @@ -187,16 +187,14 @@ class Driver { */ Result compile(); - /** - * Performs global transformations on the generated code. - */ - Result transformUnits(); - /** * Returns the current HILTI context. Valid only once compilation has * started, otherwise null. */ - auto context() const { return _ctx; } + const auto& context() const { return _ctx; } + + /** Shortcut to return the current context's options. */ + const auto& options() const { return _ctx->options(); } /** * Initializes HILTI's runtime system to prepare for execution of @@ -372,42 +370,43 @@ class Driver { virtual std::string hookAugmentUsage() { return ""; } /** - * Hook for derived classes to execute custom code when a new source path + * Hook for derived classes to execute custom code when a new unit * is being added as an input file. */ - virtual void hookAddInput(const hilti::rt::filesystem::path& path) {} + virtual void hookAddInput(std::shared_ptr unit) {} /** - * Hook for derived classes to execute custom code when a new AST module - * is being added as an input file. + * Hook for derived classes to execute custom code when a new source code + * file is being added as an input file. */ - virtual void hookAddInput(const hilti::Module& m, const hilti::rt::filesystem::path& path) {} + virtual void hookAddInput(const hilti::rt::filesystem::path& path) {} /** * Hook for derived classes to execute custom code when an HILTI AST has * been loaded. This hook will run before the AST has been compiled (and * hence it'll be fully unprocessed). */ - virtual void hookNewASTPreCompilation(const ID& name, const std::optional& path, - const Node& root) {} + virtual void hookNewASTPreCompilation(std::shared_ptr unit) {} /** - * Hook for derived classes to execute custom code when a HILTI AST has - * been finalized. This hook will run after the AST has been compiled - * (and hence it'll be fully processed). + * Hook for derived classes to execute custom code when a code unit has + * been finalized. This hook will run after the AST has been be fully + * processed by the suitable plugin, but before it's being transformed. */ - virtual void hookNewASTPostCompilation(const ID& name, const std::optional& path, - const Node& root) {} + virtual void hookNewASTPostCompilation(std::shared_ptr unit) {} /** * Hook for derived classes to execute custom code when all input files - * have been compiled to HILTI & Spicy code (but not yet linked). If the - * hook return an error that will abort all further processing. The hook - * may add further inputs files through the `add()` methods, which will - * then be compiled next. If so, this hook will execute again once all - * new inputs have likewise been compiled. + * have been fully processes by a plugin. If the hook returns an error that + * will abort all further processing. The hook may add further inputs files + * through the `add()` methods, which will then be compiled next. This + * hook will execute again once all new inputs have likewise been compiled. + * The new files, however, must not need processing by any plugin that has + * already completed compilation previously. + * + * @param plugin plugin that has finished now */ - virtual Result hookCompilationFinished() { return Nothing(); } + virtual Result hookCompilationFinished(const Plugin& plugin) { return Nothing(); } /** * Hook for derived classes to execute custom code when the HILTI runtime @@ -426,8 +425,44 @@ class Driver { // operation. enum Stage { UNINITIALIZED, INITIALIZED, COMPILED, CODEGENED, LINKED, JITTED } _stage = UNINITIALIZED; - void _addUnit(Unit unit); - Result _compileUnit(Unit unit); + // Backend for adding a new unit. + void _addUnit(std::shared_ptr unit); + + // Run a specific plugini's AST passes on all units with the corresponding extension. + Result _resolveUnitsWithPlugin(const Plugin& plugin, std::vector> units, int& round); + + // Runs a specific plugin's transform step on a given set of units. + Result _transformUnitsWithPlugin(const Plugin& plugin, std::vector> units); + + // Run all plugins on current units, iterating until finished. + Result _resolveUnits(); + + // Turns all HILTI units into C++. + Result _codegenUnits(); + + // Performs global transformations on the generated code. + Result _optimizeUnits(); + + // Sends a debug dump of a unit's AST to the global logger. + void _dumpAST(std::shared_ptr unit, const logging::DebugStream& stream, const Plugin& plugin, + const std::string& prefix, int round); + + // Sends a debug dump of a unit's AST to the global logger. + void _dumpAST(std::shared_ptr unit, const logging::DebugStream& stream, const std::string& prefix); + + // Sends a debug dump of a unit's AST to an output stream. + void _dumpAST(std::shared_ptr unit, std::ostream& stream, const Plugin& plugin, const std::string& prefix, + int round); + + // Records a reduced debug dump of a unit's AST limited to just declarations. + void _dumpDeclarations(std::shared_ptr unit, const Plugin& plugin); + + // Records a debug dump of a unit's AST to disk. + void _saveIterationAST(std::shared_ptr unit, const Plugin& plugin, const std::string& prefix, int round); + + // Records a debug dump of a unit's AST to disk. + void _saveIterationAST(std::shared_ptr unit, const Plugin& plugin, const std::string& prefix, + std::string tag); /** * Look up a symbol in the global namespace. @@ -441,9 +476,8 @@ class Driver { driver::Options _driver_options; hilti::Options _compiler_options; - std::vector _pending_units; - - std::set _processed_units; + std::vector> _pending_units; + std::set _processed_units; std::set _processed_paths; std::shared_ptr _ctx; // driver's compiler context @@ -454,7 +488,7 @@ class Driver { std::unordered_map _libraries; std::vector _external_cxxs; std::vector _mds; - std::vector _hlts; + std::vector> _hlts; bool _runtime_initialized = false; // true once initRuntime() has succeeded std::set _tmp_files; // all tmp files created, so that we can clean them up. diff --git a/hilti/toolchain/include/compiler/jit.h b/hilti/toolchain/include/compiler/jit.h index e2a0e6593..ffe5ece27 100644 --- a/hilti/toolchain/include/compiler/jit.h +++ b/hilti/toolchain/include/compiler/jit.h @@ -160,10 +160,10 @@ class JIT { Result> build(); /** Returns the compiler context in use. */ - auto context() const { return _context; } + auto context() const { return _context.lock(); } /** Returns the compiler options in use. */ - auto options() const { return _context->options(); } + auto options() const { return context()->options(); } private: // Check if we have a working compiler. @@ -187,8 +187,8 @@ class JIT { hilti::rt::filesystem::path _makeTmp(std::string base, std::string ext); - std::shared_ptr _context; // global context for options - bool _dump_code; // save all C++ code for debugging + std::weak_ptr _context; // global context for options + bool _dump_code; // save all C++ code for debugging std::vector _files; // all added source files std::vector _codes; // all C++ code units to be compiled diff --git a/hilti/toolchain/include/compiler/optimizer.h b/hilti/toolchain/include/compiler/optimizer.h index 701db0f39..062a9add6 100644 --- a/hilti/toolchain/include/compiler/optimizer.h +++ b/hilti/toolchain/include/compiler/optimizer.h @@ -13,14 +13,17 @@ namespace hilti { struct Optimizer { public: - Optimizer(std::vector* units, const std::shared_ptr ctx) : _units(units), _ctx(std::move(ctx)) {} - ~Optimizer() { _units = nullptr; } + Optimizer(const std::vector>& units, const std::shared_ptr ctx) + : _units(units), _ctx(std::move(ctx)) {} + ~Optimizer() {} void run(); + auto context() const { return _ctx.lock(); } + private: - std::vector* _units = nullptr; - std::shared_ptr _ctx; + const std::vector>& _units; + std::weak_ptr _ctx; }; } // namespace hilti diff --git a/hilti/toolchain/include/compiler/plugin.h b/hilti/toolchain/include/compiler/plugin.h index d2fcf07ea..8a193c5a2 100644 --- a/hilti/toolchain/include/compiler/plugin.h +++ b/hilti/toolchain/include/compiler/plugin.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include #include @@ -31,22 +32,22 @@ class Stream; } // namespace printer /** - * Compiler plugin that can hook into the compilation process that's driven - * by `Unit`. + * Compiler plugin that implements AST-to-AST translation through a set of + * passes. * - * A plugin gets access to the AST at all major stages. In particular it can - * add support implement support for new language using HILTI as its code - * generation backend by providing a parse method building its AST, along - * with a transformation method converting any non-standard nodes HILTI - * equivalents. + * The HILTI compiler itself is the one plugin that's always available. On top + * of that, further plugins may implement passes as needed to preprocess an AST + * before it gets to the HILTI plugin. That way, an external plugin can + * implement support for new language using HILTI as its codegen backend by (1) + * reading its representation into an AST using its own set of nodes (which may + * include reusing existing HILTI AST nodes where convinient), (2) implementing + * the resolution passes to fully resolve that AST (reusing HILTI passes + * internally where convenient), and (3) finally transforming that AST into a + * pure HILTI AST consisting only of the HILT nodes. * - * A plugin implements a set of hook methods that get called by the - * compilation process at the appropriate times. All hooks should be - * stateless, apart from changing the AST where appropriate. - * - * @note HILTI compilation itself is also implemented through a default - * plugin that's always available. `Unit` cycles through all available - * plugins during the compilation process, including that default plugin. + * A plugin implements a set of hook methods that get called by the compilation + * process at the appropriate times. All hooks should be stateless, apart from + * changing the AST as appropriate. */ struct Plugin { /** Helper template to define the type of hook methods. */ @@ -56,6 +57,12 @@ struct Plugin { /** Name of the plugin. */ std::string component; + /** + * Plugins will be executed in numerical order, with lower order numbers + * executing first. + */ + int order = 0; + /** Extension for source files that the plugin handles. Must include the leading `.`. */ hilti::rt::filesystem::path extension; @@ -65,12 +72,6 @@ struct Plugin { */ std::vector cxx_includes; - /** - * Callbacks for plugins will be executed in numerical order, with lower - * order numbers executing first. - */ - int order = 0; - /** * Hook called to retrieve paths to search when importing modules that * this plugin handles. @@ -129,94 +130,69 @@ struct Plugin { * @param arg3 current unit being compiled * @return true if the hook modified the AST in a substantial way */ - Hook, const std::vector>&, Unit*> build_scopes; + Hook, Node*, Unit*> ast_build_scopes; /** - * Hook called to resolved IDs in a module's AST. + * Hook called to prepare an AST before any further stages execute. * * @param arg1 compiler context that's in use * @param arg2 root node of AST; the hook may modify the AST * @param arg3 current unit being compiled * @return true if the hook modified the AST in a substantial way */ - Hook, Node*, Unit*> resolve_ids; + Hook, Node*, Unit*> ast_normalize; /** - * Hook called to resolved operators in a module's AST. + * Hook called to apply type coersions to the AST. * * @param arg1 compiler context that's in use * @param arg2 root node of AST; the hook may modify the AST * @param arg3 current unit being compiled * @return true if the hook modified the AST in a substantial way */ - Hook, Node*, Unit*> resolve_operators; + Hook, Node*, Unit*> ast_coerce; /** - * Hook called perform coercions. This must carry out all the coercions - * that `coerce_type` has indicated as valid. + * Hook called to resolve unknown types and other entities. * * @param arg1 compiler context that's in use * @param arg2 root node of AST; the hook may modify the AST * @param arg3 current unit being compiled * @return true if the hook modified the AST in a substantial way */ - Hook, Node*, Unit*> apply_coercions; + Hook, Node*, Unit*> ast_resolve; /** - * Hook called to validate correctness of an AST, pre-transformation. Any + * Hook called to validate correctness of an AST once fully resolved. Any * errors must be reported by setting the nodes' error information. * * @param arg1 compiler context that's in use * @param arg2 root node of AST; the hook may not modify the AST * @param arg3 current unit being compiled - * @param arg4 pointer to boolean that the hook must set to true to - * indicate that errors were encountered. - */ - Hook, Node*, Unit*, bool*> pre_validate; - - /** - * Hook called to validate correctness of an AST, post-transformation. - * Any errors must be reported by setting the nodes' error information. - * - * @param arg1 compiler context that's in use - * @param arg2 root node of AST; the hook may not modify the AST - * @param arg3 current unit being compiled */ - Hook, Node*, Unit*> post_validate; + Hook, Node*, Unit*> ast_validate; /** - * Hook called to validate correctness of AST nodes that a module - * preserved before transformation. The hook runs just before the - * ``post_validate`` hook. Any errors must be reported by setting the - * nodes' error information. + * Hook called to print an AST back as source code. The hook gets to choose + * if it wants to print the node itself, or fall back to the default + * printer. * - * @param arg1 compiler context that's in use - * @param arg2 preserved nodes to validate - * @param arg3 current unit being compiled + * @param arg1 root of AST to print + * @param arg2 stream to print to + * @return true if the hook printed the AST, false to fall back to default */ - Hook, std::vector*, Unit*> preserved_validate; + Hook ast_print; /** - * Hook called to replace any custom AST nodes with standard HILTI - * nodes. Note that this may be called multiple times while ASTs are built. + * Hook called to replace AST nodes of one language (plugin) with nodes + * of another coming further down in the pipeline. * * @param arg1 compiler context that's in use * @param arg2 root node of AST; the hook may modify the AST - * @param arg3 boolean that's true if this hook runs for the first time on this AST. - * @param arg4 current unit being compiled + * @param arg3 current unit being compiled * @return true if the hook modified the AST in a substantial way */ - Hook, Node*, bool, Unit*> transform; - - /** - * Hook called to print an AST back as source code. The hook gets to - * choose if it wants to print the node itself, or fall back to the default printer. - * - * @param arg1 root of AST to print - * @param arg2 stream to print to - * @return true if the hook printed the AST, false to fall back to default - */ - Hook print_ast; + Hook, Node*, Unit*> ast_transform; }; class PluginRegistry; @@ -247,7 +223,13 @@ class PluginRegistry { * @param ext extension, including the leading `.` * @return plugin if any has been register for the extension */ - Result pluginForExtension(hilti::rt::filesystem::path ext) const; + Result> pluginForExtension(hilti::rt::filesystem::path ext) const; + + /** + * Shortcut to return the HILTI plugin. This must have been registered + * already when called. + */ + const Plugin& hiltiPlugin() const; /** * Checks if at least one plugin implements a given hook. diff --git a/hilti/toolchain/include/compiler/printer.h b/hilti/toolchain/include/compiler/printer.h index d44a5f700..4b05c41f9 100644 --- a/hilti/toolchain/include/compiler/printer.h +++ b/hilti/toolchain/include/compiler/printer.h @@ -16,13 +16,22 @@ class Stream { public: Stream(std::ostream& s, bool _compact) : _stream(s), _compact(_compact), _nl(_compact ? ' ' : '\n') {} - void beginLine() { _stream << std::string(_indent * 4, ' '); } - void endLine() { _stream << (_compact ? ' ' : '\n'); } + void beginLine() { + _flush_pending(); + _stream << std::string(_indent * 4, ' '); + } + void endLine() { + if ( _compact ) + _pending = ' '; + else + _stream << '\n'; + } + void emptyLine() { if ( _wrote_nl ) return; - _stream << (_compact ? ' ' : '\n'); + endLine(); _wrote_nl = true; } @@ -58,6 +67,7 @@ class Stream { template Stream& operator<<(const T& t) { + _flush_pending(); if constexpr ( std::is_base_of::value ) { if ( auto id = Type(t).typeID() ) _stream << *id; @@ -71,6 +81,7 @@ class Stream { template Stream& operator<<(const T& t) { _wrote_nl = false; + _flush_pending(); _stream << t; _expand_subsequent_type = false; return *this; @@ -81,6 +92,8 @@ class Stream { Stream& operator<<(std::pair p) { bool first = true; for ( auto& i : p.first ) { + _flush_pending(); + if ( ! first ) _stream << p.second; @@ -92,9 +105,15 @@ class Stream { } private: + void _flush_pending() { + _stream << _pending; + _pending.clear(); + } + std::ostream& _stream; bool _compact; char _nl; + std::string _pending; int _indent = 0; bool _wrote_nl = false; bool _first_in_block = false; diff --git a/hilti/toolchain/include/compiler/unit.h b/hilti/toolchain/include/compiler/unit.h index c12a77f9e..a03470c21 100644 --- a/hilti/toolchain/include/compiler/unit.h +++ b/hilti/toolchain/include/compiler/unit.h @@ -2,9 +2,10 @@ #pragma once +#include + #include #include -#include #include #include #include @@ -21,6 +22,9 @@ #include namespace hilti { + +struct Plugin; + namespace linker { /** * Linker meta data associated with a HILTI unit. When HILTI compiles a @@ -36,37 +40,100 @@ using MetaData = detail::cxx::linker::MetaData; /** * Container for a single HILTI code module. For each HILTI source file, one * compiler unit gets instantiated. That unit then drives the process to - * compile module comp into C++ code. While that's in progress, the unit - * maintains state about the process, such as a cache of all external modules - * imported by the one that's being compiled. + * compile the module AST into C++ code. While that's in progress, the unit + * maintains state about the process, such as a list of dependencies this unit + * requires. */ class Unit { public: - /** Returns the root node of the module AST's. */ - NodeRef module() { - assert(_id); - return NodeRef(imported(_id)); + /** Destructor. */ + ~Unit(); + + /** Returns a reference to the root node of the module AST's. */ + NodeRef moduleRef() const { return _module ? NodeRef(*_module) : NodeRef(); } + + /** + * Returns the root node of the module AST's. Must only be called if + * `isCompiledHilti()` returns true. + */ + Node& module() { + assert(_module); + return *_module; } /** - * Returns the ID of the unit's top-level module (i.e., the one being - * compiled). + * Returns the index to use when storing the unit inside the context's + * unit cache. */ - auto id() const { return _id; } + const auto& cacheIndex() { return _index; } + + /** Returns the ID of the unit's module. */ + const auto& id() const { return _index.id; } + + /** Returns the path associated with the unit's module. */ + const auto& path() const { return _index.path; } /** - * Returns the path associated with the unit's top-level module (i.e., - * the one being compiled). + * Returns the file extension associated with the unit's code. This + * extension determines which plugin the unit's AST will be processed with. + * Note that this does not necessarily match the extension of module's + * source path. */ - auto path() const { return _path; } + const auto& extension() const { return _extension; } /** - * Compiles the unit's module AST into its final internal representation. - * @return set if successful, or an appropriate error result + * Set a file extension associagted with the unit's code. By default, the + * extension is set when a AST module is being created, for example from + * the file it's being parsed from. This method can explicitly override the + * extension to have the AST processed by a different plugin. + * + * @param ext new extension */ - Result compile(); + void setExtension(const hilti::rt::filesystem::path& ext) { _extension = ext; } + + enum ASTState { Modified, NotModified }; + + /** Clears any scopes and errors accumulated inside the unit's AST. */ + void resetAST(); - /** Triggers generation of C++ code from the compiled AST. */ + /** + * Runs a plugin's scope-building phase on the unit's AST. + * + * @param plugin plugin to execute + * @returns success if no error occurred, and an appropiate error otherwise + */ + Result buildASTScopes(const Plugin& plugin); + + /** + * Runs a plugin's resolver phase on the unit's AST. + * + * @param plugin plugin to execute + * @returns flag indicating whether the AST was modified or not; or an + * appropiate error if a failure occurred + */ + Result resolveAST(const Plugin& plugin); + + /** + * Runs a plugin's validation phase on the unit's AST. + * + * @param plugin plugin to execute + * @returns true if the AST does not contain any errors + */ + bool validateAST(const Plugin& plugin); + + /** + * Runs a plugin's tranformation phase on the unit's AST. + * + * @param plugin plugin to execute + * @returns success if no error occurred, and an appropiate error otherwise + */ + Result transformAST(const Plugin& plugin); + + /** + * Triggers generation of C++ code from the compiled AST. + * + * @returns success if no error occurred, and an appropiate error otherwise + */ Result codegen(); /** @@ -97,72 +164,19 @@ class Unit { */ Result cxxCode() const; - /** - * Makes an external HILTI module available to the one this unit is - * compiling. Essentially, this implements the HITLI's `import` - * statement. Importing another module means that the compiled module - * will know how to access the other one's functionality. However, that - * external module will still need to be compiled itself as well; and - * then all the compiled modules need to be linked together. - * - * This version of the `import` method imports by module ID: it searches - * `Configuration::hilti_library_paths` for a file with a corresponding - * name and extension. - * - * @param id ID of module to import - * @param ext file name extension to search; `.hlt` is HILTI's standard extension, but plugins can add support for - * other extensions as well - * @param scope if given, qualifies the ID with a path prefix to find the - * module (e.g., a scope of `a.b.c` will search the module in `/a/b/c/`.) - * @param dirs additional directories to search first - * @return the modules' cache index if successful,or an appropriate error result if not - */ - Result import(const ID& id, const hilti::rt::filesystem::path& ext, - std::optional scope = {}, - std::vector search_dirs = {}); + /** Returns the list of dependencies registered for the unit so far. */ + const auto& dependencies() { return _dependencies; } - /** - * Makes an external HILTI module available to the one this unit is - * compiling. See `import(const ID, const hilti::rt::filesystem::path)` for - * more details on importing. - * - * This version of the `import` method imports directly from a given - * file. - * - * @param path to the file to import - * @return the modules' cache index if successful,or an appropriate error result if not - */ - Result import(const hilti::rt::filesystem::path& path); - - /** - * Returns the AST for an imported module. - * - * @param id module ID - * @return Reference to the root node of the imported module's AST - * @exception `std::out_of_range` if no module of that name has been imported yet - */ - Node& imported(const ID& id) const; - - /** - * Returns set of all imported modules so far. - * - * @param code_only if true include only dependencies that require independent compilation themselves - */ - std::set allImported(bool code_only = false) const; + /** Removes any dependencies registered for the unit so far. */ + void clearDependencies() { _dependencies.clear(); }; /** - * Returns true if an imported module provides code that needs - * independent compilation to resolve references at link-time. + * Register a dependency on another unit for the current one. * - * @return a boolean that true if the module provides code for - * compilation, or an error value if no such module is known. + * @param unit the unit this one depends on + * @returns true if this is a new dependency that had not been previously added */ - Result requiresCompilation(const ID& id) { - if ( auto x = _lookupModule(id) ) - return x->requires_compilation; - - return result::Error("unknown module"); - } + bool addDependency(std::shared_ptr unit); /** * Returns the unit's meta data for the internal HILTI linker. @@ -181,40 +195,87 @@ class Unit { * usually the case, but we also represent HILTI's linker output as a * unit and there's no corresponding HILTI source code for that. */ - bool isCompiledHILTI() const { return _have_hilti_ast; } + bool isCompiledHILTI() const { return _module.has_value(); } + + /** + * Returns true if the AST has been determined to contain code that needs + * to be compiled as its own C++ module, rather than just declaration for + * other units. + */ + bool requiresCompilation(); + + /** + * Returns true if the unit has been marked as fully resolved, so that no further AST processing is needed. + */ + bool isResolved() { return _resolved; } + + /** + * Sets the resolver state for the unit. + * + * @param resolved true to mark the module as fully resolved, so that no + * further AST processing is needed + */ + void setResolved(bool resolved) { _resolved = resolved; } /** Returns the compiler context in use. */ - std::shared_ptr context() const { return _context; } + std::shared_ptr context() const { return _context.lock(); } /** Returns the compiler options in use. */ - const Options& options() const { return _context->options(); } + const Options& options() const { return context()->options(); } /** - * Factory method that instantiates a unit from an existing HILTI module - * that's be compiled. + * Factory method that instantiates a unit from an existing source file + * that it will parse. * * This method also caches the module with the global context. Note that - * the module's ID or path must not exist with the context yet. + * the module's ID or path should usually not exist with the context yet. * * @param context global compiler context - * @param module HILTI module to compile, of which the unit will take ownership - * @param path path associated with the module, if any + * @param path path to parse the module from + * @param ast_extension extension indicating which plugin to use for + * processing the AST; if not given, this will be taken from the filename * @return instantiated unit, or an appropriate error result if operation failed */ - static Result fromModule(const std::shared_ptr& context, hilti::Module&& module, - const hilti::rt::filesystem::path& path = ""); + static Result> fromSource(const std::shared_ptr& context, + const hilti::rt::filesystem::path& path, + std::optional ast_extension = {}); /** - * Factory method that instantiates a unit from an existing source file that it will parse. + * Factory method that instantiates a unit from an existing HILTI AST. * * This method also caches the module with the global context. Note that - * the module's ID or path must not exist with the context yet. + * the module's ID or path should usually not exist with the context yet. + * If it does, this one will replace the existing version. * * @param context global compiler context - * @param path path to parse the module from + * @param module AST of the module + * @param extension extension indicating which plugin to use for + * processing the AST + * @return instantiated unit, or an appropriate error result if operation failed + */ + static std::shared_ptr fromModule(const std::shared_ptr& context, hilti::Module module, + hilti::rt::filesystem::path extension); + + /** + * Factory method that instantiates a unit for an `import` statement, + * performing the search for the right source file first. + * + * This method also caches the module with the global context. Note that + * the module's ID or path should usually not exist with the context yet. + * + * @param context global compiler context + * @param id ID of the module to be imported + * @param parse_extension file extension indicating how to parse the module's source file + * @param ast_extension extension indicating which plugin to use for + * processing the AST; this will usually match `parse_extesion`, but + * doesn't need to. * @return instantiated unit, or an appropriate error result if operation failed */ - static Result fromSource(const std::shared_ptr& context, const hilti::rt::filesystem::path& path); + static Result> fromImport(const std::shared_ptr& context, const ID& id, + const hilti::rt::filesystem::path& parse_extension, + const hilti::rt::filesystem::path& ast_extension, + std::optional scope, + std::vector search_dirs); /** * Factory method that instantiates a unit from an existing HILTI module @@ -222,9 +283,11 @@ class Unit { * * @param context global compiler context * @param id ID of the cached module + * @param extension file extension to limit cache hits to * @return instantiated unit, or an appropriate error result if operation failed */ - static Result fromCache(const std::shared_ptr& context, const hilti::ID& id); + static Result> fromCache(const std::shared_ptr& context, const hilti::ID& id, + const hilti::rt::filesystem::path& extension); /** * Factory method that instantiates a unit from an existing HILTI module @@ -234,17 +297,19 @@ class Unit { * @param path path of the cached module * @return instantiated unit, or an appropriate error result if operation failed */ - static Result fromCache(const std::shared_ptr& context, const hilti::rt::filesystem::path& path); + static Result> fromCache(const std::shared_ptr& context, + const hilti::rt::filesystem::path& path); /** - * Factory method that instantiates a unit from existing C++ source code that's to compiled. + * Factory method that instantiates a unit from existing C++ source code + * that's to compiled. * * @param context global compiler context * @param path path associated with the C++ code, if any * @return instantiated unit, or an appropriate error result if operation failed */ - static Result fromCXX(std::shared_ptr context, detail::cxx::Unit cxx, - const hilti::rt::filesystem::path& path = ""); + static Result> fromCXX(std::shared_ptr context, detail::cxx::Unit cxx, + const hilti::rt::filesystem::path& path = ""); /** * Entry point for the HILTI linker, The linker combines meta data from @@ -256,7 +321,8 @@ class Unit { * @param mds set of meta data from modules to be linked together * @return a unit representing additional C++ code that the modules need to function */ - static Result link(const std::shared_ptr& context, const std::vector& mds); + static Result> link(const std::shared_ptr& context, + const std::vector& mds); /** * Reads linker meta from a file. This expects the file to contain linker @@ -278,60 +344,44 @@ class Unit { std::istream& input, const hilti::rt::filesystem::path& path = ""); private: - // Private constructor initializing the unit's meta data. Note that the - // corresponding module must then be imported into the unit as well. - // Normally you'd use the static ``Unit::from*()`` functions to do that - // while creating a unit. - Unit(std::shared_ptr context, ID id, hilti::rt::filesystem::path path, bool have_hilti_ast) - : _context(std::move(context)), _id(std::move(id)), _path(std::move(path)), _have_hilti_ast(have_hilti_ast) {} + // Private constructor initializing the unit's meta data. Use the public + // `from*()` factory functions instead to instantiate a unit. + Unit(std::shared_ptr context, ID id, hilti::rt::filesystem::path path, + hilti::rt::filesystem::path extension, Node&& module) + : _index(id, util::normalizePath(path)), + _extension(extension), + _module(std::move(module)), + _context(std::move(context)) {} + + Unit(std::shared_ptr context, ID id, hilti::rt::filesystem::path path, + hilti::rt::filesystem::path extension, std::optional cxx_unit = {}) + : _index(id, util::normalizePath(path)), + _extension(extension), + _context(std::move(context)), + _cxx_unit(std::move(cxx_unit)) {} - // Returns a list of all currently known/imported modules. - std::vector> _currentModules() const; + // Backend for the public import() methods. + Result _import(const hilti::rt::filesystem::path& path, std::optional expected_name); - // Looks up a module by its ID. The module must have been imported into - // the unit to succeed. Assuming so, it returns the context's cache entry - // for the module. - std::optional _lookupModule(const ID& id) const; + // Reports any errors recorded in the AST to stderr. + // + // @returns false if there were errors, true if the AST is all good + bool _collectErrors(); - // Backend for the public import() methods. - Result _import(const hilti::rt::filesystem::path& path, std::optional expected_name); - - // Runs a validation pass on a set of modules and reports any errors. - bool _validateASTs(std::vector>& modules, - const std::function& run_hooks_callback); - - // Runs a validation pass on a module and reports any errors. - bool _validateAST(const ID& id, NodeRef module, const std::function& run_hooks_callback); - - // Runs a validation pass on a set of nodes and reports any errors. - bool _validateASTs(const ID& id, std::vector& nodes, - const std::function&)>& run_hooks_callback); - // Updates the requires_compilation flags for all of a module's imports. - void _determineCompilationRequirements(const Node& module); - // Sends a debug dump of a module's AST to the global logger. - void _dumpAST(const Node& module, const logging::DebugStream& stream, const std::string& prefix, int round = 0); - // Sends a debug dump of all modules parsed so far to the global logger. - void _dumpASTs(const logging::DebugStream& stream, const std::string& prefix, int round = 0); - // Sends a debug dump of a module's AST to an output stream. - void _dumpAST(const Node& module, std::ostream& stream, const std::string& prefix, int round = 0); - // Sends a debug dump of all modules parsed so far to an output stream. - void _dumpASTs(std::ostream& stream, const std::string& prefix, int round = 0); - // Records a debug dump of all modules parsed so far to disk. - void _saveIterationASTs(const std::string& prefix, int round = 0); - // Clear any previously computed AST state before beginning a pass over the AST. - void _resetNodes(const ID& id, Node* root); + // Recursively destroys the module's AST. + void _destroyModule(); // Parses a source file with the appropriate plugin. - static Result parse(const std::shared_ptr& context, - const hilti::rt::filesystem::path& path); - - std::shared_ptr _context; // global context - ID _id; // ID of top-level module being compiled by this unit - hilti::rt::filesystem::path _path; // path of top-level module being compiled by this unit - bool _have_hilti_ast; // true if the top-level module comes with a HILTI AST (normally the case, but not for the - // linker's C++ code) - std::set _modules; // set of all module ASTs this unit has parsed and processed (inc. imported ones) - std::optional _cxx_unit; // compiled C++ code for this unit, once available + static Result _parse(const std::shared_ptr& context, + const hilti::rt::filesystem::path& path); + + context::CacheIndex _index; // index for the context's module cache + hilti::rt::filesystem::path _extension; // AST extension, which may differ from source file + std::optional _module; // root node for AST (always a `Module`), if available + std::vector> _dependencies; // recorded dependencies + std::weak_ptr _context; // global context + std::optional _cxx_unit; // compiled C++ code for this unit, once available + bool _resolved = false; // state of resolving the AST }; } // namespace hilti diff --git a/hilti/toolchain/include/global.h b/hilti/toolchain/include/global.h index c909f08e6..d89f440da 100644 --- a/hilti/toolchain/include/global.h +++ b/hilti/toolchain/include/global.h @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/hilti/toolchain/src/ast/builder/builder.cc b/hilti/toolchain/src/ast/builder/builder.cc index 5fca2cbc0..8b0da2960 100644 --- a/hilti/toolchain/src/ast/builder/builder.cc +++ b/hilti/toolchain/src/ast/builder/builder.cc @@ -68,7 +68,7 @@ Expression Builder::addTmp(const std::string& prefix, const Type& t, const Expre } void Builder::addDebugMsg(const std::string& stream, const std::string& fmt, std::vector args) { - if ( ! _context->options().debug ) + if ( ! context()->options().debug ) return; Expression call; @@ -88,7 +88,7 @@ void Builder::addDebugMsg(const std::string& stream, const std::string& fmt, std } void Builder::addDebugIndent(const std::string& stream) { - if ( ! _context->options().debug ) + if ( ! context()->options().debug ) return; auto call = builder::call("hilti::debugIndent", {builder::string(stream)}); @@ -96,7 +96,7 @@ void Builder::addDebugIndent(const std::string& stream) { } void Builder::addDebugDedent(const std::string& stream) { - if ( ! _context->options().debug ) + if ( ! context()->options().debug ) return; auto call = builder::call("hilti::debugDedent", {builder::string(stream)}); diff --git a/hilti/toolchain/src/ast/builder/type.cc b/hilti/toolchain/src/ast/builder/type.cc index c705d8a29..ee634dc4b 100644 --- a/hilti/toolchain/src/ast/builder/type.cc +++ b/hilti/toolchain/src/ast/builder/type.cc @@ -1,9 +1,9 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "hilti/ast/builder/type.h" - #include +#include + using namespace hilti; Type hilti::builder::typeByID(::hilti::ID id, Meta m) { return hilti::type::UnresolvedID(std::move(id), std::move(m)); } diff --git a/hilti/toolchain/src/ast/declarations/imported-module.cc b/hilti/toolchain/src/ast/declarations/imported-module.cc new file mode 100644 index 000000000..ccd388ea5 --- /dev/null +++ b/hilti/toolchain/src/ast/declarations/imported-module.cc @@ -0,0 +1,14 @@ +// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace hilti; + +node::Properties declaration::ImportedModule::properties() const { + return node::Properties{{"module", (_unit.lock() ? _unit.lock()->module().renderedRid() : std::string("-"))}, + {"parse_extension", _parse_extension.native()}, + {"path", _path.native()}, + {"scope", (_scope ? _scope->str() : std::string("-"))}}; +} diff --git a/hilti/toolchain/src/ast/expressions/id.cc b/hilti/toolchain/src/ast/expressions/id.cc index cd188f5aa..9aa6a5647 100644 --- a/hilti/toolchain/src/ast/expressions/id.cc +++ b/hilti/toolchain/src/ast/expressions/id.cc @@ -1,5 +1,7 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. +#include + #include #include #include @@ -7,22 +9,22 @@ using namespace hilti; -Type expression::ResolvedID::type() const { - struct Visitor : hilti::visitor::PreOrder { +const Type& expression::ResolvedID::type() const { + struct Visitor : hilti::visitor::PreOrder, Visitor> { result_t operator()(const declaration::Constant& c) { return c.type(); } result_t operator()(const declaration::Expression& e) { return e.expression().type(); } - result_t operator()(const declaration::Forward& f) { return *dispatch(f.callback()()); } + result_t operator()(const declaration::Field& f) { return f.type(); } result_t operator()(const declaration::Function& f) { return f.function().type(); } result_t operator()(const declaration::GlobalVariable& v) { return v.type(); } result_t operator()(const declaration::LocalVariable& v) { return v.type(); } result_t operator()(const declaration::Parameter& p) { return p.type(); } - result_t operator()(const declaration::Type& t) { return type::Type_(t.type(), t.meta()); } + result_t operator()(const declaration::Type& t) { return t.type(); } }; - if ( ! isValid() ) - return type::unknown; + if ( ! declarationRef() ) + return type::auto_; - if ( auto x = Visitor().dispatch(Node(declaration())) ) + if ( const auto& x = Visitor().dispatch(*declarationRef()) ) return *x; logger().internalError(util::fmt("unsupported declaration type %s", declaration().typename_()), *this); @@ -32,14 +34,17 @@ bool expression::ResolvedID::isConstant() const { struct Visitor : hilti::visitor::PreOrder { result_t operator()(const declaration::Constant& c) { return true; } // NOLINT result_t operator()(const declaration::Expression& e) { return e.expression().isConstant(); } - result_t operator()(const declaration::Forward& f) { return *dispatch(f.callback()()); } + result_t operator()(const declaration::Field& f) { return f.isConstant(); } result_t operator()(const declaration::Function& f) { return true; } // NOLINT result_t operator()(const declaration::GlobalVariable& v) { return v.isConstant(); } result_t operator()(const declaration::LocalVariable& v) { return v.isConstant(); } result_t operator()(const declaration::Parameter& p) { return p.isConstant(); } }; - if ( auto x = Visitor().dispatch(declaration()) ) + if ( ! declarationRef() ) + return false; + + if ( const auto& x = Visitor().dispatch(*declarationRef()) ) return *x; logger().internalError(util::fmt("unsupported declaration type %s", declaration().typename_()), *this); diff --git a/hilti/toolchain/src/ast/module.cc b/hilti/toolchain/src/ast/module.cc index f28c04cdb..bfd6d55d2 100644 --- a/hilti/toolchain/src/ast/module.cc +++ b/hilti/toolchain/src/ast/module.cc @@ -19,28 +19,33 @@ void Module::clear() { childs()[1] = statement::Block({}, meta()); } -NodeRef Module::preserve(Node n) { - detail::clearErrors(&n); - _preserved.push_back(std::move(n)); - return NodeRef(_preserved.back()); -} - -Result Module::moduleProperty(const ID& id) const { +hilti::optional_ref Module::moduleProperty(const ID& id) const { for ( const auto& d : declarations() ) { - if ( auto p = d.tryAs(); p && p->id() == id ) - return *p; + if ( ! d.isA() ) + return {}; + + auto& x = d.as(); + if ( x.id() == id ) + return {x}; } - return result::Error("no property of specified id"); + return {}; } -std::vector Module::moduleProperties(const ID& id) const { - std::vector props; +node::Set Module::moduleProperties(const std::optional& id) const { + node::Set props; for ( const auto& d : declarations() ) { - if ( auto p = d.tryAs(); p && p->id() == id ) - props.push_back(*p); + if ( auto p = d.tryAs(); p && (! id || p->id() == id) ) + props.insert(*p); } return props; } + +void Module::destroyPreservedNodes() { + for ( auto& n : _preserved ) + n.destroyChilds(); + + _preserved.clear(); +} diff --git a/hilti/toolchain/src/ast/node.cc b/hilti/toolchain/src/ast/node.cc index 7ed30b9b4..083a0a9da 100644 --- a/hilti/toolchain/src/ast/node.cc +++ b/hilti/toolchain/src/ast/node.cc @@ -1,10 +1,12 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. #include +#include #include #include -#include +#include +#include #include using namespace hilti; @@ -35,17 +37,14 @@ std::string Node::render(bool include_location) const { auto location = (include_location && meta().location()) ? util::fmt(" (%s)", meta().location().render(true)) : ""; auto id = rid() ? util::fmt(" %s", renderedRid()) : ""; - auto orig = originalNode() ? util::fmt(" (original %s)", originalNode()->renderedRid()) : ""; + auto prune = (this->pruneWalk() ? " (prune)" : ""); std::string type; if ( auto x = this->tryAs() ) - type = util::fmt(" (type: %s)", x->type()); + type = util::fmt(" (type: %s [@t:%p])", x->type(), x->type().identity()); - else if ( auto x = this->tryAs() ) - type = util::fmt(" (type: %s)", x->type()); - - auto s = util::fmt("%s%s%s%s%s%s", name, id, orig, sprops, type, location); + auto s = util::fmt("%s%s%s%s%s%s", name, id, sprops, type, prune, location); if ( auto t = this->tryAs() ) { std::vector flags; @@ -68,21 +67,47 @@ std::string Node::render(bool include_location) const { if ( t->isWildcard() ) s += " (wildcard)"; + + s += (type::isResolved(t) ? " (resolved)" : " (not resolved)"); } - else if ( auto e = this->tryAs() ) + else if ( auto e = this->tryAs() ) { s += (e->isConstant() ? " (const)" : " (non-const)"); + s += (type::isResolved(e->type()) ? " (resolved)" : " (not resolved)"); + } + + else if ( auto d = this->tryAs() ) { + s += util::fmt(" [canon-id: %s]", d->canonicalID() ? d->canonicalID().str() : "not set"); + + if ( auto t = this->tryAs() ) + s += (type::isResolved(t->type()) ? " (resolved)" : " (not resolved)"); + } + + s += util::fmt(" [@%s:%p]", util::tolower(name.substr(0, 1)), identity()); // Format errors last on the line since they are not properly delimited. if ( hasErrors() ) - for ( auto&& e : errors() ) - s += util::fmt(" [ERROR] %s%s", e.message, (e.priority == node::ErrorPriority::Low ? " (low prio)" : "")); + for ( auto&& e : errors() ) { + auto prio = ""; + if ( e.priority == node::ErrorPriority::Low ) + prio = " (low prio)"; + else if ( e.priority == node::ErrorPriority::High ) + prio = " (high prio)"; + + s += util::fmt(" [ERROR] %s%s", e.message, prio); + } return s; } void Node::print(std::ostream& out, bool compact) const { detail::printAST(*this, out, compact); } +std::string Node::print() const { + std::stringstream out; + detail::printAST(*this, out, true); + return out.str(); +} + node::Properties operator+(const node::Properties& p1, const node::Properties& p2) { node::Properties p; @@ -94,3 +119,25 @@ node::Properties operator+(const node::Properties& p1, const node::Properties& p return p; } + +void node::detail::flattenedChilds(const hilti::Node& n, node::Set* dst) { + const auto& childs = n.childs(); + for ( auto i = 0u; i < childs.size(); i++ ) { + dst->insert(childs[i]); + flattenedChilds(childs[i], dst); + } +} + +static void _destroyChildsRecursively(Node* n) { + for ( auto& c : n->childs() ) { + if ( ! c.pruneWalk() ) + _destroyChildsRecursively(&c); + } + + n->childs().clear(); +} + +void Node::destroyChilds() { + _destroyChildsRecursively(this); + childs().clear(); +} diff --git a/hilti/toolchain/src/ast/node_ref.cc b/hilti/toolchain/src/ast/node_ref.cc index 4520b3aa9..0f7aabf84 100644 --- a/hilti/toolchain/src/ast/node_ref.cc +++ b/hilti/toolchain/src/ast/node_ref.cc @@ -1,15 +1,15 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. +#include #include -#include using namespace hilti; uint64_t node_ref::detail::Control::_rid_counter = 0; -NodeRef::NodeRef(Node& n) : _control(n._control()) {} +NodeRef::NodeRef(const Node& n) : _control(n._control()) {} -Node* NodeRef::_node() const { +const Node* NodeRef::_node() const { if ( ! _control ) throw node_ref::Invalid("access to uninitialized node reference"); diff --git a/hilti/toolchain/src/ast/scope-lookup.cc b/hilti/toolchain/src/ast/scope-lookup.cc index e587d052c..06e3dcdb3 100644 --- a/hilti/toolchain/src/ast/scope-lookup.cc +++ b/hilti/toolchain/src/ast/scope-lookup.cc @@ -8,10 +8,6 @@ using namespace hilti; -namespace hilti::logging::debug { -inline const DebugStream Resolver("resolver"); -} // namespace hilti::logging::debug - std::pair>> hilti::scope::detail::lookupID(const ID& id, const Node& n) { auto resolved = n.scope()->lookupAll(id); @@ -28,6 +24,12 @@ std::pair>> hilti::scope::detail::lookupID(c return std::make_pair(false, std::move(err)); } + if ( r.node->isA() ) { + // Explict stop of scope traversal. + auto err = result::Error(util::fmt("unknown ID '%s'", id)); + return std::make_pair(true, std::move(err)); + } + if ( auto d = r.node->template tryAs() ) { if ( auto c = d->tryAs() ) { auto err = result::Error(util::fmt("cannot use module '%s' as an ID", id)); @@ -54,13 +56,16 @@ std::pair>> hilti::scope::detail::lookupID(c } } - HILTI_DEBUG(logging::debug::Resolver, util::fmt("resolved ID %s (%s) to %s", id, id.meta().location(), - resolved.front().node->render())); - auto x = std::make_pair(resolved.front().node, ID(resolved.front().qualified)); return std::make_pair(true, std::move(x)); } + if ( r.node->isA() ) { + // Likely a node delete through assignment. + auto err = result::Error("node has been deleted"); + return std::make_pair(false, std::move(err)); + } + logger().internalError(util::fmt("ID '%s' resolved to something else than a declaration (%s)", id, resolved.front().node->typename_()), resolved.front().node->meta().location()); diff --git a/hilti/toolchain/src/ast/scope.cc b/hilti/toolchain/src/ast/scope.cc index 9b4506b13..d848cbbb5 100644 --- a/hilti/toolchain/src/ast/scope.cc +++ b/hilti/toolchain/src/ast/scope.cc @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -10,17 +11,28 @@ using namespace hilti; -void Scope::insert(const ID& id, NodeRef n) { +void Scope::insert(ID id, NodeRef&& n) { + assert(n && n->isA()); + const auto& d = n->as(); auto& nodes = _items[std::string(id)]; + + // Filter out duplicates + for ( const auto& i : nodes ) { + if ( i && i->as() == d ) + return; + } + nodes.push_back(std::move(n)); } -void Scope::insert(const ID& id, Node&& n) { - auto p = std::make_shared(std::move(n)); - _nodes.push_back(p); - insert(id, NodeRef(*_nodes.back())); +void Scope::insert(NodeRef&& n) { + assert(n && n->isA()); + const auto& d = n->as(); + insert(d.id(), std::move(n)); } +void Scope::insertNotFound(ID id) { _items[std::string(id)] = {NodeRef(node::none)}; } + static auto createRefs(const std::vector& refs, const std::string& ns, bool external) { std::vector result; @@ -34,13 +46,12 @@ static auto createRefs(const std::vector& refs, const std::strin return result; } -static auto createRefs(std::vector refs, const std::string& id, bool external) { +static auto createRefs(const std::vector& refs, const std::string& id, bool external) { std::vector result; result.reserve(refs.size()); - for ( auto& n : refs ) { - result.push_back(Scope::Referee{.node = std::move(n), .qualified = id, .external = external}); - } + for ( auto& n : refs ) + result.push_back(Scope::Referee{.node = NodeRef(n), .qualified = id, .external = external}); return result; } @@ -64,7 +75,7 @@ std::vector Scope::_findID(const Scope* scope, const ID& id, boo return createRefs(i->second, h, external); for ( const auto& v : (*i).second ) { - Scope* scope_ = (*v).scope().get(); + Scope* scope_ = v->scope().get(); if ( auto m = v->tryAs() ) scope_ = m->root().scope().get(); @@ -89,11 +100,18 @@ std::vector Scope::_findID(const ID& id, bool external) const { void Scope::render(std::ostream& out, const std::string& prefix) const { for ( const auto& [k, v] : items() ) { for ( const auto& x : v ) { - auto s = util::fmt("%s%s -> %s", prefix, k, x ? x->render(false) : ""); + if ( ! x ) { + out << util::fmt("%s%s -> \n", prefix, k); + continue; + } + + auto s = util::fmt("%s%s -> %s", prefix, k, x->render(false)); if ( x ) { if ( auto d = x->tryAs() ) - s += util::fmt(" (type: %s)", d->expression().type()); + s += util::fmt(" (type: %s @t:%p)", d->expression().type(), d->expression().type().identity()); + else + s += util::fmt(" ([@d:%p])", x->identity()); } out << s << '\n'; diff --git a/hilti/toolchain/src/ast/type.cc b/hilti/toolchain/src/ast/type.cc index 3dc9407a9..9fbe9b406 100644 --- a/hilti/toolchain/src/ast/type.cc +++ b/hilti/toolchain/src/ast/type.cc @@ -1,6 +1,40 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. #include -#include +#include +#include +#include using namespace hilti; + +bool type::isResolved(const Type& t) { + ResolvedState rstate; + return isResolved(t, &rstate); +} + +bool type::detail::isResolved(const hilti::Type& t, ResolvedState* rstate) { + if ( ! rstate ) + return type::isResolved(t); + + if ( type::isParameterized(t) ) { + if ( rstate->find(t.identity()) != rstate->end() ) + return true; + + rstate->insert(t.identity()); + } + + return t._isResolved(rstate); +} + +void type::detail::applyPruneWalk(hilti::Type& t) { + // We prune at the types that have an ID, as only they can create cycles. + if ( t.typeID() ) { + t.addFlag(type::Flag::PruneWalk); + return; + } + + for ( auto&& c : t.childs() ) { + if ( auto x = c.tryAs() ) + applyPruneWalk(const_cast(*x)); + } +} diff --git a/hilti/toolchain/src/ast/types/enum.cc b/hilti/toolchain/src/ast/types/enum.cc index 99a260020..592ef8511 100644 --- a/hilti/toolchain/src/ast/types/enum.cc +++ b/hilti/toolchain/src/ast/types/enum.cc @@ -4,28 +4,77 @@ #include #include +#include #include using namespace hilti; -std::vector type::Enum::_normalizeLabels(std::vector labels) { +std::vector type::Enum::_normalizeLabels(std::vector labels) { auto max = std::max_element(labels.begin(), labels.end(), [](const auto& l1, const auto& l2) { return l1.value() < l2.value(); }); auto next_value = (max != labels.end() ? max->value() + 1 : 0); - std::vector nlabels; + std::vector nlabels; - for ( auto l : labels ) { + for ( auto&& l : labels ) { if ( util::tolower(l.id()) == "undef" ) throw std::out_of_range("reserved enum label 'Undef' cannot be redefined"); + type::enum_::Label nlabel; + if ( l.value() < 0 ) - nlabels.emplace_back(l.id(), next_value++, l.meta()); + nlabel = type::enum_::Label(l.id(), next_value++, l.meta()); else - nlabels.push_back(std::move(l)); + nlabel = std::move(l); + + Declaration d = declaration::Constant(nlabel.id(), expression::Ctor(ctor::Enum(nlabel))); + nlabels.push_back(std::move(d)); } - nlabels.emplace_back(ID("Undef"), -1); + auto undef_label = type::enum_::Label(ID("Undef"), -1); + auto undef = declaration::Constant(undef_label.id(), expression::Ctor(ctor::Enum(undef_label))); + nlabels.push_back(std::move(std::move(undef))); return nlabels; } + +std::vector> type::Enum::labels() const { + std::vector> labels; + + for ( const auto& c : childs() ) { + const auto& label = + c.as().value().as().ctor().as().value(); + labels.push_back(label); + } + + return labels; +} + +std::vector> type::Enum::uniqueLabels() const { + auto pred_gt = [](const enum_::Label& e1, const enum_::Label& e2) { return e1.value() > e2.value(); }; + auto pred_eq = [](const enum_::Label& e1, const enum_::Label& e2) { return e1.value() == e2.value(); }; + + auto in = labels(); + std::vector> out; + std::copy(in.begin(), in.end(), std::back_inserter(out)); + std::sort(out.begin(), out.end(), pred_gt); + out.erase(std::unique(out.begin(), out.end(), pred_eq), out.end()); + return out; +} + + +void type::Enum::initLabelTypes(Node* n) { + auto& etype = n->as(); + + std::vector nlabels; + + for ( const auto& l : etype.labels() ) { + auto nlabel = enum_::Label(l.get(), NodeRef(*n)); + Declaration d = declaration::Constant(nlabel.id(), expression::Ctor(ctor::Enum(nlabel))); + nlabels.push_back(std::move(d)); + } + + n->childs() = std::move(nlabels); + + etype._initialized = true; +} diff --git a/hilti/toolchain/src/ast/types/tuple.cc b/hilti/toolchain/src/ast/types/tuple.cc index ca592c124..9a0d3a424 100644 --- a/hilti/toolchain/src/ast/types/tuple.cc +++ b/hilti/toolchain/src/ast/types/tuple.cc @@ -1,24 +1,20 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "hilti/ast/types/tuple.h" - #include #include +#include -using namespace hilti; +#include -std::vector type::Tuple::ids() const { - auto ids = childsOfType(); - if ( ! ids.empty() ) - return ids; +using namespace hilti; - return std::vector(types().size(), ID()); -} +std::optional> type::Tuple::elementByID(const ID& id) const { + int i = 0; + for ( const auto& e : elements() ) { + if ( e.id() == id ) + return std::make_optional(std::make_pair(i, &e)); -std::optional> type::Tuple::elementByID(const ID& id) { - for ( const auto&& [i, e] : util::enumerate(elements()) ) { - if ( e.first == id ) - return std::make_pair(i, e.second); + i++; } return {}; diff --git a/hilti/toolchain/src/base/preprocessor.cc b/hilti/toolchain/src/base/preprocessor.cc index 678227279..03a9b130c 100644 --- a/hilti/toolchain/src/base/preprocessor.cc +++ b/hilti/toolchain/src/base/preprocessor.cc @@ -3,8 +3,7 @@ #include #include - -#include "base/util.h" +#include using namespace hilti::util; diff --git a/hilti/toolchain/src/base/type_erase.cc b/hilti/toolchain/src/base/type_erase.cc index 5952583ee..79049e255 100644 --- a/hilti/toolchain/src/base/type_erase.cc +++ b/hilti/toolchain/src/base/type_erase.cc @@ -31,12 +31,12 @@ void type_erasure::summary(std::ostream& out) { for ( const auto& [k, v] : unordered ) ordered.insert(std::make_pair(v, k)); - out << "\n=== Top-20 type-erased instances (#max/#min)\n\n"; + out << "\n=== Top-20 type-erased instances (#max/#current)\n\n"; int count = 20; for ( const auto& [v, k] : ordered ) { if ( v.max >= 100 ) - out << fmt("%40s %10d (%5.2f) %10d (%5.2f)\n", util::demangle(k), v.max, (100.0 * v.max / total_max), + out << fmt("%40s %10d (%5.2f%%) %10d (%5.2f%%)\n", util::demangle(k), v.max, (100.0 * v.max / total_max), v.current, (100.0 * v.current / total_current)); if ( --count == 0 ) diff --git a/hilti/toolchain/src/base/util.cc b/hilti/toolchain/src/base/util.cc index eb7279df5..33a986adc 100644 --- a/hilti/toolchain/src/base/util.cc +++ b/hilti/toolchain/src/base/util.cc @@ -1,7 +1,5 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "hilti/base/util.h" - #include #include #include @@ -17,6 +15,7 @@ #include #include +#include // We include pathfind directly here so we do not have to work // around it being installed by its default install target. diff --git a/hilti/toolchain/src/compiler/codegen/codegen.cc b/hilti/toolchain/src/compiler/codegen/codegen.cc index c74b86db0..df8eb2a67 100644 --- a/hilti/toolchain/src/compiler/codegen/codegen.cc +++ b/hilti/toolchain/src/compiler/codegen/codegen.cc @@ -25,6 +25,8 @@ using namespace hilti::detail::codegen; namespace { +// This visitor will only receive AST nodes of the first two levels (i.e., +// the module and its declarations). struct GlobalsVisitor : hilti::visitor::PreOrder { explicit GlobalsVisitor(CodeGen* cg, bool include_implementation) : cg(cg), include_implementation(include_implementation) {} @@ -41,7 +43,10 @@ struct GlobalsVisitor : hilti::visitor::PreOrder { static void addDeclarations(CodeGen* cg, const Node& module, const ID& module_id, cxx::Unit* unit, bool include_implementation) { auto v = GlobalsVisitor(cg, include_implementation); - for ( auto i : v.walk(module) ) + + v.dispatch(module); + + for ( const auto& i : module.childs() ) v.dispatch(i); if ( v.globals.empty() && v.constants.empty() ) @@ -83,16 +88,16 @@ struct GlobalsVisitor : hilti::visitor::PreOrder { auto id = cxx::ID{ns, "__init_globals"}; auto body_decl = - cxx::declaration::Function{.result = cg->compile(type::Void(), codegen::TypeUsage::FunctionResult), + cxx::declaration::Function{.result = cg->compile(type::void_, codegen::TypeUsage::FunctionResult), .id = id, - .args = {{.id = "ctx", .type = "hilti::rt::Context*"}}, + .args = {{.id = "ctx", .type = "::hilti::rt::Context*"}}, .linkage = "extern"}; auto body = cxx::Block(); cg->pushCxxBlock(&body); if ( ! v.globals.empty() ) - body.addStatement("hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index)"); + body.addStatement("::hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index)"); for ( auto g : v.globals ) { if ( g.init ) @@ -120,8 +125,8 @@ struct GlobalsVisitor : hilti::visitor::PreOrder { return cxx::type::Struct{.members = std::move(fields), .type_name = "__globals_t"}; } - void operator()(const declaration::GlobalVariable& n) { - auto args = util::transform(n.typeArguments(), [this](auto a) { return cg->compile(a); }); + void operator()(const declaration::GlobalVariable& n, position_t p) { + auto args = node::transform(n.typeArguments(), [this](auto a) { return cg->compile(a); }); auto init = n.init() ? cg->compile(*n.init()) : cg->typeDefaultValue(n.type()); auto x = cxx::declaration::Global{.id = {cg->unit()->cxxNamespace(), n.id()}, .type = cg->compile(n.type(), codegen::TypeUsage::Storage), @@ -132,7 +137,7 @@ struct GlobalsVisitor : hilti::visitor::PreOrder { globals.push_back(x); } - void operator()(const declaration::Constant& n) { + void operator()(const declaration::Constant& n, position_t p) { auto x = cxx::declaration::Global{.id = {cg->unit()->cxxNamespace(), n.id()}, .type = cg->compile(n.type(), codegen::TypeUsage::Storage), .init = cg->compile(n.value()), @@ -141,12 +146,15 @@ struct GlobalsVisitor : hilti::visitor::PreOrder { constants.push_back(x); } - void operator()(const declaration::Type& n) { - if ( include_implementation ) - cg->addTypeInfoDefinition(n.type()); + void operator()(const declaration::Type& n, position_t p) { + assert(n.typeID()); + cg->compile(n.type(), codegen::TypeUsage::Storage); + cg->addTypeInfoDefinition(n.type()); } }; +// This visitor will only receive AST nodes of the first two levels (i.e., +// the module and its declarations). struct Visitor : hilti::visitor::PreOrder { Visitor(CodeGen* cg, const Scope& module_scope, cxx::Unit* unit) : cg(cg), unit(unit), module_scope(module_scope) {} CodeGen* cg; @@ -191,7 +199,8 @@ struct Visitor : hilti::visitor::PreOrder { } void operator()(const declaration::ImportedModule& n) { - GlobalsVisitor::addDeclarations(cg, *n.module(), n.id(), unit, false); + assert(n.unit()); + GlobalsVisitor::addDeclarations(cg, n.unit()->module(), n.id(), unit, false); } void operator()(const declaration::LocalVariable& n) { @@ -218,11 +227,11 @@ struct Visitor : hilti::visitor::PreOrder { return; auto f = n.function(); - auto ft = f.type(); + auto ft = f.ftype(); auto ns = unit->cxxNamespace(); auto id = n.id(); auto linkage = n.linkage(); - auto is_hook = (n.function().type().flavor() == type::function::Flavor::Hook); + auto is_hook = (n.function().ftype().flavor() == type::function::Flavor::Hook); auto id_module = n.id().sub(-3); @@ -241,7 +250,7 @@ struct Visitor : hilti::visitor::PreOrder { if ( auto a = AttributeSet::find(n.function().attributes(), "&cxxname") ) { // Just add the prototype. Make sure to skip any custom namespacing. - d.id = cxx::ID(fmt("::%s", *a->valueAs())); + d.id = cxx::ID(fmt("::%s", *a->valueAsString())); cg->unit()->add(d); return; } @@ -249,7 +258,7 @@ struct Visitor : hilti::visitor::PreOrder { int64_t priority = 0; if ( is_hook && f.attributes() ) { if ( auto x = f.attributes()->find("&priority") ) { - if ( auto i = x->valueAs() ) + if ( auto i = x->valueAsInteger() ) priority = *i; else // Should have been caught earlier already. @@ -278,7 +287,7 @@ struct Visitor : hilti::visitor::PreOrder { // the type currently. d.args.push_back(cxx::declaration::Argument{ .id = "__self", - .type = fmt("hilti::rt::ValueReference<%s>&", id_struct_type), + .type = fmt("::hilti::rt::ValueReference<%s>&", id_struct_type), }); // Make any additional types the hook needs known to local unit and linker. @@ -396,10 +405,10 @@ struct Visitor : hilti::visitor::PreOrder { // Add runtime stack size check at beginning of function. // Cannot do this for "preinit" functions as we won't have a // runtime yet. - body.addStatementAtFront("hilti::rt::detail::checkStack()"); + body.addStatementAtFront("::hilti::rt::detail::checkStack()"); if ( n.linkage() == declaration::Linkage::Struct && ! f.isStatic() ) { - if ( ! is_hook ) { + if ( ! is_hook && ! f.isStatic() ) { // Need a LHS value for __self. auto self = cxx::declaration::Local{.id = "__self", .type = "auto", @@ -416,7 +425,7 @@ struct Visitor : hilti::visitor::PreOrder { std::vector args; std::vector fmts; - for ( const auto& p : f.type().parameters() ) { + for ( const auto& p : f.ftype().parameters() ) { args.emplace_back(fmt(", %s", cxx::ID(p.id()))); fmts.emplace_back("%s"); } @@ -462,11 +471,11 @@ struct Visitor : hilti::visitor::PreOrder { ", "); // If the function returns void synthesize a `Nothing` return value here. - if ( ft.result().type() != type::Void() ) + if ( ft.result().type() != type::void_ ) cb.addReturn(fmt("%s(%s)", d.id, inner_args)); else { cb.addStatement(fmt("%s(%s)", d.id, inner_args)); - cb.addReturn("hilti::rt::Nothing()"); + cb.addReturn("::hilti::rt::Nothing()"); } body.addLambda("cb", "[args_on_heap](hilti::rt::resumable::Handle* r) -> hilti::rt::any", std::move(cb)); @@ -477,7 +486,7 @@ struct Visitor : hilti::visitor::PreOrder { auto extern_d = d; extern_d.id = cxx::ID( util::replace(extern_d.id, cg->options().cxx_namespace_intern, cg->options().cxx_namespace_extern)); - extern_d.result = "hilti::rt::Resumable"; + extern_d.result = "::hilti::rt::Resumable"; auto extern_cxx_func = cxx::Function{.declaration = extern_d, .body = std::move(body)}; @@ -491,7 +500,7 @@ struct Visitor : hilti::visitor::PreOrder { auto body = cxx::Block(); cxx::Expression forward_call = fmt("%s(%s)", d.id, util::join(cxx_func.declaration.args, ", ")); - if ( ft.result().type() != type::Void() ) + if ( ft.result().type() != type::void_ ) body.addReturn(forward_call); else body.addStatement(forward_call); @@ -525,8 +534,6 @@ struct Visitor : hilti::visitor::PreOrder { cg->unit()->addPreInitialization(call_preinit_func); } } - - void operator()(const declaration::Type& n) { cg->compile(n.type(), codegen::TypeUsage::Storage); } }; } // anonymous namespace @@ -606,16 +613,34 @@ cxx::declaration::Function CodeGen::compile(const ID& id, type::Function ft, dec return cxx::declaration::Function{.result = result_(), .id = {ns, cxx_id}, - .args = util::transform(ft.parameters(), param_), + .args = node::transform(ft.parameters(), param_), .linkage = linkage_()}; } -std::vector CodeGen::compileCallArguments(const std::vector& args, - const std::vector& params) { - auto kinds = util::transform(params, [](auto& x) { return x.kind(); }); - return util::transform(util::zip2(args, kinds), [this](auto& x) { - return compile(x.first, x.second == declaration::parameter::Kind::InOut); - }); +std::vector CodeGen::compileCallArguments(const node::Range& args, + const node::Set& params) { + assert(args.size() == params.size()); + + auto kinds = node::transform(params, [](auto& x) { return x.kind(); }); + + std::vector x; + for ( auto i = 0u; i < args.size(); i++ ) + x.emplace_back(compile(args[i], params[i].kind() == declaration::parameter::Kind::InOut)); + + return x; +} + +std::vector CodeGen::compileCallArguments(const node::Range& args, + const node::Range& params) { + assert(args.size() == params.size()); + + auto kinds = node::transform(params, [](auto& x) { return x.kind(); }); + + std::vector x; + for ( auto i = 0u; i < args.size(); i++ ) + x.emplace_back(compile(args[i], params[i].kind() == declaration::parameter::Kind::InOut)); + + return x; } Result CodeGen::compileModule(Node& root, hilti::Unit* hilti_unit, bool include_implementation) { @@ -625,7 +650,9 @@ Result CodeGen::compileModule(Node& root, hilti::Unit* hilti_unit, bo _hilti_unit = hilti_unit; auto v = Visitor(this, *root.scope(), _cxx_unit.get()); - for ( auto i : v.walk(&root) ) + v.dispatch(root); + + for ( auto i : root.childs() ) v.dispatch(i); GlobalsVisitor::addDeclarations(this, root, ID(std::string(_cxx_unit->moduleID())), _cxx_unit.get(), diff --git a/hilti/toolchain/src/compiler/codegen/coercions.cc b/hilti/toolchain/src/compiler/codegen/coercions.cc index 0e0ae32b9..36b03ad5a 100644 --- a/hilti/toolchain/src/compiler/codegen/coercions.cc +++ b/hilti/toolchain/src/compiler/codegen/coercions.cc @@ -22,14 +22,15 @@ struct Visitor : public hilti::visitor::PreOrder { result_t operator()(const type::Bytes& src) { if ( auto t = dst.tryAs() ) - return fmt("hilti::rt::Stream(%s)", expr); + return fmt("::hilti::rt::Stream(%s)", expr); logger().internalError(fmt("codegen: unexpected type coercion from bytes to %s", dst.typename_())); } - result_t operator()(const type::Enum& src) { + result_t operator()(const type::Enum& src, position_t p) { if ( auto t = dst.tryAs() ) { - auto id = cg->compile(src, codegen::TypeUsage::Storage); + auto etype = p.node.as(); // preserve type ID + auto id = cg->compile(std::move(etype), codegen::TypeUsage::Storage); return fmt("(%s != %s::Undef)", expr, id); } @@ -43,9 +44,18 @@ struct Visitor : public hilti::visitor::PreOrder { logger().internalError(fmt("codegen: unexpected type coercion from error to %s", dst.typename_())); } + result_t operator()(const type::Interval& src) { + if ( auto t = dst.tryAs() ) { + auto id = cg->compile(src, codegen::TypeUsage::Storage); + return fmt("(%s != hilti::rt::Interval())", expr); + } + + logger().internalError(fmt("codegen: unexpected type coercion from interval to %s", dst.typename_())); + } + result_t operator()(const type::List& src) { if ( auto t = dst.tryAs() ) - return fmt("hilti::rt::Set(%s)", expr); + return fmt("::hilti::rt::Set(%s)", expr); if ( auto t = dst.tryAs() ) { auto x = cg->compile(t->elementType(), codegen::TypeUsage::Storage); @@ -54,7 +64,7 @@ struct Visitor : public hilti::visitor::PreOrder { if ( auto def = cg->typeDefaultValue(t->elementType()) ) allocator = fmt(", hilti::rt::vector::Allocator<%s, %s>", x, *def); - return fmt("hilti::rt::Vector<%s%s>(%s)", x, allocator, expr); + return fmt("::hilti::rt::Vector<%s%s>(%s)", x, allocator, expr); } logger().internalError(fmt("codegen: unexpected type coercion from lisst to %s", dst.typename_())); @@ -81,7 +91,7 @@ struct Visitor : public hilti::visitor::PreOrder { return fmt("%s.derefAsValue()", expr); if ( auto t = dst.tryAs() ) - return fmt("hilti::rt::WeakReference<%s>(%s)", + return fmt("::hilti::rt::WeakReference<%s>(%s)", cg->compile(src.dereferencedType(), codegen::TypeUsage::Ctor), expr); if ( src.dereferencedType() == dst ) @@ -90,6 +100,16 @@ struct Visitor : public hilti::visitor::PreOrder { logger().internalError(fmt("codegen: unexpected type coercion from %s to %s", Type(src), dst.typename_())); } + result_t operator()(const type::Time& src) { + if ( auto t = dst.tryAs() ) { + auto id = cg->compile(src, codegen::TypeUsage::Storage); + return fmt("(%s != hilti::rt::Time())", expr); + } + + logger().internalError(fmt("codegen: unexpected type coercion from time to %s", dst.typename_())); + } + + result_t operator()(const type::Result& src) { if ( auto t = dst.tryAs() ) return fmt("static_cast(%s)", expr); @@ -105,10 +125,10 @@ struct Visitor : public hilti::visitor::PreOrder { return fmt("static_cast(%s)", expr); if ( auto t = dst.tryAs() ) - return fmt("hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); + return fmt("::hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); if ( auto t = dst.tryAs() ) - return fmt("hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); + return fmt("::hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); logger().internalError(fmt("codegen: unexpected type coercion from signed integer to %s", dst.typename_())); } @@ -120,9 +140,10 @@ struct Visitor : public hilti::visitor::PreOrder { logger().internalError(fmt("codegen: unexpected type coercion from stream to %s", dst.typename_())); } - result_t operator()(const type::Union& src) { + result_t operator()(const type::Union& src, position_t p) { if ( auto t = dst.tryAs() ) { - auto id = cg->compile(src, codegen::TypeUsage::Storage); + auto utype = p.node.as(); // preserve type ID + auto id = cg->compile(std::move(utype), codegen::TypeUsage::Storage); return fmt("(%s.index() > 0)", expr); } @@ -138,16 +159,17 @@ struct Visitor : public hilti::visitor::PreOrder { result_t operator()(const type::Tuple& src) { if ( auto t = dst.tryAs() ) { - int i = 0; std::vector exprs; - for ( auto [src, dst] : util::zip2(src.types(), t->types()) ) - exprs.push_back(cg->coerce(fmt("std::get<%d>(%s)", i++, expr), src, dst)); + assert(src.elements().size() == t->elements().size()); + for ( auto i = 0u; i < src.elements().size(); i++ ) + exprs.push_back( + cg->coerce(fmt("std::get<%d>(%s)", i, expr), src.elements()[i].type(), t->elements()[i].type())); return fmt("std::make_tuple(%s)", util::join(exprs, ", ")); } - logger().internalError(fmt("codegen: unexpected type coercion from result to %s", dst.typename_())); + logger().internalError(fmt("codegen: unexpected type coercion from tuple to %s", dst.typename_())); } result_t operator()(const type::UnsignedInteger& src) { @@ -155,10 +177,10 @@ struct Visitor : public hilti::visitor::PreOrder { return fmt("static_cast(%s)", expr); if ( auto t = dst.tryAs() ) - return fmt("hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); + return fmt("::hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); if ( auto t = dst.tryAs() ) - return fmt("hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); + return fmt("::hilti::rt::integer::safe(%s)", std::max(16, t->width()), expr); logger().internalError(fmt("codegen: unexpected type coercion from unsigned integer to %s", dst.typename_())); } @@ -168,7 +190,7 @@ struct Visitor : public hilti::visitor::PreOrder { return fmt("static_cast(%s)", expr); if ( auto t = dst.tryAs() ) - return fmt("hilti::rt::StrongReference<%s>(%s)", + return fmt("::hilti::rt::StrongReference<%s>(%s)", cg->compile(src.dereferencedType(), codegen::TypeUsage::Ctor), expr); if ( auto t = dst.tryAs() ) @@ -177,25 +199,29 @@ struct Visitor : public hilti::visitor::PreOrder { if ( src.dereferencedType() == dst ) return fmt("(*%s)", expr); - logger().internalError(fmt("codegen: unexpected type coercion from result to %s", dst.typename_())); + logger().internalError(fmt("codegen: unexpected type coercion from weak reference to %s", dst.typename_())); } result_t operator()(const type::ValueReference& src) { if ( auto t = dst.tryAs() ) return cg->coerce(fmt("*%s", expr), src.dereferencedType(), dst); + if ( auto t = dst.tryAs(); + t && type::sameExceptForConstness(src.dereferencedType(), t->dereferencedType()) ) + return fmt("%s", expr); + if ( auto t = dst.tryAs() ) - return fmt("hilti::rt::StrongReference<%s>(%s)", + return fmt("::hilti::rt::StrongReference<%s>(%s)", cg->compile(src.dereferencedType(), codegen::TypeUsage::Ctor), expr); if ( auto t = dst.tryAs() ) - return fmt("hilti::rt::WeakReference<%s>(%s)", + return fmt("::hilti::rt::WeakReference<%s>(%s)", cg->compile(src.dereferencedType(), codegen::TypeUsage::Ctor), expr); if ( src.dereferencedType() == dst ) return fmt("(*%s)", expr); - logger().internalError(fmt("codegen: unexpected type coercion from result to %s", dst.typename_())); + logger().internalError(fmt("codegen: unexpected type coercion from value reference to %s", dst.typename_())); } }; diff --git a/hilti/toolchain/src/compiler/codegen/ctors.cc b/hilti/toolchain/src/compiler/codegen/ctors.cc index fea7e5901..23f26de1c 100644 --- a/hilti/toolchain/src/compiler/codegen/ctors.cc +++ b/hilti/toolchain/src/compiler/codegen/ctors.cc @@ -21,20 +21,20 @@ struct Visitor : hilti::visitor::PreOrder { explicit Visitor(CodeGen* cg) : cg(cg) {} CodeGen* cg; - result_t operator()(const ctor::Address& n) { return fmt("hilti::rt::Address(\"%s\")", n.value()); } + result_t operator()(const ctor::Address& n) { return fmt("::hilti::rt::Address(\"%s\")", n.value()); } - result_t operator()(const ctor::Bool& n) { return fmt("hilti::rt::Bool(%s)", n.value() ? "true" : "false"); } + result_t operator()(const ctor::Bool& n) { return fmt("::hilti::rt::Bool(%s)", n.value() ? "true" : "false"); } result_t operator()(const ctor::Bytes& n) { return fmt("\"%s\"_b", util::escapeBytesForCxx(n.value())); } result_t operator()(const ctor::Coerced& n) { return cg->compile(n.coercedCtor()); } result_t operator()(const ctor::Default& n) { - auto args = util::join(util::transform(n.typeArguments(), [&](const auto e) { return cg->compile(e); }), ", "); + auto args = util::join(node::transform(n.typeArguments(), [&](const auto e) { return cg->compile(e); }), ", "); return fmt("(%s(%s))", cg->compile(n.type(), codegen::TypeUsage::Ctor), args); } - result_t operator()(const ctor::Error& n) { return fmt("hilti::rt::result::Error(\"%s\")", n.value()); } + result_t operator()(const ctor::Error& n) { return fmt("::hilti::rt::result::Error(\"%s\")", n.value()); } result_t operator()(const ctor::Exception& n) { std::string type; @@ -48,41 +48,45 @@ struct Visitor : hilti::visitor::PreOrder { } result_t operator()(const ctor::Interval& n) { - return fmt("hilti::rt::Interval(hilti::rt::integer::safe(%" PRId64 + return fmt("::hilti::rt::Interval(hilti::rt::integer::safe(%" PRId64 "), hilti::rt::Interval::NanosecondTag())", n.value().nanoseconds()); } + result_t operator()(const ctor::Library& n) { + return fmt("%s(%s)", n.type().as().cxxName(), cg->compile(n.value())); + } + result_t operator()(const ctor::List& n) { if ( n.elementType() == type::unknown ) // Can only be the empty list. - return "hilti::rt::vector::Empty()"; + return "::hilti::rt::vector::Empty()"; - return fmt("hilti::rt::Vector<%s>({%s})", cg->compile(n.elementType(), codegen::TypeUsage::Storage), - util::join(util::transform(n.value(), [this](auto e) { return cg->compile(e); }), ", ")); + return fmt("::hilti::rt::Vector<%s>({%s})", cg->compile(n.elementType(), codegen::TypeUsage::Storage), + util::join(node::transform(n.value(), [this](auto e) { return cg->compile(e); }), ", ")); } result_t operator()(const ctor::Map& n) { - if ( n.elementType() == type::unknown ) + if ( n.valueType() == type::unknown ) // Can only be the empty map. - return "hilti::rt::map::Empty()"; + return "::hilti::rt::map::Empty()"; auto k = cg->compile(n.keyType(), codegen::TypeUsage::Storage); - auto v = cg->compile(n.elementType(), codegen::TypeUsage::Storage); + auto v = cg->compile(n.valueType(), codegen::TypeUsage::Storage); - return fmt("hilti::rt::Map<%s, %s>({%s})", k, v, - util::join(util::transform(n.value(), - [this](auto e) { - return fmt("{%s, %s}", cg->compile(e.first), cg->compile(e.second)); + return fmt("::hilti::rt::Map<%s, %s>({%s})", k, v, + util::join(node::transform(n.value(), + [this](const auto& e) { + return fmt("{%s, %s}", cg->compile(e.key()), cg->compile(e.value())); }), ", ")); } result_t operator()(const ctor::Network& n) { - return fmt("hilti::rt::Network(\"%s\", %u)", n.value().prefix(), n.value().length()); + return fmt("::hilti::rt::Network(\"%s\", %u)", n.value().prefix(), n.value().length()); } - result_t operator()(const ctor::Null& n) { return fmt("hilti::rt::Null()"); } + result_t operator()(const ctor::Null& n) { return fmt("::hilti::rt::Null()"); } result_t operator()(const ctor::Optional& n) { if ( auto e = n.value() ) @@ -91,7 +95,7 @@ struct Visitor : hilti::visitor::PreOrder { return fmt("std::optional<%s>()", cg->compile(n.dereferencedType(), codegen::TypeUsage::Ctor)); } - result_t operator()(const ctor::Port& n) { return fmt("hilti::rt::Port(\"%s\")", n.value()); } + result_t operator()(const ctor::Port& n) { return fmt("::hilti::rt::Port(\"%s\")", n.value()); } result_t operator()(const ctor::Real& n) { // We use hexformat for lossless serialization. Older platforms like @@ -113,7 +117,7 @@ struct Visitor : hilti::visitor::PreOrder { } result_t operator()(const ctor::StrongReference& n) { - return fmt("hilti::rt::StrongReference<%s>()", cg->compile(n.dereferencedType(), codegen::TypeUsage::Ctor)); + return fmt("::hilti::rt::StrongReference<%s>()", cg->compile(n.dereferencedType(), codegen::TypeUsage::Ctor)); } result_t operator()(const ctor::RegExp& n) { @@ -123,9 +127,11 @@ struct Visitor : hilti::visitor::PreOrder { flags.emplace_back(".no_sub = true"); auto t = (n.value().size() == 1 ? "std::string" : "std::vector"); - return fmt("hilti::rt::RegExp(%s{%s}, {%s})", t, + return fmt("::hilti::rt::RegExp(%s{%s}, {%s})", t, util::join(util::transform(n.value(), - [&](auto s) { return fmt("\"%s\"", util::escapeUTF8(s, true, false)); }), + [&](const auto& s) { + return fmt("\"%s\"", util::escapeUTF8(s, true, false)); + }), ", "), util::join(flags, ", ")); } @@ -133,30 +139,30 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const ctor::Set& n) { if ( n.elementType() == type::unknown ) // Can only be the empty list. - return "hilti::rt::set::Empty()"; + return "::hilti::rt::set::Empty()"; const auto k = cg->compile(n.elementType(), codegen::TypeUsage::Storage); - return fmt("hilti::rt::Set<%s>({%s})", k, - util::join(util::transform(n.value(), [this](auto e) { return fmt("%s", cg->compile(e)); }), ", ")); + return fmt("::hilti::rt::Set<%s>({%s})", k, + util::join(node::transform(n.value(), [this](auto e) { return fmt("%s", cg->compile(e)); }), ", ")); } result_t operator()(const ctor::SignedInteger& n) { if ( /* n.width() == 64 && */ n.value() == INT64_MIN ) - return fmt("hilti::rt::integer::safe{INT64_MIN}"); + return fmt("::hilti::rt::integer::safe{INT64_MIN}"); - return fmt("hilti::rt::integer::safe{%" PRId64 "}", n.width(), n.value()); + return fmt("::hilti::rt::integer::safe{%" PRId64 "}", n.width(), n.value()); } result_t operator()(const ctor::Stream& n) { - return fmt("hilti::rt::Stream(\"%s\"_b)", util::escapeBytesForCxx(n.value())); + return fmt("::hilti::rt::Stream(\"%s\"_b)", util::escapeBytesForCxx(n.value())); } result_t operator()(const ctor::String& n) { return fmt("std::string(\"%s\")", util::escapeUTF8(n.value(), true)); } result_t operator()(const ctor::Tuple& n) { return fmt("std::make_tuple(%s)", - util::join(util::transform(n.value(), [this](auto e) { return cg->compile(e); }), ", ")); + util::join(node::transform(n.value(), [this](auto e) { return cg->compile(e); }), ", ")); } result_t operator()(const ctor::Struct& n) { @@ -165,20 +171,20 @@ struct Visitor : hilti::visitor::PreOrder { auto is_field = [&](auto f) { return ! f.type().template isA(); }; auto convert_field = [&](auto f) { - if ( const auto& c = n.field(f.id()) ) - return cg->compile(c->second); + if ( auto c = n.field(f.id()) ) + return cg->compile(c->expression()); return cxx::Expression("{}"); }; return fmt("%s(%s)", id, - util::join(util::transform(util::filter(n.type().as().fields(), is_field), + util::join(node::transform(node::filter(n.type().as().fields(), is_field), convert_field), ", ")); } result_t operator()(const ctor::Time& n) { - return fmt("hilti::rt::Time(%" PRId64 ", hilti::rt::Time::NanosecondTag())", n.value().nanoseconds()); + return fmt("::hilti::rt::Time(%" PRId64 ", hilti::rt::Time::NanosecondTag())", n.value().nanoseconds()); } result_t operator()(const ctor::Enum& n) { @@ -187,14 +193,14 @@ struct Visitor : hilti::visitor::PreOrder { } result_t operator()(const ctor::ValueReference& n) { - return fmt("hilti::rt::reference::make_value<%s>(%s)", + return fmt("::hilti::rt::reference::make_value<%s>(%s)", cg->compile(n.dereferencedType(), codegen::TypeUsage::Ctor), cg->compile(n.expression())); } result_t operator()(const ctor::Vector& n) { if ( n.elementType() == type::unknown ) // Can only be the empty list. - return "hilti::rt::vector::Empty()"; + return "::hilti::rt::vector::Empty()"; auto x = cg->compile(n.elementType(), codegen::TypeUsage::Storage); @@ -202,16 +208,16 @@ struct Visitor : hilti::visitor::PreOrder { if ( auto def = cg->typeDefaultValue(n.elementType()) ) allocator = fmt(", hilti::rt::vector::Allocator<%s, %s>", x, *def); - return fmt("hilti::rt::Vector<%s%s>({%s})", x, allocator, - util::join(util::transform(n.value(), [this](auto e) { return fmt("%s", cg->compile(e)); }), ", ")); + return fmt("::hilti::rt::Vector<%s%s>({%s})", x, allocator, + util::join(node::transform(n.value(), [this](auto e) { return fmt("%s", cg->compile(e)); }), ", ")); } result_t operator()(const ctor::UnsignedInteger& n) { - return fmt("hilti::rt::integer::safe{%" PRId64 "u}", n.width(), n.value()); + return fmt("::hilti::rt::integer::safe{%" PRId64 "u}", n.width(), n.value()); } result_t operator()(const ctor::WeakReference& n) { - return fmt("hilti::rt::WeakReference<%s>()", cg->compile(n.dereferencedType(), codegen::TypeUsage::Ctor)); + return fmt("::hilti::rt::WeakReference<%s>()", cg->compile(n.dereferencedType(), codegen::TypeUsage::Ctor)); } }; diff --git a/hilti/toolchain/src/compiler/codegen/expressions.cc b/hilti/toolchain/src/compiler/codegen/expressions.cc index 1da6c397f..3487ef442 100644 --- a/hilti/toolchain/src/compiler/codegen/expressions.cc +++ b/hilti/toolchain/src/compiler/codegen/expressions.cc @@ -21,14 +21,6 @@ struct Visitor : hilti::visitor::PreOrder { bool lhs; result_t operator()(const expression::Assign& n) { - if ( auto c = n.target().tryAs() ) { - if ( c->ctor().type().isA() ) { - auto t = c->ctor().as().value(); - auto l = util::join(util::transform(t, [this](auto& x) { return cg->compile(x, true); }), ", "); - return fmt("std::tie(%s) = %s", l, cg->compile(n.source())); - } - } - return fmt("%s = %s", cg->compile(n.target(), true), cg->compile(n.source())); } @@ -41,14 +33,14 @@ struct Visitor : hilti::visitor::PreOrder { // not present for certain globals: // // auto arguments = - // util::join(util::transform(n.arguments(), [this](auto& x) { return cg->compile(x, true); }), ", "); + // util::join(node::transform(n.arguments(), [this](auto& x) { return cg->compile(x, true); }), ", "); // // return fmt("%s(%s)", cxx::ID(n.cxxname()), arguments); cxx::Block block; cg->pushCxxBlock(&block); auto arguments = - util::join(util::transform(n.arguments(), [this](auto& x) { return cg->compile(x, true); }), ", "); + util::join(node::transform(n.arguments(), [this](auto& x) { return cg->compile(x, true); }), ", "); cg->popCxxBlock(); block.addStatement(fmt("%s(%s)", cxx::ID(n.cxxname()), arguments)); @@ -77,11 +69,11 @@ struct Visitor : hilti::visitor::PreOrder { if ( n.catchException() ) return fmt( - "hilti::rt::DeferredExpression<%s>([=]() -> %s { try { return %s; } catch ( ... ) { return " - "hilti::rt::result::Error(\"n/a\"); } })", + "::hilti::rt::DeferredExpression<%s>([=]() -> %s { try { return %s; } catch ( ... ) { return " + "::hilti::rt::result::Error(\"n/a\"); } })", type, type, value); else - return fmt("hilti::rt::DeferredExpression<%s>([=]() -> %s { return %s; })", type, type, value); + return fmt("::hilti::rt::DeferredExpression<%s>([=]() -> %s { return %s; })", type, type, value); } result_t operator()(const expression::Grouping& n) { return fmt("(%s)", cg->compile(n.expression())); } @@ -96,7 +88,7 @@ struct Visitor : hilti::visitor::PreOrder { } result_t operator()(const expression::ListComprehension& n) { - auto id = cxx::ID(n.id()); + auto id = cxx::ID(n.local().id()); auto input = cg->compile(n.input()); auto itype = cg->compile(n.input().type().elementType(), codegen::TypeUsage::Storage); auto otype = cg->compile(n.output().type(), codegen::TypeUsage::Storage); @@ -105,7 +97,7 @@ struct Visitor : hilti::visitor::PreOrder { auto allocator = std::string(); if ( auto def = cg->typeDefaultValue(n.output().type()) ) { - allocator = fmt("hilti::rt::vector::Allocator<%s, %s>", otype, *def); + allocator = fmt("::hilti::rt::vector::Allocator<%s, %s>", otype, *def); } else { allocator = fmt("std::allocator<%s>", otype); @@ -114,7 +106,7 @@ struct Visitor : hilti::visitor::PreOrder { if ( auto c = n.condition() ) pred = fmt(", [](auto&& %s) -> bool { return %s; }", id, cg->compile(*c)); - return fmt("hilti::rt::vector::make<%s, %s, %s>(%s, [](auto&& %s) -> %s { return %s; }%s)", allocator, itype, + return fmt("::hilti::rt::vector::make<%s, %s, %s>(%s, [](auto&& %s) -> %s { return %s; }%s)", allocator, itype, otype, input, id, otype, output, pred); } @@ -171,7 +163,7 @@ struct Visitor : hilti::visitor::PreOrder { } } - if ( auto p = n.declaration().tryAs(); p && p->isStructParameter() ) { + if ( auto p = n.declaration().tryAs(); p && p->isTypeParameter() ) { // Need to adjust here for potential automatic change to a weak reference. if ( type::isReferenceType(p->type()) ) return cxx::Expression(fmt("%s->__p_%s.derefAsValue()", cg->self(), p->id())); diff --git a/hilti/toolchain/src/compiler/codegen/operators.cc b/hilti/toolchain/src/compiler/codegen/operators.cc index 8eccf1b39..044e3eb7b 100644 --- a/hilti/toolchain/src/compiler/codegen/operators.cc +++ b/hilti/toolchain/src/compiler/codegen/operators.cc @@ -37,6 +37,10 @@ struct Visitor : hilti::visitor::PreOrder { return util::transform(exprs, [&](const auto& e) { return cg->compile(e); }); } + auto compileExpressions(const node::Range& exprs) { + return node::transform(exprs, [&](const auto& e) { return cg->compile(e); }); + } + auto tupleArguments(const expression::ResolvedOperatorBase& o, const Expression& op) { auto ctor = op.as().ctor(); @@ -365,10 +369,10 @@ struct Visitor : hilti::visitor::PreOrder { /// Real result_t operator()(const operator_::real::CastToInterval& n) { - return fmt("hilti::rt::Interval(%f, hilti::rt::Interval::NanosecondTag())", op0(n)); + return fmt("::hilti::rt::Interval(%f, hilti::rt::Interval::NanosecondTag())", op0(n)); } result_t operator()(const operator_::real::CastToTime& n) { - return fmt("hilti::rt::Time(%f, hilti::rt::Time::SecondTag())", op0(n)); + return fmt("::hilti::rt::Time(%f, hilti::rt::Time::SecondTag())", op0(n)); } result_t operator()(const operator_::real::Difference& n) { return binary(n, "-"); } result_t operator()(const operator_::real::DifferenceAssign& n) { return binary(n, "-="); } @@ -417,11 +421,11 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const operator_::generic::New& n) { if ( auto tv = n.op0().type().tryAs() ) { auto args = util::join(tupleArguments(n, n.op1()), ", "); - return fmt("hilti::rt::reference::make_strong<%s>(%s)", + return fmt("::hilti::rt::reference::make_strong<%s>(%s)", cg->compile(tv->typeValue(), codegen::TypeUsage::Ctor), args); } else { - return fmt("hilti::rt::reference::make_strong<%s>(%s)", + return fmt("::hilti::rt::reference::make_strong<%s>(%s)", cg->compile(n.op0().type(), codegen::TypeUsage::Ctor), op0(n)); } } @@ -437,16 +441,16 @@ struct Visitor : hilti::visitor::PreOrder { auto name = op0(n); if ( auto a = AttributeSet::find(f.function().attributes(), "&cxxname") ) { - auto s = a->valueAs(); + auto s = a->valueAsString(); if ( s ) name = *s; else logger().error(s, n); } - auto values = n.op1().as().ctor().as().value(); + const auto& values = n.op1().as().ctor().as().value(); return fmt("%s(%s)", name, - util::join(cg->compileCallArguments(values, f.function().type().parameters()), ", ")); + util::join(cg->compileCallArguments(values, f.function().ftype().parameters()), ", ")); } result_t operator()(const operator_::regexp::Match& n) { @@ -481,7 +485,7 @@ struct Visitor : hilti::visitor::PreOrder { // Optional result_t operator()(const operator_::optional::Deref& n) { - return fmt("hilti::rt::optional::value(%s, \"%s\")", op0(n), n.op0().meta().location().render()); + return fmt("::hilti::rt::optional::value(%s, \"%s\")", op0(n), n.op0().meta().location().render()); } /// Port @@ -641,19 +645,19 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const operator_::string::Encode& n) { auto [self, args] = methodArguments(n); - return fmt("hilti::rt::Bytes(%s, %s)", self, args[0]); + return fmt("::hilti::rt::Bytes(%s, %s)", self, args[0]); } result_t operator()(const operator_::string::Modulo& n) { if ( n.op1().type().isA() ) { if ( auto ctor = n.op1().tryAs() ) { auto t = ctor->ctor().as().value(); - return fmt("hilti::rt::fmt(%s, %s)", op0(n), - util::join(util::transform(t, [this](auto& x) { return cg->compile(x); }), ", ")); + return fmt("::hilti::rt::fmt(%s, %s)", op0(n), + util::join(node::transform(t, [this](auto& x) { return cg->compile(x); }), ", ")); } } - return fmt("hilti::rt::fmt(%s, %s)", op0(n), op1(n)); + return fmt("::hilti::rt::fmt(%s, %s)", op0(n), op1(n)); } // Strong reference @@ -681,15 +685,15 @@ struct Visitor : hilti::visitor::PreOrder { if ( lhs ) { if ( d ) - return fmt("hilti::rt::optional::valueOrInit(%s, %s)", attr, cg->compile(*d)); + return fmt("::hilti::rt::optional::valueOrInit(%s, %s)", attr, cg->compile(*d)); - return fmt("hilti::rt::optional::valueOrInit(%s)", attr); + return fmt("::hilti::rt::optional::valueOrInit(%s)", attr); } if ( d ) return fmt("%s.value_or(%s)", attr, cg->compile(*d)); - return fmt("hilti::rt::optional::value(%s, \"%s\")", attr, op0.meta().location().render()); + return fmt("::hilti::rt::optional::value(%s, \"%s\")", attr, op0.meta().location().render()); } return attr; @@ -702,17 +706,16 @@ struct Visitor : hilti::visitor::PreOrder { auto id = n.op1().as().id(); auto ft = n.op1().as().type().as(); auto args = n.op2().as().ctor().as().value(); - auto kinds = util::transform(ft.parameters(), [](auto& x) { return x.kind(); }); - auto zipped = util::zip2(args, kinds); + + std::vector> zipped; + + for ( auto i = 0u; i < args.size(); i++ ) + zipped.emplace_back(args[i], ft.parameters()[i].kind() == declaration::parameter::Kind::InOut); + return memberAccess(n, fmt("%s(%s)", id, util::join(util::transform(zipped, - [this](auto& x) { - return cg - ->compile(x.first, - x.second == - declaration::parameter::Kind::InOut); - }), + [this](auto& x) { return cg->compile(x.first, x.second); }), ", ")), false); } @@ -736,7 +739,8 @@ struct Visitor : hilti::visitor::PreOrder { if ( auto d = f->default_() ) return memberAccess(n, fmt("value_or(%s)", cg->compile(*d))); - return fmt("hilti::rt::struct_::value_or_exception(%s, \"%s\")", attr, n.op0().meta().location().render()); + return fmt("::hilti::rt::struct_::value_or_exception(%s, \"%s\")", attr, + n.op0().meta().location().render()); } return structMember(n, n.op1()); @@ -759,16 +763,16 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const operator_::union_::MemberConst& n) { auto idx = unionFieldIndex(n.op0(), n.op1()); - return fmt("hilti::rt::union_::get<%u>(%s)", idx, op0(n)); + return fmt("::hilti::rt::union_::get<%u>(%s)", idx, op0(n)); } result_t operator()(const operator_::union_::MemberNonConst& n) { auto idx = unionFieldIndex(n.op0(), n.op1()); if ( lhs ) - return fmt("hilti::rt::union_::get_proxy<%u>(%s)", idx, op0(n)); + return fmt("::hilti::rt::union_::get_proxy<%u>(%s)", idx, op0(n)); else - return fmt("hilti::rt::union_::get<%u>(%s)", idx, op0(n)); + return fmt("::hilti::rt::union_::get<%u>(%s)", idx, op0(n)); } result_t operator()(const operator_::union_::HasMember& n) { @@ -779,7 +783,7 @@ struct Visitor : hilti::visitor::PreOrder { // Signed integer result_t operator()(const operator_::signed_integer::CastToInterval& n) { - return fmt("hilti::rt::Interval(hilti::rt::integer::safe(%" PRId64 + return fmt("::hilti::rt::Interval(hilti::rt::integer::safe(%" PRId64 ") * 1000000000, hilti::rt::Interval::NanosecondTag())", op0(n)); } @@ -806,7 +810,7 @@ struct Visitor : hilti::visitor::PreOrder { result_t operator()(const operator_::signed_integer::Multiple& n) { return fmt("%s * %s", op0(n), op1(n)); } result_t operator()(const operator_::signed_integer::MultipleAssign& n) { return fmt("%s *= %s", op0(n), op1(n)); } result_t operator()(const operator_::signed_integer::Power& n) { - return fmt("hilti::rt::pow(%s, %s)", op0(n), op1(n)); + return fmt("::hilti::rt::pow(%s, %s)", op0(n), op1(n)); } result_t operator()(const operator_::signed_integer::SignNeg& n) { return fmt("(-%s)", op0(n)); } result_t operator()(const operator_::signed_integer::Sum& n) { return fmt("%s + %s", op0(n), op1(n)); } @@ -844,6 +848,12 @@ struct Visitor : hilti::visitor::PreOrder { // Tuple + result_t operator()(const operator_::tuple::CustomAssign& n) { + auto t = n.operands()[0].as().ctor().as().value(); + auto l = util::join(node::transform(t, [this](auto& x) { return cg->compile(x, true); }), ", "); + return fmt("std::tie(%s) = %s", l, op1(n)); + } + result_t operator()(const operator_::tuple::Equal& n) { return fmt("%s == %s", op0(n), op1(n)); } result_t operator()(const operator_::tuple::Unequal& n) { return fmt("%s != %s", op0(n), op1(n)); } @@ -869,12 +879,12 @@ struct Visitor : hilti::visitor::PreOrder { return fmt("::hilti::rt::enum_::from_uint<%s>(%s)", cg->compile(t, codegen::TypeUsage::Storage), op0(n)); } result_t operator()(const operator_::unsigned_integer::CastToInterval& n) { - return fmt("hilti::rt::Interval(hilti::rt::integer::safe(%" PRIu64 + return fmt("::hilti::rt::Interval(hilti::rt::integer::safe(%" PRIu64 ") * 1000000000, hilti::rt::Interval::NanosecondTag())", op0(n)); } result_t operator()(const operator_::unsigned_integer::CastToTime& n) { - return fmt("hilti::rt::Time(hilti::rt::integer::safe(%" PRIu64 + return fmt("::hilti::rt::Time(hilti::rt::integer::safe(%" PRIu64 ") * 1'000'000'000, hilti::rt::Time::NanosecondTag())", op0(n)); } @@ -902,7 +912,7 @@ struct Visitor : hilti::visitor::PreOrder { } result_t operator()(const operator_::unsigned_integer::Negate& n) { return fmt("~%s", op0(n)); } result_t operator()(const operator_::unsigned_integer::Power& n) { - return fmt("hilti::rt::pow(%s, %s)", op0(n), op1(n)); + return fmt("::hilti::rt::pow(%s, %s)", op0(n), op1(n)); } result_t operator()(const operator_::unsigned_integer::ShiftLeft& n) { return fmt("(%s << %s)", op0(n), op1(n)); } result_t operator()(const operator_::unsigned_integer::ShiftRight& n) { return fmt("(%s >> %s)", op0(n), op1(n)); } diff --git a/hilti/toolchain/src/compiler/codegen/statements.cc b/hilti/toolchain/src/compiler/codegen/statements.cc index cd67cbac3..b1848db2b 100644 --- a/hilti/toolchain/src/compiler/codegen/statements.cc +++ b/hilti/toolchain/src/compiler/codegen/statements.cc @@ -60,7 +60,7 @@ struct Visitor : hilti::visitor::PreOrder { cxx::Block try_body; try_body.addTmp( - cxx::declaration::Local{.id = "_", .type = "hilti::rt::exception::DisableAbortOnExceptions"}); + cxx::declaration::Local{.id = "_", .type = "::hilti::rt::exception::DisableAbortOnExceptions"}); try_body.addStatement(fmt("%s", cg->compile(n.expression()))); if ( cg->options().debug_flow ) @@ -119,20 +119,17 @@ struct Visitor : hilti::visitor::PreOrder { logger().internalError("statements can only declare local variables"); std::vector args; - - auto t = d->type(); - if ( type::isReferenceType(t) ) - t = t.dereferencedType(); - - if ( auto s = t.tryAs() ) - args = cg->compileCallArguments(d->typeArguments(), s->parameters()); - std::optional init; if ( auto i = d->init() ) init = cg->compile(*i); - else + + else { + if ( auto s = d->type().tryAs() ) + args = cg->compileCallArguments(d->typeArguments(), s->parameters()); + init = cg->typeDefaultValue(d->type()); + } auto l = cxx::declaration::Local{.id = cxx::ID(d->id()), .type = cg->compile(d->type(), codegen::TypeUsage::Storage), @@ -149,7 +146,7 @@ struct Visitor : hilti::visitor::PreOrder { std::string cond; if ( auto x = n.init() ) { - auto& l = x->template as(); + auto& l = *x; std::optional cxx_init; if ( auto i = l.init() ) @@ -182,7 +179,7 @@ struct Visitor : hilti::visitor::PreOrder { } void operator()(const statement::For& n) { - auto id = cxx::ID(n.id()); + auto id = cxx::ID(n.local().id()); auto seq = cg->compile(n.sequence()); auto body = cg->compile(n.body()); @@ -192,7 +189,7 @@ struct Visitor : hilti::visitor::PreOrder { cxx::Block b; b.setEnsureBracesforBlock(); b.addTmp(cxx::declaration::Local{.id = "__seq", .type = "auto", .init = seq}); - b.addForRange(true, id, fmt("hilti::rt::range(__seq)"), body); + b.addForRange(true, id, fmt("::hilti::rt::range(__seq)"), body); block->addBlock(std::move(b)); } } @@ -215,28 +212,24 @@ struct Visitor : hilti::visitor::PreOrder { std::string cxx_type; std::string cxx_init; - if ( n.init() ) { - auto init = n.init()->as(); - cxx_type = cg->compile(n.type(), codegen::TypeUsage::Storage); - cxx_id = cxx::ID(init.id()); - cxx_init = cg->compile(*init.init()); - } - else { - cxx_type = cxx::ID("const auto"); - cxx_id = cxx::ID("__x"); - cxx_init = cg->compile(n.expression()); - } + auto cond = n.condition(); + cxx_type = cg->compile(cond.type(), codegen::TypeUsage::Storage); + cxx_id = cxx::ID(cond.id()); + cxx_init = cg->compile(*cond.init()); bool first = true; for ( const auto& c : n.cases() ) { + if ( c.isDefault() ) + continue; // will handle below + std::string cond; auto exprs = c.preprocessedExpressions(); if ( exprs.size() == 1 ) - cond = cg->compile(exprs.front()); + cond = cg->compile(*exprs.begin()); else - cond = util::join(util::transform(exprs, [&](auto& e) { return cg->compile(e); }), " || "); + cond = util::join(node::transform(exprs, [&](auto& e) { return cg->compile(e); }), " || "); auto body = cg->compile(c.body()); @@ -301,7 +294,7 @@ struct Visitor : hilti::visitor::PreOrder { std::optional cxx_init; if ( n.init() ) - init = n.init()->as(); + init = n.init(); if ( init ) { if ( auto i = init->init() ) @@ -380,7 +373,7 @@ struct Visitor : hilti::visitor::PreOrder { if ( cg->options().debug_flow ) block->addStatement(fmt(R"(HILTI_RT_DEBUG("hilti-flow", "%s: yield"))", n.meta().location())); - block->addStatement("hilti::rt::detail::yield()"); + block->addStatement("::hilti::rt::detail::yield()"); } }; diff --git a/hilti/toolchain/src/compiler/codegen/types.cc b/hilti/toolchain/src/compiler/codegen/types.cc index a708ec3d4..00aabc497 100644 --- a/hilti/toolchain/src/compiler/codegen/types.cc +++ b/hilti/toolchain/src/compiler/codegen/types.cc @@ -29,9 +29,14 @@ struct VisitorDeclaration : hilti::visitor::PreOrder().typeID(); } + auto cxxID(const Node& n) { return n.as().cxxID(); } + result_t operator()(const type::Struct& n, const position_t p) { + assert(typeID(p.node)); + auto scope = cxx::ID{cg->unit()->cxxNamespace()}; - auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("struct_%p", &n))}; + auto sid = cxx::ID{*typeID(p.node)}; if ( sid.namespace_() ) scope = scope.namespace_(); @@ -102,23 +107,26 @@ struct VisitorDeclaration : hilti::visitor::PreOrderbody() ) { auto cxx_body = cxx::Block(); - // Need a LHS for __self. - auto tid = n.typeID(); + if ( ! f.isStatic() ) { + // Need a LHS for __self. + auto tid = typeID(p.node); - if ( ! tid ) - logger().internalError("Struct type with hooks does not have a type ID"); + if ( ! tid ) + logger().internalError("Struct type with hooks does not have a type ID"); - auto id_module = tid->sub(-2); - auto id_class = tid->sub(-1); + auto id_module = tid->sub(-2); + auto id_class = tid->sub(-1); - if ( id_module.empty() ) - id_module = cg->hiltiUnit()->id(); + if ( id_module.empty() ) + id_module = cg->hiltiUnit()->id(); + + auto id_type = cxx::ID(id_module, id_class); + auto self = cxx::declaration::Local{.id = "__self", + .type = "auto", + .init = fmt("%s::__self()", id_type)}; + cxx_body.addLocal(self); + } - auto id_type = cxx::ID(id_module, id_class); - auto self = cxx::declaration::Local{.id = "__self", - .type = "auto", - .init = fmt("%s::__self()", id_type)}; - cxx_body.addLocal(self); cg->compile(*func->body(), &cxx_body); auto method_impl = cxx::Function{.declaration = d, .body = std::move(cxx_body)}; @@ -128,7 +136,7 @@ struct VisitorDeclaration : hilti::visitor::PreOrderflavor() == type::function::Flavor::Hook ) { - auto tid = n.typeID(); + auto tid = typeID(p.node); if ( ! tid ) logger().internalError("Struct type with hooks does not have a type ID"); @@ -180,7 +188,7 @@ struct VisitorDeclaration : hilti::visitor::PreOrdercompile(type::ValueReference(n), + .type = cg->compile(type::ValueReference(p.node.as()), codegen::TypeUsage::InOutParameter)}); cg->unit()->add(hook); } @@ -251,15 +259,17 @@ struct VisitorDeclaration : hilti::visitor::PreOrderunit()->cxxNamespace()}; - auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("union_%p", &n))}; + auto sid = cxx::ID{*typeID(p.node)}; if ( sid.namespace_() ) scope = scope.namespace_(); @@ -296,8 +306,10 @@ struct VisitorDeclaration : hilti::visitor::PreOrderunit()->cxxNamespace()}; - auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("enum_%p", &n))}; + auto sid = cxx::ID{*typeID(p.node)}; if ( sid.namespace_() ) scope = scope.namespace_(); @@ -305,7 +317,8 @@ struct VisitorDeclaration : hilti::visitor::PreOrderunit()->cxxNamespace()}; - auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("exception_%p", &n))}; + auto sid = cxx::ID{*typeID(p.node)}; if ( sid.namespace_() ) scope = scope.namespace_(); @@ -347,22 +362,27 @@ struct VisitorStorage : hilti::visitor::PreOrder { util::Cache* cache; codegen::TypeUsage usage; - result_t operator()(const type::Address& n) { return CxxTypes{.base_type = "hilti::rt::Address"}; } + auto typeID(const Node& n) { return n.as().typeID(); } + auto cxxID(const Node& n) { return n.as().cxxID(); } + + result_t operator()(const type::Address& n) { return CxxTypes{.base_type = "::hilti::rt::Address"}; } - result_t operator()(const type::Any& n) { return CxxTypes{.base_type = "hilti::rt::any"}; } + result_t operator()(const type::Any& n) { return CxxTypes{.base_type = "::hilti::rt::any"}; } - result_t operator()(const type::Bool& n) { return CxxTypes{.base_type = "hilti::rt::Bool"}; } + result_t operator()(const type::Bool& n) { return CxxTypes{.base_type = "::hilti::rt::Bool"}; } - result_t operator()(const type::Bytes& n) { return CxxTypes{.base_type = "hilti::rt::Bytes"}; } + 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"}; } - result_t operator()(const type::Enum& n, const position_t p) { - if ( auto cxx = n.cxxID() ) + result_t operator()(const type::Enum& n, position_t p) { + assert(typeID(p.node)); + + if ( auto cxx = cxxID(p.node) ) return CxxTypes{.base_type = cxx::Type(*cxx), .default_ = cxx::Expression(cxx::ID(*cxx, "Undef"))}; auto scope = cxx::ID{cg->unit()->cxxNamespace()}; - auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("enum_%p", &n))}; + auto sid = cxx::ID{*typeID(p.node)}; if ( sid.namespace_() ) scope = scope.namespace_(); @@ -372,8 +392,8 @@ struct VisitorStorage : hilti::visitor::PreOrder { // Add tailored to_string() function. auto cases = util::transform(n.uniqueLabels(), [&](auto l) { auto b = cxx::Block(); - b.addReturn(fmt("\"%s\"", cxx::ID(id.local(), l.id()))); - return std::make_pair(cxx::Expression(cxx::ID(id, l.id())), std::move(b)); + b.addReturn(fmt("\"%s\"", cxx::ID(id.local(), l.get().id()))); + return std::make_pair(cxx::Expression(cxx::ID(id, l.get().id())), std::move(b)); }); auto default_ = cxx::Block(); @@ -384,7 +404,7 @@ struct VisitorStorage : hilti::visitor::PreOrder { body.addSwitch("x", cases, std::move(default_)); auto ts_decl = cxx::declaration::Function{.result = "std::string", - .id = {"hilti::rt::detail::adl", "to_string"}, + .id = {"::hilti::rt::detail::adl", "to_string"}, .args = {cxx::declaration::Argument{.id = "x", .type = cxx::Type(id)}, cxx::declaration::Argument{.id = "", .type = "adl::tag"}}, .linkage = "inline"}; @@ -410,44 +430,48 @@ struct VisitorStorage : hilti::visitor::PreOrder { cg->unit()->add(render_decl); cg->unit()->add(render_impl); - cg->addDeclarationFor(n); + cg->addDeclarationFor(p.node.as()); return CxxTypes{.base_type = std::string(sid), .default_ = cxx::Expression(cxx::ID(sid, "Undef"))}; } - result_t operator()(const type::Error& n) { return CxxTypes{.base_type = "hilti::rt::result::Error"}; } + result_t operator()(const type::Error& n) { return CxxTypes{.base_type = "::hilti::rt::result::Error"}; } result_t operator()(const type::Exception& n, const position_t p) { - if ( auto cxx = n.cxxID() ) + if ( auto cxx = cxxID(p.node) ) return CxxTypes{.base_type = cxx::Type(*cxx)}; - cg->addDeclarationFor(n); - - auto sid = cxx::ID{(n.typeID() ? std::string(*n.typeID()) : fmt("exception_%p", &n))}; - return CxxTypes{.base_type = std::string(sid), .storage = "hilti::rt::Exception"}; + if ( auto id = typeID(p.node) ) { + cg->addDeclarationFor(p.node.as()); + return CxxTypes{.base_type = std::string(*id), .storage = "::hilti::rt::Exception"}; + } + else + return CxxTypes{.base_type = "::hilti::rt::Exception"}; } result_t operator()(const type::Function& n) { return CxxTypes{}; } - result_t operator()(const type::Interval& n) { return CxxTypes{.base_type = "hilti::rt::Interval"}; } + result_t operator()(const type::Interval& n) { return CxxTypes{.base_type = "::hilti::rt::Interval"}; } - result_t operator()(const type::bytes::Iterator& n) { return CxxTypes{.base_type = "hilti::rt::bytes::Iterator"}; } + result_t operator()(const type::bytes::Iterator& n) { + return CxxTypes{.base_type = "::hilti::rt::bytes::Iterator"}; + } result_t operator()(const type::stream::Iterator& n) { - return CxxTypes{.base_type = "hilti::rt::stream::SafeConstIterator"}; + return CxxTypes{.base_type = "::hilti::rt::stream::SafeConstIterator"}; } result_t operator()(const type::list::Iterator& n) { auto t = - fmt("hilti::rt::Vector<%s>::iterator_t", cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage)); + fmt("::hilti::rt::Vector<%s>::iterator_t", cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage)); return CxxTypes{.base_type = fmt("%s", t)}; } result_t operator()(const type::map::Iterator& n) { auto i = (n.isConstant() ? "const_iterator" : "iterator"); - auto k = cg->compile(n.containerType().as().keyType(), codegen::TypeUsage::Storage); - auto v = cg->compile(n.containerType().as().elementType(), codegen::TypeUsage::Storage); + auto k = cg->compile(n.keyType(), codegen::TypeUsage::Storage); + auto v = cg->compile(n.valueType(), codegen::TypeUsage::Storage); - auto t = fmt("hilti::rt::Map<%s, %s>::%s", k, v, i); + auto t = fmt("::hilti::rt::Map<%s, %s>::%s", k, v, i); return CxxTypes{.base_type = fmt("%s", t)}; } @@ -455,7 +479,7 @@ struct VisitorStorage : hilti::visitor::PreOrder { auto i = (n.isConstant() ? "const_iterator" : "iterator"); auto x = cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage); - auto t = fmt("hilti::rt::Set<%s>::%s", x, i); + auto t = fmt("::hilti::rt::Set<%s>::%s", x, i); return CxxTypes{.base_type = fmt("%s", t)}; } @@ -468,7 +492,7 @@ struct VisitorStorage : hilti::visitor::PreOrder { if ( auto def = cg->typeDefaultValue(n.dereferencedType()) ) allocator = fmt(", hilti::rt::vector::Allocator<%s, %s>", x, *def); - auto t = fmt("hilti::rt::Vector<%s%s>::%s", x, allocator, i); + auto t = fmt("::hilti::rt::Vector<%s%s>::%s", x, allocator, i); return CxxTypes{.base_type = fmt("%s", t)}; } @@ -479,9 +503,9 @@ struct VisitorStorage : hilti::visitor::PreOrder { if ( n.elementType() == type::unknown ) // Can only be the empty list. - t = "hilti::rt::vector::Empty"; + t = "::hilti::rt::vector::Empty"; else - t = fmt("hilti::rt::Vector<%s>", cg->compile(n.elementType(), codegen::TypeUsage::Storage)); + t = fmt("::hilti::rt::Vector<%s>", cg->compile(n.elementType(), codegen::TypeUsage::Storage)); return CxxTypes{.base_type = fmt("%s", t)}; } @@ -491,32 +515,32 @@ struct VisitorStorage : hilti::visitor::PreOrder { if ( n.elementType() == type::unknown ) // Can only be the empty map. - t = "hilti::rt::map::Empty"; + t = "::hilti::rt::map::Empty"; else { auto k = cg->compile(n.keyType(), codegen::TypeUsage::Storage); auto v = cg->compile(n.elementType(), codegen::TypeUsage::Storage); - t = fmt("hilti::rt::Map<%s, %s>", k, v); + t = fmt("::hilti::rt::Map<%s, %s>", k, v); } return CxxTypes{.base_type = fmt("%s", t)}; } - result_t operator()(const type::Network& n) { return CxxTypes{.base_type = "hilti::rt::Network"}; } + result_t operator()(const type::Network& n) { return CxxTypes{.base_type = "::hilti::rt::Network"}; } - result_t operator()(const type::Null& n) { return CxxTypes{.base_type = "hilti::rt::Null"}; } + result_t operator()(const type::Null& n) { return CxxTypes{.base_type = "::hilti::rt::Null"}; } - result_t operator()(const type::Port& n) { return CxxTypes{.base_type = "hilti::rt::Port"}; } + result_t operator()(const type::Port& n) { return CxxTypes{.base_type = "::hilti::rt::Port"}; } - result_t operator()(const type::RegExp& n) { return CxxTypes{.base_type = "hilti::rt::RegExp"}; } + result_t operator()(const type::RegExp& n) { return CxxTypes{.base_type = "::hilti::rt::RegExp"}; } result_t operator()(const type::SignedInteger& n) { cxx::Type t; switch ( n.width() ) { - case 8: t = "hilti::rt::integer::safe"; break; - case 16: t = "hilti::rt::integer::safe"; break; - case 32: t = "hilti::rt::integer::safe"; break; - case 64: t = "hilti::rt::integer::safe"; break; + case 8: t = "::hilti::rt::integer::safe"; break; + case 16: t = "::hilti::rt::integer::safe"; break; + case 32: t = "::hilti::rt::integer::safe"; break; + case 64: t = "::hilti::rt::integer::safe"; break; default: logger().internalError("codegen: unexpected integer width", n); } @@ -528,23 +552,25 @@ struct VisitorStorage : hilti::visitor::PreOrder { if ( n.elementType() == type::unknown ) // Can only be the empty list. - t = "hilti::rt::set::Empty"; + t = "::hilti::rt::set::Empty"; else { auto x = cg->compile(n.elementType(), codegen::TypeUsage::Storage); - t = fmt("hilti::rt::Set<%s>", x); + t = fmt("::hilti::rt::Set<%s>", x); } return CxxTypes{.base_type = fmt("%s", t)}; } - result_t operator()(const type::Stream& n) { return CxxTypes{.base_type = "hilti::rt::Stream"}; } + result_t operator()(const type::Stream& n) { return CxxTypes{.base_type = "::hilti::rt::Stream"}; } + + result_t operator()(const type::Union& n, position_t p) { + assert(typeID(p.node)); - result_t operator()(const type::Union& n) { - if ( auto x = n.cxxID() ) + if ( auto x = cxxID(p.node) ) return CxxTypes{.base_type = cxx::Type(*x)}; auto scope = cxx::ID{cg->unit()->cxxNamespace().namespace_()}; - auto sid = cxx::ID{scope, (n.typeID() ? std::string(*n.typeID()) : fmt("union_%p", &n))}; + auto sid = cxx::ID{scope, *typeID(p.node)}; auto ns = sid.namespace_(); if ( cg->prioritizeTypes() ) @@ -569,7 +595,7 @@ struct VisitorStorage : hilti::visitor::PreOrder { cg->unit()->add(render_decl); cg->unit()->add(render_impl); - cg->addDeclarationFor(n); + cg->addDeclarationFor(p.node.as()); return cxx_types; }); @@ -580,7 +606,7 @@ struct VisitorStorage : hilti::visitor::PreOrder { if ( n.elementType() == type::unknown ) // Can only be the empty list. - t = "hilti::rt::vector::Empty"; + t = "::hilti::rt::vector::Empty"; else { auto x = cg->compile(n.elementType(), codegen::TypeUsage::Storage); @@ -588,24 +614,24 @@ struct VisitorStorage : hilti::visitor::PreOrder { if ( auto def = cg->typeDefaultValue(n.elementType()) ) allocator = fmt(", hilti::rt::vector::Allocator<%s, %s>", x, *def); - t = fmt("hilti::rt::Vector<%s%s>", x, allocator); + t = fmt("::hilti::rt::Vector<%s%s>", x, allocator); } return CxxTypes{.base_type = fmt("%s", t)}; } - result_t operator()(const type::Time& n) { return CxxTypes{.base_type = "hilti::rt::Time"}; } + result_t operator()(const type::Time& n) { return CxxTypes{.base_type = "::hilti::rt::Time"}; } result_t operator()(const type::UnsignedInteger& n) { cxx::Type t; switch ( n.width() ) { case 8: - t = "hilti::rt::integer::safe"; + t = "::hilti::rt::integer::safe"; break; // 2 bytes to avoid overloading confusion with uchar_t - case 16: t = "hilti::rt::integer::safe"; break; - case 32: t = "hilti::rt::integer::safe"; break; - case 64: t = "hilti::rt::integer::safe"; break; + case 16: t = "::hilti::rt::integer::safe"; break; + case 32: t = "::hilti::rt::integer::safe"; break; + case 64: t = "::hilti::rt::integer::safe"; break; default: logger().internalError("codegen: unexpected integer width", n); } @@ -627,29 +653,20 @@ struct VisitorStorage : hilti::visitor::PreOrder { std::string t; if ( auto ct = n.dereferencedType(); ! ct.isWildcard() ) - t = fmt("hilti::rt::StrongReference<%s>", cg->compile(ct, codegen::TypeUsage::Ctor)); // XXX + t = fmt("::hilti::rt::StrongReference<%s>", cg->compile(ct, codegen::TypeUsage::Ctor)); // XXX else t = "*"; return CxxTypes{.base_type = t, .param_in = fmt("const %s", t), .param_inout = fmt("%s", t)}; } - result_t operator()(const type::stream::View& n) { return CxxTypes{.base_type = "hilti::rt::stream::View"}; } - - result_t operator()(const type::ResolvedID& n) { - if ( auto x = dispatch(n.type()) ) - return *x; - - logger().internalError(fmt("codegen: ID resolves to type %s, which does not have a visitor", - to_node(n.type()).render()), - n); - } + result_t operator()(const type::stream::View& n) { return CxxTypes{.base_type = "::hilti::rt::stream::View"}; } result_t operator()(const type::Result& n) { std::string t; if ( auto ct = n.dereferencedType(); ! ct.isWildcard() ) - t = fmt("hilti::rt::Result<%s>", cg->compile(ct, codegen::TypeUsage::Storage)); + t = fmt("::hilti::rt::Result<%s>", cg->compile(ct, codegen::TypeUsage::Storage)); else t = "*"; @@ -658,12 +675,14 @@ struct VisitorStorage : hilti::visitor::PreOrder { result_t operator()(const type::String& n) { return CxxTypes{.base_type = "std::string"}; } - result_t operator()(const type::Struct& n) { - if ( auto x = n.cxxID() ) + result_t operator()(const type::Struct& n, position_t p) { + assert(typeID(p.node)); + + if ( auto x = cxxID(p.node) ) return CxxTypes{.base_type = cxx::Type(*x)}; auto scope = cxx::ID{cg->unit()->cxxNamespace().namespace_()}; - auto sid = cxx::ID{scope, (n.typeID() ? std::string(*n.typeID()) : fmt("struct_%p", &n))}; + auto sid = cxx::ID{scope, *typeID(p.node)}; auto ns = sid.namespace_(); if ( cg->prioritizeTypes() ) @@ -688,14 +707,15 @@ struct VisitorStorage : hilti::visitor::PreOrder { cg->unit()->add(render_decl); cg->unit()->add(render_impl); - cg->addDeclarationFor(n); + cg->addDeclarationFor(p.node.as()); return cxx_types; }); } result_t operator()(const type::Tuple& n) { - auto x = util::transform(n.types(), [this](auto t) { return cg->compile(t, codegen::TypeUsage::Storage); }); + auto x = node::transform(n.elements(), + [this](auto e) { return cg->compile(e.type(), codegen::TypeUsage::Storage); }); auto t = fmt("std::tuple<%s>", util::join(x, ", ")); return CxxTypes{.base_type = t}; } @@ -707,29 +727,14 @@ struct VisitorStorage : hilti::visitor::PreOrder { result_t operator()(const type::Void& n) { return CxxTypes{.base_type = "void"}; } result_t operator()(const type::Auto& n) { - if ( auto x = dispatch(n.type()) ) - return *x; - - logger().internalError(fmt("codegen: type wrapper (auto) resolves to type %s, which does not have a visitor", - to_node(n.type()).render()), - n); - } - - result_t operator()(const type::Computed& n) { - if ( auto x = dispatch(n.type()) ) - return *x; - - logger() - .internalError(fmt("codegen: type wrapper (computed) resolves to type %s, which does not have a visitor", - to_node(n.type()).render()), - n); + logger().internalError("codegen: automatic type has not been replaced"); } result_t operator()(const type::WeakReference& n) { std::string t; if ( auto ct = n.dereferencedType(); ! ct.isWildcard() ) - t = fmt("hilti::rt::WeakReference<%s>", cg->compile(ct, codegen::TypeUsage::Ctor)); + t = fmt("::hilti::rt::WeakReference<%s>", cg->compile(ct, codegen::TypeUsage::Ctor)); else t = "*"; @@ -741,7 +746,7 @@ struct VisitorStorage : hilti::visitor::PreOrder { if ( auto ct = n.dereferencedType(); ! ct.isWildcard() ) { auto element_type = cg->compile(ct, codegen::TypeUsage::Ctor); - return CxxTypes{.base_type = fmt("hilti::rt::ValueReference<%s>", element_type), .ctor = element_type}; + return CxxTypes{.base_type = fmt("::hilti::rt::ValueReference<%s>", element_type), .ctor = element_type}; } else return CxxTypes{.base_type = "*"}; @@ -754,49 +759,32 @@ struct VisitorTypeInfoPredefined : hilti::visitor::PreOrder().typeID(); } + auto cxxID(const Node& n) { return n.as().cxxID(); } + result_t operator()(const type::Enum& n) { std::vector labels; for ( const auto& l : n.labels() ) - labels.push_back(fmt("hilti::rt::type_info::enum_::Label{ \"%s\", %d }", cxx::ID(l.id()), l.value())); + labels.push_back( + fmt("::hilti::rt::type_info::enum_::Label{ \"%s\", %d }", cxx::ID(l.get().id()), l.get().value())); - return fmt("hilti::rt::type_info::Enum(std::vector({%s}))", + return fmt("::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({%s}))", util::join(labels, ", ")); } - result_t operator()(const type::Exception& n) { - if ( n.typeID() ) { + result_t operator()(const type::Exception& n, position_t p) { + if ( typeID(p.node) && ! cxxID(p.node) ) { // We use this opportunity to create an empty virtual destructor // that will trigger inclusion of vtable for the exception's // type; see rt/exception.h. We do this only if we have a type ID @@ -825,7 +817,7 @@ struct VisitorTypeInfoDynamic : hilti::visitor::PreOrderunit()->cxxNamespace()}; - auto sid = cxx::ID(*n.typeID()); + auto sid = cxx::ID{*typeID(p.node)}; if ( sid.namespace_() ) scope = scope.namespace_(); @@ -834,59 +826,59 @@ struct VisitorTypeInfoDynamic : hilti::visitor::PreOrderunit()->add(std::move(func)); } // Done with virtual method. - return "hilti::rt::type_info::Exception()"; + return "::hilti::rt::type_info::Exception()"; } - result_t operator()(const type::Function& n) { return "hilti::rt::type_info::Function()"; } + result_t operator()(const type::Function& n) { return "::hilti::rt::type_info::Function()"; } - result_t operator()(const type::Library& n) { return "hilti::rt::type_info::Library()"; } + result_t operator()(const type::Library& n) { return "::hilti::rt::type_info::Library()"; } result_t operator()(const type::Map& n) { auto ktype = cg->compile(n.keyType(), codegen::TypeUsage::Storage); auto vtype = cg->compile(n.elementType(), codegen::TypeUsage::Storage); auto deref_type = type::Tuple({n.keyType(), n.elementType()}); - return fmt("hilti::rt::type_info::Map(%s, %s, hilti::rt::type_info::Map::accessor<%s, %s>())", + return fmt("::hilti::rt::type_info::Map(%s, %s, ::hilti::rt::type_info::Map::accessor<%s, %s>())", cg->typeInfo(n.keyType()), cg->typeInfo(n.elementType()), cg->compile(n.keyType(), codegen::TypeUsage::Storage), cg->compile(n.elementType(), codegen::TypeUsage::Storage)); } result_t operator()(const type::map::Iterator& n) { - const auto& m = n.containerType().as(); - return fmt("hilti::rt::type_info::MapIterator(%s, %s, hilti::rt::type_info::MapIterator::accessor<%s, %s>())", - cg->typeInfo(m.keyType()), cg->typeInfo(m.elementType()), - cg->compile(m.keyType(), codegen::TypeUsage::Storage), - cg->compile(m.elementType(), codegen::TypeUsage::Storage)); + return fmt( + "::hilti::rt::type_info::MapIterator(%s, %s, ::hilti::rt::type_info::MapIterator::accessor<%s, %s>())", + cg->typeInfo(n.keyType()), cg->typeInfo(n.valueType()), + cg->compile(n.keyType(), codegen::TypeUsage::Storage), + cg->compile(n.valueType(), codegen::TypeUsage::Storage)); } result_t operator()(const type::Optional& n) { - return fmt("hilti::rt::type_info::Optional(%s, hilti::rt::type_info::Optional::accessor<%s>())", + return fmt("::hilti::rt::type_info::Optional(%s, ::hilti::rt::type_info::Optional::accessor<%s>())", cg->typeInfo(n.dereferencedType()), cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage)); } result_t operator()(const type::Result& n) { - return fmt("hilti::rt::type_info::Result(%s, hilti::rt::type_info::Result::accessor<%s>())", + return fmt("::hilti::rt::type_info::Result(%s, ::hilti::rt::type_info::Result::accessor<%s>())", cg->typeInfo(n.dereferencedType()), cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage)); } result_t operator()(const type::Set& n) { - return fmt("hilti::rt::type_info::Set(%s, hilti::rt::type_info::Set::accessor<%s>())", + return fmt("::hilti::rt::type_info::Set(%s, ::hilti::rt::type_info::Set::accessor<%s>())", cg->typeInfo(n.elementType()), cg->compile(n.elementType(), codegen::TypeUsage::Storage)); } result_t operator()(const type::set::Iterator& n) { - return fmt("hilti::rt::type_info::SetIterator(%s, hilti::rt::type_info::SetIterator::accessor<%s>())", + return fmt("::hilti::rt::type_info::SetIterator(%s, ::hilti::rt::type_info::SetIterator::accessor<%s>())", cg->typeInfo(n.dereferencedType()), cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage)); } - result_t operator()(const type::Struct& n) { + result_t operator()(const type::Struct& n, position_t p) { std::vector fields; for ( const auto& f : n.fields() ) { @@ -899,55 +891,60 @@ struct VisitorTypeInfoDynamic : hilti::visitor::PreOrder()", + accessor = fmt(", ::hilti::rt::type_info::struct_::Field::accessor_optional<%s>()", cg->compile(f.type(), codegen::TypeUsage::Storage)); - fields.push_back(fmt("hilti::rt::type_info::struct_::Field{ \"%s\", %s, offsetof(%s, %s), %s%s }", - cxx::ID(f.id()), cg->typeInfo(f.type()), cxx::ID(*n.typeID()), cxx::ID(f.id()), - f.isInternal(), accessor)); + cxx::ID cxx_type_id{*typeID(p.node)}; + if ( auto x = cxxID(p.node) ) + cxx_type_id = *x; + + fields.push_back(fmt("::hilti::rt::type_info::struct_::Field{ \"%s\", %s, offsetof(%s, %s), %s%s }", + cxx::ID(f.id()), cg->typeInfo(f.type()), cxx_type_id, cxx::ID(f.id()), f.isInternal(), + accessor)); } - return fmt("hilti::rt::type_info::Struct(std::vector({%s}))", + return fmt("::hilti::rt::type_info::Struct(std::vector<::hilti::rt::type_info::struct_::Field>({%s}))", util::join(fields, ", ")); } - result_t operator()(const type::Tuple& n) { + result_t operator()(const type::Tuple& n, position_t p) { std::vector elems; - auto ttype = cg->compile(n, codegen::TypeUsage::Storage); + auto ttype = cg->compile(p.node.as(), codegen::TypeUsage::Storage); for ( const auto&& [i, e] : util::enumerate(n.elements()) ) elems.push_back( - fmt("hilti::rt::type_info::tuple::Element{ \"%s\", %s, hilti::rt::tuple::elementOffset<%s, %d>() }", - e.first, cg->typeInfo(e.second), ttype, i)); + fmt("::hilti::rt::type_info::tuple::Element{ \"%s\", %s, hilti::rt::tuple::elementOffset<%s, %d>() }", + e.id() ? *e.id() : ID(), cg->typeInfo(e.type()), ttype, i)); - return fmt("hilti::rt::type_info::Tuple(std::vector({%s}))", + return fmt("::hilti::rt::type_info::Tuple(std::vector<::hilti::rt::type_info::tuple::Element>({%s}))", util::join(elems, ", ")); } - result_t operator()(const type::Union& n) { + result_t operator()(const type::Union& n, position_t p) { std::vector fields; for ( const auto& f : n.fields() ) fields.push_back( - fmt("hilti::rt::type_info::union_::Field{ \"%s\", %s }", cxx::ID(f.id()), cg->typeInfo(f.type()))); + fmt("::hilti::rt::type_info::union_::Field{ \"%s\", %s }", cxx::ID(f.id()), cg->typeInfo(f.type()))); return fmt( - "hilti::rt::type_info::Union(std::vector({%s}), " - "hilti::rt::type_info::Union::accessor<%s>())", - util::join(fields, ", "), cg->compile(n, codegen::TypeUsage::Storage)); + "::hilti::rt::type_info::Union(std::vector<::hilti::rt::type_info::union_::Field>({%s}), " + "::hilti::rt::type_info::Union::accessor<%s>())", + util::join(fields, ", "), cg->compile(p.node.as(), codegen::TypeUsage::Storage)); } result_t operator()(const type::StrongReference& n) { - return fmt("hilti::rt::type_info::StrongReference(%s, hilti::rt::type_info::StrongReference::accessor<%s>())", - cg->typeInfo(n.dereferencedType()), cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage)); + return fmt( + "::hilti::rt::type_info::StrongReference(%s, ::hilti::rt::type_info::StrongReference::accessor<%s>())", + cg->typeInfo(n.dereferencedType()), cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage)); } result_t operator()(const type::ValueReference& n) { - return fmt("hilti::rt::type_info::ValueReference(%s, hilti::rt::type_info::ValueReference::accessor<%s>())", + return fmt("::hilti::rt::type_info::ValueReference(%s, ::hilti::rt::type_info::ValueReference::accessor<%s>())", cg->typeInfo(n.dereferencedType()), cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage)); } result_t operator()(const type::WeakReference& n) { - return fmt("hilti::rt::type_info::WeakReference(%s, hilti::rt::type_info::WeakReference::accessor<%s>())", + return fmt("::hilti::rt::type_info::WeakReference(%s, ::hilti::rt::type_info::WeakReference::accessor<%s>())", cg->typeInfo(n.dereferencedType()), cg->compile(n.dereferencedType(), codegen::TypeUsage::Storage)); } @@ -958,7 +955,7 @@ struct VisitorTypeInfoDynamic : hilti::visitor::PreOrdertypeDefaultValue(n.elementType()) ) allocator = fmt(", hilti::rt::vector::Allocator<%s, %s>", x, *def); - return fmt("hilti::rt::type_info::Vector(%s, hilti::rt::type_info::Vector::accessor<%s%s>())", + return fmt("::hilti::rt::type_info::Vector(%s, ::hilti::rt::type_info::Vector::accessor<%s%s>())", cg->typeInfo(n.elementType()), x, allocator); } @@ -969,29 +966,13 @@ struct VisitorTypeInfoDynamic : hilti::visitor::PreOrdertypeDefaultValue(n.dereferencedType()) ) allocator = fmt(", hilti::rt::vector::Allocator<%s, %s>", x, *def); - return fmt("hilti::rt::type_info::VectorIterator(%s, hilti::rt::type_info::VectorIterator::accessor<%s%s>())", - cg->typeInfo(n.dereferencedType()), x, allocator); + return fmt( + "::hilti::rt::type_info::VectorIterator(%s, ::hilti::rt::type_info::VectorIterator::accessor<%s%s>())", + cg->typeInfo(n.dereferencedType()), x, allocator); } result_t operator()(const type::Auto& n) { - if ( auto x = dispatch(n.type()) ) - return *x; - else - return {}; - } - - result_t operator()(const type::Computed& n) { - if ( auto x = dispatch(n.type()) ) - return *x; - else - return {}; - } - - result_t operator()(const type::ResolvedID& n) { - if ( auto x = dispatch(n.type()) ) - return *x; - else - return {}; + logger().internalError("codegen: automatic type has not been replaced"); } result_t operator()(const type::UnresolvedID& n) { @@ -1004,8 +985,10 @@ struct VisitorTypeInfoDynamic : hilti::visitor::PreOrder base_type; if ( x->base_type && usage != codegen::TypeUsage::Ctor ) @@ -1087,15 +1070,18 @@ cxx::Type CodeGen::compile(const hilti::Type& t, codegen::TypeUsage usage) { std::optional CodeGen::typeDefaultValue(const hilti::Type& t) { auto x = VisitorStorage(this, &_cache_types_storage, codegen::TypeUsage::None).dispatch(t); - if ( ! x ) + if ( ! x ) { + hilti::render(std::cerr, t); logger().internalError(fmt("codegen: type %s does not have a visitor", t), t); + } + return std::move(x->default_); }; std::list CodeGen::typeDependencies(const hilti::Type& t) { VisitorDeclaration v(this, &_cache_types_declarations); - v.dispatch(type::effectiveType(t)); + v.dispatch(t); return v.dependencies; }; @@ -1129,7 +1115,7 @@ const CxxTypeInfo& CodeGen::_getOrCreateTypeInfo(const hilti::Type& t) { return CxxTypeInfo{.predefined = true, .reference = fmt("&%s", *x)}; auto forward = cxx::declaration::Constant{.id = tid, - .type = "hilti::rt::TypeInfo", + .type = "::hilti::rt::TypeInfo", .linkage = "extern", .forward_decl = true}; unit()->add(forward); @@ -1150,7 +1136,7 @@ const CxxTypeInfo& CodeGen::_getOrCreateTypeInfo(const hilti::Type& t) { auto init = fmt("{ %s, \"%s\", new %s }", id_init, display.str(), *x); ti.declaration = - cxx::declaration::Constant{.id = tid, .type = "hilti::rt::TypeInfo", .init = init, .linkage = ""}; + cxx::declaration::Constant{.id = tid, .type = "::hilti::rt::TypeInfo", .init = init, .linkage = ""}; unit()->add(*ti.declaration); diff --git a/hilti/toolchain/src/compiler/codegen/unpack.cc b/hilti/toolchain/src/compiler/codegen/unpack.cc index a8140d8b3..fea23e0f7 100644 --- a/hilti/toolchain/src/compiler/codegen/unpack.cc +++ b/hilti/toolchain/src/compiler/codegen/unpack.cc @@ -24,19 +24,19 @@ struct Visitor : hilti::visitor::PreOrder { const std::vector& args; result_t operator()(const type::Address& n) { - return fmt("hilti::rt::address::unpack(%s, %s, %s)", data, args[0], args[1]); + return fmt("::hilti::rt::address::unpack(%s, %s, %s)", data, args[0], args[1]); } result_t operator()(const type::UnsignedInteger& n) { - return fmt("hilti::rt::integer::unpack(%s, %s)", n.width(), data, args[0]); + return fmt("::hilti::rt::integer::unpack(%s, %s)", n.width(), data, args[0]); } result_t operator()(const type::SignedInteger& n) { - return fmt("hilti::rt::integer::unpack(%s, %s)", n.width(), data, args[0]); + return fmt("::hilti::rt::integer::unpack(%s, %s)", n.width(), data, args[0]); } result_t operator()(const type::Real& n) { - return fmt("hilti::rt::real::unpack(%s, %s, %s)", data, args[0], args[1]); + return fmt("::hilti::rt::real::unpack(%s, %s, %s)", data, args[0], args[1]); } }; diff --git a/hilti/toolchain/src/compiler/coercion.cc b/hilti/toolchain/src/compiler/coercion.cc index a0ddb7de5..f05d4761b 100644 --- a/hilti/toolchain/src/compiler/coercion.cc +++ b/hilti/toolchain/src/compiler/coercion.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -23,9 +24,559 @@ using namespace hilti; using namespace util; namespace hilti::logging::debug { -inline const DebugStream Resolver("resolver"); +inline const DebugStream Operator("operator"); } // namespace hilti::logging::debug + +namespace { + +struct VisitorCtor : public visitor::PreOrder, VisitorCtor> { + VisitorCtor(const Type& dst, bitmask style) : dst(dst), style(style) {} + + const Type& dst; + bitmask style; + + result_t operator()(const ctor::Enum& c) { + if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) + return ctor::Bool(c.value().id() != ID("Undef"), c.meta()); + + return {}; + } + + result_t operator()(const ctor::Map& c) { + if ( auto t = dst.tryAs() ) { + std::vector nelemns; + for ( const auto& e : c.value() ) { + auto k = hilti::coerceExpression(e.key(), t->keyType(), style); + auto v = hilti::coerceExpression(e.value(), t->elementType(), style); + + if ( k && v ) + nelemns.emplace_back(*k.coerced, *v.coerced); + else + return {}; + } + + return ctor::Map(t->keyType(), t->elementType(), nelemns, c.meta()); + } + + return {}; + } + + result_t operator()(const ctor::Null& c) { + if ( auto t = dst.tryAs() ) + return ctor::Optional(t->dereferencedType()); + + if ( auto t = dst.tryAs() ) + return ctor::StrongReference(t->dereferencedType()); + + if ( auto t = dst.tryAs() ) + return ctor::WeakReference(t->dereferencedType()); + + return {}; + } + + result_t operator()(const ctor::List& c) { + if ( auto t = dst.tryAs() ) { + std::vector nexprs; + for ( const auto& e : c.value() ) { + if ( auto x = hilti::coerceExpression(e, t->elementType(), CoercionStyle::TryAllForAssignment) ) + nexprs.push_back(*x.coerced); + else + return {}; + } + return ctor::List(t->elementType(), std::move(nexprs), c.meta()); + } + + if ( auto t = dst.tryAs() ) { + auto dt = t->isWildcard() ? c.elementType() : t->elementType(); + + std::vector nexprs; + for ( const auto& e : c.value() ) { + if ( auto x = hilti::coerceExpression(e, dt, CoercionStyle::TryAllForAssignment) ) + nexprs.push_back(*x.coerced); + else + return {}; + } + return ctor::Vector(dt, std::move(nexprs), c.meta()); + } + + if ( auto t = dst.tryAs() ) { + auto dt = t->isWildcard() ? c.elementType() : t->elementType(); + + std::vector nexprs; + for ( const auto& e : c.value() ) { + if ( auto x = hilti::coerceExpression(e, dt, CoercionStyle::TryAllForAssignment) ) + nexprs.push_back(*x.coerced); + else + return {}; + } + return ctor::Set(dt, std::move(nexprs), c.meta()); + } + + return {}; + } + + result_t operator()(const ctor::Real& c) { + // Note: double->Integral constant conversions check 'non-narrowing' via + // double->Int->double roundtrip - the generated code looks good. + + if ( auto t = dst.tryAs() ) { + double d = c.value(); + + if ( static_cast(static_cast(d)) == d ) { + switch ( t->isWildcard() ? 64 : t->width() ) { + case 8: + if ( static_cast(int8_t(d)) == d ) + return ctor::SignedInteger(int64_t(d), 8, c.meta()); + break; + + case 16: + if ( static_cast(static_cast(d)) == d ) + return ctor::SignedInteger(static_cast(d), 16, c.meta()); + break; + + case 32: + if ( static_cast(static_cast(d)) == d ) + return ctor::SignedInteger(static_cast(d), 32, c.meta()); + break; + + case 64: return ctor::SignedInteger(static_cast(d), 64, c.meta()); break; + } + } + } + + if ( auto t = dst.tryAs() ) { + double d = c.value(); + + if ( static_cast(static_cast(d)) == d ) { + switch ( t->isWildcard() ? 64 : t->width() ) { + case 8: + if ( static_cast(static_cast(d)) == d ) + return ctor::UnsignedInteger(static_cast(d), 8, c.meta()); + break; + + case 16: + if ( static_cast(static_cast(d)) == d ) + return ctor::UnsignedInteger(uint64_t(d), 16, c.meta()); + break; + + case 32: + if ( static_cast(static_cast(d)) == d ) + return ctor::UnsignedInteger(static_cast(d), 32, c.meta()); + break; + + case 64: return ctor::UnsignedInteger(static_cast(d), 64, c.meta()); break; + } + } + } + + return {}; + } + + result_t operator()(const ctor::Set& c) { + if ( auto t = dst.tryAs() ) { + std::vector nexprs; + for ( const auto& e : c.value() ) { + if ( auto x = hilti::coerceExpression(e, t->elementType(), style) ) + nexprs.push_back(*x.coerced); + else + return {}; + } + return ctor::Set(t->elementType(), std::move(nexprs), c.meta()); + } + + return {}; + } + + result_t operator()(const ctor::SignedInteger& c) { + if ( auto t = dst.tryAs() ) { + if ( t->width() == 64 ) + return c; + + int64_t i = c.value(); + + if ( t->isWildcard() ) + return ctor::SignedInteger(i, c.width(), c.meta()); + + if ( auto [imin, imax] = util::signed_integer_range(t->width()); i >= imin && i <= imax ) + return ctor::SignedInteger(i, t->width(), c.meta()); + } + + if ( auto t = dst.tryAs(); t && c.value() >= 0 ) { + auto u = static_cast(c.value()); + + if ( t->isWildcard() ) + return ctor::UnsignedInteger(u, c.width(), c.meta()); + + if ( auto [zero, umax] = util::unsigned_integer_range(t->width()); u <= umax ) + return ctor::UnsignedInteger(u, t->width(), c.meta()); + } + + if ( auto t = dst.tryAs() ) { + if ( static_cast(static_cast(c.value())) == c.value() ) + return ctor::Real(static_cast(c.value())); + } + + if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) + return ctor::Bool(c.value() != 0, c.meta()); + + return {}; + } + + result_t operator()(const ctor::Vector& c) { + if ( auto t = dst.tryAs() ) { + std::vector nexprs; + for ( const auto& e : c.value() ) { + if ( auto x = hilti::coerceExpression(e, t->elementType(), style) ) + nexprs.push_back(*x.coerced); + else + return {}; + } + return ctor::Vector(t->elementType(), std::move(nexprs), c.meta()); + } + + return {}; + } + + result_t operator()(const ctor::UnsignedInteger& c) { + if ( auto t = dst.tryAs() ) { + if ( t->width() == 64 ) + return c; + + uint64_t u = c.value(); + + if ( t->isWildcard() ) + return ctor::UnsignedInteger(u, c.width(), c.meta()); + + if ( auto [umin, umax] = util::unsigned_integer_range(t->width()); u >= umin && u <= umax ) + return ctor::UnsignedInteger(u, t->width(), c.meta()); + } + + if ( auto t = dst.tryAs(); t && static_cast(c.value()) >= 0 ) { + auto i = static_cast(c.value()); + + if ( t->isWildcard() ) + return ctor::SignedInteger(i, c.width(), c.meta()); + + if ( auto [imin, imax] = util::signed_integer_range(t->width()); i >= imin && i <= imax ) + return ctor::SignedInteger(i, t->width(), c.meta()); + } + + if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) + return ctor::Bool(c.value() != 0, c.meta()); + + if ( auto t = dst.tryAs() ) { + if ( static_cast(static_cast(c.value())) == c.value() ) + return ctor::Real(static_cast(c.value())); + } + + return {}; + } + + result_t operator()(const ctor::Tuple& c) { + if ( auto t = dst.tryAs() ) { + auto vc = c.value(); + auto ve = t.value().elements(); + + if ( vc.size() != ve.size() ) + return {}; + + std::vector coerced; + coerced.reserve(vc.size()); + + for ( auto i = std::make_pair(vc.begin(), ve.begin()); i.first != vc.end(); ++i.first, ++i.second ) { + if ( auto x = + hilti::coerceExpression(*i.first, (*i.second).type(), CoercionStyle::TryAllForAssignment) ) { + coerced.push_back(*x.coerced); + } + else + return {}; + } + + return ctor::Tuple(std::move(coerced), c.meta()); + } + + return {}; + } + + result_t operator()(const ctor::Struct& c) { + auto dst_ = dst; + + if ( (dst.isA() || dst.isA()) && ! type::isReferenceType(dst) ) + // Allow coercion from value to reference type with new instance. + dst_ = dst.dereferencedType(); + + if ( auto dtype = dst_.tryAs() ) { + if ( ! dst_.typeID() ) + // Wait for this to be resolved. + return {}; + + auto stype = c.type().as(); + + std::set src_fields; + for ( const auto& f : stype.fields() ) + src_fields.insert(f.id()); + + std::set dst_fields; + for ( const auto& f : dtype->fields() ) + dst_fields.insert(f.id()); + + // Check for fields in ctor that type does not have. + if ( ! util::set_difference(src_fields, dst_fields).empty() ) + return {}; + + // Check for fields in type that ctor does not have, they must be + // optional, + auto x = util::set_difference(dst_fields, src_fields); + + std::set can_be_missing; + + for ( const auto& k : x ) { + auto f = dtype->field(k); + if ( f->isOptional() || f->default_() || f->type().isA() ) + can_be_missing.insert(k); + } + + x = util::set_difference(x, can_be_missing); + + if ( ! x.empty() ) + // Uninitialized fields. + return {}; + + // Coerce each field. + std::vector nf; + + for ( const auto& sf : stype.fields() ) { + const auto& df = dtype->field(sf.id()); + const auto& se = c.field(sf.id()); + assert(df && se); + if ( const auto& ne = hilti::coerceExpression(se->expression(), df->type(), style) ) + nf.emplace_back(sf.id(), *ne.coerced); + else + // Cannot coerce. + return {}; + } + + return ctor::Struct(std::move(nf), dst_, c.meta()); + } + + return {}; + } +}; + +struct VisitorType : public visitor::PreOrder, VisitorType> { + VisitorType(const Type& dst, bitmask style) : dst(dst), style(style) {} + + const Type& dst; + bitmask style; + + result_t operator()(const type::Enum& c) { + if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) + return dst; + + return {}; + } + + result_t operator()(const type::Interval& c) { + if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) + return dst; + + return {}; + } + + result_t operator()(const type::Null& c) { + if ( auto t = dst.tryAs() ) + return dst; + + if ( auto t = dst.tryAs() ) + return dst; + + if ( auto t = dst.tryAs() ) + return dst; + + return {}; + } + + result_t operator()(const type::Bytes& c) { + if ( dst.tryAs() && (style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall)) ) + return dst; + + return {}; + } + + result_t operator()(const type::Error& e) { + if ( auto t = dst.tryAs() ) + return dst; + + return {}; + } + + result_t operator()(const type::List& e) { + if ( auto t = dst.tryAs(); t && t->elementType() == e.elementType() ) + return dst; + + if ( auto t = dst.tryAs(); t && t->elementType() == e.elementType() ) + return dst; + + return {}; + } + + result_t operator()(const type::Optional& r) { + if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) + return dst; + + return {}; + } + + result_t operator()(const type::StrongReference& r) { + if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) + return dst; + + if ( type::isReferenceType(dst) ) { + if ( type::sameExceptForConstness(r.dereferencedType(), dst.dereferencedType()) ) + return dst; + } + + if ( ! (style & CoercionStyle::Assignment) ) { + if ( r.dereferencedType() == dst ) + return dst; + } + + return {}; + } + + result_t operator()(const type::Time& c) { + if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) + return dst; + + return {}; + } + + result_t operator()(const type::Result& r) { + if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) + return dst; + + if ( auto t = dst.tryAs(); t && t->dereferencedType() == r.dereferencedType() ) + return dst; + + return {}; + } + + result_t operator()(const type::SignedInteger& src) { + if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) + return dst; + + if ( auto t = dst.tryAs() ) { + if ( src.width() <= t->width() ) + return dst; + } + + return {}; + } + + result_t operator()(const type::Stream& c) { + if ( auto t = dst.tryAs() ) + return dst; + + return {}; + } + + result_t operator()(const type::stream::View& c) { + if ( dst.tryAs() && (style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall)) ) + return dst; + + return {}; + } + + result_t operator()(const type::Type_& src) { + if ( auto t = dst.tryAs() ) { + // We don't allow arbitrary coercions here, just (more or less) direct matches. + if ( auto x = hilti::coerceType(src.typeValue(), t->typeValue(), CoercionStyle::TryDirectForMatching) ) + return type::Type_(*x); + } + + return {}; + } + + result_t operator()(const type::Union& c) { + if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) + return dst; + + return {}; + } + + result_t operator()(const type::UnsignedInteger& src) { + if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) + return dst; + + if ( auto t = dst.tryAs() ) { + if ( src.width() <= t->width() ) + return dst; + } + + if ( auto t = dst.tryAs() ) { + // As long as the target type has more bits, we can coerce. + if ( src.width() < t->width() ) + return dst; + } + + return {}; + } + + result_t operator()(const type::Tuple& src) { + if ( auto t = dst.tryAs() ) { + auto vc = src.elements(); + auto ve = t->elements(); + + if ( vc.size() != ve.size() ) + return {}; + + for ( auto i = std::make_pair(vc.begin(), ve.begin()); i.first != vc.end(); ++i.first, ++i.second ) { + if ( auto x = hilti::coerceType((*i.first).type(), (*i.second).type()); ! x ) + return {}; + } + + return dst; + } + + return {}; + } + + result_t operator()(const type::ValueReference& r) { + if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) + return hilti::coerceType(r.dereferencedType(), dst, style); + + if ( type::isReferenceType(dst) ) { + if ( type::sameExceptForConstness(r.dereferencedType(), dst.dereferencedType()) ) + return dst; + } + + if ( r.dereferencedType() == dst ) + return dst; + + return {}; + } + + result_t operator()(const type::WeakReference& r) { + if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) + return dst; + + if ( type::isReferenceType(dst) ) { + if ( type::sameExceptForConstness(r.dereferencedType(), dst.dereferencedType()) ) + return dst; + } + + if ( ! (style & CoercionStyle::Assignment) ) { + if ( r.dereferencedType() == dst ) + return dst; + } + + return {}; + } +}; + +} // anonymous namespace + // Public version going through all plugins. Result hilti::coerceCtor(Ctor c, const Type& dst, bitmask style) { if ( c.type() == dst ) @@ -42,10 +593,7 @@ Result hilti::coerceCtor(Ctor c, const Type& dst, bitmask s return result::Error("could not coeerce type for constructor"); } -static Result _coerceParameterizedType(const Type& src_, const Type& dst_, bitmask style) { - auto src = type::effectiveType(src_); - Type dst = type::effectiveType(dst_); - +static Result _coerceParameterizedType(const Type& src, const Type& dst, bitmask style) { if ( src == dst ) return dst; @@ -72,18 +620,9 @@ static Result _coerceParameterizedType(const Type& src_, const Type& dst_, // of src == dst has been handled already, that usually does it.) return {}; - t1 = type::effectiveType(*t1); - t2 = type::effectiveType(*t2); - if ( t2->isWildcard() ) have_wildcard = true; - if ( const auto& orig = t1->originalNode(); (style & CoercionStyle::PreferOriginalType) && orig ) - t1 = orig->as(); - - if ( const auto& orig = t2->originalNode(); (style & CoercionStyle::PreferOriginalType) && orig ) - t2 = orig->as(); - if ( ! coerceType(*t1, *t2, style) ) return {}; } @@ -97,15 +636,19 @@ static Result _coerceParameterizedType(const Type& src_, const Type& dst_, return have_wildcard ? src : dst; } -static Result _coerceType(const Type& src_, const Type& dst_, bitmask style) { - auto src = type::effectiveType(src_); - Type dst = type::effectiveType(dst_); - +static Result _coerceType(const Type& src, const Type& dst, bitmask style) { // TODO(robin): Not sure if this should/must replicate all the type coercion // login in coerceExpression(). If so, we should factor that out. // Update: I believe the answer is yes ... Added a few more cases, but this will // likely need more work. + if ( src.typeID() && dst.typeID() ) { + if ( *src.typeID() == *dst.typeID() ) + return dst; + else + return result::Error("type IDs do not match"); + } + if ( src == dst ) return src; @@ -115,7 +658,7 @@ static Result _coerceType(const Type& src_, const Type& dst_, bitmaskdereferencedType(), style) ) + if ( auto x = coerceType(src, opt->dereferencedType(), style) ) return {type::Optional(*x, src.meta())}; } @@ -124,13 +667,13 @@ static Result _coerceType(const Type& src_, const Type& dst_, bitmaskdereferencedType(), style) ) + if ( auto x = coerceType(src, opt->dereferencedType(), style) ) return {type::Result(*x, src.meta())}; } if ( auto x = dst.tryAs(); x && ! type::isReferenceType(src) ) { // All types converts into a corresponding value_ref. - if ( auto y = coerceType(src_, x->dereferencedType(), style) ) + if ( auto y = coerceType(src, x->dereferencedType(), style) ) return {type::ValueReference(*x, src.meta())}; } } @@ -152,23 +695,13 @@ static Result _coerceType(const Type& src_, const Type& dst_, bitmask hilti::coerceType(const Type& src_, const Type& dst_, bitmask style) { - auto src = type::effectiveType(src_); - - if ( const auto& orig = src.originalNode(); (style & CoercionStyle::PreferOriginalType) && orig ) { - if ( auto nt = hilti::coerceType(type::effectiveType(orig->as()), dst_, style) ) - return *nt; - } - - return _coerceType(src_, dst_, style); +Result hilti::coerceType(const Type& src, const Type& dst, bitmask style) { + return _coerceType(src, dst, style); } std::string hilti::to_string(bitmask style) { std::vector labels; - if ( style & CoercionStyle::PreferOriginalType ) - labels.emplace_back("prefer-original-type"); - if ( style & CoercionStyle::TryExactMatch ) labels.emplace_back("try-exact-match"); @@ -196,12 +729,12 @@ std::string hilti::to_string(bitmask style) { return util::join(labels, ","); }; -Result>> hilti::coerceOperands(const std::vector& exprs, +Result>> hilti::coerceOperands(const node::Range& exprs, const std::vector& operands, bitmask style) { int num_type_changes = 0; bool changed = false; - std::vector transformed; + std::vector transformed; if ( exprs.size() > operands.size() ) return result::Error("more expressions than operands"); @@ -222,26 +755,24 @@ Result>> hilti::coerceOperands(const std continue; } - auto oat = operator_::type(op.type, exprs, transformed); + auto oat = operator_::type(op.type, exprs, node::Range(transformed.begin(), transformed.end())); if ( ! oat ) return result::Error("could not look up operand type"); auto result = coerceExpression(exprs[i], *oat, style); - - if ( result.coerced ) { - HILTI_DEBUG(logging::debug::Resolver, - util::fmt(" [param %d] matching %s against %s -> success: %s (coerced expression is %s) (%s)", - i, exprs[i].type(), *oat, result.coerced->type(), - (result.coerced->isConstant() ? "const" : "non-const"), - (result.consider_type_changed ? "type changed" : "type not changed"))); - } - else { - HILTI_DEBUG(logging::debug::Resolver, + if ( ! result ) { + HILTI_DEBUG(logging::debug::Operator, util::fmt(" [param %d] matching %s against %s -> failure", i, exprs[i].type(), *oat)); return result::Error("could not match coercion operands"); } + HILTI_DEBUG(logging::debug::Operator, + util::fmt(" [param %d] matching %s against %s -> success: %s (coerced expression is %s) (%s)", i, + exprs[i].type(), *oat, result.coerced->type(), + (result.coerced->isConstant() ? "const" : "non-const"), + (result.consider_type_changed ? "type changed" : "type not changed"))); + // We check if the primary type of the alternative has changed. Only // one operand must change its primary type for an alternative to // match. @@ -255,21 +786,23 @@ Result>> hilti::coerceOperands(const std changed = true; } - return std::make_pair(changed, std::move(transformed)); + std::vector x; + for ( const auto& n : transformed ) + x.push_back(n.as()); + + return std::make_pair(changed, std::move(x)); } -static CoercedExpression _coerceExpression(const Expression& e, const Type& src_, const Type& dst_, +static CoercedExpression _coerceExpression(const Expression& e, const Type& src, const Type& dst, bitmask style) { std::unique_ptr dbg_indent; if ( style & CoercionStyle::_Recursing ) - dbg_indent = std::make_unique(logging::debug::Resolver); + dbg_indent = std::make_unique(logging::debug::Operator); else style |= CoercionStyle::_Recursing; const auto no_change = CoercedExpression(e); - auto src = type::effectiveType(src_); - auto dst = type::effectiveType(dst_); CoercedExpression _result; int _line = 0; @@ -280,10 +813,32 @@ static CoercedExpression _coerceExpression(const Expression& e, const Type& src_ goto exit; \ } - if ( auto auto_ = dst_.tryAs() ) + if ( dst.isA() ) // Always accept, we're going to update the auto type later. RETURN(no_change); + if ( src.cxxID() && dst.cxxID() ) { + if ( *src.cxxID() == *dst.cxxID() ) { + RETURN(no_change); + } + } + + if ( src.typeID() && dst.typeID() ) { + if ( *src.typeID() == *dst.typeID() ) { + RETURN(no_change); + } + else { + RETURN(result::Error()); + } + } + + /* + * if ( ! dst.isA() ) { + * if ( ! (type::isResolved(src) && type::isResolved(dst)) ) + * RETURN(result::Error("types not resolved")); + * } + */ + if ( style & CoercionStyle::TryExactMatch ) { if ( src == dst ) { if ( e.isConstant() == type::isConstant(dst) ) @@ -349,7 +904,7 @@ static CoercedExpression _coerceExpression(const Expression& e, const Type& src_ // Make sure the expression remains a member expression, as we will // be expecting to cast it to that. if ( auto t = hilti::coerceType(x->type(), dst, style) ) { - RETURN(CoercedExpression(src_, expression::Member(x->id(), *t, x->meta()))); + RETURN(CoercedExpression(src, expression::Member(x->id(), *t, x->meta()))); } else RETURN(result::Error()); @@ -357,8 +912,8 @@ static CoercedExpression _coerceExpression(const Expression& e, const Type& src_ if ( auto o = dst.template tryAs() ) { // Match tuple against operands according to function call rules. - HILTI_DEBUG(logging::debug::Resolver, util::fmt("matching against call parameters")); - logging::DebugPushIndent _(logging::debug::Resolver); + HILTI_DEBUG(logging::debug::Operator, util::fmt("matching against call parameters")); + logging::DebugPushIndent _(logging::debug::Operator); auto c = e.template tryAs(); if ( ! c ) @@ -392,7 +947,7 @@ static CoercedExpression _coerceExpression(const Expression& e, const Type& src_ // All types converts into a corresponding optional. if ( auto x = coerceExpression(e, opt->dereferencedType(), style) ) - RETURN(CoercedExpression(src_, expression::Coerced(*x.coerced, dst, e.meta()))); + RETURN(CoercedExpression(src, expression::Coerced(*x.coerced, dst, e.meta()))); } if ( auto result = dst.tryAs() ) { @@ -401,33 +956,33 @@ static CoercedExpression _coerceExpression(const Expression& e, const Type& src_ // All types convert into a corresponding result. if ( auto x = coerceExpression(e, result->dereferencedType(), style) ) - RETURN(CoercedExpression(src_, expression::Coerced(*x.coerced, dst, e.meta()))); + RETURN(CoercedExpression(src, expression::Coerced(*x.coerced, dst, e.meta()))); } if ( auto x = dst.tryAs(); x && ! type::isReferenceType(src) ) { // All types converts into a corresponding value_ref. if ( auto y = coerceExpression(e, x->dereferencedType(), style) ) - RETURN(CoercedExpression(src_, expression::Coerced(*y.coerced, dst, e.meta()))); + RETURN(CoercedExpression(src, expression::Coerced(*y.coerced, dst, e.meta()))); } } if ( style & CoercionStyle::TryCoercion ) { if ( auto c = e.tryAs() ) { if ( auto nc = hilti::coerceCtor(c->ctor(), dst, style) ) - RETURN(CoercedExpression(src_, expression::Ctor(ctor::Coerced(c->ctor(), *nc, c->meta()), e.meta()))); + RETURN(CoercedExpression(src, expression::Ctor(ctor::Coerced(c->ctor(), *nc, c->meta()), e.meta()))); } if ( auto t = hilti::coerceType(src, dst, style) ) // We wrap the expression into a coercion even if the new type is // the same as *dst*. That way the overloader has a way to // recognize that the types aren't identical. - RETURN(CoercedExpression(src_, expression::Coerced(e, *t, e.meta()))); + RETURN(CoercedExpression(src, expression::Coerced(e, *t, e.meta()))); } _result = result::Error(); exit: - HILTI_DEBUG(logging::debug::Resolver, + HILTI_DEBUG(logging::debug::Operator, util::fmt("coercing %s %s (%s) to %s%s (%s) -> %s [%s] (%s) (#%d)", (e.isConstant() ? "const" : "non-const"), to_node(src), util::replace(src.typename_(), "hilti::type::", ""), @@ -443,18 +998,36 @@ static CoercedExpression _coerceExpression(const Expression& e, const Type& src_ return _result; } -CoercedExpression hilti::coerceExpression(const Expression& e, const Type& src_, const Type& dst_, +// Public version going through all plugins. +CoercedExpression hilti::coerceExpression(const Expression& e, const Type& src, const Type& dst, bitmask style) { - auto src = type::effectiveType(src_); - - if ( const auto& orig = src.originalNode(); (style & CoercionStyle::PreferOriginalType) && orig ) { - if ( auto nt = hilti::coerceExpression(e, type::effectiveType(orig->as()), dst_, style) ) - return nt; - } - - return _coerceExpression(e, src_, dst_, style); + return _coerceExpression(e, src, dst, style); } +// Public version going through all plugins. CoercedExpression hilti::coerceExpression(const Expression& e, const Type& dst, bitmask style) { return coerceExpression(e, e.type(), dst, style); } + + +// Plugin-specific version just kicking off the local visitor. +std::optional hilti::detail::coerceCtor(Ctor c, const Type& dst, bitmask style) { + if ( ! (type::isResolved(c.type()) && type::isResolved(dst)) ) + return {}; + + if ( auto nc = VisitorCtor(dst, style).dispatch(std::move(c)) ) + return *nc; + + return {}; +} + +// Plugin-specific version just kicking off the local visitor. +std::optional hilti::detail::coerceType(Type t, const Type& dst, bitmask style) { + if ( ! (type::isResolved(t) && type::isResolved(dst)) ) + return {}; + + if ( auto nt = VisitorType(dst, style).dispatch(std::move(t)) ) + return *nt; + + return {}; +} diff --git a/hilti/toolchain/src/compiler/context.cc b/hilti/toolchain/src/compiler/context.cc index 7eff25519..85f31995a 100644 --- a/hilti/toolchain/src/compiler/context.cc +++ b/hilti/toolchain/src/compiler/context.cc @@ -3,12 +3,14 @@ #include #include #include +#include using namespace hilti; using namespace hilti::context; namespace hilti::logging::debug { inline const DebugStream Compiler("compiler"); +inline const DebugStream AstCache("ast-cache"); } // namespace hilti::logging::debug Result Options::parseDebugAddl(const std::string& flags) { @@ -65,89 +67,118 @@ Context::Context(Options options) : _options(std::move(std::move(options))) { Context::~Context() { // We explicitly clear out the modules to break any reference cycles they // may contain. - for ( auto& m : _modules ) - m.first->as().clear(); + for ( auto& u : _unit_cache_by_id ) + u.second->unit = nullptr; + + for ( auto& u : _unit_cache_by_path ) + u.second->unit = nullptr; } -const CachedModule& Context::registerModule(const ModuleIndex& idx, Node&& module, bool requires_compilation) { - auto id = module.as().id(); - if ( _module_cache_by_id.find(id) != _module_cache_by_id.end() ) - logger().internalError(util::fmt("module '%s' registered more than once in context", id)); +void Context::cacheUnit(std::shared_ptr unit) { + auto entry = std::make_shared(unit); + auto idx = unit->cacheIndex(); + + auto i = _unit_cache_by_id.find(idx.id); + if ( i == _unit_cache_by_id.end() ) { + HILTI_DEBUG(logging::debug::Compiler, + util::fmt("registering %s AST for module %s (%s)", unit->extension(), idx.id, idx.path)); - HILTI_DEBUG(logging::debug::Compiler, util::fmt("registering AST for module %s (%s)", idx.id, idx.path)); + _unit_cache_by_id.insert({idx.id, entry}); - _modules.emplace_back(std::make_unique(std::move(module)), nullptr); - auto cached = std::make_shared(idx, NodeRef(*_modules.back().first)); - cached->requires_compilation = requires_compilation; - _modules.back().second = cached; - _module_cache_by_id.insert({idx.id, cached}); + if ( ! idx.path.empty() ) + _unit_cache_by_path.insert({idx.path, entry}); + } + else { + HILTI_DEBUG(logging::debug::Compiler, util::fmt("updating cached AST for module %s", unit->id())); + i->second->unit = unit; + } +} - if ( ! idx.path.empty() ) - _module_cache_by_path.insert({idx.path, cached}); +std::optional Context::lookupUnit(const ID& id, const hilti::rt::filesystem::path& extension) { + if ( auto x = _unit_cache_by_id.find(id); x != _unit_cache_by_id.end() ) { + if ( x->second->unit->extension() == extension ) + return *x->second; + } - return *cached; + return {}; } -void Context::updateModule(const CachedModule& module) { - assert(module.index.id); +std::optional Context::lookupUnit(const hilti::rt::filesystem::path& path, + std::optional ast_extension) { + if ( ! ast_extension ) + ast_extension = path.extension(); - auto i = _module_cache_by_id.find(module.index.id); - if ( i == _module_cache_by_id.end() ) - logger().internalError(util::fmt("module '%s' to update has not been registered", module.index.id)); + if ( auto x = _unit_cache_by_path.find(util::normalizePath(path)); x != _unit_cache_by_path.end() ) { + if ( x->second->unit->extension() == *ast_extension ) + return *x->second; + } - const auto& cached = i->second; + return {}; +} - if ( cached->node->identity() != module.node->identity() ) - logger().internalError("updating module with name of existing but different AST"); - *cached = module; +static void _dependencies(std::weak_ptr u, std::vector>* seen) { + auto unit = u.lock(); - std::string deps = "n/a"; - if ( cached->dependencies ) { - deps = util::join(util::transform(*cached->dependencies, [](const auto& idx) { return idx.id; }), ", "); - if ( deps.empty() ) - deps = "(none)"; + for ( const auto& d : *seen ) { + if ( d.lock().get() == unit.get() ) + return; } - std::string requires_compilation = (cached->requires_compilation ? "yes" : "no"); - std::string final = (cached->final ? "yes" : "no"); + seen->push_back(u); - HILTI_DEBUG(logging::debug::Compiler, - util::fmt("updated cached AST for module %s (final: %s, requires_compilation: %s, dependencies: %s)", - cached->index.id, final, requires_compilation, deps)); + for ( const auto& x : unit->dependencies() ) + _dependencies(x, seen); } -std::optional Context::lookupModule(const ID& id) { - if ( auto x = _module_cache_by_id.find(id); x != _module_cache_by_id.end() ) - return *x->second; - else +std::vector> Context::lookupDependenciesForUnit(const ID& id, + const hilti::rt::filesystem::path& extension) { + auto m = lookupUnit(id, extension); + if ( ! m ) return {}; -} -std::optional Context::lookupModule(const hilti::rt::filesystem::path& path) { - if ( auto x = _module_cache_by_path.find(util::normalizePath(path)); x != _module_cache_by_path.end() ) - return *x->second; - else - return {}; + std::vector> seen; + _dependencies(m->unit, &seen); + seen.erase(seen.begin()); // don't report entry point + return seen; } -std::vector Context::lookupDependenciesForModule(const ID& id) { - auto m = lookupModule(id); - if ( ! m ) - return {}; +void Context::dumpUnitCache(const hilti::logging::DebugStream& stream) { + HILTI_DEBUG(stream, "### Unit cache"); + HILTI_DEBUG(stream, ""); - if ( ! m->dependencies ) - return {}; + for ( const auto& x : _unit_cache_by_id ) { + auto idx = x.first; + auto unit = x.second->unit; + HILTI_DEBUG(stream, util::fmt("- %s -> %s %s [%p] [%p]", idx, unit->id(), unit->extension(), + unit->module().renderedRid(), unit.get())); + } - std::vector deps; + HILTI_DEBUG(stream, ""); - for ( const auto& x : *m->dependencies ) { - auto d = lookupModule(x.id); - if ( ! d ) - return {}; // Shouldn't really happen. Assert? + for ( const auto& x : _unit_cache_by_path ) { + auto idx = x.first; + auto unit = x.second->unit; + HILTI_DEBUG(stream, util::fmt("- %s -> %s %s [%p] [%p]", idx, unit->id(), unit->extension(), + unit->module().renderedRid(), unit.get())); + } + + HILTI_DEBUG(stream, ""); + + for ( const auto& x : _unit_cache_by_id ) { + auto unit = x.second->unit; + HILTI_DEBUG(stream, util::fmt("### %s %s [%p] [%p]", unit->id(), unit->extension(), + unit->module().renderedRid(), unit.get())); + + for ( const auto& d_ : unit->dependencies() ) { + auto d = d_.lock(); + HILTI_DEBUG(stream, util::fmt("### Dependency: %s %s [%p] [%p]", d->id(), d->extension(), + d->module().renderedRid(), d.get())); + } - deps.push_back(*d); + hilti::render(stream, unit->module(), true); + HILTI_DEBUG(stream, ""); } - return deps; + HILTI_DEBUG(stream, ""); } diff --git a/hilti/toolchain/src/compiler/cxx/elements.cc b/hilti/toolchain/src/compiler/cxx/elements.cc index 3c9da4b58..ae77ed0d1 100644 --- a/hilti/toolchain/src/compiler/cxx/elements.cc +++ b/hilti/toolchain/src/compiler/cxx/elements.cc @@ -502,7 +502,7 @@ std::string cxx::type::Union::str() const { visitor_calls.emplace_back(fmt("_(\"%s\", std::get_if<%d>(&this->value)); ", decl.id, idx + 1)); } - auto base = fmt("hilti::rt::Union<%s>", util::join(types, ", ")); + auto base = fmt("::hilti::rt::Union<%s>", util::join(types, ", ")); auto header = fmt(" using %s::Union;", base); auto visit = fmt(" template void __visit(F _) const { %s}", util::join(visitor_calls, "")); return fmt("struct %s : public %s {\n%s\n%s\n}", type_name, base, header, visit); diff --git a/hilti/toolchain/src/compiler/cxx/formatter.cc b/hilti/toolchain/src/compiler/cxx/formatter.cc index e80c2df10..eb1f45c7d 100644 --- a/hilti/toolchain/src/compiler/cxx/formatter.cc +++ b/hilti/toolchain/src/compiler/cxx/formatter.cc @@ -13,7 +13,7 @@ void Formatter::pushNamespace(std::string relative_ns) { f.separator(); if ( util::startsWith(relative_ns, "::") ) - relative_ns = relative_ns.substr(3); + relative_ns = relative_ns.substr(2); if ( util::endsWith(relative_ns, "::") ) { assert(relative_ns != "::"); diff --git a/hilti/toolchain/src/compiler/cxx/linker.cc b/hilti/toolchain/src/compiler/cxx/linker.cc index 2fe38f8a0..4c586c6b5 100644 --- a/hilti/toolchain/src/compiler/cxx/linker.cc +++ b/hilti/toolchain/src/compiler/cxx/linker.cc @@ -53,7 +53,6 @@ void cxx::Linker::finalize() { .debug = _codegen->context()->options().debug, .optimize = _codegen->context()->options().optimize}; - unit.add(cxx::declaration::IncludeFile{"hilti/rt/libhilti.h"}); for ( const auto& p : plugin::registry().plugins() ) for ( const auto& i : p.cxx_includes ) unit.add(cxx::declaration::IncludeFile{i}); diff --git a/hilti/toolchain/src/compiler/cxx/unit.cc b/hilti/toolchain/src/compiler/cxx/unit.cc index 6ac313211..121cd6d96 100644 --- a/hilti/toolchain/src/compiler/cxx/unit.cc +++ b/hilti/toolchain/src/compiler/cxx/unit.cc @@ -167,17 +167,17 @@ void Unit::_addModuleInitFunction() { }; if ( _init_globals ) - addInitFunction(_context.get(), _init_globals, "__init_globals"); + addInitFunction(context().get(), _init_globals, "__init_globals"); if ( _init_module ) - addInitFunction(_context.get(), _init_module, "__init_module"); + addInitFunction(context().get(), _init_module, "__init_module"); if ( _preinit_module ) - addInitFunction(_context.get(), _preinit_module, "__preinit_module"); + addInitFunction(context().get(), _preinit_module, "__preinit_module"); if ( moduleID() != cxx::ID("__linker__") ) { cxx::Block register_; - register_.addStatement(fmt("hilti::rt::detail::registerModule({ \"%s\", %s, %s, %s})", moduleID(), + register_.addStatement(fmt("::hilti::rt::detail::registerModule({ \"%s\", %s, %s, %s})", moduleID(), _init_module ? "&__init_module" : "nullptr", _uses_globals ? "&__init_globals" : "nullptr", _uses_globals ? "&__globals_index" : "nullptr")); @@ -185,7 +185,7 @@ void Unit::_addModuleInitFunction() { if ( _preinit_module ) register_.addStatement(fmt("__preinit_module()")); - auto id = addInitFunction(_context.get(), register_, "__register_module"); + auto id = addInitFunction(context().get(), register_, "__register_module"); add(fmt("HILTI_PRE_INIT(%s)", id)); } } @@ -228,7 +228,9 @@ void Unit::_generateCode(Formatter& f, bool prototypes_only) { // Write out those types first that we have in _types_in_order. for ( const auto& id : _types_in_order ) { auto i = _types.find(id); - assert(i != _types.end()); + if ( i != _types.end() ) + continue; + auto& t = i->second; if ( t.id.namespace_() == ns && ! t.forward_decl ) f << t; @@ -412,7 +414,7 @@ void Unit::importDeclarations(const Unit& other) { } hilti::detail::cxx::ID Unit::cxxNamespace() const { - return cxx::ID(_context->options().cxx_namespace_intern, moduleID()); + return cxx::ID(context()->options().cxx_namespace_intern, moduleID()); } hilti::Result Unit::linkerMetaData() const { diff --git a/hilti/toolchain/src/compiler/driver.cc b/hilti/toolchain/src/compiler/driver.cc index babbca21a..445c273a6 100644 --- a/hilti/toolchain/src/compiler/driver.cc +++ b/hilti/toolchain/src/compiler/driver.cc @@ -11,13 +11,26 @@ #include #include +#include +#include +#include +#include #include -#include +#include using namespace hilti; using util::fmt; namespace hilti::logging::debug { +inline const DebugStream AstCache("ast-cache"); +inline const DebugStream AstCodegen("ast-codegen"); +inline const DebugStream AstDeclarations("ast-declarations"); +inline const DebugStream AstDumpIterations("ast-dump-iterations"); +inline const DebugStream AstFinal("ast-final"); +inline const DebugStream AstOrig("ast-orig"); +inline const DebugStream AstPrintTransformed("ast-print-transformed"); +inline const DebugStream AstResolved("ast-resolved"); +inline const DebugStream AstTransformed("ast-transformed"); inline const DebugStream Compiler("compiler"); inline const DebugStream Driver("driver"); } // namespace hilti::logging::debug @@ -47,6 +60,13 @@ static struct option long_driver_options[] = {{"abort-on-exceptions", required_a {"version", no_argument, nullptr, 'v'}, {nullptr, 0, nullptr, 0}}; +static auto pluginForUnit(const std::shared_ptr& u) { + auto p = plugin::registry().pluginForExtension(u->extension()); + if ( ! p ) + logger().internalError(util::fmt("no plugin for unit extension %s: %s", u->extension(), p.error())); + + return *p; +} Driver::Driver(std::string name) : _name(std::move(name)) { configuration().initLocation(false); } @@ -203,9 +223,9 @@ Result Driver::writeToTemp(std::ifstream& in, const void Driver::dumpUnit(const Unit& unit) { if ( unit.isCompiledHILTI() ) { - auto output_path = util::fmt("dbg.%s.hlt", unit.id()); + auto output_path = util::fmt("dbg.%s%s", unit.id(), unit.extension().native()); if ( auto out = openOutput(output_path) ) { - HILTI_DEBUG(logging::debug::Driver, fmt("saving HILTI code for module %s to %s", unit.id(), output_path)); + HILTI_DEBUG(logging::debug::Driver, fmt("saving code for module %s to %s", unit.id(), output_path)); unit.print(*out); } } @@ -303,17 +323,17 @@ Result Driver::parseOptions(int argc, char** argv) { ++num_output_types; break; - case 'g': { - _driver_options.global_optimizations = false; - break; - } - case 'j': _driver_options.execute_code = true; _driver_options.include_linker = true; ++num_output_types; break; + case 'g': { + _driver_options.global_optimizations = false; + break; + } + case 'l': _driver_options.output_linker = true; _driver_options.include_linker = true; @@ -421,20 +441,22 @@ void Driver::setDriverOptions(driver::Options options) { _driver_options = std::move(options); } -void Driver::_addUnit(Unit unit) { - if ( _processed_units.find(unit.id()) != _processed_units.end() ) +void Driver::_addUnit(std::shared_ptr unit) { + if ( _processed_units.find(unit->id()) != _processed_units.end() ) return; - if ( (! unit.path().empty()) && _processed_paths.find(unit.path()) != _processed_paths.end() ) + if ( ! unit->path().empty() && _processed_paths.find(unit->path()) != _processed_paths.end() ) return; - _processed_units.insert(unit.id()); + _processed_units.insert(unit->id()); - if ( (! unit.path().empty()) ) - _processed_paths.insert(unit.path()); + if ( ! unit->path().empty() ) + _processed_paths.insert(unit->path()); - hookNewASTPreCompilation(unit.id(), unit.path(), unit.module()); - _pending_units.emplace_back(std::move(unit)); + if ( std::find(_pending_units.begin(), _pending_units.end(), unit) == _pending_units.end() ) + _pending_units.push_back(unit); + + hookNewASTPreCompilation(unit); } Result Driver::_symbol(const std::string& symbol) { @@ -456,7 +478,7 @@ Result Driver::_symbol(const std::string& symbol) { } Result Driver::addInput(const hilti::rt::filesystem::path& path) { - if ( path.empty() || _processed_paths.find(path) != _processed_paths.end() ) + if ( _processed_paths.find(path) != _processed_paths.end() ) return Nothing(); // Calling hook before stage check so that it can execute initialize() @@ -473,7 +495,7 @@ Result Driver::addInput(const hilti::rt::filesystem::path& path) { HILTI_DEBUG(logging::debug::Driver, fmt("adding source file %s", path)); if ( auto unit = Unit::fromCache(_ctx, path) ) { - HILTI_DEBUG(logging::debug::Driver, fmt("reusing previously cached module %s", unit->id())); + HILTI_DEBUG(logging::debug::Driver, fmt("reusing previously cached module %s", (*unit)->id())); _addUnit(std::move(*unit)); } else { @@ -497,9 +519,10 @@ Result Driver::addInput(const hilti::rt::filesystem::path& path) { // always include when emitting C++. std::fstream file(path); auto [_, md] = Unit::readLinkerMetaData(file, path); - if ( md ) + if ( md ) { return result::Error( "Loading generated C++ files is not supported with transformations enabled, rerun with '-g'"); + } } HILTI_DEBUG(logging::debug::Driver, fmt("adding external C++ file %s", path)); @@ -526,16 +549,16 @@ Result Driver::addInput(const hilti::rt::filesystem::path& path) { return error("unsupported file type", path); } -Result Driver::addInput(hilti::Module&& module, const hilti::rt::filesystem::path& path) { - if ( _processed_units.find(module.id()) != _processed_units.end() ) +Result Driver::addInput(std::shared_ptr u) { + if ( _processed_units.find(u->id()) != _processed_units.end() ) return Nothing(); - if ( (! path.empty()) && _processed_paths.find(path) != _processed_paths.end() ) + if ( ! u->path().empty() && _processed_paths.find(u->path()) != _processed_paths.end() ) return Nothing(); // Calling hook before stage check so that it can execute initialize() // just in time if it so desires. - hookAddInput(module, path); + hookAddInput(u); if ( _stage == Stage::UNINITIALIZED ) logger().internalError(" driver must be initialized before inputs can be added"); @@ -543,65 +566,236 @@ Result Driver::addInput(hilti::Module&& module, const hilti::rt::filesy if ( _stage != Stage::INITIALIZED ) logger().internalError("no further inputs can be added after compilation has finished already"); - HILTI_DEBUG(logging::debug::Driver, fmt("adding source AST %s", module.id())); - auto unit = Unit::fromModule(_ctx, std::move(module), path); - if ( ! unit ) - return augmentError(unit.error()); - - _addUnit(std::move(*unit)); + _addUnit(std::move(u)); return Nothing(); } -Result Driver::_compileUnit(Unit unit) { +Result Driver::_resolveUnitsWithPlugin(const Plugin& plugin, std::vector> units, + int& round) { + HILTI_DEBUG(logging::debug::Compiler, + fmt("resolving units with plugin %s: %s", plugin.component, + util::join(util::transform(units, [](const auto& u) { return u->id(); }), ", "))); + logging::DebugPushIndent _(logging::debug::Compiler); - HILTI_DEBUG(logging::debug::Driver, fmt("compiling input unit %s", unit.id())); + for ( const auto& u : units ) { + // Double-check that we only get units for the provided plugin. + assert(u->extension() == plugin.extension); + _dumpAST(u, logging::debug::AstOrig, plugin, "Original AST", 0); + _saveIterationAST(u, plugin, "AST before first iteration", 0); + } - if ( auto x = unit.compile(); ! x ) - // Specific errors have already been reported. - return error("aborting after errors"); + int extra_rounds = 0; // set to >0 for debugging - hookNewASTPostCompilation(unit.id(), unit.path(), unit.module()); + while ( true ) { + HILTI_DEBUG(logging::debug::Compiler, fmt("processing ASTs, round %d", round)); + logging::DebugPushIndent _(logging::debug::Compiler); - if ( _driver_options.execute_code && ! _driver_options.skip_dependencies ) { - for ( const auto& d : unit.allImported(true) ) { - // Compile any implicit dependencies as well. Note that once we run - // the completion hook, that may compile further modules and hence in - // turn add more dependencies. - HILTI_DEBUG(logging::debug::Compiler, fmt("imported module %s needs compilation", d.id)); + bool modified = false; + std::vector> dependencies; + + for ( auto&& u : units ) + u->resetAST(); + + for ( auto&& u : units ) { + auto rc = u->buildASTScopes(plugin); + if ( ! rc ) + return rc.error(); + } - if ( auto rc = addInput(d.path); ! rc ) + for ( auto&& u : units ) { + auto rc = u->resolveAST(plugin); + if ( ! rc ) return rc.error(); + + for ( const auto& d : u->dependencies() ) { + if ( std::find(dependencies.begin(), dependencies.end(), d.lock()) == dependencies.end() ) + dependencies.push_back(d.lock()); + } + + _dumpAST(u, logging::debug::AstResolved, plugin, "AST after resolving", round); + _saveIterationAST(u, plugin, "AST after resolving", round); + modified = modified || (*rc == Unit::Modified); + } + + // Check for newly encountered dependencies that we need to compile as well. + for ( const auto& d : dependencies ) { + if ( d->isResolved() ) + continue; + + if ( std::find(units.begin(), units.end(), d) == units.end() ) { + HILTI_DEBUG(logging::debug::Compiler, + fmt("new dependency to process: %s (%s)", d->id(), d->extension())); + units.push_back(d); + modified = true; + } + } + + if ( ! modified && extra_rounds-- == 0 ) + break; + + if ( ++round >= 50 ) + logger().internalError("hilti::Unit::compile() didn't terminate, AST keeps changing"); + } + + for ( const auto& u : units ) { + _dumpAST(u, logging::debug::AstFinal, plugin, "Final AST", round); + _dumpDeclarations(u, plugin); + _saveIterationAST(u, plugin, "Final AST", round); + + if ( _driver_options.dump_code ) + dumpUnit(*u); // may be overwritten again later after optimization + } + + if ( ! options().skip_validation ) { + bool have_errors = false; + for ( const auto& u : units ) { + if ( ! u->validateAST(plugin) ) + have_errors = true; + } + + if ( have_errors || logger().errors() ) + return result::Error("aborting after errors"); + } + + for ( const auto& u : units ) { + HILTI_DEBUG(logging::debug::Compiler, fmt("finalized module %s", u->id())); + u->setResolved(true); + + if ( u->dependencies().size() ) { + logging::DebugPushIndent _(logging::debug::Compiler); + HILTI_DEBUG(logging::debug::Compiler, + fmt("dependencies: %s", + util::join(util::transform(u->dependencies(), [](const auto& u) { return u.lock()->id(); }), + ", "))); + } + + hookNewASTPostCompilation(u); + } + + if ( auto rc = hookCompilationFinished(plugin); ! rc ) + return augmentError(rc.error()); + + if ( _driver_options.execute_code && ! _driver_options.skip_dependencies ) { + // Compile any implicit dependencies as well. + for ( const auto& unit : units ) { + for ( const auto& d : unit->dependencies() ) { + if ( auto rc = addInput(d.lock()); ! rc ) + return rc.error(); + } } } - _hlts.push_back(std::move(unit)); return Nothing(); } -Result Driver::compileUnits() { +Result Driver::_transformUnitsWithPlugin(const Plugin& plugin, std::vector> units) { + if ( ! plugin.ast_transform ) + return Nothing(); + + HILTI_DEBUG(logging::debug::Compiler, + fmt("transforming units with plugin %s: %s", plugin.component, + util::join(util::transform(units, [](const auto& u) { return u->id(); }), ", "))); + + logging::DebugPushIndent _(logging::debug::Compiler); + + for ( const auto& unit : units ) { + if ( auto rc = unit->transformAST(plugin); ! rc ) + return rc; + + unit->setResolved(false); + context()->cacheUnit(unit); + + _dumpAST(unit, logging::debug::AstTransformed, plugin, "Transformed AST", 0); + _saveIterationAST(unit, plugin, "Transformed AST", 0); + + if ( logger().isEnabled(logging::debug::AstPrintTransformed) ) + hilti::print(std::cout, *unit->moduleRef()); + + if ( logger().errors() ) + return result::Error("aborting after errors"); + } + + return Nothing(); +} + +/** + * Filters a set of units for those associated with a specified plugin + * extension. For those matching, includes all their dependencies for the same + * extension as well. One can choose to have only fully resolved units + * considered. + */ +static auto _unitsForPlugin(const std::vector>& units, std::string extension, + bool include_resolved) { + auto cmp = [](const auto& u1, const auto& u2) { return u1->id().str() < u2->id().str(); }; + std::set, decltype(cmp)> nunits(cmp); + + for ( auto&& u : units ) { + if ( u->extension() == extension && (! u->isResolved() || include_resolved) ) { + nunits.insert(std::move(u)); + + for ( const auto& d_ : u->dependencies() ) { + auto d = d_.lock(); + if ( d->extension() == extension && (! d->isResolved() || include_resolved) ) + nunits.insert(d); + } + } + } + + std::vector> nunits_vec; + nunits_vec.reserve(nunits.size()); + for ( auto&& u : nunits ) + nunits_vec.push_back(u); + + return nunits_vec; +} + +Result Driver::_resolveUnits() { if ( _stage != Stage::INITIALIZED ) logger().internalError("unexpected driver stage in compileUnits()"); - while ( _pending_units.size() ) { - auto pending_units = std::move(_pending_units); - _pending_units.clear(); - - for ( auto&& unit : pending_units ) { - if ( auto rc = _compileUnit(std::move(unit)); ! rc ) + int round = 0; + + auto plugin = plugin::registry().plugins().begin(); + while ( plugin != plugin::registry().plugins().end() ) { + // Get remaining units that are relevant for the current plugin. Note + // that the list of pending units may change during this loop if more + // input files get added. If that happens, we will process any new ones + // that are not associated with plugins that we have already finish + // with. + if ( auto units = _unitsForPlugin(_pending_units, plugin->extension, false); units.size() ) { + if ( auto rc = _resolveUnitsWithPlugin(*plugin, units, round); ! rc ) return rc; } + else { + // All done, switch to next plugin, but first perform any pending + // transformations. + auto all_units = _unitsForPlugin(_pending_units, plugin->extension, true); + if ( auto rc = _transformUnitsWithPlugin(*plugin, all_units); ! rc ) + return rc; - if ( auto rc = hookCompilationFinished(); ! rc ) - return augmentError(rc.error()); + ++plugin; + + context()->dumpUnitCache(logging::debug::AstCache); + } } - _stage = Stage::COMPILED; + for ( const auto& unit : _pending_units ) { + // We should have only fully resolved HILTI modules now. + if ( unit->extension() != ".hlt" ) + return result::Error(fmt("module %s was not compiled down to HILTI", unit->id())); + + if ( ! unit->isResolved() ) + return result::Error(fmt("module %s was not marked as resolved", unit->id())); + _hlts.push_back(unit); + } + + _stage = Stage::COMPILED; return Nothing(); } -Result Driver::codegenUnits() { +Result Driver::_codegenUnits() { if ( _stage != Stage::COMPILED ) logger().internalError("unexpected driver stage in codegenUnits()"); @@ -609,23 +803,66 @@ Result Driver::codegenUnits() { // No need to kick off code generation. return Nothing(); - HILTI_DEBUG(logging::debug::Driver, "compiling modules to C++"); - logging::DebugPushIndent _(logging::debug::Driver); + logging::DebugPushIndent _(logging::debug::Compiler); for ( auto& unit : _hlts ) { - HILTI_DEBUG(logging::debug::Driver, fmt("codegen for input unit %s", unit.id())); + HILTI_DEBUG(logging::debug::Driver, fmt("codegen for input unit %s", unit->id())); + + _dumpAST(unit, logging::debug::AstCodegen, "Before C++ codegen"); - if ( auto rc = unit.codegen(); ! rc ) + if ( auto rc = unit->codegen(); ! rc ) return augmentError(rc.error()); - if ( auto md = unit.linkerMetaData() ) + if ( auto md = unit->linkerMetaData() ) _mds.push_back(*md); if ( _driver_options.dump_code ) - dumpUnit(unit); + dumpUnit(*unit); } _stage = Stage::CODEGENED; + return Nothing(); +} + +Result Driver::_optimizeUnits() { + if ( ! _driver_options.global_optimizations ) + return Nothing(); + + HILTI_DEBUG(logging::debug::Driver, "performing global transformations"); + + Optimizer opt(_hlts, _ctx); + opt.run(); + + return Nothing(); +} + +Result Driver::compileUnits() { + if ( auto rc = _resolveUnits(); ! rc ) + return error(rc.error()); + + if ( auto rc = _optimizeUnits(); ! rc ) + return rc; + + if ( _driver_options.output_hilti ) { + std::string output_path = (_driver_options.output_path.empty() ? "/dev/stdout" : _driver_options.output_path); + auto output = openOutput(output_path, false); + if ( ! output ) + return error(output.error()); + + for ( auto& unit : _hlts ) { + if ( ! unit->isCompiledHILTI() ) + continue; + + HILTI_DEBUG(logging::debug::Driver, util::fmt("saving HILTI code for module %s", unit->id())); + if ( ! unit->print(*output) ) + return error(fmt("error print HILTI code for module %s", unit->id())); + } + + return Nothing(); + } + + if ( auto rc = _codegenUnits(); ! rc ) + return error(rc.error()); return Nothing(); } @@ -635,11 +872,11 @@ Result Driver::run() { for ( const auto& i : _driver_options.inputs ) { if ( auto rc = addInput(i); ! rc ) - return rc.error(); + return rc; } if ( auto x = compile(); ! x ) - return x; + return x.error(); if ( ! _driver_options.execute_code || ! _driver_options.output_path.empty() ) return Nothing(); @@ -662,63 +899,29 @@ Result Driver::run() { return result::Error(fmt("uncaught exception of type %s: %s", util::demangle(typeid(e).name()), e.what())); } - return Nothing(); -} - -Result Driver::transformUnits() { - if ( ! _driver_options.global_optimizations ) - return Nothing(); + _ctx = nullptr; - HILTI_DEBUG(logging::debug::Driver, "performing global transformations"); - - Optimizer opt(&_hlts, _ctx); - - opt.run(); - - - return Nothing(); + return {}; } Result Driver::compile() { if ( auto rc = compileUnits(); ! rc ) return rc; - if ( auto rc = transformUnits(); ! rc ) - return rc; - - if ( _driver_options.output_hilti ) { - std::string output_path = (_driver_options.output_path.empty() ? "/dev/stdout" : _driver_options.output_path); - auto output = openOutput(output_path, false); - if ( ! output ) - return output.error(); - - for ( auto& unit : _hlts ) { - if ( ! unit.isCompiledHILTI() ) - continue; - - HILTI_DEBUG(logging::debug::Driver, util::fmt("saving HILTI code for module %s", unit.id())); - if ( ! unit.print(*output) ) - return error(fmt("error print HILTI code for module %s", unit.id())); - } - } - - if ( auto rc = codegenUnits(); ! rc ) - return rc; - if ( _driver_options.include_linker ) { if ( auto rc = linkUnits(); ! rc ) - return rc; + return error(rc.error()); } if ( _driver_options.output_hilti ) return Nothing(); if ( auto rc = outputUnits(); ! rc ) - return rc; + return error(rc.error()); if ( _driver_options.execute_code && ! _driver_options.output_prototypes ) { if ( auto rc = jitUnits(); ! rc ) - return rc; + return error(rc.error()); if ( _driver_options.output_path.empty() ) { // OK if not available. @@ -741,12 +944,15 @@ Result Driver::compile() { } } + _pending_units.clear(); + _hlts.clear(); + return Nothing(); } Result Driver::linkUnits() { if ( _stage != Stage::CODEGENED ) - logger().internalError("unexpected driver stage in linkUnits()"); + logger().internalError("unexpected driver stage in linkModule()"); _stage = Stage::LINKED; @@ -775,7 +981,6 @@ Result Driver::linkUnits() { } auto linker_unit = Unit::link(_ctx, _mds); - if ( ! linker_unit ) return error("aborting after linker errors"); @@ -787,15 +992,15 @@ Result Driver::linkUnits() { return output.error(); HILTI_DEBUG(logging::debug::Driver, fmt("writing linker code to %s", output_path)); - linker_unit->cxxCode()->save(*output); + (*linker_unit)->cxxCode()->save(*output); return Nothing(); // All done. } if ( _driver_options.dump_code ) - dumpUnit(*linker_unit); + dumpUnit(**linker_unit); - if ( linker_unit->cxxCode()->code() && linker_unit->cxxCode()->code()->size() ) - _hlts.push_back(std::move(*linker_unit)); + if ( (*linker_unit)->cxxCode()->code() && (*linker_unit)->cxxCode()->code()->size() ) + _hlts.push_back(*linker_unit); return Nothing(); } @@ -808,7 +1013,7 @@ Result Driver::outputUnits() { bool append = false; for ( auto& unit : _hlts ) { - if ( auto cxx = unit.cxxCode() ) { + if ( auto cxx = unit->cxxCode() ) { if ( _driver_options.output_cxx ) { auto cxx_path = output_path; @@ -827,7 +1032,7 @@ Result Driver::outputUnits() { if ( ! output ) return output.error(); - HILTI_DEBUG(logging::debug::Driver, fmt("saving C++ code for module %s to %s", unit.id(), cxx_path)); + HILTI_DEBUG(logging::debug::Driver, fmt("saving C++ code for module %s to %s", unit->id(), cxx_path)); cxx->save(*output); } @@ -837,14 +1042,20 @@ Result Driver::outputUnits() { return output.error(); HILTI_DEBUG(logging::debug::Driver, - fmt("saving C++ prototypes for module %s to %s", unit.id(), output_path)); - unit.createPrototypes(*output); + fmt("saving C++ prototypes for module %s to %s", unit->id(), output_path)); + unit->createPrototypes(*output); } if ( _driver_options.output_dependencies != driver::Dependencies::None ) { - for ( const auto& [id, path] : - unit.allImported(_driver_options.output_dependencies == driver::Dependencies::Code) ) - std::cout << fmt("%s (%s)\n", id, util::normalizePath(path).native()); + const bool code_only = (_driver_options.output_dependencies == driver::Dependencies::Code); + + for ( const auto& unit_ : context()->lookupDependenciesForUnit(unit->id(), unit->extension()) ) { + auto unit = unit_.lock(); + if ( code_only && ! unit->requiresCompilation() ) + continue; + + std::cout << fmt("%s (%s)\n", unit->id(), util::normalizePath(unit->path()).native()); + } } _generated_cxxs.push_back(std::move(*cxx)); @@ -854,7 +1065,7 @@ Result Driver::outputUnits() { append = _driver_options.output_cxx_prefix.empty(); } else - return error(fmt("error retrieving C++ code for module %s", unit.id())); + return error(fmt("error retrieving C++ code for module %s", unit->id())); } return Nothing(); @@ -983,3 +1194,69 @@ Result Driver::finishRuntime() { return Nothing(); } + +void Driver::_dumpAST(std::shared_ptr unit, const logging::DebugStream& stream, const Plugin& plugin, + const std::string& prefix, int round) { + if ( ! logger().isEnabled(stream) ) + return; + + std::string r; + + if ( round > 0 ) + r = fmt(" (round %d)", round); + + HILTI_DEBUG(stream, fmt("# [%s] %s: %s%s", pluginForUnit(unit).get().component, unit->id(), prefix, r)); + detail::renderNode(*unit->moduleRef(), stream, true); +} + +void Driver::_dumpAST(std::shared_ptr unit, std::ostream& stream, const Plugin& plugin, const std::string& prefix, + int round) { + std::string r; + + if ( round > 0 ) + r = fmt(" (round %d)", round); + + stream << fmt("# [%s] %s: %s%s\n", pluginForUnit(unit).get().component, unit->id(), prefix, r); + detail::renderNode(unit->module(), stream, true); +} + +void Driver::_dumpAST(std::shared_ptr unit, const logging::DebugStream& stream, const std::string& prefix) { + HILTI_DEBUG(stream, fmt("# %s: %s\n", unit->id(), prefix)); + detail::renderNode(unit->module(), stream, true); +} + +void Driver::_dumpDeclarations(std::shared_ptr unit, const Plugin& plugin) { + if ( ! logger().isEnabled(logging::debug::AstDeclarations) ) + return; + + logger().debugSetIndent(logging::debug::AstDeclarations, 0); + HILTI_DEBUG(logging::debug::AstDeclarations, fmt("# [%s] %s", pluginForUnit(unit).get().component, unit->id())); + + for ( const auto i : visitor::PreOrder<>().walk(unit->module()) ) { + auto decl = i.node.tryAs(); + if ( ! decl ) + continue; + + logger().debugSetIndent(logging::debug::AstDeclarations, i.path.size() - 1); + HILTI_DEBUG(logging::debug::AstDeclarations, + fmt("- %s \"%s\" (%s)", ID(i.node.typename_()).local(), decl->id(), decl->canonicalID())); + } +} + +void Driver::_saveIterationAST(std::shared_ptr unit, const Plugin& plugin, const std::string& prefix, + int round = 0) { + if ( ! logger().isEnabled(logging::debug::AstDumpIterations) ) + return; + + std::ofstream out(fmt("ast-%s-%d.tmp", plugin.component, round)); + _dumpAST(unit, out, plugin, prefix, round); +} + +void Driver::_saveIterationAST(std::shared_ptr unit, const Plugin& plugin, const std::string& prefix, + std::string tag) { + if ( ! logger().isEnabled(logging::debug::AstDumpIterations) ) + return; + + std::ofstream out(fmt("ast-%s-%s.tmp", plugin.component, tag)); + _dumpAST(unit, out, plugin, prefix, 0); +} diff --git a/hilti/toolchain/src/compiler/optimizer.cc b/hilti/toolchain/src/compiler/optimizer.cc index 9ee1dafa6..65c57e4ee 100644 --- a/hilti/toolchain/src/compiler/optimizer.cc +++ b/hilti/toolchain/src/compiler/optimizer.cc @@ -36,16 +36,6 @@ inline const DebugStream Optimizer("optimizer"); inline const DebugStream OptimizerCollect("optimizer-collect"); } // namespace logging::debug -template -void replaceNode(Position& p, Node replacement) { - p.node = std::move(replacement); -} - -template -static void removeNode(Position& p) { - replaceNode(p, node::none); -} - // Helper function to extract innermost type, removing any wrapping in reference or container types. Type innermostType(Type type) { if ( type::isReferenceType(type) ) @@ -61,6 +51,19 @@ class OptimizerVisitor { public: enum class Stage { COLLECT, PRUNE_USES, PRUNE_DECLS }; Stage _stage = Stage::COLLECT; + Module* _current_module = nullptr; + + template + void replaceNode(Position& p, Node replacement) { + assert(_current_module); + _current_module->preserve(p.node); + p.node = std::move(replacement); + } + + template + void removeNode(Position& p) { + replaceNode(p, node::none); + } virtual ~OptimizerVisitor() = default; virtual void collect(Node&) {} @@ -84,8 +87,7 @@ struct FunctionVisitor : OptimizerVisitor, visitor::PreOrder - static std::optional> typeID(T&& x) { + static std::optional> typeID(const Type& x) { auto id = x.typeID(); if ( ! id ) return {}; @@ -96,6 +98,8 @@ struct FunctionVisitor : OptimizerVisitor, visitor::PreOrder(); assert(current_module); @@ -116,8 +120,8 @@ struct FunctionVisitor : OptimizerVisitor, visitor::PreOrderget().childsOfType(); if ( std::any_of(imports.begin(), imports.end(), [&](const auto& imported_module) { - if ( const auto& m = imported_module.module() ) - return m->id() == ns_local; + if ( const auto& u = imported_module.unit() ) + return u->id() == ns_local; return false; }) ) return std::make_tuple(ns_local, ID(), local); @@ -130,16 +134,18 @@ struct FunctionVisitor : OptimizerVisitor, visitor::PreOrder getID(const type::struct_::Field& x, position_t p) { - auto field_id = x.id(); + static std::optional getID(const declaration::Field& x, position_t p) { + auto parent = p.parent().tryAs(); + if ( ! (parent && parent->isA()) ) + return {}; - auto struct_type = typeID(p.parent().as()); + auto struct_type = typeID(*parent); if ( ! struct_type ) return {}; const auto& [module_id, struct_id] = *struct_type; - return Identifier(util::join({module_id, struct_id, field_id}, "::")); + return Identifier(util::join({module_id, struct_id, x.id()}, "::")); } static std::optional getID(const declaration::Function& x, position_t p) { @@ -239,7 +245,12 @@ struct FunctionVisitor : OptimizerVisitor, visitor::PreOrder(); + return false; + } + + result_t operator()(const declaration::Field& x, position_t p) { if ( ! x.type().isA() ) return false; @@ -261,7 +272,7 @@ struct FunctionVisitor : OptimizerVisitor, visitor::PreOrderbody() ) function.defined = true; // If the unit is wrapped in a type with a `&cxxname` @@ -325,7 +336,7 @@ struct FunctionVisitor : OptimizerVisitor, visitor::PreOrder(fn.id().namespace_(), p, "type"); @@ -397,6 +408,8 @@ struct FunctionVisitor : OptimizerVisitor, visitor::PreOrder() ) - if ( auto fn = member->memberType()->tryAs() ) { + if ( auto fn = member->type().tryAs() ) { HILTI_DEBUG(logging::debug::Optimizer, util::fmt("replacing call to unimplemented function %s with default value", *function_id)); @@ -473,7 +486,7 @@ struct FunctionVisitor : OptimizerVisitor, visitor::PreOrderfunction().type().result().type()))); + p.node = Expression(expression::Ctor(ctor::Default(fn->function().ftype().result().type()))); return true; } @@ -519,6 +532,33 @@ struct TypeVisitor : OptimizerVisitor, visitor::PreOrder { return any_modification; } + result_t operator()(const Module& m, position_t p) { + _current_module = &p.node.as(); + return false; + } + + result_t operator()(const declaration::Field& x, position_t p) { + switch ( _stage ) { + case Stage::COLLECT: { + const auto type_id = x.type().typeID(); + + if ( ! type_id ) + break; + + // Record this type as used. + _used[*type_id] = true; + + break; + } + case Stage::PRUNE_USES: + case Stage::PRUNE_DECLS: + // Nothing. + break; + } + + return false; + } + result_t operator()(const declaration::Type& x, position_t p) { // We currently only handle type declarations for struct types or enum types. // @@ -541,17 +581,7 @@ struct TypeVisitor : OptimizerVisitor, visitor::PreOrder { case Stage::PRUNE_DECLS: if ( ! _used.at(*type_id) ) { HILTI_DEBUG(logging::debug::Optimizer, util::fmt("removing unused type '%s'", *type_id)); - removeNode(p); - - if ( auto module_ = p.findParent() ) - // If this type was declared under a top-level module also clear the module declaration - // cache. The cache will get repopulated the next time the module's declarations are - // requested. - // - // TODO(bbannier): there has to be a better way to mutate the module. - const_cast(module_->get()).clearCache(); - return true; } @@ -561,18 +591,16 @@ struct TypeVisitor : OptimizerVisitor, visitor::PreOrder { return false; } - result_t operator()(const type::ResolvedID& x, position_t p) { + result_t operator()(const Type& type, position_t p) { + if ( p.parent().isA() ) + return false; + switch ( _stage ) { case Stage::COLLECT: { - const auto type = innermostType(x.type()); + if ( const auto& type_id = type.typeID() ) + // Record this type as used. + _used[*type_id] = true; - const auto type_id = type.typeID(); - - if ( ! type_id ) - break; - - // Record this type as used. - _used[*type_id] = true; break; } @@ -630,49 +658,6 @@ struct TypeVisitor : OptimizerVisitor, visitor::PreOrder { return false; } - - result_t operator()(const type::ValueReference& x, position_t p) { - switch ( _stage ) { - case Stage::COLLECT: { - const auto& type_id = x.typeID(); - - if ( ! type_id ) - break; - - // Record this type as used. - _used[*type_id] = true; - break; - } - case Stage::PRUNE_USES: - case Stage::PRUNE_DECLS: - // Nothing. - break; - } - - return false; - } - - result_t operator()(const type::struct_::Field& x, position_t p) { - switch ( _stage ) { - case Stage::COLLECT: { - const auto type_id = x.type().typeID(); - - if ( ! type_id ) - break; - - // Record this type as used. - _used[*type_id] = true; - - break; - } - case Stage::PRUNE_USES: - case Stage::PRUNE_DECLS: - // Nothing. - break; - } - - return false; - } }; struct ConstantFoldingVisitor : OptimizerVisitor, visitor::PreOrder { @@ -715,6 +700,11 @@ struct ConstantFoldingVisitor : OptimizerVisitor, visitor::PreOrder(); + return false; + } + bool operator()(const declaration::Constant& x, position_t p) { if ( x.type() != type::Bool() ) return false; @@ -740,7 +730,7 @@ struct ConstantFoldingVisitor : OptimizerVisitor, visitor::PreOrder().removeFalse(); return true; } } @@ -859,16 +849,7 @@ struct FeatureRequirementsVisitor : visitor::PreOrder() ) - // If this global was declared under a top-level module also clear the module declaration - // cache. The cache will get repopulated the next time the module's declarations are - // requested. - // - // TODO(bbannier): there has to be a better way to mutate the module. - const_cast(module_->get()).clearCache(); + p.node.as().setValue(builder::bool_(false)); } break; @@ -890,13 +871,13 @@ struct FeatureRequirementsVisitor : visitor::PreOrderfunction().type().parameters() ) { + for ( const auto& parameter : fn->function().ftype().parameters() ) { // The requirements of this parameter. std::set reqs; for ( const auto& requirement : AttributeSet::findAll(parameter.attributes(), "&requires-type-feature") ) { - auto feature = *requirement.valueAs(); + auto feature = *requirement.valueAsString(); reqs.insert(std::move(feature)); } @@ -965,14 +946,14 @@ struct FeatureRequirementsVisitor : visitor::PreOrderattributes(), "&needed-by-feature") ) { - const auto feature = *requirement.template valueAs(); + const auto feature = *requirement.valueAsString(); if ( ! ignored_features.count(*type_id) || ! ignored_features.at(*type_id).count(feature) ) // Enable the required feature. - _features[*type_id][*requirement.valueAs()] = true; + _features[*type_id][*requirement.valueAsString()] = true; } // Check if call imposes requirements on any of the types of the arguments. - if ( auto fn = member.memberType()->tryAs() ) { + if ( auto fn = member.type().tryAs() ) { const auto parameters = fn->parameters(); if ( parameters.empty() ) break; @@ -995,7 +976,7 @@ struct FeatureRequirementsVisitor : visitor::PreOrder(); + const auto feature = *requirement.valueAsString(); if ( ! ignored_features.count(*type_id) || ! ignored_features.at(*type_id).count(feature) ) { // Enable the required feature. @@ -1083,7 +1064,7 @@ struct FeatureRequirementsVisitor : visitor::PreOrderattributes(), "&needed-by-feature") ) { - const auto feature = *requirement.template valueAs(); + const auto feature = *requirement.valueAsString(); // Enable the required feature if it is not ignored here. if ( ! ignored_features.count(*typeID) || ! ignored_features.at(*typeID).count(feature) ) @@ -1153,8 +1134,13 @@ struct MemberVisitor : OptimizerVisitor, visitor::PreOrder return any_modification; } - result_t operator()(const type::struct_::Field& x, position_t p) { - auto type_id = p.parent().as().typeID(); + result_t operator()(const Module& m, position_t p) { + _current_module = &p.node.as(); + return false; + } + + result_t operator()(const declaration::Field& x, position_t p) { + auto type_id = p.parent().as().typeID(); if ( ! type_id ) return false; @@ -1183,12 +1169,12 @@ struct MemberVisitor : OptimizerVisitor, visitor::PreOrder const auto& features = _features.at(*type_id); auto dependent_features = - util::transform(AttributeSet::findAll(x.attributes(), "&needed-by-feature"), - [](const Attribute& attr) { return *attr.valueAs(); }); + hilti::node::transform(AttributeSet::findAll(x.attributes(), "&needed-by-feature"), + [](const Attribute& attr) { return *attr.valueAsString(); }); for ( const auto& dependent_feature_ : AttributeSet::findAll(x.attributes(), "&needed-by-feature") ) { - auto dependent_feature = *dependent_feature_.valueAs(); + auto dependent_feature = *dependent_feature_.valueAsString(); // The feature flag is known and the feature is active. if ( features.count(dependent_feature) && features.at(dependent_feature) ) @@ -1224,7 +1210,7 @@ struct MemberVisitor : OptimizerVisitor, visitor::PreOrder if ( ! struct_ ) break; - auto type_id = struct_->typeID(); + auto type_id = type.typeID(); if ( ! type_id ) break; @@ -1302,17 +1288,19 @@ void Optimizer::run() { auto units = [&]() { // We initially store the list as a `set` to ensure uniqueness, but // convert to a `vector` so we can mutate entries while iterating. - auto NodeRefCmp = [](const NodeRef& lhs, const NodeRef& rhs) { return lhs->identity() < rhs->identity(); }; - std::set units(NodeRefCmp); + auto UnitCmp = [](const std::shared_ptr& lhs, const std::shared_ptr& rhs) { + return lhs->id() < rhs->id(); + }; + std::set, decltype(UnitCmp)> units(UnitCmp); - for ( auto& unit : *_units ) { - units.insert(NodeRef(unit.imported(unit.id()))); + for ( auto& unit : _units ) { + units.insert(unit); - for ( const auto& dep : _ctx->lookupDependenciesForModule(unit.id()) ) - units.insert(NodeRef(unit.imported(dep.index.id))); + for ( const auto& dep : unit->dependencies() ) + units.insert(dep.lock()); } - return std::vector{units.begin(), units.end()}; + return std::vector>{units.begin(), units.end()}; }(); const auto passes__ = rt::getenv("HILTI_OPTIMIZER_PASSES"); @@ -1327,10 +1315,10 @@ void Optimizer::run() { // it needs to see the code before any optimization edits. FeatureRequirementsVisitor v; for ( auto& unit : units ) - v.collect(*unit); + v.collect(unit->module()); for ( auto& unit : units ) - v.transform(*unit); + v.transform(unit->module()); } const std::map()>> creators = @@ -1360,15 +1348,15 @@ void Optimizer::run() { for ( auto& v : vs ) { for ( auto& unit : units ) { HILTI_DEBUG(logging::debug::OptimizerCollect, - util::fmt("processing %s round=%d", unit->location().file(), round)); - v->collect(*unit); + util::fmt("processing %s round=%d", unit->module().location().file(), round)); + v->collect(unit->module()); } for ( auto& unit : units ) - modified = v->prune_uses(*unit) || modified; + modified = v->prune_uses(unit->module()) || modified; for ( auto& unit : units ) - modified = v->prune_decls(*unit) || modified; + modified = v->prune_decls(unit->module()) || modified; }; if ( ! modified ) @@ -1379,11 +1367,8 @@ void Optimizer::run() { // Clear cached information which might become outdated due to edits. for ( auto& unit : units ) { - for ( auto i : hilti::visitor::PreOrder<>().walk(&*unit) ) { + for ( auto i : hilti::visitor::PreOrder<>().walk(&unit->module()) ) { i.node.clearScope(); - - if ( i.node.isA() ) - i.node.as().preserved().clear(); } } } diff --git a/hilti/toolchain/src/compiler/parser/parser.yy b/hilti/toolchain/src/compiler/parser/parser.yy index b230ce012..e376a62df 100644 --- a/hilti/toolchain/src/compiler/parser/parser.yy +++ b/hilti/toolchain/src/compiler/parser/parser.yy @@ -33,8 +33,8 @@ namespace hilti { namespace detail { class Parser; } } %verbose %glr-parser -%expect 120 -%expect-rr 185 +%expect 115 +%expect-rr 258 %{ @@ -199,7 +199,6 @@ static uint64_t check_int64_range(uint64_t x, bool positive, const hilti::Meta& %token RESULT "result" %token RETURN "return" %token SCOPE "scope" -%token SELF "self" %token SET "set" %token SHIFTLEFT "<<" %token SHIFTRIGHT ">>" @@ -231,14 +230,15 @@ static uint64_t check_int64_range(uint64_t x, bool positive, const hilti::Meta& %token YIELD "yield" %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 +%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 global_scope_items %type base_type_no_attrs base_type type function_type tuple_type struct_type enum_type union_type %type ctor tuple struct_ list regexp map set %type expr tuple_elem tuple_expr member_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 %type opt_func_default_expr -%type function_with_body function_without_body +%type function_with_body method_with_body hook_with_body function_without_body %type func_param %type opt_func_param_kind %type func_result @@ -253,12 +253,8 @@ static uint64_t check_int64_range(uint64_t x, bool positive, const hilti::Meta& %type opt_attributes %type tuple_type_elem %type tuple_type_elems -%type struct_field -%type struct_fields %type struct_elems %type struct_elem -%type union_field -%type union_fields opt_union_fields %type map_elems opt_map_elems %type map_elem %type enum_label @@ -361,7 +357,11 @@ opt_type_arguments function_decl : opt_linkage FUNCTION function_with_body { $$ = hilti::declaration::Function($3, $1, __loc__); } - | METHOD function_with_body { $$ = hilti::declaration::Function($2, hilti::declaration::Linkage::Struct, __loc__); } + | METHOD method_with_body { $$ = hilti::declaration::Function($2, hilti::declaration::Linkage::Struct, __loc__); } + | opt_linkage HOOK hook_with_body { $$ = hilti::declaration::Function($3, $1, __loc__); } + + + | METHOD method_with_body { $$ = hilti::declaration::Function($2, hilti::declaration::Linkage::Struct, __loc__); } | DECLARE opt_linkage function_without_body ';' { $$ = hilti::declaration::Function($3, $2, __loc__); } ; @@ -391,10 +391,24 @@ scoped_function_id: | SCOPED_FINALIZE { $$ = hilti::ID($1, __loc__); } function_with_body - : opt_func_flavor opt_func_cc func_result scoped_function_id '(' opt_func_params ')' opt_attributes braced_block + : opt_func_cc func_result scoped_function_id '(' opt_func_params ')' opt_attributes braced_block { - auto ftype = hilti::type::Function($3, $6, $1, __loc__); - $$ = hilti::Function($4, std::move(ftype), $9, $2, $8, __loc__); + auto ftype = hilti::type::Function($2, $5, type::function::Flavor::Standard, __loc__); + $$ = hilti::Function($3, std::move(ftype), $8, $1, $7, __loc__); + } + +method_with_body + : opt_func_cc func_result scoped_function_id '(' opt_func_params ')' opt_attributes braced_block + { + auto ftype = hilti::type::Function($2, $5, type::function::Flavor::Method, __loc__); + $$ = hilti::Function($3, std::move(ftype), $8, $1, $7, __loc__); + } + +hook_with_body + : opt_func_cc func_result scoped_function_id '(' opt_func_params ')' opt_attributes braced_block + { + auto ftype = hilti::type::Function($2, $5, type::function::Flavor::Hook, __loc__); + $$ = hilti::Function($3, std::move(ftype), $8, $1, $7, __loc__); } function_without_body @@ -422,8 +436,7 @@ func_params : func_params ',' func_param { $$ = std::move($1); $$.push_back($3); func_param : opt_func_param_kind type local_id opt_func_default_expr opt_attributes { $$ = hilti::type::function::Parameter($3, $2, $1, $4, $5, __loc__); } - | opt_func_param_kind AUTO local_id opt_func_default_expr opt_attributes - { $$ = hilti::type::function::Parameter($3, type::Auto(__loc__), $1, $4, $5, __loc__); } + ; func_result : type { $$ = hilti::type::function::Result(std::move($1), __loc__); } @@ -477,7 +490,7 @@ stmt : stmt_expr ';' { $$ = std::move($1); } | SWITCH '(' expr ')' '{' opt_switch_cases '}' { $$ = hilti::statement::Switch(std::move($3), std::move($6), __loc__); } | SWITCH '(' local_init_decl ')' '{' opt_switch_cases '}' - { $$ = hilti::statement::Switch($3, expression::UnresolvedID($3.as().id()), std::move($6), __loc__); } + { $$ = hilti::statement::Switch(std::move($3), std::move($6), __loc__); } | TRY block try_catches { $$ = hilti::statement::Try(std::move($2), std::move($3), __loc__); } | ASSERT expr ';' { $$ = hilti::statement::Assert(std::move($2), {}, __loc__); } @@ -544,7 +557,7 @@ stmt_expr : expr { $$ = hilti::statement::Expres base_type_no_attrs : ANY { $$ = hilti::type::Any(__loc__); } | ADDRESS { $$ = hilti::type::Address(__loc__); } - | AUTO { $$ = hilti::type::Auto(__loc__); } + | AUTO { $$ = hilti::type::auto_; } | BOOL { $$ = hilti::type::Bool(__loc__); } | BYTES { $$ = hilti::type::Bytes(__loc__); } | ERROR { $$ = hilti::type::Error(__loc__); } @@ -556,7 +569,7 @@ base_type_no_attrs | STREAM { $$ = hilti::type::Stream(__loc__); } | STRING { $$ = hilti::type::String(__loc__); } | TIME { $$ = hilti::type::Time(__loc__); } - | VOID { $$ = hilti::type::Void(__loc__); } + | VOID { $$ = hilti::type::void_; } | INT type_param_begin CUINTEGER type_param_end { $$ = hilti::type::SignedInteger($3, __loc__); } | INT type_param_begin '*' type_param_end { $$ = hilti::type::SignedInteger(hilti::type::Wildcard(), __loc__); } @@ -626,12 +639,12 @@ tuple_type : TUPLE type_param_begin '*' type_param_end { $$ = tuple_type_elems : tuple_type_elems ',' tuple_type_elem { $$ = std::move($1); $$.push_back(std::move($3)); } - | tuple_type_elem { $$ = std::vector>{ std::move($1) }; } + | tuple_type_elem { $$ = std::vector{ std::move($1) }; } ; tuple_type_elem - : type { $$ = std::make_pair(hilti::ID(), std::move($1)); } - | local_id ':' type { $$ = std::make_pair(std::move($1), std::move($3)); } + : type { $$ = hilti::type::tuple::Element(hilti::ID(), std::move($1), __loc__); } + | local_id ':' type { $$ = hilti::type::tuple::Element(std::move($1), std::move($3), __loc__); } ; struct_type : STRUCT opt_struct_params '{' struct_fields '}' { $$ = hilti::type::Struct(std::move($2), std::move($4), __loc__); } @@ -641,29 +654,29 @@ opt_struct_params | /* empty */ { $$ = std::vector{}; } struct_fields : struct_fields struct_field { $$ = std::move($1); $$.push_back($2); } - | /* empty */ { $$ = std::vector{}; } + | /* empty */ { $$ = std::vector{}; } -struct_field : type local_id opt_attributes ';' { $$ = hilti::type::struct_::Field(std::move($2), std::move($1), std::move($3), __loc__); } +struct_field : type local_id opt_attributes ';' { $$ = hilti::declaration::Field(std::move($2), std::move($1), std::move($3), __loc__); } | func_flavor opt_func_cc func_result function_id '(' opt_func_params ')' opt_attributes ';' { auto ftype = hilti::type::Function(std::move($3), std::move($6), $1, __loc__); - $$ = hilti::type::struct_::Field(std::move($4), $2, std::move(ftype), $8, __loc__); + $$ = hilti::declaration::Field(std::move($4), $2, std::move(ftype), $8, __loc__); } | func_flavor opt_func_cc func_result function_id '(' opt_func_params ')' opt_attributes braced_block { auto ftype = hilti::type::Function(std::move($3), std::move($6), $1, __loc__); auto func = hilti::Function($4, std::move(ftype), std::move($9), $2, {}); - $$ = hilti::type::struct_::Field($4, std::move(func), $8, __loc__); + $$ = hilti::declaration::Field($4, std::move(func), $8, __loc__); } union_type : UNION opt_attributes'{' opt_union_fields '}' { $$ = hilti::type::Union(std::move($4), __loc__); } opt_union_fields : union_fields { $$ = $1; } - | /* empty */ { $$ = std::vector(); } + | /* empty */ { $$ = std::vector(); } union_fields : union_fields ',' union_field { $$ = std::move($1); $$.push_back(std::move($3)); } - | union_field { $$ = std::vector(); $$.push_back(std::move($1)); } + | union_field { $$ = std::vector(); $$.push_back(std::move($1)); } -union_field : type local_id opt_attributes { $$ = hilti::type::union_::Field(std::move($2), std::move($1), std::move($3), __loc__); } +union_field : type local_id opt_attributes { $$ = hilti::declaration::Field(std::move($2), std::move($1), std::move($3), __loc__); } enum_type : ENUM '{' enum_labels '}' { $$ = hilti::type::Enum(std::move($3), __loc__); } | ENUM '<' '*' '>' { $$ = hilti::type::Enum(type::Wildcard(), __loc__); } @@ -898,12 +911,12 @@ re_pattern_constant { $$ = std::move($3); } opt_map_elems : map_elems { $$ = std::move($1); } - | /* empty */ { $$ = std::vector(); } + | /* empty */ { $$ = std::vector(); } map_elems : map_elems ',' map_elem { $$ = std::move($1); $$.push_back(std::move($3)); } - | map_elem { $$ = std::vector(); $$.push_back(std::move($1)); } + | map_elem { $$ = std::vector(); $$.push_back(std::move($1)); } -map_elem : expr ':' expr { $$ = std::make_pair($1, $3); } +map_elem : expr ':' expr { $$ = hilti::ctor::map::Element($1, $3, __loc__); } attribute : ATTRIBUTE { $$ = hilti::Attribute(std::move($1), __loc__); } diff --git a/hilti/toolchain/src/compiler/plugin.cc b/hilti/toolchain/src/compiler/plugin.cc index e876f34c6..c9eb6a211 100644 --- a/hilti/toolchain/src/compiler/plugin.cc +++ b/hilti/toolchain/src/compiler/plugin.cc @@ -1,6 +1,7 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. #include +#include #include #include @@ -9,14 +10,28 @@ using namespace hilti::detail; PluginRegistry::PluginRegistry() = default; // Neded here to allow PluginRegistry to be forward declared. -Result PluginRegistry::pluginForExtension(hilti::rt::filesystem::path ext) const { +Result> PluginRegistry::pluginForExtension(hilti::rt::filesystem::path ext) const { auto p = std::find_if(_plugins.begin(), _plugins.end(), [&](auto& p) { return p.extension == ext; }); if ( p != _plugins.end() ) - return *p; + return {*p}; return result::Error(util::fmt("no plugin registered for extension %s", ext)); } +const Plugin& PluginRegistry::hiltiPlugin() const { + static const Plugin* hilti_plugin = nullptr; + + if ( ! hilti_plugin ) { + auto p = std::find_if(_plugins.begin(), _plugins.end(), [&](auto& p) { return p.component == "HILTI"; }); + if ( p == _plugins.end() ) + logger().fatalError("cannot retrieve HILTI plugin"); + + hilti_plugin = &*p; + } + + return *hilti_plugin; +} + PluginRegistry& plugin::registry() { static PluginRegistry singleton; return singleton; @@ -31,9 +46,9 @@ void PluginRegistry::register_(const Plugin& p) { static Plugin hilti_plugin() { return Plugin{ .component = "HILTI", + .order = 10, .extension = ".hlt", .cxx_includes = {"hilti/rt/libhilti.h"}, - .order = 1, .library_paths = [](const std::shared_ptr& ctx) { return hilti::configuration().hilti_library_paths; }, @@ -46,22 +61,27 @@ static Plugin hilti_plugin() { .coerce_type = [](Type t, const Type& dst, bitmask style) { return detail::coerceType(std::move(t), dst, style); }, - .build_scopes = [](const std::shared_ptr& ctx, const std::vector>& m, - Unit* u) { buildScopes(m, u); }, - - .resolve_ids = [](const std::shared_ptr& ctx, Node* n, Unit* u) { return resolveIDs(n, u); }, + .ast_build_scopes = + [](const std::shared_ptr& ctx, Node* m, Unit* u) { + ast::buildScopes(ctx, m, u); + return false; + }, - .resolve_operators = [](const std::shared_ptr& ctx, Node* n, - Unit* u) { return resolveOperators(n, u); }, + .ast_normalize = [](const std::shared_ptr& ctx, Node* m, + Unit* u) { return ast::normalize(m, u); }, - .apply_coercions = [](const std::shared_ptr& ctx, Node* n, - Unit* u) { return applyCoercions(n, u); }, + .ast_coerce = [](const std::shared_ptr& ctx, Node* m, Unit* u) { return ast::coerce(m, u); }, - .pre_validate = {}, + .ast_resolve = [](const std::shared_ptr& ctx, Node* m, + Unit* u) { return ast::resolve(ctx, m, u); }, - .post_validate = [](const std::shared_ptr& ctx, Node* n, Unit* u) { validateAST(n); }, + .ast_validate = + [](const std::shared_ptr& ctx, Node* m, Unit* u) { + ast::validate(m); + return false; + }, - .transform = {}, + .ast_transform = {}, }; } diff --git a/hilti/toolchain/src/compiler/unit.cc b/hilti/toolchain/src/compiler/unit.cc index 4c8e37aca..a4a0a65c0 100644 --- a/hilti/toolchain/src/compiler/unit.cc +++ b/hilti/toolchain/src/compiler/unit.cc @@ -5,7 +5,9 @@ #include #include +#include #include +#include #include #include #include @@ -17,335 +19,249 @@ using namespace hilti::context; using util::fmt; namespace hilti::logging::debug { -inline const DebugStream Compiler("compiler"); -inline const DebugStream AstFinal("ast-final"); inline const DebugStream AstCodegen("ast-codegen"); -inline const DebugStream AstOrig("ast-orig"); -inline const DebugStream AstResolved("ast-resolved"); -inline const DebugStream AstScopes("ast-scopes"); -inline const DebugStream AstPreTransformed("ast-pre-transformed"); -inline const DebugStream AstTransformed("ast-transformed"); -inline const DebugStream AstDumpIterations("ast-dump-iterations"); +inline const DebugStream Compiler("compiler"); } // namespace hilti::logging::debug template -bool runHooks(PluginMember hook, const std::string& debug_msg, const Args&... args) { - for ( const auto& p : plugin::registry().plugins() ) { - if ( ! (p.*hook) ) - continue; +bool runHook(bool* modified, const Plugin& plugin, const Node* module, const std::string& extension, PluginMember hook, + const std::string& debug_msg, const Args&... args) { + if ( ! (plugin.*hook) ) + return true; - auto msg = debug_msg; + auto p = plugin::registry().pluginForExtension(extension); + if ( ! p ) + logger().internalError(util::fmt("no plugin for unit extension %s: %s", extension, p.error())); - if ( p.component != "HILTI" ) - msg += fmt(" (%s)", p.component); + if ( p->get().component != plugin.component ) + return true; - HILTI_DEBUG(logging::debug::Compiler, msg); - (*(p.*hook))(args...); + auto msg = fmt("[%s] %s", plugin.component, debug_msg); - if ( logger().errors() ) - return false; + HILTI_DEBUG(logging::debug::Compiler, msg); + if ( (*(plugin.*hook))(args...) ) { + *modified = true; + HILTI_DEBUG(logging::debug::Compiler, " -> modified"); } - return true; -} - -template -bool runModifyingHooks(bool* modified, PluginMember hook, const std::string& debug_msg, const Args&... args) { - for ( const auto& p : plugin::registry().plugins() ) { - if ( ! (p.*hook) ) - continue; - - auto msg = debug_msg; - - if ( p.component != "HILTI" ) - msg += fmt(" (%s)", p.component); - - HILTI_DEBUG(logging::debug::Compiler, msg); - if ( (*(p.*hook))(args...) ) { - *modified = true; - HILTI_DEBUG(logging::debug::Compiler, " -> modified"); - } - - if ( logger().errors() ) - return false; - } + if ( logger().errors() ) + return false; return true; } -Result Unit::fromModule(const std::shared_ptr& context, hilti::Module&& module, - const hilti::rt::filesystem::path& path) { - auto unit = Unit(context, module.id(), path, true); - auto cached = context->registerModule({unit.id(), path}, std::move(module), true); - unit._modules.insert(cached.index.id); - return unit; -} +Unit::~Unit() { _destroyModule(); } -Result Unit::fromCache(const std::shared_ptr& context, const hilti::rt::filesystem::path& path) { - auto cached = context->lookupModule(path); - if ( ! cached ) +Result> Unit::fromCache(const std::shared_ptr& context, + const hilti::rt::filesystem::path& path) { + if ( auto cached = context->lookupUnit(path) ) + return cached->unit; + else return result::Error(fmt("unknown module %s", path)); - - auto unit = Unit(context, cached->index.id, cached->index.path, true); - unit._modules.insert(cached->index.id); - return unit; } -Result Unit::fromCache(const std::shared_ptr& context, const hilti::ID& id) { - auto cached = context->lookupModule(id); - if ( ! cached ) +Result> Unit::fromCache(const std::shared_ptr& context, const hilti::ID& id, + const hilti::rt::filesystem::path& extension) { + if ( auto cached = context->lookupUnit(id, extension) ) + return cached->unit; + else return result::Error(fmt("unknown module %s", id)); - - auto unit = Unit(context, cached->index.id, cached->index.path, true); - unit._modules.insert(cached->index.id); - return unit; } -Result Unit::fromSource(const std::shared_ptr& context, const hilti::rt::filesystem::path& path) { - auto module = Unit::parse(context, path); +Result> Unit::fromSource(const std::shared_ptr& context, + const hilti::rt::filesystem::path& path, + std::optional process_extension) { + if ( auto cached = context->lookupUnit(path, process_extension) ) + return cached->unit; + + auto module = _parse(context, path); if ( ! module ) return module.error(); - return fromModule(context, std::move(*module), path); -} + if ( ! process_extension ) + process_extension = path.extension(); + + auto id = module->id(); + auto unit = std::shared_ptr(new Unit(context, std::move(id), path, *process_extension, + std::move(*module))); // no make_shared, ctor is private + context->cacheUnit(unit); -Result Unit::fromCXX(std::shared_ptr context, detail::cxx::Unit cxx, - const hilti::rt::filesystem::path& path) { - auto unit = Unit(std::move(context), ID(fmt("", path.native())), path, false); - unit._cxx_unit = std::move(cxx); - // No entry in _modules. return unit; } -Result Unit::parse(const std::shared_ptr& context, const hilti::rt::filesystem::path& path) { - util::timing::Collector _("hilti/compiler/parser"); - - std::ifstream in; - in.open(path); - - if ( ! in ) - return result::Error(fmt("cannot open source file %s", path)); - - auto plugin = plugin::registry().pluginForExtension(path.extension()); +std::shared_ptr Unit::fromModule(const std::shared_ptr& context, hilti::Module module, + hilti::rt::filesystem::path extension) { + auto id = module.id(); + auto unit = std::shared_ptr(new Unit(context, std::move(id), {}, extension, + std::move(module))); // no make_shared, ctor is private + context->cacheUnit(unit); + return unit; +} - if ( ! (plugin && plugin->parse) ) - return result::Error(fmt("no plugin provides support for importing *%s files", path.extension())); +Result> Unit::fromImport(const std::shared_ptr& context, const ID& id, + const hilti::rt::filesystem::path& parse_extension, + const hilti::rt::filesystem::path& process_extension, + std::optional scope, + std::vector search_dirs) { + if ( auto cached = context->lookupUnit(id, process_extension) ) + return cached->unit; - auto dbg_message = fmt("parsing file %s", path); + auto parse_plugin = plugin::registry().pluginForExtension(parse_extension); - if ( plugin->component != "HILTI" ) - dbg_message += fmt(" (%s)", plugin->component); + if ( ! (parse_plugin && parse_plugin->get().parse) ) + return result::Error(fmt("no plugin provides support for importing *%s files", parse_extension.native())); - HILTI_DEBUG(logging::debug::Compiler, dbg_message); + auto name = fmt("%s%s", util::tolower(id), parse_extension.native()); - auto module = (*plugin->parse)(in, path); - if ( ! module ) - return module.error(); + if ( scope ) + name = fmt("%s/%s", util::replace(scope->str(), ".", "/"), name); - return module->as(); -} + std::vector library_paths = std::move(search_dirs); -Result Unit::compile() { - _dumpASTs(logging::debug::AstOrig, "Original AST"); - _saveIterationASTs("AST before first iteration"); + if ( parse_plugin->get().library_paths ) + library_paths = util::concat(std::move(library_paths), (*parse_plugin->get().library_paths)(context)); - int round = 1; - int extra_rounds = 0; // set to >0 for debugging + library_paths = util::concat(std::move(library_paths), context->options().library_paths); - while ( true ) { - HILTI_DEBUG(logging::debug::Compiler, fmt("processing AST, round %d", round)); - logging::DebugPushIndent _(logging::debug::Compiler); + auto path = util::findInPaths(name, library_paths); + if ( ! path ) { + HILTI_DEBUG(logging::debug::Compiler, fmt("Failed to find module '%s' in search paths:", name)); + for ( const auto& p : library_paths ) + HILTI_DEBUG(logging::debug::Compiler, fmt(" %s", p)); - bool modified = false; + return result::Error(fmt("cannot find file")); + } - std::set performed_imports; - while ( true ) { - auto orig_modules = _modules; // _modules may be modified by importer pass + auto unit = fromSource(context, *path, process_extension); + if ( ! unit ) + return unit; - for ( const auto& id : orig_modules ) { - if ( performed_imports.find(id) != performed_imports.end() ) - continue; + if ( (*unit)->id() != id ) + return result::Error( + util::fmt("file %s does not contain expected module %s (but %s)", path->native(), id, (*unit)->id())); - auto cached = _context->lookupModule(id); - assert(cached); + return unit; +} - HILTI_DEBUG(logging::debug::Compiler, fmt("performing missing imports for module %s", id)); - { - logging::DebugPushIndent _(logging::debug::Compiler); - cached->dependencies = detail::importModules(*cached->node, this); - _context->updateModule(*cached); - performed_imports.insert(id); - } - } +Result> Unit::fromCXX(std::shared_ptr context, detail::cxx::Unit cxx, + const hilti::rt::filesystem::path& path) { + return std::shared_ptr(new Unit(context, ID(fmt("", path.native())), ".cxx", path, std::move(cxx))); +} - if ( logger().errors() ) - return result::Error("errors encountered during import"); +Result Unit::_parse(const std::shared_ptr& context, const hilti::rt::filesystem::path& path) { + util::timing::Collector _("hilti/compiler/parser"); - if ( _modules.size() == orig_modules.size() ) - // repeat while as long as we keep adding modules - break; - } + std::ifstream in; + in.open(path); - HILTI_DEBUG(logging::debug::Compiler, fmt("modules: %s", util::join(_modules, ", "))); + if ( ! in ) + return result::Error(fmt("cannot open source file %s", path)); - auto modules = _currentModules(); + auto plugin = plugin::registry().pluginForExtension(path.extension()); - for ( auto& [id, module] : modules ) - _resetNodes(id, &*module); + if ( ! (plugin && plugin->get().parse) ) + return result::Error(fmt("no plugin provides support for importing *%s files", path.extension().native())); - if ( ! runHooks(&Plugin::build_scopes, "building scopes for all module modules", context(), modules, this) ) - return result::Error("errors encountered during scope building"); + auto dbg_message = fmt("parsing file %s as %s code", path, plugin->get().component); - _dumpASTs(logging::debug::AstScopes, "AST with scopes", round); + if ( plugin->get().component != "HILTI" ) + dbg_message += fmt(" (%s)", plugin->get().component); - if ( logger().errors() ) - return result::Error("errors encountered during scope building"); + HILTI_DEBUG(logging::debug::Compiler, dbg_message); - for ( auto& [id, module] : modules ) { - if ( ! runModifyingHooks(&modified, &Plugin::resolve_ids, fmt("resolving IDs in module %s", id), context(), - &*module, this) ) - return result::Error("errors encountered during ID resolving"); - } + auto node = (*plugin->get().parse)(in, path); + if ( ! node ) + return node.error(); - for ( auto& [id, module] : modules ) { - if ( ! runModifyingHooks(&modified, &Plugin::resolve_operators, fmt("resolving operators in module %s", id), - context(), &*module, this) ) - return result::Error("errors encountered during operator resolving"); - } + const auto& module = node->as(); + if ( ! module.id() ) + return result::Error(fmt("module in %s does not have an ID", path.native())); - for ( auto& [id, module] : modules ) { - if ( ! runModifyingHooks(&modified, &Plugin::apply_coercions, fmt("coercing expressions for %s", id), - context(), &*module, this) ) - return result::Error("errors encountered during expression coercion"); - } + return std::move(module); +} - _dumpASTs(logging::debug::AstResolved, "AST after resolving", round); - - if ( plugin::registry().hasHookFor(&Plugin::transform) ) { - _dumpASTs(logging::debug::AstPreTransformed, "Pre-transformed AST", round); - - for ( auto& [id, module] : modules ) { - bool found_errors = false; - - if ( ! runHooks(&Plugin::pre_validate, fmt("validating module %s (pre-transform)", id), context(), - &*module, this, &found_errors) ) - return result::Error("errors encountered during pre-transform validation"); - - if ( found_errors ) { - // We may have errors already set in the AST that we - // don't want to report, as normally they'd go away - // during further cycles. So we clear the AST and then - // run the hook again to get just the errors that it puts - // in place. - detail::clearErrors(&*module); - auto valid = _validateAST(id, NodeRef(module), [&](const ID& id, NodeRef& module) { - bool found_errors = false; - return runHooks(&Plugin::pre_validate, - fmt("validating module %s (pre-transform, 2nd pass)", id), context(), &*module, - this, &found_errors); - }); - - (void)valid; // Fore use of `valid` since below `assert` might become a noop. - assert(! valid); // We already know it's failing. - _dumpAST(module, logging::debug::AstFinal, "Final AST"); - _saveIterationASTs("Final AST", round); - return result::Error("errors encountered during pre-transform validation"); - } - - if ( ! runModifyingHooks(&modified, &Plugin::transform, fmt("transforming module %s", id), context(), - &*module, round == 1, this) ) - return result::Error("errors encountered during source-to-source translation"); - } +Result Unit::buildASTScopes(const Plugin& plugin) { + if ( ! _module ) + return Nothing(); - _dumpASTs(logging::debug::AstTransformed, "Transformed AST", round); - } + bool modified = false; // not used - if ( ! modified && extra_rounds-- == 0 ) - break; + if ( ! runHook(&modified, plugin, &*_module, _extension, &Plugin::ast_build_scopes, + fmt("building scopes for module %s", id()), context(), &*_module, this) ) + return result::Error("errors encountered during scope building"); - _saveIterationASTs("AST after iteration", round); + return Nothing(); +} - if ( ++round >= 50 ) - logger().internalError("hilti::Unit::compile() didn't terminate, AST keeps changing"); - } +Result Unit::resolveAST(const Plugin& plugin) { + bool modified = false; - auto& module = imported(_id); - auto current = _currentModules(); + if ( ! runHook(&modified, plugin, &*_module, _extension, &Plugin::ast_normalize, + fmt("normalizing nodes in module %s", id()), context(), &*_module, this) ) + return result::Error("errors encountered during normalizing"); - for ( auto& [id, module] : current ) { - auto valid = - _validateASTs(module->as().id(), (*module).as().preserved(), - [&](const ID& id, auto& preserved) { - for ( auto& m : preserved ) - _resetNodes(id, &m); + if ( ! runHook(&modified, plugin, &*_module, _extension, &Plugin::ast_coerce, + fmt("coercing nodes in module %s", id()), context(), &*_module, this) ) + return result::Error("errors encountered during coercing"); - return runHooks(&Plugin::preserved_validate, fmt("validating module %s (preserved)", id), - context(), &preserved, this); - }); + if ( ! runHook(&modified, plugin, &*_module, _extension, &Plugin::ast_resolve, + fmt("resolving nodes in module %s", id()), context(), &*_module, this) ) + return result::Error("errors encountered during resolving"); - if ( ! valid ) { - _dumpAST(module, logging::debug::AstFinal, "Final AST"); - _saveIterationASTs("Final AST", round); - return result::Error("errors encountered during validation of preserved nodes"); - } - } + return modified ? Modified : NotModified; +} - auto valid = _validateASTs(current, [&](const ID& id, NodeRef& module) { - return runHooks(&Plugin::post_validate, fmt("validating module %s (post-transform)", id), context(), &*module, - this); - }); +bool Unit::validateAST(const Plugin& plugin) { + if ( ! _module ) + return true; - _dumpAST(module, logging::debug::AstFinal, "Final AST"); - _saveIterationASTs("Final AST", round); + bool modified = false; // not used + runHook(&modified, plugin, &*_module, _extension, &Plugin::ast_validate, fmt("validating module %s", id()), + context(), &*_module, this); - if ( ! valid ) - return result::Error("errors encountered during post-transform validation"); + return _collectErrors(); +} - for ( auto& [id, module] : _currentModules() ) { - _determineCompilationRequirements(*module); +Result Unit::transformAST(const Plugin& plugin) { + if ( ! _module ) + return Nothing(); - // Cache the module's final state. - auto cached = _context->lookupModule(id); - cached->final = true; - _context->updateModule(*cached); - } + bool modified = false; + runHook(&modified, plugin, &*_module, _extension, &Plugin::ast_transform, fmt("transforming module %s", id()), + context(), &*_module, this); return Nothing(); } Result Unit::codegen() { - auto& module = imported(_id); - - _dumpASTs(logging::debug::AstCodegen, "AST for codegen"); + if ( ! _module ) + return Nothing(); - HILTI_DEBUG(logging::debug::Compiler, fmt("compiling module %s to C++", _id)); + HILTI_DEBUG(logging::debug::Compiler, fmt("compiling module %s to C++", id())); logging::DebugPushIndent _(logging::debug::Compiler); // Compile to C++. - auto c = detail::CodeGen(_context).compileModule(module, this, true); + auto c = detail::CodeGen(context()).compileModule(*_module, this, true); if ( logger().errors() ) return result::Error("errors encountered during code generation"); if ( ! c ) logger().internalError( - fmt("code generation for module %s failed, but did not log error (%s)", _id, c.error().description())); + fmt("code generation for module %s failed, but did not log error (%s)", id(), c.error().description())); - // Now compile the other modules to because we may need some of their - // declarations. + // Import declarations from our dependencies. They will have been compiled + // at this point. // - // TODO(robin): Would be nice if we had a "cheap" compilation mode - // that only generated declarations. - for ( auto& [id, module] : _currentModules() ) { - if ( id == _id ) - continue; - - HILTI_DEBUG(logging::debug::Compiler, fmt("importing declarations from module %s", id)); - auto other = detail::CodeGen(_context).compileModule(*module, this, false); + // TODO(robin): Would be nice if we had a "cheap" compilation mode that + // only generated declarations. + for ( const auto& unit : dependencies() ) { + HILTI_DEBUG(logging::debug::Compiler, fmt("importing declarations from module %s", unit.lock()->id())); + auto other = detail::CodeGen(context()).compileModule(unit.lock()->module(), unit.lock().get(), false); c->importDeclarations(*other); } - HILTI_DEBUG(logging::debug::Compiler, fmt("finalizing module %s", _id)); + HILTI_DEBUG(logging::debug::Compiler, fmt("finalizing module %s", id())); if ( auto x = c->finalize(); ! x ) return x.error(); @@ -353,29 +269,10 @@ Result Unit::codegen() { return Nothing(); } -std::vector> Unit::_currentModules() const { - std::vector> modules; - - for ( const auto& id : _modules ) { - auto cached = _context->lookupModule(id); - assert(cached); - modules.emplace_back(id, NodeRef(cached->node)); - } - - return modules; -} - -std::optional Unit::_lookupModule(const ID& id) const { - if ( _modules.find(id) == _modules.end() ) - return {}; - - auto cached = _context->lookupModule(id); - assert(cached); - return cached; -} - Result Unit::print(std::ostream& out) const { - detail::printAST(imported(_id), out); + if ( _module ) + detail::printAST(*_module, out); + return Nothing(); } @@ -399,88 +296,21 @@ Result Unit::cxxCode() const { return CxxCode{_cxx_unit->moduleID(), cxx}; } -Result Unit::import(const ID& id, const hilti::rt::filesystem::path& ext, std::optional scope, - std::vector search_dirs) { - if ( auto cached = _lookupModule(id) ) - return cached->index; - - if ( auto cached = _context->lookupModule(id) ) { - _modules.insert(id); - return cached->index; - } - - auto plugin = plugin::registry().pluginForExtension(ext); - - if ( ! (plugin && plugin->parse) ) - return result::Error(fmt("no plugin provides support for importing *%s files", ext)); - - auto name = fmt("%s%s", util::tolower(id), ext.native()); - - if ( scope ) - name = fmt("%s/%s", util::replace(scope->str(), ".", "/"), name); - - std::vector library_paths = std::move(search_dirs); - - if ( plugin->library_paths ) - library_paths = util::concat(std::move(library_paths), (*plugin->library_paths)(context())); - - library_paths = util::concat(std::move(library_paths), options().library_paths); - - auto path = util::findInPaths(name, library_paths); - if ( ! path ) { - HILTI_DEBUG(logging::debug::Compiler, fmt("Failed to find module '%s' in search paths:", name)); - for ( const auto& p : library_paths ) - HILTI_DEBUG(logging::debug::Compiler, fmt(" %s", p)); - - return result::Error(fmt("cannot find file")); - } - - return _import(*path, id); -} - -Result Unit::import(const hilti::rt::filesystem::path& path) { - if ( auto cached = _context->lookupModule(path) ) { - _modules.insert(cached->index.id); - return cached->index; +bool Unit::addDependency(std::shared_ptr unit) { + for ( const auto& d : _dependencies ) { + if ( d.lock().get() == unit.get() ) + return false; } - return _import(path, {}); -} - -Result Unit::_import(const hilti::rt::filesystem::path& path, std::optional expected_name) { - auto module = parse(context(), path); - if ( ! module ) - return module.error(); - - auto id = module->id(); - - if ( expected_name && id != *expected_name ) - return result::Error(fmt("file %s does not contain expected module %s (but %s)", path, *expected_name, id)); - - HILTI_DEBUG(logging::debug::Compiler, fmt("loaded module %s from %s", id, path)); - - if ( auto cached = _lookupModule(id) ) - return cached->index; - - auto cached = context()->registerModule({id, path}, std::move(*module), false); - cached.dependencies = detail::importModules(*cached.node, this); - context()->updateModule(cached); - _modules.insert(id); - return cached.index; -} - -Node& Unit::imported(const ID& id) const { - if ( auto cached = _lookupModule(id) ) - return *cached->node; - else - throw std::out_of_range("no such module"); + _dependencies.push_back(std::move(unit)); + return true; } -void Unit::_determineCompilationRequirements(const Node& module) { +bool Unit::requiresCompilation() { // Visitor that goes over an AST and flags whether any node provides // code that needs compilation. - struct VisitorModule : hilti::visitor::PreOrder { - explicit VisitorModule() = default; + struct Visitor : hilti::visitor::PreOrder { + explicit Visitor() = default; result_t operator()(const declaration::GlobalVariable& n, const_position_t p) { return true; } result_t operator()(const declaration::Function& n, const_position_t p) { @@ -488,233 +318,88 @@ void Unit::_determineCompilationRequirements(const Node& module) { } }; - // Visitor that extracts all imported modules from an AST and sets their - // requires-compilation flags. - struct VisitorImports : hilti::visitor::PreOrder { - explicit VisitorImports(std::shared_ptr ctx, const std::set& modules) - : context(std::move(ctx)), modules(modules) {} - std::shared_ptr context; - const std::set& modules; - - void operator()(const declaration::ImportedModule& n, const_position_t p) { - for ( const auto& i : p.node.scope()->items() ) { - for ( const auto& m : i.second ) { - auto md = m->tryAs(); - if ( ! md ) - continue; - - auto v = VisitorModule(); - for ( auto i : v.walk(md->root()) ) { - if ( auto x = v.dispatch(i); ! (x && *x) ) - continue; - - if ( auto cached = context->lookupModule(n.id()) ) { - cached->requires_compilation = true; - context->updateModule(*cached); - break; - } - } - } - } + auto v = Visitor(); + for ( auto i : v.walk(*_module) ) { + if ( auto rc = v.dispatch(i) ) { + if ( rc && *rc ) + return true; } - }; - - // Run the visitors. - auto v = VisitorImports(context(), _modules); - for ( auto i : v.walk(module) ) - v.dispatch(i); -} - -bool Unit::_validateASTs(std::vector>& modules, - const std::function& run_hooks_callback) { - if ( options().skip_validation ) - return true; - - auto valid = true; - - for ( auto& [id, module] : modules ) { - if ( ! _validateAST(id, NodeRef(module), run_hooks_callback) ) - valid = false; } - return valid; + return false; } -/** - * Recursive helper function to traverse the AST and collect relevant errors. - * We pick errors on child nodes first, and then hide any further ones - * located in parents along the way. We take only the first error in each - * priority class (normal & low). If a node doesn't have a location, we - * substitute the closest parent location. - * - * @param n root node for validation - * @param closest_location location closest to *n* on the path leading to it - * @param errors errors recorded for reporting so far; function will extend this - * @return two booleans with the 1st indicating if we have already found a - * normal priroity error on the path, and the 2nd if we have already found a - * low priority error on the path. - */ -static std::pair _recursiveValidateAST(const Node& n, Location closest_location, - std::vector* errors) { - auto have_normal = false; - auto have_low = false; - +static node::ErrorPriority _recursiveValidateAST(const Node& n, Location closest_location, node::ErrorPriority prio, + int level, std::vector* errors) { if ( n.location() ) closest_location = n.location(); - for ( const auto& c : n.childs() ) { - auto [normal, low] = _recursiveValidateAST(c, closest_location, errors); - have_normal = (have_normal || normal); - have_low = (have_low || low); + if ( ! n.pruneWalk() ) { + auto oprio = prio; + for ( const auto& c : n.childs() ) + prio = std::max(prio, _recursiveValidateAST(c, closest_location, oprio, level + 1, errors)); } - if ( have_normal ) - return std::make_pair(have_normal, have_low); + auto errs = n.errors(); + auto nprio = prio; + for ( auto e = errs.begin(); e != errs.end(); e++ ) { + if ( ! e->location && closest_location ) + e->location = closest_location; - if ( n.hasErrors() ) { - for ( auto&& e : n.errors() ) { - if ( ! e.location && closest_location ) - e.location = closest_location; + if ( e->priority > prio ) + errors->push_back(*e); - if ( ! (have_normal || have_low) ) - errors->emplace_back(std::move(e)); - } - - for ( const auto& e : n.errors() ) { - if ( e.priority == node::ErrorPriority::Normal ) - return std::make_pair(true, have_low); - else - return std::make_pair(have_normal, true); - } + nprio = std::max(nprio, e->priority); } - return std::make_pair(have_normal, have_low); + return nprio; } static void _reportErrors(const std::vector& errors) { - // We skip any low-priority errors at first. Only if we have only those, - // we will report them. We also generally suppress duplicates. + // We only report the highest priority error category. std::set reported; - auto report_one = [&](const auto& e) { - if ( reported.find(e) == reported.end() ) { - logger().error(e.message, e.context, e.location); - reported.insert(e); - } - }; + auto prios = {node::ErrorPriority::High, node::ErrorPriority::Normal, node::ErrorPriority::Low}; - for ( const auto& e : errors ) { - if ( e.priority != node::ErrorPriority::Low ) - report_one(e); - } + for ( auto p : prios ) { + for ( const auto& e : errors ) { + if ( e.priority != p ) + continue; - if ( reported.size() ) - return; + if ( reported.find(e) == reported.end() ) { + logger().error(e.message, e.context, e.location); + reported.insert(e); + } + } - for ( const auto& e : errors ) { - if ( e.priority == node::ErrorPriority::Low ) - report_one(e); + if ( reported.size() ) + break; } } -bool Unit::_validateASTs(const ID& id, std::vector& nodes, - const std::function&)>& run_hooks_callback) { - if ( options().skip_validation ) - return true; - - if ( ! run_hooks_callback(id, nodes) ) - return false; - +bool Unit::_collectErrors() { std::vector errors; - for ( auto& n : nodes ) - _recursiveValidateAST(n, Location(), &errors); - - if ( errors.empty() ) - return true; - - _reportErrors(errors); - return false; -} + _recursiveValidateAST(*_module, Location(), node::ErrorPriority::NoError, 0, &errors); -bool Unit::_validateAST(const ID& id, NodeRef module, - const std::function& run_hooks_callback) { - if ( options().skip_validation ) - return true; - - if ( ! run_hooks_callback(id, module) ) + if ( errors.size() ) { + _reportErrors(errors); return false; - - std::vector errors; - _recursiveValidateAST(*module, Location(), &errors); - - if ( errors.empty() ) - return true; - - _reportErrors(errors); - return false; -} - -void Unit::_dumpAST(const Node& module, const logging::DebugStream& stream, const std::string& prefix, int round) { - if ( ! logger().isEnabled(stream) ) - return; - - const auto& m = module.as(); - - std::string r; - - if ( round > 0 ) - r = fmt(" (round %d)", round); - - HILTI_DEBUG(stream, fmt("# %s: %s%s", m.id(), prefix, r)); - detail::renderNode(module, stream, true); - - if ( m.preserved().size() ) { - HILTI_DEBUG(stream, fmt("# %s: Preserved nodes%s", m.id(), r)); - for ( const auto& i : m.preserved() ) - detail::renderNode(i, stream, true); - } -} - -void Unit::_dumpASTs(const logging::DebugStream& stream, const std::string& prefix, int round) { - if ( ! logger().isEnabled(stream) ) - return; - - for ( auto& [id, module] : _currentModules() ) - _dumpAST(*module, stream, prefix, round); -} - -void Unit::_dumpAST(const Node& module, std::ostream& stream, const std::string& prefix, int round) { - const auto& m = module.as(); - - std::string r; - - if ( round > 0 ) - r = fmt(" (round %d)", round); - - stream << fmt("# %s: %s%s\n", m.id(), prefix, r); - detail::renderNode(module, stream, true); - - if ( m.preserved().size() ) { - stream << fmt("# %s: Preserved nodes%s\n", m.id(), r); - for ( const auto& i : m.preserved() ) - detail::renderNode(i, stream, true); } -} -void Unit::_dumpASTs(std::ostream& stream, const std::string& prefix, int round) { - for ( auto& [id, module] : _currentModules() ) - _dumpAST(*module, stream, prefix, round); + return true; } -void Unit::_saveIterationASTs(const std::string& prefix, int round) { - if ( ! logger().isEnabled(logging::debug::AstDumpIterations) ) +void Unit::_destroyModule() { + if ( ! _module ) return; - std::ofstream out(fmt("ast-%d.tmp", round)); - _dumpASTs(out, prefix, round); + _module->as().destroyPreservedNodes(); + _module->destroyChilds(); + _module = {}; } -Result Unit::link(const std::shared_ptr& context, const std::vector& mds) { +Result> Unit::link(const std::shared_ptr& context, + const std::vector& mds) { HILTI_DEBUG(logging::debug::Compiler, fmt("linking %u modules", mds.size())); auto cxx_unit = detail::CodeGen(context).linkUnits(mds); @@ -730,27 +415,13 @@ std::pair> Unit::readLinkerMetaData(std::i return detail::cxx::Unit::readLinkerMetaData(input); } -std::set Unit::allImported(bool code_only) const { - std::set all; - - for ( const auto& m : _modules ) { - auto cached = _lookupModule(m); - assert(cached); - - if ( code_only && ! cached->requires_compilation ) - continue; - - all.insert(cached->index); - } - - return all; -} +void Unit::resetAST() { + if ( ! _module ) + return; -void Unit::_resetNodes(const ID& id, Node* root) { - HILTI_DEBUG(logging::debug::Compiler, fmt("resetting nodes for module %s", id)); + HILTI_DEBUG(logging::debug::Compiler, fmt("resetting nodes for module %s", id())); - for ( const auto&& i : hilti::visitor::PreOrder<>().walk(root) ) { - i.node.clearCache(); + for ( auto&& i : hilti::visitor::PreOrder<>().walk(&*_module) ) { i.node.clearScope(); i.node.clearErrors(); } diff --git a/hilti/toolchain/src/compiler/visitors/apply-coercions.cc b/hilti/toolchain/src/compiler/visitors/apply-coercions.cc deleted file mode 100644 index d89830e15..000000000 --- a/hilti/toolchain/src/compiler/visitors/apply-coercions.cc +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace hilti; -using util::fmt; - -namespace { - -struct Visitor : public visitor::PreOrder { - Visitor(Unit* unit) : unit(unit) {} - Unit* unit; - bool modified = false; - - /** Returns a method call's i-th argument. */ - auto methodArgument(const expression::ResolvedOperatorBase& o, size_t i) { - auto ops = o.op2(); - - // If the argument list was the result of a coercion unpack its result. - if ( auto coerced = ops.tryAs() ) - ops = coerced->expression(); - - if ( auto ctor_ = ops.tryAs() ) { - auto ctor = ctor_->ctor(); - - // If the argument was the result of a coercion unpack its result. - if ( auto x = ctor.tryAs() ) - ctor = x->coercedCtor(); - - if ( auto args = ctor.tryAs(); args && i < args->value().size() ) - return args->value()[i]; - } - - util::cannot_be_reached(); - } - -#if 0 - void preDispatch(const Node& n, int level) override { - auto indent = std::string(level * 2, ' '); - std::cerr << "# " << indent << "> " << n.render() << std::endl; - n.scope()->render(std::cerr, " | "); - }; -#endif - - template - void replaceNode(position_t* p, T&& n) { - p->node = std::forward(n); - modified = true; - } - - /** Coerces an expression to a given type, recording an error if not possible. */ - std::optional coerceTo(Node* n, const Expression& e, const Type& t, bool contextual, bool assignment) { - if ( t == type::unknown ) - return {}; - - bitmask style = - (assignment ? CoercionStyle::TryAllForAssignment : CoercionStyle::TryAllForMatching); - - if ( contextual ) - style |= CoercionStyle::ContextualConversion; - - if ( auto c = hilti::coerceExpression(e, t, style) ) - return c.nexpr; - - n->addError(fmt("cannot coerce expression '%s' of type '%s' to type '%s'", e, e.type(), t)); - return {}; - } - - Result>> coerceCallArguments(Node* n, std::vector exprs, - std::vector params) { - // Build a tuple to coerce expression according to an OperandList. - auto src = expression::Ctor(ctor::Tuple(std::move(exprs))); - auto dst = type::OperandList::fromParameters(std::move(params)); - - auto coerced = coerceExpression(src, type::constant(dst), CoercionStyle::TryAllForFunctionCall); - if ( ! coerced ) - return result::Error("coercion failed"); - - if ( ! coerced.nexpr ) - // No change. - return {std::nullopt}; - - return {coerced.nexpr->as().ctor().as().value()}; - } - - Result>> coerceExpressions(const Type& dst, - const std::vector& exprs) { - bool changed = false; - std::vector nexprs; - - for ( const auto& e : exprs ) { - auto coerced = coerceExpression(e, type::constant(dst), CoercionStyle::TryAllForAssignment); - if ( ! coerced ) - return result::Error("coercion failed"); - - if ( coerced.nexpr ) - changed = true; - - nexprs.emplace_back(std::move(*coerced.coerced)); - } - - if ( changed ) - return {std::move(nexprs)}; - else - // No change. - return {std::nullopt}; - } - - void operator()(const Attribute& n) { - // TODO(robin): Coerce attributes with expressions. - } - - void operator()(const ctor::List& n, position_t p) { - if ( auto t = n.elementType(); t != type::unknown ) { - // Coerce all elements to the type's element type. - auto coerced = coerceExpressions(t, n.value()); - if ( ! coerced ) { - p.node.addError("type mismatch in list elements"); - return; - } - - if ( *coerced ) { - auto m = ctor::List(std::move(t), **coerced, n.meta()); - replaceNode(&p, std::move(m)); - } - } - } - - void operator()(const ctor::Map& n, position_t p) { - auto keys = util::transform(n.value(), [](const auto& e) { return e.first; }); - auto values = util::transform(n.value(), [](const auto& e) { return e.second; }); - bool changed = false; - - if ( auto t = n.keyType(); t != type::unknown ) { - // Coerce all elements to the type's element type. - auto coerced = coerceExpressions(t, keys); - if ( ! coerced ) { - p.node.addError("type mismatch in map keys"); - return; - } - - if ( *coerced ) { - keys = **coerced; - changed = true; - } - } - - if ( auto t = n.elementType(); t != type::unknown ) { - // Coerce all elements to the type's element type. - auto coerced = coerceExpressions(t, values); - if ( ! coerced ) { - p.node.addError("type mismatch in map values"); - return; - } - - if ( *coerced ) { - values = **coerced; - changed = true; - } - } - - if ( changed ) { - auto m = ctor::Map(n.keyType(), n.elementType(), util::zip2(keys, values), n.meta()); - replaceNode(&p, std::move(m)); - } - } - - void operator()(const ctor::Set& n, position_t p) { - if ( auto t = n.elementType(); t != type::unknown ) { - // Coerce all elements to the type's element type. - auto coerced = coerceExpressions(t, n.value()); - if ( ! coerced ) { - p.node.addError("type mismatch in set elements"); - return; - } - - if ( *coerced ) { - auto m = ctor::Set(std::move(t), **coerced, n.meta()); - replaceNode(&p, std::move(m)); - } - } - } - - void operator()(const ctor::Vector& n, position_t p) { - if ( auto t = n.elementType(); t != type::unknown ) { - // Coerce all elements to the type's element type. - auto coerced = coerceExpressions(t, n.value()); - if ( ! coerced ) { - p.node.addError("type mismatch in vector elements"); - return; - } - - if ( *coerced ) { - auto m = ctor::Vector(std::move(t), **coerced, n.meta()); - replaceNode(&p, std::move(m)); - } - } - } - - void operator()(const ctor::Default& n, position_t p) { - auto t = n.type(); - - if ( auto vr = t.tryAs() ) - t = vr->dereferencedType(); - - if ( auto stype = t.tryAs() ) { - if ( auto x = n.typeArguments(); x.size() ) { - if ( auto coerced = coerceCallArguments(&p.node, x, stype->parameters()); coerced && *coerced ) { - auto m = ctor::Default::setTypeArguments(n, **coerced); - replaceNode(&p, std::move(m)); - } - } - } - } - - void operator()(const declaration::Constant& n, position_t p) { - if ( auto v = n.value(); v.type() != n.type() ) { - if ( auto x = coerceTo(&p.node, v, n.type(), false, true) ) { - auto new_ = declaration::Constant::setValue(n, std::move(*x)); - replaceNode(&p, std::move(new_)); - } - } - } - - void operator()(const declaration::Parameter& n, position_t p) { - if ( auto def = n.default_(); def && def->type() != n.type() ) - if ( auto x = coerceTo(&p.node, *def, n.type(), false, true) ) { - auto m = declaration::Parameter::setDefault(n, *x); - replaceNode(&p, std::move(m)); - } - } - - void operator()(const declaration::LocalVariable& n, position_t p) { - std::optional init; - std::optional> args; - - if ( auto def = n.init(); def && def->type() != n.type() ) { - if ( auto x = coerceTo(&p.node, *def, n.type(), false, true) ) - init = std::move(*x); - } - - if ( auto stype = n.type().tryAs() ) { - if ( ! n.typeArguments().empty() ) { - if ( auto coerced = coerceCallArguments(&p.node, n.typeArguments(), stype->parameters()); - coerced && *coerced ) - args = std::move(*coerced); - } - } - - if ( init || args ) { - Declaration new_ = n; - - if ( init ) - new_ = declaration::LocalVariable::setInit(new_.as(), std::move(*init)); - - if ( args ) - new_ = declaration::LocalVariable::setTypeArguments(new_.as(), - std::move(*args)); - - replaceNode(&p, std::move(new_)); - } - } - - void operator()(const declaration::GlobalVariable& n, position_t p) { - std::optional init; - std::optional> args; - - if ( auto def = n.init(); def && def->type() != n.type() ) { - if ( auto x = coerceTo(&p.node, *def, n.type(), false, true) ) - init = std::move(*x); - } - - if ( auto stype = n.type().tryAs() ) { - if ( auto x = n.typeArguments(); x.size() ) { - if ( auto coerced = coerceCallArguments(&p.node, x, stype->parameters()); coerced && *coerced ) - args = std::move(*coerced); - } - } - - if ( init || args ) { - Declaration new_ = n; - - if ( init ) - new_ = declaration::GlobalVariable::setInit(new_.as(), std::move(*init)); - - if ( args ) - new_ = declaration::GlobalVariable::setTypeArguments(new_.as(), - std::move(*args)); - - replaceNode(&p, std::move(new_)); - } - } - - void operator()(const operator_::generic::New& n, position_t p) { - if ( auto etype = n.op0().tryAs() ) { - if ( auto stype = etype->typeValue().tryAs() ) { - auto args = n.op1().as().ctor().as().value(); - if ( auto coerced = coerceCallArguments(&p.node, args, stype->parameters()); coerced && *coerced ) { - Expression ntuple = expression::Ctor(ctor::Tuple(**coerced), n.op1().meta()); - auto nop = expression::resolved_operator::setOp1(n, std::move(ntuple)); - replaceNode(&p, nop); - } - } - } - } - - void operator()(const operator_::vector::PushBack& n, position_t p) { - // Need to coerce the element here as the normal overload resolution - // couldn't know the element type yet. - auto etype = type::effectiveType(n.op0().type()).as().elementType(); - auto elem = methodArgument(n, 0); - - if ( etype != elem.type() ) { - if ( auto x = coerceTo(&p.node, n.op2(), type::Tuple({etype}), false, true) ) { - auto nop = expression::resolved_operator::setOp2(n, *x); - replaceNode(&p, nop); - } - } - } - - void operator()(const statement::Assert& n, position_t p) { - if ( ! n.expectsException() && n.expression().type() != type::Bool() ) { - if ( auto x = coerceTo(&p.node, n.expression(), type::Bool(), true, false) ) { - auto m = statement::Assert::setCondition(n, *x); - replaceNode(&p, std::move(m)); - } - } - } - - void operator()(const statement::If& n, position_t p) { - if ( n.condition() ) { - if ( n.condition()->type() != type::Bool() ) { - if ( auto x = coerceTo(&p.node, *n.condition(), type::Bool(), true, false) ) { - auto m = statement::If::setCondition(n, *x); - replaceNode(&p, std::move(m)); - } - } - } - - else { - auto init = (*n.init()).as(); - Expression ncond = expression::UnresolvedID(init.id()); - Statement nif = statement::If::setCondition(n, ncond); - replaceNode(&p, std::move(nif)); - } - } - - void operator()(const statement::Return& n, position_t p) { - if ( auto func = p.findParent() ) { - if ( auto e = n.expression(); e && e->type() != func->get().type().result().type() ) { - if ( auto x = coerceTo(&p.node, *e, func->get().type().result().type(), false, true) ) { - auto m = statement::Return::setExpression(n, *x); - replaceNode(&p, std::move(m)); - } - } - } - else - p.node.addError("return outside of function"); - } - - void operator()(const statement::While& n, position_t p) { - if ( n.condition() ) { - if ( n.condition()->type() != type::Bool() ) { - if ( auto x = coerceTo(&p.node, *n.condition(), type::Bool(), true, false) ) { - auto m = statement::While::setCondition(n, *x); - replaceNode(&p, std::move(m)); - } - } - } - - else { - auto init = (*n.init()).as(); - auto ninit = declaration::LocalVariable::setInit(init, {}).as(); - ninit = declaration::LocalVariable::setType(ninit, init.type()).as(); - Expression ncond = expression::Assign(expression::UnresolvedID(init.id()), *init.init()); - - if ( ncond.type() != type::Bool() && ncond.type() != type::unknown ) { - if ( auto x = coerceTo(&p.node, ncond, type::Bool(), true, false) ) { - ncond = builder::equal(ncond, builder::bool_(true)); - auto nwhile = statement::While::setInit(n, ninit).as(); - nwhile = statement::While::setCondition(nwhile, ncond).as(); - replaceNode(&p, nwhile); - } - } - } - } - - void operator()(const type::struct_::Field& f, position_t p) { - if ( auto a = f.attributes() ) { - AttributeSet attrs = *a; - if ( auto x = attrs.coerceValueTo("&default", f.type()) ) { - if ( *x ) { - auto nattrs = type::struct_::Field::setAttributes(f, std::move(attrs)); - replaceNode(&p, std::move(nattrs)); - } - - return; - } - else - p.node.addError(fmt("cannot coerce default expression to type '%s'", f.type())); - } - } - - void operator()(const expression::Assign& n, position_t p) { - if ( n.source().type() != n.target().type() ) { - // We allow assignments from const to non-const here, assignment - // is by value. - if ( auto x = coerceTo(&p.node, n.source(), n.target().type(), false, true) ) { - auto m = expression::Assign::setSource(n, *x); - replaceNode(&p, std::move(m)); - } - } - } - - void operator()(const expression::BuiltinFunction& n, position_t p) { - if ( auto coerced = coerceCallArguments(&p.node, n.arguments(), n.parameters()); coerced && *coerced ) - expression::BuiltinFunction::setArguments(n, **coerced); - } - - void operator()(const expression::LogicalAnd& n, position_t p) { - expression::LogicalAnd nn = n; - bool changed = false; - - if ( n.op0().type() != type::Bool() ) { - if ( auto x = coerceTo(&p.node, n.op0(), type::Bool(), true, false) ) { - nn = expression::LogicalAnd::setOp0(nn, *x).as(); - changed = true; - } - } - - if ( n.op1().type() != type::Bool() ) { - if ( auto x = coerceTo(&p.node, n.op1(), type::Bool(), true, false) ) { - nn = expression::LogicalAnd::setOp1(nn, *x).as(); - changed = true; - } - } - - if ( changed ) - replaceNode(&p, std::move(nn)); - } - - void operator()(const expression::LogicalNot& n, position_t p) { - if ( n.expression().type() != type::Bool() ) { - if ( auto x = coerceTo(&p.node, n.expression(), type::Bool(), true, false) ) { - auto m = expression::LogicalNot::setExpression(n, *x); - replaceNode(&p, std::move(m)); - } - } - } - - void operator()(const expression::LogicalOr& n, position_t p) { - expression::LogicalOr nn = n; - bool changed = false; - - if ( n.op0().type() != type::Bool() ) { - if ( auto x = coerceTo(&p.node, n.op0(), type::Bool(), true, false) ) { - nn = expression::LogicalOr::setOp0(nn, *x).as(); - changed = true; - } - } - - if ( n.op1().type() != type::Bool() ) { - if ( auto x = coerceTo(&p.node, n.op1(), type::Bool(), true, false) ) { - nn = expression::LogicalOr::setOp1(nn, *x).as(); - changed = true; - } - } - - if ( changed ) - replaceNode(&p, std::move(nn)); - } - - void operator()(const expression::PendingCoerced& pc, position_t p) { - if ( auto ner = hilti::coerceExpression(pc.expression(), pc.type()); ner.coerced ) { - if ( ner.nexpr ) { - // A coercion expression was created, use it. - p.node = *ner.nexpr; - modified = true; - } - else { - // Coercion not needed, use original expression. - p.node = pc.expression(); - modified = true; - } - } - else - p.node.addError(fmt("cannot coerce expression '%s' to type '%s'", pc.expression(), pc.type())); - } -}; - -} // anonymous namespace - -bool hilti::detail::applyCoercions(Node* root, Unit* unit) { - util::timing::Collector _("hilti/compiler/apply-coercions"); - - auto v = Visitor(unit); - for ( auto i : v.walk(root) ) - v.dispatch(i); - return v.modified; -} diff --git a/hilti/toolchain/src/compiler/visitors/coercer.cc b/hilti/toolchain/src/compiler/visitors/coercer.cc index 77a1cca11..b9a185355 100644 --- a/hilti/toolchain/src/compiler/visitors/coercer.cc +++ b/hilti/toolchain/src/compiler/visitors/coercer.cc @@ -1,572 +1,536 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include - -#include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include +#include #include -#include +#include #include using namespace hilti; +using util::fmt; -namespace { - -struct VisitorCtor : public visitor::PreOrder, VisitorCtor> { - VisitorCtor(const Type& dst, bitmask style) : dst(dst), style(style) {} +namespace hilti::logging::debug { +inline const hilti::logging::DebugStream Coercer("coercer"); +} // namespace hilti::logging::debug - const Type& dst; - bitmask style; +namespace { - result_t operator()(const ctor::Enum& c) { - if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) - return ctor::Bool(c.value().id() != ID("Undef"), c.meta()); +struct Visitor : public visitor::PreOrder { + Visitor(Unit* unit) : unit(unit) {} + Unit* unit; + bool modified = false; - return {}; + // Log debug message recording updating attributes. + void logChange(const Node& old, const Node& new_, const char* desc) { + HILTI_DEBUG(logging::debug::Coercer, + util::fmt("[%s] %s -> %s %s (%s)", old.typename_(), old, desc, new_, old.location())); } - result_t operator()(const ctor::Map& c) { - if ( auto t = dst.tryAs() ) { - std::vector nelemns; - for ( const auto& e : c.value() ) { - auto k = hilti::coerceExpression(e.first, t->keyType(), style); - auto v = hilti::coerceExpression(e.second, t->elementType(), style); - - if ( k && v ) - nelemns.emplace_back(*k.coerced, *v.coerced); - else - return {}; - } - - return ctor::Map(t->keyType(), t->elementType(), nelemns, c.meta()); - } + /** Returns a method call's i-th argument. */ + const Expression& methodArgument(const expression::ResolvedOperatorBase& o, size_t i) { + auto ops = o.op2(); - return {}; - } + // If the argument list was the result of a coercion unpack its result. + if ( auto coerced = ops.tryAs() ) + ops = coerced->expression(); - result_t operator()(const ctor::Null& c) { - if ( auto t = dst.tryAs() ) - return ctor::Optional(t->dereferencedType()); + if ( auto ctor_ = ops.tryAs() ) { + auto ctor = ctor_->ctor(); - if ( auto t = dst.tryAs() ) - return ctor::StrongReference(t->dereferencedType()); + // If the argument was the result of a coercion unpack its result. + if ( auto x = ctor.tryAs() ) + ctor = x->coercedCtor(); - if ( auto t = dst.tryAs() ) - return ctor::WeakReference(t->dereferencedType()); + if ( auto args = ctor.tryAs(); args && i < args->value().size() ) + return args->value()[i]; + } - return {}; + util::cannot_be_reached(); } - result_t operator()(const ctor::List& c) { - if ( auto t = dst.tryAs() ) { - std::vector nexprs; - for ( const auto& e : c.value() ) { - if ( auto x = hilti::coerceExpression(e, t->elementType(), CoercionStyle::TryAllForAssignment) ) - nexprs.push_back(*x.coerced); - else - return {}; - } - return ctor::List(t->elementType(), std::move(nexprs), c.meta()); - } + /** + * Coerces an expression to a given type, return the new value if it's + * changed from the the old one. Records an error with the node if coercion + * is not possible. Will indicate no-chage if expression or type hasn't + * been resolved. + **/ + std::optional coerceTo(Node* n, const Expression& e, const Type& t, bool contextual, bool assignment) { + if ( ! (expression::isResolved(e) && type::isResolved(t)) ) + return {}; - if ( auto t = dst.tryAs() ) { - auto dt = t->isWildcard() ? c.elementType() : t->elementType(); + if ( e.type() == t ) + return {}; - std::vector nexprs; - for ( const auto& e : c.value() ) { - if ( auto x = hilti::coerceExpression(e, dt, CoercionStyle::TryAllForAssignment) ) - nexprs.push_back(*x.coerced); - else - return {}; - } - return ctor::Vector(dt, std::move(nexprs), c.meta()); - } + bitmask style = + (assignment ? CoercionStyle::TryAllForAssignment : CoercionStyle::TryAllForMatching); - if ( auto t = dst.tryAs() ) { - auto dt = t->isWildcard() ? c.elementType() : t->elementType(); + if ( contextual ) + style |= CoercionStyle::ContextualConversion; - std::vector nexprs; - for ( const auto& e : c.value() ) { - if ( auto x = hilti::coerceExpression(e, dt, CoercionStyle::TryAllForAssignment) ) - nexprs.push_back(*x.coerced); - else - return {}; - } - return ctor::Set(dt, std::move(nexprs), c.meta()); - } + if ( auto c = hilti::coerceExpression(e, t, style) ) + return c.nexpr; + n->addError(fmt("cannot coerce expression '%s' of type '%s' to type '%s'", e, e.type(), t)); return {}; } - result_t operator()(const ctor::Real& c) { - // Note: double->Integral constant conversions check 'non-narrowing' via - // double->Int->double roundtrip - the generated code looks good. + template + Result>> coerceCallArguments(Container1 exprs, Container2 params) { + // Build a tuple to coerce expression according to an OperandList. + if ( ! expression::isResolved(exprs) ) + return {std::nullopt}; - if ( auto t = dst.tryAs() ) { - double d = c.value(); + auto src = expression::Ctor(ctor::Tuple(std::move(exprs.copy()))); + auto dst = type::OperandList::fromParameters(std::move(params)); - if ( static_cast(static_cast(d)) == d ) { - switch ( t->isWildcard() ? 64 : t->width() ) { - case 8: - if ( static_cast(int8_t(d)) == d ) - return ctor::SignedInteger(int64_t(d), 8, c.meta()); - break; + auto coerced = coerceExpression(src, type::constant(dst), CoercionStyle::TryAllForFunctionCall); + if ( ! coerced ) + return result::Error("coercion failed"); - case 16: - if ( static_cast(static_cast(d)) == d ) - return ctor::SignedInteger(static_cast(d), 16, c.meta()); - break; + if ( ! coerced.nexpr ) + // No change. + return {std::nullopt}; - case 32: - if ( static_cast(static_cast(d)) == d ) - return ctor::SignedInteger(static_cast(d), 32, c.meta()); - break; + return {coerced.nexpr->template as().ctor().template as().value().copy()}; + } - case 64: return ctor::SignedInteger(static_cast(d), 64, c.meta()); break; - } - } - } + // Will do nothing if expressions or type aren't resolved. + template + Result>> coerceExpressions(const Container& exprs, const Type& dst) { + if ( ! type::isResolved(dst) ) + return {std::nullopt}; - if ( auto t = dst.tryAs() ) { - double d = c.value(); + for ( const auto& e : exprs ) { + if ( ! expression::isResolved(e) ) + return {std::nullopt}; + } - if ( static_cast(static_cast(d)) == d ) { - switch ( t->isWildcard() ? 64 : t->width() ) { - case 8: - if ( static_cast(static_cast(d)) == d ) - return ctor::UnsignedInteger(static_cast(d), 8, c.meta()); - break; + bool changed = false; + std::vector nexprs; - case 16: - if ( static_cast(static_cast(d)) == d ) - return ctor::UnsignedInteger(uint64_t(d), 16, c.meta()); - break; + for ( const auto& e : exprs ) { + auto coerced = coerceExpression(e, type::constant(dst), CoercionStyle::TryAllForAssignment); + if ( ! coerced ) + return result::Error("coercion failed"); - case 32: - if ( static_cast(static_cast(d)) == d ) - return ctor::UnsignedInteger(static_cast(d), 32, c.meta()); - break; + if ( coerced.nexpr ) + changed = true; - case 64: return ctor::UnsignedInteger(static_cast(d), 64, c.meta()); break; - } - } + nexprs.emplace_back(std::move(*coerced.coerced)); } - return {}; + if ( changed ) + return {std::move(nexprs)}; + else + // No change. + return {std::nullopt}; } - result_t operator()(const ctor::Set& c) { - if ( auto t = dst.tryAs() ) { - std::vector nexprs; - for ( const auto& e : c.value() ) { - if ( auto x = hilti::coerceExpression(e, t->elementType(), style) ) - nexprs.push_back(*x.coerced); - else - return {}; - } - return ctor::Set(t->elementType(), std::move(nexprs), c.meta()); - } - - return {}; + void operator()(const Attribute& n) { + // TODO(robin): Coerce attributes with expressions. } - result_t operator()(const ctor::SignedInteger& c) { - if ( auto t = dst.tryAs() ) { - if ( t->width() == 64 ) - return c; - - int64_t i = c.value(); - - if ( t->isWildcard() ) - return ctor::SignedInteger(i, c.type().width(), c.meta()); - - if ( auto [imin, imax] = util::signed_integer_range(t->width()); i >= imin && i <= imax ) - return ctor::SignedInteger(i, t->width(), c.meta()); + void operator()(const ctor::List& n, position_t p) { + if ( auto coerced = coerceExpressions(n.value(), n.elementType()) ) { + if ( *coerced ) { + logChange(p.node, ctor::Tuple(**coerced), "elements"); + p.node.as().setValue(std::move(**coerced)); + modified = true; + } } + else { + if ( n.type().elementType() != type::unknown ) + p.node.addError("type mismatch in list elements"); + } + } - if ( auto t = dst.tryAs(); t && c.value() >= 0 ) { - auto u = static_cast(c.value()); - - if ( t->isWildcard() ) - return ctor::UnsignedInteger(u, c.type().width(), c.meta()); + void operator()(const ctor::Map& n, position_t p) { + if ( ! (type::isResolved(n.keyType()) && type::isResolved(n.valueType())) ) + return; - if ( auto [zero, umax] = util::unsigned_integer_range(t->width()); u <= umax ) - return ctor::UnsignedInteger(u, t->width(), c.meta()); + for ( const auto& e : n.value() ) { + if ( ! (expression::isResolved(e.key()) && expression::isResolved(e.value())) ) + return; } - if ( auto t = dst.tryAs() ) { - if ( static_cast(static_cast(c.value())) == c.value() ) - return ctor::Real(static_cast(c.value())); - } + bool changed = false; - if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) - return ctor::Bool(c.value() != 0, c.meta()); + std::vector nelems; + for ( const auto& e : n.value() ) { + auto k = coerceExpression(e.key(), n.keyType()); + if ( ! k ) { + p.node.addError("type mismatch in map keys"); + return; + } - return {}; - } + auto v = coerceExpression(e.value(), n.valueType()); + if ( ! v ) { + p.node.addError("type mismatch in map values"); + return; + } - result_t operator()(const ctor::Vector& c) { - if ( auto t = dst.tryAs() ) { - std::vector nexprs; - for ( const auto& e : c.value() ) { - if ( auto x = hilti::coerceExpression(e, t->elementType(), style) ) - nexprs.push_back(*x.coerced); - else - return {}; + if ( k.nexpr || v.nexpr ) { + nelems.emplace_back(*k.coerced, *v.coerced); + changed = true; } - return ctor::Vector(t->elementType(), std::move(nexprs), c.meta()); + else + nelems.push_back(e); } - return {}; - } - - result_t operator()(const ctor::UnsignedInteger& c) { - if ( auto t = dst.tryAs() ) { - if ( t->width() == 64 ) - return c; - - uint64_t u = c.value(); - - if ( t->isWildcard() ) - return ctor::UnsignedInteger(u, c.type().width(), c.meta()); - - if ( auto [umin, umax] = util::unsigned_integer_range(t->width()); u >= umin && u <= umax ) - return ctor::UnsignedInteger(u, t->width(), c.meta()); + if ( changed ) { + logChange(p.node, ctor::Map(nelems), "value"); + p.node.as().setValue(std::move(nelems)); + modified = true; } + } - if ( auto t = dst.tryAs(); t && static_cast(c.value()) >= 0 ) { - auto i = static_cast(c.value()); - - if ( t->isWildcard() ) - return ctor::SignedInteger(i, c.type().width(), c.meta()); - - if ( auto [imin, imax] = util::signed_integer_range(t->width()); i >= imin && i <= imax ) - return ctor::SignedInteger(i, t->width(), c.meta()); + void operator()(const ctor::Set& n, position_t p) { + auto coerced = coerceExpressions(n.value(), n.elementType()); + if ( ! coerced ) + p.node.addError("type mismatch in set elements"); + else if ( *coerced ) { + logChange(p.node, ctor::Tuple(**coerced), "value"); + p.node.as().setValue(std::move(**coerced)); + modified = true; } + } - if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) - return ctor::Bool(c.value() != 0, c.meta()); - - if ( auto t = dst.tryAs() ) { - if ( static_cast(static_cast(c.value())) == c.value() ) - return ctor::Real(static_cast(c.value())); + void operator()(const ctor::Vector& n, position_t p) { + auto coerced = coerceExpressions(n.value(), n.elementType()); + if ( ! coerced ) + p.node.addError("type mismatch in vector elements"); + else if ( *coerced ) { + logChange(p.node, ctor::Tuple(**coerced), "value"); + p.node.as().setValue(std::move(**coerced)); + modified = true; } - - return {}; } - result_t operator()(const ctor::Tuple& c) { - if ( auto t = dst.tryAs() ) { - auto vc = c.value(); - auto vt = t.value().types(); + void operator()(const ctor::Default& n, position_t p) { + if ( ! type::isResolved(n.type()) ) + return; - if ( vc.size() != vt.size() ) - return {}; + auto t = n.type(); - std::vector coerced; - coerced.reserve(vc.size()); + if ( auto vr = t.tryAs() ) + t = vr->dereferencedType(); - for ( auto i = std::make_pair(vc.cbegin(), vt.cbegin()); i.first != vc.cend(); ++i.first, ++i.second ) { - if ( auto x = hilti::coerceExpression(*i.first, *i.second, CoercionStyle::TryAllForAssignment) ) { - coerced.push_back(*x.coerced); + if ( type::takesArguments(t) ) { + if ( auto x = n.typeArguments(); x.size() ) { + if ( auto coerced = coerceCallArguments(x, t.parameters()); coerced && *coerced ) { + logChange(p.node, ctor::Tuple(**coerced), "call arguments"); + p.node.as().setTypeArguments(std::move(**coerced)); + modified = true; } - else - return {}; } - - return ctor::Tuple(std::move(coerced), c.meta()); } - - return {}; } - result_t operator()(const ctor::Struct& c) { - auto dst_ = dst; - - if ( (dst.isA() || dst.isA()) && ! type::isReferenceType(dst) ) - // Allow coercion from value to reference type with new instance. - dst_ = dst.dereferencedType(); - - if ( auto dtype = dst_.tryAs() ) { - auto stype = c.type().as(); - - std::set src_fields; - for ( const auto& f : stype.fields() ) - src_fields.insert(f.id()); + void operator()(const declaration::Constant& n, position_t p) { + if ( auto x = coerceTo(&p.node, n.value(), n.type(), false, true) ) { + logChange(p.node, *x, "value"); + p.node.as().setValue(std::move(*x)); + modified = true; + } + } - std::set dst_fields; - for ( const auto& f : dtype->fields() ) - dst_fields.insert(f.id()); + void operator()(const declaration::Parameter& n, position_t p) { + if ( auto def = n.default_() ) { + if ( auto x = coerceTo(&p.node, *def, n.type(), false, true) ) { + logChange(p.node, *x, "default value"); + p.node.as().setDefault(std::move(*x)); + modified = true; + } + } + } - // Check for fields in ctor that type does not have. - if ( ! util::set_difference(src_fields, dst_fields).empty() ) - return {}; + void operator()(const declaration::LocalVariable& n, position_t p) { + std::optional init; + std::optional> args; - // Check for fields in type that ctor does not have, they must be - // optional, - auto x = util::set_difference(dst_fields, src_fields); + if ( auto e = n.init() ) { + if ( auto x = coerceTo(&p.node, *e, n.type(), false, true) ) + init = std::move(*x); + } - std::set can_be_missing; + if ( type::takesArguments(n.type()) && n.typeArguments().size() ) { + auto coerced = coerceCallArguments(n.typeArguments(), n.type().parameters()); + if ( coerced && *coerced ) + args = std::move(*coerced); + } - for ( const auto& k : x ) { - auto f = dtype->field(k); - if ( f->isOptional() || f->default_() || f->type().isA() ) - can_be_missing.insert(k); + if ( init || args ) { + if ( init ) { + logChange(p.node, *init, "init expression"); + p.node.as().setInit(std::move(*init)); } - x = util::set_difference(x, can_be_missing); - - if ( ! x.empty() ) - // Uninitialized fields. - return {}; - - // Coerce each field. - std::vector nf; - - for ( const auto& sf : stype.fields() ) { - auto df = dtype->field(sf.id()); - auto se = c.field(sf.id()); - assert(df && se); - if ( auto ne = hilti::coerceExpression((*se).second, df->type(), style) ) - nf.emplace_back(sf.id(), *ne.coerced); - else - // Cannot coerce. - return {}; + if ( args ) { + logChange(p.node, ctor::Tuple(*args), "type arguments"); + p.node.as().setTypeArguments(std::move(*args)); } - return ctor::Struct(std::move(nf), *dtype, c.meta()); + modified = true; } - - return {}; - } -}; - -struct VisitorType : public visitor::PreOrder, VisitorType> { - VisitorType(const Type& dst, bitmask style) : dst(dst), style(style) {} - - const Type& dst; - bitmask style; - - result_t operator()(const type::Enum& c) { - if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) - return dst; - - return {}; } - result_t operator()(const type::Null& c) { - if ( auto t = dst.tryAs() ) - return dst; + void operator()(const declaration::GlobalVariable& n, position_t p) { + std::optional init; + std::optional> args; - if ( auto t = dst.tryAs() ) - return dst; + if ( auto e = n.init() ) { + if ( auto x = coerceTo(&p.node, *e, n.type(), false, true) ) + init = std::move(*x); + } - if ( auto t = dst.tryAs() ) - return dst; + if ( type::takesArguments(n.type()) && n.typeArguments().size() ) { + auto coerced = coerceCallArguments(n.typeArguments(), n.type().parameters()); + if ( coerced && *coerced ) + args = std::move(*coerced); + } - return {}; - } + if ( init || args ) { + if ( init ) { + logChange(p.node, *init, "init expression"); + p.node.as().setInit(std::move(*init)); + } - result_t operator()(const type::Bytes& c) { - if ( dst.tryAs() && (style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall)) ) - return dst; + if ( args ) { + logChange(p.node, ctor::Tuple(*args), "type arguments"); + p.node.as().setTypeArguments(std::move(*args)); + } - return {}; + modified = true; + } } - result_t operator()(const type::Error& e) { - if ( auto t = dst.tryAs() ) - return dst; - - return {}; + void operator()(const operator_::generic::New& n, position_t p) { + auto etype = n.op0().tryAs(); + if ( ! etype ) + return; + + if ( type::takesArguments(etype->typeValue()) ) { + auto args = n.op1().as().ctor().as().value(); + if ( auto coerced = coerceCallArguments(args, etype->typeValue().parameters()); coerced && *coerced ) { + Expression ntuple = expression::Ctor(ctor::Tuple(**coerced), n.op1().meta()); + logChange(p.node, ntuple, "type arguments"); + p.node.as().setOp1(std::move(ntuple)); + modified = true; + } + } } - result_t operator()(const type::List& e) { - if ( auto t = dst.tryAs(); t && t->elementType() == e.elementType() ) - return dst; + void operator()(const operator_::vector::PushBack& n, position_t p) { + if ( ! (expression::isResolved(n.op0()) && expression::isResolved(n.op2())) ) + return; - if ( auto t = dst.tryAs(); t && t->elementType() == e.elementType() ) - return dst; + // Need to coerce the element here as the normal overload resolution + // couldn't know the element type yet. + auto etype = n.op0().type().as().elementType(); + auto elem = methodArgument(n, 0); - return {}; + if ( auto x = coerceTo(&p.node, n.op2(), type::Tuple({etype}), false, true) ) { + logChange(p.node, *x, "element type"); + p.node.as().setOp2(std::move(*x)); + modified = true; + } } - result_t operator()(const type::Optional& r) { - if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) - return dst; + void operator()(const statement::Assert& n, position_t p) { + if ( n.expectsException() ) + return; - return {}; + if ( auto x = coerceTo(&p.node, n.expression(), type::Bool(), true, false) ) { + logChange(p.node, *x, "expression"); + p.node.as().setCondition(std::move(*x)); + modified = true; + } } - result_t operator()(const type::StrongReference& r) { - if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) - return dst; - - if ( type::isReferenceType(dst) ) { - if ( type::sameExceptForConstness(r.dereferencedType(), dst.dereferencedType()) ) - return dst; + void operator()(const statement::If& n, position_t p) { + if ( auto cond = n.condition() ) { + if ( auto x = coerceTo(&p.node, *cond, type::Bool(), true, false) ) { + logChange(p.node, *x, "condition"); + p.node.as().setCondition(std::move(*x)); + modified = true; + } } + } - if ( ! (style & CoercionStyle::Assignment) ) { - if ( r.dereferencedType() == dst ) - return dst; + void operator()(const statement::Return& n, position_t p) { + auto func = p.findParent(); + if ( ! func ) { + p.node.addError("return outside of function"); + return; } - return {}; - } - - result_t operator()(const type::Result& r) { - if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) - return dst; + auto e = n.expression(); + if ( ! e ) + return; - if ( auto t = dst.tryAs(); t && t->dereferencedType() == r.dereferencedType() ) - return dst; + const auto& t = func->get().ftype().result().type(); - return {}; + if ( auto x = coerceTo(&p.node, *e, t, false, true) ) { + logChange(p.node, *x, "expression"); + p.node.as().setExpression(std::move(*x)); + modified = true; + } } - result_t operator()(const type::SignedInteger& src) { - if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) - return dst; - - if ( auto t = dst.tryAs() ) { - if ( src.width() <= t->width() ) - return dst; + void operator()(const statement::While& n, position_t p) { + if ( auto cond = n.condition() ) { + if ( auto x = coerceTo(&p.node, *cond, type::Bool(), true, false) ) { + logChange(p.node, *x, "condition"); + p.node.as().setCondition(std::move(*x)); + modified = true; + } } - - return {}; } - result_t operator()(const type::Stream& c) { - if ( auto t = dst.tryAs() ) - return dst; + void operator()(const declaration::Field& f, position_t p) { + if ( auto a = f.attributes() ) { + AttributeSet attrs = *a; + if ( auto x = attrs.coerceValueTo("&default", f.type()) ) { + if ( *x ) { + logChange(p.node, attrs, "attributes"); + p.node.as().setAttributes(std::move(attrs)); + modified = true; + } - return {}; + return; + } + else + p.node.addError(fmt("cannot coerce default expression to type '%s'", f.type())); + } } - result_t operator()(const type::stream::View& c) { - if ( dst.tryAs() && (style & (CoercionStyle::Assignment | CoercionStyle::FunctionCall)) ) - return dst; - - return {}; + void operator()(const expression::Assign& n, position_t p) { + // We allow assignments from const to non-const here, assignment + // is by value. + if ( auto x = coerceTo(&p.node, n.source(), n.target().type(), false, true) ) { + logChange(p.node, *x, "source"); + p.node.as().setSource(std::move(*x)); + modified = true; + } } - result_t operator()(const type::Type_& src) { - if ( auto t = dst.tryAs() ) { - // We don't allow arbitrary coercions here, just (more or less) direct matches. - if ( auto x = hilti::coerceType(src.typeValue(), t->typeValue(), CoercionStyle::TryDirectForMatching) ) - return type::Type_(*x); + void operator()(const expression::BuiltinFunction& n, position_t p) { + if ( auto coerced = coerceCallArguments(n.arguments(), n.parameters()); coerced && *coerced ) { + logChange(p.node, ctor::Tuple(**coerced), "call arguments"); + p.node.as().setArguments(std::move(**coerced)); + modified = true; } - - return {}; } - result_t operator()(const type::Union& c) { - if ( auto t = dst.tryAs(); t && (style & CoercionStyle::ContextualConversion) ) - return dst; + void operator()(const expression::LogicalAnd& n, position_t p) { + if ( auto x = coerceTo(&p.node, n.op0(), type::Bool(), true, false) ) { + logChange(p.node, *x, "op0"); + p.node.as().setOp0(std::move(*x)); + modified = true; + } - return {}; + if ( auto x = coerceTo(&p.node, n.op1(), type::Bool(), true, false) ) { + logChange(p.node, *x, "op1"); + p.node.as().setOp1(std::move(*x)); + modified = true; + } } - result_t operator()(const type::UnsignedInteger& src) { - if ( dst.isA() && (style & CoercionStyle::ContextualConversion) ) - return dst; - - if ( auto t = dst.tryAs() ) { - if ( src.width() <= t->width() ) - return dst; + void operator()(const expression::LogicalNot& n, position_t p) { + if ( auto x = coerceTo(&p.node, n.expression(), type::Bool(), true, false) ) { + logChange(p.node, *x, "expression"); + p.node.as().setExpression(std::move(*x)); + modified = true; } + } - if ( auto t = dst.tryAs() ) { - // As long as the target type has more bits, we can coerce. - if ( src.width() < t->width() ) - return dst; + void operator()(const expression::LogicalOr& n, position_t p) { + if ( auto x = coerceTo(&p.node, n.op0(), type::Bool(), true, false) ) { + logChange(p.node, *x, "op0"); + p.node.as().setOp0(std::move(*x)); + modified = true; } - return {}; + if ( auto x = coerceTo(&p.node, n.op1(), type::Bool(), true, false) ) { + logChange(p.node, *x, "op1"); + p.node.as().setOp1(std::move(*x)); + modified = true; + } } - result_t operator()(const type::Tuple& src) { - if ( auto t = dst.tryAs() ) { - auto vc = src.types(); - auto vt = t->types(); - - if ( vc.size() != vt.size() ) - return {}; - - for ( auto i = std::make_pair(vc.cbegin(), vt.cbegin()); i.first != vc.cend(); ++i.first, ++i.second ) { - if ( auto x = hilti::coerceType(*i.first, *i.second); ! x ) - return {}; + void operator()(const expression::PendingCoerced& pc, position_t p) { + if ( auto ner = hilti::coerceExpression(pc.expression(), pc.type()); ner.coerced ) { + if ( ner.nexpr ) { + // A coercion expression was created, use it. + p.node = *ner.nexpr; + modified = true; + } + else { + // Coercion not needed, use original expression. + p.node = pc.expression(); + modified = true; } - - return dst; } - - return {}; + else + p.node.addError(fmt("cannot coerce expression '%s' to type '%s'", pc.expression(), pc.type())); } - result_t operator()(const type::ValueReference& r) { - if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) - return hilti::coerceType(r.dereferencedType(), dst, style); + void operator()(const operator_::tuple::CustomAssign& n, position_t p) { + if ( ! (expression::isResolved(n.op0()) && expression::isResolved(n.op1())) ) + return; - if ( type::isReferenceType(dst) ) { - if ( type::sameExceptForConstness(r.dereferencedType(), dst.dereferencedType()) ) - return dst; - } + auto lhs = n.op0().as().ctor().as(); + auto lhs_type = lhs.type().as(); + auto rhs_type = n.op1().type().tryAs(); - if ( r.dereferencedType() == dst ) - return dst; + if ( ! rhs_type || lhs_type.elements().size() != rhs_type->elements().size() ) + // Validator will catch these. + return; - return {}; - } + if ( lhs_type == *rhs_type ) + // Nothing to coerce. + return; - result_t operator()(const type::WeakReference& r) { - if ( auto t = dst.tryAs(); (style & CoercionStyle::ContextualConversion) && t ) - return dst; + bool changed = false; + std::vector new_elems; - if ( type::isReferenceType(dst) ) { - if ( type::sameExceptForConstness(r.dereferencedType(), dst.dereferencedType()) ) - return dst; - } + for ( auto i = 0u; i < lhs_type.elements().size(); i++ ) { + const auto& lhs_elem_type = lhs_type.elements()[i].type(); + auto rhs_elem_type = rhs_type->elements()[i].type(); + auto rhs_elem = + expression::TypeWrapped(operator_::tuple::Index::Operator().instantiate({n.op1(), builder::integer(i)}, + n.meta()), + rhs_elem_type); - if ( ! (style & CoercionStyle::Assignment) ) { - if ( r.dereferencedType() == dst ) - return dst; + if ( auto x = coerceTo(&p.node, rhs_elem, lhs_elem_type, false, true) ) { + changed = true; + new_elems.push_back(std::move(*x)); + } + else + new_elems.push_back(std::move(rhs_elem)); } - return {}; + if ( changed ) { + auto new_rhs = builder::tuple(std::move(new_elems)); + logChange(p.node, new_rhs, "tuple assign"); + p.node.as().setOp1(std::move(new_rhs)); + modified = true; + } } }; } // anonymous namespace -// Plugin-specific version just kicking off the local visitor. -std::optional detail::coerceCtor(Ctor c, const Type& dst, bitmask style) { - if ( auto nc = VisitorCtor(dst, style).dispatch(std::move(c)) ) - return *nc; - - return {}; -} +bool hilti::detail::ast::coerce(Node* root, Unit* unit) { + util::timing::Collector _("hilti/compiler/ast/coerce"); -// Plugin-specific version just kicking off the local visitor. -std::optional detail::coerceType(Type t, const Type& dst, bitmask style) { - if ( auto nt = VisitorType(dst, style).dispatch(std::move(t)) ) - return *nt; + auto v = Visitor(unit); + for ( auto i : v.walk(root) ) + v.dispatch(i); - return {}; + return v.modified; } diff --git a/hilti/toolchain/src/compiler/visitors/id-resolver.cc b/hilti/toolchain/src/compiler/visitors/id-resolver.cc deleted file mode 100644 index e3f3c360c..000000000 --- a/hilti/toolchain/src/compiler/visitors/id-resolver.cc +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace hilti; - -namespace { - -inline const hilti::logging::DebugStream Resolver("resolver"); - -struct Visitor : public visitor::PreOrder { - explicit Visitor(Unit* unit) : unit(unit) {} - Unit* unit; - ID module_id = ID(""); - bool modified = false; - - template - void replaceNode(position_t* p, T&& n, int line, bool set_modified = true) { - p->node = std::forward(n); - if ( set_modified ) { - HILTI_DEBUG(Resolver, hilti::util::fmt(" modified by HILTI %s/%d", - hilti::rt::filesystem::path(__FILE__).filename().native(), line)) - modified = true; - } - } - -#if 0 - void preDispatch(const Node& n, int level) override { - auto indent = std::string(level * 2, ' '); - std::cerr << "# " << indent << "> " << n.render() << std::endl; - n.scope()->render(std::cerr, " | "); - } -#endif - - void operator()(const Module& m) { module_id = m.id(); } - - void operator()(const type::UnresolvedID& u, position_t p) { - auto resolved = scope::lookupID(u.id(), p, "type"); - - if ( ! resolved ) { - p.node.addError(resolved.error()); - return; - } - - Type t = type::ResolvedID(resolved->second, NodeRef(resolved->first), u.meta()); - - if ( resolved->first->as().isOnHeap() ) { - // TODO(robin): This logic is pretty brittle as we need make sure - // to skip the transformation for certain AST nodes. Not sure how - // to improve this. - auto pc = p.parent().tryAs(); - auto pe = p.parent().tryAs(); - auto pt = p.parent().tryAs(); - - auto replace = true; - - if ( pt && type::isReferenceType(*pt) ) - replace = false; - - if ( pc && type::isReferenceType(pc->type()) ) - replace = false; - - if ( pc && pc->isA() ) - replace = false; - - if ( pe && pe->isA() ) - replace = false; - - if ( pe && pe->isA() ) { - if ( pe->isA() ) - replace = false; - - if ( pe->isA() ) - replace = false; - - if ( pe->isA() ) - replace = false; - } - - if ( pe && pe->isA() ) { - if ( pe->as().kind() == operator_::Kind::Deref ) - replace = false; - } - - if ( pe && pe->isA() ) - replace = false; - - if ( replace ) - t = type::ValueReference(t, Location("")); - } - - replaceNode(&p, t, __LINE__); - } - - void operator()(const type::Computed& u, position_t p) { - // As soon as we now the computed type, we swap it in. - if ( auto t = u.type(); ! t.isA() ) { - if ( auto id = t.typeID() ) - replaceNode(&p, type::UnresolvedID(*id, p.node.meta()), __LINE__); - else - replaceNode(&p, t, __LINE__); - } - } - - void operator()(const statement::Return& r, position_t p) { - if ( ! r.expression() || r.expression()->type().isA() ) - return; - - // Resolve any `auto` return type. - const auto& function = p.findParent(); - if ( ! function ) - return; - - if ( auto a = function->get().type().result().type().tryAs(); a && ! a->isSet() ) { - a->typeNode() = r.expression()->type(); - modified = true; - } - } - - void operator()(const expression::ResolvedOperator& r, position_t p) { - // For calls that have been resolved to user-defined functions and - // methods, check if any of the targetis have `auto` parameters thar - // aren't resolved yet. If so, replace those automatic types with the - // type of the argument that we know now. - - std::vector params; - - if ( r.operator_().kind() == operator_::Kind::Call && r.op0().type().isA() ) - // Call to function. - params = r.op0().type().as().parameters(); - else if ( r.operator_().kind() == operator_::Kind::MemberCall && r.op0().type().isA() && - r.op1().type().isA() ) - // Call to struct method. - params = r.op1().type().as().parameters(); - else - // Not interested in these. - return; - - for ( auto&& [arg_, param] : util::enumerate(params) ) { - auto at = param.type().tryAs(); - if ( ! at || at->isSet() ) - continue; - - // Compute new type for 'auto' parameter. - auto arg = arg_; // need this so that the lambda can capture it - Type type = type::Computed(NodeRef(p.node), [arg](Node& n) { - auto r = n.as(); - switch ( r.operator_().kind() ) { - case operator_::Kind::Call: - return type::effectiveType( - r.op1().as().ctor().as().value()[arg].type()); - - case operator_::Kind::MemberCall: - return type::effectiveType( - r.op2().as().ctor().as().value()[arg].type()); - - default: hilti::util::cannot_be_reached(); - } - - hilti::util::cannot_be_reached(); - }); - - param.type().as().typeNode() = type; - modified = true; - } - } - - void operator()(const expression::UnresolvedID& u, position_t p) { - auto resolved = scope::lookupID(u.id(), p, "declaration"); - - if ( ! resolved ) { - p.node.addError(resolved.error()); - return; - } - - if ( auto t = resolved->first->tryAs() ) { - auto nt = type::setTypeID(t->type(), resolved->second); - if ( ! t->typeID() ) - *resolved->first = declaration::Type::setType(*t, nt); - - replaceNode(&p, expression::Type_(nt, u.meta()), __LINE__); - return; - } - - // If we are inside a call expression, leave it alone. The operator - // resolver will take care of that. - if ( auto op = p.parent().tryAs(); op && op->kind() == operator_::Kind::Call ) - return; - - replaceNode(&p, expression::ResolvedID(resolved->second, NodeRef(resolved->first), u.meta()), __LINE__); - } - - void operator()(const expression::ResolvedID& u, position_t p) { - auto& parent = p.parent(); - if ( auto op = parent.tryAs(); - op && op->operator_().kind() == operator_::Kind::Call ) - return; - - if ( auto op = parent.tryAs(); op && op->kind() == operator_::Kind::Call ) - // If we are inside a call expression, leave it alone. The operator - // resolver will take care of that. - return; - - // We can get into trouble when re-look-upping "self" inside a new - // context. As a work-around, just ignore self here. - // TODO: We should do this differently, and ideally not need any - // re-loopup anyways (see below) - if ( u.id().str() == "self" ) - return; - - // Look it up again because the AST may have changed the mapping. - // - // TODO(robin): Not quite sure in which cases this happen, ideally it - // shouldn't be necessary to re-lookup an ID once it has been - // resolved. - auto resolved = scope::lookupID(u.id(), p, "declaration"); - - if ( ! resolved ) - return; - - // We replace the node, but don't flag the AST as modified because that - // could loop. - // - // Note: We *always* make the replacement even if nothing has changed - // because it's actually expensive to find out if the new node - // differs from the old. Originally, there was an if-statement (*) - // here, but it turns out that's super-expensive in terms of CPU - // performance, presumably because it needs to cycle through - // potentially large ASTs for the comparision. There was some - // evidence that it's expensive only in a debug build, but I didn't - // further investigate; just always doing the replacement seems to be - // the cheapest approach either way. - // - // (*) if ( (! u.isValid()) || u.declaration() != resolved->first->as() ) - replaceNode(&p, expression::ResolvedID(resolved->second, NodeRef(resolved->first), u.meta()), __LINE__, false); - } - - void operator()(const declaration::Type& d, position_t p) { - auto type_id = ID(module_id, d.id()); - - std::optional cxx_id; - - if ( auto a = AttributeSet::find(d.attributes(), "&cxxname") ) - cxx_id = *a->valueAs(); - - if ( d.type().typeID() != type_id ) { - auto nt = type::setTypeID(d.type(), std::move(type_id)); - - if ( cxx_id && d.cxxID() != *cxx_id ) - nt = type::setCxxID(nt, std::move(*cxx_id)); - - replaceNode(&p, declaration::Type::setType(d, nt), __LINE__); - } - - else if ( cxx_id && d.cxxID() != *cxx_id ) { - auto nt = type::setCxxID(d.type(), std::move(*cxx_id)); - replaceNode(&p, declaration::Type::setType(d, nt), __LINE__); - } - } -}; - -// 2nd pass of further transformations that would intefere with those in the -// 1st pass. -// -// TODO: This could eventually become a separate type-resolver pass. -struct Visitor2 : public visitor::PostOrder { - explicit Visitor2(Unit* unit) : unit(unit) {} - Unit* unit; - bool modified = false; - - template - void replaceNode(position_t* p, T&& n, int line, bool set_modified = true) { - p->node = std::forward(n); - if ( set_modified ) { - HILTI_DEBUG(Resolver, hilti::util::fmt(" modified by HILTI %s/%d", - hilti::rt::filesystem::path(__FILE__).filename().native(), line)) - modified = true; - } - } - - void operator()(const expression::ListComprehension& e, position_t p) { - if ( ! e.type().isA() ) - return; - - if ( e.output().type().isA() ) - return; - - p.node.as().typeNode() = type::List(e.output().type()); - modified = true; - } -}; - -} // anonymous namespace - -bool hilti::detail::resolveIDs(Node* root, Unit* unit) { - util::timing::Collector _("hilti/compiler/id-resolver"); - - auto v = Visitor(unit); - for ( auto i : v.walk(root) ) - v.dispatch(i); - - auto v2 = Visitor2(unit); - for ( auto i : v2.walk(root) ) - v2.dispatch(i); - - return v.modified || v2.modified; -} diff --git a/hilti/toolchain/src/compiler/visitors/importer.cc b/hilti/toolchain/src/compiler/visitors/importer.cc deleted file mode 100644 index 8a833e97f..000000000 --- a/hilti/toolchain/src/compiler/visitors/importer.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#include -#include -#include -#include - -using namespace hilti; - -namespace { - -struct Visitor : public visitor::PreOrder { - Visitor(Unit* unit) : unit(unit) {} - std::set imported; - - void operator()(const declaration::ImportedModule& m) { - hilti::rt::filesystem::path path; - - if ( m.path().empty() ) { - if ( auto x = unit->import(m.id(), m.extension(), m.scope(), m.searchDirectories()) ) - path = x->path; - else - logger().error(util::fmt("cannot import module '%s': %s", m.id(), x.error()), m); - } - else { - if ( auto x = unit->import(m.path()) ) { - if ( x->id != m.id() ) - logger().error(util::fmt("unexpected module '%s' in %s", x->id, path), m); - - path = m.path(); - } - else - logger().error(util::fmt("cannot import module %s: %s", m.path(), x.error()), m); - } - - imported.emplace(m.id(), path); - } - - Unit* unit; -}; - -} // anonymous namespace - - -std::set hilti::detail::importModules(const Node& root, Unit* unit) { - util::timing::Collector _("hilti/compiler/importer"); - - auto v = Visitor(unit); - for ( auto i : v.walk(root) ) - v.dispatch(i); - - return v.imported; -} diff --git a/hilti/toolchain/src/compiler/visitors/normalizer.cc b/hilti/toolchain/src/compiler/visitors/normalizer.cc new file mode 100644 index 000000000..df059647c --- /dev/null +++ b/hilti/toolchain/src/compiler/visitors/normalizer.cc @@ -0,0 +1,275 @@ +// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; + +namespace hilti::logging::debug { +inline const hilti::logging::DebugStream Normalizer("normalizer"); +} // namespace hilti::logging::debug +namespace { + + +struct VisitorNormalizer : public visitor::PreOrder { + bool modified = false; + + // Log debug message recording resolving a epxxression. + void logChange(const Node& old, const Expression& nexpr) { + HILTI_DEBUG(logging::debug::Normalizer, + util::fmt("[%s] %s -> expression %s (%s)", old.typename_(), old, nexpr, old.location())); + } + + // Log debug message recording resolving a statement. + void logChange(const Node& old, const Statement& nstmt) { + HILTI_DEBUG(logging::debug::Normalizer, + util::fmt("[%s] %s -> statement %s (%s)", old.typename_(), old, nstmt, old.location())); + } + + // Log debug message recording resolving a type. + void logChange(const Node& old, const Type& ntype) { + HILTI_DEBUG(logging::debug::Normalizer, + util::fmt("[%s] %s -> type %s (%s)", old.typename_(), old, ntype, old.location())); + } + + void operator()(const declaration::Function& u, position_t p) { + if ( u.linkage() == declaration::Linkage::Struct ) { + // Link method implementations to their parent type. + auto ns = u.id().namespace_(); + if ( ! ns ) + return; + + auto resolved = scope::lookupID(ns, p, "struct type"); + if ( ! resolved ) { + p.node.addError(resolved.error()); + return; + } + + if ( ! resolved->first->isA() ) { + p.node.addError( + util::fmt("namespace %s does not resolve to a type (but to %s)", ns, resolved->first->typename_())); + return; + } + + p.node.as().setParentRef(NodeRef(resolved->first)); + } + } + + void operator()(const expression::Assign& assign, position_t p) { + // Rewrite assignments to map elements to use the `index_assign` operator. + auto& lhs = assign.childs().front(); + if ( auto index_non_const = lhs.tryAs() ) { + const auto& map = index_non_const->op0(); + const auto& map_type = map.type().as(); + const auto& key_type = map_type.keyType(); + const auto& value_type = map_type.valueType(); + + auto key = index_non_const->op1(); + if ( key.type() != key_type ) { + if ( auto nexpr = hilti::coerceExpression(key, key_type).nexpr ) + key = std::move(*nexpr); + } + + auto value = assign.source(); + if ( value.type() != value_type ) { + if ( auto nexpr = hilti::coerceExpression(value, value_type).nexpr ) + value = std::move(*nexpr); + } + + Expression index_assign = + hilti::expression::UnresolvedOperator(hilti::operator_::Kind::IndexAssign, + {std::move(map), std::move(key), std::move(value)}, + assign.meta()); + + logChange(p.node, index_assign); + p.node = std::move(index_assign); + modified = true; + return; + } + + // Rewrite assignments involving struct elements to use the non-const member operator. + if ( auto member_const = lhs.tryAs() ) { + auto new_lhs = operator_::struct_::MemberNonConst::Operator().instantiate(member_const->operands().copy(), + member_const->meta()); + Expression n = expression::Assign(new_lhs, assign.source(), assign.meta()); + logChange(p.node, n); + p.node = std::move(n); + modified = true; + return; + } + + // Rewrite assignments involving tuple ctors on the LHS to use the + // tuple's custom by-element assign operator. We need this to get + // constness right. + auto lhs_ctor = lhs.tryAs(); + if ( lhs_ctor && lhs_ctor->ctor().isA() ) { + if ( expression::isResolved(assign.source()) && expression::isResolved(assign.target()) ) { + auto n = operator_::tuple::CustomAssign::Operator().instantiate({assign.target(), assign.source()}, + assign.meta()); + logChange(p.node, n); + p.node = std::move(n); + modified = true; + return; + } + } + } + + void operator()(const statement::If& n, position_t p) { + if ( n.init() && ! n.condition() ) { + auto cond = expression::UnresolvedID(n.init()->id()); + logChange(p.node, cond); + p.node.as().setCondition(std::move(cond)); + modified = true; + } + } + + void operator()(const statement::Switch& s, position_t p) { p.node.as().preprocessCases(); } + + void operator()(const type::Library& t, position_t p) { + auto& type = p.node.as(); + + if ( ! type.cxxID() ) + // Make it equal to types with the same C++ representation. + type.setCxxID(ID(t.cxxName())); + } + + void operator()(const type::Struct& t, position_t p) { + if ( ! t.selfRef() ) + type::Struct::setSelf(&p.node); + } +}; + +} // anonymous namespace + +struct VisitorComputeCanonicalIDs; +static void _computeCanonicalIDs(VisitorComputeCanonicalIDs* v, Node* node, ID current); + +// Visitor to unset all canonical IDs in preparation for their recalculation. +struct VisitorClearCanonicalIDs : public visitor::PreOrder { + result_t operator()(const Declaration& d, position_t p) { p.node.as().setCanonicalID(ID()); }; +}; + +// Visitor computing canonical IDs. +struct VisitorComputeCanonicalIDs : public visitor::PreOrder { + // This visitor runs twice, with slightly differnet behaviour by pass. + VisitorComputeCanonicalIDs(int pass) : pass(pass) { assert(pass == 1 || pass == 2); } + + int pass; + ID parent_id; + ID module_id; + int ctor_struct_count = 0; + Scope* module_scope = nullptr; + + result_t operator()(const Module& m, position_t p) { + module_id = m.id(); + module_scope = p.node.scope().get(); + return m.id(); + } + + result_t operator()(const Declaration& d, position_t p) { + ID id; + + // A couple of special-cases for top-level declarations. + if ( parent_id.length() == 1 ) { + // 1. If the ID is qualified with the current module, the ID is + // fine as it is. + if ( d.id().sub(0) == module_id ) + id = d.id(); + + // 2. If the ID refers to something inside an imported module, we + // likewise use the ID as it is. + else if ( auto x = module_scope->lookup(d.id().sub(0)); x && x->node->isA() ) + id = d.id(); + } + + if ( auto x = d.tryAs() ) + // Use the namespace to the imported module as our ID. + id = x->id(); + + if ( ! id ) + // By default, prefix the ID with the current parent. + id = ID(parent_id, d.id()); + + // Record the ID if we don't have one yet. + if ( ! d.canonicalID() ) + p.node.as().setCanonicalID(id); + + // During the 1st pass, we also prefer shorter IDs over longer ones to + // avoid ambigious if we have multiple paths reaching the node. + else if ( pass == 1 && id.length() < d.canonicalID().length() ) + p.node.as().setCanonicalID(id); + + return d.canonicalID(); + } + + result_t operator()(const expression::Ctor& d, position_t p) { + // Special-case: Struct ctors are creating temporary struct types, + // inside which our standard scheme wouldn't assign any canonical IDs + // because we don't descend down into expressions. So we do this + // manually here. However, we need to "invent" a random ID for the type + // as their's no globally reachable declaration. + if ( ! d.type().isA() ) + return {}; + + // Create a fake current ID and then restart ID computation below the + // current node. + auto id = ID(util::fmt("%s::", parent_id, ++ctor_struct_count)); + _computeCanonicalIDs(this, const_cast(&d.childs()[0]), std::move(id)); + return {}; + } +}; + +// Visitor double-checking that all declarations have their canonical IDs set. +struct VisitorCheckCanonicalIDs : public visitor::PreOrder { + result_t operator()(const Declaration& d, position_t p) { + if ( ! d.canonicalID() ) + hilti::render(std::cerr, p.node); + assert(d.canonicalID()); + }; +}; + +static void _computeCanonicalIDs(VisitorComputeCanonicalIDs* v, Node* node, ID current) { + v->parent_id = current; + + if ( auto x = v->dispatch(node) ) + current = *x; + + if ( node->pruneWalk() ) + return; + + if ( v->pass == 1 && node->isA() ) + // During the 1st pass we don't descend into expressions to avoid + // ambiguities with multiple paths reaching the same node. + return; + + for ( auto& c : node->childs() ) + _computeCanonicalIDs(v, &c, current); +} + +bool hilti::detail::ast::normalize(Node* root, Unit* unit) { + util::timing::Collector _("hilti/compiler/ast/normalizer"); + + auto v1 = VisitorNormalizer(); + for ( auto i : v1.walk(root) ) + v1.dispatch(i); + + auto v2 = VisitorComputeCanonicalIDs(1); + _computeCanonicalIDs(&v2, root, ID()); + + auto v3 = VisitorComputeCanonicalIDs(2); + _computeCanonicalIDs(&v3, root, ID()); + +#ifndef NDEBUG + auto v4 = VisitorCheckCanonicalIDs(); + for ( auto i : v4.walk(root) ) + v4.dispatch(i); +#endif + + return v1.modified; +} diff --git a/hilti/toolchain/src/compiler/visitors/operator-resolver.cc b/hilti/toolchain/src/compiler/visitors/operator-resolver.cc deleted file mode 100644 index ad60dc2fe..000000000 --- a/hilti/toolchain/src/compiler/visitors/operator-resolver.cc +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace hilti; -using namespace hilti::detail; - -namespace hilti::logging::debug { -inline const DebugStream Resolver("resolver"); -} // namespace hilti::logging::debug - -/** Returns a set of overload alternatives matching given operand expression. */ -static std::vector _resolve(const std::vector& candidates, const std::vector& operands, - const Meta& meta, bool disallow_type_changes = false) { - static const std::vector> styles = { - CoercionStyle::PreferOriginalType | CoercionStyle::OperandMatching | CoercionStyle::TryExactMatch, - CoercionStyle::PreferOriginalType | CoercionStyle::OperandMatching | CoercionStyle::TryExactMatch | - CoercionStyle::TryConstPromotion, - CoercionStyle::PreferOriginalType | CoercionStyle::OperandMatching | CoercionStyle::TryExactMatch | - CoercionStyle::TryConstPromotion | CoercionStyle::TryCoercion, - }; - - auto deref_operands = [&](const std::vector& ops) { - std::vector nops; - - for ( auto&& op : ops ) { - if ( type::isReferenceType(op.type()) ) - nops.push_back(builder::type_wrapped(builder::deref(op), op.type().dereferencedType())); - else - nops.push_back(op); - } - - return nops; - }; - - auto try_candidate = [&](const auto& c, const std::vector& ops, auto style, - const auto& dbg_msg) -> std::optional { - auto nops = coerceOperands(ops, c.operands(), style); - if ( ! nops ) { - if ( (style & CoercionStyle::TryCoercion) && ! (style & CoercionStyle::DisallowTypeChanges) ) { - // If any of the operands is a reference type, try the - // derefed operands, too. - for ( const auto& op : ops ) { - if ( type::isReferenceType(op.type()) ) - nops = coerceOperands(deref_operands(ops), c.operands(), style); - } - } - } - - if ( ! nops ) - return {}; - - auto r = c.instantiate(nops->second, meta); - HILTI_DEBUG(logging::debug::Resolver, util::fmt("-> %s, resolves to %s %s", dbg_msg, to_node(r), - (r.isConstant() ? "(const)" : "(non-const)"))); - return r; - }; - - for ( auto style : styles ) { - if ( disallow_type_changes ) - style |= CoercionStyle::DisallowTypeChanges; - - HILTI_DEBUG(logging::debug::Resolver, util::fmt("style: %s", to_string(style))); - logging::DebugPushIndent _(logging::debug::Resolver); - - std::vector resolved; - - for ( const auto& c : candidates ) { - HILTI_DEBUG(logging::debug::Resolver, util::fmt("candidate: %s", c.typename_())); - logging::DebugPushIndent _(logging::debug::Resolver); - - if ( auto r = try_candidate(c, operands, style, "candidate matches") ) - resolved.emplace_back(std::move(*r)); - else { - // Try to swap the operators for commutative operators. - if ( operator_::isCommutative(c.kind()) && operands.size() == 2 ) { - if ( auto r = try_candidate(c, {operands[1], operands[0]}, style, - "candidate matches with operands swapped") ) - resolved.emplace_back(std::move(*r)); - } - } - } - - if ( resolved.size() ) - return resolved; - } - - return {}; -} - -namespace { - -/** Visitor that applies common AST transformation before the actual operator resolution process. */ -struct Normalizer : public hilti::visitor::PostOrder { - Normalizer(hilti::Module* module) : module(module) {} - - hilti::Module* module; - bool modified = false; - - template - void replaceNode(position_t* p, T&& n) { - auto x = p->node; - p->node = std::forward(n); - p->node.setOriginalNode(module->preserve(x)); - modified = true; - } - -#if 0 - void preDispatch(const Node& n, int level) override { - auto indent = std::string(level * 2, ' '); - std::cerr << "#1 " << indent << "> " << n.render() << std::endl; - n.scope()->render(std::cerr, " | "); - }; -#endif - - void operator()(const expression::UnresolvedOperator& u, position_t p) { - // Replace member operators that work on references with - // corresponding versions that first deref the target instance. - - auto deref_op0 = [&]() { - std::vector ops = u.operands(); - ops[0] = hilti::expression::UnresolvedOperator(hilti::operator_::Kind::Deref, {ops[0]}, ops[0].meta()); - Expression x = hilti::expression::UnresolvedOperator(u.kind(), std::move(ops), u.meta()); - replaceNode(&p, std::move(x)); - }; - - switch ( u.kind() ) { - case operator_::Kind::Member: - case operator_::Kind::MemberCall: - case operator_::Kind::HasMember: - case operator_::Kind::TryMember: { - if ( type::isReferenceType(u.operands()[0].type()) ) - deref_op0(); - } - default: { /* ignore */ - } - } - } - - void operator()(const expression::Assign& assign, position_t p) { - // Rewrite assignments to map elements to use the `index_assign` operator. - auto& lhs = assign.childs().front(); - if ( const auto& index_non_const = lhs.tryAs() ) { - const auto map = index_non_const->op0(); - - const auto map_type = map.type().as(); - const auto key_type = map_type.keyType(); - auto value_type = map_type.elementType(); - - auto key = index_non_const->op1(); - if ( key.type() != key_type ) { - if ( auto nexpr = hilti::coerceExpression(key, key_type).nexpr ) - key = std::move(*nexpr); - } - - auto value = assign.source(); - if ( value.type() != value_type ) { - if ( auto nexpr = hilti::coerceExpression(value, value_type).nexpr ) - value = std::move(*nexpr); - } - - Expression index_assign = - hilti::expression::UnresolvedOperator(hilti::operator_::Kind::IndexAssign, - {std::move(map), std::move(key), std::move(value)}, - assign.meta()); - replaceNode(&p, std::move(index_assign)); - } - } -}; - -struct Visitor : public hilti::visitor::PostOrder { - bool modified = false; - -#if 0 - void preDispatch(const Node& n, int level) override { - auto indent = std::string(level * 2, ' '); - std::cerr << "#2 " << indent << "> " << n.render() << std::endl; - n.scope()->render(std::cerr, " | "); - }; -#endif - - bool resolveOperator(const expression::UnresolvedOperator& u, position_t p) { // TODO(google-runtime-references) - for ( const auto& o : u.operands() ) { - if ( o.type().isA() ) - return false; - } - - HILTI_DEBUG(logging::debug::Resolver, - util::fmt("== resolving operator: %s (%s)", to_node(u), u.meta().location().render(true))); - logging::DebugPushIndent _(logging::debug::Resolver); - - std::vector resolved; - - auto candidates = operator_::registry().allOfKind(u.kind()); - - if ( u.kind() == operator_::Kind::MemberCall && u.operands().size() >= 2 ) { - // Pre-filter list of all member-call operators down to those - // with matching methods. This is just a performance - // optimization. - auto member = u.operands()[1].template as().id(); - - auto filtered = util::filter(candidates, [&](const auto& c) { - return std::get(c.operands()[1].type).template as() == member; - }); - - resolved = _resolve(filtered, u.operands(), u.meta()); - } - - else - resolved = _resolve(candidates, u.operands(), u.meta(), u.kind() == operator_::Kind::Cast); - - if ( resolved.empty() ) { - p.node.addError(util::fmt("cannot resolve operator: %s", renderOperatorInstance(u))); - return false; - } - - if ( resolved.size() > 1 ) { - std::vector context = {"candidates:"}; - for ( auto i : resolved ) - context.emplace_back(util::fmt("- %s [%s]", - renderOperatorPrototype(i.as()), - i.typename_())); - - p.node.addError(util::fmt("operator usage is ambiguous: %s", renderOperatorInstance(u)), - std::move(context)); - return true; - } - - p.node = resolved[0]; - modified = true; - -#ifndef NDEBUG - Expression new_op = p.node.as(); - HILTI_DEBUG(logging::debug::Resolver, - util::fmt("=> resolved to %s (result: %s, expression is %s)", p.node.render(), new_op, - (new_op.isConstant() ? "const" : "non-const"))); -#endif - return true; - } - - bool resolveFunctionCall(const expression::UnresolvedOperator& u, position_t p) { - auto operands = u.operands(); - - if ( operands.size() != 2 ) - return false; - - auto callee = operands[0].tryAs(); - auto args_ctor = operands[1].tryAs(); - - if ( ! callee ) - return false; - - if ( ! args_ctor ) { - p.node.addError("function call's argument must be a tuple constant"); - return true; - } - - auto args = args_ctor->ctor().tryAs(); - - if ( ! args ) { - p.node.addError("function call's argument must be a tuple constant"); - return true; - } - - std::vector candidates; - - for ( auto i = p.path.rbegin(); i != p.path.rend(); i++ ) { - auto resolved = (**i).scope()->lookupAll(callee->id()); - - if ( resolved.empty() ) - continue; - - for ( auto& r : resolved ) { - auto d = r.node->tryAs(); - - if ( ! d ) { - p.node.addError(util::fmt("ID '%s' resolves to something other than just functions", callee->id())); - return true; - } - - if ( r.external && d->linkage() != declaration::Linkage::Public ) { - p.node.addError(util::fmt("function has not been declared public: %s", r.qualified)); - return true; - } - - auto op = operator_::function::Call::Operator(r, d->function().type()); - candidates.emplace_back(op); - } - - std::vector overloads = _resolve(candidates, operands, u.meta()); - - if ( overloads.empty() ) - break; - - if ( overloads.size() > 1 ) { - // Ok as long as it's all the same hook, report otherwise. - auto function = [](auto n) { - auto rid = - n.template as().op0().template as(); - return std::make_pair(rid.id(), rid.declaration().template as().function()); - }; - - auto [id, func] = function(overloads[0]); - - if ( func.type().flavor() != type::function::Flavor::Hook ) { - std::vector context = {"candidate functions:"}; - - for ( auto i : overloads ) - context.emplace_back( - util::fmt("- %s", renderOperatorPrototype(i.as()))); - - p.node.addError(util::fmt("call is ambiguous: %s", renderOperatorInstance(u)), std::move(context)); - return true; - } - - for ( auto& i : overloads ) { - auto [oid, ofunc] = function(i); - if ( id != oid || Type(func.type()) != Type(ofunc.type()) ) { - std::vector context = {"candidate functions:"}; - - for ( auto i : overloads ) - context.emplace_back( - util::fmt("- %s", renderOperatorPrototype(i.as()))); - - - p.node.addError(util::fmt("call is ambiguous: %s", renderOperatorInstance(u)), - std::move(context)); - return true; - } - } - } - - // Found a match. - HILTI_DEBUG(logging::debug::Resolver, - util::fmt("resolved function call %s to %s", callee->id(), overloads.front().render()), - p.node.location()); - - p.node = overloads.front(); - modified = true; - return true; - } - - std::vector context; - - if ( ! candidates.empty() ) { - context.emplace_back("candidate functions:"); - for ( const auto& i : candidates ) { - auto rop = i.instantiate(u.operands(), u.meta()).as(); - context.emplace_back(util::fmt("- %s", renderOperatorPrototype(rop))); - } - } - - p.node.addError(util::fmt("call does not match any function: %s", renderOperatorInstance(u)), - std::move(context)); - return true; - } - - bool resolveMethodCall(const expression::UnresolvedOperator& u, position_t p) { - auto operands = u.operands(); - - if ( operands.size() != 3 ) - return false; - - auto stype = type::effectiveType(operands[0].type()).tryAs(); - auto callee = operands[1].tryAs(); - auto args_ctor = operands[2].tryAs(); - - if ( ! (stype && callee) ) - return false; - - if ( ! args_ctor ) { - p.node.addError("method call's argument must be a tuple constant"); - return true; - } - - auto args = args_ctor->ctor().tryAs(); - - if ( ! args ) { - p.node.addError("method call's argument must be a tuple constant"); - return true; - } - - auto fields = stype->fields(callee->id()); - - if ( fields.empty() ) { - p.node.addError(util::fmt("struct type does not have a method `%s`", callee->id())); - return false; // Continue trying to find another match. - } - - for ( auto& f : fields ) { - if ( ! f.type().isA() ) { - p.node.addError(util::fmt("struct attribute '%s' is not a function", callee->id())); - return true; - } - } - - auto candidates = util::transform(fields, [&](const auto& f) -> Operator { - return operator_::struct_::MemberCall::Operator(*stype, f); - }); - - std::vector overloads = _resolve(candidates, operands, u.meta()); - - if ( overloads.empty() ) { - std::vector context; - - if ( ! candidates.empty() ) { - context.emplace_back("candidate methods:"); - for ( const auto& i : candidates ) { - auto rop = i.instantiate(u.operands(), u.meta()).as(); - context.emplace_back(util::fmt("- %s", renderOperatorPrototype(rop))); - } - } - - p.node.addError(util::fmt("call does not match any method: %s", renderOperatorInstance(u)), - std::move(context)); - return true; - } - - if ( overloads.size() > 1 ) { - std::vector context = {"candidates:"}; - for ( auto i : overloads ) - context.emplace_back(util::fmt("- %s", renderOperatorPrototype(i.as()))); - - p.node.addError(util::fmt("method call to is ambiguous: %s", renderOperatorInstance(u)), - std::move(context)); - return true; - } - - HILTI_DEBUG(logging::debug::Resolver, - util::fmt("resolved method call %s to %s", callee->id(), overloads.front().render()), - p.node.location()); - - p.node = overloads.front(); - modified = true; - return true; - } - - void operator()(const expression::UnresolvedOperator& u, position_t p) { - if ( u.kind() == operator_::Kind::Call && resolveFunctionCall(u, p) ) - return; - - if ( u.kind() == operator_::Kind::MemberCall && resolveMethodCall(u, p) ) - return; - - if ( resolveOperator(u, p) ) - return; - - if ( u.kind() == operator_::Kind::Cast ) { - // We hardcode here that a cast<> operator can always perform any - // legal coercion. This helps in cases where we need to force a - // specific coercion to take place. - auto expr = u.operands()[0]; - auto dst = u.operands()[1].as().typeValue(); - - if ( dst != type::unknown ) { - const auto style = CoercionStyle::TryAllForMatching | CoercionStyle::ContextualConversion; - if ( auto c = hilti::coerceExpression(expr, dst, style) ) { - HILTI_DEBUG(logging::debug::Resolver, util::fmt("resolved cast to type '%s' through coercion", dst), - p.node.location()); - - p.node = operator_::generic::CastedCoercion::Operator().instantiate(u.operands(), u.meta()); - modified = true; - return; - } - } - } - } -}; - -} // anonymous namespace - -bool detail::resolveOperators(Node* root, Unit* unit) { - util::timing::Collector _("hilti/compiler/operator-resolver"); - - auto n = Normalizer(&root->as()); - for ( auto i : n.walk(root) ) - n.dispatch(i); - - auto v = Visitor(); - for ( auto i : v.walk(root) ) - v.dispatch(i); - - return n.modified || v.modified; -} diff --git a/hilti/toolchain/src/compiler/visitors/printer.cc b/hilti/toolchain/src/compiler/visitors/printer.cc index 5665b376e..fa724e41c 100644 --- a/hilti/toolchain/src/compiler/visitors/printer.cc +++ b/hilti/toolchain/src/compiler/visitors/printer.cc @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include #include @@ -21,6 +23,7 @@ static std::string renderOperator(operator_::Kind kind, const std::vector(%s)", ops[1], ops[0]); + case operator_::Kind::CustomAssign: return fmt("%s = %s", ops[0], ops[1]); case operator_::Kind::DecrPostfix: return fmt("%s--", ops[0]); case operator_::Kind::DecrPrefix: return fmt("--%s", ops[0]); case operator_::Kind::Delete: return fmt("delete %s[%s]", ops[0], ops[1]); @@ -71,7 +74,7 @@ static std::string renderExpressionType(const Expression& e) { return fmt("%s%s", const_, e.type()); } -static std::string renderOperand(operator_::Operand op, const std::vector& exprs) { +static std::string renderOperand(operator_::Operand op, const node::Range& exprs) { auto t = operator_::type(op.type, exprs, exprs); std::string s = (t ? fmt("%s", *t) : ""); @@ -119,7 +122,9 @@ struct Visitor : visitor::PreOrder { } } - auto const_(const Type& t) { return (out.isCompact() && type::isConstant(t)) ? "const " : ""; } + auto const_(const Type& t) { + return (out.isCompact() && type::isConstant(t) && type::isMutable(t)) ? "const " : ""; + } void operator()(const Attribute& n) { out << n.tag(); @@ -146,7 +151,7 @@ struct Visitor : visitor::PreOrder { if ( n.callingConvention() != function::CallingConvention::Standard ) out << to_string(n.callingConvention()) << ' '; - printFunctionType(n.type(), n.id()); + printFunctionType(n.ftype(), n.id()); if ( n.attributes() ) out << ' ' << std::make_pair(n.attributes()->attributes(), " "); @@ -171,21 +176,23 @@ struct Visitor : visitor::PreOrder { out.pushScope(n.id()); - auto printDecls = [&](auto decls) { + auto printDecls = [&](const auto& decls) { for ( const auto& d : decls ) - out << d; + out << Declaration(d); if ( decls.size() ) out.emptyLine(); }; + printDecls(node::filter(n.declarations(), + [](const auto& d) { return d.template isA(); })); + printDecls(node::filter(n.declarations(), [](const auto& d) { return d.template isA(); })); printDecls( - util::filter(n.declarations(), [](auto d) { return d.template isA(); })); - printDecls(util::filter(n.declarations(), [](auto d) { return d.template isA(); })); - printDecls(util::filter(n.declarations(), [](auto d) { return d.template isA(); })); + node::filter(n.declarations(), [](const auto& d) { return d.template isA(); })); + printDecls(node::filter(n.declarations(), + [](const auto& d) { return d.template isA(); })); printDecls( - util::filter(n.declarations(), [](auto d) { return d.template isA(); })); - printDecls(util::filter(n.declarations(), [](auto d) { return d.template isA(); })); + node::filter(n.declarations(), [](const auto& d) { return d.template isA(); })); for ( const auto& s : n.statements().statements() ) out << s; @@ -214,7 +221,7 @@ struct Visitor : visitor::PreOrder { out << "default<" << n.type() << ">(" << std::make_pair(n.typeArguments(), ", ") << ")"; } - void operator()(const ctor::Enum& n) { out << *n.type().typeID() << "::" << n.value(); } + void operator()(const ctor::Enum& n, position_t p) { out << *p.node.as().typeID() << "::" << n.value(); } void operator()(const ctor::Error& n) { out << "error(\"" << n.value() << "\")"; } @@ -223,7 +230,7 @@ struct Visitor : visitor::PreOrder { void operator()(const ctor::List& n) { out << '[' << std::make_pair(n.value(), ", ") << ']'; } void operator()(const ctor::Map& n) { - auto elems = util::transform(n.value(), [](const auto& e) { return fmt("%s: %s", e.first, e.second); }); + auto elems = node::transform(n.value(), [](const auto& e) { return fmt("%s: %s", e.key(), e.value()); }); out << "map(" << std::make_pair(elems, ", ") << ')'; } @@ -281,7 +288,7 @@ struct Visitor : visitor::PreOrder { else first = false; - out << '$' << f.first << "=" << f.second; + out << '$' << f.id() << "=" << f.expression(); } out << "]"; @@ -304,16 +311,53 @@ struct Visitor : visitor::PreOrder { void operator()(const declaration::Constant& n) { out.beginLine(); out << linkage(n.linkage()) << "const "; - - if ( ! n.hasAutomaticType() ) - out << n.type(); - + out << n.type(); out << ' ' << n.id() << " = " << n.value() << ';'; out.endLine(); } void operator()(const declaration::Expression& n) { out << n.expression(); } + void operator()(const declaration::Field& n) { + out << " "; + + if ( auto ft = n.type().tryAs() ) { + out << to_string(ft->flavor()) << " "; + + if ( n.callingConvention() != function::CallingConvention::Standard ) + out << to_string(n.callingConvention()) << ' '; + + out << ft->result().type() << " " << n.id() << "(" << std::make_pair(ft->parameters(), ", ") << ")"; + } + + else + out << n.type() << ' ' << n.id(); + + if ( n.attributes() ) + out << ' ' << *n.attributes(); + + if ( auto f = n.inlineFunction(); f && f->body() ) { + const auto& block = f->body()->tryAs(); + if ( block && block->statements().empty() ) { + out << " {}"; + out.endLine(); + } + else if ( block && block->statements().size() == 1 ) { + auto old_compact = out.setCompact(true); + out << " { " << *block->statements().begin() << " }"; + out.setCompact(old_compact); + out.endLine(); + } + else { + out.incrementIndent(); + out << ' ' << *f->body(); + out.decrementIndent(); + } + } + else + out << ";" << out.newline(); + } + void operator()(const declaration::Parameter& n) { auto kind = [&](auto k) { switch ( k ) { @@ -375,12 +419,7 @@ struct Visitor : visitor::PreOrder { void operator()(const declaration::LocalVariable& n) { // Will be printed through a statement, hence no outer formatting. out << "local "; - - if ( n.hasAutomaticType() ) - out << "auto"; - else - out << n.type(); - + out << n.type(); out << ' ' << n.id(); if ( n.typeArguments().size() ) @@ -393,12 +432,7 @@ struct Visitor : visitor::PreOrder { void operator()(const declaration::GlobalVariable& n) { out.beginLine(); out << linkage(n.linkage()) << "global "; - - if ( n.hasAutomaticType() ) - out << "auto"; - else - out << n.type(); - + out << n.type(); out << ' ' << n.id(); if ( n.typeArguments().size() ) @@ -416,7 +450,7 @@ struct Visitor : visitor::PreOrder { void operator()(const expression::Assign& n) { out << n.target() << " = " << n.source(); } void operator()(const expression::BuiltinFunction& n) { - out << n.name() << "(" << util::join(util::transform(n.arguments(), [](auto& p) { return fmt("%s", p); }), ", ") + out << n.name() << "(" << util::join(node::transform(n.arguments(), [](auto& p) { return fmt("%s", p); }), ", ") << ")"; } @@ -437,7 +471,7 @@ struct Visitor : visitor::PreOrder { } result_t operator()(const expression::ListComprehension& n) { - out << '[' << n.output() << " for " << n.id() << " in " << n.input(); + out << '[' << n.output() << " for " << n.local() << " in " << n.input(); if ( n.condition() ) out << " if " << *n.condition(); @@ -567,7 +601,7 @@ struct Visitor : visitor::PreOrder { void operator()(const statement::For& n) { out.emptyLine(); out.beginLine(); - out << "for ( " << n.id() << " in " << n.sequence() << " ) " << n.body(); + out << "for ( " << n.local().id() << " in " << n.sequence() << " ) " << n.body(); out.endLine(); } @@ -577,7 +611,7 @@ struct Visitor : visitor::PreOrder { out << "if ( "; if ( auto e = n.init() ) - out << *e << "; "; + out << Declaration(*e) << "; "; if ( auto e = n.condition() ) out << *e; @@ -608,10 +642,10 @@ struct Visitor : visitor::PreOrder { out.beginLine(); out << "switch ( "; - if ( auto i = n.init() ) - out << *i; + if ( auto cond = n.condition(); cond.id().str() != "__x" ) + out << cond; else - out << n.expression(); + out << *cond.init(); out << " ) {"; out.incrementIndent(); @@ -619,7 +653,13 @@ struct Visitor : visitor::PreOrder { for ( const auto& c : n.cases() ) { out.beginLine(); - out << "case " << std::make_pair(c.expressions(), ", ") << ": " << c.body(); + + if ( ! c.isDefault() ) + out << "case " << std::make_pair(c.expressions(), ", ") << ": "; + else + out << "default: "; + + out << c.body(); out.endLine(); } @@ -694,11 +734,11 @@ struct Visitor : visitor::PreOrder { } void operator()(const expression::ResolvedOperator& n) { - out << renderOperator(n.operator_().kind(), util::transform(n.operands(), [](auto o) { return fmt("%s", o); })); + out << renderOperator(n.operator_().kind(), node::transform(n.operands(), [](auto o) { return fmt("%s", o); })); } void operator()(const expression::UnresolvedOperator& n) { - out << renderOperator(n.kind(), util::transform(n.operands(), [](auto o) { return fmt("%s", o); })); + out << renderOperator(n.kind(), node::transform(n.operands(), [](auto o) { return fmt("%s", o); })); } ////// Types @@ -713,14 +753,12 @@ struct Visitor : visitor::PreOrder { void operator()(const type::Bytes& n) { out << const_(n) << "bytes"; } - void operator()(const type::Computed& n) { out << const_(n) << n.type(); } - void operator()(const type::enum_::Label& n) { out << n.id() << " = " << n.value(); } - void operator()(const type::Enum& n) { + void operator()(const type::Enum& n, position_t p) { if ( ! out.isExpandSubsequentType() ) { out.setExpandSubsequentType(false); - if ( auto id = n.typeID() ) { + if ( auto id = p.node.as().typeID() ) { out << *id; return; } @@ -728,7 +766,9 @@ struct Visitor : visitor::PreOrder { out.setExpandSubsequentType(false); - auto x = util::filter(n.labels(), [](auto l) { return l.id() != ID("Undef"); }); + auto x = util::transform(util::filter(n.labels(), [](const auto& l) { return l.get().id() != ID("Undef"); }), + [](const auto& l) { return l.get(); }); + out << const_(n) << "enum { " << std::make_pair(std::move(x), ", ") << " }"; } @@ -802,8 +842,8 @@ struct Visitor : visitor::PreOrder { void operator()(const type::stream::View& n) { out << const_(n) << "view"; } - void operator()(const type::Library& n) { - if ( auto id = n.typeID() ) + void operator()(const type::Library& n, position_t p) { + if ( auto id = p.node.as().typeID() ) out << const_(n) << *id; else out << const_(n) << fmt("__library_type(\"%s\")", n.cxxName()); @@ -828,14 +868,12 @@ struct Visitor : visitor::PreOrder { if ( n.isWildcard() ) out << const_(n) << "map<*>"; else { - out << const_(n) << "map<" << n.keyType() << ", " << n.elementType() << ">"; + out << const_(n) << "map<" << n.keyType() << ", " << n.valueType() << ">"; } } void operator()(const type::RegExp& n) { out << const_(n) << "regexp"; } - void operator()(const type::ResolvedID& n) { out << const_(n) << n.id(); } - void operator()(const type::Result& n) { if ( n.isWildcard() ) out << const_(n) << "result<*>"; @@ -868,49 +906,9 @@ struct Visitor : visitor::PreOrder { void operator()(const type::String& n) { out << const_(n) << "string"; } - void operator()(const type::struct_::Field& n) { - out << " "; - - if ( auto ft = n.type().tryAs() ) { - out << to_string(ft->flavor()) << " "; - - if ( n.callingConvention() != function::CallingConvention::Standard ) - out << to_string(n.callingConvention()) << ' '; - - out << ft->result().type() << " " << n.id() << "(" << std::make_pair(ft->parameters(), ", ") << ")"; - } - - else - out << n.type() << ' ' << n.id(); - - if ( n.attributes() ) - out << ' ' << *n.attributes(); - - if ( auto f = n.inlineFunction(); f && f->body() ) { - const auto& block = f->body()->tryAs(); - if ( block && block->statements().empty() ) { - out << " {}"; - out.endLine(); - } - else if ( block && block->statements().size() == 1 ) { - auto old_compact = out.setCompact(true); - out << " { " << block->statements().front() << " }"; - out.setCompact(old_compact); - out.endLine(); - } - else { - out.incrementIndent(); - out << ' ' << *f->body(); - out.decrementIndent(); - } - } - else - out << ";" << out.newline(); - } - void operator()(const type::Struct& n, position_t p) { if ( ! out.isExpandSubsequentType() ) { - if ( auto id = n.typeID() ) { + if ( auto id = p.node.as().typeID() ) { out << *id; if ( n.parameters().size() ) @@ -927,18 +925,14 @@ struct Visitor : visitor::PreOrder { if ( n.parameters().size() ) out << " (" << std::make_pair(n.parameters(), ", ") << ')'; - out << " {" << out.newline(); - - for ( const auto& f : n.fields() ) { - if ( ! f.type().isA() ) - out << f; - } - - for ( const auto& f : n.fields() ) { - if ( f.type().isA() ) - out << f; - } + auto printFields = [&](const auto& fields) { + for ( const auto& f : fields ) + out << Declaration(f); + }; + out << " {" << out.newline(); + printFields(node::filter(n.fields(), [](const auto& f) { return ! f.type().template isA(); })); + printFields(node::filter(n.fields(), [](const auto& f) { return f.type().template isA(); })); out << "}"; } @@ -946,18 +940,9 @@ struct Visitor : visitor::PreOrder { void operator()(const type::Type_& n) { out << const_(n) << fmt("type<%s>", n.typeValue()); } - void operator()(const type::union_::Field& n) { - out << " " << n.type() << ' ' << n.id(); - - if ( n.attributes() ) - out << ' ' << *n.attributes(); - - out << ";" << out.newline(); - } - void operator()(const type::Union& n, position_t p) { if ( ! out.isExpandSubsequentType() ) { - if ( auto id = n.typeID() ) { + if ( auto id = p.node.as().typeID() ) { out << *id; return; } @@ -988,8 +973,8 @@ struct Visitor : visitor::PreOrder { else { out << const_(n) << "tuple<"; - auto types = util::transform(n.elements(), [](const auto& x) { - return x.first ? fmt("%s: %s", x.first, x.second) : fmt("%s", x.second); + auto types = node::transform(n.elements(), [](const auto& x) { + return x.id() ? fmt("%s: %s", *x.id(), x.type()) : fmt("%s", x.type()); }); out << util::join(types, ", ") << '>'; @@ -1046,10 +1031,10 @@ void hilti::detail::printAST(const Node& root, printer::Stream& stream) { } for ( auto& p : plugin::registry().plugins() ) { - if ( ! p.print_ast ) + if ( ! p.ast_print ) continue; - if ( (*p.print_ast)(root, stream) ) + if ( (*p.ast_print)(root, stream) ) return; } @@ -1089,14 +1074,14 @@ std::string hilti::detail::renderOperatorPrototype(const expression::ResolvedOpe } } -static std::string _renderOperatorInstance(operator_::Kind kind, const std::vector& exprs) { +static std::string _renderOperatorInstance(operator_::Kind kind, const node::Range& exprs) { switch ( kind ) { case operator_::Kind::Call: { assert(exprs.size() == 2); auto id = exprs[0]; auto ops = exprs[1].as().ctor().as().value(); auto args = - util::join(util::transform(ops, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); }), ", "); + util::join(node::transform(ops, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); }), ", "); return fmt("%s(%s)", id, args); } @@ -1106,13 +1091,13 @@ static std::string _renderOperatorInstance(operator_::Kind kind, const std::vect auto id = exprs[1]; auto ops = exprs[2].as().ctor().as().value(); auto args = - util::join(util::transform(ops, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); }), ", "); + util::join(node::transform(ops, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); }), ", "); return fmt("<%s>.%s(%s)", renderExpressionType(self), id, args); } default: return renderOperator(kind, - util::transform(exprs, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); })); + node::transform(exprs, [&](auto x) { return fmt("<%s>", renderExpressionType(x)); })); } } diff --git a/hilti/toolchain/src/compiler/visitors/renderer.cc b/hilti/toolchain/src/compiler/visitors/renderer.cc index 21c81b782..d7021e45f 100644 --- a/hilti/toolchain/src/compiler/visitors/renderer.cc +++ b/hilti/toolchain/src/compiler/visitors/renderer.cc @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include @@ -13,29 +13,12 @@ using util::fmt; static void render(const Node& n, std::ostream* out, std::optional dbg, bool include_scopes) { util::timing::Collector _("hilti/renderer"); - int dbg_level = 0; - - for ( const auto&& i : visitor::PreOrder<>().walk(n) ) { - int new_dbg_level = i.path.size(); - - while ( dbg_level < new_dbg_level ) { - logger().debugPushIndent(*dbg); - dbg_level++; - } - - while ( dbg_level > new_dbg_level ) { - logger().debugPopIndent(*dbg); - dbg_level--; - } - -#if 0 - // Condense AST output, struct types can be very long. - if ( i.findParent() ) - continue; -#endif + for ( const auto i : visitor::PreOrder<>().walk(n) ) { + if ( dbg ) + logger().debugSetIndent(*dbg, i.path.size()); if ( out ) - (*out) << std::string(dbg_level - 1, ' '); + (*out) << std::string(i.path.size() - 1, ' '); auto s = fmt("- %s", i.node.render()); @@ -63,8 +46,8 @@ static void render(const Node& n, std::ostream* out, std::optional 0 ) - logger().debugPopIndent(*dbg); + if ( dbg ) + logger().debugSetIndent(*dbg, 0); } void detail::renderNode(const Node& n, std::ostream& out, bool include_scopes) { diff --git a/hilti/toolchain/src/compiler/visitors/resolver.cc b/hilti/toolchain/src/compiler/visitors/resolver.cc new file mode 100644 index 000000000..04a3cf65a --- /dev/null +++ b/hilti/toolchain/src/compiler/visitors/resolver.cc @@ -0,0 +1,977 @@ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hilti; + +namespace hilti::logging::debug { +inline const hilti::logging::DebugStream Resolver("resolver"); +inline const hilti::logging::DebugStream Operator("operator"); +} // namespace hilti::logging::debug + +namespace { + +struct Visitor : public visitor::PostOrder { + Visitor(const std::shared_ptr& ctx, Node* module, Unit* unit) + : visitor::PostOrder(), _context(ctx), unit(unit), _module(module->as()) {} + + std::shared_ptr _context; + Unit* unit; + Module& _module; + + bool modified = false; + std::map auto_params; // miapping of `auto` parameters inferred, indexed by canonical ID + +#if 0 + std::set seen; + + void preDispatch(const Node& n, int level) override { + std::string prefix = "# "; + + if ( seen.find(&n) != seen.end() ) + prefix = "! "; + else + seen.insert(&n); + + auto indent = std::string(level * 2, ' '); + std::cerr << prefix << indent << "> " << n.render() << std::endl; + n.scope()->render(std::cerr, " | "); + }; +#endif + + // Log debug message recording resolving a epxxression. + void logChange(const Node& old, const Expression& nexpr) { + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("[%s] %s -> expression %s (%s)", old.typename_(), old, nexpr, old.location())); + } + + // Log debug message recording resolving a statement. + void logChange(const Node& old, const Statement& nstmt) { + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("[%s] %s -> statement %s (%s)", old.typename_(), old, nstmt, old.location())); + } + + // Log debug message recording resolving a type. + void logChange(const Node& old, const Type& ntype) { + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("[%s] %s -> type %s (%s)", old.typename_(), old, ntype, old.location())); + } + + void logChange(const Node& old, const std::string& msg) { + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("[%s] %s -> %s (%s)", old.typename_(), old, msg, old.location())); + } + + // Attempt to infer a common type from a list of expression. + hilti::optional_ref typeForExpressions(position_t* p, node::Range exprs) { + hilti::optional_ref t; + + for ( const auto& e : exprs ) { + if ( ! type::isResolved(e.type()) ) + return {}; + + if ( ! t ) + t = e.type(); + else { + if ( e.type() != *t ) + return type::unknown; // inconsistent, will need some other way to resolve + } + } + + return t; + } + + // Associate a type ID, and potentially `&cxxname` with a type. + Type addTypeID(Type t, ID fully_qualified_id, const hilti::optional_ref& attrs) { + t.setTypeID(fully_qualified_id); + + if ( attrs ) { + /* + * if ( auto a = AttributeSet::find(*attrs, "&alias") ) + * t.setTypeID(ID(*a->valueAsString())); + */ + + if ( auto a = AttributeSet::find(*attrs, "&cxxname") ) + t.setCxxID(ID(*a->valueAsString())); + } + + return t; + } + + // If an expression is a reference, dereference it; otherwise return the + // expression itself. + Expression derefOperand(const Expression& op) { + if ( ! type::isReferenceType(op.type()) ) + return op; + + if ( op.type().isA() ) + return operator_::value_reference::Deref::Operator().instantiate({op}, op.meta()); + else if ( op.type().isA() ) + return operator_::strong_reference::Deref::Operator().instantiate({op}, op.meta()); + else if ( op.type().isA() ) + return operator_::weak_reference::Deref::Operator().instantiate({op}, op.meta()); + else + logger().internalError("unknown reference type"); + } + + void operator()(const ctor::List& u, position_t p) { + if ( type::isResolved(u.type()) ) + return; + + if ( auto ntype = typeForExpressions(&p, u.value()) ) { + logChange(p.node, *ntype); + p.node.as().setElementType(std::move(*ntype)); + modified = true; + } + } + + void operator()(const ctor::Map& u, position_t p) { + if ( type::isResolved(u.keyType()) && type::isResolved(u.valueType()) ) + return; + + hilti::optional_ref key; + hilti::optional_ref value; + + for ( const auto& e : u.value() ) { + if ( ! (type::isResolved(e.key().type()) && type::isResolved(e.value().type())) ) + return; + + if ( ! key ) + key = e.key().type(); + else if ( e.key().type() != *key ) { + p.node.addError("inconsistent key types in map"); + return; + } + + if ( ! value ) + value = e.value().type(); + else if ( e.value().type() != *value ) { + p.node.addError("inconsistent value types in map"); + return; + } + } + + if ( ! (key && value) ) { + // empty map + key = type::unknown; + value = type::unknown; + } + + logChange(p.node, type::Tuple({*key, *value})); + p.node.as().setElementType(std::move(*key), std::move(*value)); + modified = true; + } + + void operator()(const ctor::Optional& u, position_t p) { + if ( type::isResolved(u.type()) || ! u.value() || ! type::isResolved(u.value()->type()) ) + return; + + logChange(p.node, u.value()->type()); + p.node.as().setDereferencedType(u.value()->type()); + modified = true; + } + + void operator()(const ctor::Result& u, position_t p) { + if ( type::isResolved(u.type()) || ! u.value() || ! type::isResolved(u.value()->type()) ) + return; + + logChange(p.node, u.value()->type()); + p.node.as().setDereferencedType(u.value()->type()); + modified = true; + } + + void operator()(const ctor::Set& u, position_t p) { + if ( type::isResolved(u.type()) ) + return; + + if ( auto ntype = typeForExpressions(&p, u.value()) ) { + logChange(p.node, *ntype); + p.node.as().setElementType(std::move(*ntype)); + modified = true; + } + } + + void operator()(const ctor::Struct& u, position_t p) { + if ( type::isResolved(u.type()) ) + return; + + std::vector field_types; + + for ( const auto& f : u.fields() ) { + if ( ! expression::isResolved(f.expression()) ) + return; + + field_types.emplace_back(declaration::Field(f.id(), f.expression().type(), std::nullopt, f.id().meta())); + } + + auto ntype = type::Struct(std::move(field_types), u.meta()); + logChange(p.node, ntype); + p.node.as().setType(ntype); + modified = true; + } + + void operator()(const ctor::Tuple& u, position_t p) { + if ( type::isResolved(u.type()) || ! expression::isResolved(u.value()) ) + return; + + auto types = node::transform(u.value(), [](const auto& e) -> Type { return Type(e.type()); }); + + logChange(p.node, type::Tuple(types)); + p.node.as().setElementTypes(std::move(types)); + modified = true; + } + + void operator()(const ctor::ValueReference& u, position_t p) { + if ( type::isResolved(u.type()) || ! type::isResolved(u.expression().type()) ) + return; + + logChange(p.node, u.expression().type()); + p.node.as().setDereferencedType(u.expression().type()); + modified = true; + } + + void operator()(const ctor::Vector& u, position_t p) { + if ( type::isResolved(u.type()) ) + return; + + if ( auto ntype = typeForExpressions(&p, u.value()) ) { + logChange(p.node, *ntype); + p.node.as().setElementType(std::move(*ntype)); + modified = true; + } + } + + void operator()(const declaration::Function& u, position_t p) { + if ( u.linkage() != declaration::Linkage::Struct ) { + // See if the namespace refers to a struct. If so, change linkage + // because that's what the normalizer will look for when linking + // methods to their parent type. + if ( auto r = scope::lookupID(u.id().namespace_(), p, "type") ) { + if ( auto d = r->first->tryAs(); d && d->type().isA() ) { + HILTI_DEBUG(logging::debug::Resolver, util::fmt("[%s] setting linkage to 'struct' (%s)", + p.node.typename_(), p.node.location())); + p.node.as().setLinkage(declaration::Linkage::Struct); + modified = true; + } + } + } + } + + void operator()(const declaration::ImportedModule& m, position_t p) { + std::shared_ptr imported_unit = m.unit(); + + if ( ! imported_unit ) { + std::string name; + Result> u; + + if ( m.path().empty() ) { + name = m.id().str(); + u = Unit::fromImport(_context, m.id(), m.parseExtension(), unit->extension(), m.scope(), + m.searchDirectories()); + } + else { + name = m.path().native(); + u = Unit::fromSource(_context, m.path(), unit->extension()); + } + + if ( u ) + imported_unit = *u; + else { + logger().error(util::fmt("cannot import module '%s': %s", name, u.error()), m.meta().location()); + return; + } + } + + auto current = _context->lookupUnit(_module.id(), unit->extension()); + assert(current); // unit must have been created before we started resolving the AST + + if ( current->unit->addDependency(imported_unit) || ! m.unit() ) { + logChange(p.node, "imported"); + p.node.as().setUnit(imported_unit); + modified = true; + } + } + + void operator()(const declaration::Type& u, position_t p) { + if ( u.type().typeID() ) + return; + + assert(u.canonicalID()); + auto t = addTypeID(u.type(), u.canonicalID(), u.attributes()); + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("[%s] setting type ID to %s (%s)", p.node.typename_(), *t.typeID(), p.node.location())); + p.node.as().setType(std::move(t)); + modified = true; + } + + void operator()(const expression::Deferred& e, position_t p) { + if ( type::isResolved(e.type()) ) + return; + + if ( expression::isResolved(e.expression()) ) { + logChange(p.node, e.expression().type()); + p.node.as().setType(e.expression().type()); + modified = true; + } + } + + void operator()(const expression::ListComprehension& e, position_t p) { + if ( ! type::isResolved(e.type()) && type::isResolved(e.output().type()) ) { + logChange(p.node, e.output().type()); + p.node.as().setElementType(e.output().type()); + modified = true; + } + + if ( ! type::isResolved(e.local().type()) && type::isResolved(e.input().type()) ) { + auto container = e.input().type(); + if ( ! type::isIterable(container) ) { + p.node.addError("right-hand side of list comprehension is not iterable"); + return; + } + + const auto& et = container.elementType(); + logChange(p.node, et); + p.node.as().setLocalType(et); + modified = true; + } + } + + void operator()(const expression::UnresolvedID& u, position_t p) { + auto resolved = scope::lookupID(u.id(), p, "declaration"); + if ( ! resolved ) { + if ( u.id() == ID("__dd") ) + // Provide better error message + p.node.addError("$$ is not available in this context", node::ErrorPriority::High); + else + p.node.addError(resolved.error(), node::ErrorPriority::High); + + return; + } + + if ( auto x = resolved->first->tryAs() ) { + // Resolve to to type expression, with type ID set. + auto t = addTypeID(x->type(), resolved->second, x->attributes()); + logChange(p.node, t); + p.node = expression::Type_(t, u.meta()); + modified = true; + } + else { + // If we are inside a call expression, leave it alone, operator + // resolving will take care of that. + auto op = p.parent().tryAs(); + if ( op && op->kind() == operator_::Kind::Call ) + return; + + auto n = expression::ResolvedID(resolved->second, NodeRef(resolved->first), u.meta()); + if ( ! type::isResolved(n.type()) ) + return; + + logChange(p.node, n); + p.node = std::move(n); + modified = true; + } + } + + // Helpers for operator resolving + bool resolveOperator(const expression::UnresolvedOperator& u, position_t p); + bool resolveFunctionCall(const expression::UnresolvedOperator& u, position_t p); + bool resolveMethodCall(const expression::UnresolvedOperator& u, position_t p); + bool resolveCast(const expression::UnresolvedOperator& u, position_t p); + void recordAutoParameters(const Type& type, const Expression& args); + std::vector matchOverloads(const std::vector& candidates, const node::Range& operands, + const Meta& meta, bool disallow_type_changes = false); + + void operator()(const expression::UnresolvedOperator& u, position_t p) { + if ( u.kind() == operator_::Kind::Call && resolveFunctionCall(u, p) ) + return; + + if ( u.kind() == operator_::Kind::MemberCall && resolveMethodCall(u, p) ) + return; + + if ( u.kind() == operator_::Kind::Cast && resolveCast(u, p) ) + return; + + resolveOperator(u, p); + } + + void operator()(const statement::For& u, position_t p) { + if ( type::isResolved(u.local().type()) ) + return; + + if ( ! type::isResolved(u.sequence().type()) ) + return; + + const auto& t = u.sequence().type(); + if ( ! type::isIterable(t) ) { + p.node.addError("expression is not iterable"); + return; + } + + const auto& et = t.iteratorType(true).dereferencedType(); + logChange(p.node, et); + p.node.as().setLocalType(std::move(et)); + modified = true; + } + + void operator()(const Function& f, position_t p) { + if ( ! f.ftype().result().type().isA() ) + return; + + // Look for a `return` to infer the return type. + for ( const auto i : visitor::PreOrder<>().walk(&p.node) ) { + if ( auto x = i.node.tryAs(); + x && x->expression() && type::isResolved(x->expression()->type()) ) { + const auto& rt = x->expression()->type(); + logChange(p.node, rt); + const_cast(p.node.as().ftype().result()).setType(rt); + modified = true; + break; + } + } + } + + void operator()(const type::Enum& m, position_t p) { + if ( type::isResolved(p.node.as()) ) + return; + + if ( ! p.node.as().typeID() ) + // Need to make sure we know the type ID before proceeding. + return; + + // The labels need to store a reference the type's node. + type::Enum::initLabelTypes(&p.node); + modified = true; + } + + void operator()(const type::UnresolvedID& u, position_t p) { + auto resolved = scope::lookupID(u.id(), p, "type"); + if ( ! resolved ) { + p.node.addError(resolved.error(), node::ErrorPriority::High); + return; + } + + // Note: We accept types here even when they aren't fully resolved yet, + // so that we can handle dependency cycles. + + const auto& d = resolved->first->as(); + auto t = d.type(); + t = addTypeID(std::move(t), resolved->second, d.attributes()); + + if ( d.isOnHeap() ) { + auto replace = false; + + if ( p.parent().tryAs() ) + replace = true; + + if ( p.parent().isA() && ! p.parent(2).isA() ) + replace = false; + + if ( replace ) + t = type::ValueReference(std::move(t), Location("")); + } + + logChange(p.node, t); + p.node = hilti::type::pruneWalk(std::move(t)); // alias to avoid visitor cycles + modified = true; + } +}; + +// Visitor to resolve any auto parameters that we inferred during the main resolver pass. +struct VisitorApplyAutoParameters : public visitor::PreOrder { + VisitorApplyAutoParameters(const ::Visitor& v) : visitor(v) {} + + const ::Visitor& visitor; + bool modified = false; + + void operator()(const declaration::Parameter& u, position_t p) { + if ( ! u.type().isA() ) + return; + + assert(u.canonicalID()); + auto i = visitor.auto_params.find(u.canonicalID()); + if ( i == visitor.auto_params.end() ) + return; + + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("[%s] %s -> type %s (%s)", p.node.typename_(), p.node, i->second, p.node.location())); + + p.node.as().setType(i->second); + modified = true; + } +}; + +bool Visitor::resolveOperator(const expression::UnresolvedOperator& u, position_t p) { + if ( ! u.areOperandsResolved() ) + return false; + + HILTI_DEBUG(logging::debug::Operator, + util::fmt("== trying to resolve operator: %s (%s)", to_node(u), u.meta().location().render(true))); + logging::DebugPushIndent _(logging::debug::Operator); + + std::vector resolved; + + const auto& candidates = operator_::registry().allOfKind(u.kind()); + + if ( u.kind() == operator_::Kind::MemberCall && u.operands().size() >= 2 ) { + // Pre-filter list of all member-call operators down to those + // with matching methods. This is just a performance + // optimization. + const auto& member_id = u.operands()[1].template as().id(); + auto filtered = util::filter(candidates, [&](const auto& c) { + return std::get(c.operands()[1].type).template as() == member_id; + }); + + resolved = matchOverloads(filtered, u.operands(), u.meta()); + } + else + resolved = matchOverloads(candidates, u.operands(), u.meta(), u.kind() == operator_::Kind::Cast); + + if ( resolved.empty() ) + return false; + + if ( resolved.size() > 1 ) { + std::vector context = {"candidates:"}; + for ( auto i : resolved ) + context.emplace_back(util::fmt("- %s [%s]", + detail::renderOperatorPrototype(i.as()), + i.typename_())); + + p.node.addError(util::fmt("operator usage is ambiguous: %s", detail::renderOperatorInstance(u)), + std::move(context)); + return true; + } + + logChange(p.node, resolved[0].as()); + p.node = std::move(resolved[0]); + modified = true; + +#ifndef NDEBUG + const Expression& new_op = p.node.as(); + HILTI_DEBUG(logging::debug::Operator, util::fmt("=> resolved to %s (result: %s, expression is %s)", p.node.render(), + new_op, (new_op.isConstant() ? "const" : "non-const"))); +#endif + return true; +} + +bool Visitor::resolveFunctionCall(const expression::UnresolvedOperator& u, position_t p) { + auto operands = u.operands(); + if ( operands.size() != 2 ) + return false; + + auto callee = operands[0].tryAs(); + if ( ! callee ) + return false; + + auto args_ctor = operands[1].tryAs(); + if ( ! args_ctor ) { + p.node.addError("function call's argument must be a tuple constant"); + return true; + } + + if ( ! type::isResolved(args_ctor->type()) ) + return true; + + auto args = args_ctor->ctor().tryAs(); + if ( ! args ) { + p.node.addError("function call's argument must be a tuple constant"); + return true; + } + + std::vector candidates; + + for ( auto i = p.path.rbegin(); i != p.path.rend(); i++ ) { + auto resolved = (**i).scope()->lookupAll(callee->id()); + if ( resolved.empty() ) + continue; + + for ( const auto& r : resolved ) { + auto d = r.node->tryAs(); + if ( ! d ) { + p.node.addError(util::fmt("ID '%s' resolves to something other than just functions", callee->id())); + return true; + } + + if ( r.external && d->linkage() != declaration::Linkage::Public ) { + p.node.addError(util::fmt("function has not been declared public: %s", r.qualified)); + return true; + } + + auto op = operator_::function::Call::Operator(r, d->function().ftype()); + candidates.emplace_back(op); + } + + auto overloads = matchOverloads(candidates, operands, u.meta()); + if ( overloads.empty() ) + break; + + if ( overloads.size() > 1 ) { + // Ok as long as it's all the same hook, report otherwise. + const auto& rid = overloads.front() + .template as() + .op0() + .template as(); + const auto& func = rid.declaration().template as().function(); + const auto& id = rid.id(); + + if ( func.ftype().flavor() != type::function::Flavor::Hook ) { + std::vector context = {"candidate functions:"}; + + for ( const auto& i : overloads ) + context.emplace_back( + util::fmt("- %s", detail::renderOperatorPrototype(i.as()))); + + p.node.addError(util::fmt("call is ambiguous: %s", detail::renderOperatorInstance(u)), + std::move(context)); + return true; + } + + for ( const auto& i : overloads ) { + const auto& rid = + i.template as().op0().template as(); + const auto& ofunc = rid.declaration().template as().function(); + const auto& oid = rid.id(); + + if ( id != oid || func.type() != ofunc.type() ) { + std::vector context = {"candidate functions:"}; + + for ( const auto& i : overloads ) + context.emplace_back( + util::fmt("- %s", detail::renderOperatorPrototype(i.as()))); + + p.node.addError(util::fmt("call is ambiguous: %s", detail::renderOperatorInstance(u)), + std::move(context)); + return true; + } + } + } + + auto n = std::move(overloads.front()); + const auto& r = n.as(); + const auto& func = r.op0().as().declaration().as().function(); + recordAutoParameters(func.type(), r.op1()); + + if ( ! type::isResolved(r.result()) ) + // Don't do anything before we know the function's return type. + // Note that we delay this check until we have checked for any auto + // parameters we might be able to resolve. + return true; + + // Found a match. + logChange(p.node, n.as()); + p.node = std::move(n); + modified = true; + + return true; + } + + // No match found. + std::vector context; + + if ( ! candidates.empty() ) { + context.emplace_back("candidate functions:"); + for ( const auto& i : candidates ) { + auto rop = i.instantiate(u.operands().copy(), u.meta()).as(); + context.emplace_back(util::fmt("- %s", detail::renderOperatorPrototype(rop))); + } + } + + p.node.addError(util::fmt("call does not match any function: %s", detail::renderOperatorInstance(u)), + std::move(context)); + return true; +} + +bool Visitor::resolveMethodCall(const expression::UnresolvedOperator& u, position_t p) { + auto operands = u.operands(); + if ( operands.size() != 3 ) + return false; + + std::vector shadow_ops; + + auto stype = operands[0].type().tryAs(); + if ( ! stype ) { + // Allow a still unresolved ID here so that we can start resolving auto parameters below. + if ( auto id = operands[0].tryAs() ) { + if ( auto resolved = scope::lookupID(id->id(), p, "declaration") ) { + // We temporarily create our own resolved ID for overload matching. + shadow_ops.emplace_back(Expression(expression::ResolvedID(resolved->second, NodeRef(resolved->first)))); + shadow_ops.emplace_back(operands[1]); + shadow_ops.emplace_back(operands[2]); + stype = shadow_ops[0].as().type().tryAs(); + } + } + } + + if ( ! stype && type::isResolved(operands[0].type()) && type::isReferenceType(operands[0].type()) ) + stype = derefOperand(operands[0]).type().tryAs(); + + if ( ! stype ) + return false; + + auto member = operands[1].tryAs(); + if ( ! member ) + return false; + + auto args_ctor = operands[2].tryAs(); + if ( ! args_ctor ) { + p.node.addError("method call's argument must be a tuple constant"); + return true; + } + + if ( ! type::isResolved(args_ctor->type()) ) + return true; + + auto args = args_ctor->ctor().tryAs(); + if ( ! args ) { + p.node.addError("method call's argument must be a tuple constant"); + return true; + } + + auto fields = stype->fields(member->id()); + if ( fields.empty() ) { + p.node.addError(util::fmt("struct type does not have a method `%s`", member->id())); + return false; // Continue trying to find another match. + } + + for ( const auto& f : fields ) { + if ( ! f.type().isA() ) { + p.node.addError(util::fmt("struct attribute '%s' is not a function", member->id())); + return true; + } + } + + auto candidates = node::transform(fields, [&](const auto& f) -> Operator { + auto x = operator_::struct_::MemberCall::Operator(*stype, f); + return std::move(x); + }); + + auto overloads = + matchOverloads(candidates, shadow_ops.empty() ? operands : node::Range(shadow_ops), u.meta()); + + if ( overloads.empty() ) { + std::vector context; + + if ( ! candidates.empty() ) { + context.emplace_back("candidate methods:"); + for ( const auto& i : candidates ) { + auto rop = i.instantiate(u.operands().copy(), u.meta()).as(); + context.emplace_back(util::fmt("- %s", detail::renderOperatorPrototype(rop))); + } + } + + p.node.addError(util::fmt("call does not match any method: %s", detail::renderOperatorInstance(u)), + std::move(context)); + return true; + } + + if ( overloads.size() > 1 ) { + std::vector context = {"candidates:"}; + for ( const auto& i : overloads ) + context.emplace_back( + util::fmt("- %s", detail::renderOperatorPrototype(i.as()))); + + p.node.addError(util::fmt("method call is ambiguous: %s", detail::renderOperatorInstance(u)), + std::move(context)); + return true; + } + + const auto& n = overloads.front().as(); + const auto& ft = n.op1().as().type().as(); + const auto& id = n.op1().as().id(); + const auto& ftype = stype->field(id)->type(); + recordAutoParameters(ftype, n.op2()); + + if ( ! type::isResolved(ft.result().type()) || shadow_ops.size() ) + // Don't do anything before we know the function's return type. + // Note that we delay this check until we have checked for any auto + // parameters we might be able to resolve. + return true; + + logChange(p.node, n); + p.node = std::move(overloads.front()); + modified = true; + + return true; +} + +/** Returns a set of overload alternatives matching a given operand expression. */ +bool Visitor::resolveCast(const expression::UnresolvedOperator& u, position_t p) { + if ( ! u.areOperandsResolved() ) + return false; + + // We hardcode that a cast<> operator can always perform any + // legal coercion. This helps in cases where we need to force a + // specific coercion to take place. + const auto& expr = u.operands()[0]; + const auto& dst = u.operands()[1].as().typeValue(); + const auto style = CoercionStyle::TryAllForMatching | CoercionStyle::ContextualConversion; + + if ( auto c = hilti::coerceExpression(expr, dst, style) ) { + auto nop = operator_::generic::CastedCoercion::Operator().instantiate(u.operands().copy(), u.meta()); + + logChange(p.node, nop); + p.node = std::move(nop); + modified = true; + return true; + } + + return false; +} + +void Visitor::recordAutoParameters(const Type& type, const Expression& args) { + const auto& ftype = type.as(); + + auto arg = args.as().ctor().as().value().begin(); + std::vector params; + for ( auto& rp : ftype.parameterRefs() ) { + auto p = rp->as(); + if ( ! p.type().isA() ) + continue; + + auto t = (*arg).type(); + if ( ! type::isResolved(t) ) + continue; + + assert(p.canonicalID()); + const auto& i = auto_params.find(p.canonicalID()); + if ( i == auto_params.end() ) { + auto_params.emplace(p.canonicalID(), t); + HILTI_DEBUG(logging::debug::Resolver, + util::fmt("recording auto parameter %s as of type %s", p.canonicalID(), t)); + } + else { + if ( i->second != t ) + const_cast(*rp).addError("mismatch for auto parameter"); + } + + ++arg; + } +} + +std::vector Visitor::matchOverloads(const std::vector& candidates, + const node::Range& operands, const Meta& meta, + bool disallow_type_changes) { + static const std::vector> styles = { + CoercionStyle::OperandMatching | CoercionStyle::TryExactMatch, + CoercionStyle::OperandMatching | CoercionStyle::TryExactMatch | CoercionStyle::TryCoercion, + CoercionStyle::OperandMatching | CoercionStyle::TryExactMatch | CoercionStyle::TryConstPromotion, + CoercionStyle::OperandMatching | CoercionStyle::TryExactMatch | CoercionStyle::TryConstPromotion | + CoercionStyle::TryCoercion, + }; + + auto deref_operands = [&](const node::Range& ops) { + std::vector nops; + + for ( const auto& op : ops ) + nops.push_back(derefOperand(op)); + + return nops; + }; + + auto try_candidate = [&](const auto& candidate, const node::Range& ops, auto style, + const auto& dbg_msg) -> std::optional { + auto nops = coerceOperands(ops, candidate.operands(), style); + if ( ! nops ) { + if ( ! (style & CoercionStyle::DisallowTypeChanges) ) { + // If any of the operands is a reference type, try the derefed operands, too. + for ( const auto& op : ops ) { + if ( type::isReferenceType(op.type()) ) + nops = + coerceOperands(node::Range(deref_operands(ops)), candidate.operands(), style); + } + } + } + + if ( ! nops ) + return {}; + + auto r = candidate.instantiate(nops->second, meta); + + // Some operators may not be able to determine their type before the + // resolver had a chance to provife the information needed. They will + // return "auto" in that case (specficially, that's the case for Spicy + // unit member acccess). Note we can't check if type::isResolved() here + // because operators may legitimatley return other unresolved types + // (e.g., IDs that still need to be looked up). + if ( r.type() == type::auto_ ) + return {}; + + HILTI_DEBUG(logging::debug::Operator, util::fmt("-> %s, resolves to %s %s", dbg_msg, to_node(r), + (r.isConstant() ? "(const)" : "(non-const)"))); + return r; + }; + + for ( auto style : styles ) { + if ( disallow_type_changes ) + style |= CoercionStyle::DisallowTypeChanges; + + HILTI_DEBUG(logging::debug::Operator, util::fmt("style: %s", to_string(style))); + logging::DebugPushIndent _(logging::debug::Operator); + + std::vector resolved; + + for ( const auto& c : candidates ) { + HILTI_DEBUG(logging::debug::Operator, util::fmt("candidate: %s", c.typename_())); + logging::DebugPushIndent _(logging::debug::Operator); + + if ( auto r = try_candidate(c, operands, style, "candidate matches") ) + resolved.emplace_back(std::move(*r)); + else { + // Try to swap the operators for commutative operators. + if ( operator_::isCommutative(c.kind()) && operands.size() == 2 ) { + if ( auto r = try_candidate(c, node::Range({operands[1], operands[0]}), style, + "candidate matches with operands swapped") ) + resolved.emplace_back(std::move(*r)); + } + } + } + + if ( resolved.size() ) + return resolved; + } + + return {}; +} + +} // anonymous namespace + +bool hilti::detail::ast::resolve(const std::shared_ptr& ctx, Node* root, Unit* unit) { + util::timing::Collector _("hilti/compiler/ast/resolver"); + + auto v1 = Visitor(ctx, root, unit); + for ( auto i : v1.walk(root) ) + v1.dispatch(i); + + auto v2 = VisitorApplyAutoParameters(v1); + for ( auto i : v2.walk(root) ) + v2.dispatch(i); + + return v1.modified || v2.modified; +} diff --git a/hilti/toolchain/src/compiler/visitors/scope-builder.cc b/hilti/toolchain/src/compiler/visitors/scope-builder.cc index 454d78e01..fd806e011 100644 --- a/hilti/toolchain/src/compiler/visitors/scope-builder.cc +++ b/hilti/toolchain/src/compiler/visitors/scope-builder.cc @@ -2,13 +2,18 @@ #include #include -#include #include #include +#include +#include #include #include #include #include +#include +#include +#include +#include #include #include @@ -16,322 +21,173 @@ using namespace hilti; namespace { -struct VisitorPass1 : public visitor::PostOrder { - explicit VisitorPass1(Unit* unit) : unit(unit) {} +struct Visitor : public visitor::PostOrder { + explicit Visitor(std::shared_ptr ctx, Unit* unit) : context(std::move(ctx)), unit(unit) {} + + std::shared_ptr context; Unit* unit; void operator()(const Module& m, position_t p) { - Node d = Declaration(declaration::Module(NodeRef(p.node), m.meta())); - p.node.scope()->insert(m.id(), std::move(d)); - } + auto scope = p.node.scope(); - void operator()(const declaration::ImportedModule& m, position_t p) { - auto& other = unit->imported(m.id()); - p.node = declaration::ImportedModule::setModule(m, NodeRef(other)); - p.node.setScope(other.scope()); + // Insert module name into top-level scope. + Declaration d = declaration::Module(NodeRef(p.node), m.meta()); + d.setCanonicalID(m.id()); + auto n = p.node.as().preserve(d); + scope->insert(std::move(n)); } - void operator()(const type::Function& m, position_t p) { - if ( p.parent().isA() ) - p.node.scope()->moveInto(p.parent().scope().get()); + void operator()(const declaration::GlobalVariable& d, position_t p) { + if ( p.parent().isA() ) + p.parent().scope()->insert(NodeRef(p.node)); } - void operator()(const type::Enum& m, position_t p) { - if ( auto t = p.parent().tryAs() ) { - for ( const auto& l : m.labels() ) { - auto e = expression::Ctor(ctor::Enum(l, NodeRef(p.parent()), l.meta()), l.meta()); - auto d = declaration::Constant(l.id(), std::move(e), t->linkage(), l.meta()); - p.parent().scope()->insert(l.id(), Declaration(std::move(d))); - } - } + void operator()(const declaration::Type& d, position_t p) { + if ( p.parent().isA() ) + p.parent().scope()->insert(NodeRef(p.node)); } - void operator()(const type::Struct& m, position_t p) { - if ( auto t = p.parent().tryAs() ) { - auto id = ID("self", m.meta()); - auto type = type::Computed(NodeRef(p.parent()), - [](Node& n) { return type::ValueReference(n.as().type()); }); - auto self = expression::Keyword(expression::keyword::Kind::Self, type, m.meta()); - auto d = declaration::Expression(id, self, declaration::Linkage::Private, m.meta()); - p.parent().scope()->insert(id, Declaration(d)); - - // Make parameters accessible - for ( auto&& x : p.node.as().parameterNodes() ) - p.parent().scope()->insert(x->as().id(), NodeRef(x)); + void operator()(const declaration::Constant& d, position_t p) { + if ( p.parent().isA() ) + p.parent().scope()->insert(NodeRef(p.node)); + } - for ( auto& f : m.fields() ) { - // If &id is specified, make field directly accessible under - // given ID (i.e., as alias to "self.[...]"). - ID id; + void operator()(const declaration::Expression& d, position_t p) { + if ( p.parent().isA() ) + p.parent().scope()->insert(NodeRef(p.node)); + } - if ( auto x = AttributeSet::find(f.attributes(), "&id") ) - id = ID(*x->valueAs(), f.meta()); + void operator()(const declaration::Field& f, position_t p) { + if ( auto func = f.inlineFunction() ) { + for ( auto&& x : func->ftype().parameterRefs() ) + p.node.scope()->insert(std::move(x)); + } - if ( id ) { - Expression self = - expression::ResolvedID("self", NodeRef(p.parent().scope()->lookup("self")->node), f.meta()); + if ( f.isStatic() ) + // Insert static member into struct's namespace. We create new + // declarations here (rather than point to instances already + // existing inside the AST) as that's (a) easier and (b) ok + // because everything is checked to be fully resolved already. + // + p.parent(2).scope()->insert(NodeRef(p.node)); + } - self = Expression( - operator_::value_reference::Deref::Operator().instantiate({std::move(self)}, f.meta())); + void operator()(const declaration::Function& f, position_t p) { + if ( p.parent().isA() ) + p.parent().scope()->insert(NodeRef(p.node)); - auto e = - operator_::struct_::MemberConst::Operator().instantiate({std::move(self), - expression::Member(f.id(), f.meta())}, - f.meta()); + for ( auto&& x : f.function().ftype().parameterRefs() ) + p.node.scope()->insert(std::move(x)); - auto d = declaration::Expression(id, std::move(e), {}, declaration::Linkage::Private, f.meta()); + if ( f.linkage() == declaration::Linkage::Struct ) { + if ( ! f.id().namespace_() ) { + p.node.addError("method lacks a type namespace"); + return; + } + } - p.parent().scope()->insert(id, Declaration(d)); - } + if ( f.linkage() == declaration::Linkage::Struct && f.parentStructType() && f.parentStructType()->selfRef() ) { + const auto& t = *f.parentStructType(); + auto ns = f.id().namespace_(); - if ( f.isStatic() ) { - // Insert static member into struct's namespace. - auto field_id = f.id(); - auto module_id = p.template findParent()->get().id(); - auto qualified_id = ID(module_id, t->id(), f.id()); - - std::optional decl; - - if ( f.type().isA() ) { - auto wrapper = type::Computed(NodeRef(p.node), [field_id](auto n) { - auto t = n.template as(); - return t.field(field_id)->type(); - }); - - auto nf = Function(f.id(), wrapper, {}, f.callingConvention()); - decl = declaration::Function(std::move(nf), t->linkage(), m.meta()); - } - else - // Using a local here is cheating a bit: We just need to - // get the ID through to codegen. - decl = declaration::LocalVariable(qualified_id, f.type()); - - p.parent().scope()->insert(f.id(), *decl); - } + auto fields = t.fields(f.id().local()); + if ( fields.empty() ) { + p.node.addError(util::fmt("type %s does not have a method '%s'", ns, f.id().local())); + return; } - } - } - void operator()(const statement::Switch& s, position_t p) { - auto wrapper = - type::Computed(NodeRef(p.node), [](Node& n) { return n.template as().type(); }); + bool found = false; + for ( const auto& sf : fields ) { + auto sft = sf.type().tryAs(); - auto d = declaration::LocalVariable(ID("__x"), wrapper, {}, true, s.meta()); - p.node.scope()->insert(d.id(), Declaration(d)); - } + if ( ! sft ) { + p.node.addError(util::fmt("%s is not a method", ID(ns, f.id().local()))); + return; + } - void operator()(const statement::Declaration& d, position_t p) { - p.node.scope()->moveInto(p.parent().scope().get()); - } + if ( areEquivalent(*sft, f.function().ftype()) ) + found = true; + } - void operator()(const declaration::Parameter& d, position_t p) { - if ( p.parent(2).isA() ) - p.parent(2).scope()->insert(d.id(), NodeRef(p.node)); + if ( ! found ) { + p.node.addError( + util::fmt("type %s does not have a method '%s' matching the signature", ns, f.id().local())); + return; + } - if ( p.parent(1).isA() ) - p.parent(1).scope()->insert(d.id(), NodeRef(p.node)); - } + p.node.scope()->insert(t.selfRef()); - void operator()(const declaration::LocalVariable& d, position_t p) { - if ( p.parent().isA() ) { - // Statement node may be replaced later, so insert an indirect - // reference to the local. - NodeRef x = NodeRef(p.parent()); - auto forward = - declaration::Forward([x]() -> Declaration { return *x->as().init(); }, d.meta()); - p.parent().scope()->insert(d.id(), Declaration(forward)); - return; + for ( auto&& x : t.parameterRefs() ) + p.node.scope()->insert(NodeRef(x)); } + } - if ( p.parent().isA() ) { - // Statement node may be replaced later, so insert an indirect - // reference to the local. - NodeRef x = NodeRef(p.parent()); - auto forward = - declaration::Forward([x]() -> Declaration { return *x->as().init(); }, d.meta()); - p.parent().scope()->insert(d.id(), Declaration(forward)); - return; + void operator()(const declaration::ImportedModule& m, position_t p) { + if ( const auto& cached = context->lookupUnit(m.id(), unit->extension()) ) { + auto other = cached->unit->moduleRef(); + p.node.setScope(other->scope()); + auto n = unit->module().as().preserve(p.node); + const_cast(*n).setScope(other->scope()); + p.parent().scope()->insert(std::move(n)); } - - p.parent().scope()->insert(d.id(), NodeRef(p.node)); } - void operator()(const expression::ListComprehension& e, position_t p) { - if ( p.node.scope()->has(e.id()) ) - // We can encounter this node multiple times. - return; + void operator()(const expression::ListComprehension& e, position_t p) { p.node.scope()->insert(e.localRef()); } - auto wrapper = type::Computed(NodeRef(p.node), [](auto n) { - const auto& lc = n.template as(); - if ( lc.input().type().template isA() ) - return lc.input().type(); + void operator()(const statement::Declaration& d, position_t p) { p.parent().scope()->insert(d.declarationRef()); } - if ( auto t = lc.input().type(); type::isIterable(t) ) - return t.iteratorType(true).dereferencedType(); - else - return type::unknown; - }); + void operator()(const statement::For& s, position_t p) { p.node.scope()->insert(s.localRef()); } - auto d = declaration::LocalVariable(e.id(), wrapper, {}, true, e.id().meta()); - p.node.scope()->insert(d.id(), Declaration(d)); + void operator()(const statement::If& s, position_t p) { + if ( s.initRef() ) + p.node.scope()->insert(s.initRef()); } - void operator()(const statement::For& s, position_t p) { - auto wrapper = type::Computed(NodeRef(p.node), [](auto n) { - auto t = n.template as().sequence().type(); - if ( t.template isA() ) - return t; - - if ( ! type::isIterable(t) ) - return type::unknown; + void operator()(const statement::Switch& s, position_t p) { p.node.scope()->insert(s.conditionRef()); } - return t.iteratorType(true).dereferencedType(); - }); - - auto d = declaration::LocalVariable(s.id(), wrapper, {}, true, s.id().meta()); - s.scope()->insert(d.id(), Declaration(d)); + void operator()(const statement::try_::Catch& s, position_t p) { + if ( auto x = s.parameterRef() ) + p.node.scope()->insert(std::move(x)); } -}; -struct VisitorPass2 : public visitor::PostOrder { - explicit VisitorPass2(Unit* unit) : unit(unit) {} - Unit* unit; - - void operator()(const Declaration& d, position_t p) { - if ( p.parent().isA() && d.id().namespace_().empty() ) - p.parent().scope()->insert(d.id(), NodeRef(p.node)); + void operator()(const statement::While& s, position_t p) { + if ( auto x = s.initRef() ) + p.node.scope()->insert(std::move(x)); } -}; -struct VisitorPass3 : public visitor::PostOrder { - explicit VisitorPass3(Unit* unit) : unit(unit) {} - Unit* unit; - - std::pair> lookupType(Node* u, const ID& id) { - auto resolved = u->scope()->lookupAll(id); - - if ( resolved.empty() ) - return std::make_pair(false, std::nullopt); - - if ( resolved.size() == 1 ) { - auto& r = resolved.front(); - - if ( auto t = r.node->template tryAs() ) { - if ( t->type().isA() ) - return std::make_pair(false, r.node); - } + void operator()(const type::Enum& m, position_t p) { + if ( ! p.parent().isA() ) + return; - u->addError(util::fmt("ID %s does not resolve to a type (but to %s)", id, r.node->typename_())); - return std::make_pair(true, std::nullopt); - } + if ( ! p.node.as().typeID() ) + // We need to associate the type ID with the declaration we're + // creating, so wait for that to have been set by the resolver. + return; - u->addError(util::fmt("type namespace %s is ambiguous", id)); - return std::make_pair(true, std::nullopt); + for ( auto&& d : p.node.as().labelDeclarationRefs() ) + p.parent().scope()->insert(std::move(d)); } - void operator()(const declaration::Function& f, position_t p) { - if ( f.linkage() == declaration::Linkage::Struct && ! f.function().isStatic() ) { - auto ns = f.id().namespace_(); - - if ( ns.empty() ) { - p.node.addError("method lacks a type namespace"); - return; - } - - for ( auto i = p.path.rbegin(); i != p.path.rend(); i++ ) { - auto [stop, node] = lookupType(&**i, ns); - - if ( stop ) - return; - - if ( ! node ) - continue; - - auto t = (*node)->as().type().as(); - auto fields = t.fields(f.id().local()); - - if ( fields.empty() ) { - p.node.addError(util::fmt("type %s does not have a method '%s'", ns, f.id().local())); - return; - } - - bool found = false; - for ( const auto& sf : fields ) { - auto sft = sf.type().tryAs(); - - if ( ! sft ) { - p.node.addError(util::fmt("%s is not a method", ID(ns, f.id().local()))); - return; - } - - if ( areEquivalent(*sft, f.function().type()) ) { - // Link any "auto" parameters to the declaration. When - // we update one later, all linked instanced will - // reflect the change. For types that are already - // resolved, we can just update any remaining auto - // directly. - auto field_params = sft->parameters(); - auto method_params = f.function().type().parameters(); - - for ( auto&& [pf, pm] : util::zip2(field_params, method_params) ) { - auto af = pf.type().tryAs(); - auto am = pm.type().tryAs(); - - if ( af && am ) - am->linkTo(*af); // both will be resolved together - else if ( af ) - af->typeNode() = pm.type(); // the other is already resolved - else if ( am ) - am->typeNode() = pf.type(); // the other is already resolved - } - - found = true; - } - } - - if ( ! found ) { - p.node.addError( - util::fmt("type %s does not have a method '%s' matching the signature", ns, f.id().local())); - return; - } + void operator()(const type::Struct& t, position_t p) { + if ( ! p.node.as().typeID() ) + // We need to associate the type ID with the declaration we're + // creating, so wait for that to have been set by the resolver. + return; - p.node.setScope((*node)->scope()); - return; - } + if ( t.selfRef() ) + p.node.scope()->insert(t.selfRef()); - p.node.addError(util::fmt("cannot resolve type namespace %s", ns)); - } + for ( auto&& x : t.parameterRefs() ) + p.parent().scope()->insert(std::move(x)); } }; } // anonymous namespace -void hilti::detail::clearErrors(Node* root) { - for ( const auto&& i : hilti::visitor::PreOrder<>().walk(root) ) - i.node.clearErrors(); -} - -void hilti::detail::buildScopes(const std::vector>& modules, Unit* unit) { - util::timing::Collector _("hilti/compiler/scope-builder"); - - // Need to run each phase on all modules first before proceeding to the - // next as they maybe be cross-module dependencies in later phases. +void hilti::detail::ast::buildScopes(const std::shared_ptr& ctx, Node* root, Unit* unit) { + util::timing::Collector _("hilti/compiler/ast/scope-builder"); - for ( auto& [id, m] : modules ) { - auto v1 = VisitorPass1(unit); - for ( auto i : v1.walk(&*m) ) - v1.dispatch(i); - } - - for ( auto& [id, m] : modules ) { - auto v2 = VisitorPass2(unit); - for ( auto i : v2.walk(&*m) ) - v2.dispatch(i); - } - - for ( auto& [id, m] : modules ) { - auto v3 = VisitorPass3(unit); - for ( auto i : v3.walk(&*m) ) - v3.dispatch(i); - } + auto v = Visitor(ctx, unit); + for ( auto i : v.walk(root) ) + v.dispatch(i); } diff --git a/hilti/toolchain/src/compiler/visitors/validator.cc b/hilti/toolchain/src/compiler/visitors/validator.cc index af86482ba..3623de1df 100644 --- a/hilti/toolchain/src/compiler/visitors/validator.cc +++ b/hilti/toolchain/src/compiler/visitors/validator.cc @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include #include @@ -66,10 +68,10 @@ struct Visitor : public visitor::PostOrder { void operator()(const Function& f, position_t p) { if ( auto attrs = f.attributes() ) { if ( auto prio = attrs->find("&priority") ) { - if ( f.type().flavor() != type::function::Flavor::Hook ) + if ( f.ftype().flavor() != type::function::Flavor::Hook ) error("only hooks can have priorities", p); - else if ( auto x = prio->valueAs(); ! x ) + else if ( auto x = prio->valueAsInteger(); ! x ) error(x.error(), p); } } @@ -89,18 +91,23 @@ struct Visitor : public visitor::PostOrder { if ( n.type().isWildcard() ) error("cannot use wildcard type for variables", p); - if ( ! n.typeArguments().empty() ) { - auto t = n.type(); + if ( p.parent().isA() ) { + // If we're at the block level, check type arguments. If not, we're + // part of another statement (like if/while/...) where + // initialization happens internally. + if ( ! n.typeArguments().empty() ) { + auto t = n.type(); - if ( type::isReferenceType(t) ) - t = t.dereferencedType(); + if ( type::isReferenceType(t) ) + t = t.dereferencedType(); - if ( ! t.isA() ) - error("only struct types can have arguments", p); - } + if ( ! type::takesArguments(t) ) + error("type does not take arguments", p); + } - if ( auto st = n.type().tryAs() ) - _checkStructArguments(n.typeArguments(), st->parameters(), p); + if ( type::takesArguments(n.type()) ) + _checkStructArguments(n.typeArguments(), n.type().parameters(), p); + } // Check whether this local variable was declared at module scope. We // need to match exact parent nodes here to not match other locals @@ -111,9 +118,14 @@ struct Visitor : public visitor::PostOrder { error("local variables cannot be declared at module scope", p); } + void operator()(const declaration::ImportedModule& n, position_t p) { + if ( ! n.unit() ) + error(fmt("could not import module %s", n.id()), p); + } + void operator()(const declaration::Parameter& n, position_t p) { if ( ! n.type().isA() ) { - if ( ! type::isAllocable(n.type()) && n.type() != type::Any() ) + if ( ! type::isAllocable(n.type()) && type::nonConstant(n.type()) != type::Any() ) error(fmt("type '%s' cannot be used for function parameter", n.type()), p); } @@ -141,7 +153,7 @@ struct Visitor : public visitor::PostOrder { error(fmt("invalid attribute '%s' for function parameter", attr.tag()), p); else { - if ( auto x = attr.valueAs(); ! x ) + if ( auto x = attr.valueAsString(); ! x ) error(x.error(), p); } } @@ -155,12 +167,12 @@ struct Visitor : public visitor::PostOrder { error("cannot use wildcard type for variables", p); if ( auto args = n.typeArguments(); args.size() ) { - if ( ! n.type().isA() ) - error("only struct types can have arguments", p); + if ( ! type::takesArguments(n.type()) ) + error("type does not take arguments", p); } - if ( auto st = n.type().tryAs() ) - _checkStructArguments(n.typeArguments(), st->parameters(), p); + if ( type::takesArguments(n.type()) ) + _checkStructArguments(n.typeArguments(), n.type().parameters(), p); } ////// Ctors @@ -171,8 +183,13 @@ struct Visitor : public visitor::PostOrder { if ( auto vr = t.tryAs() ) t = vr->dereferencedType(); - if ( auto st = t.tryAs() ) - _checkStructArguments(c.typeArguments(), st->parameters(), p); + if ( auto args = c.typeArguments(); args.size() ) { + if ( ! type::takesArguments(t) ) + error("type does not take arguments", p); + } + + if ( type::takesArguments(t) ) + _checkStructArguments(c.typeArguments(), t.parameters(), p); } void operator()(const hilti::ctor::Exception& e, position_t p) { @@ -193,14 +210,14 @@ struct Visitor : public visitor::PostOrder { } void operator()(const ctor::Map& n, position_t p) { - if ( ! n.value().empty() && (n.keyType() == type::unknown || n.elementType() == type::unknown) ) + if ( ! n.value().empty() && (n.keyType() == type::unknown || n.valueType() == type::unknown) ) error("map elements have inconsistent types", p); } void operator()(const ctor::Null& c, position_t p) {} void operator()(const ctor::SignedInteger& n, position_t p) { - auto [min, max] = util::signed_integer_range(n.type().width()); + auto [min, max] = util::signed_integer_range(n.width()); if ( n.value() < min || n.value() > max ) error("integer value out of range for type", p); @@ -216,7 +233,7 @@ struct Visitor : public visitor::PostOrder { } void operator()(const ctor::UnsignedInteger& n, position_t p) { - auto [min, max] = util::unsigned_integer_range(n.type().width()); + auto [min, max] = util::unsigned_integer_range(n.width()); if ( n.value() < min || n.value() > max ) error("integer value out of range for type", p); @@ -253,16 +270,18 @@ struct Visitor : public visitor::PostOrder { p); } - void operator()(const expression::TypeWrapped& n, position_t p) { - if ( n.validateTypeMatch() && n.expression().type() != n.type() ) - error(fmt("type mismatch, expression has type '%s', but expected '%s'", n.expression().type(), n.type()), - p); - } - void operator()(const expression::UnresolvedID& n, position_t p) { - // We prefer the error message from a parent UnresolvedOperator. - if ( ! p.node.hasErrors() && ! p.parent().isA() ) - error("unresolved ID", p); + if ( auto decl = p.findParent(); decl && ! decl->get().isA() ) { + if ( n.id() == decl->get().id() ) { + error("ID cannot be used inside its own declaration", p); + return; + } + } + + // We prefer the error message from a parent's unresolved call operator. + auto op = p.parent().tryAs(); + if ( ! op || op->kind() != operator_::Kind::Call ) + error(fmt("unknown ID '%s'", n.id()), p); } ////// Statements @@ -305,7 +324,7 @@ struct Visitor : public visitor::PostOrder { return; } - if ( func->get().type().result().type() == type::Void() ) { + if ( func->get().ftype().result().type() == type::void_ ) { if ( n.expression() ) error("void function cannot return a value", p); } @@ -355,13 +374,6 @@ struct Visitor : public visitor::PostOrder { error("'while' header lacking both condition and declaration", p); } - void operator()(const expression::ResolvedID& n, position_t p) { - if ( auto decl = p.findParent(); decl && ! decl->get().isA() ) { - if ( n.id() == decl->get().id() ) - error("ID cannot be used inside its own declaration", p); - } - } - void operator()(const expression::ResolvedOperator& n, position_t p) { // We are running after both overload resolution and the // apply-coercion pass, so operands types are ensured to be fine at @@ -370,14 +382,14 @@ struct Visitor : public visitor::PostOrder { } void operator()(const expression::UnresolvedOperator& n, position_t p) { - error(fmt("unsupported operator: %s", hilti::detail::renderOperatorInstance(n)), p, node::ErrorPriority::Low); + if ( p.node.errors().empty() ) + error(fmt("unsupported operator: %s", hilti::detail::renderOperatorInstance(n)), p); } ////// Types void operator()(const type::Auto& n, position_t p) { - if ( ! n.isSet() ) - error("'auto' type has not been resolved", p); + error("automatic type has not been resolved", p, node::ErrorPriority::Low); } void operator()(const type::Exception& n, position_t p) { @@ -388,34 +400,11 @@ struct Visitor : public visitor::PostOrder { void operator()(const type::Function& n, position_t p) { if ( n.flavor() == type::function::Flavor::Hook ) { auto r = n.result().type(); - if ( ! (r == type::Void() || r.isA()) ) + if ( ! (r == type::void_ || r.isA()) ) error(fmt("hooks must have return type either void or optional"), p); } } - void operator()(const expression::Keyword& n, position_t p) { - switch ( n.kind() ) { - case expression::keyword::Kind::DollarDollar: - if ( const auto& function = p.findParent() ) { - for ( const auto& hook : function->get().childsOfType() ) { - if ( hook.flavor() != type::function::Flavor::Hook ) - continue; - - const auto& parameters = hook.parameters(); - if ( parameters.end() == std::find_if(parameters.begin(), parameters.end(), - [](auto&& p) { return p.id() == ID("__dd"); }) ) - error("$$ is not available in this hook", p); - } - break; - } - - case expression::keyword::Kind::Captures: - case expression::keyword::Kind::Self: - // Nothing. - break; - } - } - void operator()(const type::SignedInteger& n, position_t p) { auto w = n.width(); @@ -516,9 +505,9 @@ struct Visitor : public visitor::PostOrder { } void operator()(const type::Tuple& n, position_t p) { - for ( const auto& t : n.types() ) { - if ( ! type::isAllocable(t) ) - error(fmt("type '%s' cannot be used inside a tuple", t), p); + for ( const auto& e : n.elements() ) { + if ( ! type::isAllocable(e.type()) && ! e.type().isA() ) + error(fmt("type '%s' cannot be used inside a tuple", e.type()), p); } } @@ -540,8 +529,8 @@ struct Visitor : public visitor::PostOrder { void operator()(const operator_::generic::New& n, position_t p) { // We reuse the _checkStructArguments() here, that's why this operator is covered here. if ( auto t = n.operands()[0].type().tryAs() ) { - if ( auto st = t->typeValue().tryAs() ) { - std::vector args; + if ( type::takesArguments(t->typeValue()) ) { + node::Range args; if ( n.operands().size() > 1 ) { auto ctor = n.operands()[1].as().ctor(); if ( auto x = ctor.tryAs() ) @@ -550,12 +539,12 @@ struct Visitor : public visitor::PostOrder { args = ctor.as().value(); } - _checkStructArguments(args, st->parameters(), p); + _checkStructArguments(args, t->typeValue().parameters(), p); } } } - void _checkStructArguments(const std::vector& have, const std::vector& want, + void _checkStructArguments(const node::Range& have, const node::Set& want, position_t& p) { if ( have.size() > want.size() ) error(fmt("type expects %u parameter%s, but receives %u", have.size(), (have.size() > 1 ? "s" : ""), @@ -564,10 +553,14 @@ struct Visitor : public visitor::PostOrder { for ( size_t i = 0; i < want.size(); i++ ) { if ( i < have.size() ) { - if ( have[i].type() != want[i].type() ) - error(fmt("type expects %s for parameter %u, but receives %s", want[i].type(), i + 1, - have[i].type()), - p); + if ( have[i].type() == want[i].type() ) + continue; + + if ( type::sameExceptForConstness(have[i].type(), want[i].type()) && type::isConstant(want[i].type()) ) + continue; + + error(fmt("type expects %s for parameter %u, but receives %s", want[i].type(), i + 1, have[i].type()), + p); } else if ( ! want[i].default_() ) error(fmt("type parameter %u is missing (%s)", i + 1, want[i].id()), p); @@ -577,7 +570,9 @@ struct Visitor : public visitor::PostOrder { } // anonymous namespace -void hilti::detail::validateAST(Node* root) { +void hilti::detail::ast::validate(Node* root) { + util::timing::Collector _("hilti/compiler/ast/validator"); + auto v = Visitor(); for ( auto i : v.walk(root) ) v.dispatch(i); diff --git a/hilti/toolchain/tests/visitor.cc b/hilti/toolchain/tests/visitor.cc index 78f210c4e..c1645e255 100644 --- a/hilti/toolchain/tests/visitor.cc +++ b/hilti/toolchain/tests/visitor.cc @@ -17,7 +17,7 @@ static auto ast() { auto s = hilti::declaration::Type(hilti::ID("s"), hilti::type::String()); auto i32 = hilti::declaration::Type(hilti::ID("i32"), hilti::type::SignedInteger(32)); auto d = hilti::declaration::Type(hilti::ID("d"), hilti::type::Real()); - auto e = hilti::declaration::LocalVariable(hilti::ID("e"), hilti::type::Void()); + auto e = hilti::declaration::LocalVariable(hilti::ID("e"), hilti::type::void_); auto c = hilti::declaration::LocalVariable(hilti::ID("c"), hilti::type::Bool(), hilti::expression::Ctor(hilti::ctor::Bool(true))); @@ -94,7 +94,7 @@ TEST_CASE("Visitor, pre-order, no result, constant nodes") { std::string x; const std::string expected = - "(mo),(id),-,-,(id),(ts)(t),-,-,(id),(ti)(t),-,-,(id),(t),-,-,(id),(t),-,-,(id),(t),(e:c),(c:b),"; + "(mo),(id),-,-,(id),(ts)(t),-,-,(id),(ti)(t),-,-,(id),(t),-,-,(id),(t),-,-,(id),(t),(e:c),(c:b),(t),"; }; // Node an rvalue. @@ -128,7 +128,7 @@ TEST_CASE("Visitor, pre-order, no result, constant nodes") { auto walk = Visitor().walk(root4); std::for_each(walk.begin(), walk.end(), [&](auto&&) { ++c; }); - CHECK(c == 24); + CHECK(c == 25); } TEST_CASE("Visitor, pre-order, with result, constant nodes") { @@ -160,7 +160,7 @@ TEST_CASE("Visitor, pre-order, with result, constant nodes") { std::string x; const std::string expected = - "(mo),(id),-,-,(id),(ts),-,-,(id),(ti),-,-,(id),(t),-,-,(id),(t),-,-,(id),(t),(e:c),(c:b),"; + "(mo),(id),-,-,(id),(ts),-,-,(id),(ti),-,-,(id),(t),-,-,(id),(t),-,-,(id),(t),(e:c),(c:b),(t),"; }; @@ -197,7 +197,7 @@ TEST_CASE("Visitor, post-order") { std::string x; const std::string expected = - "(id),-,(id),(ts)(t),-,-,(id),(ti)(t),-,-,(id),(t),-,-,(id),(t),-,-,(id),(t),(c:b),(e:c),-,(mo),"; + "(id),-,(id),(ts)(t),-,-,(id),(ti)(t),-,-,(id),(t),-,-,(id),(t),-,-,(id),(t),(t),(c:b),(e:c),-,(mo),"; }; auto root = ast(); @@ -241,9 +241,9 @@ TEST_CASE("Find specific parent") { TEST_CASE("Copy node by value") { hilti::Type t = hilti::type::Vector(hilti::type::String()); CHECK(! hilti::type::isConstant(t)); - auto t2 = hilti::type::setConstant(t._clone(), true); - auto t3 = hilti::type::setConstant(t, true); - auto t4(hilti::type::setConstant(t, true)); + auto t2 = hilti::type::constant(t._clone().as()); + auto t3 = hilti::type::constant(t); + auto t4(hilti::type::constant(t)); CHECK(hilti::type::isConstant(t2)); CHECK(hilti::type::isConstant(t3)); CHECK(hilti::type::isConstant(t4)); diff --git a/scripts/autogen-type-erased b/scripts/autogen-type-erased index 8739ac3b7..824ef8486 100755 --- a/scripts/autogen-type-erased +++ b/scripts/autogen-type-erased @@ -50,6 +50,9 @@ public: $class _clone() const; hilti::IntrusivePtr _clone_ptr() const; + +protected: + $class_members }; $model_methods_impl @@ -181,6 +184,16 @@ def parseTrait(s): return t + +def parseMember(s): + class Member: + pass + + m = Member() + x = s.split() + m.declaration = " ".join(x[1:]) + return m + # Main @@ -203,6 +216,7 @@ traits = [] attrs = [] class_ = None trait_methods = [] +class_members = [] in_comment = False comment = "" @@ -251,6 +265,12 @@ for line in open(args.api): comment = "" continue + if line.startswith("member"): + m = parseMember(line) + class_members.append(m) + comment = "" + continue + if line.startswith("}"): break @@ -283,13 +303,13 @@ class_methods_impl = ["inline {} {}::{}({}) {} {{ return data()->{}({}); }}\n".f m.params), m.attrs, m.name, ", ".join([forwardParam(p) for p in m.params])) for m in methods] class_methods_impl += ["inline bool {}::{}() const {{ return data()->{}(); }}\n" .format( class_, t.method, t.method) for t in trait_methods] +class_members = ["{};\n".format(m.declaration) for m in class_members] model_methods = ["{} {}({}) {} final;" .format( m.result, m.name, varsToString(m.params), m.attrs) for m in methods] model_methods += ["bool {}() const final;".format(t.method) for t in trait_methods] - def mmi(m): result = "return data().{}({});".format( m.name, ", ".join([a.name for a in m.params])) @@ -348,6 +368,7 @@ vals = { "class_methods_impl": "\n".join(class_methods_impl), "class_requires": class_requires, "class_traits": "".join(class_traits), + "class_members": "\n ".join(class_members), "concept_ctors": "\n ".join(concept_ctors), "concept_operators": "\n ".join(concept_operators), "concept_private_attrs": "\n ".join(concept_private_attrs), diff --git a/spicy/lib/spicy.spicy b/spicy/lib/spicy.spicy index b52784999..e0bfa6276 100644 --- a/spicy/lib/spicy.spicy +++ b/spicy/lib/spicy.spicy @@ -8,16 +8,16 @@ module spicy; public type AddressFamily = enum { IPv4, # IP4 address IPv6 # IPv6 address -} &cxxname="hilti::rt::AddressFamily"; +} &cxxname="::hilti::rt::AddressFamily"; ## Captures the state of base64 encoding/decoding for the corresponding library functions. -public type Base64Stream = __library_type("spicy::rt::base64::Stream"); +public type Base64Stream = __library_type("::spicy::rt::base64::Stream"); ## Specifies the bit order for individual bit ranges inside a bitfield. public type BitOrder = enum { LSB0, # bits are interpreted as lowest-significant-bit coming first MSB0 # bits are interpreted as most-significant-bit coming first -} &cxxname="hilti::rt::integer::BitOrder"; +} &cxxname="::hilti::rt::integer::BitOrder" ; ## Specifies byte order for data operations. public type ByteOrder = enum { @@ -25,44 +25,51 @@ public type ByteOrder = enum { Big, # data is in big-endian byte order Network, # data is in network byte order (same a big endian) Host # data is in byte order of the host we are executing on -} &cxxname="hilti::rt::ByteOrder"; +} &cxxname="::hilti::rt::ByteOrder" ; + +## Specifies the character set for bytes encoding/decoding. +public type Charset = enum { + ASCII, + UTF8 +} &cxxname="::hilti::rt::bytes::Charset"; ## Captures state for incremental regular expression matching. -public type MatchState = __library_type("hilti::rt::regexp::MatchState"); +public type MatchState = struct { +} &cxxname="::hilti::rt::regexp::MatchState"; ## Specifies a transport-layer protocol. public type Protocol = enum { TCP, UDP, ICMP -} &cxxname="hilti::rt::Protocol"; +} &cxxname="::hilti::rt::Protocol"; ## Specifies the type of a real value. public type RealType = enum { IEEE754_Single, # single precision in IEEE754 format IEEE754_Double # double precision in IEEE754 format -} &cxxname="hilti::rt::real::Type"; +} &cxxname="::hilti::rt::real::Type" ; ## Specifies the policy for a sink's reassembler when encountering overlapping data. public type ReassemblerPolicy = enum { First # take the original data & discard the new data -} &cxxname="spicy::rt::sink::ReassemblerPolicy"; +} &cxxname="::spicy::rt::sink::ReassemblerPolicy"; ## Specifies a side an operation should operate on. public type Side = enum { Left, # operate on left side Right, # operate on right side Both # operate on both sides -} &cxxname="hilti::rt::bytes::Side"; +} &cxxname="::hilti::rt::bytes::Side" ; ## Specifies direction of a search. public type Direction = enum { Forward, # search forward Backward, # search backward -} &cxxname="hilti::rt::stream::Direction"; +} &cxxname="::hilti::rt::stream::Direction"; ## Captures the state of gzip decompression for the corresponding library functions. -public type ZlibStream = __library_type("spicy::rt::zlib::Stream"); +public type ZlibStream = __library_type("::spicy::rt::zlib::Stream"); ## Initializes a zlib stream for decompression. ## @@ -70,31 +77,31 @@ public type ZlibStream = __library_type("spicy::rt::zlib::Stream"); ## (see https://www.zlib.net/manual.html). ## ## Will throw a `ZlibError` exception if initialization fails. -public function zlib_init(window_bits: int64) : ZlibStream &cxxname="spicy::rt::zlib::init" &have_prototype; +public function zlib_init(window_bits: int64) : ZlibStream &cxxname="::spicy::rt::zlib::init" &have_prototype; ## Decompresses a chunk of data through the given zlib stream. -public function zlib_decompress(inout stream_: ZlibStream, data: bytes) : bytes &cxxname="spicy::rt::zlib::decompress" &have_prototype; +public function zlib_decompress(inout stream_: ZlibStream, data: bytes) : bytes &cxxname="::spicy::rt::zlib::decompress" &have_prototype; ## Finalizes a zlib stream used for decompression. -public function zlib_finish(inout stream_: ZlibStream) : bytes &cxxname="spicy::rt::zlib::finish" &have_prototype; +public function zlib_finish(inout stream_: ZlibStream) : bytes &cxxname="::spicy::rt::zlib::finish" &have_prototype; ## Encodes a stream of data into base64. -public function base64_encode(inout stream_: Base64Stream, data: bytes) : bytes &cxxname="spicy::rt::base64::encode" &have_prototype; +public function base64_encode(inout stream_: Base64Stream, data: bytes) : bytes &cxxname="::spicy::rt::base64::encode" &have_prototype; ## Decodes a stream of base64 data back into the clear. -public function base64_decode(inout stream_: Base64Stream, data: bytes) : bytes &cxxname="spicy::rt::base64::decode" &have_prototype; +public function base64_decode(inout stream_: Base64Stream, data: bytes) : bytes &cxxname="::spicy::rt::base64::decode" &have_prototype; ## Finalizes a base64 stream used for decoding or encoding. -public function base64_finish(inout stream_: Base64Stream) : bytes &cxxname="spicy::rt::base64::finish" &have_prototype; +public function base64_finish(inout stream_: Base64Stream) : bytes &cxxname="::spicy::rt::base64::finish" &have_prototype; ## Returns the initialization value for CRC32 computation. -public function crc32_init() : uint64 &cxxname="spicy::rt::zlib::crc32_init" &have_prototype; +public function crc32_init() : uint64 &cxxname="::spicy::rt::zlib::crc32_init" &have_prototype; ## Computes a running CRC32. -public function crc32_add(crc: uint64, data: bytes) : uint64 &cxxname="spicy::rt::zlib::crc32_add" &have_prototype; +public function crc32_add(crc: uint64, data: bytes) : uint64 &cxxname="::spicy::rt::zlib::crc32_add" &have_prototype; ## Returns the current wall clock time. -public function current_time() : time &cxxname="hilti::rt::time::current_time" &have_prototype; +public function current_time() : time &cxxname="::hilti::rt::time::current_time" &have_prototype; ## Constructs a time value from a tuple of broken-out elements specifying local time. ## @@ -104,13 +111,13 @@ public function current_time() : time &cxxname="hilti::rt::time::current_time" & ## - *H*: hour (0-23) ## - *M*: minute (0-59) ## - *S*: second (0-59) -public function mktime(y: uint64, m: uint64, d: uint64, H: uint64, M: uint64, S: uint64) : time &cxxname="hilti::rt::time::mktime" &have_prototype; +public function mktime(y: uint64, m: uint64, d: uint64, H: uint64, M: uint64, S: uint64) : time &cxxname="::hilti::rt::time::mktime" &have_prototype; ## Returns a bytes value rendered as a hex string. -public function bytes_to_hexstring(value: bytes) : string &cxxname="spicy::rt::bytes_to_hexstring" &have_prototype; +public function bytes_to_hexstring(value: bytes) : string &cxxname="::spicy::rt::bytes_to_hexstring" &have_prototype; ## Returns the value of an environment variable, if set. -public function getenv(name: string) : optional &cxxname="hilti::rt::getenv" &have_prototype; +public function getenv(name: string) : optional &cxxname="::hilti::rt::getenv" &have_prototype; ## Formats a time according to user-specified format string. ## @@ -122,7 +129,7 @@ public function getenv(name: string) : optional &cxxname="hilti::rt::get ## ## This function can raise InvalidArgument if the timestamp could not be ## converted to local time or formatted. -public function strftime(format: string, timestamp: time) : string &cxxname="hilti::rt::strftime" &have_prototype; +public function strftime(format: string, timestamp: time) : string &cxxname="::hilti::rt::strftime" &have_prototype; ## Parse time from string. ## @@ -134,4 +141,4 @@ public function strftime(format: string, timestamp: time) : string &cxxname="hil ## This function raises InvalidArgument if the string could not be parsed ## with the given format string, or OutOfRange if the parsed time value cannot ## be represented. -public function strptime(buf: string, format: string) : time &cxxname="hilti::rt::strptime" &have_prototype; +public function strptime(buf: string, format: string) : time &cxxname="::hilti::rt::strptime" &have_prototype; diff --git a/spicy/lib/spicy_rt.hlt b/spicy/lib/spicy_rt.hlt index 193d62658..e220880de 100644 --- a/spicy/lib/spicy_rt.hlt +++ b/spicy/lib/spicy_rt.hlt @@ -1,13 +1,13 @@ module spicy_rt { -public type ParseError = exception &cxxname="spicy::rt::ParseError"; -public type Backtrack = exception &cxxname="spicy::rt::Backtrack"; -public type UnitAlreadyConnected = exception &cxxname="spicy::rt::UnitAlreadyConnected"; +public type ParseError = exception &cxxname="::spicy::rt::ParseError"; +public type Backtrack = exception &cxxname="::spicy::rt::Backtrack"; +public type UnitAlreadyConnected = exception &cxxname="::spicy::rt::UnitAlreadyConnected"; # State stored inside a unit to allow connecting it to a sink. -public type SinkState = __library_type("spicy::rt::sink::detail::State*"); +public type SinkState = __library_type("::spicy::rt::sink::detail::State*"); -public type ParsedUnit = __library_type("spicy::rt::ParsedUnit"); +public type ParsedUnit = __library_type("::spicy::rt::ParsedUnit"); public type TypeInfo = __library_type("const hilti::rt::TypeInfo*"); # Type for a Sink instance. When adding methods here, extend the C++-side spicy::rt::Sink type as well. @@ -26,22 +26,22 @@ public type Sink = struct { method void skip(uint<64> seq); method void trim(uint<64> seq); method void write(bytes data, optional> seq = Null, optional> len = Null); -} &cxxname="spicy::rt::Sink"; +} &cxxname="::spicy::rt::Sink"; -public type HiltiResumable = __library_type("hilti::rt::Resumable"); +public type HiltiResumable = __library_type("::hilti::rt::Resumable"); -public type Filters = __library_type("spicy::rt::filter::detail::Filters"); -public type Forward = __library_type("spicy::rt::filter::detail::Forward"); +public type Filters = __library_type("::spicy::rt::filter::detail::Filters"); +public type Forward = __library_type("::spicy::rt::filter::detail::Forward"); -declare public strong_ref filter_init(inout any unit &requires-type-feature="supports_filters", inout value_ref data, view cur) &cxxname="spicy::rt::filter::init" &have_prototype; -declare public void filter_connect(inout any unit &requires-type-feature="supports_filters", strong_ref<*> filter &requires-type-feature="is_filter") &cxxname="spicy::rt::filter::connect" &have_prototype; -declare public void filter_disconnect(inout any unit &requires-type-feature="supports_filters") &cxxname="spicy::rt::filter::disconnect" &have_prototype; -declare public void filter_forward(inout any filter &requires-type-feature="is_filter", bytes b) &cxxname="spicy::rt::filter::forward" &have_prototype; -declare public void filter_forward_eod(inout any filter &requires-type-feature="is_filter") &cxxname="spicy::rt::filter::forward_eod" &have_prototype; +declare public strong_ref filter_init(inout any unit &requires-type-feature="supports_filters", inout value_ref data, view cur) &cxxname="::spicy::rt::filter::init" &have_prototype; +declare public void filter_connect(inout any unit &requires-type-feature="supports_filters", strong_ref<*> filter &requires-type-feature="is_filter") &cxxname="::spicy::rt::filter::connect" &have_prototype; +declare public void filter_disconnect(inout any unit &requires-type-feature="supports_filters") &cxxname="::spicy::rt::filter::disconnect" &have_prototype; +declare public void filter_forward(inout any filter &requires-type-feature="is_filter", bytes b) &cxxname="::spicy::rt::filter::forward" &have_prototype; +declare public void filter_forward_eod(inout any filter &requires-type-feature="is_filter") &cxxname="::spicy::rt::filter::forward_eod" &have_prototype; -public type UnitContext = __library_type("spicy::rt::UnitContext"); -declare public UnitContext createContext(any ctx, TypeInfo ti) &cxxname="spicy::rt::detail::createContext" &have_prototype; -declare public void setContext(inout any unit, optional ctx, TypeInfo ti) &cxxname="spicy::rt::detail::setContext" &have_prototype; +public type UnitContext = __library_type("::spicy::rt::UnitContext"); +declare public UnitContext createContext(any ctx, TypeInfo ti) &cxxname="::spicy::rt::detail::createContext" &have_prototype; +declare public void setContext(inout any unit, optional ctx, TypeInfo ti) &cxxname="::spicy::rt::detail::setContext" &have_prototype; # Type for a parser definition. When making changes, adapt the C++-side # spicy::rt::Parser as well. @@ -55,31 +55,31 @@ public type Parser = struct { string description; any mime_types; vector ports; -} &cxxname="spicy::rt::Parser"; +} &cxxname="::spicy::rt::Parser"; -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"); -public type ParserPort = __library_type("spicy::rt::ParserPort"); +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"); +public type ParserPort = __library_type("::spicy::rt::ParserPort"); declare public void registerParser(inout Parser parse_func, string linker_scope, any instance) &cxxname="spicy::rt::detail::registerParser" &have_prototype; declare public void printParserState(string unit_id, value_ref data, view cur, int<64> lahead, iterator lahead_end, string literal_mode, bool trim) &cxxname="spicy::rt::detail::printParserState" &have_prototype; -declare public bool waitForInputOrEod(inout value_ref data, view cur, inout strong_ref filters) &cxxname="spicy::rt::detail::waitForInputOrEod" &have_prototype; -declare public bool waitForInputOrEod(inout value_ref data, view cur, uint<64> n, inout strong_ref filters) &cxxname="spicy::rt::detail::waitForInputOrEod" &have_prototype; -declare public void waitForInput(inout value_ref data, view cur, string error_msg, string location, inout strong_ref filters) &cxxname="spicy::rt::detail::waitForInput" &have_prototype; -declare public void waitForInput(inout value_ref data, view cur, uint<64> n, string error_msg, string location, strong_ref filters) &cxxname="spicy::rt::detail::waitForInput" &have_prototype; -declare public bool waitForEod(inout value_ref data, view cur, inout strong_ref filters) &cxxname="spicy::rt::detail::waitForEod" &have_prototype; -declare public bool atEod(inout value_ref data, view cur, inout strong_ref filters) &cxxname="spicy::rt::detail::atEod" &have_prototype; +declare public bool waitForInputOrEod(inout value_ref data, view cur, inout strong_ref filters) &cxxname="::spicy::rt::detail::waitForInputOrEod" &have_prototype; +declare public bool waitForInputOrEod(inout value_ref data, view cur, uint<64> n, inout strong_ref filters) &cxxname="::spicy::rt::detail::waitForInputOrEod" &have_prototype; +declare public void waitForInput(inout value_ref data, view cur, string error_msg, string location, inout strong_ref filters) &cxxname="::spicy::rt::detail::waitForInput" &have_prototype; +declare public void waitForInput(inout value_ref data, view cur, uint<64> n, string error_msg, string location, strong_ref filters) &cxxname="::spicy::rt::detail::waitForInput" &have_prototype; +declare public bool waitForEod(inout value_ref data, view cur, inout strong_ref filters) &cxxname="::spicy::rt::detail::waitForEod" &have_prototype; +declare public bool atEod(inout value_ref data, view cur, inout strong_ref filters) &cxxname="::spicy::rt::detail::atEod" &have_prototype; -declare public optional> unit_find(iterator begin_, iterator end_, optional> i, bytes needle, FindDirection dir) &cxxname="spicy::rt::detail::unitFind" &have_prototype; +declare public optional> unit_find(iterator begin_, iterator end_, optional> i, bytes needle, FindDirection dir) &cxxname="::spicy::rt::detail::unitFind" &have_prototype; -declare public void backtrack() &cxxname="spicy::rt::detail::backtrack" &have_prototype; +declare public void backtrack() &cxxname="::spicy::rt::detail::backtrack" &have_prototype; -public type BitOrder = enum { LSB0, MSB0 } &cxxname="hilti::rt::integer::BitOrder"; +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 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; +declare public void initializeParsedUnit(inout ParsedUnit punit, any unit, TypeInfo ti) &cxxname="::spicy::rt::ParsedUnit::initialize" &have_prototype; } diff --git a/spicy/runtime/include/parser.h b/spicy/runtime/include/parser.h index a82145f2d..425ddb90e 100644 --- a/spicy/runtime/include/parser.h +++ b/spicy/runtime/include/parser.h @@ -143,7 +143,7 @@ struct Parser { parse2(std::move(parse2)), parse3(parse3), context_new(context_new), - type(type), + type_info(type), description(std::move(description)), mime_types(std::move(mime_types)), ports(std::move(ports)) {} @@ -155,7 +155,7 @@ struct Parser { parse1(parse1), parse2(std::move(parse2)), parse3(parse3), - type(type), + type_info(type), description(std::move(description)), mime_types(std::move(mime_types)), ports(std::move(ports)) {} @@ -222,7 +222,7 @@ struct Parser { */ ContextNewFunction context_new = nullptr; - const hilti::rt::TypeInfo* type; + const hilti::rt::TypeInfo* type_info; /** * Human-readable description associated with this parser. diff --git a/spicy/runtime/src/base64.cc b/spicy/runtime/src/base64.cc index f401b2955..b51d633e9 100644 --- a/spicy/runtime/src/base64.cc +++ b/spicy/runtime/src/base64.cc @@ -1,9 +1,9 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "spicy/rt/base64.h" - #include +#include + extern "C" { #include #include diff --git a/spicy/runtime/src/util.cc b/spicy/runtime/src/util.cc index e22415109..6a401ae4e 100644 --- a/spicy/runtime/src/util.cc +++ b/spicy/runtime/src/util.cc @@ -1,13 +1,12 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "spicy/rt/util.h" - #include #include #include #include #include +#include std::string spicy::rt::version() { constexpr char spicy_version[] = PROJECT_VERSION_STRING_LONG; diff --git a/spicy/toolchain/CMakeLists.txt b/spicy/toolchain/CMakeLists.txt index e1783f859..7de958104 100644 --- a/spicy/toolchain/CMakeLists.txt +++ b/spicy/toolchain/CMakeLists.txt @@ -43,10 +43,12 @@ autogen_dispatchers(PRODUCTIONS_TYPE_ERASED ${AUTOGEN_H}/__dispatchers-productio ${CMAKE_CURRENT_SOURCE_DIR}/include/compiler/detail/codegen/productions.decl) set(SOURCES_COMPILER + src/ast/hook.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 + src/compiler/coercion.cc src/compiler/codegen/codegen.cc src/compiler/codegen/grammar.cc src/compiler/codegen/grammar-builder.cc @@ -61,9 +63,8 @@ set(SOURCES_COMPILER src/compiler/driver.cc src/compiler/parser/driver.cc src/compiler/plugin.cc - src/compiler/visitors/apply-coercions.cc - src/compiler/visitors/coercer.cc - src/compiler/visitors/id-resolver.cc + src/compiler/visitors/normalizer.cc + src/compiler/visitors/resolver.cc src/compiler/visitors/printer.cc src/compiler/visitors/scope-builder.cc src/compiler/visitors/validator.cc diff --git a/spicy/toolchain/bin/spicy-doc.cc b/spicy/toolchain/bin/spicy-doc.cc index 7c11d9b3c..3fbcf092f 100644 --- a/spicy/toolchain/bin/spicy-doc.cc +++ b/spicy/toolchain/bin/spicy-doc.cc @@ -33,6 +33,7 @@ static std::string kindToString(hilti::operator_::Kind kind) { KIND_TO_STRING(hilti::operator_::Kind::BitXor); KIND_TO_STRING(hilti::operator_::Kind::Call); KIND_TO_STRING(hilti::operator_::Kind::Cast); + KIND_TO_STRING(hilti::operator_::Kind::CustomAssign); KIND_TO_STRING(hilti::operator_::Kind::DecrPostfix); KIND_TO_STRING(hilti::operator_::Kind::DecrPrefix); KIND_TO_STRING(hilti::operator_::Kind::Delete); @@ -83,10 +84,10 @@ static json operandToJSON(const hilti::operator_::Operand& o) { hilti::Type t; - if ( auto f = - std::get_if(const std::vector&, - const std::vector&)>>(&o.type) ) - t = *(*f)({}, {}); + if ( auto f = std::get_if(const hilti::node::Range&, + const hilti::node::Range&)>>( + &o.type) ) + t = *(*f)(hilti::node::Range{}, hilti::node::Range{}); else t = std::get(o.type); @@ -123,7 +124,7 @@ int main(int argc, char** argv) { jop["kind"] = kindToString(op.kind()); jop["doc"] = op.doc(); jop["namespace"] = namespace_; - jop["rtype"] = formatType(op.result({})); + jop["rtype"] = formatType(op.result(hilti::node::Range())); jop["commutative"] = hilti::operator_::isCommutative(op.kind()); jop["operands"] = json(); diff --git a/spicy/toolchain/include/ast/aliases.h b/spicy/toolchain/include/ast/aliases.h index 4d26e3f16..be77070c5 100644 --- a/spicy/toolchain/include/ast/aliases.h +++ b/spicy/toolchain/include/ast/aliases.h @@ -11,8 +11,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/spicy/toolchain/include/ast/declarations/unit-hook.h b/spicy/toolchain/include/ast/declarations/unit-hook.h index 70f46bb5b..f26f36e13 100644 --- a/spicy/toolchain/include/ast/declarations/unit-hook.h +++ b/spicy/toolchain/include/ast/declarations/unit-hook.h @@ -9,46 +9,26 @@ #include #include -#include +#include namespace spicy { namespace declaration { /** AST node for a declaration of an external (i.e., module-level) unit hook. */ -class UnitHook : public hilti::NodeBase, public hilti::trait::isDeclaration { +class UnitHook : public hilti::DeclarationBase { public: - UnitHook(ID id, Type unit, const type::unit::Item& hook, Meta m = Meta()) - : NodeBase(hilti::nodes(std::move(id), std::move(unit), hook), std::move(m)) { - if ( ! hook.isA() ) - hilti::logger().internalError("non-unit hook passed into declaration::UnitHook"); + UnitHook(ID id, const Hook& hook, Meta m = Meta()) : DeclarationBase(hilti::nodes(id, hook), std::move(m)) { + childs()[1].as().setID(id); } - std::optional unitType() const { - Type t = type::effectiveType(childs()[1].as()); + const auto& hook() const { return child(1); } - if ( auto x = t.tryAs() ) - t = x->dereferencedType(); - - if ( t.isA() ) - return t.as(); - - if ( t.isA() ) - return t.originalNode()->as(); - - // Not resolved yet. - return {}; - } - - const auto& unitHook() const { return child(2); } - - bool operator==(const UnitHook& other) const { - return unitType() == other.unitType() && unitHook() == other.unitHook(); - } + bool operator==(const UnitHook& other) const { return id() == other.id() && hook() == other.hook(); } /** Implements `Declaration` interface. */ bool isConstant() const { return true; } /** Implements `Declaration` interface. */ - const auto& id() const { return child(0); } + const ID& id() const { return child(0); } /** Implements `Declaration` interface. */ Linkage linkage() const { return Linkage::Private; } /** Implements `Declaration` interface. */ diff --git a/spicy/toolchain/include/ast/hook.h b/spicy/toolchain/include/ast/hook.h index 13ab1d384..5e455f597 100644 --- a/spicy/toolchain/include/ast/hook.h +++ b/spicy/toolchain/include/ast/hook.h @@ -6,6 +6,8 @@ #include #include +#include +#include #include #include @@ -13,38 +15,65 @@ namespace spicy { +namespace type { + +class Unit; + +namespace unit::item { +class Field; +} + +} // namespace type + /** AST node representing a Spicy unit hook. */ -class Hook : public Function { +class Hook : public hilti::NodeBase { public: Hook(const std::vector& params, std::optional body, Engine engine, std::optional attrs = {}, Meta m = Meta()) - : Function(ID(""), - type::Function(type::function::Result(type::Void(), m), params, type::function::Flavor::Hook, m), - std::move(body), hilti::function::CallingConvention::Standard, std::move(attrs), std::move(m)), + : NodeBase(nodes(Function(ID(), + type::Function(type::function::Result(type::void_, m), params, + type::function::Flavor::Hook, m), + std::move(body), hilti::function::CallingConvention::Standard, std::move(attrs), m), + hilti::node::none), + m), _engine(engine) {} Hook() = default; + const auto& function() const { return child(0); } + + auto body() const { return function().body(); } + const auto& ftype() const { return function().ftype(); } + const auto& id() const { return function().id(); } + const auto& type() const { return function().type(); } + Engine engine() const { return _engine; } - bool isForEach() const { return AttributeSet::find(attributes(), "foreach").has_value(); } - bool isDebug() const { return AttributeSet::find(attributes(), "%debug").has_value(); } + NodeRef ddRef() const; + hilti::optional_ref unitType() const; + hilti::optional_ref unitField() const; + std::optional priority() const; - std::optional priority() const { - if ( auto p = AttributeSet::find(attributes(), "priority") ) - return *p->valueAs(); + bool isForEach() const { return AttributeSet::find(function().attributes(), "foreach").has_value(); } + bool isDebug() const { return AttributeSet::find(function().attributes(), "%debug").has_value(); } - return {}; - } + void setID(ID id) { childs()[0].as().setID(std::move(id)); } + void setUnitTypeRef(NodeRef p) { _unit_type = std::move(p); } + void setFieldRef(NodeRef p) { _unit_field = std::move(p); } + void setDDType(Type t) { childs()[1] = hilti::expression::Keyword::createDollarDollarDeclaration(t); } + void setResultType(Type t) { childs()[0].as().setResultType(std::move(t)); } - bool operator==(const Hook& other) const { - return static_cast(*this) == static_cast(other) && // NOLINT (cppcoreguidelines-slicing) - _engine == other._engine; - } + bool operator==(const Hook& other) const { return function() == other.function() && _engine == other._engine; } - auto properties() const { return Function::properties() + node::Properties{{"engine", to_string(_engine)}}; } + auto properties() const { + return node::Properties{{"engine", to_string(_engine)}, + {"unit_type", _unit_type.renderedRid()}, + {"unit_field", _unit_field.renderedRid()}}; + } private: Engine _engine = {}; + NodeRef _unit_type; + NodeRef _unit_field; }; /** Creates an AST node representing a `Hook`. */ diff --git a/spicy/toolchain/include/ast/nodes.decl b/spicy/toolchain/include/ast/nodes.decl index 696ef276e..b5a02b190 100644 --- a/spicy/toolchain/include/ast/nodes.decl +++ b/spicy/toolchain/include/ast/nodes.decl @@ -2,6 +2,7 @@ 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 diff --git a/spicy/toolchain/include/ast/operators/bitfield.h b/spicy/toolchain/include/ast/operators/bitfield.h index 608b3e5bd..8407be281 100644 --- a/spicy/toolchain/include/ast/operators/bitfield.h +++ b/spicy/toolchain/include/ast/operators/bitfield.h @@ -31,14 +31,14 @@ static hilti::expression::Member memberExpression(const Expression& op) { static inline void checkName(const Expression& op0, const Expression& op1, Node& n) { auto id = memberExpression(op1).id().local(); - if ( auto f = op0.type().as().bits(id); ! f ) + if ( const auto& f = op0.type().as().bits(id); ! f ) n.addError(hilti::util::fmt("bitfield type does not have attribute '%s'", id)); } static inline Type itemType(const Expression& op0, const Expression& op1) { if ( auto st = op0.type().tryAs() ) { - if ( auto f = st->bits(memberExpression(op1).id().local()) ) - return f->type(); + if ( const auto& f = st->bits(memberExpression(op1).id().local()) ) + return f->itemType(); } return type::unknown; @@ -47,7 +47,7 @@ static inline Type itemType(const Expression& op0, const Expression& op1) { } // namespace bitfield::detail BEGIN_OPERATOR_CUSTOM(bitfield, Member) - Type result(const std::vector& ops) const { + Type result(const hilti::node::Range& ops) const { if ( ops.empty() ) return type::DocOnly(""); diff --git a/spicy/toolchain/include/ast/operators/sink.h b/spicy/toolchain/include/ast/operators/sink.h index e18485676..776584d33 100644 --- a/spicy/toolchain/include/ast/operators/sink.h +++ b/spicy/toolchain/include/ast/operators/sink.h @@ -30,7 +30,7 @@ filters attached, this returns the value after filtering. BEGIN_METHOD(sink, Close) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "close", .args = {}, .doc = R"( @@ -46,7 +46,7 @@ END_METHOD BEGIN_METHOD(sink, Connect) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "connect", .args = {{.id = "u", .type = type::StrongReference(type::Unit(type::Wildcard()))}}, @@ -62,7 +62,7 @@ END_METHOD BEGIN_METHOD(sink, ConnectMIMETypeString) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "connect_mime_type", .args = {{.id = "mt", .type = type::String()}}, .doc = R"( @@ -77,9 +77,9 @@ END_METHOD BEGIN_METHOD(sink, ConnectMIMETypeBytes) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "connect_mime_type", - .args = {{.id = "mt", .type = type::Bytes()}}, + .args = {{.id = "mt", .type = type::constant(type::Bytes())}}, .doc = R"( Connects parsing units to a sink for all parsers that support a given MIME type. All subsequent write operations to the sink will pass their data on to @@ -92,7 +92,7 @@ END_METHOD BEGIN_METHOD(sink, ConnectFilter) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = hilti::type::Void(), + .result = hilti::type::void_, .id = "connect_filter", .args = {{.id = "filter", .type = hilti::type::StrongReference( @@ -115,7 +115,7 @@ END_METHOD BEGIN_METHOD(sink, Gap) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "gap", .args = {{.id = "seq", .type = type::UnsignedInteger(64)}, {.id = "len", .type = type::UnsignedInteger(64)}}, @@ -142,7 +142,7 @@ END_METHOD BEGIN_METHOD(sink, SetAutoTrim) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "set_auto_trim", .args = {{.id = "enable", .type = type::Bool()}}, .doc = R"( @@ -156,7 +156,7 @@ END_METHOD BEGIN_METHOD(sink, SetInitialSequenceNumber) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "set_initial_sequence_number", .args = { @@ -173,7 +173,7 @@ END_METHOD BEGIN_METHOD(sink, SetPolicy) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "set_policy", .args = { @@ -194,7 +194,7 @@ END_METHOD BEGIN_METHOD(sink, Skip) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "skip", .args = { @@ -214,7 +214,7 @@ END_METHOD BEGIN_METHOD(sink, Trim) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "trim", .args = { @@ -236,7 +236,7 @@ END_METHOD BEGIN_METHOD(sink, Write) auto signature() const { return hilti::operator_::Signature{.self = spicy::type::Sink(), - .result = type::Void(), + .result = type::void_, .id = "write", .args = {{.id = "data", .type = type::Bytes()}, {.id = "seq", .type = type::UnsignedInteger(64), .optional = true}, diff --git a/spicy/toolchain/include/ast/operators/unit.h b/spicy/toolchain/include/ast/operators/unit.h index 5dd67e45f..40103bc16 100644 --- a/spicy/toolchain/include/ast/operators/unit.h +++ b/spicy/toolchain/include/ast/operators/unit.h @@ -1,21 +1,235 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. +// +// This code adapts a number of operators from HILTI's struct type to Spicy's unit type. #pragma once +#include #include #include +#include #include #include +#include #include -#include #include +#include #include +#include #include +using namespace ::hilti::operator_; + namespace spicy::operator_ { +namespace unit::detail { + +// Returns an operand as a member expression. +static hilti::expression::Member memberExpression(const Expression& op) { + if ( auto c = op.tryAs() ) + return c->expression().as(); + + return op.as(); +} + +// Checks if an operand refers to a valid field inside a unit. +static inline void checkName(const Expression& op0, const Expression& op1, Node& node) { + auto id = memberExpression(op1).id().local(); + auto i = op0.type().as().itemByName(id); + + if ( ! i ) + node.addError(hilti::util::fmt("type does not have field '%s'", id)); +} + +// Returns the type of a unit field referenced by an operand. +static inline Type itemType(const Expression& op0, const Expression& op1) { + if ( auto st = op0.type().tryAs() ) { + if ( auto i = st->itemByName(memberExpression(op1).id().local()) ) + return i->itemType(); + } + + return type::unknown; +} + +} // namespace unit::detail + +BEGIN_OPERATOR_CUSTOM(unit, Unset) + Type result(const hilti::node::Range& ops) const { return type::void_; } + + bool isLhs() const { return true; } + + std::vector operands() const { + return {{.type = type::Unit(type::Wildcard()), .doc = "unit"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const hilti::expression::ResolvedOperator& i, hilti::operator_::position_t p) const { + detail::checkName(i.op0(), i.op1(), p.node); + } + + std::string doc() const { + return R"( +Clears an optional field. +)"; + } +END_OPERATOR_CUSTOM_x + +BEGIN_OPERATOR_CUSTOM_x(unit, MemberNonConst, Member) + Type result(const hilti::node::Range& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return detail::itemType(ops[0], ops[1]); + } + + bool isLhs() const { return true; } + + std::vector operands() const { + return {{.type = type::Unit(type::Wildcard()), .doc = "unit"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const hilti::expression::ResolvedOperator& i, hilti::operator_::position_t p) const { + detail::checkName(i.op0(), i.op1(), p.node); + + if ( i.op0().isConstant() ) + p.node.addError("cannot assign to field of constant unit instance"); + } + + std::string doc() const { + return R"( +Retrieves the value of a unit's field. If the field does not have a value assigned, +it returns its ``&default`` expression if that has been defined; otherwise it +triggers an exception. +)"; + } +END_OPERATOR_CUSTOM_x + +BEGIN_OPERATOR_CUSTOM_x(unit, MemberConst, Member) + Type result(const hilti::node::Range& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return detail::itemType(ops[0], ops[1]); + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::constant(type::Unit(type::Wildcard())), .doc = "unit"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const hilti::expression::ResolvedOperator& i, position_t p) const { + detail::checkName(i.op0(), i.op1(), p.node); + } + + std::string doc() const { + return R"( +Retrieves the value of a unit's field. If the field does not have a value assigned, +it returns its ``&default`` expression if that has been defined; otherwise it +triggers an exception. +)"; + } +END_OPERATOR_CUSTOM_x + +BEGIN_OPERATOR_CUSTOM(unit, TryMember) + Type result(const hilti::node::Range& ops) const { + if ( ops.empty() ) + return type::DocOnly(""); + + return detail::itemType(ops[0], ops[1]); + } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::Unit(type::Wildcard()), .doc = "unit"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const hilti::expression::ResolvedOperator& i, position_t p) const { + detail::checkName(i.op0(), i.op1(), p.node); + } + + std::string doc() const { + return R"( +Retrieves the value of a unit's field. If the field does not have a value +assigned, it returns its ``&default`` expression if that has been defined; +otherwise it signals a special non-error exception to the host application +(which will normally still lead to aborting execution, similar to the standard +dereference operator, unless the host application specifically handles this +exception differently). +)"; + } +END_OPERATOR_CUSTOM + +BEGIN_OPERATOR_CUSTOM(unit, HasMember) + Type result(const hilti::node::Range& /* ops */) const { return type::Bool(); } + + bool isLhs() const { return false; } + + std::vector operands() const { + return {{.type = type::constant(type::Unit(type::Wildcard())), .doc = "unit"}, + {.type = type::Member(type::Wildcard()), .doc = ""}}; + } + + void validate(const hilti::expression::ResolvedOperator& i, position_t p) const { + detail::checkName(i.op0(), i.op1(), p.node); + } + + std::string doc() const { + return "Returns true if the unit's field has a value assigned (not counting any ``&default``)."; + } +END_OPERATOR_CUSTOM + +OPERATOR_DECLARE_ONLY(unit, MemberCall) + +namespace unit { + +class MemberCall : public hilti::expression::ResolvedOperatorBase { +public: + using hilti::expression::ResolvedOperatorBase::ResolvedOperatorBase; + + struct Operator : public hilti::trait::isOperator { + Operator(const type::Unit& stype, const type::unit::item::Field& f) : _field(f) { + auto ftype = f.itemType().as(); + auto op0 = Operand{.type = stype}; + auto op1 = Operand{.type = type::Member(f.id())}; + auto op2 = Operand{.type = type::OperandList::fromParameters(ftype.parameters())}; + _operands = {op0, op1, op2}; + _result = ftype.result().type(); + }; + + static Kind kind() { return Kind::MemberCall; } + std::vector operands() const { return _operands; } + Type result(const hilti::node::Range& /* ops */) const { return _result; } + bool isLhs() const { return false; } + void validate(const hilti::expression::ResolvedOperator& /* i */, position_t p) const {} + std::string doc() const { return ""; } + std::string docNamespace() const { return ""; } + + Expression instantiate(const std::vector& operands, const Meta& meta) const { + auto ops = std::vector{operands[0], + hilti::expression::Member(_field.id(), _field.itemType(), _field.meta()), + operands[2]}; + + auto ro = hilti::expression::ResolvedOperator(MemberCall(*this, ops, meta)); + ro.setMeta(meta); + return ro; + } + + private: + type::unit::item::Field _field; + std::vector _operands; + Type _result; + }; +}; + +} // namespace unit + BEGIN_METHOD(unit, Offset) auto signature() const { return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), @@ -57,7 +271,7 @@ END_METHOD BEGIN_METHOD(unit, Input) auto signature() const { - return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), + return hilti::operator_::Signature{.self = type::constant(spicy::type::Unit(type::Wildcard())), .result = hilti::type::stream::Iterator(), .id = "input", .args = {}, @@ -76,7 +290,7 @@ END_METHOD BEGIN_METHOD(unit, SetInput) auto signature() const { return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), - .result = hilti::type::Void(), + .result = hilti::type::void_, .id = "set_input", .args = {{.id = "i", .type = type::constant(hilti::type::stream::Iterator())}}, @@ -125,7 +339,7 @@ END_METHOD BEGIN_METHOD(unit, ConnectFilter) auto signature() const { return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), - .result = hilti::type::Void(), + .result = hilti::type::void_, .id = "connect_filter", .args = {{.id = "filter", .type = hilti::type::StrongReference( @@ -144,7 +358,7 @@ END_METHOD BEGIN_METHOD(unit, Forward) auto signature() const { return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), - .result = hilti::type::Void(), + .result = hilti::type::void_, .id = "forward", .args = {{.id = "data", .type = hilti::type::Bytes()}}, .doc = R"( @@ -158,7 +372,7 @@ END_METHOD BEGIN_METHOD(unit, ForwardEod) auto signature() const { return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), - .result = hilti::type::Void(), + .result = hilti::type::void_, .id = "forward_eod", .args = {}, .doc = R"( @@ -172,7 +386,7 @@ END_METHOD BEGIN_METHOD(unit, Backtrack) auto signature() const { return hilti::operator_::Signature{.self = hilti::type::constant(spicy::type::Unit(type::Wildcard())), - .result = hilti::type::Void(), + .result = hilti::type::void_, .id = "backtrack", .args = {}, .doc = R"( @@ -183,12 +397,14 @@ Aborts parsing at the current position and returns back to the most recent END_METHOD static inline auto contextResult(bool is_const) { - return [=](const std::vector& /* orig_ops */, - const std::vector& resolved_ops) -> std::optional { + return [=](const hilti::node::Range& /* orig_ops */, + const hilti::node::Range& resolved_ops) -> std::optional { if ( resolved_ops.empty() ) return type::DocOnly("&"); - return type::Computed(hilti::builder::member(hilti::builder::id("self"), "__context")); + const auto& ctype = resolved_ops[0].type().as().contextType(); + assert(ctype); + return Type(type::StrongReference(*ctype)); }; } diff --git a/spicy/toolchain/include/ast/statements/print.h b/spicy/toolchain/include/ast/statements/print.h index daac06a1b..723c2bfe9 100644 --- a/spicy/toolchain/include/ast/statements/print.h +++ b/spicy/toolchain/include/ast/statements/print.h @@ -8,6 +8,8 @@ #include #include +#include + namespace spicy { namespace statement { diff --git a/spicy/toolchain/include/ast/statements/stop.h b/spicy/toolchain/include/ast/statements/stop.h index 1be89c0c8..dec13ef38 100644 --- a/spicy/toolchain/include/ast/statements/stop.h +++ b/spicy/toolchain/include/ast/statements/stop.h @@ -7,6 +7,8 @@ #include #include +#include + namespace spicy { namespace statement { diff --git a/spicy/toolchain/include/ast/types/bitfield.h b/spicy/toolchain/include/ast/types/bitfield.h index 3c426793f..b05e5291a 100644 --- a/spicy/toolchain/include/ast/types/bitfield.h +++ b/spicy/toolchain/include/ast/types/bitfield.h @@ -6,7 +6,9 @@ #include #include +#include #include +#include #include #include #include @@ -26,7 +28,11 @@ class Bits : public hilti::NodeBase { public: Bits() : NodeBase({ID(""), hilti::node::none}, Meta()) {} Bits(ID id, int lower, int upper, int field_width, std::optional attrs = {}, Meta m = Meta()) - : hilti::NodeBase(nodes(std::move(id), std::move(attrs)), std::move(m)), + : hilti::NodeBase(nodes(std::move(id), + hilti::expression::Keyword::createDollarDollarDeclaration( + hilti::type::UnsignedInteger(field_width)), + hilti::type::auto_, std::move(attrs)), + std::move(m)), _lower(lower), _upper(upper), _field_width(field_width) {} @@ -34,8 +40,11 @@ class Bits : public hilti::NodeBase { const auto& id() const { return child(0); } auto lower() const { return _lower; } auto upper() const { return _upper; } - Type type() const; - auto attributes() const { return childs()[1].tryReferenceAs(); } + auto fieldWidth() const { return _field_width; } + auto attributes() const { return childs()[3].tryAs(); } + const Type& ddType() const { return childs()[1].as().expression().type(); } + NodeRef ddRef() const { return NodeRef(childs()[1]); } + const auto& itemType() const { return child(2); } /** Implements the `Node` interface. */ auto properties() const { @@ -46,28 +55,19 @@ class Bits : public hilti::NodeBase { }; } + void setAttributes(AttributeSet attrs) { childs()[3] = std::move(attrs); } + void setItemType(Type t) { childs()[2] = std::move(t); } + bool operator==(const Bits& other) const { return id() == other.id() && _lower == other._lower && _upper == other._upper && - _field_width == other._field_width && attributes() == other.attributes(); - } - - /** - * Copies an existing bits instance but replaces its attributes. - * - * @param f original instance - * @param attrs new attributes - * @return new instances with attributes replaced - */ - static Bits setAttributes(const Bits& f, const AttributeSet& attrs) { - auto x = Bits(f); - x.childs()[1] = attrs; - return x; + _field_width == other._field_width && itemType() == other.itemType() && + attributes() == other.attributes(); } private: - int _lower{0}; - int _upper{0}; - int _field_width{0}; + int _lower = 0; + int _upper = 0; + int _field_width = 0; }; inline hilti::Node to_node(Bits f) { return hilti::Node(std::move(f)); } @@ -81,42 +81,33 @@ class Bitfield : public hilti::TypeBase, hilti::type::trait::isMutable { public: Bitfield(int width, std::vector bits, Meta m = Meta()) - : TypeBase(nodes(std::move(bits)), std::move(m)), _width(width) {} - Bitfield(Wildcard /*unused*/, Meta m = Meta()) : TypeBase({}, std::move(m)), _wildcard(true) {} + : TypeBase(nodes(type::UnsignedInteger(width, m), hilti::type::auto_, std::move(bits)), m), _width(width) {} + 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 childsOfType(); } - std::optional bits(const ID& id) const; + auto bits() const { return childs(2, -1); } + hilti::optional_ref bits(const ID& id) const; std::optional bitsIndex(const ID& id) const; - Type type() const; + const Type& parseType() const { return child(0); } + const Type& type() const { return child(1); } - bool operator==(const Bitfield& other) const { return width() == other.width() && bits() == other.bits(); } + void addField(bitfield::Bits f) { addChild(std::move(f)); } + void setType(Type t) { childs()[1] = std::move(t); } - /** For internal use by the builder API only. */ - auto _bitsNodes() { return nodesOfType(); } + 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; } + /** Implements the `Type` interface. */ auto typeParameters() const { return hilti::util::slice(childs(), 1); } /** Implements the `Type` interface. */ auto isWildcard() const { return _wildcard; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } - /** - * Copies an existing type and adds a new field to the copy. - * - * @param s original type - * @param f field to add - * @return new typed with field added - */ - static Bitfield addField(const Bitfield& s, bitfield::Bits f) { - auto x = Type(s)._clone().as(); - x.addChild(std::move(f)); - return x; - } - private: int _width = 0; bool _wildcard = false; diff --git a/spicy/toolchain/include/ast/types/sink.h b/spicy/toolchain/include/ast/types/sink.h index 4c845de63..7318d742e 100644 --- a/spicy/toolchain/include/ast/types/sink.h +++ b/spicy/toolchain/include/ast/types/sink.h @@ -18,6 +18,8 @@ class Sink : public hilti::TypeBase, hilti::type::trait::isAllocable { /** 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; } /** Implements the `Node` interface. */ auto properties() const { return node::Properties{}; } }; diff --git a/spicy/toolchain/include/ast/types/unit-item.api b/spicy/toolchain/include/ast/types/unit-item.api index 49d4dba87..0ac9960e5 100644 --- a/spicy/toolchain/include/ast/types/unit-item.api +++ b/spicy/toolchain/include/ast/types/unit-item.api @@ -2,7 +2,10 @@ class Item(trait::isUnitItem) : hilti::trait::isNode { /** Returns the type of the parsed unit item. */ - Type itemType() const; + const Type& itemType() const; + + /** Returns true if the item's type has been resolved. */ + bool isResolved() const; /** Returns true if the unit item is equivalent to another one. */ bool isEqual(const Item& other) const; @@ -23,11 +26,5 @@ class Item(trait::isUnitItem) : hilti::trait::isNode { void setMeta(Meta m); /** Implements the `Node` interface. */ - const NodeRef& originalNode() const; - - /** Implements the `Node` interface. */ - void setOriginalNode(const NodeRef& n); - - /** Implements the `Node` interface. */ - void clearCache(); + bool pruneWalk() const; }; diff --git a/spicy/toolchain/include/ast/types/unit-item.h b/spicy/toolchain/include/ast/types/unit-item.h index b82798927..07d6856ce 100644 --- a/spicy/toolchain/include/ast/types/unit-item.h +++ b/spicy/toolchain/include/ast/types/unit-item.h @@ -48,30 +48,15 @@ inline Node to_node(T t) { } // namespace type } // namespace spicy -inline bool operator==(const spicy::type::unit::Item& x, const spicy::type::unit::Item& y) { +namespace spicy::type::unit::detail { +inline bool operator==(const Item& x, const Item& y) { if ( &x == &y ) return true; assert(x.isEqual(y) == y.isEqual(x)); // Expected to be symmetric. return x.isEqual(y); } - -// TODO(robin): Not clear why we need this. Without it, vector comparisions dont' -// seem to find the eleement comparision operator. -inline bool operator==(const std::vector& t1, const std::vector& t2) { - if ( &t1 == &t2 ) - return true; - - if ( t1.size() != t2.size() ) - return false; - - for ( auto i = std::make_pair(t1.cbegin(), t2.cbegin()); i.first != t1.end() && i.second != t2.end(); - ++i.first, ++i.second ) - if ( ! (*i.first == *i.second) ) - return false; - - return true; -} +} // namespace spicy::type::unit::detail inline bool operator!=(const spicy::type::unit::Item& d1, const spicy::type::unit::Item& d2) { return ! (d1 == d2); } diff --git a/spicy/toolchain/include/ast/types/unit-items/field.h b/spicy/toolchain/include/ast/types/unit-items/field.h index afce46ea6..348419509 100644 --- a/spicy/toolchain/include/ast/types/unit-items/field.h +++ b/spicy/toolchain/include/ast/types/unit-items/field.h @@ -6,8 +6,11 @@ #include #include +#include +#include #include #include +#include #include #include @@ -22,13 +25,14 @@ class Field : public hilti::NodeBase, public spicy::trait::isUnitItem { std::optional repeat, const std::vector& sinks, std::optional attrs = {}, std::optional cond = {}, std::vector hooks = {}, Meta m = Meta()) - : NodeBase(nodes((id ? id : _uniquer.get("anon")), std::move(type), node::none, 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, repeat, std::move(attrs), std::move(cond), + args, sinks, hooks), std::move(m)), _is_forwarding(false), _is_transient(! id.has_value()), _engine(e), - _args_start(6), + _args_start(9), _args_end(_args_start + static_cast(args.size())), _sinks_start(_args_end), _sinks_end(_sinks_start + static_cast(sinks.size())), @@ -39,13 +43,13 @@ class Field : public hilti::NodeBase, public spicy::trait::isUnitItem { std::optional repeat, const std::vector& sinks, std::optional attrs = {}, std::optional cond = {}, std::vector hooks = {}, Meta m = Meta()) - : NodeBase(nodes((id ? id : _uniquer.get("anon")), ctor.type(), ctor, repeat, std::move(attrs), std::move(cond), - args, sinks, hooks), + : NodeBase(nodes((id ? id : _uniquer.get("anon")), hilti::node::none, hilti::type::auto_, hilti::node::none, + hilti::type::auto_, ctor, repeat, std::move(attrs), std::move(cond), args, sinks, hooks), std::move(m)), _is_forwarding(false), _is_transient(! id.has_value()), _engine(e), - _args_start(6), + _args_start(9), _args_end(_args_start + static_cast(args.size())), _sinks_start(_args_end), _sinks_end(_sinks_start + static_cast(sinks.size())), @@ -56,19 +60,40 @@ class Field : public hilti::NodeBase, public spicy::trait::isUnitItem { std::optional repeat, const std::vector& sinks, std::optional attrs = {}, std::optional cond = {}, std::vector hooks = {}, const Meta& m = Meta()) - : NodeBase(nodes((id ? id : _uniquer.get("anon")), item.itemType(), item, repeat, std::move(attrs), - std::move(cond), args, sinks, hooks), + : NodeBase(nodes((id ? id : _uniquer.get("anon")), hilti::node::none, hilti::type::auto_, hilti::node::none, + hilti::type::auto_, std::move(item), repeat, std::move(attrs), std::move(cond), args, sinks, + hooks), m), _is_forwarding(false), _is_transient(! id.has_value()), _engine(e), - _args_start(6), + _args_start(9), _args_end(_args_start + static_cast(args.size())), _sinks_start(_args_end), _sinks_end(_sinks_start + static_cast(sinks.size())), _hooks_start(_sinks_end), _hooks_end(_hooks_start + static_cast(hooks.size())) {} + Field(const std::optional& id, NodeRef type, Engine e, const std::vector& args, + std::optional repeat, const std::vector& sinks, + std::optional attrs = {}, std::optional cond = {}, std::vector hooks = {}, + const Meta& m = Meta()) + : NodeBase(nodes((id ? id : _uniquer.get("anon")), node::none, hilti::type::auto_, hilti::node::none, + hilti::type::auto_, node::none, repeat, std::move(attrs), std::move(cond), args, sinks, hooks), + std::move(m)), + _type(std::move(type)), + _is_forwarding(false), + _is_transient(! id.has_value()), + _engine(e), + _args_start(9), + _args_end(_args_start + static_cast(args.size())), + _sinks_start(_args_end), + _sinks_end(_sinks_start + static_cast(sinks.size())), + _hooks_start(_sinks_end), + _hooks_end(_hooks_start + static_cast(hooks.size())) { + (*_type)->isA(); + } + Field() = delete; Field(const Field& other) = default; Field(Field&& other) = default; @@ -76,12 +101,12 @@ class Field : public hilti::NodeBase, public spicy::trait::isUnitItem { const auto& id() const { return childs()[0].as(); } auto index() const { return _index; } - auto ctor() const { return childs()[2].tryReferenceAs(); } - auto item() const { return childs()[2].tryReferenceAs(); } + auto ctor() const { return childs()[5].tryAs(); } + auto item() const { return childs()[5].tryAs(); } - auto repeatCount() const { return childs()[3].tryReferenceAs(); } - auto attributes() const { return childs()[4].tryReferenceAs(); } - auto condition() const { return childs()[5].tryReferenceAs(); } + auto repeatCount() const { return childs()[6].tryAs(); } + auto attributes() const { return childs()[7].tryAs(); } + auto condition() const { return childs()[8].tryAs(); } auto arguments() const { return childs(_args_start, _args_end); } auto sinks() const { return childs(_sinks_start, _sinks_end); } auto hooks() const { return childs(_hooks_start, _hooks_end); } @@ -92,31 +117,63 @@ class Field : public hilti::NodeBase, public spicy::trait::isUnitItem { bool isTransient() const { return _is_transient; } bool emitHook() const { return ! isTransient() || hooks().size(); } - Type parseType() const; + const Type& originalType() const { + if ( _type ) + return (*_type)->as().type(); - Type originalType() const { return childs()[1].as(); } + if ( auto t = childs()[1].tryAs() ) + return *t; - Node& originalTypeNode() { return childs()[1]; } - Node& ctorNode() { return childs()[2]; } - Node& itemNode() { return childs()[2]; } - Node& attributesNode() { return childs()[4]; } + if ( auto c = ctor() ) + return c->type(); + + if ( auto i = item() ) + return i->itemType(); + + hilti::util::cannot_be_reached(); + } + + const Type& parseType() const { return childs()[2].as(); } + NodeRef parseTypeRef() const { return NodeRef(childs()[2]); } + const Type& itemType() const { return childs()[4].as(); } + + const Type& ddType() const { + if ( auto x = childs()[3].tryAs() ) + return x->expression().type(); + else + return hilti::type::auto_; + } + + NodeRef ddRef() const { + if ( childs()[3].isA() ) + return NodeRef(childs()[3]); + else + return {}; + } + + auto itemRef() { return NodeRef(childs()[5]); } // Get the `&convert` expression, if any. - // - // For unit-level converts, returns the unit type as well. - std::optional>> convertExpression() const; + std::optional>> convertExpression() const; + + void setForwarding(bool is_forwarding) { _is_forwarding = is_forwarding; } + void setDDType(Type t) { childs()[3] = hilti::expression::Keyword::createDollarDollarDeclaration(std::move(t)); } + void setIndex(uint64_t index) { _index = index; } + void setItemType(Type t) { childs()[4] = hilti::type::pruneWalk(std::move(t)); } + void setParseType(Type t) { childs()[2] = hilti::type::pruneWalk(std::move(t)); } bool operator==(const Field& other) const { return _engine == other._engine && id() == other.id() && originalType() == other.originalType() && + itemType() == other.itemType() && parseType() == other.parseType() && attributes() == other.attributes() && arguments() == other.arguments() && sinks() == other.sinks() && - condition() == other.condition() && hooks() == other.hooks(); // TODO + condition() == other.condition() && hooks() == other.hooks(); } Field& operator=(const Field& other) = default; Field& operator=(Field&& other) = default; // Unit item interface - Type itemType() const; + bool isResolved() const { return _type || item() || type::isResolved(itemType()); } auto isEqual(const Item& other) const { return node::isEqual(this, other); } // Node interface. @@ -126,41 +183,8 @@ class Field : public hilti::NodeBase, public spicy::trait::isUnitItem { {"forwarding", _is_forwarding}}; } - // Helper function for vector fields that returns the type of the - // vector's elements. The helper can be used in situations where the - // field type might not be fully resolved. It computes the type - // indirectly and dynamically: It looks up the struct that `self` is - // currently pointing to, and then extracts the auxiliary type from the - // struct's field named *id*. That type must be a vector, from which it - // then retrieves the element type. - // - // This is rather specialized of course, but necessary in some contexts. - // Note that it can only be used (1) at code locations where ``self`` - // evaluates to the desired struct, and (2) the field's auxiliary type - // has been set to a vector type (as we do for structs corresponding to - // units, where the auxiliary type is the parse type of the field). - static Type vectorElementTypeThroughSelf(ID id); - - /** - * Copies an existing field but changes its unit index. - * - * @param unit original field - * @param index the new index of the field - * @return new Field with unit index set as requested - */ - static Field setIndex(const Field& f, uint64_t index) { - auto x = Item(f)._clone().as(); - x._index = index; - return x; - } - - static Field setForwarding(const Field& f, bool is_forwarding) { - auto x = Item(f)._clone().as(); - x._is_forwarding = is_forwarding; - return x; - } - private: + std::optional _type; std::optional _index; bool _is_forwarding; bool _is_transient; diff --git a/spicy/toolchain/include/ast/types/unit-items/property.h b/spicy/toolchain/include/ast/types/unit-items/property.h index fb9cc0056..9ca15913a 100644 --- a/spicy/toolchain/include/ast/types/unit-items/property.h +++ b/spicy/toolchain/include/ast/types/unit-items/property.h @@ -22,8 +22,8 @@ class Property : public hilti::NodeBase, public spicy::trait::isUnitItem { : NodeBase(nodes(std::move(id), std::move(expr), std::move(attrs)), std::move(m)), _inherited(inherited) {} const auto& id() const { return child(0); } - auto expression() const { return childs()[1].tryReferenceAs(); } - auto attributes() const { return childs()[2].tryReferenceAs(); } + auto expression() const { return childs()[1].tryAs(); } + auto attributes() const { return childs()[2].tryAs(); } bool interited() const { return _inherited; } bool operator==(const Property& other) const { @@ -31,7 +31,8 @@ class Property : public hilti::NodeBase, public spicy::trait::isUnitItem { } // Unit field interface - Type itemType() const { return type::Void(); } + const Type& itemType() const { return type::void_; } + bool isResolved() const { return type::isResolved(itemType()); } auto isEqual(const Item& other) const { return node::isEqual(this, other); } // Node interface. diff --git a/spicy/toolchain/include/ast/types/unit-items/sink.h b/spicy/toolchain/include/ast/types/unit-items/sink.h index 8117a9386..5a57e4c57 100644 --- a/spicy/toolchain/include/ast/types/unit-items/sink.h +++ b/spicy/toolchain/include/ast/types/unit-items/sink.h @@ -18,15 +18,16 @@ namespace spicy::type::unit::item { class Sink : public hilti::NodeBase, public spicy::trait::isUnitItem { public: Sink(ID id, std::optional attrs = {}, Meta m = Meta()) - : NodeBase(nodes(std::move(id), std::move(attrs)), std::move(m)) {} + : NodeBase(nodes(std::move(id), std::move(attrs), type::Sink(m)), m) {} const auto& id() const { return child(0); } - auto attributes() const { return childs()[1].tryReferenceAs(); } + auto attributes() const { return childs()[1].tryAs(); } bool operator==(const Sink& other) const { return id() == other.id() && attributes() == other.attributes(); } // Unit field interface - Type itemType() const { return type::Sink(meta()); } + const Type& itemType() const { return child(2); } + bool isResolved() const { return type::isResolved(itemType()); } auto isEqual(const Item& other) const { return node::isEqual(this, other); } // Node interface. diff --git a/spicy/toolchain/include/ast/types/unit-items/switch.h b/spicy/toolchain/include/ast/types/unit-items/switch.h index d41e3001c..cc8b30c39 100644 --- a/spicy/toolchain/include/ast/types/unit-items/switch.h +++ b/spicy/toolchain/include/ast/types/unit-items/switch.h @@ -34,7 +34,7 @@ class Case : public hilti::NodeBase { auto expressions() const { return childsOfType(); } auto items() const { return childsOfType(); } - auto itemNodes() { return nodesOfType(); } + auto itemRefs() const { return childRefsOfType(); } /** Returns true if this is the default case. */ bool isDefault() const { return expressions().empty() && ! _look_ahead; } @@ -42,12 +42,20 @@ class Case : public hilti::NodeBase { /** Returns true if this is a look-ahead case. */ bool isLookAhead() const { return _look_ahead; } - auto properties() const { return node::Properties{{"default", isDefault()}, {"look-ahead", isLookAhead()}}; } + /** Returns true if all items have been resolved. */ + bool isResolved() const { + for ( const auto& i : items() ) { + if ( ! i.isResolved() ) + return false; + } - bool operator==(const Case& other) const { - return expressions() == other.expressions() && items() == other.items(); + return true; } + auto properties() const { return node::Properties{{"default", isDefault()}, {"look-ahead", isLookAhead()}}; } + + bool operator==(const Case& other) const; + private: bool _look_ahead = false; }; @@ -64,20 +72,16 @@ class Switch : public hilti::NodeBase, public spicy::trait::isUnitItem { Meta m = Meta()) : NodeBase(nodes(std::move(expr), std::move(cond), std::move(attributes), cases, std::move(hooks)), std::move(m)), - _engine(e), - _cases_start(3), - _cases_end(_cases_start + static_cast(cases.size())), - _hooks_start(_cases_end), - _hooks_end(-1) {} + _engine(e) {} - auto expression() const { return childs()[0].tryReferenceAs(); } Engine engine() const { return _engine; } - auto condition() const { return childs()[1].tryReferenceAs(); } - auto cases() const { return childs(_cases_start, _cases_end); } - auto casesNodes() { return nodesOfType(); } - auto attributes() const { return childs()[2].tryReferenceAs(); } + auto attributes() const { return childs()[2].tryAs(); } + auto cases() const { return childsOfType(); } + auto condition() const { return childs()[1].tryAs(); } + auto expression() const { return childs()[0].tryAs(); } + auto hooks() const { return childsOfType(); } - auto hooks() const { return childs(_hooks_start, _hooks_end); } + auto itemRefs() const { return childRefsOfType(); } /** Returns true if there's no field storing information. */ bool hasNoFields() const; @@ -87,7 +91,7 @@ class Switch : public hilti::NodeBase, public spicy::trait::isUnitItem { * * i: The field. */ - std::optional case_(const type::unit::item::Field& x); + hilti::optional_ref case_(const type::unit::item::Field& x); bool operator==(const Switch& other) const { return expression() == other.expression() && engine() == other.engine() && condition() == other.condition() && @@ -95,7 +99,17 @@ class Switch : public hilti::NodeBase, public spicy::trait::isUnitItem { } // Unit item interface - Type itemType() const { return type::Void(); } + const Type& itemType() const { return type::void_; } + + bool isResolved() const { + for ( const auto& c : cases() ) { + if ( ! c.isResolved() ) + return false; + } + + return true; + } + auto isEqual(const Item& other) const { return node::isEqual(this, other); } // Node interface. @@ -103,10 +117,6 @@ class Switch : public hilti::NodeBase, public spicy::trait::isUnitItem { private: Engine _engine; - const int _cases_start; - const int _cases_end; - const int _hooks_start; - const int _hooks_end; }; } // namespace spicy::type::unit::item diff --git a/spicy/toolchain/include/ast/types/unit-items/unit-hook.h b/spicy/toolchain/include/ast/types/unit-items/unit-hook.h index 7b6587c8d..8b10ad4ff 100644 --- a/spicy/toolchain/include/ast/types/unit-items/unit-hook.h +++ b/spicy/toolchain/include/ast/types/unit-items/unit-hook.h @@ -13,7 +13,9 @@ namespace spicy::type::unit::item { /** AST node for a unit hook. */ class UnitHook : public hilti::NodeBase, public spicy::trait::isUnitItem { public: - UnitHook(ID id, Hook hook, Meta m = Meta()) : NodeBase(nodes(std::move(id), std::move(hook)), std::move(m)) {} + UnitHook(ID id, Hook hook, Meta m = Meta()) : NodeBase(nodes(id, std::move(hook)), std::move(m)) { + childs()[1].as().setID(id); + } const auto& id() const { return child(0); } const auto& hook() const { return child(1); } @@ -22,7 +24,8 @@ class UnitHook : public hilti::NodeBase, public spicy::trait::isUnitItem { bool operator==(const UnitHook& other) const { return id() == other.id() && hook() == other.hook(); } // Unit field interface - Type itemType() const { return hook().type(); } + const Type& itemType() const { return hook().function().type(); } + bool isResolved() const { return type::isResolved(itemType()); } auto isEqual(const Item& other) const { return node::isEqual(this, other); } // Node interface. diff --git a/spicy/toolchain/include/ast/types/unit-items/unresolved-field.h b/spicy/toolchain/include/ast/types/unit-items/unresolved-field.h index a73ee27ea..831dfa01a 100644 --- a/spicy/toolchain/include/ast/types/unit-items/unresolved-field.h +++ b/spicy/toolchain/include/ast/types/unit-items/unresolved-field.h @@ -73,25 +73,25 @@ class UnresolvedField : public hilti::NodeBase, public spicy::trait::isUnitItem _sinks_start(_args_end), _sinks_end(_sinks_start + static_cast(sinks.size())) {} - auto fieldID() const { return childs()[2].tryReferenceAs(); } - + auto fieldID() const { return childs()[2].tryAs(); } + auto unresolvedID() const { return childs()[0].tryAs(); } const auto& index() const { return _index; } - auto unresolvedID() const { return childs()[0].tryReferenceAs(); } - // Only one of these will have return value. - auto type() const { return childs()[1].tryReferenceAs(); } - auto ctor() const { return childs()[1].tryReferenceAs(); } - auto item() const { return childs()[1].tryReferenceAs(); } + auto ctor() const { return childs()[1].tryAs(); } + auto item() const { return childs()[1].tryAs(); } + auto type() const { return childs()[1].tryAs(); } - auto repeatCount() const { return childs()[3].tryReferenceAs(); } - auto attributes() const { return childs()[4].tryReferenceAs(); } - auto condition() const { return childs()[5].tryReferenceAs(); } + auto repeatCount() const { return childs()[3].tryAs(); } + auto attributes() const { return childs()[4].tryAs(); } + auto condition() const { return childs()[5].tryAs(); } auto arguments() const { return childs(_args_start, _args_end); } auto sinks() const { return childs(_sinks_start, _sinks_end); } auto hooks() const { return childs(_sinks_end, -1); } Engine engine() const { return _engine; } + void setIndex(uint64_t index) { _index = index; } + bool operator==(const UnresolvedField& other) const { return _engine == other._engine && unresolvedID() == other.unresolvedID() && fieldID() == other.fieldID() && attributes() == other.attributes() && arguments() == other.arguments() && sinks() == other.sinks() && @@ -99,25 +99,13 @@ class UnresolvedField : public hilti::NodeBase, public spicy::trait::isUnitItem } // Unit item interface - Type itemType() const { return hilti::type::unknown; } + const Type& itemType() const { return hilti::type::auto_; } + bool isResolved() const { return false; } auto isEqual(const Item& other) const { return node::isEqual(this, other); } // Node interface. auto properties() const { return node::Properties{{"engine", to_string(_engine)}}; } - /** - * Copies an existing field but changes it unit index. - * - * @param unit original field - * @param index the new index of the field - * @return new Field with unit index set as requested - */ - static UnresolvedField setIndex(const UnresolvedField& f, uint64_t index) { - auto x = Item(f)._clone().as(); - x._index = index; - return x; - } - private: Engine _engine; std::optional _index; diff --git a/spicy/toolchain/include/ast/types/unit-items/variable.h b/spicy/toolchain/include/ast/types/unit-items/variable.h index daebb552f..17026bc2b 100644 --- a/spicy/toolchain/include/ast/types/unit-items/variable.h +++ b/spicy/toolchain/include/ast/types/unit-items/variable.h @@ -25,8 +25,8 @@ class Variable : public hilti::NodeBase, public spicy::trait::isUnitItem { : NodeBase(nodes(std::move(id), std::move(type), default_, std::move(attrs)), std::move(m)) {} const auto& id() const { return child(0); } - auto default_() const { return childs()[2].tryReferenceAs(); } - auto attributes() const { return childs()[3].tryReferenceAs(); } + auto default_() const { return childs()[2].tryAs(); } + auto attributes() const { return childs()[3].tryAs(); } bool isOptional() const { return AttributeSet::find(attributes(), "&optional").has_value(); } @@ -35,8 +35,9 @@ class Variable : public hilti::NodeBase, public spicy::trait::isUnitItem { attributes() == other.attributes(); } - // Unit field interface - Type itemType() const { return type::effectiveType(child(1)); } + // Unit item interface + const Type& itemType() const { return child(1); } + bool isResolved() const { return type::isResolved(itemType()); } auto isEqual(const Item& other) const { return node::isEqual(this, other); } // Node interface. diff --git a/spicy/toolchain/include/ast/types/unit.h b/spicy/toolchain/include/ast/types/unit.h index 257451694..2115f47de 100644 --- a/spicy/toolchain/include/ast/types/unit.h +++ b/spicy/toolchain/include/ast/types/unit.h @@ -2,13 +2,19 @@ #pragma once +#include #include #include +#include #include #include #include +#include +#include +#include #include +#include #include #include @@ -52,39 +58,38 @@ class Unit : detail::AssignIndices, public hilti::TypeBase, hilti::type::trait::isAllocable, hilti::type::trait::isParameterized, - hilti::type::trait::isOnHeap { + hilti::type::trait::takesArguments, + hilti::type::trait::isMutable { public: - Unit(std::vector p, std::vector i, + Unit(std::vector params, std::vector i, const std::optional& /* attrs */ = {}, Meta m = Meta()) - : TypeBase(hilti::nodes(std::move(p), assignIndices(std::move(i))), std::move(m)) { - _state().flags += type::Flag::NoInheritScope; - } - - Unit(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(std::move(m)), _wildcard(true) { - _state().flags += type::Flag::NoInheritScope; - } - - auto parameters() const { return childsOfType(); } - auto items() const { return childsOfType(); } - - std::optional attributes() const { - auto x = childsOfType(); - if ( x.size() ) - return x[0]; + : TypeBase(hilti::nodes(node::none, node::none, node::none, + hilti::util::transform(params, + [](auto p) { + p.setIsTypeParameter(); + return Declaration(p); + }), + assignIndices(std::move(i))), + std::move(m)) {} + + Unit(Wildcard /*unused*/, Meta m = Meta()) : TypeBase(std::move(m)), _wildcard(true) {} + + NodeRef selfRef() const { + if ( childs()[0].isA() ) + return NodeRef(childs()[0]); else return {}; } - auto types() const { - std::vector types; - for ( auto c : childs() ) - types.push_back(c.as().itemType()); - - return types; - } + auto id() const { return childs()[1].tryAs(); } + auto parameters() const { return childsOfType(); } + auto parameterRefs() const { return childRefsOfType(); } + auto items() const { return childsOfType(); } + auto itemRefs() const { return childRefsOfType(); } + auto attributes() const { return childs()[2].tryAs(); } /** Returns the type set through ``%context`, if available. */ - std::optional contextType() const { + hilti::optional_ref contextType() const { if ( auto context = propertyItem("%context") ) return context->expression()->as().typeValue(); else @@ -92,30 +97,28 @@ class Unit : detail::AssignIndices, } /** - * Returns the field of a given name if it exists. This descends + * Returns the item of a given name if it exists. This descends * recursively into childs as well. */ - std::optional field(const ID& id) const; + hilti::optional_ref itemByName(const ID& id) const; + + /** Returns a reference to an item give by its ID. */ + NodeRef itemRefByName(const ID& id) const; /** * Returns all of the unit's items of a particular subtype T. **/ template auto items() const { - std::vector v; - for ( const auto& c : childs() ) { - if ( auto x = c.tryAs() ) - v.push_back(*x); - } - return v; + return childsOfType(); } /** * Returns the property of a given name if it exists. If it exists more * than once, it's undefined which one is returned. */ - std::optional propertyItem(const std::string& name) const { - for ( auto i : items() ) { + hilti::optional_ref propertyItem(const std::string& name) const { + for ( const auto& i : items() ) { if ( i.id() == name ) return i; } @@ -125,11 +128,11 @@ class Unit : detail::AssignIndices, /** Returns all properties of a given name. */ auto propertyItems(const std::string& name) const { - std::vector props; + hilti::node::Set props; for ( const auto& i : items() ) { if ( i.id() == name ) - props.push_back(i); + props.insert(i); } return props; @@ -179,17 +182,44 @@ class Unit : detail::AssignIndices, */ bool isFilter() const { return propertyItem("%filter").has_value(); } - /** Returns the grammar associated with the type. It must have been set before through `setGrammar()`. */ + /** Returns the grammar associated with the type. It must have been set + * before through `setGrammar()`. */ const spicy::detail::codegen::Grammar& grammar() const { assert(_grammar); return *_grammar; } - bool operator==(const Unit& other) const { return typeID() == other.typeID(); } + /** Adds a number of new items to the unit. */ + void addItems(std::vector items) { + auto new_items = assignIndices(items); + + for ( auto i : new_items ) + childs().emplace_back(std::move(i)); + } + + void setAttributes(AttributeSet attrs) { childs()[2] = std::move(attrs); } + void setGrammar(std::shared_ptr g) { _grammar = std::move(g); } + void setID(ID id) { childs()[1] = std::move(id); } + void setPublic(bool p) { _public = p; } + + bool operator==(const Unit& other) const { + // We treat units as equal (only) if their type IDs match. That's + // checked upstream in the Type's comparision operator. + return false; + } // Type interface. auto isEqual(const Type& other) const { return node::isEqual(this, other); } + auto _isResolved(ResolvedState* rstate) const { + for ( const auto& i : items() ) { + if ( ! i.isResolved() ) + return false; + } + + return true; + } + // type::trait::Parameterized interface. auto typeParameters() const { return childs(); } auto isWildcard() const { return _wildcard; } @@ -198,59 +228,18 @@ class Unit : detail::AssignIndices, auto properties() const { return node::Properties{{"public", _public}}; } /** - * Copies an existing unit type but changes it ``public`` state. - * - * @param unit original unit type - * @param p true if the copied type is to be public - * @return new type with ``public`` state set as requested - */ - static Unit setPublic(const Unit& unit, bool p) { - auto x = Type(unit)._clone().as(); - x._public = p; - return x; - } - - /** - * Copies an existing unit type, adding further unit items. - * - * @param unit original unit type - * @param items additional items to add - * @return new unit type that includes the additional items - */ - static Unit addItems(const Unit& unit, std::vector items) { - auto x = Type(unit)._clone().as(); - auto new_items = x.assignIndices(items); - - for ( auto i : new_items ) - x.childs().emplace_back(std::move(i)); - - return x; - } - - /** - * Copies an existing unit type, adding further attributes. - * - * @param unit original unit type - * @param attrs additional attributes to add - * @return new unit type that includes the additional attributes - */ - static Unit addAttributes(const Unit& unit, AttributeSet attrs) { - auto x = Type(unit)._clone().as(); - x.childs().push_back(std::move(attrs)); - return x; - } - - /** - * Copies an existing unit type, setting its accociated grammar. - * - * @param unit original unit type - * @param g the grammar - * @return new type with the grammar associated + * Given an existing node wrapping a unit type, updates the contained unit + * type to have its `self` declaration initialized. Note that the unit + * type's constructor cannot do this because we need the `Node` shell for + * this. */ - static Unit setGrammar(const Unit& unit, std::shared_ptr g) { - auto x = Type(unit)._clone().as(); - x._grammar = std::move(g); - return x; + static void setSelf(Node* n) { + assert(n->isA()); + Expression self = hilti::expression::Keyword(hilti::expression::keyword::Kind::Self, + StrongReference(hilti::type::pruneWalk(n->as())), n->meta()); + Declaration d = + hilti::declaration::Expression("self", std::move(self), declaration::Linkage::Private, n->meta()); + n->childs()[0] = std::move(d); } private: diff --git a/spicy/toolchain/include/compiler/detail/codegen/codegen.h b/spicy/toolchain/include/compiler/detail/codegen/codegen.h index d522d44b7..18619f3f5 100644 --- a/spicy/toolchain/include/compiler/detail/codegen/codegen.h +++ b/spicy/toolchain/include/compiler/detail/codegen/codegen.h @@ -32,10 +32,10 @@ class CodeGen { CodeGen(std::shared_ptr context) : _context(std::move(context)), _gb(this), _pb(this) {} /** Entry point for transformation from a Spicy AST to a HILTI AST. */ - bool compileModule(hilti::Node* root, bool init, hilti::Unit* u); + bool compileModule(hilti::Node* root, hilti::Unit* u); - const auto& context() const { return _context; } - const auto& options() const { return _context->options(); } + auto context() const { return _context.lock(); } + const auto& options() const { return context()->options(); } hilti::Type compileUnit(const type::Unit& unit, bool declare_only = true); // Compiles a Unit type into its HILTI struct representation. @@ -51,9 +51,6 @@ class CodeGen { codegen::GrammarBuilder* grammarBuilder() { return &_gb; } hilti::Unit* hiltiUnit() const; // will abort if not compiling a module. hilti::Module* hiltiModule() const; // will abort if not compiling a module. - NodeRef preserveNode(Expression x); - NodeRef preserveNode(Statement x); - NodeRef preserveNode(Type x); auto uniquer() { return &_uniquer; } const auto& moduleProperties() const { return _properties; } @@ -68,7 +65,7 @@ class CodeGen { private: - std::shared_ptr _context; + std::weak_ptr _context; codegen::GrammarBuilder _gb; codegen::ParserBuilder _pb; diff --git a/spicy/toolchain/include/compiler/detail/codegen/parser-builder.h b/spicy/toolchain/include/compiler/detail/codegen/parser-builder.h index c8db69e3e..d26de17bf 100644 --- a/spicy/toolchain/include/compiler/detail/codegen/parser-builder.h +++ b/spicy/toolchain/include/compiler/detail/codegen/parser-builder.h @@ -466,7 +466,7 @@ class ParserBuilder { void guardFeatureCode(const type::Unit& unit, std::string_view feature, std::function f); CodeGen* cg() const { return _cg; } - const std::shared_ptr& context() const; + std::shared_ptr context() const; const hilti::Options& options() const; private: diff --git a/spicy/toolchain/include/compiler/detail/coercion.h b/spicy/toolchain/include/compiler/detail/coercion.h new file mode 100644 index 000000000..c891a08a8 --- /dev/null +++ b/spicy/toolchain/include/compiler/detail/coercion.h @@ -0,0 +1,16 @@ +// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#pragma once + +#include + +#include + +namespace spicy::detail { + +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +std::optional coerceCtor(Ctor c, const Type& dst, bitmask style); +/** Implements the corresponding functionality for the Spicy compiler plugin. */ +std::optional coerceType(Type t, const Type& dst, bitmask style); + +} // namespace spicy::detail diff --git a/spicy/toolchain/include/compiler/detail/parser/driver.h b/spicy/toolchain/include/compiler/detail/parser/driver.h index b4417cd59..e3afe6ba1 100644 --- a/spicy/toolchain/include/compiler/detail/parser/driver.h +++ b/spicy/toolchain/include/compiler/detail/parser/driver.h @@ -71,16 +71,14 @@ struct yystype_spicy { std::vector function_parameters; std::vector switch_cases; - std::pair tuple_type_elem; - std::vector> tuple_type_elems; + hilti::type::tuple::Element tuple_type_elem; + std::vector tuple_type_elems; - hilti::type::struct_::Field struct_field; hilti::ctor::struct_::Field struct_elem; - std::vector struct_fields; std::vector struct_elems; - hilti::ctor::Map::Element map_elem; - std::vector map_elems; + hilti::ctor::map::Element map_elem; + std::vector map_elems; hilti::type::enum_::Label enum_label; std::vector enum_labels; diff --git a/spicy/toolchain/include/compiler/detail/visitors.h b/spicy/toolchain/include/compiler/detail/visitors.h index 9e6838895..0e8cf5fbe 100644 --- a/spicy/toolchain/include/compiler/detail/visitors.h +++ b/spicy/toolchain/include/compiler/detail/visitors.h @@ -3,52 +3,38 @@ #pragma once #include +#include #include #include -#include -#include -#include -#include +#include +#include namespace hilti { +class Node; class Unit; -} // namespace hilti -namespace hilti::printer { -class Stream; -} // namespace hilti::printer -namespace spicy { -using hilti::Result; -// namespace spicy -} // namespace spicy - -namespace spicy::detail { - -/** Implements the corresponding functionality for the Spicy compiler plugin. */ -void buildScopes(const std::vector>& modules, hilti::Unit* unit); -/** Implements the corresponding functionality for the Spicy compiler plugin. */ -bool resolveIDs(hilti::Node* root, hilti::Unit* unit); +namespace printer { +class Stream; +} -/** Implements the corresponding functionality for the Spicy compiler plugin. */ -bool applyCoercions(hilti::Node* root, hilti::Unit* unit); +} // namespace hilti -/** Implements the corresponding functionality for the Spicy compiler plugin. */ -void preTransformValidateAST(hilti::Node* root, hilti::Unit* unit, bool* found_errors); +namespace spicy::detail::ast { /** Implements the corresponding functionality for the Spicy compiler plugin. */ -void postTransformValidateAST(hilti::Node* root, hilti::Unit* unit); +void buildScopes(const std::shared_ptr& ctx, hilti::Node* root, hilti::Unit* unit); /** Implements the corresponding functionality for the Spicy compiler plugin. */ -void preservedValidateAST(std::vector* nodes, hilti::Unit* unit); +bool normalize(const std::shared_ptr& ctx, hilti::Node* root, hilti::Unit* unit); /** Implements the corresponding functionality for the Spicy compiler plugin. */ -bool printAST(const hilti::Node& root, hilti::printer::Stream& out); // NOLINT +bool print(const hilti::Node& root, hilti::printer::Stream& out); /** Implements the corresponding functionality for the Spicy compiler plugin. */ -std::optional coerceCtor(hilti::Ctor c, const hilti::Type& dst, bitmask style); +bool resolve(const std::shared_ptr& ctx, hilti::Node* root, hilti::Unit* unit); /** Implements the corresponding functionality for the Spicy compiler plugin. */ -std::optional coerceType(hilti::Type t, const hilti::Type& dst, bitmask style); +void validate(const std::shared_ptr& ctx, hilti::Node* root, hilti::Unit* unit); -} // namespace spicy::detail +} // namespace spicy::detail::ast diff --git a/spicy/toolchain/src/ast/hook.cc b/spicy/toolchain/src/ast/hook.cc new file mode 100644 index 000000000..620a7e048 --- /dev/null +++ b/spicy/toolchain/src/ast/hook.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#include +#include +#include + +using namespace spicy; + +hilti::optional_ref Hook::unitType() const { + if ( _unit_type ) + return _unit_type->as(); + else + return {}; +} + +hilti::optional_ref Hook::unitField() const { + if ( _unit_field ) + return _unit_field->as(); + else + return {}; +} + +std::optional Hook::priority() const { + if ( auto p = AttributeSet::find(function().attributes(), "priority") ) + return *p->valueAsExpression(); + + return {}; +} + +NodeRef Hook::ddRef() const { + if ( childs()[1].isA() ) + return NodeRef(childs()[1]); + else + return {}; +} diff --git a/spicy/toolchain/src/ast/types/bitfield.cc b/spicy/toolchain/src/ast/types/bitfield.cc index a1487961a..d2cf8eb37 100644 --- a/spicy/toolchain/src/ast/types/bitfield.cc +++ b/spicy/toolchain/src/ast/types/bitfield.cc @@ -1,41 +1,24 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include #include #include using namespace spicy; -Type type::bitfield::Bits::type() const { - if ( auto a = AttributeSet::find(attributes(), "&convert") ) - return hilti::type::Computed(*a->valueAs(), meta()); - - return hilti::type::UnsignedInteger(_field_width); -} - -Type type::Bitfield::type() const { - std::vector> elems; - - for ( const auto& b : bits() ) - elems.emplace_back(b.id(), b.type()); - - return type::Tuple(std::move(elems), meta()); -} - -std::optional type::Bitfield::bitsIndex(const ID& id) const { - for ( const auto&& [i, b] : hilti::util::enumerate(bits()) ) { +hilti::optional_ref type::Bitfield::bits(const ID& id) const { + for ( const auto& b : bits() ) { if ( id == b.id() ) - return i; + return b; } return {}; } -std::optional type::Bitfield::bits(const ID& id) const { - for ( const auto& b : bits() ) { +std::optional type::Bitfield::bitsIndex(const ID& id) const { + for ( const auto&& [i, b] : hilti::util::enumerate(bits()) ) { if ( id == b.id() ) - return b; + return i; } return {}; diff --git a/spicy/toolchain/src/ast/types/unit-items/field.cc b/spicy/toolchain/src/ast/types/unit-items/field.cc index 70f7476b7..a331f0e8e 100644 --- a/spicy/toolchain/src/ast/types/unit-items/field.cc +++ b/spicy/toolchain/src/ast/types/unit-items/field.cc @@ -1,147 +1,31 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "spicy/ast/types/unit-items/field.h" +#include #include -#include #include #include #include #include +#include using namespace spicy; using namespace spicy::detail; -namespace { - -struct Visitor : public hilti::visitor::PreOrder { - explicit Visitor(bool want_parse_type) : want_parse_type(want_parse_type) {} - - bool want_parse_type; - - result_t operator()(const type::Bitfield& t) { return want_parse_type ? t : t.type(); } - result_t operator()(const hilti::type::RegExp& /* t */) { return hilti::type::Bytes(); } -}; - -} // namespace - -static Type _adaptType(Type t, bool want_parse_type) { - if ( auto e = Visitor(want_parse_type).dispatch(t) ) - return std::move(*e); - - return t; -} - -static Type _itemType(Type type, bool want_parse_type, bool is_container, Meta meta) { - type = _adaptType(std::move(type), want_parse_type); - - if ( is_container ) - return type::Vector(std::move(type), meta); - else - return type; -} - -Type spicy::type::unit::item::Field::parseType() const { - return _itemType(originalType(), true, isContainer(), meta()); -} - -Type spicy::type::unit::item::Field::itemType() const { - if ( auto convert = convertExpression() ) { - // Helper to adapt the type of a &convert expression further. - const auto adapt_type = [](const Type& t) -> Type { - if ( t.isA() ) - return t; - - // If there's list comprehension, morph the type into a vector. - // Assignment will transparently work. - if ( auto x = t.tryAs() ) - return hilti::type::Vector(x->elementType(), x->meta()); - - return t; - }; - - if ( convert->second ) { - // For a unit-level &convert, we derive the type of the expression - // from the __convert() function that the code generator creates to - // evaluate it. That function has an "auto" return type that will - // eventually be resolved to the correct type. (Note there's no - // __convert function for field-level attribute.) - // - // This approach is bit messy, but ensures that "self" inside the - // expression gets resolved correctly independent of where the - // itemType() that this method returns will be used. (Which - // otherwise could be a problem because of our current way of - // dynamically computing types. This will become more - // straight-forward once we clean up type resolving.) - auto& unit = convert->second->as(); - auto t = hilti::builder::typeByID(ID(*unit.typeID())); - return type::Computed(t, [adapt_type](Node& n) -> Type { - auto t = n.as(); - - if ( auto x = t.tryAs() ) - t = x->dereferencedType(); - - auto st = t.tryAs(); - if ( ! st ) - return type::unknown; - - auto field = st->field("__convert"); - assert(field); - - auto f = field->type().as(); - return adapt_type(f.result().type()); - }); - } - else { - return type::Computed(convert->first, [&adapt_type](Node& n) -> Type { - auto t = type::effectiveType(n.as().type()); - return adapt_type(t); - }); - } - } - - if ( const auto& i = item(); i && i->isA() ) - return _itemType(i->itemType(), false, isContainer(), meta()); - else - return _itemType(originalType(), false, isContainer(), meta()); -} - -Type spicy::type::unit::item::Field::vectorElementTypeThroughSelf(ID id) { - return hilti::type::Computed(hilti::builder::id("self"), [id](Node& n) { - Type t = n.as().type(); - - if ( auto x = t.tryAs() ) - t = x->dereferencedType(); - - if ( auto x = t.tryAs() ) - return x->field(id)->auxType()->as().elementType(); - - return hilti::type::unknown; - }); -} - -std::optional>> spicy::type::unit::item::Field::convertExpression() const { +std::optional>> spicy::type::unit::item::Field:: + convertExpression() const { if ( auto convert = AttributeSet::find(attributes(), "&convert") ) - return std::make_pair(*convert->valueAs(), std::nullopt); + return std::make_pair((*convert->valueAsExpression()).get(), std::nullopt); - auto t = parseType(); + Type t = parseType(); if ( auto x = t.tryAs() ) t = x->dereferencedType(); if ( auto x = t.tryAs() ) { if ( auto convert = AttributeSet::find(x->attributes(), "&convert") ) - return std::make_pair(*convert->valueAs(), x); - } - - // The original unit type may have been replaced with the generated struct - // already. - if ( auto x = t.tryAs(); x && x->originalNode() ) { - if ( auto y = x->originalNode()->tryAs() ) { - if ( auto convert = AttributeSet::find(y->attributes(), "&convert") ) - return std::make_pair(*convert->valueAs(), y); - } + return std::make_pair(*convert->valueAsExpression(), std::move(t)); } return {}; diff --git a/spicy/toolchain/src/ast/types/unit-items/switch.cc b/spicy/toolchain/src/ast/types/unit-items/switch.cc index 373ae1066..32ae9bc8f 100644 --- a/spicy/toolchain/src/ast/types/unit-items/switch.cc +++ b/spicy/toolchain/src/ast/types/unit-items/switch.cc @@ -3,6 +3,7 @@ #include #include +#include #include using namespace spicy; @@ -19,7 +20,7 @@ bool spicy::type::unit::item::Switch::hasNoFields() const { return true; } -std::optional spicy::type::unit::item::Switch::case_( +hilti::optional_ref spicy::type::unit::item::Switch::case_( const type::unit::item::Field& x) { for ( const auto& c : cases() ) { for ( const auto& f : c.items() ) { @@ -30,3 +31,7 @@ std::optional spicy::type::unit::item::S return {}; } + +bool spicy::type::unit::item::switch_::Case::operator==(const Case& other) const { + return expressions() == other.expressions() && items() == other.items(); +} diff --git a/spicy/toolchain/src/ast/types/unit.cc b/spicy/toolchain/src/ast/types/unit.cc index dcfe91bbd..d0b5f3118 100644 --- a/spicy/toolchain/src/ast/types/unit.cc +++ b/spicy/toolchain/src/ast/types/unit.cc @@ -1,17 +1,47 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include "spicy/ast/types/unit.h" - #include +#include #include #include using namespace spicy; -std::optional type::Unit::field(const ID& id) const { - for ( const auto& f : hilti::node::flattenedChilds(*this) ) { - if ( f.id() == id ) - return f; +static NodeRef _itemByName(const Node& i, const ID& id) { + if ( auto x = i.tryAs(); x && x->id() == id ) + return NodeRef(i); + + if ( auto x = i.tryAs(); x && x->id() == id ) + return NodeRef(i); + + if ( auto x = i.tryAs(); x && x->id() == id ) + return NodeRef(i); + + if ( auto x = i.tryAs() ) { + for ( const auto& c : x->cases() ) { + for ( const auto& si : c.itemRefs() ) { + if ( auto x = _itemByName(*si, id) ) + return x; + } + } + } + + return {}; +} + +hilti::optional_ref type::Unit::itemByName(const ID& id) const { + for ( const auto& i : itemRefs() ) { + if ( auto x = _itemByName(i, id) ) + return x->as(); + } + + return {}; +} + +NodeRef type::Unit::itemRefByName(const ID& id) const { + for ( const auto& i : itemRefs() ) { + if ( auto x = _itemByName(*i, id) ) + return x; } return {}; @@ -21,11 +51,11 @@ struct AssignFieldIndicesVisitor : public hilti::visitor::PreOrder().setIndex(next_index++); } result_t operator()(const type::unit::item::UnresolvedField& n, position_t p) { - p.node = type::unit::Item(type::unit::item::UnresolvedField::setIndex(n, next_index++)); + p.node.as().setIndex(next_index++); } uint64_t next_index; diff --git a/spicy/toolchain/src/compiler/codegen/codegen.cc b/spicy/toolchain/src/compiler/codegen/codegen.cc index 063daa6c7..d645344a7 100644 --- a/spicy/toolchain/src/compiler/codegen/codegen.cc +++ b/spicy/toolchain/src/compiler/codegen/codegen.cc @@ -3,20 +3,24 @@ #include #include +#include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -24,6 +28,7 @@ #include #include #include +#include using namespace spicy; using namespace spicy::detail; @@ -36,83 +41,56 @@ namespace builder = hilti::builder; namespace { // Visitor that runs only once the 1st time AST transformation is triggered. -struct VisitorPassInit : public hilti::visitor::PreOrder { - VisitorPassInit(CodeGen* cg, hilti::Module* module) : cg(cg), module(module) {} +struct VisitorPass1 : public hilti::visitor::PreOrder { + VisitorPass1(CodeGen* cg, hilti::Module* module) : cg(cg), module(module) {} CodeGen* cg; hilti::Module* module; ID module_id = ID(""); bool modified = false; - std::vector> new_nodes; - template void replaceNode(position_t* p, T&& n) { - auto x = p->node; p->node = std::forward(n); - p->node.setOriginalNode(module->preserve(x)); modified = true; } - void finalize() { - if ( new_nodes.empty() ) - return; - - for ( auto& n : new_nodes ) - *n.first = std::move(n.second); + void replaceNode(position_t* p, Node&& n) { + if ( p->node.location() && ! n.location() ) { + auto m = n.meta(); + m.setLocation(p->node.location()); + n.setMeta(std::move(m)); + } - new_nodes.clear(); + p->node = std::move(n); modified = true; } - void operator()(const hilti::declaration::Property& m) { cg->recordModuleProperty(m); } - - void operator()(const hilti::Module& m) { - module_id = m.id(); - cg->addDeclaration(builder::import("hilti", m.meta())); - cg->addDeclaration(builder::import("spicy_rt", m.meta())); - } - void operator()(const hilti::declaration::Type& t, position_t p) { + // Replace unit type with compiled struct type. auto u = t.type().tryAs(); - if ( ! u ) return; - auto nu = type::setTypeID(*u, ID(module_id, t.id())).as(); - - if ( t.linkage() == declaration::Linkage::Public && ! nu.isPublic() ) - nu = type::Unit::setPublic(nu, true); - - // Create unit property items from global module items where the unit - // does not provide an overriding one. - std::vector ni; - for ( auto& p : cg->moduleProperties() ) { - if ( ! u->propertyItem(p.id()) ) - ni.emplace_back(type::unit::item::Property(p.id(), *p.expression(), {}, true, p.meta())); - } - - if ( ni.size() ) - nu = type::Unit::addItems(nu, ni); - - auto nt = hilti::declaration::Type::setType(t, nu); - replaceNode(&p, nt); - // Build the unit's grammar. - if ( auto r = cg->grammarBuilder()->run(nu, &p.node, cg); ! r ) { + if ( auto r = cg->grammarBuilder()->run(*u, &p.node, cg); ! r ) { hilti::logger().error(r.error().description(), p.node.location()); return; } - auto ns = cg->compileUnit(nu, false); + // Make sure references to the Spicy type remain valid after replacing + // the declaration. + module->preserve(p.node); + + auto ns = cg->compileUnit(*u, false); auto attrs = AttributeSet({Attribute("&on-heap")}); - auto nd = hilti::declaration::Type(nt.id(), ns, attrs, nt.linkage(), nt.meta()); + auto nd = hilti::declaration::Type(t.id(), ns, attrs, t.linkage(), t.meta()); replaceNode(&p, nd); } }; -// Visitor that runs multiple times whenever a transformation pass is triggered. -struct VisitorPassIterate : public hilti::visitor::PreOrder { - VisitorPassIterate(CodeGen* cg, hilti::Module* module) : cg(cg), module(module) {} +// Visitor that runs repeatedly over the AST until no further changes. +struct VisitorPass2 : public hilti::visitor::PreOrder { + VisitorPass2(CodeGen* cg, hilti::Module* module) : cg(cg), module(module) {} CodeGen* cg; hilti::Module* module; ID module_id = ID(""); @@ -120,9 +98,18 @@ struct VisitorPassIterate : public hilti::visitor::PreOrder void replaceNode(position_t* p, T&& n) { - auto x = p->node; p->node = std::forward(n); - p->node.setOriginalNode(module->preserve(x)); + modified = true; + } + + void replaceNode(position_t* p, Node&& n) { + if ( p->node.location() && ! n.location() ) { + auto m = n.meta(); + m.setLocation(p->node.location()); + n.setMeta(std::move(m)); + } + + p->node = std::move(n); modified = true; } @@ -143,42 +130,70 @@ struct VisitorPassIterate : public hilti::visitor::PreOrdernode; - - if ( x.location() && ! n.location() ) { - auto m = n.meta(); - m.setLocation(x.location()); - n.setMeta(std::move(m)); - } - - p->node = std::move(n); - p->node.setOriginalNode(module->preserve(x)); - modified = true; - } + void operator()(const hilti::declaration::Property& m) { cg->recordModuleProperty(m); } void operator()(const declaration::UnitHook& n, position_t p) { - const auto& unit_type = n.unitType(); - const auto& hook = n.unitHook().hook(); - - if ( ! unit_type ) - // Not resolved yet. - return; + const auto& hook = n.hook(); + auto unit_type = hook.unitType(); + assert(unit_type); - auto func = cg->compileHook(*unit_type, ID(*unit_type->typeID(), n.unitHook().id()), {}, hook.isForEach(), - hook.isDebug(), hook.type().parameters(), hook.body(), hook.priority(), n.meta()); + auto func = cg->compileHook(*unit_type, n.hook().id(), {}, hook.isForEach(), hook.isDebug(), + hook.ftype().parameters().copy(), hook.body(), hook.priority(), n.meta()); replaceNode(&p, std::move(func)); } + void operator()(const hilti::expression::ResolvedID& n, position_t p) { + // Re-resolve IDs (except function calls). + if ( ! p.parent().isA() ) + replaceNode(&p, hilti::expression::UnresolvedID(n.id(), p.node.meta())); + } + + /* + * void operator()(const hilti::expression::ResolvedOperator& n, position_t p) { + * // Re-resolve operators. + * replaceNode(&p, hilti::expression::UnresolvedOperator(n.operator_().kind(), n.operands(), p.node.meta())); + * } + */ result_t operator()(const operator_::bitfield::Member& n, position_t p) { - auto id = n.op1().as().id(); + 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(); + replaceNode(&p, builder::unset(n.op0(), id, n.meta())); + } + + result_t operator()(const operator_::unit::MemberConst& n, position_t p) { + const auto& id = n.op1().as().id(); + replaceNode(&p, builder::member(n.op0(), id, n.meta())); + } + + result_t operator()(const operator_::unit::MemberNonConst& n, position_t p) { + const auto& id = n.op1().as().id(); + replaceNode(&p, builder::member(n.op0(), id, n.meta())); + } + + result_t operator()(const operator_::unit::TryMember& n, position_t p) { + const auto& id = n.op1().as().id(); + replaceNode(&p, builder::tryMember(n.op0(), id, n.meta())); + } + + result_t operator()(const operator_::unit::HasMember& n, position_t p) { + const auto& id = n.op1().as().id(); + replaceNode(&p, builder::hasMember(n.op0(), id, n.meta())); + } + + result_t operator()(const operator_::unit::MemberCall& n, position_t p) { + const auto& id = n.op1().as().id(); + const auto& args = n.op2().as().ctor().as(); + replaceNode(&p, builder::memberCall(n.op0(), id, args, n.meta())); + } + result_t operator()(const operator_::unit::Offset& n, position_t p) { auto begin = builder::deref(builder::member(n.op0(), ID("__begin"))); auto cur = builder::deref(builder::member(n.op0(), ID("__position"))); @@ -190,7 +205,7 @@ struct VisitorPassIterate : public hilti::visitor::PreOrder(); + ! t && ! p.parent(2).tryAs() ) { + assert(n.id()); + replaceNode(&p, hilti::type::UnresolvedID(*n.id(), p.node.meta())); + } + } }; } // anonymous namespace -bool CodeGen::compileModule(hilti::Node* root, bool init, hilti::Unit* u) { +bool CodeGen::compileModule(hilti::Node* root, hilti::Unit* u) { hilti::util::timing::Collector _("spicy/compiler/codegen"); _hilti_unit = u; _root = root; - bool modified = false; - - if ( init ) { - auto v = VisitorPassInit(this, &root->as()); - for ( auto i : v.walk(root) ) - v.dispatch(i); + auto v1 = VisitorPass1(this, &root->as()); + for ( auto i : v1.walk(root) ) + v1.dispatch(i); - v.finalize(); + bool v2_modified = false; - modified = (modified || v.modified); + while ( true ) { + auto v2 = VisitorPass2(this, &root->as()); + for ( auto i : v2.walk(root) ) + v2.dispatch(i); - if ( hilti::logger().errors() ) - goto done; + v2_modified = v2_modified || v2.modified; - if ( _new_decls.size() ) { - for ( const auto& n : _new_decls ) - hiltiModule()->add(n); + if ( ! hilti::logger().errors() ) { + if ( _new_decls.size() ) { + for ( const auto& n : _new_decls ) + hiltiModule()->add(n); - _new_decls.clear(); - modified = true; + _new_decls.clear(); + continue; // modified, next round + } } - } - - { - auto v = VisitorPassIterate(this, &root->as()); - for ( auto i : v.walk(root) ) - v.dispatch(i); - modified = (modified || v.modified); + if ( ! v2.modified ) + break; } -done: + u->setExtension(".hlt"); + _hilti_unit = nullptr; _root = nullptr; - return modified; + return v1.modified || v2_modified; } std::optional CodeGen::compileHook( @@ -430,24 +441,27 @@ std::optional CodeGen::compileHook( } else { // Try to locate field by ID. - if ( auto i = unit.field(id.local()) ) { - auto f = i->as(); - if ( ! f.parseType().isA() ) { - is_container = f.isContainer(); - original_field_type = f.originalType(); + if ( auto i = unit.itemByName(id.local()) ) { + if ( auto f = i->tryAs() ) { + if ( ! f->parseType().isA() ) { + is_container = f->isContainer(); + field = *f; + original_field_type = f->originalType(); + } } } } if ( foreach ) { - params.push_back({ID("__dd"), hilti::type::Auto(), hilti::type::function::parameter::Kind::In, {}, {}}); + params.push_back( + {ID("__dd"), field->get().ddType().elementType(), hilti::type::function::parameter::Kind::In, {}, {}}); params.push_back({ID("__stop"), type::Bool(), hilti::type::function::parameter::Kind::InOut, {}, {}}); } else if ( original_field_type ) { - params.push_back({ID("__dd"), hilti::type::Auto(), hilti::type::function::parameter::Kind::In, {}, {}}); + params.push_back({ID("__dd"), field->get().itemType(), hilti::type::function::parameter::Kind::In, {}, {}}); // Pass on captures for fields of type regexp, which are the only - // ones they have it (for vector of regexps, it wouldn't be clear what + // ones that have it (for vector of regexps, it wouldn't be clear what // to bind to). if ( original_field_type->isA() && ! is_container ) params.push_back({ID("__captures"), @@ -466,7 +480,7 @@ std::optional CodeGen::compileHook( hid = "__str__"; } else { - result = hilti::type::Void(); + result = hilti::type::void_; hid = fmt("__on_%s%s", id.local(), (foreach ? "_foreach" : "")); } @@ -499,9 +513,3 @@ hilti::Unit* CodeGen::hiltiUnit() const { return _hilti_unit; } - -NodeRef CodeGen::preserveNode(Expression x) { return hiltiModule()->preserve(std::move(x)); } - -NodeRef CodeGen::preserveNode(Statement x) { return hiltiModule()->preserve(std::move(x)); } - -NodeRef CodeGen::preserveNode(Type x) { return hiltiModule()->preserve(std::move(x)); } diff --git a/spicy/toolchain/src/compiler/codegen/grammar-builder.cc b/spicy/toolchain/src/compiler/codegen/grammar-builder.cc index 51faa639a..44bb9fe74 100644 --- a/spicy/toolchain/src/compiler/codegen/grammar-builder.cc +++ b/spicy/toolchain/src/compiler/codegen/grammar-builder.cc @@ -24,21 +24,21 @@ struct Visitor : public hilti::visitor::PreOrder { codegen::GrammarBuilder* gb; Grammar* grammar; - using CurrentField = std::pair&>; + using CurrentField = std::pair; std::vector fields; hilti::util::Cache cache; - auto currentField() { return fields.back(); } + const auto& currentField() { return fields.back(); } void pushField(CurrentField f) { fields.emplace_back(f); } void popField() { fields.pop_back(); } bool haveField() { return ! fields.empty(); } - std::optional productionForItem(std::reference_wrapper node) { - auto field = node.get().tryAs(); + std::optional productionForItem(NodeRef item) { + auto field = item->tryAs(); if ( field ) - pushField({*field, node}); + pushField({*field, NodeRef(item)}); - auto p = dispatch(node); + auto p = dispatch(item); if ( field ) popField(); @@ -51,7 +51,7 @@ struct Visitor : public hilti::visitor::PreOrder { } Production productionForType(const Type& t, const ID& id) { - if ( auto prod = dispatch(hilti::type::effectiveType(t)) ) + if ( auto prod = dispatch(t) ) return std::move(*prod); // Fallback: Just a plain type. @@ -60,7 +60,7 @@ struct Visitor : public hilti::visitor::PreOrder { Production productionForLoop(Production sub, position_t p) { const auto& loc = p.node.location(); - auto& field = currentField().first; + const auto& field = currentField().first; auto id = cg->uniquer()->get(field.id()); auto eod = AttributeSet::find(field.attributes(), "&eod"); auto count = AttributeSet::find(field.attributes(), "&count"); @@ -84,7 +84,7 @@ struct Visitor : public hilti::visitor::PreOrder { return production::Counter(id, *repeat, sub, loc); if ( count ) - return production::Counter(id, *count->valueAs(), sub, loc); + return production::Counter(id, *count->valueAsExpression(), sub, loc); if ( size ) // When parsing, our view will be limited to the specified input @@ -113,14 +113,14 @@ struct Visitor : public hilti::visitor::PreOrder { Production operator()(const spicy::type::unit::item::Field& n, position_t p) { Production prod; - if ( auto c = n.ctor() ) { + if ( const auto& c = n.ctor() ) { prod = productionForCtor(*c, n.id()); if ( n.isContainer() ) prod = productionForLoop(prod, p); } else if ( n.item() ) { - auto sub = productionForItem(p.node.as().itemNode()); + auto sub = productionForItem(p.node.as().itemRef()); auto m = sub->meta(); if ( n.isContainer() ) @@ -128,7 +128,7 @@ struct Visitor : public hilti::visitor::PreOrder { else { if ( sub->meta().field() ) { auto field = sub->meta().fieldRef(); - *field = type::unit::item::Field::setForwarding(field->as(), true); + const_cast(field->as()).setForwarding(true); } prod = production::Enclosure(cg->uniquer()->get(n.id()), *sub); @@ -145,11 +145,11 @@ struct Visitor : public hilti::visitor::PreOrder { } Production operator()(const spicy::type::unit::item::Switch& n, position_t p) { - auto productionForCase = [this](Node& c, const std::string& label) { + auto productionForCase = [this](const spicy::type::unit::item::switch_::Case& c, const std::string& label) { std::vector prods; - for ( auto&& n : c.as().itemNodes() ) { - if ( auto prod = productionForItem(n) ) + for ( const auto& n : c.itemRefs() ) { + if ( auto prod = productionForItem(NodeRef(n)) ) prods.push_back(*prod); } @@ -164,14 +164,12 @@ struct Visitor : public hilti::visitor::PreOrder { std::optional default_; int i = 0; - for ( auto&& n : p.node.as().casesNodes() ) { - auto c = n.get().as(); - + for ( const auto& c : p.node.as().cases() ) { if ( c.isDefault() ) - default_ = productionForCase(n, fmt("%s_default", switch_sym)); + default_ = productionForCase(c, fmt("%s_default", switch_sym)); else { - auto prod = productionForCase(n, fmt("%s_case_%d", switch_sym, ++i)); - cases.emplace_back(c.expressions(), std::move(prod)); + auto prod = productionForCase(c, fmt("%s_case_%d", switch_sym, ++i)); + cases.emplace_back(c.expressions().copy(), std::move(prod)); } } @@ -190,15 +188,13 @@ struct Visitor : public hilti::visitor::PreOrder { int i = 0; auto d = production::look_ahead::Default::None; - for ( auto&& n : p.node.as().casesNodes() ) { - auto c = n.get().as(); - + for ( const auto& c : p.node.as().cases() ) { Production prod; if ( c.isDefault() ) - prod = productionForCase(n, fmt("%s_default", switch_sym)); + prod = productionForCase(c, fmt("%s_default", switch_sym)); else - prod = productionForCase(n, fmt("%s_case_%d", switch_sym, ++i)); + prod = productionForCase(c, fmt("%s_case_%d", switch_sym, ++i)); if ( ! prev ) { prev = prod; @@ -225,23 +221,23 @@ struct Visitor : public hilti::visitor::PreOrder { Production operator()(const type::Unit& n, position_t p) { auto prod = cache.getOrCreate( - *n.typeID(), []() { return production::Unresolved(); }, + *n.id(), []() { return production::Unresolved(); }, [&](auto& unresolved) { - auto id = cg->uniquer()->get(*n.typeID()); + auto id = cg->uniquer()->get(*n.id()); std::vector items; - for ( auto n : p.node.as().nodesOfType() ) { - if ( auto p = productionForItem(n) ) + for ( const auto& n : p.node.as().childRefsOfType() ) { + if ( auto p = productionForItem(NodeRef(n)) ) items.push_back(*p); } - std::vector args; + hilti::node::Range args; if ( haveField() ) args = currentField().first.arguments(); - auto unit = production::Unit(id, n, std::move(args), std::move(items), n.meta().location()); + auto unit = production::Unit(id, n, args.copy(), std::move(items), n.meta().location()); grammar->resolve(&unresolved.template as(), std::move(unit)); return unresolved; }); @@ -253,20 +249,6 @@ struct Visitor : public hilti::visitor::PreOrder { return prod; } - Production operator()(const type::ResolvedID& n) { - auto t = (*n.ref()).as().type(); - auto x = dispatch(t); - assert(x); - return *x; - } - - Production operator()(const type::Struct& n, position_t /* p */) { - // Must be a unit that's already been converted. - assert(n.originalNode()); - auto x = dispatch(*n.originalNode()); - return *x; - } - Production operator()(const type::ValueReference& n, position_t /* p */) { // Forward to referenced type, which will usually be a unit. auto x = dispatch(n.dereferencedType()); @@ -283,8 +265,8 @@ struct Visitor : public hilti::visitor::PreOrder { } // anonymous namespace Result GrammarBuilder::run(const type::Unit& unit, Node* node, CodeGen* cg) { - assert(unit.typeID()); - auto id = *unit.typeID(); + assert(unit.id()); + const auto& id = *unit.id(); Grammar g(id, node->location()); auto v = Visitor(cg, this, &g); @@ -306,9 +288,9 @@ Result GrammarBuilder::run(const type::Unit& unit, Node* node, CodeGen* } const Grammar& GrammarBuilder::grammar(const type::Unit& unit) { - if ( _grammars.find(*unit.typeID()) == _grammars.end() ) - hilti::logger().internalError(fmt("grammar for unit %s accessed before it's been computed", *unit.typeID()), + if ( _grammars.find(*unit.id()) == _grammars.end() ) + hilti::logger().internalError(fmt("grammar for unit %s accessed before it's been computed", *unit.id()), unit.meta().location()); - return _grammars[*unit.typeID()]; + return _grammars[*unit.id()]; } diff --git a/spicy/toolchain/src/compiler/codegen/parser-builder.cc b/spicy/toolchain/src/compiler/codegen/parser-builder.cc index 094ff134a..865566e46 100644 --- a/spicy/toolchain/src/compiler/codegen/parser-builder.cc +++ b/spicy/toolchain/src/compiler/codegen/parser-builder.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -50,7 +51,7 @@ const hilti::Expression look_ahead::Eod = builder::integer(-1); // TODO(cpp ParserState::ParserState(const type::Unit& unit, const Grammar& grammar, Expression data, Expression cur) : unit(std::cref(unit)), - unit_id(*unit.typeID()), + unit_id(*unit.id()), needs_look_ahead(grammar.needsLookAhead()), self(hilti::expression::UnresolvedID(ID("self"))), data(std::move(data)), @@ -102,7 +103,7 @@ struct ProductionVisitor ParserBuilder* pb; const Grammar& grammar; hilti::util::Cache parse_functions; - std::vector new_fields; + std::vector new_fields; std::vector _destinations; void beginProduction(const Production& p) { @@ -141,8 +142,9 @@ struct ProductionVisitor std::optional addl_param; - if ( ! unit ) // for units, "self" is the destination - addl_param = builder::parameter("__dst", type::Auto(), declaration::parameter::Kind::InOut); + if ( ! unit && p.meta().field() ) // for units, "self" is the destination + addl_param = + builder::parameter("__dst", p.meta().field()->parseType(), declaration::parameter::Kind::InOut); // In the following, we structure the parsing into two // stages. Depending on whether the unit may have @@ -206,11 +208,11 @@ struct ProductionVisitor if ( field && field->id() ) msg = field->id(); - if ( type && type->typeID() ) { + if ( type && unit->id() ) { if ( msg.empty() ) - msg = *type->typeID(); + msg = *unit->id(); else - msg = fmt("%s: %s", msg, *type->typeID()); + msg = fmt("%s: %s", msg, *unit->id()); } builder()->addDebugMsg("spicy", msg); @@ -233,7 +235,11 @@ struct ProductionVisitor pstate.lahead = builder::id("__lah"); pstate.lahead_end = builder::id("__lahe"); - auto result_type = type::Tuple({type::stream::View(), look_ahead::Type, type::stream::Iterator()}); + // Note: Originally, we had the init expression (`{...}`) + // inside the tuple ctor, but that triggered ASAN to report + // a memory leak. + std::vector x = {type::stream::View(), look_ahead::Type, type::stream::Iterator()}; + auto result_type = type::Tuple(std::move(x)); auto store_result = builder()->addTmp("result", result_type); auto try_ = begin_try(); @@ -302,7 +308,7 @@ struct ProductionVisitor // Second stage parse functionality implementing the main // part of the unit's parsing. auto build_parse_stage2_logic = [&]() { - if ( ! unit ) + if ( ! unit && p.meta().field() ) pushDestination(builder::id("__dst")); else pushDestination(builder::id("self")); @@ -340,7 +346,11 @@ struct ProductionVisitor pushState(std::move(pstate)); pushBuilder(); - auto result_type = type::Tuple({type::stream::View(), look_ahead::Type, type::stream::Iterator()}); + // Note: Originally, we had the init expression (`{...}`) + // inside the tuple ctor, but that triggered ASAN to report + // a memory leak. + std::vector x = {type::stream::View(), look_ahead::Type, type::stream::Iterator()}; + auto result_type = type::Tuple(std::move(x)); auto store_result = builder()->addTmp("result", result_type); auto try_ = begin_try(join_stages); @@ -384,7 +394,7 @@ struct ProductionVisitor state().data, state().cur, state().trim, state().lahead, state().lahead_end, }; - if ( ! unit ) + if ( ! unit && p.meta().field() ) args.push_back(destination()); auto call = builder::memberCall(state().self, id, args); @@ -413,7 +423,7 @@ struct ProductionVisitor // Push destination for parsed value onto stack. if ( auto c = meta.container() ) { - auto etype = type::unit::item::Field::vectorElementTypeThroughSelf(c->id()); + auto etype = c->parseType().elementType(); auto container_element = builder()->addTmp("elem", etype); pushDestination(container_element); } @@ -462,14 +472,14 @@ struct ProductionVisitor hilti::logger().internalError( fmt("ParserBuilder: atomic production %s not handled (%s)", p.typename_(), p)); } - else if ( auto unit = p.tryAs(); unit && *unit->unitType().typeID() != state().unit_id ) { + else if ( auto unit = p.tryAs(); unit && *unit->unitType().id() != state().unit_id ) { // Parsing a different unit type. We call the other unit's parse // function, but don't have to create it here. std::vector args = {pb->state().data, pb->state().cur, pb->state().trim, pb->state().lahead, pb->state().lahead_end}; Location location; - std::vector type_args; + hilti::node::Range type_args; if ( meta.field() ) { location = meta.fieldRef()->location(); @@ -477,7 +487,7 @@ struct ProductionVisitor } Expression default_ = - builder::default_(builder::typeByID(*unit->unitType().typeID()), std::move(type_args), location); + builder::default_(builder::typeByID(*unit->unitType().id()), std::move(type_args), location); builder()->addAssign(destination(), std::move(default_)); auto call = builder::memberCall(destination(), "__parse_stage1", std::move(args)); @@ -563,10 +573,10 @@ struct ProductionVisitor } if ( auto a = AttributeSet::find(field->attributes(), "&parse-from") ) - redirectInputToBytesValue(*a->valueAs()); + redirectInputToBytesValue(*a->valueAsExpression()); if ( auto a = AttributeSet::find(field->attributes(), "&parse-at") ) - redirectInputToStreamPosition(*a->valueAs()); + redirectInputToStreamPosition(*a->valueAsExpression()); // `&size` and `&max-size` share the same underlying infrastructure // so try to extract both of them and compute the ultimate value. @@ -575,12 +585,12 @@ struct ProductionVisitor assert(! (AttributeSet::find(field->attributes(), "&size") && AttributeSet::find(field->attributes(), "&max-size"))); if ( auto a = AttributeSet::find(field->attributes(), "&size") ) - length = builder::coerceTo(*a->valueAs(), type::UnsignedInteger(64)); + length = builder::coerceTo(*a->valueAsExpression(), type::UnsignedInteger(64)); if ( auto a = AttributeSet::find(field->attributes(), "&max-size") ) // Append a sentinel byte for `&max-size` so we can detect reads beyond the expected length. length = builder()->addTmp("max_size", - builder::sum(builder::coerceTo(*a->valueAs(), type::UnsignedInteger(64)), + builder::sum(builder::coerceTo(*a->valueAsExpression(), type::UnsignedInteger(64)), builder::integer(1))); if ( length ) { @@ -669,13 +679,13 @@ struct ProductionVisitor }); } - auto dd = destination(); + auto val = destination(); if ( field->convertExpression() ) { // Value was stored in temporary. Apply expression and store result // at destination. popDestination(); - pb->applyConvertExpression(*field, dd, destination()); + pb->applyConvertExpression(*field, val, destination()); } popState(); // From &size (pushed even if absent). @@ -691,7 +701,7 @@ struct ProductionVisitor if ( ! meta.container() ) { if ( pb->isEnabledDefaultNewValueForField() && state().literal_mode == LiteralMode::Default ) - pb->newValueForField(meta, destination(), dd); + pb->newValueForField(meta, destination(), val); } if ( state().captures ) @@ -913,7 +923,7 @@ struct ProductionVisitor function::CallingConvention::Standard, attributes, m); if ( add_decl ) - new_fields.emplace_back(type::struct_::Field(id, func.function().type())); + new_fields.emplace_back(hilti::declaration::Field(id, func.function().type())); cg()->addDeclaration(func); } @@ -994,15 +1004,15 @@ struct ProductionVisitor builder()->addCall("hilti::debugIndent", {builder::string("spicy")}); if ( const auto& a = AttributeSet::find(p.attributes(), "&parse-from") ) - redirectInputToBytesValue(*a->valueAs()); + redirectInputToBytesValue(*a->valueAsExpression()); if ( auto a = AttributeSet::find(p.attributes(), "&parse-at") ) - redirectInputToStreamPosition(*a->valueAs()); + redirectInputToStreamPosition(*a->valueAsExpression()); std::optional ncur; if ( const auto& a = AttributeSet::find(p.attributes(), "&size") ) { // Limit input to the specified length. - auto length = builder::coerceTo(*a->valueAs(), type::UnsignedInteger(64)); + auto length = builder::coerceTo(*a->valueAsExpression(), type::UnsignedInteger(64)); auto limited = builder()->addTmp("limited", builder::memberCall(state().cur, "limit", {length})); // Establish limited view, remembering position to continue at. @@ -1068,12 +1078,12 @@ struct ProductionVisitor assert(! (AttributeSet::find(p.unitType().attributes(), "&size") && AttributeSet::find(p.unitType().attributes(), "&max-size"))); if ( auto a = AttributeSet::find(p.unitType().attributes(), "&size") ) - length = builder::coerceTo(*a->valueAs(), type::UnsignedInteger(64)); + length = builder::coerceTo(*a->valueAsExpression(), type::UnsignedInteger(64)); else if ( auto a = AttributeSet::find(p.unitType().attributes(), "&max-size") ) // Append a sentinel byte for `&max-size` so we can detect reads beyond the expected length. length = builder()->addTmp("max_size", - builder::sum(builder::coerceTo(*a->valueAs(), type::UnsignedInteger(64)), + builder::sum(builder::coerceTo(*a->valueAsExpression(), type::UnsignedInteger(64)), builder::integer(1))); if ( length ) { @@ -1253,9 +1263,9 @@ struct ProductionVisitor } // namespace spicy::detail::codegen static auto parseMethodIDs(const type::Unit& t) { - assert(t.typeID()); - return std::make_tuple(ID(fmt("%s::parse1", *t.typeID())), ID(fmt("%s::parse2", *t.typeID())), - ID(fmt("%s::parse3", *t.typeID())), ID(fmt("%s::context_new", *t.typeID()))); + assert(t.id()); + return std::make_tuple(ID(fmt("%s::parse1", *t.id())), ID(fmt("%s::parse2", *t.id())), + ID(fmt("%s::parse3", *t.id())), ID(fmt("%s::context_new", *t.id()))); } hilti::type::Function ParserBuilder::parseMethodFunctionType(std::optional addl_param, @@ -1277,7 +1287,7 @@ hilti::type::Function ParserBuilder::parseMethodFunctionType(std::optional& ParserBuilder::context() const { return _cg->context(); } +std::shared_ptr ParserBuilder::context() const { return _cg->context(); } const hilti::Options& ParserBuilder::options() const { return _cg->options(); } @@ -1286,6 +1296,14 @@ std::shared_ptr ParserBuilder::pushBuilder() { return _builders.back(); } +hilti::declaration::Function _setBody(const hilti::declaration::Function& d, Statement body) { + auto x = Node(d)._clone().as(); + auto f = Node(d.function())._clone().as(); + f.setBody(std::move(body)); + x.setFunction(std::move(f)); + return x; +} + hilti::type::Struct ParserBuilder::addParserMethods(hilti::type::Struct s, const type::Unit& t, bool declare_only, bool always_emit) { auto [id_ext_overload1, id_ext_overload2, id_ext_overload3, id_ext_context_new] = parseMethodIDs(t); @@ -1306,7 +1324,7 @@ hilti::type::Struct ParserBuilder::addParserMethods(hilti::type::Struct s, const auto f_ext_overload2_result = type::stream::View(); auto f_ext_overload2 = builder::function(id_ext_overload2, f_ext_overload2_result, - {builder::parameter("unit", hilti::type::UnresolvedID(*t.typeID()), + {builder::parameter("unit", hilti::type::UnresolvedID(*t.id()), declaration::parameter::Kind::InOut), builder::parameter("data", type::ValueReference(type::Stream()), declaration::parameter::Kind::InOut), @@ -1340,26 +1358,26 @@ hilti::type::Struct ParserBuilder::addParserMethods(hilti::type::Struct s, const // because doing so triggers generation of the whole parser, including // the internal parsing functions. auto sf_ext_overload1 = - type::struct_::Field(f_ext_overload1.id().local(), function::CallingConvention::Extern, - f_ext_overload1.function().type(), f_ext_overload1.function().attributes()); + hilti::declaration::Field(f_ext_overload1.id().local(), function::CallingConvention::Extern, + f_ext_overload1.function().ftype(), f_ext_overload1.function().attributes()); auto sf_ext_overload2 = - type::struct_::Field(f_ext_overload2.id().local(), function::CallingConvention::Extern, - f_ext_overload2.function().type(), f_ext_overload2.function().attributes()); + hilti::declaration::Field(f_ext_overload2.id().local(), function::CallingConvention::Extern, + f_ext_overload2.function().ftype(), f_ext_overload2.function().attributes()); auto sf_ext_overload3 = - type::struct_::Field(f_ext_overload3.id().local(), function::CallingConvention::Extern, - f_ext_overload3.function().type(), f_ext_overload3.function().attributes()); + hilti::declaration::Field(f_ext_overload3.id().local(), function::CallingConvention::Extern, + f_ext_overload3.function().ftype(), f_ext_overload3.function().attributes()); - s = hilti::type::Struct::addField(s, sf_ext_overload1); - s = hilti::type::Struct::addField(s, sf_ext_overload2); - s = hilti::type::Struct::addField(s, sf_ext_overload3); + s.addField(std::move(sf_ext_overload1)); + s.addField(std::move(sf_ext_overload2)); + s.addField(std::move(sf_ext_overload3)); if ( auto ctx = t.contextType() ) { auto sf_ext_ctor = - type::struct_::Field(f_ext_context_new.id().local(), function::CallingConvention::Extern, - f_ext_context_new.function().type(), f_ext_context_new.function().attributes()); + hilti::declaration::Field(f_ext_context_new.id().local(), function::CallingConvention::Extern, + f_ext_context_new.function().ftype(), f_ext_context_new.function().attributes()); - s = hilti::type::Struct::addField(s, sf_ext_ctor); + s.addField(sf_ext_ctor); } if ( ! declare_only ) { @@ -1379,7 +1397,7 @@ hilti::type::Struct ParserBuilder::addParserMethods(hilti::type::Struct s, const {builder::member(builder::id("unit"), "__context"), ctx, builder::typeinfo(*context)}); }; - HILTI_DEBUG(spicy::logging::debug::ParserBuilder, fmt("creating parser for %s", *t.typeID())); + HILTI_DEBUG(spicy::logging::debug::ParserBuilder, fmt("creating parser for %s", *t.id())); hilti::logging::DebugPushIndent _(spicy::logging::debug::ParserBuilder); auto grammar = cg()->grammarBuilder()->grammar(t); @@ -1391,8 +1409,8 @@ hilti::type::Struct ParserBuilder::addParserMethods(hilti::type::Struct s, const // Create parse1() body. pushBuilder(); builder()->addLocal("unit", builder::value_reference( - builder::default_(builder::typeByID(*t.typeID()), - hilti::util::transform(parameters, [](const auto& p) { + builder::default_(builder::typeByID(*t.id()), + hilti::node::transform(t.parameters(), [](const auto& p) { return *p.default_(); })))); builder()->addLocal("ncur", type::stream::View(), @@ -1417,19 +1435,19 @@ hilti::type::Struct ParserBuilder::addParserMethods(hilti::type::Struct s, const popState(); auto body_ext_overload1 = popBuilder(); - auto d_ext_overload1 = hilti::declaration::Function::setBody(f_ext_overload1, body_ext_overload1->block()); + auto d_ext_overload1 = _setBody(f_ext_overload1, body_ext_overload1->block()); cg()->addDeclaration(d_ext_overload1); // Create parse3() body. pushBuilder(); builder()->addLocal("unit", builder::value_reference( - builder::default_(builder::typeByID(*t.typeID()), - hilti::util::transform(parameters, [](const auto& p) { + builder::default_(builder::typeByID(*t.id()), + hilti::node::transform(parameters, [](const auto& p) { return *p.default_(); })))); builder()->addCall(ID("spicy_rt::initializeParsedUnit"), - {builder::id("gunit"), builder::id("unit"), builder::typeinfo(t)}); + {builder::id("gunit"), builder::id("unit"), builder::typeinfo(builder::id(*t.id()))}); builder()->addLocal("ncur", type::stream::View(), builder::ternary(builder::id("cur"), builder::deref(builder::id("cur")), builder::cast(builder::deref(builder::id("data")), @@ -1453,7 +1471,7 @@ hilti::type::Struct ParserBuilder::addParserMethods(hilti::type::Struct s, const popState(); auto body_ext_overload3 = popBuilder(); - auto d_ext_overload3 = hilti::declaration::Function::setBody(f_ext_overload3, body_ext_overload3->block()); + auto d_ext_overload3 = _setBody(f_ext_overload3, body_ext_overload3->block()); cg()->addDeclaration(d_ext_overload3); } @@ -1481,7 +1499,7 @@ hilti::type::Struct ParserBuilder::addParserMethods(hilti::type::Struct s, const auto body_ext_overload2 = popBuilder(); - auto d_ext_overload2 = hilti::declaration::Function::setBody(f_ext_overload2, body_ext_overload2->block()); + auto d_ext_overload2 = _setBody(f_ext_overload2, body_ext_overload2->block()); cg()->addDeclaration(d_ext_overload2); if ( auto ctx = t.contextType() ) { @@ -1492,13 +1510,12 @@ hilti::type::Struct ParserBuilder::addParserMethods(hilti::type::Struct s, const builder()->addReturn(builder::call("spicy_rt::createContext", {std::move(obj), std::move(ti)})); auto body_ext_context_new = popBuilder(); - auto d_ext_context_new = - hilti::declaration::Function::setBody(f_ext_context_new, body_ext_context_new->block()); + auto d_ext_context_new = _setBody(f_ext_context_new, body_ext_context_new->block()); cg()->addDeclaration(d_ext_context_new); } for ( auto f : visitor.new_fields ) - s = hilti::type::Struct::addField(s, std::move(f)); + s.addField(std::move(f)); } return s; @@ -1532,12 +1549,12 @@ void ParserBuilder::newValueForField(const production::Meta& meta, const Express // set already, and is hence accessible to the condition through // "self.". auto block = builder()->addBlock(); - block->addLocal(ID("__dd"), field->parseType(), dd); - auto cond = block->addTmp("requires", *a.valueAs()); + block->addLocal(ID("__dd"), field->ddType(), dd); + auto cond = block->addTmp("requires", *a.valueAsExpression()); pushBuilder(block->addIf(builder::not_(cond)), [&]() { parseError("&requires failed", a.value().location()); }); } - if ( ! field->parseType().isA() && ! value.type().isA() ) { + if ( ! field->originalType().isA() && ! value.type().isA() ) { builder()->addDebugMsg("spicy", fmt("%s = %%s", field->id()), {value}); builder()->addDebugMsg("spicy-verbose", fmt("- setting field '%s' to '%%s'", field->id()), {value}); } @@ -1601,7 +1618,7 @@ Expression ParserBuilder::newContainerItem(const type::unit::item::Field& field, }; if ( auto a = AttributeSet::find(field.attributes(), "&until") ) { - eval_condition(*a->valueAs()); + eval_condition(*a->valueAsExpression()); run_hook(); push_element(); } @@ -1609,11 +1626,11 @@ Expression ParserBuilder::newContainerItem(const type::unit::item::Field& field, else if ( auto a = AttributeSet::find(field.attributes(), "&until-including") ) { run_hook(); push_element(); - eval_condition(*a->valueAs()); + eval_condition(*a->valueAsExpression()); } else if ( auto a = AttributeSet::find(field.attributes(), "&while") ) { - eval_condition(builder::not_(*a->valueAs())); + eval_condition(builder::not_(*a->valueAsExpression())); run_hook(); push_element(); } @@ -1636,7 +1653,7 @@ Expression ParserBuilder::applyConvertExpression(const type::unit::item::Field& if ( ! convert->second ) { auto block = builder()->addBlock(); - block->addLocal(ID("__dd"), field.parseType(), value); + block->addLocal(ID("__dd"), field.ddType(), value); block->addAssign(*dst, convert->first); } else @@ -1684,8 +1701,9 @@ void ParserBuilder::finalizeUnit(bool success, const Location& l) { // so that (1) that one can rely on the condition, and (2) we keep // running either "%done" or "%error". for ( auto attr : AttributeSet::findAll(unit.attributes(), "&requires") ) { - auto cond = *attr.valueAs(); - pushBuilder(builder()->addIf(builder::not_(cond)), [&]() { parseError("&requires failed", cond.meta()); }); + auto cond = *attr.valueAsExpression(); + pushBuilder(builder()->addIf(builder::not_(cond)), + [&]() { parseError("&requires failed", cond.get().meta()); }); } } @@ -1851,7 +1869,7 @@ void ParserBuilder::guardFeatureCode(const type::Unit& unit, std::string_view fe return; } - const auto& typeID = unit.typeID(); + const auto& typeID = unit.id(); if ( ! typeID ) { f(); diff --git a/spicy/toolchain/src/compiler/codegen/parsers/types.cc b/spicy/toolchain/src/compiler/codegen/parsers/types.cc index 8eac7379f..60e95825e 100644 --- a/spicy/toolchain/src/compiler/codegen/parsers/types.cc +++ b/spicy/toolchain/src/compiler/codegen/parsers/types.cc @@ -81,16 +81,16 @@ struct Visitor : public hilti::visitor::PreOrder { std::optional byte_order; if ( const auto& a = AttributeSet::find(meta.field()->attributes(), "&byte-order") ) - byte_order = *a->valueAs(); + byte_order = *a->valueAsExpression(); else if ( const auto& a = AttributeSet::find(state().unit.get().attributes(), "&byte-order") ) - byte_order = *a->valueAs(); + byte_order = *a->valueAsExpression(); else if ( const auto& p = state().unit.get().propertyItem("%byte-order") ) byte_order = *p->expression(); if ( byte_order ) - return builder::expect_type(std::move(*byte_order), builder::typeByID("spicy::ByteOrder")); + return std::move(*byte_order); else return builder::id("hilti::ByteOrder::Network"); } @@ -98,6 +98,7 @@ struct Visitor : public hilti::visitor::PreOrder { result_t operator()(const hilti::type::Address& t) { auto v4 = AttributeSet::find(meta.field()->attributes(), "&ipv4"); auto v6 = AttributeSet::find(meta.field()->attributes(), "&ipv6"); + (void)v6; assert(! (v4 && v6)); if ( v4 ) @@ -112,7 +113,7 @@ struct Visitor : public hilti::visitor::PreOrder { } result_t operator()(const spicy::type::Bitfield& t) { - auto itype = hilti::type::UnsignedInteger(t.width(), t.meta()); + auto itype = t.parseType(); auto value = builder()->addTmp("bitfield", itype); performUnpack(value, itype, t.width() / 8, {state().cur, fieldByteOrder()}, t.meta(), is_try); @@ -125,7 +126,7 @@ struct Visitor : public hilti::visitor::PreOrder { auto bit_order = builder::id("spicy_rt::BitOrder::LSB0"); if ( const auto& a = AttributeSet::find(meta.field()->attributes(), "&bit-order") ) - bit_order = *a->valueAs(); + bit_order = *a->valueAsExpression(); else if ( const auto& p = state().unit.get().propertyItem("%bit-order") ) bit_order = *p->expression(); @@ -135,10 +136,10 @@ struct Visitor : public hilti::visitor::PreOrder { builder::integer(b.upper()), bit_order})); if ( auto a = AttributeSet::find(b.attributes(), "&convert") ) { - auto converted = builder()->addTmp(ID("converted"), b.type()); + auto converted = builder()->addTmp(ID("converted"), b.itemType()); auto block = builder()->addBlock(); block->addLocal(ID("__dd"), itype, x); - block->addAssign(converted, *a->valueAs()); + block->addAssign(converted, *a->valueAsExpression()); x = converted; } @@ -157,7 +158,7 @@ struct Visitor : public hilti::visitor::PreOrder { auto type = AttributeSet::find(meta.field()->attributes(), "&type"); assert(type); return performUnpack(destination(t), type::Real(), 4, - {state().cur, *type->valueAs(), fieldByteOrder()}, t.meta(), is_try); + {state().cur, *type->valueAsExpression(), fieldByteOrder()}, t.meta(), is_try); } result_t operator()(const hilti::type::SignedInteger& t) { @@ -172,7 +173,7 @@ struct Visitor : public hilti::visitor::PreOrder { if ( auto size = AttributeSet::find(meta.field()->attributes(), "&size") ) // Even though we we do not store parsed data, we still need to consume // data in the input stream so that `&size` checks can work. - pb->advanceInput(*size->valueAs()); + pb->advanceInput(*size->valueAsExpression()); else if ( auto eod = AttributeSet::find(meta.field()->attributes(), "&eod") ) { pb->waitForEod(); @@ -180,13 +181,14 @@ struct Visitor : public hilti::visitor::PreOrder { } else if ( auto until_attr = AttributeSet::find(meta.field()->attributes(), "&until") ) { - Expression until_expr = builder::coerceTo(*until_attr->valueAs(), hilti::type::Bytes()); + Expression until_expr = builder::coerceTo(*until_attr->valueAsExpression(), hilti::type::Bytes()); auto until_bytes_var = builder()->addTmp("until_bytes", until_expr); auto until_bytes_size_var = builder()->addTmp("until_bytes_sz", builder::size(until_bytes_var)); auto body = builder()->addWhile(builder::bool_(true)); pushBuilder(body, [&]() { - pb->waitForInput(until_bytes_size_var, "end-of-data reached before &until expression found", t.meta()); + pb->waitForInput(until_bytes_size_var, "end-of-data reached before &until expression found", + until_attr->meta()); auto find = builder::memberCall(state().cur, "find", {until_bytes_var}); auto found_id = ID("found"); @@ -282,9 +284,9 @@ struct Visitor : public hilti::visitor::PreOrder { if ( until_attr || until_including_attr ) { Expression until_expr; if ( until_attr ) - until_expr = builder::coerceTo(*until_attr->valueAs(), hilti::type::Bytes()); + until_expr = builder::coerceTo(*until_attr->valueAsExpression(), hilti::type::Bytes()); else - until_expr = builder::coerceTo(*until_including_attr->valueAs(), hilti::type::Bytes()); + until_expr = builder::coerceTo(*until_including_attr->valueAsExpression(), hilti::type::Bytes()); auto until_bytes_var = builder()->addTmp("until_bytes", until_expr); auto until_bytes_size_var = builder()->addTmp("until_bytes_sz", builder::size(until_bytes_var)); @@ -313,7 +315,7 @@ struct Visitor : public hilti::visitor::PreOrder { pb->waitForInput(until_bytes_size_var, fmt("end-of-data reached before %s expression found", (until_attr ? "&until" : "&until-including")), - t.meta()); + until_expr.meta()); auto find = builder::memberCall(state().cur, "find", {until_bytes_var}); auto found_id = ID("found"); @@ -357,7 +359,7 @@ Expression ParserBuilder::_parseType(const Type& t, const production::Meta& meta bool is_try) { assert(! is_try || (t.isA() || t.isA())); - if ( auto e = Visitor(this, meta, dst, is_try).dispatch(type::effectiveType(t)) ) + if ( auto e = Visitor(this, meta, dst, is_try).dispatch(t) ) return std::move(*e); hilti::logger().internalError(fmt("codegen: type parser did not return expression for '%s'", t)); diff --git a/spicy/toolchain/src/compiler/codegen/production.cc b/spicy/toolchain/src/compiler/codegen/production.cc index 2a72fa88f..c57238e44 100644 --- a/spicy/toolchain/src/compiler/codegen/production.cc +++ b/spicy/toolchain/src/compiler/codegen/production.cc @@ -47,7 +47,7 @@ std::string codegen::production::to_string(const Production& p) { if ( auto x = f->arguments(); x.size() ) { args = hilti::util::fmt(", args: (%s)", - hilti::util::join(hilti::util::transform(x, + hilti::util::join(hilti::node::transform(x, [](auto& a) { return hilti::util::fmt("%s", a); }), diff --git a/spicy/toolchain/src/compiler/codegen/unit-builder.cc b/spicy/toolchain/src/compiler/codegen/unit-builder.cc index ad16ce26b..d043d5f4c 100644 --- a/spicy/toolchain/src/compiler/codegen/unit-builder.cc +++ b/spicy/toolchain/src/compiler/codegen/unit-builder.cc @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -29,9 +30,9 @@ struct FieldBuilder : public hilti::visitor::PreOrder { FieldBuilder(CodeGen* cg, const spicy::type::Unit& unit) : cg(cg), unit(unit) {} CodeGen* cg; const spicy::type::Unit& unit; - std::vector fields; + std::vector fields; - void addField(hilti::type::struct_::Field f) { fields.emplace_back(std::move(f)); } + void addField(hilti::declaration::Field f) { fields.emplace_back(std::move(f)); } void operator()(const spicy::type::unit::item::Field& f, position_t p) { if ( ! f.parseType().isA() ) { @@ -47,9 +48,7 @@ struct FieldBuilder : public hilti::visitor::PreOrder { // times. attrs = AttributeSet::add(attrs, Attribute("&no-emit")); - // We set the field's auxiliary type to the parse type, so that - // we retain that information. - auto nf = hilti::type::struct_::Field(f.id(), f.itemType(), f.parseType(), std::move(attrs), f.meta()); + auto nf = hilti::declaration::Field(f.id(), f.itemType(), std::move(attrs), f.meta()); addField(std::move(nf)); } @@ -57,14 +56,15 @@ struct FieldBuilder : public hilti::visitor::PreOrder { auto addHookDeclaration = [&](const auto& f, bool foreach) { if ( auto hook_decl = cg->compileHook(unit, f.id(), {f}, foreach, false, {}, {}, {}, f.meta()) ) { auto nf = - hilti::type::struct_::Field(hook_decl->id().local(), hook_decl->function().type(), {}, f.meta()); + hilti::declaration::Field(hook_decl->id().local(), hook_decl->function().type(), {}, f.meta()); addField(std::move(nf)); } }; auto addHookImplementation = [&](auto& hook) { - if ( auto hook_impl = cg->compileHook(unit, ID(*unit.typeID(), f.id()), f, hook.isForEach(), hook.isDebug(), - hook.type().parameters(), hook.body(), hook.priority(), hook.meta()) ) + if ( auto hook_impl = + cg->compileHook(unit, ID(*unit.id(), f.id()), f, hook.isForEach(), hook.isDebug(), + hook.ftype().parameters().copy(), hook.body(), hook.priority(), hook.meta()) ) cg->addDeclaration(*hook_impl); }; @@ -117,7 +117,7 @@ struct FieldBuilder : public hilti::visitor::PreOrder { if ( f.isOptional() ) attrs = AttributeSet::add(attrs, Attribute("&optional")); - auto nf = hilti::type::struct_::Field(f.id(), std::move(ftype), std::move(attrs), f.meta()); + auto nf = hilti::declaration::Field(f.id(), std::move(ftype), std::move(attrs), f.meta()); addField(std::move(nf)); } @@ -126,14 +126,15 @@ struct FieldBuilder : public hilti::visitor::PreOrder { AttributeSet attrs({Attribute("&default", builder::new_(std::move(type))), Attribute("&internal"), Attribute("&needed-by-feature", builder::string("supports_sinks"))}); - auto nf = hilti::type::struct_::Field(s.id(), type::Sink(), std::move(attrs), s.meta()); + auto nf = hilti::declaration::Field(s.id(), type::Sink(), std::move(attrs), s.meta()); addField(std::move(nf)); } void operator()(const spicy::type::unit::item::UnitHook& h, const position_t /* p */) { auto hook = h.hook(); - if ( auto hook_impl = cg->compileHook(unit, ID(*unit.typeID(), h.id()), {}, hook.isForEach(), hook.isDebug(), - hook.type().parameters(), hook.body(), hook.priority(), h.meta()) ) + if ( auto hook_impl = + cg->compileHook(unit, ID(*unit.id(), h.id()), {}, hook.isForEach(), hook.isDebug(), + hook.ftype().parameters().copy(), hook.body(), hook.priority(), h.meta()) ) cg->addDeclaration(*hook_impl); } }; @@ -149,24 +150,23 @@ Type CodeGen::compileUnit(const type::Unit& unit, bool declare_only) { auto add_hook = [&](std::string id, std::vector params) { if ( auto hook_decl = compileHook(unit, ID(std::move(id)), {}, false, false, std::move(params), {}, {}, unit.meta()) ) { - auto nf = - hilti::type::struct_::Field(hook_decl->id().local(), hook_decl->function().type(), {}, unit.meta()); + auto nf = hilti::declaration::Field(hook_decl->id().local(), hook_decl->function().type(), {}, unit.meta()); v.addField(std::move(nf)); } }; if ( options().getAuxOption("spicy.track_offsets", false) ) { - v.addField(hilti::type::struct_::Field(ID("__offsets"), - hilti::type::Vector(hilti::type::Optional(hilti::type::Tuple( - {type::UnsignedInteger(64), - hilti::type::Optional(type::UnsignedInteger(64))}))), - AttributeSet({Attribute("&internal"), Attribute("&always-emit")}))); + v.addField(hilti::declaration::Field(ID("__offsets"), + hilti::type::Vector(hilti::type::Optional(hilti::type::Tuple( + {type::UnsignedInteger(64), + hilti::type::Optional(type::UnsignedInteger(64))}))), + AttributeSet({Attribute("&internal"), Attribute("&always-emit")}))); } if ( auto context = unit.contextType() ) { auto attrs = AttributeSet({Attribute("&internal")}); auto ftype = hilti::type::StrongReference(*context); - auto f = hilti::type::struct_::Field(ID("__context"), ftype, std::move(attrs), unit.meta()); + auto f = hilti::declaration::Field(ID("__context"), ftype, std::move(attrs), unit.meta()); v.addField(std::move(f)); } @@ -176,20 +176,20 @@ Type CodeGen::compileUnit(const type::Unit& unit, bool declare_only) { add_hook("0x25_print", {}); add_hook("0x25_finally", {}); - if ( auto typeID = unit.typeID() ) { - typeID = hilti::rt::replace(*typeID, ":", "_"); + if ( unit.id() ) { + ID typeID = ID(hilti::rt::replace(*unit.id(), ":", "_")); if ( unit.isFilter() ) - addDeclaration(builder::constant(ID(fmt("__feat%%%s%%is_filter", *typeID)), builder::bool_(true))); + addDeclaration(builder::constant(ID(fmt("__feat%%%s%%is_filter", typeID)), builder::bool_(true))); if ( unit.supportsFilters() ) - addDeclaration(builder::constant(ID(fmt("__feat%%%s%%supports_filters", *typeID)), builder::bool_(true))); + addDeclaration(builder::constant(ID(fmt("__feat%%%s%%supports_filters", typeID)), builder::bool_(true))); if ( unit.supportsSinks() ) - addDeclaration(builder::constant(ID(fmt("__feat%%%s%%supports_sinks", *typeID)), builder::bool_(true))); + addDeclaration(builder::constant(ID(fmt("__feat%%%s%%supports_sinks", typeID)), builder::bool_(true))); if ( unit.usesRandomAccess() ) - addDeclaration(builder::constant(ID(fmt("__feat%%%s%%uses_random_access", *typeID)), builder::bool_(true))); + addDeclaration(builder::constant(ID(fmt("__feat%%%s%%uses_random_access", typeID)), builder::bool_(true))); } if ( unit.supportsSinks() ) { @@ -205,13 +205,13 @@ Type CodeGen::compileUnit(const type::Unit& unit, bool declare_only) { if ( unit.usesRandomAccess() ) { auto attr_random_access = Attribute("&needed-by-feature", builder::string("uses_random_access")); - auto f1 = hilti::type::struct_::Field(ID("__begin"), hilti::type::Optional(hilti::type::stream::Iterator()), - AttributeSet({Attribute("&internal"), attr_random_access})); - auto f2 = hilti::type::struct_::Field(ID("__position"), hilti::type::Optional(hilti::type::stream::Iterator()), - AttributeSet({Attribute("&internal"), attr_random_access})); + auto f1 = hilti::declaration::Field(ID("__begin"), hilti::type::Optional(hilti::type::stream::Iterator()), + AttributeSet({Attribute("&internal"), attr_random_access})); + auto f2 = hilti::declaration::Field(ID("__position"), hilti::type::Optional(hilti::type::stream::Iterator()), + AttributeSet({Attribute("&internal"), attr_random_access})); auto f3 = - hilti::type::struct_::Field(ID("__position_update"), hilti::type::Optional(hilti::type::stream::Iterator()), - AttributeSet({Attribute("&internal"), attr_random_access})); + hilti::declaration::Field(ID("__position_update"), hilti::type::Optional(hilti::type::stream::Iterator()), + AttributeSet({Attribute("&internal"), attr_random_access})); v.addField(std::move(f1)); v.addField(std::move(f2)); v.addField(std::move(f3)); @@ -227,7 +227,7 @@ Type CodeGen::compileUnit(const type::Unit& unit, bool declare_only) { attrs = AttributeSet::add(std::move(attrs), Attribute("&needed-by-feature", builder::string("is_filter"))); auto parser = - hilti::type::struct_::Field(ID("__parser"), builder::typeByID("spicy_rt::Parser"), std::move(attrs)); + hilti::declaration::Field(ID("__parser"), builder::typeByID("spicy_rt::Parser"), std::move(attrs)); v.addField(std::move(parser)); } @@ -241,58 +241,57 @@ Type CodeGen::compileUnit(const type::Unit& unit, bool declare_only) { if ( unit.propertyItem("%mime-type") ) attrs = AttributeSet::add(attrs, Attribute("&always-emit")); - auto sink = hilti::type::struct_::Field(ID("__sink"), builder::typeByID("spicy_rt::SinkState"), attrs); + auto sink = hilti::declaration::Field(ID("__sink"), builder::typeByID("spicy_rt::SinkState"), attrs); v.addField(std::move(sink)); } if ( unit.supportsFilters() ) { - auto filters = hilti::type::struct_::Field(ID("__filters"), - hilti::type::StrongReference(builder::typeByID("spicy_rt::Filters")), - AttributeSet({Attribute("&internal"), - Attribute("&needed-by-feature", - builder::string("supports_filters"))})); + auto filters = hilti::declaration::Field(ID("__filters"), + hilti::type::StrongReference(builder::typeByID("spicy_rt::Filters")), + AttributeSet({Attribute("&internal"), + Attribute("&needed-by-feature", + builder::string("supports_filters"))})); v.addField(std::move(filters)); } if ( unit.isFilter() ) { auto forward = - hilti::type::struct_::Field(ID("__forward"), - hilti::type::WeakReference(builder::typeByID("spicy_rt::Forward")), - AttributeSet({Attribute("&internal"), - Attribute("&needed-by-feature", builder::string("is_filter"))})); + hilti::declaration::Field(ID("__forward"), + hilti::type::WeakReference(builder::typeByID("spicy_rt::Forward")), + AttributeSet({Attribute("&internal"), + Attribute("&needed-by-feature", builder::string("is_filter"))})); v.addField(std::move(forward)); } auto ft = _pb.parseMethodFunctionType({}, unit.meta()); - v.addField(type::struct_::Field(type::struct_::Field("__parse_stage1", std::move(ft)))); + v.addField(hilti::declaration::Field(hilti::declaration::Field("__parse_stage1", std::move(ft)))); if ( auto convert = AttributeSet::find(unit.attributes(), "&convert") ) { - auto expression = *convert->valueAs(); - auto result = type::Auto(); + auto expression = *convert->valueAsExpression(); + auto result = type::auto_; auto params = std::vector(); - auto ftype = type::Function(type::function::Result(std::move(result), expression.meta()), std::move(params), - hilti::type::function::Flavor::Method, expression.meta()); + auto ftype = type::Function(type::function::Result(std::move(result), expression.get().meta()), + std::move(params), hilti::type::function::Flavor::Method, expression.get().meta()); _pb.pushBuilder(); _pb.builder()->addReturn(expression); auto body = _pb.popBuilder(); auto function = hilti::Function(ID("__convert"), std::move(ftype), body->block()); - auto convert_ = hilti::type::struct_::Field(ID("__convert"), std::move(function)); + auto convert_ = hilti::declaration::Field(ID("__convert"), std::move(function)); v.addField(std::move(convert_)); } - assert(unit.typeID()); - Type s = hilti::type::Struct(unit.parameters(), std::move(v.fields)); - s = type::setTypeID(s, *unit.typeID()); + assert(unit.id()); + Type s = hilti::type::Struct(unit.parameters().copy(), std::move(v.fields)); + s = type::setTypeID(s, *unit.id()); s = _pb.addParserMethods(s.as(), unit, declare_only, unit.isFilter()); if ( unit.isPublic() || unit.isFilter() ) { auto builder = builder::Builder(context()); auto description = unit.propertyItem("%description"); - auto mime_types = hilti::util::transform(unit.propertyItems("%mime-type"), [](auto p) { - return builder::library_type_value(*p.expression(), "spicy_rt::MIMEType"); - }); - auto ports = hilti::util::transform(unit.propertyItems("%port"), [](auto p) { + auto mime_types = + hilti::node::transform(unit.propertyItems("%mime-type"), [](const auto& p) { return *p.expression(); }); + auto ports = hilti::node::transform(unit.propertyItems("%port"), [](auto p) { auto dir = builder::id("spicy_rt::Direction::Both"); if ( const auto& attrs = p.attributes() ) { @@ -306,7 +305,7 @@ Type CodeGen::compileUnit(const type::Unit& unit, bool declare_only) { dir = builder::id("spicy_rt::Direction::Responder"); } - return builder::library_type_value(builder::tuple({*p.expression(), dir}), "spicy_rt::ParserPort"); + return builder::tuple({*p.expression(), dir}); }); Expression parse1 = builder::null(); @@ -325,12 +324,12 @@ Type CodeGen::compileUnit(const type::Unit& unit, bool declare_only) { context_new = _pb.contextNewFunction(unit); auto parser = - builder::struct_({{ID("name"), builder::string(*unit.typeID())}, + builder::struct_({{ID("name"), builder::string(*unit.id())}, {ID("parse1"), parse1}, {ID("parse2"), _pb.parseMethodExternalOverload2(unit)}, {ID("parse3"), parse3}, {ID("context_new"), context_new}, - {ID("type_info"), builder::typeinfo(unit)}, + {ID("type_info"), builder::typeinfo(builder::id(*unit.id()))}, {ID("description"), (description ? *description->expression() : builder::string(""))}, {ID("mime_types"), builder::vector(builder::typeByID("spicy_rt::MIMEType"), std::move(mime_types))}, @@ -338,20 +337,18 @@ Type CodeGen::compileUnit(const type::Unit& unit, bool declare_only) { builder::vector(builder::typeByID("spicy_rt::ParserPort"), std::move(ports))}}, unit.meta()); - builder.addAssign(builder::id(ID(*unit.typeID(), "__parser")), parser); + builder.addAssign(builder::id(ID(*unit.id(), "__parser")), parser); if ( unit.isPublic() ) - builder.addExpression( - builder::call("spicy_rt::registerParser", - {builder::id(ID(*unit.typeID(), "__parser")), builder::call("hilti::linker_scope", {}), - builder::strong_reference(unit)})); + builder.addExpression(builder::call("spicy_rt::registerParser", {builder::id(ID(*unit.id(), "__parser")), + builder::call("hilti::linker_scope", {}), + builder::strong_reference(unit)})); auto register_unit = - builder::function(ID(fmt("__register_%s", hilti::util::replace(*unit.typeID(), "::", "_"))), type::Void(), - {}, builder.block(), type::function::Flavor::Standard, declaration::Linkage::Init); + builder::function(ID(fmt("__register_%s", hilti::util::replace(*unit.id(), "::", "_"))), type::void_, {}, + builder.block(), type::function::Flavor::Standard, declaration::Linkage::Init); addDeclaration(std::move(register_unit)); } - s.setOriginalNode(preserveNode(unit)); return s; } diff --git a/spicy/toolchain/src/compiler/coercion.cc b/spicy/toolchain/src/compiler/coercion.cc new file mode 100644 index 000000000..8bc0f0968 --- /dev/null +++ b/spicy/toolchain/src/compiler/coercion.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include + +#include +#include +#include + + +using namespace spicy; + +namespace hilti::logging::debug { +inline const DebugStream Operator("operator"); +} // namespace hilti::logging::debug + + +namespace { + +struct VisitorCtor : public hilti::visitor::PreOrder, VisitorCtor> { + VisitorCtor(const Type& dst, bitmask style) : dst(dst), style(style) {} + + const Type& dst; + bitmask style; + + result_t operator()(const hilti::ctor::String& c) { + if ( auto x = dst.tryAs(); x && x->cxxName() == "::spicy::rt::MIMEType" ) + return hilti::ctor::Library(c, dst, c.meta()); + + return {}; + } + + result_t operator()(const hilti::ctor::Tuple& c) { + if ( auto x = dst.tryAs(); x && x->cxxName() == "::spicy::rt::ParserPort" ) + return hilti::ctor::Library(c, dst, c.meta()); + + return {}; + } +}; + +struct VisitorType : public hilti::visitor::PreOrder, VisitorType> { + VisitorType(const Type& dst, bitmask style) : dst(dst), style(style) {} + + const Type& dst; + bitmask style; + + result_t operator()(const type::Unit& t, position_t p) { + if ( auto x = dst.tryAs(); x && x->dereferencedType() == p.node.as() ) + // Our codegen will turn a unit T into value_ref, which coerces to strong_ref. + return dst; + + return {}; + } +}; + +} // anonymous namespace + +// Plugin-specific version just kicking off the local visitor. +std::optional spicy::detail::coerceCtor(Ctor c, const Type& dst, bitmask style) { + if ( ! (type::isResolved(c.type()) && type::isResolved(dst)) ) + return {}; + + if ( auto nc = VisitorCtor(dst, style).dispatch(c) ) + return *nc; + + return (*hilti::plugin::registry().hiltiPlugin().coerce_ctor)(std::move(c), std::move(dst), style); +} + +// Plugin-specific version just kicking off the local visitor. +std::optional spicy::detail::coerceType(Type t, const Type& dst, bitmask style) { + if ( ! (type::isResolved(t) && type::isResolved(dst)) ) + return {}; + + if ( auto nt = VisitorType(dst, style).dispatch(t) ) + return *nt; + + return (*hilti::plugin::registry().hiltiPlugin().coerce_type)(std::move(t), std::move(dst), style); +} diff --git a/spicy/toolchain/src/compiler/parser/parser.yy b/spicy/toolchain/src/compiler/parser/parser.yy index 2a5c80021..f2fedf932 100644 --- a/spicy/toolchain/src/compiler/parser/parser.yy +++ b/spicy/toolchain/src/compiler/parser/parser.yy @@ -233,7 +233,8 @@ static int _field_width = 0; %token WHILE %type local_id scoped_id dotted_id unit_hook_id -%type local_decl local_init_decl global_decl type_decl import_decl constant_decl function_decl global_scope_decl property_decl hook_decl +%type local_decl local_init_decl global_decl type_decl import_decl constant_decl function_decl global_scope_decl property_decl hook_decl struct_field +%type struct_fields %type global_scope_items %type base_type_no_ref base_type type tuple_type struct_type enum_type unit_type bitfield_type reference_type %type ctor tuple struct_ regexp list vector map set @@ -255,8 +256,6 @@ static int _field_width = 0; %type opt_attributes opt_unit_hook_attributes %type tuple_type_elem %type tuple_type_elems -%type struct_field -%type struct_fields %type struct_elems %type struct_elem %type map_elems opt_map_elems @@ -340,8 +339,10 @@ global_scope_decl | hook_decl { $$ = std::move($1); } type_decl : opt_linkage TYPE scoped_id '=' type opt_attributes ';' { - if ( auto x = $5.tryAs(); x && $6 && *$6 ) - $5 = Type(type::Unit::addAttributes(*x, *$6)); + if ( auto x = $5.isA(); x && $6 && *$6 ) { + $5.as().setAttributes(*$6); + $6 = {}; // don't associate with declaration + } $$ = hilti::declaration::Type(std::move($3), std::move($5), std::move($6), std::move($1), __loc__); } @@ -392,8 +393,7 @@ hook_decl : ON unit_hook_id unit_hook { ID unit = $2.namespace_(); if ( unit.empty() ) error(@$, "hook requires unit namespace"); - auto hook = spicy::type::unit::item::UnitHook($2.local(), std::move($3), __loc__); - $$ = spicy::declaration::UnitHook($2, hilti::type::UnresolvedID(unit), std::move(hook), __loc__); + $$ = spicy::declaration::UnitHook(std::move($2), std::move($3), __loc__); } ; @@ -442,7 +442,7 @@ func_param : opt_func_param_kind local_id ':' type opt_init_expression func_result : ':' type { $$ = hilti::type::function::Result(std::move($2), __loc__); } opt_func_result : func_result { $$ = std::move($1); } - | /* empty */ { $$ = hilti::type::function::Result(hilti::type::Void(__loc__), __loc__); } + | /* empty */ { $$ = hilti::type::function::Result(hilti::type::void_, __loc__); } opt_func_param_kind : INOUT { $$ = hilti::declaration::parameter::Kind::InOut; } @@ -492,7 +492,7 @@ stmt : stmt_expr ';' { $$ = std::move($1); } | SWITCH '(' expr ')' '{' switch_cases '}' { $$ = hilti::statement::Switch(std::move($3), std::move($6), __loc__); } | SWITCH '(' local_init_decl ')' '{' switch_cases '}' - { $$ = hilti::statement::Switch($3, hilti::expression::UnresolvedID($3.as().id()), std::move($6), __loc__); } + { $$ = hilti::statement::Switch(std::move($3), std::move($6), __loc__); } | WHILE '(' local_init_decl ';' expr ')' block { $$ = hilti::statement::While(std::move($3), std::move($5), std::move($7), std::nullopt, __loc__); } | WHILE '(' expr ')' block @@ -558,7 +558,7 @@ base_type_no_ref | STREAM { $$ = hilti::type::Stream(__loc__); } | STRING { $$ = hilti::type::String(__loc__); } | TIME { $$ = hilti::type::Time(__loc__); } - | VOID { $$ = hilti::type::Void(__loc__); } + | VOID { $$ = hilti::type::void_; } | INT8 { $$ = hilti::type::SignedInteger(8, __loc__); } | INT16 { $$ = hilti::type::SignedInteger(16, __loc__); } @@ -591,10 +591,10 @@ base_type_no_ref ; /* We split this out from "base_type" because it can lead to ambigitious in some contexts. */ -reference_type: type '&' { $$ = hilti::type::StrongReference(std::move($1), true, __loc__); } +reference_type: type '&' { $$ = hilti::type::StrongReference(std::move($1), __loc__); } base_type : base_type_no_ref - reference_type: type '&' { $$ = hilti::type::StrongReference(std::move($1), true, __loc__); } + reference_type: type '&' { $$ = hilti::type::StrongReference(std::move($1), __loc__); } ; type : base_type { $$ = std::move($1); } @@ -618,20 +618,20 @@ tuple_type_elems : tuple_type_elems ',' tuple_type_elem { $$ = std::move($1); $$.push_back(std::move($3)); } | tuple_type_elems ',' { $$ = std::move($1); } - | tuple_type_elem { $$ = std::vector>{ std::move($1) }; } + | tuple_type_elem { $$ = std::vector{ std::move($1) }; } ; tuple_type_elem - : type { $$ = std::make_pair(hilti::ID(), std::move($1)); } - | local_id ':' type { $$ = std::make_pair(std::move($1), std::move($3)); } + : type { $$ = type::tuple::Element(std::move($1)); } + | local_id ':' type { $$ = type::tuple::Element(std::move($1), std::move($3)); } ; struct_type : STRUCT '{' struct_fields '}' { $$ = hilti::type::Struct(std::move($3), __loc__); } struct_fields : struct_fields struct_field { $$ = std::move($1); $$.push_back($2); } - | /* empty */ { $$ = std::vector{}; } + | /* empty */ { $$ = std::vector{}; } -struct_field : type local_id opt_attributes ';' { $$ = hilti::type::struct_::Field(std::move($2), std::move($1), std::move($3), __loc__); } +struct_field : type local_id opt_attributes ';' { $$ = hilti::declaration::Field(std::move($2), std::move($1), std::move($3), __loc__); } enum_type : ENUM '{' enum_labels '}' { $$ = hilti::type::Enum(std::move($3), __loc__); } @@ -915,7 +915,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::Keyword(hilti::expression::keyword::Kind::DollarDollar, __loc__); } + | DOLLARDOLLAR { $$ = hilti::expression::UnresolvedID(std::move("__dd"), __loc__);} | DOLLAR_NUMBER { // $N -> $@[N] (with $@ being available internally only, not exposed to users) auto captures = hilti::expression::Keyword(hilti::expression::keyword::Kind::Captures, hilti::builder::typeByID("hilti::Captures"), __loc__); auto index = hilti::expression::Ctor(hilti::ctor::UnsignedInteger($1, 64, __loc__), __loc__); @@ -1038,12 +1038,12 @@ re_pattern_constant { $$ = std::move($3); } opt_map_elems : map_elems { $$ = std::move($1); } - | /* empty */ { $$ = std::vector(); } + | /* empty */ { $$ = std::vector(); } map_elems : map_elems ',' map_elem { $$ = std::move($1); $$.push_back(std::move($3)); } - | map_elem { $$ = std::vector(); $$.push_back(std::move($1)); } + | map_elem { $$ = std::vector(); $$.push_back(std::move($1)); } -map_elem : expr ':' expr { $$ = std::make_pair($1, $3); } +map_elem : expr ':' expr { $$ = hilti::ctor::map::Element($1, $3); } /* Attributes */ diff --git a/spicy/toolchain/src/compiler/parser/scanner.ll b/spicy/toolchain/src/compiler/parser/scanner.ll index 0c87ed1bf..9121ad699 100644 --- a/spicy/toolchain/src/compiler/parser/scanner.ll +++ b/spicy/toolchain/src/compiler/parser/scanner.ll @@ -52,7 +52,7 @@ static std::string preprocessor_directive; address4 ({digits}"."){3}{digits} address6 ("["({hexs}:){7}{hexs}"]")|("["0x{hexs}({hexs}|:)*"::"({hexs}|:)*"]")|("["({hexs}|:)*"::"({hexs}|:)*"]")|("["({hexs}|:)*"::"({hexs}|:)*({digits}"."){3}{digits}"]") -attribute \&(bit-order|byte-order|chunked|convert|count|cxxname|default|eod|internal|ipv4|ipv6|length|max-size|no-emit|nosub|on-heap|optional|originator|parse-at|parse-from|priority|requires|responder|size|static|synchronize|transient|try|type|until|until-including|while|have_prototype) +attribute \&(bit-order|byte-order|chunked|convert|count|cxxname|default|eod|internal|ipv4|ipv6|hilti_type|length|max-size|no-emit|nosub|on-heap|optional|originator|parse-at|parse-from|priority|requires|responder|size|static|synchronize|transient|try|type|until|until-including|while|have_prototype) blank [ \t] comment [ \t]*#[^\n]*\n? digit [0-9] diff --git a/spicy/toolchain/src/compiler/plugin.cc b/spicy/toolchain/src/compiler/plugin.cc index a81c5b874..25ea11d64 100644 --- a/spicy/toolchain/src/compiler/plugin.cc +++ b/spicy/toolchain/src/compiler/plugin.cc @@ -1,10 +1,12 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. #include +#include #include #include #include +#include #include #include @@ -14,9 +16,9 @@ using namespace spicy::detail; static hilti::Plugin spicy_plugin() { return hilti::Plugin{ .component = "Spicy", + .order = 5, // before HILTI .extension = ".spicy", .cxx_includes = {"spicy/rt/libspicy.h"}, - .order = 2, .library_paths = [](const std::shared_ptr& /* ctx */) { return spicy::configuration().spicy_library_paths; }, @@ -29,37 +31,33 @@ static hilti::Plugin spicy_plugin() { .coerce_type = [](Type t, const Type& dst, bitmask style) { return detail::coerceType(std::move(t), dst, style); }, - .build_scopes = [](const std::shared_ptr& /* ctx */, - const std::vector>& m, hilti::Unit* u) { buildScopes(m, u); }, + .ast_build_scopes = + [](const std::shared_ptr& ctx, Node* m, hilti::Unit* u) { + ast::buildScopes(ctx, m, u); + return false; + }, - .resolve_ids = [](const std::shared_ptr& /* ctx */, Node* n, - hilti::Unit* u) { return resolveIDs(n, u); }, + .ast_normalize = [](const std::shared_ptr& ctx, Node* m, + hilti::Unit* u) { return ast::normalize(ctx, m, u); }, - // TODO(bbannier): The following line is inconsistently formatted - // between clang-format-10 and clang-format-11. Remove this opt out - // once we bump the CI formatting to llvm-11. - // clang-format off - .resolve_operators = [](const std::shared_ptr& /* ctx */, Node* /* n */, hilti::Unit * - /* u */) -> bool { return false; }, - // clang-format on + .ast_coerce = [](const std::shared_ptr& ctx, Node* m, + hilti::Unit* u) { return (*hilti::plugin::registry().hiltiPlugin().ast_coerce)(ctx, m, u); }, - .apply_coercions = [](const std::shared_ptr& /* ctx */, Node* n, - hilti::Unit* u) { return applyCoercions(n, u); }, + .ast_resolve = [](const std::shared_ptr& ctx, Node* m, + hilti::Unit* u) { return ast::resolve(ctx, m, u); }, - .pre_validate = [](const std::shared_ptr& /* ctx */, Node* n, hilti::Unit* u, - bool* found_errors) { preTransformValidateAST(n, u, found_errors); }, + .ast_validate = + [](const std::shared_ptr& ctx, Node* m, hilti::Unit* u) { + ast::validate(ctx, m, u); + return false; + }, - .post_validate = [](const std::shared_ptr& /* ctx */, Node* n, - hilti::Unit* u) { postTransformValidateAST(n, u); }, + .ast_print = [](const Node& root, hilti::printer::Stream& out) { return ast::print(root, out); }, - .preserved_validate = [](const std::shared_ptr& /* ctx */, std::vector* n, - hilti::Unit* u) { preservedValidateAST(n, u); }, - - .transform = [](std::shared_ptr ctx, Node* n, bool init, hilti::Unit* u) -> bool { - return CodeGen(std::move(ctx)).compileModule(n, init, u); + .ast_transform = [](std::shared_ptr ctx, Node* n, hilti::Unit* u) -> bool { + return CodeGen(std::move(ctx)).compileModule(n, u); }, - - .print_ast = [](const Node& root, hilti::printer::Stream& out) { return printAST(root, out); }}; + }; } static hilti::plugin::Register _(spicy_plugin()); diff --git a/spicy/toolchain/src/compiler/visitors/apply-coercions.cc b/spicy/toolchain/src/compiler/visitors/apply-coercions.cc deleted file mode 100644 index 5e09bd3aa..000000000 --- a/spicy/toolchain/src/compiler/visitors/apply-coercions.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#include -#include -#include -#include - -#include -#include - -using namespace spicy; - -namespace { - -struct Visitor : public hilti::visitor::PreOrder { - // Currently nothing to do here. Note that we coerce field attributes on - // access through builder::coerceTo(). - - bool modified = false; - - Expression coerceToPending(const Expression& e, const Type& t) { - return hilti::expression::PendingCoerced(e, t, e.meta()); - } - - template - void replaceNode(position_t* p, T&& n) { - p->node = std::forward(n); - modified = true; - } -}; - -} // anonymous namespace - -bool spicy::detail::applyCoercions(Node* root, hilti::Unit* /* unit */) { - hilti::util::timing::Collector _("spicy/compiler/apply-coercions"); - - auto v = Visitor(); - for ( auto i : v.walk(root) ) - v.dispatch(i); - - return v.modified; -} diff --git a/spicy/toolchain/src/compiler/visitors/coercer.cc b/spicy/toolchain/src/compiler/visitors/coercer.cc deleted file mode 100644 index 65d94afd8..000000000 --- a/spicy/toolchain/src/compiler/visitors/coercer.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#include - -#include - -#include -#include - -using namespace spicy; - -namespace { - -struct VisitorCtor : public hilti::visitor::PreOrder, VisitorCtor> { - VisitorCtor(const Type& dst, bitmask style) : dst(dst), style(style) {} - const hilti::Type& dst; - bitmask style; -}; - -struct VisitorType : public hilti::visitor::PreOrder, VisitorType> { - VisitorType(const Type& dst, bitmask style) : dst(dst), style(style) {} - const hilti::Type& dst; - bitmask style; -}; - -} // anonymous namespace - -std::optional detail::coerceCtor(hilti::Ctor c, const hilti::Type& dst, - bitmask style) { - if ( auto nc = VisitorCtor(dst, style).dispatch(std::move(c)) ) - return *nc; - - return {}; -} - -std::optional detail::coerceType(hilti::Type t, const hilti::Type& dst, bitmask style) { - if ( auto nt = VisitorType(dst, style).dispatch(std::move(t)) ) - return *nt; - - return {}; -} diff --git a/spicy/toolchain/src/compiler/visitors/id-resolver.cc b/spicy/toolchain/src/compiler/visitors/id-resolver.cc deleted file mode 100644 index fe5d18f0c..000000000 --- a/spicy/toolchain/src/compiler/visitors/id-resolver.cc +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace spicy; - -namespace { - -inline const hilti::logging::DebugStream Resolver("resolver"); - -// Turns an unresolved field into a resolved field. -template -auto resolveField(const type::unit::item::UnresolvedField& u, const T& t) { - auto field = type::unit::item::Field(u.fieldID(), std::move(t), u.engine(), u.arguments(), u.repeatCount(), - u.sinks(), u.attributes(), u.condition(), u.hooks(), u.meta()); - - assert(u.index()); - return type::unit::item::Field::setIndex(std::move(field), *u.index()); -} - -struct Visitor1 : public hilti::visitor::PostOrder { - explicit Visitor1(hilti::Unit* unit) : unit(unit) {} - hilti::Unit* unit; - bool modified = false; - - template - void replaceNode(position_t* p, T&& n, int line) { - p->node = std::forward(n); - HILTI_DEBUG(Resolver, hilti::util::fmt(" modified by Spicy %s:%d", - hilti::rt::filesystem::path(__FILE__).filename().native(), line)) - modified = true; - } - - void replaceUnresolvedField(const type::unit::item::UnresolvedField& u, position_t p) { - if ( auto id = u.unresolvedID() ) { // check for unresolved IDs first to overrides the other cases below - auto resolved = hilti::scope::lookupID(*id, p, "field"); - - if ( ! resolved ) { - p.node.addError(resolved.error()); - return; - } - - if ( auto t = resolved->first->tryAs() ) { - auto unit_type = t->type().tryAs(); - - if ( ! unit_type && t->type().originalNode() ) - unit_type = t->type().originalNode()->tryAs(); - - // Because we're doing type resolution ourselves here, we - // need to account for any &on-heap attribute. Normally, - // HILTI would take care of that for us when resolving a - // type. - Type tt = hilti::type::ResolvedID(*id, NodeRef(resolved->first), u.meta()); - - if ( unit_type || AttributeSet::has(t->attributes(), "&on-heap") ) - tt = hilti::type::ValueReference(tt, u.meta()); - - // If a unit comes with a &convert attribute, we wrap it into a - // subitem so that we have our recursive machinery available - // (which we don't have for pure types). - if ( unit_type && AttributeSet::has(unit_type->attributes(), "&convert") ) { - auto inner_field = type::unit::item::Field({}, std::move(tt), spicy::Engine::All, u.arguments(), {}, - {}, {}, {}, {}, u.meta()); - inner_field = type::unit::item::Field::setIndex(std::move(inner_field), *u.index()); - - auto outer_field = - type::unit::item::Field(u.fieldID(), std::move(inner_field), u.engine(), {}, u.repeatCount(), - u.sinks(), u.attributes(), u.condition(), u.hooks(), u.meta()); - - outer_field = type::unit::item::Field::setIndex(std::move(outer_field), *u.index()); - replaceNode(&p, std::move(outer_field), __LINE__); - } - - else - // Default treatment for types is to create a corresponding field. - replaceNode(&p, resolveField(u, std::move(tt)), __LINE__); - } - - else if ( auto c = resolved->first->tryAs() ) { - if ( auto ctor = c->value().tryAs() ) - replaceNode(&p, resolveField(u, ctor->ctor()), __LINE__); - else - p.node.addError("field value must be a constant"); - } - else - p.node.addError(hilti::util::fmt("field value must be a constant or type (but is a %s)", - resolved->first->as().displayName())); - } - - else if ( auto c = u.ctor() ) - replaceNode(&p, resolveField(u, *c), __LINE__); - - else if ( auto t = u.type() ) - replaceNode(&p, resolveField(u, *t), __LINE__); - - else if ( auto i = u.item() ) { - auto f = resolveField(u, *i); - replaceNode(&p, f, __LINE__); - } - else - hilti::logger().internalError("no known type for unresolved field", p.node.location()); - } - - void operator()(const type::unit::item::UnresolvedField& u, position_t p) { replaceUnresolvedField(u, p); } - - void operator()(const type::Unit& n, position_t p) { - if ( auto t = p.parent().tryAs(); - ! t && ! p.parent(2).tryAs() ) - replaceNode(&p, hilti::type::UnresolvedID(*n.typeID(), p.node.meta()), __LINE__); - } -}; - -struct Visitor2 : public hilti::visitor::PostOrder { - explicit Visitor2(hilti::Unit* unit) : unit(unit) {} - hilti::Unit* unit; - bool modified = false; - - template - void replaceNode(position_t* p, T&& n, int line) { - p->node = std::forward(n); - HILTI_DEBUG(Resolver, hilti::util::fmt(" modified by Spicy %s:%d", - hilti::rt::filesystem::path(__FILE__).filename().native(), line)) - modified = true; - } - - void operator()(const hilti::expression::Keyword& n, position_t p) { - if ( n.kind() == hilti::expression::keyword::Kind::DollarDollar && ! n.isSet() ) { - std::optional dd; - - if ( auto f = p.findParent() ) { - for ( const auto& p : f->get().type().parameters() ) { - if ( p.id() == ID("__dd") ) { - // Inside a free function that defines a "__dd" parameter; use it. - dd = type::Computed(hilti::builder::id("__dd")); - break; - } - } - } - - if ( ! dd ) { - auto f = p.findParent(); - - if ( ! f ) - return; - - if ( auto t = p.findParent() ) { - // Inside a field's hook. - if ( t->get().isForEach() ) - dd = type::unit::item::Field::vectorElementTypeThroughSelf(f->get().id()); - else - dd = f->get().itemType(); - } - - else if ( auto a = p.findParent() ) { - // Inside an attribute expression. - if ( a->get().tag() == "&until" || a->get().tag() == "&until-including" || - a->get().tag() == "&while" ) - dd = type::unit::item::Field::vectorElementTypeThroughSelf(f->get().id()); - else { - dd = f->get().parseType(); - - if ( auto bf = dd->tryAs() ) - dd = type::UnsignedInteger(bf->width(), bf->meta()); - } - } - } - - if ( dd ) - replaceNode(&p, - hilti::expression::Keyword(hilti::expression::keyword::Kind::DollarDollar, *dd, - p.node.meta()), - __LINE__); - else { - p.node.addError("$$ not supported here"); - return; - } - } - } - - void operator()(const type::Unit& n, position_t p) { - if ( auto t = p.parent().tryAs(); - ! t && ! p.parent(2).tryAs() ) - replaceNode(&p, hilti::type::UnresolvedID(*n.typeID(), p.node.meta()), __LINE__); - } -}; - -} // anonymous namespace - -bool spicy::detail::resolveIDs(hilti::Node* root, hilti::Unit* unit) { - hilti::util::timing::Collector _("spicy/compiler/id-resolver"); - - auto v1 = Visitor1(unit); - for ( auto i : v1.walk(root) ) - v1.dispatch(i); - - auto v2 = Visitor2(unit); - for ( auto i : v2.walk(root) ) - v2.dispatch(i); - - return v1.modified || v2.modified; -} diff --git a/spicy/toolchain/src/compiler/visitors/normalizer.cc b/spicy/toolchain/src/compiler/visitors/normalizer.cc new file mode 100644 index 000000000..614a96162 --- /dev/null +++ b/spicy/toolchain/src/compiler/visitors/normalizer.cc @@ -0,0 +1,220 @@ +// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace spicy; + +namespace hilti::logging::debug { +inline const hilti::logging::DebugStream Normalizer("normalizer"); +} // namespace hilti::logging::debug + +namespace { + +struct Visitor : public hilti::visitor::PreOrder { + explicit Visitor(Node* root) : root(root) {} + Node* root; + bool modified = false; + + // Log debug message recording resolving a epxxression. + void logChange(const Node& old, const Expression& nexpr) { + HILTI_DEBUG(hilti::logging::debug::Normalizer, + hilti::util::fmt("[%s] %s -> expression %s (%s)", old.typename_(), old, nexpr, old.location())); + } + + // Log debug message recording resolving a statement. + void logChange(const Node& old, const Statement& nstmt) { + HILTI_DEBUG(hilti::logging::debug::Normalizer, + hilti::util::fmt("[%s] %s -> statement %s (%s)", old.typename_(), old, nstmt, old.location())); + } + + // Log debug message recording resolving a type. + void logChange(const Node& old, const Type& ntype, const char* msg = "type") { + HILTI_DEBUG(hilti::logging::debug::Normalizer, + hilti::util::fmt("[%s] %s -> %s %s (%s)", old.typename_(), old, msg, ntype, old.location())); + } + + void logChange(const Node& old, const std::string_view msg) { + HILTI_DEBUG(hilti::logging::debug::Normalizer, + hilti::util::fmt("[%s] %s -> %s (%s)", old.typename_(), old, msg, old.location())); + } + + // Log debug message recording resolving a unit item. + void logChange(const Node& old, const type::unit::Item& i) { + HILTI_DEBUG(hilti::logging::debug::Normalizer, + hilti::util::fmt("[%s] %s -> %s (%s)", old.typename_(), old, i, old.location())); + } + + void operator()(const Module& m, position_t p) { + // Because we alias some Spicy types to HILTI types, we need to make + // the HILTI library available. + if ( m.id() == ID("spicy_rt") || m.id() == ID("hilti") ) + return; + + bool have_hilti_import = false; + + for ( const auto& d : m.declarations() ) { + if ( auto i = d.tryAs(); i && i->id() == ID("spicy_rt") ) + have_hilti_import = true; + } + + if ( ! have_hilti_import ) { + // Import "spicy_rt", which uses HILTI syntax, so we need to set + // the parsing extension to ".hlt". We then however process it as + // an Spicy AST, so that it participates in our resolving. + logChange(p.node, "import spicy_rt & hilti"); + p.node.as().add(hilti::builder::import("spicy_rt", ".hlt")); + p.node.as().add(hilti::builder::import("hilti", ".hlt")); + modified = true; + } + } + + void operator()(const hilti::declaration::Type& t, position_t p) { + if ( auto u = t.type().tryAs() ) { + if ( t.linkage() == declaration::Linkage::Public && ! u->isPublic() ) { + logChange(p.node, "set public"); + const_cast(t.type().as()).setPublic(true); + modified = true; + } + + // Create unit property items from global module items where the unit + // does not provide an overriding one. + std::vector ni; + for ( const auto& prop : root->as().moduleProperties({}) ) { + if ( u->propertyItem(prop.id()) ) + continue; + + auto i = type::unit::item::Property(prop.id(), *prop.expression(), {}, true, prop.meta()); + logChange(p.node, hilti::util::fmt("add module-level property %s", prop.id())); + const_cast(t.type().as()).addItems({std::move(i)}); + modified = true; + } + } + } + + void operator()(const Hook& h, position_t p) { + if ( h.unitType() && h.unitField() ) + return; + + // A`%print` hook returns a string as the rendering to print, need + // to adjust its return type, which defaults to void. + if ( h.id().local().str() == "0x25_print" ) { + if ( h.ftype().result().type().isA() ) { + logChange(p.node, "setting %print result to string"); + p.node.as().setResultType(type::Optional(type::String())); + modified = true; + } + } + + // Link hook to its unit type and field. + + NodeRef unit_type_ref = p.findParentRef(); + if ( ! unit_type_ref ) { + // External hook, do name lookuo. + auto ns = h.id().namespace_(); + if ( ! ns ) + return; + + auto resolved = hilti::scope::lookupID(ns, p, "unit type"); + if ( ! resolved ) { + p.node.addError(resolved.error()); + return; + } + + unit_type_ref = resolved->first->as().typeRef(); + } + + assert(unit_type_ref); + + if ( ! h.unitType() ) { + logChange(p.node, unit_type_ref->as(), "unit type"); + p.node.as().setUnitTypeRef(NodeRef(unit_type_ref)); + modified = true; + } + + NodeRef unit_field_ref = p.findParentRef(); + if ( ! unit_field_ref ) { + // External or out-of-line hook. + if ( ! h.id() ) { + p.node.addError("hook name missing"); + return; + } + + unit_field_ref = unit_type_ref->as().itemRefByName(h.id().local()); + if ( ! unit_field_ref ) + // We do not record an error here because we'd need to account + // for %init/%done/etc. We'll leave that to the validator. + return; + + if ( ! unit_field_ref->isA() ) { + p.node.addError(hilti::util::fmt("'%s' is not a unit field", h.id())); + return; + } + } + + assert(unit_field_ref); + + if ( ! h.unitField() ) { + logChange(p.node, unit_field_ref->as()); + p.node.as().setFieldRef(std::move(unit_field_ref)); + modified = true; + } + } + + void operator()(const hilti::expression::Assign& assign, position_t p) { + // Rewrite assignments involving unit fields to use the non-const member operator. + if ( auto member_const = assign.childs().front().tryAs() ) { + auto new_lhs = operator_::unit::MemberNonConst::Operator().instantiate(member_const->operands().copy(), + member_const->meta()); + Expression n = hilti::expression::Assign(new_lhs, assign.source(), assign.meta()); + logChange(p.node, n); + p.node = std::move(n); + modified = true; + return; + } + } + + void operator()(const type::Unit& u, position_t p) { + if ( ! p.node.as().typeID() ) + return; + + if ( ! u.selfRef() ) + type::Unit::setSelf(&p.node); + + const auto& t = p.node.as(); + + if ( ! t.hasFlag(type::Flag::NoInheritScope) ) { + logChange(p.node, "set no-inherit"); + p.node.as().addFlag(type::Flag::NoInheritScope); + modified = true; + } + + if ( t.typeID() && ! u.id() ) { + logChange(p.node, hilti::util::fmt("unit ID %s", *t.typeID())); + p.node.as().setID(*t.typeID()); + modified = true; + } + } +}; + +} // anonymous namespace + +bool spicy::detail::ast::normalize(const std::shared_ptr& ctx, hilti::Node* root, hilti::Unit* unit) { + bool hilti_modified = (*hilti::plugin::registry().hiltiPlugin().ast_normalize)(ctx, root, unit); + + hilti::util::timing::Collector _("spicy/compiler/normalizer"); + + auto v = Visitor(root); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + return v.modified || hilti_modified; +} diff --git a/spicy/toolchain/src/compiler/visitors/operator-resolver.cc b/spicy/toolchain/src/compiler/visitors/operator-resolver.cc deleted file mode 100644 index 6199760f7..000000000 --- a/spicy/toolchain/src/compiler/visitors/operator-resolver.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. - -#include -#include -#include -#include -#include - -#include -#include -#include - -using namespace spicy; -using namespace hilti; - -namespace { - -struct Visitor : public visitor::PostOrder { - Visitor(hilti::Module* module) : module(module) {} - - hilti::Module* module; - bool modified = false; - - Expression argument(const Expression& args, int i) { - auto ctor = args.as().ctor(); - - if ( auto x = ctor.tryAs() ) - ctor = x->coercedCtor(); - - return ctor.as().value()[i]; - } - - template - void replaceNode(position_t& p, T&& n) { - auto x = p.node; - p.node = std::move(n); - p.node.setOriginalNode(module->preserve(x)); - modified = true; - } -}; - -} // anonymous namespace - -bool spicy::detail::resolveOperators(hilti::Node* root, hilti::Unit* unit) { - util::timing::Collector _("spicy/compiler/resolve-operators"); - - auto v = Visitor(&root->as()); - for ( auto i : v.walk(root) ) - v.dispatch(i); - - return v.modified; -} diff --git a/spicy/toolchain/src/compiler/visitors/printer.cc b/spicy/toolchain/src/compiler/visitors/printer.cc index e9f50f0e3..0549630c5 100644 --- a/spicy/toolchain/src/compiler/visitors/printer.cc +++ b/spicy/toolchain/src/compiler/visitors/printer.cc @@ -32,9 +32,9 @@ struct Visitor : hilti::visitor::PreOrder { out << ";" << out.newline(); } - void operator()(const type::Bitfield& n, position_t /* p */) { + void operator()(const type::Bitfield& n, position_t p) { if ( ! out.isExpandSubsequentType() ) { - if ( auto id = n.typeID() ) { + if ( auto id = p.node.as().typeID() ) { out << *id; return; } @@ -60,12 +60,14 @@ struct Visitor : hilti::visitor::PreOrder { } } + void operator()(const type::unit::item::Field& n) { out << n.id(); } + hilti::printer::Stream& out; }; } // anonymous namespace -bool detail::printAST(const hilti::Node& root, hilti::printer::Stream& out) { +bool spicy::detail::ast::print(const hilti::Node& root, hilti::printer::Stream& out) { hilti::util::timing::Collector _("spicy/printer"); return Visitor(out).dispatch(root); diff --git a/spicy/toolchain/src/compiler/visitors/resolver.cc b/spicy/toolchain/src/compiler/visitors/resolver.cc new file mode 100644 index 000000000..70a5b3382 --- /dev/null +++ b/spicy/toolchain/src/compiler/visitors/resolver.cc @@ -0,0 +1,343 @@ +// Copyrights (c) 2020-2021 by the Zeek Project. See LICENSE for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace spicy; + +namespace spicy::logging::debug { +inline const hilti::logging::DebugStream Resolver("resolver"); +inline const hilti::logging::DebugStream Operator("operator"); +} // namespace spicy::logging::debug + +namespace { +// Turns an unresolved field into a resolved field. +template +auto resolveField(const type::unit::item::UnresolvedField& u, T t) { + auto field = type::unit::item::Field(u.fieldID(), std::move(t), u.engine(), u.arguments().copy(), u.repeatCount(), + u.sinks().copy(), u.attributes(), u.condition(), u.hooks().copy(), u.meta()); + + assert(u.index()); + field.setIndex(*u.index()); + return field; +} + +// Helper type to select which type of a unit field we are interested in. +enum class FieldType { + DDType, // type for $$ + ItemType, // final type of the field's value + ParseType, // type that the field is being parsed at +}; + +// 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(); } +}; + +// Helper function to compute one of several kinds of a field's types. +static std::optional _fieldType(const type::unit::item::Field& f, const Type& type, FieldType ft, + bool is_container, Meta meta) { + Type nt; + if ( auto e = FieldTypeVisitor(ft).dispatch(type) ) + nt = std::move(*e); + else + nt = type; + + if ( ! type::isResolved(nt) ) + return {}; + + if ( is_container ) + return type::Vector(std::move(nt), meta); + else + return nt; +} + +struct Visitor : public hilti::visitor::PreOrder { + explicit Visitor(hilti::Unit* unit) : unit(unit) {} + hilti::Unit* unit; + bool modified = false; + +#if 0 + std::set seen; + + void preDispatch(const Node& n, int level) override { + std::string prefix = "# "; + + if ( seen.find(&n) != seen.end() ) + prefix = "! "; + else + seen.insert(&n); + + auto indent = std::string(level * 2, ' '); + std::cerr << prefix << indent << "> " << n.render() << std::endl; + n.scope()->render(std::cerr, " | "); + }; +#endif + + // Log debug message recording resolving a epxxression. + void logChange(const Node& old, const Expression& nexpr) { + HILTI_DEBUG(logging::debug::Resolver, + hilti::util::fmt("[%s] %s -> expression %s (%s)", old.typename_(), old, nexpr, old.location())); + } + + // Log debug message recording resolving a statement. + void logChange(const Node& old, const Statement& nstmt) { + HILTI_DEBUG(logging::debug::Resolver, + hilti::util::fmt("[%s] %s -> statement %s (%s)", old.typename_(), old, nstmt, old.location())); + } + + // Log debug message recording resolving a type. + void logChange(const Node& old, const Type& ntype, const char* msg = "type") { + HILTI_DEBUG(logging::debug::Resolver, + hilti::util::fmt("[%s] %s -> %s %s (%s)", old.typename_(), old, msg, ntype, old.location())); + } + + // Log debug message recording resolving a unit item. + void logChange(const Node& old, const type::unit::Item& i) { + HILTI_DEBUG(logging::debug::Resolver, + hilti::util::fmt("[%s] %s -> %s (%s)", old.typename_(), old, i, old.location())); + } + + void operator()(const Hook& h, position_t p) { + if ( ! h.unitField() || h.ddRef() ) + return; + + std::optional dd; + + if ( h.isForEach() ) { + if ( ! h.unitField()->ddRef() ) + return; + + dd = h.unitField()->ddType(); + if ( ! type::isResolved(*dd) ) + return; + + if ( ! type::isIterable(*dd) ) { + p.node.addError("'foreach' hook can only be used with containers"); + return; + } + + dd = dd->elementType(); + } + else + dd = h.unitField()->itemType(); + + if ( dd.has_value() && type::isResolved(*dd) && ! dd->isA() ) { + logChange(p.node, *dd, "$$ type"); + p.node.as().setDDType(std::move(*dd)); + modified = true; + } + } + + 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(std::move(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(std::move(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()) ) { + logChange(p.node, *t, "parse type"); + p.node.as().setParseType(std::move(*t)); + } + } + + if ( ! type::isResolved(f.ddType()) && type::isResolved(f.parseType()) ) { + if ( auto dd = _fieldType(f, f.originalType(), FieldType::DDType, f.isContainer(), f.meta()) ) { + if ( ! dd->isA() ) { + logChange(p.node, *dd, "$$ type"); + p.node.as().setDDType(std::move(*dd)); + modified = true; + } + } + } + + if ( ! type::isResolved(f.itemType()) && type::isResolved(f.parseType()) ) { + std::optional t; + + if ( auto x = f.convertExpression() ) { + if ( x->second ) { + // Unit-level convert on the sub-item. + auto u = x->second->as(); + auto a = AttributeSet::find(u.attributes(), "&convert"); + assert(a); + auto e = a->valueAsExpression()->get(); + if ( hilti::expression::isResolved(e) ) + t = e.type(); + } + else if ( hilti::expression::isResolved(x->first) ) { + t = x->first.type(); + + // If there's list comprehension, morph the type into a vector. + // Assignment will transparently work. + if ( auto x = t->tryAs() ) + t = hilti::type::Vector(x->elementType(), x->meta()); + } + } + else if ( const auto& i = f.item(); i && i->isA() ) { + const auto& inner_f = i->as(); + t = _fieldType(inner_f, i->itemType(), FieldType::ItemType, f.isContainer(), f.meta()); + } + else + t = _fieldType(f, f.originalType(), FieldType::ItemType, f.isContainer(), f.meta()); + + if ( t ) { + logChange(p.node, *t, "item type"); + p.node.as().setItemType(std::move(*t)); + modified = true; + } + } + } + + void replaceField(position_t* p, type::unit::Item i) { + logChange(p->node, i); + p->node = std::move(i); + modified = true; + } + + void operator()(const type::unit::item::UnresolvedField& u, position_t p) { + if ( const auto& id = u.unresolvedID() ) { // check for unresolved IDs first to overrides the other cases below + auto resolved = hilti::scope::lookupID(*id, p, "field"); + if ( ! resolved ) { + p.node.addError(resolved.error()); + return; + } + + if ( auto t = resolved->first->tryAs() ) { + Type tt = hilti::builder::typeByID(*id); + + // If a unit comes with a &convert attribute, we wrap it into a + // subitem so that we have our recursive machinery available + // (which we don't have for pure types). + if ( auto unit_type = t->type().tryAs(); + unit_type && AttributeSet::has(unit_type->attributes(), "&convert") ) { + auto inner_field = type::unit::item::Field({}, std::move(tt), spicy::Engine::All, + u.arguments().copy(), {}, {}, {}, {}, {}, u.meta()); + inner_field.setIndex(*u.index()); + + auto outer_field = type::unit::item::Field(u.fieldID(), std::move(inner_field), u.engine(), {}, + u.repeatCount(), u.sinks().copy(), u.attributes(), + u.condition(), u.hooks().copy(), u.meta()); + + outer_field.setIndex(*u.index()); + + replaceField(&p, std::move(outer_field)); + } + + else + // Default treatment for types is to create a corresponding field. + replaceField(&p, resolveField(u, NodeRef(resolved->first))); + } + + else if ( auto c = resolved->first->tryAs() ) { + if ( auto ctor = c->value().tryAs() ) + replaceField(&p, resolveField(u, ctor->ctor())); + else + p.node.addError("field value must be a constant"); + } + else + p.node.addError(hilti::util::fmt("field value must be a constant or type (but is a %s)", + resolved->first->as().displayName())); + } + + else if ( auto c = u.ctor() ) + replaceField(&p, resolveField(u, *c)); + + else if ( auto t = u.type() ) { + if ( ! type::isResolved(t) ) + return; + + replaceField(&p, resolveField(u, *t)); + } + + else if ( auto i = u.item() ) + replaceField(&p, resolveField(u, *i)); + + else + hilti::logger().internalError("no known type for unresolved field", p.node.location()); + } +}; + +} // anonymous namespace + +bool spicy::detail::ast::resolve(const std::shared_ptr& ctx, hilti::Node* root, hilti::Unit* unit) { + bool hilti_modified = (*hilti::plugin::registry().hiltiPlugin().ast_resolve)(ctx, root, unit); + + hilti::util::timing::Collector _("spicy/compiler/resolver"); + + auto v = Visitor(unit); + for ( auto i : v.walk(root) ) + v.dispatch(i); + + return v.modified || hilti_modified; +} diff --git a/spicy/toolchain/src/compiler/visitors/scope-builder.cc b/spicy/toolchain/src/compiler/visitors/scope-builder.cc index fe01d2c6b..32dd0458b 100644 --- a/spicy/toolchain/src/compiler/visitors/scope-builder.cc +++ b/spicy/toolchain/src/compiler/visitors/scope-builder.cc @@ -1,30 +1,99 @@ // Copyright (c) 2020-2021 by the Zeek Project. See LICENSE for details. -#include #include +#include +#include +#include #include -#include +#include +#include #include using namespace spicy; -using namespace hilti; namespace { struct Visitor : public hilti::visitor::PostOrder { explicit Visitor(hilti::Unit* unit) : unit(unit) {} hilti::Unit* unit; + + void operator()(const type::Unit& t, position_t p) { + if ( auto d = t.selfRef() ) + p.node.scope()->insert(std::move(d)); + + for ( auto&& x : t.parameterRefs() ) + 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)); + } + + void operator()(const declaration::UnitHook& h, position_t p) { + if ( auto d = h.hook().ddRef() ) + p.node.scope()->insert(std::move(d)); + + if ( auto u = h.hook().unitType() ) { + if ( u->selfRef() ) + p.node.scope()->insert(u->selfRef()); + + for ( auto&& x : u->parameterRefs() ) + p.node.scope()->insert(std::move(x)); + } + } + + void operator()(const Hook& h, position_t p) { + if ( auto d = h.ddRef() ) + p.node.scope()->insert(std::move(d)); + else + // Force the scope lookup to stop here so that we don't find any + // higher-level `$$`, which may have a different type. + p.node.scope()->insertNotFound(ID("__dd")); + + for ( auto&& x : h.ftype().parameterRefs() ) + p.node.scope()->insert(std::move(x)); + + if ( auto u = h.unitType() ) { + if ( u->selfRef() ) + p.node.scope()->insert(u->selfRef()); + + for ( auto&& x : u->parameterRefs() ) + p.node.scope()->insert(std::move(x)); + } + } + + void operator()(const hilti::Attribute& a, position_t p) { + if ( a.tag() == "&until" || a.tag() == "&until-including" || a.tag() == "&while" ) { + auto f = p.findParent(); + if ( ! (f && f->get().isContainer()) ) + return; + + const auto& pt = f->get().parseType(); + if ( ! type::isResolved(pt) ) + return; + + auto dd = hilti::expression::Keyword::createDollarDollarDeclaration(pt.elementType()); + auto n = unit->module().as().preserve(std::move(dd)); + p.node.scope()->insert(std::move(n)); + } + } }; } // anonymous namespace -void spicy::detail::buildScopes(const std::vector>& modules, hilti::Unit* unit) { - util::timing::Collector _("spicy/compiler/scope-builder"); +void spicy::detail::ast::buildScopes(const std::shared_ptr& ctx, hilti::Node* root, hilti::Unit* unit) { + (*hilti::plugin::registry().hiltiPlugin().ast_build_scopes)(ctx, root, unit); - for ( auto& [id, m] : modules ) { - auto v = Visitor(unit); - for ( auto i : v.walk(&*m) ) - v.dispatch(i); - } + hilti::util::timing::Collector _("spicy/compiler/ast/scope-builder"); + + auto v = Visitor(unit); + for ( auto i : v.walk(root) ) + v.dispatch(i); } diff --git a/spicy/toolchain/src/compiler/visitors/validator.cc b/spicy/toolchain/src/compiler/visitors/validator.cc index 85530d571..0c38c4eb2 100644 --- a/spicy/toolchain/src/compiler/visitors/validator.cc +++ b/spicy/toolchain/src/compiler/visitors/validator.cc @@ -5,13 +5,16 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include +#include #include #include @@ -24,10 +27,10 @@ using hilti::util::fmt; namespace { +bool isEnumType(const Type& t, const char* expected_id) { return t.typeID() && *t.typeID() == ID(expected_id); } + // Helper to validate that a type is parseable. hilti::Result isParseableType(Type pt, const type::unit::item::Field& f) { - pt = type::effectiveType(pt); - if ( pt.isA() ) return hilti::Nothing(); @@ -91,8 +94,7 @@ hilti::Result isParseableType(Type pt, const type::unit::item::F auto type = AttributeSet::find(f.attributes(), "&type"); if ( type ) { - if ( auto t = type->valueAs()->type().tryAs(); - ! (t && t->cxxID() && *t->cxxID() == ID("hilti::rt::real::Type")) ) + if ( const auto& t = type->valueAsExpression()->get().type(); ! isEnumType(t, "spicy::RealType") ) return hilti::result::Error("&type attribute must be a spicy::RealType"); } else @@ -108,10 +110,7 @@ hilti::Result isParseableType(Type pt, const type::unit::item::F return hilti::Nothing(); if ( const auto& x = pt.tryAs() ) { - auto dt = x->dereferencedType(); - - if ( dt.originalNode() ) - dt = dt.originalNode()->as(); + const auto& dt = x->dereferencedType(); if ( auto rc = isParseableType(dt, f); ! rc ) return rc; @@ -149,7 +148,8 @@ hilti::Result isParseableType(Type pt, const type::unit::item::F } -struct PreTransformVisitor : public hilti::visitor::PreOrder { +struct Visitor : public hilti::visitor::PreOrder { + int errors = 0; // Record error at location of current node. void error(std::string msg, position_t& p, hilti::node::ErrorPriority priority = hilti::node::ErrorPriority::Normal) { @@ -171,7 +171,27 @@ struct PreTransformVisitor : public hilti::visitor::PreOrder() ) + ops = coerced->expression(); + + if ( auto ctor_ = ops.tryAs() ) { + auto ctor = ctor_->ctor(); + + // If the argument was the result of a coercion unpack its result. + if ( auto x = ctor.tryAs() ) + ctor = x->coercedCtor(); + + if ( auto args = ctor.tryAs(); args && i < args->value().size() ) + return args->value()[i]; + } + + hilti::util::cannot_be_reached(); + } void operator()(const hilti::Module& m, position_t p) { if ( auto version = m.moduleProperty("%spicy-version") ) { @@ -291,7 +311,7 @@ struct PreTransformVisitor : public hilti::visitor::PreOrdertryAs() ) { - auto mt = x->ctor().as().value(); + const auto& mt = x->ctor().as().value(); if ( ! spicy::rt::MIMEType::parse(mt) ) error("%mime-type argument must follow \"main/sub\" form", p); @@ -331,6 +351,18 @@ struct PreTransformVisitor : public hilti::visitor::PreOrdertype(), "spicy::ByteOrder") ) + error(fmt("%byte-order expression must be of spicy::ByteOrder, but is of type %s ", + i.expression()->type()), + p); + } + else error(fmt("unknown property '%s'", i.id().str()), p); } @@ -344,18 +376,21 @@ struct PreTransformVisitor : public hilti::visitor::PreOrder(); - if ( ! decl ) + if ( ! decl || ! decl->get().type().isA() ) + return; + + auto unit = p.findParent(); // note that this can be a different unit than in the decl, when nested + if ( ! unit ) return; - if ( auto unit = decl->get().type().tryAs() ) - _checkHook(*unit, i, decl->get().linkage() == hilti::declaration::Linkage::Public, p); + _checkHook(*unit, i.hook(), decl->get().linkage() == hilti::declaration::Linkage::Public, false, p); } void operator()(const Attribute& a, position_t p) { auto getAttrField = [](position_t p) -> std::optional { try { // Expected parent is AttributeSet whose expected parent is Field. - auto n = p.parent(2); + const auto& n = p.parent(2); return n.tryAs(); } catch ( std::out_of_range& ) { } @@ -377,7 +412,7 @@ struct PreTransformVisitor : public hilti::visitor::PreOrder(); ! x ) { + if ( auto x = a.valueAsExpression(); ! x ) { error(x.error(), p); } @@ -440,8 +475,8 @@ struct PreTransformVisitor : public hilti::visitor::PreOrder(); - e && e->type() != type::unknown && e->type() != type::Bytes() ) + else if ( auto e = a.valueAsExpression(); + e && e->get().type() != type::stream::Iterator() && e->get().type() != type::Bytes() ) error("&parse-from must have an expression of type either bytes or iterator", p); } } @@ -450,8 +485,8 @@ struct PreTransformVisitor : public hilti::visitor::PreOrder(); - e && e->type() != type::unknown && e->type() != type::stream::Iterator() ) + else if ( auto e = a.valueAsExpression(); e && e->get().type() != type::stream::Iterator() && + e->get().type() != type::stream::Iterator() ) error("&parse-at must have an expression of type iterator", p); } } @@ -459,8 +494,8 @@ struct PreTransformVisitor : public hilti::visitor::PreOrder(); e && e->type() != type::unknown && e->type() != type::Bool() ) - error(fmt("&requires expression must be of type bool, but is of type %d ", e->type()), p); + else if ( auto e = a.valueAsExpression(); e && e->get().type() != type::Bool() ) + error(fmt("&requires expression must be of type bool, but is of type %d ", e->get().type()), p); } } @@ -481,18 +516,25 @@ struct PreTransformVisitor : public hilti::visitor::PreOrder(); + auto e = a.valueAsExpression(); if ( ! e ) error(e.error(), p); else { - if ( e->type() != type::unknown && e->type() != type::Bool() ) - error(fmt("&requires expression must be of type bool, but is of type %d ", e->type()), p); + if ( e->get().type() != type::Bool() ) + error(fmt("&requires expression must be of type bool, but is of type %s ", e->get().type()), + p); } } else if ( a.tag() == "&byte-order" ) { - auto e = a.valueAs(); + auto e = a.valueAsExpression(); if ( ! e ) error(e.error(), p); + else { + if ( ! isEnumType(e->get().type(), "spicy::ByteOrder") ) + error(fmt("&byte-order expression must be of spicy::ByteOrder, but is of type %s ", + e->get().type()), + p); + } } else if ( a.tag() == "&convert" ) { if ( ! a.hasValue() ) @@ -506,7 +548,7 @@ struct PreTransformVisitor : public hilti::visitor::PreOrder 1 ) error("unit cannot have more than one %context", p); - if ( const auto& typeId = u.typeID() ) { + if ( const auto& typeId = u.id() ) { const auto& type_name = typeId->local(); for ( const auto& item : u.items() ) if ( auto field = item.tryAs(); field && field->id() == type_name ) @@ -541,8 +583,6 @@ struct PreTransformVisitor : public hilti::visitor::PreOrder(); @@ -666,110 +706,89 @@ struct PreTransformVisitor : public hilti::visitor::PreOrderisPublic(), p); + if ( const auto& ut = u.hook().unitType() ) + _checkHook(*ut, u.hook(), ut->isPublic(), true, p); else error("unknown unit type", p); } - void _checkHook(const type::Unit& unit, const type::unit::item::UnitHook& hook, bool is_public, position_t& p) { + void _checkHook(const type::Unit& unit, const Hook& hook, bool is_public, bool is_external, position_t& p) { // Note: We can't use any of the unit.isX() methods here that depend // on unit.isPublic() being set correctly, as they might not have // happened yet. - auto params = hook.hook().type().parameters(); - auto location = hook.location(); + auto params = hook.ftype().parameters(); + const auto& location = hook.meta().location(); - if ( ! hook.hook().type().result().type().isA() ) + if ( ! hook.ftype().result().type().isA() && hook.id().local().str() != "0x25_print" ) error("hook cannot have a return value", p, location); - if ( hook.id().namespace_() ) + if ( hook.id().namespace_() && ! is_external ) error("hook ID cannot be scoped", p, location); - else { - auto id = hook.id().local().str(); - bool needs_sink_support = false; - if ( id.find(".") != std::string::npos ) - error("cannot use paths in hooks; trigger on the top-level field instead", p, location); + auto id = hook.id().local().str(); + bool needs_sink_support = false; - else if ( hilti::util::startsWith(id, "0x25_") ) { - auto id_readable = hilti::util::replace(hook.id().local().str(), "0x25_", "%"); + if ( id.find(".") != std::string::npos ) + error("cannot use paths in hooks; trigger on the top-level field instead", p, location); - if ( id == "0x25_init" || id == "0x25_done" || id == "0x25_error" || id == "0x25_print" || - id == "0x25_finally" ) { - if ( params.size() != 0 ) - error(fmt("hook '%s' does not take any parameters", id_readable), p, location); - } - - else if ( id == "0x25_gap" ) { - needs_sink_support = true; - if ( params.size() != 2 || params[0].type() != type::UnsignedInteger(64) || - params[1].type() != type::UnsignedInteger(64) ) - error("signature for hook must be: %gap(seq: uint64, len: uint64)", p, location); - } - - else if ( id == "0x25_overlap" ) { - needs_sink_support = true; - if ( params.size() != 3 || params[0].type() != type::UnsignedInteger(64) || - params[1].type() != type::Bytes() || params[2].type() != type::Bytes() ) - error("signature for hook must be: %overlap(seq: uint64, old: bytes, new_: bytes)", p, - location); - } - - else if ( id == "0x25_skipped" ) { - needs_sink_support = true; - if ( params.size() != 1 || params[0].type() != type::UnsignedInteger(64) ) - error("signature for hook must be: %skipped(seq: uint64)", p, location); - } + else if ( hilti::util::startsWith(id, "0x25_") ) { + auto id_readable = hilti::util::replace(hook.id().local().str(), "0x25_", "%"); - else if ( id == "0x25_undelivered" ) { - needs_sink_support = true; - if ( params.size() != 2 || params[0].type() != type::UnsignedInteger(64) || - params[1].type() != type::Bytes() ) - error("signature for hook must be: %undelivered(seq: uint64, data: bytes)", p, location); - } - - else - error(fmt("unknown hook '%s'", id_readable), p, location); - - if ( needs_sink_support && ! is_public ) // don't use supportsSink() here, see above - error(fmt("cannot use hook '%s', unit type does not support sinks because it is not public", - id_readable), - p, location); + if ( id == "0x25_init" || id == "0x25_done" || id == "0x25_error" || id == "0x25_print" || + id == "0x25_finally" ) { + if ( params.size() != 0 ) + error(fmt("hook '%s' does not take any parameters", id_readable), p, location); } - else { - if ( auto i = unit.field(ID(id)); ! i ) - error(fmt("no field '%s' in unit type", id), p, location); - else if ( ! i->isA() ) - error(fmt("'%s' does not support hooks", id), p, location); + + else if ( id == "0x25_gap" ) { + needs_sink_support = true; + if ( params.size() != 2 || params[0].type() != type::UnsignedInteger(64) || + params[1].type() != type::UnsignedInteger(64) ) + error("signature for hook must be: %gap(seq: uint64, len: uint64)", p, location); } - } - } -}; -struct PostTransformVisitor : public hilti::visitor::PreOrder { - void error(std::string msg, position_t& p) { p.node.addError(msg); } -}; + else if ( id == "0x25_overlap" ) { + needs_sink_support = true; + if ( params.size() != 3 || params[0].type() != type::UnsignedInteger(64) || + params[1].type() != type::Bytes() || params[2].type() != type::Bytes() ) + error("signature for hook must be: %overlap(seq: uint64, old: bytes, new_: bytes)", p, location); + } -struct PreservedVisitor : public hilti::visitor::PreOrder { - void error(std::string msg, position_t& p) { p.node.addError(msg); } + else if ( id == "0x25_skipped" ) { + needs_sink_support = true; + if ( params.size() != 1 || params[0].type() != type::UnsignedInteger(64) ) + error("signature for hook must be: %skipped(seq: uint64)", p, location); + } - auto methodArgument(const hilti::expression::ResolvedOperatorBase& o, int i) { - auto ctor = o.op2().as().ctor(); + else if ( id == "0x25_undelivered" ) { + needs_sink_support = true; + if ( params.size() != 2 || params[0].type() != type::UnsignedInteger(64) || + params[1].type() != type::Bytes() ) + error("signature for hook must be: %undelivered(seq: uint64, data: bytes)", p, location); + } - if ( auto x = ctor.tryAs() ) - ctor = x->coercedCtor(); + else + error(fmt("unknown hook '%s'", id_readable), p, location); - return ctor.as().value()[i]; + if ( needs_sink_support && ! is_public ) // don't use supportsSink() here, see above + error(fmt("cannot use hook '%s', unit type does not support sinks because it is not public", + id_readable), + p, location); + } + else { + if ( auto i = unit.itemByName(ID(id)); ! i ) + error(fmt("no field '%s' in unit type", id), p, location); + } } void operator()(const operator_::sink::Connect& n, position_t p) { - if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->supportsSinks() ) + if ( auto x = n.op0().type().tryAs(); x && ! x->supportsSinks() ) error("unit type does not support sinks", p); } void operator()(const operator_::sink::ConnectMIMETypeBytes& n, position_t p) { - if ( auto x = n.op0().type().originalNode()->tryAs() ) { + if ( auto x = n.op0().type().tryAs() ) { if ( ! x->supportsSinks() ) error("unit type does not support sinks", p); @@ -779,7 +798,7 @@ struct PreservedVisitor : public hilti::visitor::PreOrdertryAs() ) { + if ( auto x = n.op0().type().tryAs() ) { if ( ! x->supportsSinks() ) error("unit type does not support sinks", p); @@ -789,76 +808,56 @@ struct PreservedVisitor : public hilti::visitor::PreOrdertryAs(); x && ! x->supportsFilters() ) + if ( auto x = n.op0().type().tryAs(); x && ! x->supportsFilters() ) error("unit type does not support filters", p); - if ( auto y = methodArgument(n, 0) - .type() - .as() - .dereferencedType() - .originalNode() - ->as(); + if ( const auto& y = + methodArgument(n, 0).type().as().dereferencedType().as(); ! y.isFilter() ) error("unit type cannot be a filter, %filter missing", p); } void operator()(const operator_::unit::Forward& n, position_t p) { - if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->isFilter() ) + if ( auto x = n.op0().type().tryAs(); x && ! x->isFilter() ) error("unit type cannot be a filter, %filter missing", p); } void operator()(const operator_::unit::ForwardEod& n, position_t p) { - if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->isFilter() ) + if ( auto x = n.op0().type().tryAs(); x && ! x->isFilter() ) error("unit type cannot be a filter, %filter missing", p); } void operator()(const operator_::unit::Input& n, position_t p) { - if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->usesRandomAccess() ) + if ( auto x = n.op0().type().tryAs(); x && ! x->usesRandomAccess() ) error("use of 'input()' requires unit type to have property `%random-access`", p); } void operator()(const operator_::unit::Offset& n, position_t p) { - if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->usesRandomAccess() ) + if ( auto x = n.op0().type().tryAs(); x && ! x->usesRandomAccess() ) error("use of 'offset()' requires unit type to have property `%random-access`", p); } void operator()(const operator_::unit::SetInput& n, position_t p) { - if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->usesRandomAccess() ) + if ( auto x = n.op0().type().tryAs(); x && ! x->usesRandomAccess() ) error("use of 'set_input()' requires unit type to have property `%random-access`", p); } void operator()(const operator_::unit::Find& n, position_t p) { - if ( auto x = n.op0().type().originalNode()->tryAs(); x && ! x->usesRandomAccess() ) + if ( auto x = n.op0().type().tryAs(); x && ! x->usesRandomAccess() ) error("use of 'find()' requires unit type to have property `%random-access`", p); } }; } // anonymous namespace -void spicy::detail::preTransformValidateAST(Node* root, hilti::Unit* /* unit */, bool* found_errors) { - hilti::util::timing::Collector _("spicy/compiler/validator"); - - auto v = PreTransformVisitor(); - for ( auto i : v.walk(root) ) - v.dispatch(i); +void spicy::detail::ast::validate(const std::shared_ptr& ctx, hilti::Node* root, hilti::Unit* unit) { + { + auto v = Visitor(); + hilti::util::timing::Collector _("spicy/compiler/validator"); - *found_errors = (v.errors > 0); -} - -void spicy::detail::postTransformValidateAST(Node* root, hilti::Unit* /* unit */) { - hilti::util::timing::Collector _("spicy/compiler/validator"); - - auto v = PostTransformVisitor(); - for ( auto i : v.walk(root) ) - v.dispatch(i); -} - -void spicy::detail::preservedValidateAST(std::vector* nodes, hilti::Unit* /* unit */) { - hilti::util::timing::Collector _("spicy/compiler/validator"); - - auto v = PreservedVisitor(); - for ( auto& root : *nodes ) { - for ( auto i : v.walk(&root) ) + for ( auto i : v.walk(root) ) v.dispatch(i); } + + (*hilti::plugin::registry().hiltiPlugin().ast_validate)(ctx, root, unit); } diff --git a/tests/Baseline/hilti.ast.basic-module/output b/tests/Baseline/hilti.ast.basic-module/output index 37df88f3e..5e592bf23 100644 --- a/tests/Baseline/hilti.ast.basic-module/output +++ b/tests/Baseline/hilti.ast.basic-module/output @@ -1,54 +1,49 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[debug/compiler] parsing file "<...>/basic-module.hlt" -[debug/compiler] registering AST for module Foo ("<...>//basic-module.hlt") -[debug/compiler] processing AST, round 1 -[debug/compiler] performing missing imports for module Foo -[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: (none)) -[debug/compiler] modules: Foo +[debug/compiler] parsing file "<...>/basic-module.hlt" as HILTI code +[debug/compiler] registering ".hlt" AST for module Foo ("<...>//basic-module.hlt") +[debug/compiler] resolving units with plugin HILTI: Foo +[debug/compiler] processing ASTs, round 0 [debug/compiler] resetting nodes for module Foo -[debug/compiler] building scopes for all module modules -[debug/compiler] resolving IDs in module Foo +[debug/compiler] [HILTI] building scopes for module Foo +[debug/compiler] [HILTI] normalizing nodes in module Foo +[debug/compiler] [HILTI] coercing nodes in module Foo +[debug/compiler] [HILTI] resolving nodes in module Foo [debug/compiler] -> modified -[debug/compiler] resolving operators in module Foo -[debug/compiler] coercing expressions for Foo -[debug/compiler] processing AST, round 2 -[debug/compiler] performing missing imports for module Foo -[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: (none)) -[debug/compiler] modules: Foo +[debug/compiler] processing ASTs, round 1 [debug/compiler] resetting nodes for module Foo -[debug/compiler] building scopes for all module modules -[debug/compiler] resolving IDs in module Foo -[debug/compiler] resolving operators in module Foo -[debug/compiler] coercing expressions for Foo -[debug/compiler] validating module Foo (post-transform) -[debug/ast-final] # Foo: Final AST -[debug/ast-final] - Module %1 (basic-module.hlt:5:1-11:2) -[debug/ast-final] | Foo -> declaration::Module %6 -[debug/ast-final] | X -> declaration::Type %4 -[debug/ast-final] | foo -> declaration::Function %5 -[debug/ast-final] - ID (basic-module.hlt:5:8) -[debug/ast-final] - statement::Block (basic-module.hlt:5:1-11:2) -[debug/ast-final] - declaration::Type %4 (basic-module.hlt:5:13-7:15) -[debug/ast-final] - ID (basic-module.hlt:7:6) -[debug/ast-final] - type::Bool (basic-module.hlt:7:10) (non-const) (type-id: Foo::X) -[debug/ast-final] - node::None (basic-module.hlt:5:13-7:15) -[debug/ast-final] - declaration::Function %5 (basic-module.hlt:9:1) -[debug/ast-final] - Function "> (basic-module.hlt:9:8) -[debug/ast-final] | bar -> declaration::Parameter %2 -[debug/ast-final] - ID (basic-module.hlt:9:16) -[debug/ast-final] - type::Function (basic-module.hlt:9:8) (non-const) -[debug/ast-final] - type::function::Result (basic-module.hlt:9:9) -[debug/ast-final] - type::String (basic-module.hlt:9:9) (non-const) -[debug/ast-final] - declaration::Parameter %2 (basic-module.hlt:9:20) -[debug/ast-final] - ID (basic-module.hlt:9:25) -[debug/ast-final] - type::Real (basic-module.hlt:9:20) (non-const) -[debug/ast-final] - node::None (basic-module.hlt:5:13-7:15) -[debug/ast-final] - node::None (basic-module.hlt:5:13-7:15) -[debug/ast-final] - node::None (basic-module.hlt:5:13-7:15) -[debug/ast-final] - node::None (basic-module.hlt:5:13-7:15) -[debug/compiler] updated cached AST for module Foo (final: yes, requires_compilation: yes, dependencies: (none)) -[debug/compiler] compiling module Foo to C++ -[debug/compiler] finalizing module Foo +[debug/compiler] [HILTI] building scopes for module Foo +[debug/compiler] [HILTI] normalizing nodes in module Foo +[debug/compiler] [HILTI] coercing nodes in module Foo +[debug/compiler] [HILTI] resolving nodes in module Foo +[debug/ast-final] # [HILTI] Foo: Final AST (round 1) +[debug/ast-final] - Module %4 (basic-module.hlt:5:1-11:2) [@m:XXX] +[debug/ast-final] | Foo -> declaration::Module %6 [canon-id: Foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | X -> declaration::Type %1 [canon-id: Foo::X] (resolved) [@d:XXX] ([@d:XXX]) +[debug/ast-final] | foo -> declaration::Function %2 [canon-id: Foo::foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] - ID (basic-module.hlt:5:8) [@i:XXX] +[debug/ast-final] - statement::Block (basic-module.hlt:5:1-11:2) [@s:XXX] +[debug/ast-final] - declaration::Type %1 (basic-module.hlt:5:13-7:15) [canon-id: Foo::X] (resolved) [@d:XXX] +[debug/ast-final] - ID (basic-module.hlt:7:6) [@i:XXX] +[debug/ast-final] - type::Bool (basic-module.hlt:7:10) (const) (type-id: Foo::X) (resolved) [@t:XXX] +[debug/ast-final] - node::None (basic-module.hlt:5:13-7:15) [@n:XXX] +[debug/ast-final] - declaration::Function %2 (basic-module.hlt:9:1) [canon-id: Foo::foo] [@d:XXX] +[debug/ast-final] | bar -> declaration::Parameter %3 [canon-id: Foo::foo::bar] [@d:XXX] ([@d:XXX]) +[debug/ast-final] - Function "> (basic-module.hlt:9:8) [@f:XXX] +[debug/ast-final] - ID (basic-module.hlt:9:16) [@i:XXX] +[debug/ast-final] - type::Function (basic-module.hlt:9:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::function::Result (basic-module.hlt:9:9) [@t:XXX] +[debug/ast-final] - type::String (basic-module.hlt:9:9) (const) (resolved) [@t:XXX] +[debug/ast-final] - declaration::Parameter %3 (basic-module.hlt:9:20) [canon-id: Foo::foo::bar] [@d:XXX] +[debug/ast-final] - ID (basic-module.hlt:9:25) [@i:XXX] +[debug/ast-final] - type::Real (basic-module.hlt:9:20) (const) (resolved) [@t:XXX] +[debug/ast-final] - node::None (basic-module.hlt:5:13-7:15) [@n:XXX] +[debug/ast-final] - node::None (basic-module.hlt:5:13-7:15) [@n:XXX] +[debug/ast-final] - node::None (basic-module.hlt:5:13-7:15) [@n:XXX] +[debug/ast-final] - node::None (basic-module.hlt:5:13-7:15) [@n:XXX] +[debug/compiler] [HILTI] validating module Foo +[debug/compiler] finalized module Foo +[debug/compiler] compiling module Foo to C++ +[debug/compiler] finalizing module Foo // Begin of Foo (from "<...>/basic-module.hlt") // Compiled by HILTI version X.X.X @@ -62,7 +57,7 @@ namespace __hlt::Foo { HILTI_PRE_INIT(__hlt::Foo::__register_module) -extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } +extern void __hlt::Foo::__register_module() { ::hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } /* __HILTI_LINKER_V1__ {"module":"Foo","namespace":"__hlt::Foo","path":"<...>/basic-module.hlt","version":1} diff --git a/tests/Baseline/hilti.ast.coercion/output b/tests/Baseline/hilti.ast.coercion/output index ff5a99f88..0cd3f275d 100644 --- a/tests/Baseline/hilti.ast.coercion/output +++ b/tests/Baseline/hilti.ast.coercion/output @@ -1,121 +1,173 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[debug/ast-final] # Foo: Final AST -[debug/ast-final] - Module %1 (coercion.hlt:5:1-31:2) -[debug/ast-final] | A -> declaration::GlobalVariable %3 -[debug/ast-final] | B -> declaration::GlobalVariable %4 -[debug/ast-final] | C -> declaration::GlobalVariable %5 -[debug/ast-final] | D -> declaration::GlobalVariable %6 -[debug/ast-final] | E -> declaration::GlobalVariable %11 -[debug/ast-final] | Foo -> declaration::Module %12 -[debug/ast-final] | x -> declaration::Function %7 -[debug/ast-final] | y -> declaration::Function %8 -[debug/ast-final] | z -> declaration::Function %9 -[debug/ast-final] | z2 -> declaration::Function %10 -[debug/ast-final] - ID (coercion.hlt:5:8) -[debug/ast-final] - statement::Block (coercion.hlt:5:1-31:2) -[debug/ast-final] - declaration::GlobalVariable %3 (coercion.hlt:5:13-7:19) -[debug/ast-final] - ID (coercion.hlt:7:13) -[debug/ast-final] - type::Real (coercion.hlt:7:8) (non-const) -[debug/ast-final] - expression::Ctor (coercion.hlt:7:17) (const) -[debug/ast-final] - ctor::Coerced (coercion.hlt:7:17) -[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:7:17) -[debug/ast-final] - ctor::Real (coercion.hlt:7:17) -[debug/ast-final] - declaration::GlobalVariable %4 (coercion.hlt:7:19-8:20) -[debug/ast-final] - ID (coercion.hlt:8:13) -[debug/ast-final] - type::Real (coercion.hlt:8:8) (non-const) -[debug/ast-final] - expression::Ctor (coercion.hlt:8:17) (const) -[debug/ast-final] - ctor::Coerced (coercion.hlt:8:17) -[debug/ast-final] - ctor::SignedInteger (coercion.hlt:8:17) -[debug/ast-final] - ctor::Real (coercion.hlt:8:17) -[debug/ast-final] - declaration::GlobalVariable %5 (coercion.hlt:8:20-9:24) -[debug/ast-final] - ID (coercion.hlt:9:15) -[debug/ast-final] - type::Stream (coercion.hlt:9:8) (non-const) -[debug/ast-final] - expression::Coerced (coercion.hlt:9:19) (non-const) -[debug/ast-final] - expression::Ctor (coercion.hlt:9:19) (non-const) -[debug/ast-final] - ctor::Bytes (coercion.hlt:9:19) -[debug/ast-final] - type::Stream (coercion.hlt:9:8) (non-const) -[debug/ast-final] - declaration::GlobalVariable %6 (coercion.hlt:9:24-10:24) -[debug/ast-final] - ID (coercion.hlt:10:15) -[debug/ast-final] - type::String (coercion.hlt:10:8) (non-const) -[debug/ast-final] - expression::Ctor (coercion.hlt:10:19) (const) -[debug/ast-final] - ctor::String (coercion.hlt:10:19) -[debug/ast-final] - declaration::Function %7 (coercion.hlt:10:24-14:2) -[debug/ast-final] - Function "> (coercion.hlt:12:9-14:2) -[debug/ast-final] - ID (coercion.hlt:12:15) -[debug/ast-final] - type::Function (coercion.hlt:12:9-14:2) (non-const) -[debug/ast-final] - type::function::Result (coercion.hlt:12:10) -[debug/ast-final] - type::Bool (coercion.hlt:12:10) (non-const) -[debug/ast-final] - statement::Block (coercion.hlt:12:19-14:2) -[debug/ast-final] - statement::Return (coercion.hlt:13:5) -[debug/ast-final] - expression::Ctor (coercion.hlt:13:12) (const) -[debug/ast-final] - ctor::Bool (coercion.hlt:13:12) -[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) -[debug/ast-final] - declaration::Function %8 (coercion.hlt:14:2-18:2) -[debug/ast-final] - Function "> (coercion.hlt:16:9-18:2) -[debug/ast-final] - ID (coercion.hlt:16:15) -[debug/ast-final] - type::Function (coercion.hlt:16:9-18:2) (non-const) -[debug/ast-final] - type::function::Result (coercion.hlt:16:10) -[debug/ast-final] - type::Real (coercion.hlt:16:10) (non-const) -[debug/ast-final] - statement::Block (coercion.hlt:16:19-18:2) -[debug/ast-final] - statement::Return (coercion.hlt:17:5) -[debug/ast-final] - expression::Ctor (coercion.hlt:17:12) (const) -[debug/ast-final] - ctor::Coerced (coercion.hlt:17:12) -[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:17:12) -[debug/ast-final] - ctor::Real (coercion.hlt:17:12) -[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) -[debug/ast-final] - declaration::Function %9 (coercion.hlt:18:2-22:2) -[debug/ast-final] - Function "> (coercion.hlt:20:9-22:2) -[debug/ast-final] - ID (coercion.hlt:20:23) -[debug/ast-final] - type::Function (coercion.hlt:20:9-22:2) (non-const) -[debug/ast-final] - type::function::Result (coercion.hlt:20:10) -[debug/ast-final] - type::stream::View (coercion.hlt:20:15) (non-const) -[debug/ast-final] - type::Stream (coercion.hlt:20:15) -[debug/ast-final] - statement::Block (coercion.hlt:20:27-22:2) -[debug/ast-final] - statement::Return (coercion.hlt:21:5) -[debug/ast-final] - expression::Coerced (coercion.hlt:21:12) (non-const) -[debug/ast-final] - expression::ResolvedID (type: stream) (coercion.hlt:21:12) (non-const) -[debug/ast-final] - ID (coercion.hlt:21:12) -[debug/ast-final] - type::stream::View (coercion.hlt:20:15) (non-const) -[debug/ast-final] - type::Stream (coercion.hlt:20:15) -[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) -[debug/ast-final] - declaration::Function %10 (coercion.hlt:22:2-26:2) -[debug/ast-final] - Function "> (coercion.hlt:24:9-26:2) -[debug/ast-final] - ID (coercion.hlt:24:15) -[debug/ast-final] - type::Function (coercion.hlt:24:9-26:2) (non-const) -[debug/ast-final] - type::function::Result (coercion.hlt:24:10) -[debug/ast-final] - type::Void (coercion.hlt:24:10) (non-const) -[debug/ast-final] - statement::Block (coercion.hlt:24:20-26:2) -[debug/ast-final] - statement::Return (coercion.hlt:25:5) -[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) -[debug/ast-final] - declaration::GlobalVariable %11 (coercion.hlt:26:2-28:55) -[debug/ast-final] - ID (coercion.hlt:28:37) -[debug/ast-final] - type::Tuple (coercion.hlt:28:8) (non-const) -[debug/ast-final] - ID (coercion.hlt:28:8) -[debug/ast-final] - type::Real (coercion.hlt:28:14) (non-const) -[debug/ast-final] - ID (coercion.hlt:28:8) -[debug/ast-final] - type::SignedInteger (coercion.hlt:28:20) (non-const) -[debug/ast-final] - ID (coercion.hlt:28:8) -[debug/ast-final] - type::String (coercion.hlt:28:29) (non-const) -[debug/ast-final] - expression::Ctor (coercion.hlt:28:41) (const) -[debug/ast-final] - ctor::Coerced (coercion.hlt:28:41) -[debug/ast-final] - ctor::Tuple (coercion.hlt:28:41) -[debug/ast-final] - expression::Ctor (coercion.hlt:28:42) (const) -[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28:42) -[debug/ast-final] - expression::Ctor (coercion.hlt:28:45) (const) -[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28:45) -[debug/ast-final] - expression::Ctor (coercion.hlt:28:48) (const) -[debug/ast-final] - ctor::String (coercion.hlt:28:48) -[debug/ast-final] - ctor::Tuple (coercion.hlt:28:41) -[debug/ast-final] - expression::Ctor (coercion.hlt:28:42) (const) -[debug/ast-final] - ctor::Coerced (coercion.hlt:28:42) -[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28:42) -[debug/ast-final] - ctor::Real (coercion.hlt:28:42) -[debug/ast-final] - expression::Ctor (coercion.hlt:28:45) (const) -[debug/ast-final] - ctor::Coerced (coercion.hlt:28:45) -[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28:45) -[debug/ast-final] - ctor::SignedInteger (coercion.hlt:28:45) -[debug/ast-final] - expression::Ctor (coercion.hlt:28:48) (const) -[debug/ast-final] - ctor::String (coercion.hlt:28:48) +[debug/ast-final] # [HILTI] Foo: Final AST (round 2) +[debug/ast-final] - Module %10 (coercion.hlt:5:1-31:2) [@m:XXX] +[debug/ast-final] | A -> declaration::GlobalVariable %1 [canon-id: Foo::A] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | B -> declaration::GlobalVariable %2 [canon-id: Foo::B] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | C -> declaration::GlobalVariable %3 [canon-id: Foo::C] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | D -> declaration::GlobalVariable %4 [canon-id: Foo::D] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | E -> declaration::GlobalVariable %9 [canon-id: Foo::E] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Foo -> declaration::Module %13 [canon-id: Foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | x -> declaration::Function %5 [canon-id: Foo::x] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | y -> declaration::Function %6 [canon-id: Foo::y] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | z -> declaration::Function %7 [canon-id: Foo::z] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | z2 -> declaration::Function %8 [canon-id: Foo::z2] [@d:XXX] ([@d:XXX]) +[debug/ast-final] - ID (coercion.hlt:5:8) [@i:XXX] +[debug/ast-final] - statement::Block (coercion.hlt:5:1-31:2) [@s:XXX] +[debug/ast-final] - declaration::GlobalVariable %1 (coercion.hlt:5:13-7:19) [canon-id: Foo::A] [@d:XXX] +[debug/ast-final] - ID (coercion.hlt:7:13) [@i:XXX] +[debug/ast-final] - type::Real (coercion.hlt:7:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:7:17) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::Coerced (coercion.hlt:7:17) [@c:XXX] +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:7:17) [@c:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:7:17) (const) (resolved) [@t:XXX] +[debug/ast-final] - ctor::Real (coercion.hlt:7:17) [@c:XXX] +[debug/ast-final] - type::Real (const) (resolved) [@t:XXX] +[debug/ast-final] - declaration::GlobalVariable %2 (coercion.hlt:7:19-8:20) [canon-id: Foo::B] [@d:XXX] +[debug/ast-final] - ID (coercion.hlt:8:13) [@i:XXX] +[debug/ast-final] - type::Real (coercion.hlt:8:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:8:17) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::Coerced (coercion.hlt:8:17) [@c:XXX] +[debug/ast-final] - ctor::SignedInteger (coercion.hlt:8:17) [@c:XXX] +[debug/ast-final] - type::SignedInteger (coercion.hlt:8:17) (const) (resolved) [@t:XXX] +[debug/ast-final] - ctor::Real (coercion.hlt:8:17) [@c:XXX] +[debug/ast-final] - type::Real (const) (resolved) [@t:XXX] +[debug/ast-final] - declaration::GlobalVariable %3 (coercion.hlt:8:20-9:24) [canon-id: Foo::C] [@d:XXX] +[debug/ast-final] - ID (coercion.hlt:9:15) [@i:XXX] +[debug/ast-final] - type::Stream (coercion.hlt:9:8) (non-const) (resolved) [@t:XXX] +[debug/ast-final] - type::stream::View (coercion.hlt:9:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::stream::Iterator (coercion.hlt:9:8) (non-const) (resolved) [@t:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:9:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Coerced (coercion.hlt:9:19) (non-const) (resolved) [@e:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:9:19) (non-const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::Bytes (coercion.hlt:9:19) [@c:XXX] +[debug/ast-final] - type::Bytes (coercion.hlt:9:19) (non-const) (resolved) [@t:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:9:19) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::bytes::Iterator (coercion.hlt:9:19) (non-const) (resolved) [@t:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:9:19) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::Stream (coercion.hlt:9:8) (non-const) (resolved) [@t:XXX] +[debug/ast-final] - type::stream::View (coercion.hlt:9:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::stream::Iterator (coercion.hlt:9:8) (non-const) (resolved) [@t:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:9:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - declaration::GlobalVariable %4 (coercion.hlt:9:24-10:24) [canon-id: Foo::D] [@d:XXX] +[debug/ast-final] - ID (coercion.hlt:10:15) [@i:XXX] +[debug/ast-final] - type::String (coercion.hlt:10:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:10:19) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::String (coercion.hlt:10:19) [@c:XXX] +[debug/ast-final] - type::String (coercion.hlt:10:19) (const) (resolved) [@t:XXX] +[debug/ast-final] - declaration::Function %5 (coercion.hlt:10:24-14:2) [canon-id: Foo::x] [@d:XXX] +[debug/ast-final] - Function "> (coercion.hlt:12:9-14:2) [@f:XXX] +[debug/ast-final] - ID (coercion.hlt:12:15) [@i:XXX] +[debug/ast-final] - type::Function (coercion.hlt:12:9-14:2) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::function::Result (coercion.hlt:12:10) [@t:XXX] +[debug/ast-final] - type::Bool (coercion.hlt:12:10) (const) (resolved) [@t:XXX] +[debug/ast-final] - statement::Block (coercion.hlt:12:19-14:2) [@s:XXX] +[debug/ast-final] - statement::Return (coercion.hlt:13:5) [@s:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:13:12) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::Bool (coercion.hlt:13:12) [@c:XXX] +[debug/ast-final] - type::Bool (coercion.hlt:13:12) (const) (resolved) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - declaration::Function %6 (coercion.hlt:14:2-18:2) [canon-id: Foo::y] [@d:XXX] +[debug/ast-final] - Function "> (coercion.hlt:16:9-18:2) [@f:XXX] +[debug/ast-final] - ID (coercion.hlt:16:15) [@i:XXX] +[debug/ast-final] - type::Function (coercion.hlt:16:9-18:2) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::function::Result (coercion.hlt:16:10) [@t:XXX] +[debug/ast-final] - type::Real (coercion.hlt:16:10) (const) (resolved) [@t:XXX] +[debug/ast-final] - statement::Block (coercion.hlt:16:19-18:2) [@s:XXX] +[debug/ast-final] - statement::Return (coercion.hlt:17:5) [@s:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:17:12) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::Coerced (coercion.hlt:17:12) [@c:XXX] +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:17:12) [@c:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:17:12) (const) (resolved) [@t:XXX] +[debug/ast-final] - ctor::Real (coercion.hlt:17:12) [@c:XXX] +[debug/ast-final] - type::Real (const) (resolved) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - declaration::Function %7 (coercion.hlt:18:2-22:2) [canon-id: Foo::z] [@d:XXX] +[debug/ast-final] - Function "> (coercion.hlt:20:9-22:2) [@f:XXX] +[debug/ast-final] - ID (coercion.hlt:20:23) [@i:XXX] +[debug/ast-final] - type::Function (coercion.hlt:20:9-22:2) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::function::Result (coercion.hlt:20:10) [@t:XXX] +[debug/ast-final] - type::stream::View (coercion.hlt:20:15) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::stream::Iterator (coercion.hlt:20:15) (non-const) (resolved) [@t:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:20:15) (const) (resolved) [@t:XXX] +[debug/ast-final] - statement::Block (coercion.hlt:20:27-22:2) [@s:XXX] +[debug/ast-final] - statement::Return (coercion.hlt:21:5) [@s:XXX] +[debug/ast-final] - expression::Coerced (coercion.hlt:21:12) (non-const) (resolved) [@e:XXX] +[debug/ast-final] - expression::ResolvedID (type: stream [@t:XXX]) (coercion.hlt:21:12) (non-const) (resolved) [@e:XXX] +[debug/ast-final] - ID (coercion.hlt:21:12) [@i:XXX] +[debug/ast-final] - type::stream::View (coercion.hlt:20:15) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::stream::Iterator (coercion.hlt:20:15) (non-const) (resolved) [@t:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:20:15) (const) (resolved) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - declaration::Function %8 (coercion.hlt:22:2-26:2) [canon-id: Foo::z2] [@d:XXX] +[debug/ast-final] - Function "> (coercion.hlt:24:9-26:2) [@f:XXX] +[debug/ast-final] - ID (coercion.hlt:24:15) [@i:XXX] +[debug/ast-final] - type::Function (coercion.hlt:24:9-26:2) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::function::Result (coercion.hlt:24:10) [@t:XXX] +[debug/ast-final] - type::Void () (const) (resolved) [@t:XXX] +[debug/ast-final] - statement::Block (coercion.hlt:24:20-26:2) [@s:XXX] +[debug/ast-final] - statement::Return (coercion.hlt:25:5) [@s:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - declaration::GlobalVariable %9 (coercion.hlt:26:2-28:55) [canon-id: Foo::E] [@d:XXX] +[debug/ast-final] - ID (coercion.hlt:28:37) [@i:XXX] +[debug/ast-final] - type::Tuple (coercion.hlt:28:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element (coercion.hlt:28:14) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - type::Real (coercion.hlt:28:14) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element (coercion.hlt:28:20) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - type::SignedInteger (coercion.hlt:28:20) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element (coercion.hlt:28:29) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - type::String (coercion.hlt:28:29) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:28:41) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::Coerced (coercion.hlt:28:41) [@c:XXX] +[debug/ast-final] - ctor::Tuple (coercion.hlt:28:41) [@c:XXX] +[debug/ast-final] - type::Tuple (coercion.hlt:28:41) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element (coercion.hlt:28:42) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:28:42) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element (coercion.hlt:28:45) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:28:45) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element (coercion.hlt:28:48) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - type::String (coercion.hlt:28:48) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:28:42) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28:42) [@c:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:28:42) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:28:45) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28:45) [@c:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:28:45) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:28:48) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::String (coercion.hlt:28:48) [@c:XXX] +[debug/ast-final] - type::String (coercion.hlt:28:48) (const) (resolved) [@t:XXX] +[debug/ast-final] - ctor::Tuple (coercion.hlt:28:41) [@c:XXX] +[debug/ast-final] - type::Tuple (coercion.hlt:28:41) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - type::Real (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element (coercion.hlt:28:45) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - type::SignedInteger (coercion.hlt:28:45) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element (coercion.hlt:28:48) [@t:XXX] +[debug/ast-final] - node::None (coercion.hlt:12:9-14:2) [@n:XXX] +[debug/ast-final] - type::String (coercion.hlt:28:48) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:28:42) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::Coerced (coercion.hlt:28:42) [@c:XXX] +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28:42) [@c:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:28:42) (const) (resolved) [@t:XXX] +[debug/ast-final] - ctor::Real (coercion.hlt:28:42) [@c:XXX] +[debug/ast-final] - type::Real (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:28:45) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::Coerced (coercion.hlt:28:45) [@c:XXX] +[debug/ast-final] - ctor::UnsignedInteger (coercion.hlt:28:45) [@c:XXX] +[debug/ast-final] - type::UnsignedInteger (coercion.hlt:28:45) (const) (resolved) [@t:XXX] +[debug/ast-final] - ctor::SignedInteger (coercion.hlt:28:45) [@c:XXX] +[debug/ast-final] - type::SignedInteger (coercion.hlt:28:45) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (coercion.hlt:28:48) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::String (coercion.hlt:28:48) [@c:XXX] +[debug/ast-final] - type::String (coercion.hlt:28:48) (const) (resolved) [@t:XXX] module Foo { global real A = 3; diff --git a/tests/Baseline/hilti.ast.imported-id/output b/tests/Baseline/hilti.ast.imported-id/output index 1423d0348..2c0c339bf 100644 --- a/tests/Baseline/hilti.ast.imported-id/output +++ b/tests/Baseline/hilti.ast.imported-id/output @@ -1,410 +1,149 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[debug/compiler] parsing file "foo.hlt" -[debug/compiler] registering AST for module Foo ("<...>//foo.hlt") -[debug/compiler] processing AST, round 1 -[debug/compiler] performing missing imports for module Foo -[debug/compiler] parsing file "./bar.hlt" -[debug/compiler] loaded module Bar from "./bar.hlt" -[debug/compiler] registering AST for module Bar ("<...>//bar.hlt") -[debug/compiler] updated cached AST for module Bar (final: no, requires_compilation: no, dependencies: Foo) -[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: Bar) -[debug/compiler] performing missing imports for module Bar -[debug/compiler] updated cached AST for module Bar (final: no, requires_compilation: no, dependencies: Foo) -[debug/compiler] modules: Bar, Foo -[debug/compiler] resetting nodes for module Bar +[debug/compiler] parsing file "foo.hlt" as HILTI code +[debug/compiler] registering ".hlt" AST for module Foo ("<...>//foo.hlt") +[debug/compiler] resolving units with plugin HILTI: Foo +[debug/compiler] processing ASTs, round 0 [debug/compiler] resetting nodes for module Foo -[debug/compiler] building scopes for all module modules -[debug/ast-scopes] # Bar: AST with scopes (round 1) -[debug/ast-scopes] - Module %2 (bar.hlt:2:1-11:2) -[debug/ast-scopes] | Bar -> declaration::Module %5 -[debug/ast-scopes] | Bar1 -> declaration::Type %10 -[debug/ast-scopes] | Bar2 -> declaration::Type %11 -[debug/ast-scopes] | Foo -> declaration::ImportedModule %9 -[debug/ast-scopes] | bar -> declaration::Function %12 -[debug/ast-scopes] - ID (bar.hlt:2:8) -[debug/ast-scopes] - statement::Block (bar.hlt:2:1-11:2) -[debug/ast-scopes] - declaration::ImportedModule %9 (bar.hlt:4:1) -[debug/ast-scopes] | Bar -> declaration::ImportedModule %13 -[debug/ast-scopes] | Foo -> declaration::Module %8 -[debug/ast-scopes] | Foo1 -> declaration::Type %14 -[debug/ast-scopes] | Foo2 -> declaration::Type %15 -[debug/ast-scopes] | foo -> declaration::Function %16 -[debug/ast-scopes] - ID (bar.hlt:4:8) -[debug/ast-scopes] - declaration::Type %10 (bar.hlt:6:1) -[debug/ast-scopes] - ID (bar.hlt:6:13) -[debug/ast-scopes] - type::String (bar.hlt:6:20) (non-const) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Type %11 (bar.hlt:7:1) -[debug/ast-scopes] - ID (bar.hlt:7:13) -[debug/ast-scopes] - type::UnresolvedID (bar.hlt:7:1) (non-const) -[debug/ast-scopes] - ID (bar.hlt:7:20) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Function %12 (bar.hlt:9:1) -[debug/ast-scopes] - Function "> (bar.hlt:9:8) -[debug/ast-scopes] | bar -> declaration::Parameter %3 -[debug/ast-scopes] | foo -> declaration::Parameter %4 -[debug/ast-scopes] - ID (bar.hlt:9:16) -[debug/ast-scopes] - type::Function (bar.hlt:9:8) (non-const) -[debug/ast-scopes] - type::function::Result (bar.hlt:9:9) -[debug/ast-scopes] - type::String (bar.hlt:9:9) (non-const) -[debug/ast-scopes] - declaration::Parameter %3 (bar.hlt:9:20) -[debug/ast-scopes] - ID (bar.hlt:9:25) -[debug/ast-scopes] - type::UnresolvedID (bar.hlt:9:20) (non-const) -[debug/ast-scopes] - ID (bar.hlt:9:20) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Parameter %4 (bar.hlt:9:29) -[debug/ast-scopes] - ID (bar.hlt:9:40) -[debug/ast-scopes] - type::UnresolvedID (bar.hlt:9:29) (non-const) -[debug/ast-scopes] - ID (bar.hlt:9:30) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] # Foo: AST with scopes (round 1) -[debug/ast-scopes] - Module %1 (foo.hlt:2:1-11:2) -[debug/ast-scopes] | Bar -> declaration::ImportedModule %13 -[debug/ast-scopes] | Foo -> declaration::Module %8 -[debug/ast-scopes] | Foo1 -> declaration::Type %14 -[debug/ast-scopes] | Foo2 -> declaration::Type %15 -[debug/ast-scopes] | foo -> declaration::Function %16 -[debug/ast-scopes] - ID (foo.hlt:2:8) -[debug/ast-scopes] - statement::Block (foo.hlt:2:1-11:2) -[debug/ast-scopes] - declaration::ImportedModule %13 (foo.hlt:4:1) -[debug/ast-scopes] | Bar -> declaration::Module %5 -[debug/ast-scopes] | Bar1 -> declaration::Type %10 -[debug/ast-scopes] | Bar2 -> declaration::Type %11 -[debug/ast-scopes] | Foo -> declaration::ImportedModule %9 -[debug/ast-scopes] | bar -> declaration::Function %12 -[debug/ast-scopes] - ID (foo.hlt:4:8) -[debug/ast-scopes] - declaration::Type %14 (foo.hlt:6:1) -[debug/ast-scopes] - ID (foo.hlt:6:13) -[debug/ast-scopes] - type::Bool (foo.hlt:6:20) (non-const) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Type %15 (foo.hlt:7:1) -[debug/ast-scopes] - ID (foo.hlt:7:13) -[debug/ast-scopes] - type::UnresolvedID (foo.hlt:7:1) (non-const) -[debug/ast-scopes] - ID (foo.hlt:7:20) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Function %16 (foo.hlt:9:1) -[debug/ast-scopes] - Function "> (foo.hlt:9:8) -[debug/ast-scopes] | bar -> declaration::Parameter %7 -[debug/ast-scopes] | foo -> declaration::Parameter %6 -[debug/ast-scopes] - ID (foo.hlt:9:16) -[debug/ast-scopes] - type::Function (foo.hlt:9:8) (non-const) -[debug/ast-scopes] - type::function::Result (foo.hlt:9:9) -[debug/ast-scopes] - type::String (foo.hlt:9:9) (non-const) -[debug/ast-scopes] - declaration::Parameter %6 (foo.hlt:9:20) -[debug/ast-scopes] - ID (foo.hlt:9:25) -[debug/ast-scopes] - type::UnresolvedID (foo.hlt:9:20) (non-const) -[debug/ast-scopes] - ID (foo.hlt:9:20) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Parameter %7 (foo.hlt:9:29) -[debug/ast-scopes] - ID (foo.hlt:9:40) -[debug/ast-scopes] - type::UnresolvedID (foo.hlt:9:29) (non-const) -[debug/ast-scopes] - ID (foo.hlt:9:30) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/compiler] resolving IDs in module Bar -[debug/resolver] modified by HILTI id-resolver.cc/264 -[debug/resolver] modified by HILTI id-resolver.cc/264 -[debug/resolver] resolved ID Foo::Foo1 (./bar.hlt:7:20) to declaration::Type %14 (foo.hlt:6:1) -[debug/resolver] modified by HILTI id-resolver.cc/104 -[debug/resolver] resolved ID Bar1 (./bar.hlt:9:20) to declaration::Type %10 (bar.hlt:6:1) -[debug/resolver] modified by HILTI id-resolver.cc/104 -[debug/resolver] resolved ID Foo::Foo1 (./bar.hlt:9:30) to declaration::Type %14 (foo.hlt:6:1) -[debug/resolver] modified by HILTI id-resolver.cc/104 -[debug/compiler] -> modified -[debug/compiler] resolving IDs in module Foo -[debug/resolver] modified by HILTI id-resolver.cc/264 -[debug/resolver] modified by HILTI id-resolver.cc/264 -[debug/resolver] resolved ID Bar::Bar1 (foo.hlt:7:20) to declaration::Type %10 (bar.hlt:6:1) -[debug/resolver] modified by HILTI id-resolver.cc/104 -[debug/resolver] resolved ID Foo1 (foo.hlt:9:20) to declaration::Type %14 (foo.hlt:6:1) -[debug/resolver] modified by HILTI id-resolver.cc/104 -[debug/resolver] resolved ID Bar::Bar1 (foo.hlt:9:30) to declaration::Type %10 (bar.hlt:6:1) -[debug/resolver] modified by HILTI id-resolver.cc/104 +[debug/compiler] [HILTI] building scopes for module Foo +[debug/compiler] [HILTI] normalizing nodes in module Foo +[debug/compiler] [HILTI] coercing nodes in module Foo +[debug/compiler] [HILTI] resolving nodes in module Foo +[debug/compiler] parsing file "./bar.hlt" as HILTI code +[debug/compiler] registering ".hlt" AST for module Bar ("<...>//bar.hlt") +[debug/resolver] [hilti::declaration::ImportedModule] import Bar; -> imported (foo.hlt:4:1) +[debug/resolver] [hilti::declaration::Type] setting type ID to Foo::Foo1 (foo.hlt:6:1) +[debug/resolver] [hilti::declaration::Type] setting type ID to Foo::Foo2 (foo.hlt:7:1) +[debug/resolver] [hilti::type::UnresolvedID] Foo1 -> type Foo::Foo1 (foo.hlt:9:20) [debug/compiler] -> modified -[debug/compiler] resolving operators in module Bar -[debug/compiler] resolving operators in module Foo -[debug/compiler] coercing expressions for Bar -[debug/compiler] coercing expressions for Foo -[debug/compiler] processing AST, round 2 -[debug/compiler] performing missing imports for module Bar -[debug/compiler] updated cached AST for module Bar (final: no, requires_compilation: no, dependencies: Foo) -[debug/compiler] performing missing imports for module Foo -[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: Bar) -[debug/compiler] modules: Bar, Foo -[debug/compiler] resetting nodes for module Bar +[debug/compiler] new dependency to process: Bar (".hlt") +[debug/compiler] processing ASTs, round 1 [debug/compiler] resetting nodes for module Foo -[debug/compiler] building scopes for all module modules -[debug/ast-scopes] # Bar: AST with scopes (round 2) -[debug/ast-scopes] - Module %2 (bar.hlt:2:1-11:2) -[debug/ast-scopes] | Bar -> declaration::Module %17 -[debug/ast-scopes] | Bar1 -> declaration::Type %10 -[debug/ast-scopes] | Bar2 -> declaration::Type %11 -[debug/ast-scopes] | Foo -> declaration::ImportedModule %9 -[debug/ast-scopes] | bar -> declaration::Function %12 -[debug/ast-scopes] - ID (bar.hlt:2:8) -[debug/ast-scopes] - statement::Block (bar.hlt:2:1-11:2) -[debug/ast-scopes] - declaration::ImportedModule %9 (bar.hlt:4:1) -[debug/ast-scopes] | Bar -> declaration::ImportedModule %13 -[debug/ast-scopes] | Foo -> declaration::Module %18 -[debug/ast-scopes] | Foo1 -> declaration::Type %14 -[debug/ast-scopes] | Foo2 -> declaration::Type %15 -[debug/ast-scopes] | foo -> declaration::Function %16 -[debug/ast-scopes] - ID (bar.hlt:4:8) -[debug/ast-scopes] - declaration::Type %10 (bar.hlt:6:1) -[debug/ast-scopes] - ID (bar.hlt:6:13) -[debug/ast-scopes] - type::String (bar.hlt:6:20) (non-const) (type-id: Bar::Bar1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Type %11 (bar.hlt:7:1) -[debug/ast-scopes] - ID (bar.hlt:7:13) -[debug/ast-scopes] - type::ResolvedID (type: Foo::Foo1) (bar.hlt:7:1) (non-const) -[debug/ast-scopes] - ID (bar.hlt:7:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Function %12 (bar.hlt:9:1) -[debug/ast-scopes] - Function "> (bar.hlt:9:8) -[debug/ast-scopes] | bar -> declaration::Parameter %3 -[debug/ast-scopes] | foo -> declaration::Parameter %4 -[debug/ast-scopes] - ID (bar.hlt:9:16) -[debug/ast-scopes] - type::Function (bar.hlt:9:8) (non-const) -[debug/ast-scopes] - type::function::Result (bar.hlt:9:9) -[debug/ast-scopes] - type::String (bar.hlt:9:9) (non-const) -[debug/ast-scopes] - declaration::Parameter %3 (bar.hlt:9:20) -[debug/ast-scopes] - ID (bar.hlt:9:25) -[debug/ast-scopes] - type::ResolvedID (type: Bar::Bar1) (bar.hlt:9:20) (non-const) -[debug/ast-scopes] - ID (bar.hlt:9:20) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Parameter %4 (bar.hlt:9:29) -[debug/ast-scopes] - ID (bar.hlt:9:40) -[debug/ast-scopes] - type::ResolvedID (type: Foo::Foo1) (bar.hlt:9:29) (non-const) -[debug/ast-scopes] - ID (bar.hlt:9:29) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] # Foo: AST with scopes (round 2) -[debug/ast-scopes] - Module %1 (foo.hlt:2:1-11:2) -[debug/ast-scopes] | Bar -> declaration::ImportedModule %13 -[debug/ast-scopes] | Foo -> declaration::Module %18 -[debug/ast-scopes] | Foo1 -> declaration::Type %14 -[debug/ast-scopes] | Foo2 -> declaration::Type %15 -[debug/ast-scopes] | foo -> declaration::Function %16 -[debug/ast-scopes] - ID (foo.hlt:2:8) -[debug/ast-scopes] - statement::Block (foo.hlt:2:1-11:2) -[debug/ast-scopes] - declaration::ImportedModule %13 (foo.hlt:4:1) -[debug/ast-scopes] | Bar -> declaration::Module %17 -[debug/ast-scopes] | Bar1 -> declaration::Type %10 -[debug/ast-scopes] | Bar2 -> declaration::Type %11 -[debug/ast-scopes] | Foo -> declaration::ImportedModule %9 -[debug/ast-scopes] | bar -> declaration::Function %12 -[debug/ast-scopes] - ID (foo.hlt:4:8) -[debug/ast-scopes] - declaration::Type %14 (foo.hlt:6:1) -[debug/ast-scopes] - ID (foo.hlt:6:13) -[debug/ast-scopes] - type::Bool (foo.hlt:6:20) (non-const) (type-id: Foo::Foo1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Type %15 (foo.hlt:7:1) -[debug/ast-scopes] - ID (foo.hlt:7:13) -[debug/ast-scopes] - type::ResolvedID (type: Bar::Bar1) (foo.hlt:7:1) (non-const) -[debug/ast-scopes] - ID (foo.hlt:7:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Function %16 (foo.hlt:9:1) -[debug/ast-scopes] - Function "> (foo.hlt:9:8) -[debug/ast-scopes] | bar -> declaration::Parameter %7 -[debug/ast-scopes] | foo -> declaration::Parameter %6 -[debug/ast-scopes] - ID (foo.hlt:9:16) -[debug/ast-scopes] - type::Function (foo.hlt:9:8) (non-const) -[debug/ast-scopes] - type::function::Result (foo.hlt:9:9) -[debug/ast-scopes] - type::String (foo.hlt:9:9) (non-const) -[debug/ast-scopes] - declaration::Parameter %6 (foo.hlt:9:20) -[debug/ast-scopes] - ID (foo.hlt:9:25) -[debug/ast-scopes] - type::ResolvedID (type: Foo::Foo1) (foo.hlt:9:20) (non-const) -[debug/ast-scopes] - ID (foo.hlt:9:20) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Parameter %7 (foo.hlt:9:29) -[debug/ast-scopes] - ID (foo.hlt:9:40) -[debug/ast-scopes] - type::ResolvedID (type: Bar::Bar1) (foo.hlt:9:29) (non-const) -[debug/ast-scopes] - ID (foo.hlt:9:29) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/compiler] resolving IDs in module Bar -[debug/resolver] modified by HILTI id-resolver.cc/264 +[debug/compiler] resetting nodes for module Bar +[debug/compiler] [HILTI] building scopes for module Foo +[debug/compiler] [HILTI] building scopes for module Bar +[debug/compiler] [HILTI] normalizing nodes in module Foo +[debug/compiler] [HILTI] coercing nodes in module Foo +[debug/compiler] [HILTI] resolving nodes in module Foo +[debug/resolver] [hilti::type::UnresolvedID] Foo::Foo2 -> type Bar::Bar1 (foo.hlt:7:1) +[debug/resolver] [hilti::type::UnresolvedID] Bar::Bar1 -> type Bar::Bar1 (foo.hlt:9:29) [debug/compiler] -> modified -[debug/compiler] resolving IDs in module Foo -[debug/resolver] modified by HILTI id-resolver.cc/264 +[debug/compiler] [HILTI] normalizing nodes in module Bar +[debug/compiler] [HILTI] coercing nodes in module Bar +[debug/compiler] [HILTI] resolving nodes in module Bar +[debug/resolver] [hilti::declaration::ImportedModule] import Foo; -> imported (./bar.hlt:4:1) +[debug/resolver] [hilti::declaration::Type] setting type ID to Bar::Bar1 (./bar.hlt:6:1) +[debug/resolver] [hilti::type::UnresolvedID] Foo::Foo1 -> type Foo::Foo1 (./bar.hlt:7:1) +[debug/resolver] [hilti::type::UnresolvedID] Bar1 -> type Bar::Bar1 (./bar.hlt:9:20) +[debug/resolver] [hilti::type::UnresolvedID] Foo::Foo1 -> type Foo::Foo1 (./bar.hlt:9:29) [debug/compiler] -> modified -[debug/compiler] resolving operators in module Bar -[debug/compiler] resolving operators in module Foo -[debug/compiler] coercing expressions for Bar -[debug/compiler] coercing expressions for Foo -[debug/compiler] processing AST, round 3 -[debug/compiler] performing missing imports for module Bar -[debug/compiler] updated cached AST for module Bar (final: no, requires_compilation: no, dependencies: Foo) -[debug/compiler] performing missing imports for module Foo -[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: Bar) -[debug/compiler] modules: Bar, Foo -[debug/compiler] resetting nodes for module Bar +[debug/compiler] processing ASTs, round 2 [debug/compiler] resetting nodes for module Foo -[debug/compiler] building scopes for all module modules -[debug/ast-scopes] # Bar: AST with scopes (round 3) -[debug/ast-scopes] - Module %2 (bar.hlt:2:1-11:2) -[debug/ast-scopes] | Bar -> declaration::Module %19 -[debug/ast-scopes] | Bar1 -> declaration::Type %10 -[debug/ast-scopes] | Bar2 -> declaration::Type %11 -[debug/ast-scopes] | Foo -> declaration::ImportedModule %9 -[debug/ast-scopes] | bar -> declaration::Function %12 -[debug/ast-scopes] - ID (bar.hlt:2:8) -[debug/ast-scopes] - statement::Block (bar.hlt:2:1-11:2) -[debug/ast-scopes] - declaration::ImportedModule %9 (bar.hlt:4:1) -[debug/ast-scopes] | Bar -> declaration::ImportedModule %13 -[debug/ast-scopes] | Foo -> declaration::Module %20 -[debug/ast-scopes] | Foo1 -> declaration::Type %14 -[debug/ast-scopes] | Foo2 -> declaration::Type %15 -[debug/ast-scopes] | foo -> declaration::Function %16 -[debug/ast-scopes] - ID (bar.hlt:4:8) -[debug/ast-scopes] - declaration::Type %10 (bar.hlt:6:1) -[debug/ast-scopes] - ID (bar.hlt:6:13) -[debug/ast-scopes] - type::String (bar.hlt:6:20) (non-const) (type-id: Bar::Bar1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Type %11 (bar.hlt:7:1) -[debug/ast-scopes] - ID (bar.hlt:7:13) -[debug/ast-scopes] - type::Bool (foo.hlt:6:20) (non-const) (type-id: Bar::Bar2) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Function %12 (bar.hlt:9:1) -[debug/ast-scopes] - Function "> (bar.hlt:9:8) -[debug/ast-scopes] | bar -> declaration::Parameter %3 -[debug/ast-scopes] | foo -> declaration::Parameter %4 -[debug/ast-scopes] - ID (bar.hlt:9:16) -[debug/ast-scopes] - type::Function (bar.hlt:9:8) (non-const) -[debug/ast-scopes] - type::function::Result (bar.hlt:9:9) -[debug/ast-scopes] - type::String (bar.hlt:9:9) (non-const) -[debug/ast-scopes] - declaration::Parameter %3 (bar.hlt:9:20) -[debug/ast-scopes] - ID (bar.hlt:9:25) -[debug/ast-scopes] - type::ResolvedID (type: Bar::Bar1) (bar.hlt:9:20) (non-const) -[debug/ast-scopes] - ID (bar.hlt:9:20) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Parameter %4 (bar.hlt:9:29) -[debug/ast-scopes] - ID (bar.hlt:9:40) -[debug/ast-scopes] - type::ResolvedID (type: Foo::Foo1) (bar.hlt:9:29) (non-const) -[debug/ast-scopes] - ID (bar.hlt:9:29) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] # Foo: AST with scopes (round 3) -[debug/ast-scopes] - Module %1 (foo.hlt:2:1-11:2) -[debug/ast-scopes] | Bar -> declaration::ImportedModule %13 -[debug/ast-scopes] | Foo -> declaration::Module %20 -[debug/ast-scopes] | Foo1 -> declaration::Type %14 -[debug/ast-scopes] | Foo2 -> declaration::Type %15 -[debug/ast-scopes] | foo -> declaration::Function %16 -[debug/ast-scopes] - ID (foo.hlt:2:8) -[debug/ast-scopes] - statement::Block (foo.hlt:2:1-11:2) -[debug/ast-scopes] - declaration::ImportedModule %13 (foo.hlt:4:1) -[debug/ast-scopes] | Bar -> declaration::Module %19 -[debug/ast-scopes] | Bar1 -> declaration::Type %10 -[debug/ast-scopes] | Bar2 -> declaration::Type %11 -[debug/ast-scopes] | Foo -> declaration::ImportedModule %9 -[debug/ast-scopes] | bar -> declaration::Function %12 -[debug/ast-scopes] - ID (foo.hlt:4:8) -[debug/ast-scopes] - declaration::Type %14 (foo.hlt:6:1) -[debug/ast-scopes] - ID (foo.hlt:6:13) -[debug/ast-scopes] - type::Bool (foo.hlt:6:20) (non-const) (type-id: Foo::Foo1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Type %15 (foo.hlt:7:1) -[debug/ast-scopes] - ID (foo.hlt:7:13) -[debug/ast-scopes] - type::String (bar.hlt:6:20) (non-const) (type-id: Foo::Foo2) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Function %16 (foo.hlt:9:1) -[debug/ast-scopes] - Function "> (foo.hlt:9:8) -[debug/ast-scopes] | bar -> declaration::Parameter %7 -[debug/ast-scopes] | foo -> declaration::Parameter %6 -[debug/ast-scopes] - ID (foo.hlt:9:16) -[debug/ast-scopes] - type::Function (foo.hlt:9:8) (non-const) -[debug/ast-scopes] - type::function::Result (foo.hlt:9:9) -[debug/ast-scopes] - type::String (foo.hlt:9:9) (non-const) -[debug/ast-scopes] - declaration::Parameter %6 (foo.hlt:9:20) -[debug/ast-scopes] - ID (foo.hlt:9:25) -[debug/ast-scopes] - type::ResolvedID (type: Foo::Foo1) (foo.hlt:9:20) (non-const) -[debug/ast-scopes] - ID (foo.hlt:9:20) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - declaration::Parameter %7 (foo.hlt:9:29) -[debug/ast-scopes] - ID (foo.hlt:9:40) -[debug/ast-scopes] - type::ResolvedID (type: Bar::Bar1) (foo.hlt:9:29) (non-const) -[debug/ast-scopes] - ID (foo.hlt:9:29) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/ast-scopes] - node::None (foo.hlt:6:1) -[debug/compiler] resolving IDs in module Bar -[debug/compiler] resolving IDs in module Foo -[debug/compiler] resolving operators in module Bar -[debug/compiler] resolving operators in module Foo -[debug/compiler] coercing expressions for Bar -[debug/compiler] coercing expressions for Foo -[debug/compiler] validating module Bar (post-transform) -[debug/compiler] validating module Foo (post-transform) -[debug/ast-final] # Foo: Final AST -[debug/ast-final] - Module %1 (foo.hlt:2:1-11:2) -[debug/ast-final] | Bar -> declaration::ImportedModule %13 -[debug/ast-final] | Foo -> declaration::Module %20 -[debug/ast-final] | Foo1 -> declaration::Type %14 -[debug/ast-final] | Foo2 -> declaration::Type %15 -[debug/ast-final] | foo -> declaration::Function %16 -[debug/ast-final] - ID (foo.hlt:2:8) -[debug/ast-final] - statement::Block (foo.hlt:2:1-11:2) -[debug/ast-final] - declaration::ImportedModule %13 (foo.hlt:4:1) -[debug/ast-final] | Bar -> declaration::Module %19 -[debug/ast-final] | Bar1 -> declaration::Type %10 -[debug/ast-final] | Bar2 -> declaration::Type %11 -[debug/ast-final] | Foo -> declaration::ImportedModule %9 -[debug/ast-final] | bar -> declaration::Function %12 -[debug/ast-final] - ID (foo.hlt:4:8) -[debug/ast-final] - declaration::Type %14 (foo.hlt:6:1) -[debug/ast-final] - ID (foo.hlt:6:13) -[debug/ast-final] - type::Bool (foo.hlt:6:20) (non-const) (type-id: Foo::Foo1) -[debug/ast-final] - node::None (foo.hlt:6:1) -[debug/ast-final] - declaration::Type %15 (foo.hlt:7:1) -[debug/ast-final] - ID (foo.hlt:7:13) -[debug/ast-final] - type::String (bar.hlt:6:20) (non-const) (type-id: Foo::Foo2) -[debug/ast-final] - node::None (foo.hlt:6:1) -[debug/ast-final] - declaration::Function %16 (foo.hlt:9:1) -[debug/ast-final] - Function "> (foo.hlt:9:8) -[debug/ast-final] | bar -> declaration::Parameter %7 -[debug/ast-final] | foo -> declaration::Parameter %6 -[debug/ast-final] - ID (foo.hlt:9:16) -[debug/ast-final] - type::Function (foo.hlt:9:8) (non-const) -[debug/ast-final] - type::function::Result (foo.hlt:9:9) -[debug/ast-final] - type::String (foo.hlt:9:9) (non-const) -[debug/ast-final] - declaration::Parameter %6 (foo.hlt:9:20) -[debug/ast-final] - ID (foo.hlt:9:25) -[debug/ast-final] - type::ResolvedID (type: Foo::Foo1) (foo.hlt:9:20) (non-const) -[debug/ast-final] - ID (foo.hlt:9:20) -[debug/ast-final] - node::None (foo.hlt:6:1) -[debug/ast-final] - node::None (foo.hlt:6:1) -[debug/ast-final] - declaration::Parameter %7 (foo.hlt:9:29) -[debug/ast-final] - ID (foo.hlt:9:40) -[debug/ast-final] - type::ResolvedID (type: Bar::Bar1) (foo.hlt:9:29) (non-const) -[debug/ast-final] - ID (foo.hlt:9:29) -[debug/ast-final] - node::None (foo.hlt:6:1) -[debug/ast-final] - node::None (foo.hlt:6:1) -[debug/ast-final] - node::None (foo.hlt:6:1) -[debug/ast-final] - node::None (foo.hlt:6:1) -[debug/compiler] updated cached AST for module Bar (final: yes, requires_compilation: no, dependencies: Foo) -[debug/compiler] updated cached AST for module Foo (final: yes, requires_compilation: yes, dependencies: Bar) -[debug/compiler] compiling module Foo to C++ -[debug/compiler] importing declarations from module Bar -[debug/compiler] finalizing module Foo +[debug/compiler] resetting nodes for module Bar +[debug/compiler] [HILTI] building scopes for module Foo +[debug/compiler] [HILTI] building scopes for module Bar +[debug/compiler] [HILTI] normalizing nodes in module Foo +[debug/compiler] [HILTI] coercing nodes in module Foo +[debug/compiler] [HILTI] resolving nodes in module Foo +[debug/compiler] [HILTI] normalizing nodes in module Bar +[debug/compiler] [HILTI] coercing nodes in module Bar +[debug/compiler] [HILTI] resolving nodes in module Bar +[debug/ast-final] # [HILTI] Foo: Final AST (round 2) +[debug/ast-final] - Module %6 (foo.hlt:2:1-11:2) [@m:XXX] +[debug/ast-final] | Bar -> declaration::ImportedModule %18 [canon-id: Bar] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Foo -> declaration::Module %19 [canon-id: Foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Foo1 -> declaration::Type %1 [canon-id: Foo::Foo1] (resolved) [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Foo2 -> declaration::Type %2 [canon-id: Foo::Foo2] (resolved) [@d:XXX] ([@d:XXX]) +[debug/ast-final] | foo -> declaration::Function %3 [canon-id: Foo::foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] - ID (foo.hlt:2:8) [@i:XXX] +[debug/ast-final] - statement::Block (foo.hlt:2:1-11:2) [@s:XXX] +[debug/ast-final] - declaration::ImportedModule (foo.hlt:4:1) [canon-id: Bar] [@d:XXX] +[debug/ast-final] | Bar -> declaration::Module %21 [canon-id: Bar] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Bar1 -> declaration::Type %12 [canon-id: Bar::Bar1] (resolved) [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Bar2 -> declaration::Type %13 [canon-id: Bar::Bar2] (resolved) [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Foo -> declaration::ImportedModule %20 [canon-id: Foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | bar -> declaration::Function %14 [canon-id: Bar::bar] [@d:XXX] ([@d:XXX]) +[debug/ast-final] - ID (foo.hlt:4:8) [@i:XXX] +[debug/ast-final] - declaration::Type %1 (foo.hlt:6:1) [canon-id: Foo::Foo1] (resolved) [@d:XXX] +[debug/ast-final] - ID (foo.hlt:6:13) [@i:XXX] +[debug/ast-final] - type::Bool (foo.hlt:6:20) (const) (type-id: Foo::Foo1) (resolved) [@t:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - declaration::Type %2 (foo.hlt:7:1) [canon-id: Foo::Foo2] (resolved) [@d:XXX] +[debug/ast-final] - ID (foo.hlt:7:13) [@i:XXX] +[debug/ast-final] - type::String (prune) (bar.hlt:6:20) (const) (type-id: Bar::Bar1) (resolved) [@t:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - declaration::Function %3 (foo.hlt:9:1) [canon-id: Foo::foo] [@d:XXX] +[debug/ast-final] | bar -> declaration::Parameter %5 [canon-id: Foo::foo::bar] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | foo -> declaration::Parameter %4 [canon-id: Foo::foo::foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] - Function "> (foo.hlt:9:8) [@f:XXX] +[debug/ast-final] - ID (foo.hlt:9:16) [@i:XXX] +[debug/ast-final] - type::Function (foo.hlt:9:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::function::Result (foo.hlt:9:9) [@t:XXX] +[debug/ast-final] - type::String (foo.hlt:9:9) (const) (resolved) [@t:XXX] +[debug/ast-final] - declaration::Parameter %4 (foo.hlt:9:20) [canon-id: Foo::foo::foo] [@d:XXX] +[debug/ast-final] - ID (foo.hlt:9:25) [@i:XXX] +[debug/ast-final] - type::Bool (prune) (foo.hlt:6:20) (const) (type-id: Foo::Foo1) (resolved) [@t:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - declaration::Parameter %5 (foo.hlt:9:29) [canon-id: Foo::foo::bar] [@d:XXX] +[debug/ast-final] - ID (foo.hlt:9:40) [@i:XXX] +[debug/ast-final] - type::String (prune) (bar.hlt:6:20) (const) (type-id: Bar::Bar1) (resolved) [@t:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] # [HILTI] Bar: Final AST (round 2) +[debug/ast-final] - Module %8 (bar.hlt:2:1-11:2) [@m:XXX] +[debug/ast-final] | Bar -> declaration::Module %21 [canon-id: Bar] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Bar1 -> declaration::Type %12 [canon-id: Bar::Bar1] (resolved) [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Bar2 -> declaration::Type %13 [canon-id: Bar::Bar2] (resolved) [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Foo -> declaration::ImportedModule %20 [canon-id: Foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | bar -> declaration::Function %14 [canon-id: Bar::bar] [@d:XXX] ([@d:XXX]) +[debug/ast-final] - ID (bar.hlt:2:8) [@i:XXX] +[debug/ast-final] - statement::Block (bar.hlt:2:1-11:2) [@s:XXX] +[debug/ast-final] - declaration::ImportedModule (bar.hlt:4:1) [canon-id: Foo] [@d:XXX] +[debug/ast-final] | Bar -> declaration::ImportedModule %18 [canon-id: Bar] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Foo -> declaration::Module %19 [canon-id: Foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Foo1 -> declaration::Type %1 [canon-id: Foo::Foo1] (resolved) [@d:XXX] ([@d:XXX]) +[debug/ast-final] | Foo2 -> declaration::Type %2 [canon-id: Foo::Foo2] (resolved) [@d:XXX] ([@d:XXX]) +[debug/ast-final] | foo -> declaration::Function %3 [canon-id: Foo::foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] - ID (bar.hlt:4:8) [@i:XXX] +[debug/ast-final] - declaration::Type %12 (bar.hlt:6:1) [canon-id: Bar::Bar1] (resolved) [@d:XXX] +[debug/ast-final] - ID (bar.hlt:6:13) [@i:XXX] +[debug/ast-final] - type::String (bar.hlt:6:20) (const) (type-id: Bar::Bar1) (resolved) [@t:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - declaration::Type %13 (bar.hlt:7:1) [canon-id: Bar::Bar2] (resolved) [@d:XXX] +[debug/ast-final] - ID (bar.hlt:7:13) [@i:XXX] +[debug/ast-final] - type::Bool (prune) (foo.hlt:6:20) (const) (type-id: Foo::Foo1) (resolved) [@t:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - declaration::Function %14 (bar.hlt:9:1) [canon-id: Bar::bar] [@d:XXX] +[debug/ast-final] | bar -> declaration::Parameter %15 [canon-id: Bar::bar::bar] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | foo -> declaration::Parameter %16 [canon-id: Bar::bar::foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] - Function "> (bar.hlt:9:8) [@f:XXX] +[debug/ast-final] - ID (bar.hlt:9:16) [@i:XXX] +[debug/ast-final] - type::Function (bar.hlt:9:8) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::function::Result (bar.hlt:9:9) [@t:XXX] +[debug/ast-final] - type::String (bar.hlt:9:9) (const) (resolved) [@t:XXX] +[debug/ast-final] - declaration::Parameter %15 (bar.hlt:9:20) [canon-id: Bar::bar::bar] [@d:XXX] +[debug/ast-final] - ID (bar.hlt:9:25) [@i:XXX] +[debug/ast-final] - type::String (prune) (bar.hlt:6:20) (const) (type-id: Bar::Bar1) (resolved) [@t:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - declaration::Parameter %16 (bar.hlt:9:29) [canon-id: Bar::bar::foo] [@d:XXX] +[debug/ast-final] - ID (bar.hlt:9:40) [@i:XXX] +[debug/ast-final] - type::Bool (prune) (foo.hlt:6:20) (const) (type-id: Foo::Foo1) (resolved) [@t:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/ast-final] - node::None (foo.hlt:6:1) [@n:XXX] +[debug/compiler] [HILTI] validating module Foo +[debug/compiler] [HILTI] validating module Bar +[debug/compiler] finalized module Foo +[debug/compiler] dependencies: Bar +[debug/compiler] finalized module Bar +[debug/compiler] dependencies: Foo +[debug/compiler] compiling module Foo to C++ +[debug/compiler] importing declarations from module Bar +[debug/compiler] finalizing module Foo // Begin of Foo (from "foo.hlt") // Compiled by HILTI version X.X.X @@ -418,7 +157,7 @@ namespace __hlt::Foo { HILTI_PRE_INIT(__hlt::Foo::__register_module) -extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } +extern void __hlt::Foo::__register_module() { ::hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } /* __HILTI_LINKER_V1__ {"module":"Foo","namespace":"__hlt::Foo","path":"<...>/foo.hlt","version":1} diff --git a/tests/Baseline/hilti.ast.types/output b/tests/Baseline/hilti.ast.types/output index 2ee7cca79..fbe583320 100644 --- a/tests/Baseline/hilti.ast.types/output +++ b/tests/Baseline/hilti.ast.types/output @@ -1,41 +1,52 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[debug/compiler] parsing file "<...>/types.hlt" -[debug/compiler] registering AST for module Foo ("<...>//types.hlt") -[debug/compiler] processing AST, round 1 -[debug/compiler] performing missing imports for module Foo -[debug/compiler] updated cached AST for module Foo (final: no, requires_compilation: yes, dependencies: (none)) -[debug/compiler] modules: Foo +[debug/compiler] parsing file "<...>/types.hlt" as HILTI code +[debug/compiler] registering ".hlt" AST for module Foo ("<...>//types.hlt") +[debug/compiler] resolving units with plugin HILTI: Foo +[debug/compiler] processing ASTs, round 0 [debug/compiler] resetting nodes for module Foo -[debug/compiler] building scopes for all module modules -[debug/compiler] resolving IDs in module Foo -[debug/compiler] resolving operators in module Foo -[debug/compiler] coercing expressions for Foo -[debug/compiler] validating module Foo (post-transform) -[debug/ast-final] # Foo: Final AST -[debug/ast-final] - Module %1 (types.hlt:5:1-10:2) -[debug/ast-final] | Foo -> declaration::Module %2 -[debug/ast-final] | x1 -> declaration::GlobalVariable %3 -[debug/ast-final] | x2 -> declaration::GlobalVariable %4 -[debug/ast-final] - ID (types.hlt:5:8) -[debug/ast-final] - statement::Block (types.hlt:5:1-10:2) -[debug/ast-final] - declaration::GlobalVariable %3 (types.hlt:5:13-7:20) -[debug/ast-final] - ID (types.hlt:7:8) -[debug/ast-final] - node::None (types.hlt:5:13-7:20) -[debug/ast-final] - expression::Ctor (types.hlt:7:13) (non-const) -[debug/ast-final] - ctor::Bytes (types.hlt:7:13) -[debug/ast-final] - declaration::GlobalVariable %4 (types.hlt:7:20-8:20) -[debug/ast-final] - ID (types.hlt:8:8) -[debug/ast-final] - node::None (types.hlt:5:13-7:20) -[debug/ast-final] - expression::Ctor (types.hlt:8:13) (const) -[debug/ast-final] - ctor::Tuple (types.hlt:8:13) -[debug/ast-final] - expression::Ctor (types.hlt:8:14) (const) -[debug/ast-final] - ctor::UnsignedInteger (types.hlt:8:14) -[debug/ast-final] - expression::Ctor (types.hlt:8:17) (const) -[debug/ast-final] - ctor::UnsignedInteger (types.hlt:8:17) -[debug/compiler] updated cached AST for module Foo (final: yes, requires_compilation: yes, dependencies: (none)) +[debug/compiler] [HILTI] building scopes for module Foo +[debug/compiler] [HILTI] normalizing nodes in module Foo +[debug/compiler] [HILTI] coercing nodes in module Foo +[debug/compiler] [HILTI] resolving nodes in module Foo +[debug/ast-final] # [HILTI] Foo: Final AST +[debug/ast-final] - Module %3 (types.hlt:5:1-10:2) [@m:XXX] +[debug/ast-final] | Foo -> declaration::Module %4 [canon-id: Foo] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | x1 -> declaration::GlobalVariable %1 [canon-id: Foo::x1] [@d:XXX] ([@d:XXX]) +[debug/ast-final] | x2 -> declaration::GlobalVariable %2 [canon-id: Foo::x2] [@d:XXX] ([@d:XXX]) +[debug/ast-final] - ID (types.hlt:5:8) [@i:XXX] +[debug/ast-final] - statement::Block (types.hlt:5:1-10:2) [@s:XXX] +[debug/ast-final] - declaration::GlobalVariable %1 (types.hlt:5:13-7:20) [canon-id: Foo::x1] [@d:XXX] +[debug/ast-final] - ID (types.hlt:7:8) [@i:XXX] +[debug/ast-final] - node::None (types.hlt:5:13-7:20) [@n:XXX] +[debug/ast-final] - expression::Ctor (types.hlt:7:13) (non-const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::Bytes (types.hlt:7:13) [@c:XXX] +[debug/ast-final] - type::Bytes (types.hlt:7:13) (non-const) (resolved) [@t:XXX] +[debug/ast-final] - type::UnsignedInteger (types.hlt:7:13) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::bytes::Iterator (types.hlt:7:13) (non-const) (resolved) [@t:XXX] +[debug/ast-final] - type::UnsignedInteger (types.hlt:7:13) (const) (resolved) [@t:XXX] +[debug/ast-final] - declaration::GlobalVariable %2 (types.hlt:7:20-8:20) [canon-id: Foo::x2] [@d:XXX] +[debug/ast-final] - ID (types.hlt:8:8) [@i:XXX] +[debug/ast-final] - node::None (types.hlt:5:13-7:20) [@n:XXX] +[debug/ast-final] - expression::Ctor (types.hlt:8:13) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::Tuple (types.hlt:8:13) [@c:XXX] +[debug/ast-final] - type::Tuple (types.hlt:8:13) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element (types.hlt:8:14) [@t:XXX] +[debug/ast-final] - node::None (types.hlt:5:13-7:20) [@n:XXX] +[debug/ast-final] - type::UnsignedInteger (types.hlt:8:14) (const) (resolved) [@t:XXX] +[debug/ast-final] - type::tuple::Element (types.hlt:8:17) [@t:XXX] +[debug/ast-final] - node::None (types.hlt:5:13-7:20) [@n:XXX] +[debug/ast-final] - type::UnsignedInteger (types.hlt:8:17) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (types.hlt:8:14) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::UnsignedInteger (types.hlt:8:14) [@c:XXX] +[debug/ast-final] - type::UnsignedInteger (types.hlt:8:14) (const) (resolved) [@t:XXX] +[debug/ast-final] - expression::Ctor (types.hlt:8:17) (const) (resolved) [@e:XXX] +[debug/ast-final] - ctor::UnsignedInteger (types.hlt:8:17) [@c:XXX] +[debug/ast-final] - type::UnsignedInteger (types.hlt:8:17) (const) (resolved) [@t:XXX] +[debug/compiler] [HILTI] validating module Foo +[debug/compiler] finalized module Foo module Foo { -global auto x1 = b"abc"; -global auto x2 = (1, 2); +global bytes x1 = b"abc"; +global tuple, uint<64>> x2 = (1, 2); } diff --git a/tests/Baseline/hilti.ast.validation/output b/tests/Baseline/hilti.ast.validation/output index 0d0f89f64..b67d86dad 100644 --- a/tests/Baseline/hilti.ast.validation/output +++ b/tests/Baseline/hilti.ast.validation/output @@ -7,5 +7,5 @@ [error] <...>/validation.hlt:23:5: cannot coerce expression '42' of type 'uint<64>' to type 'void' [error] <...>/validation.hlt:23:5: void function cannot return a value [error] <...>/validation.hlt:31:5: cannot coerce expression '("1" == "2")' of type 'bool' to type 'real' -[error] <...>/validation.hlt:32:10: cannot resolve operator: == > +[error] <...>/validation.hlt:32:10: unsupported operator: == > [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.hiltic.jit.hilti-cxx/error b/tests/Baseline/hilti.hiltic.jit.hilti-cxx/error index 531b0aec4..cacd1efd2 100644 --- a/tests/Baseline/hilti.hiltic.jit.hilti-cxx/error +++ b/tests/Baseline/hilti.hiltic.jit.hilti-cxx/error @@ -1,2 +1,2 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] C++ compiler not available or not functioning (looking for "<...>//exist") +[error] hiltic: C++ compiler not available or not functioning (looking for "<...>//exist") diff --git a/tests/Baseline/hilti.hiltic.print.empty/output b/tests/Baseline/hilti.hiltic.print.empty/output index eca65f6c3..7078b7058 100644 --- a/tests/Baseline/hilti.hiltic.print.empty/output +++ b/tests/Baseline/hilti.hiltic.print.empty/output @@ -12,7 +12,7 @@ namespace __hlt::Foo { HILTI_PRE_INIT(__hlt::Foo::__register_module) -extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } +extern void __hlt::Foo::__register_module() { ::hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } /* __HILTI_LINKER_V1__ {"module":"Foo","namespace":"__hlt::Foo","path":"<...>/empty.hlt","version":1} diff --git a/tests/Baseline/hilti.hiltic.print.globals/output b/tests/Baseline/hilti.hiltic.print.globals/output index 3744369ec..5ba7155cc 100644 --- a/tests/Baseline/hilti.hiltic.print.globals/output +++ b/tests/Baseline/hilti.hiltic.print.globals/output @@ -6,6 +6,20 @@ #include +namespace __hlt::type_info { namespace { + extern const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily; + extern const ::hilti::rt::TypeInfo __ti_hilti_BitOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_Captures; + extern const ::hilti::rt::TypeInfo __ti_hilti_Charset; + extern const ::hilti::rt::TypeInfo __ti_hilti_Exception; + extern const ::hilti::rt::TypeInfo __ti_hilti_MatchState; + extern const ::hilti::rt::TypeInfo __ti_hilti_Protocol; + extern const ::hilti::rt::TypeInfo __ti_hilti_RealType; + extern const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError; + extern const ::hilti::rt::TypeInfo __ti_hilti_Side; +} } + namespace __hlt::Foo { struct __globals_t : hilti::rt::trait::isStruct, hilti::rt::Controllable<__globals_t> { std::string X{}; @@ -14,24 +28,38 @@ namespace __hlt::Foo { inline unsigned int __globals_index; static inline auto __globals() { return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index); } - extern void __init_globals(hilti::rt::Context* ctx); + extern void __init_globals(::hilti::rt::Context* ctx); extern void __init_module(); extern void __register_module(); } +namespace __hlt::type_info { namespace { + const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily = { "hilti::AddressFamily", "hilti::AddressFamily", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IPv4", 0 }, ::hilti::rt::type_info::enum_::Label{ "IPv6", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_BitOrder = { "hilti::BitOrder", "hilti::BitOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "LSB0", 0 }, ::hilti::rt::type_info::enum_::Label{ "MSB0", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder = { "hilti::ByteOrder", "hilti::ByteOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Little", 0 }, ::hilti::rt::type_info::enum_::Label{ "Big", 1 }, ::hilti::rt::type_info::enum_::Label{ "Network", 2 }, ::hilti::rt::type_info::enum_::Label{ "Host", 3 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Captures = { "hilti::Captures", "hilti::Captures", new ::hilti::rt::type_info::Vector(&::hilti::rt::type_info::bytes, ::hilti::rt::type_info::Vector::accessor<::hilti::rt::Bytes>()) }; + const ::hilti::rt::TypeInfo __ti_hilti_Charset = { "hilti::Charset", "hilti::Charset", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "ASCII", 0 }, ::hilti::rt::type_info::enum_::Label{ "UTF8", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Exception = { "hilti::Exception", "hilti::Exception", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_MatchState = { "hilti::MatchState", "hilti::MatchState", new ::hilti::rt::type_info::Struct(std::vector<::hilti::rt::type_info::struct_::Field>({})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Protocol = { "hilti::Protocol", "hilti::Protocol", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "TCP", 0 }, ::hilti::rt::type_info::enum_::Label{ "UDP", 1 }, ::hilti::rt::type_info::enum_::Label{ "ICMP", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RealType = { "hilti::RealType", "hilti::RealType", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IEEE754_Single", 0 }, ::hilti::rt::type_info::enum_::Label{ "IEEE754_Double", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError = { "hilti::RuntimeError", "hilti::RuntimeError", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_Side = { "hilti::Side", "hilti::Side", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Left", 0 }, ::hilti::rt::type_info::enum_::Label{ "Right", 1 }, ::hilti::rt::type_info::enum_::Label{ "Both", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; +} } + HILTI_PRE_INIT(__hlt::Foo::__register_module) -extern void __hlt::Foo::__init_globals(hilti::rt::Context* ctx) { - hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); +extern void __hlt::Foo::__init_globals(::hilti::rt::Context* ctx) { + ::hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); __globals()->X = std::string("Hello, world!"); } extern void __hlt::Foo::__init_module() { __location__("<...>/globals.hlt:12:1"); - hilti::rt::print(Foo::__globals()->X, hilti::rt::Bool(true)); + ::hilti::rt::print(Foo::__globals()->X, ::hilti::rt::Bool(true)); } -extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", &__init_module, &__init_globals, &__globals_index}); } +extern void __hlt::Foo::__register_module() { ::hilti::rt::detail::registerModule({ "Foo", &__init_module, &__init_globals, &__globals_index}); } /* __HILTI_LINKER_V1__ {"module":"Foo","namespace":"__hlt::Foo","path":"<...>/globals.hlt","version":1} diff --git a/tests/Baseline/hilti.hiltic.print.hello-world/output b/tests/Baseline/hilti.hiltic.print.hello-world/output index e9e385bb9..34c0ae487 100644 --- a/tests/Baseline/hilti.hiltic.print.hello-world/output +++ b/tests/Baseline/hilti.hiltic.print.hello-world/output @@ -6,19 +6,47 @@ #include +namespace __hlt::type_info { namespace { + extern const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily; + extern const ::hilti::rt::TypeInfo __ti_hilti_BitOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_Captures; + extern const ::hilti::rt::TypeInfo __ti_hilti_Charset; + extern const ::hilti::rt::TypeInfo __ti_hilti_Exception; + extern const ::hilti::rt::TypeInfo __ti_hilti_MatchState; + extern const ::hilti::rt::TypeInfo __ti_hilti_Protocol; + extern const ::hilti::rt::TypeInfo __ti_hilti_RealType; + extern const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError; + extern const ::hilti::rt::TypeInfo __ti_hilti_Side; +} } + namespace __hlt::Foo { extern void __init_module(); extern void __register_module(); } +namespace __hlt::type_info { namespace { + const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily = { "hilti::AddressFamily", "hilti::AddressFamily", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IPv4", 0 }, ::hilti::rt::type_info::enum_::Label{ "IPv6", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_BitOrder = { "hilti::BitOrder", "hilti::BitOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "LSB0", 0 }, ::hilti::rt::type_info::enum_::Label{ "MSB0", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder = { "hilti::ByteOrder", "hilti::ByteOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Little", 0 }, ::hilti::rt::type_info::enum_::Label{ "Big", 1 }, ::hilti::rt::type_info::enum_::Label{ "Network", 2 }, ::hilti::rt::type_info::enum_::Label{ "Host", 3 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Captures = { "hilti::Captures", "hilti::Captures", new ::hilti::rt::type_info::Vector(&::hilti::rt::type_info::bytes, ::hilti::rt::type_info::Vector::accessor<::hilti::rt::Bytes>()) }; + const ::hilti::rt::TypeInfo __ti_hilti_Charset = { "hilti::Charset", "hilti::Charset", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "ASCII", 0 }, ::hilti::rt::type_info::enum_::Label{ "UTF8", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Exception = { "hilti::Exception", "hilti::Exception", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_MatchState = { "hilti::MatchState", "hilti::MatchState", new ::hilti::rt::type_info::Struct(std::vector<::hilti::rt::type_info::struct_::Field>({})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Protocol = { "hilti::Protocol", "hilti::Protocol", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "TCP", 0 }, ::hilti::rt::type_info::enum_::Label{ "UDP", 1 }, ::hilti::rt::type_info::enum_::Label{ "ICMP", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RealType = { "hilti::RealType", "hilti::RealType", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IEEE754_Single", 0 }, ::hilti::rt::type_info::enum_::Label{ "IEEE754_Double", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError = { "hilti::RuntimeError", "hilti::RuntimeError", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_Side = { "hilti::Side", "hilti::Side", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Left", 0 }, ::hilti::rt::type_info::enum_::Label{ "Right", 1 }, ::hilti::rt::type_info::enum_::Label{ "Both", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; +} } + HILTI_PRE_INIT(__hlt::Foo::__register_module) extern void __hlt::Foo::__init_module() { __location__("<...>/hello-world.hlt:10:1"); - hilti::rt::print(std::string("Hello, world!"), hilti::rt::Bool(true)); + ::hilti::rt::print(std::string("Hello, world!"), ::hilti::rt::Bool(true)); } -extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", &__init_module, nullptr, nullptr}); } +extern void __hlt::Foo::__register_module() { ::hilti::rt::detail::registerModule({ "Foo", &__init_module, nullptr, nullptr}); } /* __HILTI_LINKER_V1__ {"module":"Foo","namespace":"__hlt::Foo","path":"<...>/hello-world.hlt","version":1} diff --git a/tests/Baseline/hilti.hiltic.print.import/output b/tests/Baseline/hilti.hiltic.print.import/output index 10b760a32..b2627cb47 100644 --- a/tests/Baseline/hilti.hiltic.print.import/output +++ b/tests/Baseline/hilti.hiltic.print.import/output @@ -6,6 +6,20 @@ #include +namespace __hlt::type_info { namespace { + extern const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily; + extern const ::hilti::rt::TypeInfo __ti_hilti_BitOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_Captures; + extern const ::hilti::rt::TypeInfo __ti_hilti_Charset; + extern const ::hilti::rt::TypeInfo __ti_hilti_Exception; + extern const ::hilti::rt::TypeInfo __ti_hilti_MatchState; + extern const ::hilti::rt::TypeInfo __ti_hilti_Protocol; + extern const ::hilti::rt::TypeInfo __ti_hilti_RealType; + extern const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError; + extern const ::hilti::rt::TypeInfo __ti_hilti_Side; +} } + namespace __hlt::Bar { struct __globals_t : hilti::rt::trait::isStruct, hilti::rt::Controllable<__globals_t> { std::string bar{}; @@ -27,28 +41,42 @@ namespace __hlt::Foo { return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index); } - extern void __init_globals(hilti::rt::Context* ctx); + extern void __init_globals(::hilti::rt::Context* ctx); extern void __init_module(); extern void __register_module(); } +namespace __hlt::type_info { namespace { + const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily = { "hilti::AddressFamily", "hilti::AddressFamily", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IPv4", 0 }, ::hilti::rt::type_info::enum_::Label{ "IPv6", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_BitOrder = { "hilti::BitOrder", "hilti::BitOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "LSB0", 0 }, ::hilti::rt::type_info::enum_::Label{ "MSB0", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder = { "hilti::ByteOrder", "hilti::ByteOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Little", 0 }, ::hilti::rt::type_info::enum_::Label{ "Big", 1 }, ::hilti::rt::type_info::enum_::Label{ "Network", 2 }, ::hilti::rt::type_info::enum_::Label{ "Host", 3 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Captures = { "hilti::Captures", "hilti::Captures", new ::hilti::rt::type_info::Vector(&::hilti::rt::type_info::bytes, ::hilti::rt::type_info::Vector::accessor<::hilti::rt::Bytes>()) }; + const ::hilti::rt::TypeInfo __ti_hilti_Charset = { "hilti::Charset", "hilti::Charset", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "ASCII", 0 }, ::hilti::rt::type_info::enum_::Label{ "UTF8", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Exception = { "hilti::Exception", "hilti::Exception", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_MatchState = { "hilti::MatchState", "hilti::MatchState", new ::hilti::rt::type_info::Struct(std::vector<::hilti::rt::type_info::struct_::Field>({})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Protocol = { "hilti::Protocol", "hilti::Protocol", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "TCP", 0 }, ::hilti::rt::type_info::enum_::Label{ "UDP", 1 }, ::hilti::rt::type_info::enum_::Label{ "ICMP", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RealType = { "hilti::RealType", "hilti::RealType", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IEEE754_Single", 0 }, ::hilti::rt::type_info::enum_::Label{ "IEEE754_Double", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError = { "hilti::RuntimeError", "hilti::RuntimeError", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_Side = { "hilti::Side", "hilti::Side", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Left", 0 }, ::hilti::rt::type_info::enum_::Label{ "Right", 1 }, ::hilti::rt::type_info::enum_::Label{ "Both", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; +} } + HILTI_PRE_INIT(__hlt::Foo::__register_module) -extern void __hlt::Foo::__init_globals(hilti::rt::Context* ctx) { - hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); +extern void __hlt::Foo::__init_globals(::hilti::rt::Context* ctx) { + ::hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); __globals()->foo = std::string("Foo!"); } extern void __hlt::Foo::__init_module() { __location__("foo.hlt:10:1"); - hilti::rt::print(std::string("Hello, world from Foo!"), hilti::rt::Bool(true)); + ::hilti::rt::print(std::string("Hello, world from Foo!"), ::hilti::rt::Bool(true)); __location__("foo.hlt:11:1"); - hilti::rt::print(Foo::__globals()->foo, hilti::rt::Bool(true)); + ::hilti::rt::print(Foo::__globals()->foo, ::hilti::rt::Bool(true)); __location__("foo.hlt:12:1"); - hilti::rt::print(Bar::__globals()->bar, hilti::rt::Bool(true)); + ::hilti::rt::print(Bar::__globals()->bar, ::hilti::rt::Bool(true)); } -extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", &__init_module, &__init_globals, &__globals_index}); } +extern void __hlt::Foo::__register_module() { ::hilti::rt::detail::registerModule({ "Foo", &__init_module, &__init_globals, &__globals_index}); } /* __HILTI_LINKER_V1__ {"module":"Foo","namespace":"__hlt::Foo","path":"<...>/foo.hlt","version":1} @@ -61,6 +89,20 @@ extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule( #include +namespace __hlt::type_info { namespace { + extern const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily; + extern const ::hilti::rt::TypeInfo __ti_hilti_BitOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_Captures; + extern const ::hilti::rt::TypeInfo __ti_hilti_Charset; + extern const ::hilti::rt::TypeInfo __ti_hilti_Exception; + extern const ::hilti::rt::TypeInfo __ti_hilti_MatchState; + extern const ::hilti::rt::TypeInfo __ti_hilti_Protocol; + extern const ::hilti::rt::TypeInfo __ti_hilti_RealType; + extern const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError; + extern const ::hilti::rt::TypeInfo __ti_hilti_Side; +} } + namespace __hlt::Bar { struct __globals_t : hilti::rt::trait::isStruct, hilti::rt::Controllable<__globals_t> { std::string bar{}; @@ -69,7 +111,7 @@ namespace __hlt::Bar { inline unsigned int __globals_index; static inline auto __globals() { return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index); } - extern void __init_globals(hilti::rt::Context* ctx); + extern void __init_globals(::hilti::rt::Context* ctx); extern void __init_module(); extern void __register_module(); } @@ -87,23 +129,37 @@ namespace __hlt::Foo { } +namespace __hlt::type_info { namespace { + const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily = { "hilti::AddressFamily", "hilti::AddressFamily", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IPv4", 0 }, ::hilti::rt::type_info::enum_::Label{ "IPv6", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_BitOrder = { "hilti::BitOrder", "hilti::BitOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "LSB0", 0 }, ::hilti::rt::type_info::enum_::Label{ "MSB0", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder = { "hilti::ByteOrder", "hilti::ByteOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Little", 0 }, ::hilti::rt::type_info::enum_::Label{ "Big", 1 }, ::hilti::rt::type_info::enum_::Label{ "Network", 2 }, ::hilti::rt::type_info::enum_::Label{ "Host", 3 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Captures = { "hilti::Captures", "hilti::Captures", new ::hilti::rt::type_info::Vector(&::hilti::rt::type_info::bytes, ::hilti::rt::type_info::Vector::accessor<::hilti::rt::Bytes>()) }; + const ::hilti::rt::TypeInfo __ti_hilti_Charset = { "hilti::Charset", "hilti::Charset", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "ASCII", 0 }, ::hilti::rt::type_info::enum_::Label{ "UTF8", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Exception = { "hilti::Exception", "hilti::Exception", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_MatchState = { "hilti::MatchState", "hilti::MatchState", new ::hilti::rt::type_info::Struct(std::vector<::hilti::rt::type_info::struct_::Field>({})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Protocol = { "hilti::Protocol", "hilti::Protocol", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "TCP", 0 }, ::hilti::rt::type_info::enum_::Label{ "UDP", 1 }, ::hilti::rt::type_info::enum_::Label{ "ICMP", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RealType = { "hilti::RealType", "hilti::RealType", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IEEE754_Single", 0 }, ::hilti::rt::type_info::enum_::Label{ "IEEE754_Double", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError = { "hilti::RuntimeError", "hilti::RuntimeError", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_Side = { "hilti::Side", "hilti::Side", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Left", 0 }, ::hilti::rt::type_info::enum_::Label{ "Right", 1 }, ::hilti::rt::type_info::enum_::Label{ "Both", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; +} } + HILTI_PRE_INIT(__hlt::Bar::__register_module) -extern void __hlt::Bar::__init_globals(hilti::rt::Context* ctx) { - hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); +extern void __hlt::Bar::__init_globals(::hilti::rt::Context* ctx) { + ::hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); __globals()->bar = std::string("Bar!"); } extern void __hlt::Bar::__init_module() { __location__("bar.hlt:10:1"); - hilti::rt::print(std::string("Hello, world from Bar!"), hilti::rt::Bool(true)); + ::hilti::rt::print(std::string("Hello, world from Bar!"), ::hilti::rt::Bool(true)); __location__("bar.hlt:11:1"); - hilti::rt::print(Foo::__globals()->foo, hilti::rt::Bool(true)); + ::hilti::rt::print(Foo::__globals()->foo, ::hilti::rt::Bool(true)); __location__("bar.hlt:12:1"); - hilti::rt::print(Bar::__globals()->bar, hilti::rt::Bool(true)); + ::hilti::rt::print(Bar::__globals()->bar, ::hilti::rt::Bool(true)); } -extern void __hlt::Bar::__register_module() { hilti::rt::detail::registerModule({ "Bar", &__init_module, &__init_globals, &__globals_index}); } +extern void __hlt::Bar::__register_module() { ::hilti::rt::detail::registerModule({ "Bar", &__init_module, &__init_globals, &__globals_index}); } /* __HILTI_LINKER_V1__ {"module":"Bar","namespace":"__hlt::Bar","path":"<...>/bar.hlt","version":1} diff --git a/tests/Baseline/hilti.hiltic.print.yield/output b/tests/Baseline/hilti.hiltic.print.yield/output index 2b9d6527d..4f48e942c 100644 --- a/tests/Baseline/hilti.hiltic.print.yield/output +++ b/tests/Baseline/hilti.hiltic.print.yield/output @@ -6,42 +6,70 @@ #include +namespace __hlt::type_info { namespace { + extern const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily; + extern const ::hilti::rt::TypeInfo __ti_hilti_BitOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_Captures; + extern const ::hilti::rt::TypeInfo __ti_hilti_Charset; + extern const ::hilti::rt::TypeInfo __ti_hilti_Exception; + extern const ::hilti::rt::TypeInfo __ti_hilti_MatchState; + extern const ::hilti::rt::TypeInfo __ti_hilti_Protocol; + extern const ::hilti::rt::TypeInfo __ti_hilti_RealType; + extern const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError; + extern const ::hilti::rt::TypeInfo __ti_hilti_Side; +} } + namespace __hlt::Foo { extern void __register_module(); extern auto test(const std::string& x) -> std::string; } namespace hlt::Foo { - extern auto test(const std::string& x) -> hilti::rt::Resumable; + extern auto test(const std::string& x) -> ::hilti::rt::Resumable; } +namespace __hlt::type_info { namespace { + const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily = { "hilti::AddressFamily", "hilti::AddressFamily", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IPv4", 0 }, ::hilti::rt::type_info::enum_::Label{ "IPv6", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_BitOrder = { "hilti::BitOrder", "hilti::BitOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "LSB0", 0 }, ::hilti::rt::type_info::enum_::Label{ "MSB0", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder = { "hilti::ByteOrder", "hilti::ByteOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Little", 0 }, ::hilti::rt::type_info::enum_::Label{ "Big", 1 }, ::hilti::rt::type_info::enum_::Label{ "Network", 2 }, ::hilti::rt::type_info::enum_::Label{ "Host", 3 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Captures = { "hilti::Captures", "hilti::Captures", new ::hilti::rt::type_info::Vector(&::hilti::rt::type_info::bytes, ::hilti::rt::type_info::Vector::accessor<::hilti::rt::Bytes>()) }; + const ::hilti::rt::TypeInfo __ti_hilti_Charset = { "hilti::Charset", "hilti::Charset", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "ASCII", 0 }, ::hilti::rt::type_info::enum_::Label{ "UTF8", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Exception = { "hilti::Exception", "hilti::Exception", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_MatchState = { "hilti::MatchState", "hilti::MatchState", new ::hilti::rt::type_info::Struct(std::vector<::hilti::rt::type_info::struct_::Field>({})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Protocol = { "hilti::Protocol", "hilti::Protocol", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "TCP", 0 }, ::hilti::rt::type_info::enum_::Label{ "UDP", 1 }, ::hilti::rt::type_info::enum_::Label{ "ICMP", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RealType = { "hilti::RealType", "hilti::RealType", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IEEE754_Single", 0 }, ::hilti::rt::type_info::enum_::Label{ "IEEE754_Double", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError = { "hilti::RuntimeError", "hilti::RuntimeError", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_Side = { "hilti::Side", "hilti::Side", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Left", 0 }, ::hilti::rt::type_info::enum_::Label{ "Right", 1 }, ::hilti::rt::type_info::enum_::Label{ "Both", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; +} } + HILTI_PRE_INIT(__hlt::Foo::__register_module) -extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } +extern void __hlt::Foo::__register_module() { ::hilti::rt::detail::registerModule({ "Foo", nullptr, nullptr, nullptr}); } extern auto __hlt::Foo::test(const std::string& x) -> std::string { - hilti::rt::detail::checkStack(); + ::hilti::rt::detail::checkStack(); __location__("<...>/yield.hlt:12:5"); - hilti::rt::print(std::string("HILTI - 1 - argument: "), hilti::rt::Bool(false)); + ::hilti::rt::print(std::string("HILTI - 1 - argument: "), ::hilti::rt::Bool(false)); __location__("<...>/yield.hlt:13:5"); - hilti::rt::print(x, hilti::rt::Bool(true)); + ::hilti::rt::print(x, ::hilti::rt::Bool(true)); __location__("<...>/yield.hlt:15:5"); - hilti::rt::detail::yield(); + ::hilti::rt::detail::yield(); __location__("<...>/yield.hlt:16:5"); - hilti::rt::print(std::string("HILTI - 2"), hilti::rt::Bool(true)); + ::hilti::rt::print(std::string("HILTI - 2"), ::hilti::rt::Bool(true)); __location__("<...>/yield.hlt:17:5"); - hilti::rt::detail::yield(); + ::hilti::rt::detail::yield(); __location__("<...>/yield.hlt:18:5"); - hilti::rt::print(std::string("HILTI - 3"), hilti::rt::Bool(true)); + ::hilti::rt::print(std::string("HILTI - 3"), ::hilti::rt::Bool(true)); __location__("<...>/yield.hlt:19:5"); - hilti::rt::detail::yield(); + ::hilti::rt::detail::yield(); __location__("<...>/yield.hlt:20:5"); - hilti::rt::print(std::string("HILTI - Done"), hilti::rt::Bool(true)); + ::hilti::rt::print(std::string("HILTI - Done"), ::hilti::rt::Bool(true)); __location__("<...>/yield.hlt:22:5"); return std::string("test-result"); } -extern auto hlt::Foo::test(const std::string& x) -> hilti::rt::Resumable { +extern auto hlt::Foo::test(const std::string& x) -> ::hilti::rt::Resumable { auto args = std::make_tuple(::hilti::rt::resumable::detail::copyArg(x)); auto args_on_heap = std::make_shared(std::move(args)); auto cb = [args_on_heap](hilti::rt::resumable::Handle* r) -> hilti::rt::any { diff --git a/tests/Baseline/hilti.hiltic.print.yield/prototypes b/tests/Baseline/hilti.hiltic.print.yield/prototypes index 699239cba..a75c6c63f 100644 --- a/tests/Baseline/hilti.hiltic.print.yield/prototypes +++ b/tests/Baseline/hilti.hiltic.print.yield/prototypes @@ -11,7 +11,7 @@ namespace __hlt::Foo { } namespace hlt::Foo { - extern auto test(const std::string& x) -> hilti::rt::Resumable; + extern auto test(const std::string& x) -> ::hilti::rt::Resumable; } #endif diff --git a/tests/Baseline/hilti.optimization.const/noopt.hlt b/tests/Baseline/hilti.optimization.const/noopt.hlt index 17d55b6b6..4bfcf8977 100644 --- a/tests/Baseline/hilti.optimization.const/noopt.hlt +++ b/tests/Baseline/hilti.optimization.const/noopt.hlt @@ -3,8 +3,8 @@ module Foo { import hilti; -const t = True; -const f = False; +const bool t = True; +const bool f = False; hilti::print(Foo::t, True); hilti::print(Foo::f, True); diff --git a/tests/Baseline/hilti.optimization.const/opt.hlt b/tests/Baseline/hilti.optimization.const/opt.hlt index 9913f1f9f..dcaf436a0 100644 --- a/tests/Baseline/hilti.optimization.const/opt.hlt +++ b/tests/Baseline/hilti.optimization.const/opt.hlt @@ -3,8 +3,8 @@ module Foo { import hilti; -const t = True; -const f = False; +const bool t = True; +const bool f = False; hilti::print(True, True); hilti::print(False, True); diff --git a/tests/Baseline/hilti.optimization.unimplemented_hook/noopt.hlt b/tests/Baseline/hilti.optimization.unimplemented_hook/noopt.hlt index 9c3c8bae5..1c720cf87 100644 --- a/tests/Baseline/hilti.optimization.unimplemented_hook/noopt.hlt +++ b/tests/Baseline/hilti.optimization.unimplemented_hook/noopt.hlt @@ -8,9 +8,9 @@ type X = struct { hook optional> unimplemented_int64(); }; -global auto i = global_unimplemented_int64(); +global optional> i = global_unimplemented_int64(); global X x; -global auto j = Foo::x.unimplemented_int64(); +global optional> j = Foo::x.unimplemented_int64(); declare public function hook void global_unimplemented_void(); declare public function hook void global_implemented(); diff --git a/tests/Baseline/hilti.optimization.unimplemented_hook/opt.hlt b/tests/Baseline/hilti.optimization.unimplemented_hook/opt.hlt index 8ab20c089..76ee128cc 100644 --- a/tests/Baseline/hilti.optimization.unimplemented_hook/opt.hlt +++ b/tests/Baseline/hilti.optimization.unimplemented_hook/opt.hlt @@ -5,9 +5,9 @@ type X = struct { hook void implemented(); }; -global auto i = default>>(); +global optional> i = default>>(); global X x; -global auto j = default>>(); +global optional> j = default>>(); declare public function hook void global_implemented(); diff --git a/tests/Baseline/hilti.optimization.unused_function/noopt.hlt b/tests/Baseline/hilti.optimization.unused_function/noopt.hlt index 89510d15a..3aeb2205a 100644 --- a/tests/Baseline/hilti.optimization.unused_function/noopt.hlt +++ b/tests/Baseline/hilti.optimization.unused_function/noopt.hlt @@ -1,7 +1,7 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. module Foo { -global auto x = private_used(); +global bool x = private_used(); function bool private_unused() { return False; diff --git a/tests/Baseline/hilti.optimization.unused_function/opt.hlt b/tests/Baseline/hilti.optimization.unused_function/opt.hlt index 01e8d1e72..cbeec9093 100644 --- a/tests/Baseline/hilti.optimization.unused_function/opt.hlt +++ b/tests/Baseline/hilti.optimization.unused_function/opt.hlt @@ -1,7 +1,7 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. module Foo { -global auto x = private_used(); +global bool x = private_used(); function bool private_used() { return False; diff --git a/tests/Baseline/hilti.types.enum.equal-ops-fail/output b/tests/Baseline/hilti.types.enum.equal-ops-fail/output index a3882af4d..fa24f8b25 100644 --- a/tests/Baseline/hilti.types.enum.equal-ops-fail/output +++ b/tests/Baseline/hilti.types.enum.equal-ops-fail/output @@ -1,4 +1,4 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/equal-ops-fail.hlt:11:8: cannot resolve operator: != -[error] <...>/equal-ops-fail.hlt:15:8: cannot resolve operator: != +[error] <...>/equal-ops-fail.hlt:11:8: unsupported operator: != +[error] <...>/equal-ops-fail.hlt:15:8: unsupported operator: != [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.function.hook-priorities-fail/.stderr b/tests/Baseline/hilti.types.function.hook-priorities-fail/.stderr index 50e7c7eff..57db22b81 100644 --- a/tests/Baseline/hilti.types.function.hook-priorities-fail/.stderr +++ b/tests/Baseline/hilti.types.function.hook-priorities-fail/.stderr @@ -1,5 +1,5 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/hook-priorities-fail.hlt:10:10: value for attribute '&priority' must be an integer -[error] <...>/hook-priorities-fail.hlt:11:10: attribute '&priority' requires an integer +[error] <...>/hook-priorities-fail.hlt:10:5: value for attribute '&priority' must be an integer +[error] <...>/hook-priorities-fail.hlt:11:5: attribute '&priority' requires an integer [error] <...>/hook-priorities-fail.hlt:12:9: only hooks can have priorities [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.function.overloading/output b/tests/Baseline/hilti.types.function.overloading/output new file mode 100644 index 000000000..5ebd2b50c --- /dev/null +++ b/tests/Baseline/hilti.types.function.overloading/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. +("int64", 42) +("string", "foo") diff --git a/tests/Baseline/hilti.types.integer.mixed-operands-overflow/output.log b/tests/Baseline/hilti.types.integer.mixed-operands-overflow/output.log index c3766684c..8165d4b12 100644 --- a/tests/Baseline/hilti.types.integer.mixed-operands-overflow/output.log +++ b/tests/Baseline/hilti.types.integer.mixed-operands-overflow/output.log @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/mixed-operands-overflow.hlt:7:6: cannot resolve operator: > + > +[error] <...>/mixed-operands-overflow.hlt:7:6: unsupported operator: > + > [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.list.ctor-fail/output b/tests/Baseline/hilti.types.list.ctor-fail/output index ab537b5cd..1bba34d2f 100644 --- a/tests/Baseline/hilti.types.list.ctor-fail/output +++ b/tests/Baseline/hilti.types.list.ctor-fail/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/ctor-fail.hlt:8:15: type mismatch in list elements +[error] <...>/ctor-fail.hlt:8:15: list elements have inconsistent types [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.map.ctor-fail/output b/tests/Baseline/hilti.types.map.ctor-fail/output index 78549d6b2..f649e36a1 100644 --- a/tests/Baseline/hilti.types.map.ctor-fail/output +++ b/tests/Baseline/hilti.types.map.ctor-fail/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/ctor-fail.hlt:6:1: type mismatch in map keys +[error] <...>/ctor-fail.hlt:6:1: inconsistent key types in map [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.real.nops/output b/tests/Baseline/hilti.types.real.nops/output index 42e393fe3..e6e8fe7a8 100644 --- a/tests/Baseline/hilti.types.real.nops/output +++ b/tests/Baseline/hilti.types.real.nops/output @@ -23,15 +23,15 @@ namespace __hlt::Foo { inline unsigned int __globals_index; static inline auto __globals() { return hilti::rt::detail::moduleGlobals<__globals_t>(__globals_index); } - extern void __init_globals(hilti::rt::Context* ctx); + extern void __init_globals(::hilti::rt::Context* ctx); extern void __init_module(); extern void __register_module(); } HILTI_PRE_INIT(__hlt::Foo::__register_module) -extern void __hlt::Foo::__init_globals(hilti::rt::Context* ctx) { - hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); +extern void __hlt::Foo::__init_globals(::hilti::rt::Context* ctx) { + ::hilti::rt::detail::initModuleGlobals<__globals_t>(__globals_index); __globals()->r = 0x1.999999999999ap-4; } @@ -42,7 +42,7 @@ extern void __hlt::Foo::__init_module() { } } -extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", &__init_module, &__init_globals, &__globals_index}); } +extern void __hlt::Foo::__register_module() { ::hilti::rt::detail::registerModule({ "Foo", &__init_module, &__init_globals, &__globals_index}); } /* __HILTI_LINKER_V1__ {"module":"Foo","namespace":"__hlt::Foo","path":"<...>/nops.hlt","version":1} diff --git a/tests/Baseline/hilti.types.set.ctor-fail/output b/tests/Baseline/hilti.types.set.ctor-fail/output index 671728f1a..b56e14416 100644 --- a/tests/Baseline/hilti.types.set.ctor-fail/output +++ b/tests/Baseline/hilti.types.set.ctor-fail/output @@ -1,3 +1,4 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. [error] <...>/ctor-fail.hlt:6:1: type mismatch in set elements +[error] <...>/ctor-fail.hlt:6:1: set elements have inconsistent types [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.canonical-ids/output b/tests/Baseline/hilti.types.struct.canonical-ids/output new file mode 100644 index 000000000..e164b2ef0 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.canonical-ids/output @@ -0,0 +1,20 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[debug/ast-declarations] # [HILTI] Foo +[debug/ast-declarations] - Expression "self" (Foo::S::self) +[debug/ast-declarations] - Field "test" (Foo::S::test) +[debug/ast-declarations] - Parameter "A" (Foo::S::test::A) +[debug/ast-declarations] - Parameter "B" (Foo::S::test::B) +[debug/ast-declarations] - Parameter "A" (Foo::S::test::A) +[debug/ast-declarations] - Parameter "B" (Foo::S::test::B) +[debug/ast-declarations] - Expression "self" (Foo::::self) +[debug/ast-declarations] - Field "foo" (Foo::::foo) +[debug/ast-declarations] - Field "bar" (Foo::::bar) +[debug/ast-declarations] - Type "S" (Foo::S) +[debug/ast-declarations] - Expression "self" (Foo::S::self) +[debug/ast-declarations] - Field "test" (Foo::S::test) +[debug/ast-declarations] - Parameter "A" (Foo::S::test::A) +[debug/ast-declarations] - Parameter "B" (Foo::S::test::B) +[debug/ast-declarations] - Function "S::test" (Foo::S::test) +[debug/ast-declarations] - Parameter "A" (Foo::S::test::A) +[debug/ast-declarations] - Parameter "B" (Foo::S::test::B) +[debug/ast-declarations] - GlobalVariable "s1" (Foo::s1) diff --git a/tests/Baseline/hilti.types.struct.errors-2/output b/tests/Baseline/hilti.types.struct.errors-2/output new file mode 100644 index 000000000..2ed26e09f --- /dev/null +++ b/tests/Baseline/hilti.types.struct.errors-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] <...>/errors.hlt:12:1: field 's' is not &optional +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.errors-method-2/output b/tests/Baseline/hilti.types.struct.errors-method-2/output new file mode 100644 index 000000000..ced72c5e4 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.errors-method-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] <...>/errors-method.hlt:6:34: unknown ID 'self' +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.errors-method/output b/tests/Baseline/hilti.types.struct.errors-method/output index b6b1e811b..53156c531 100644 --- a/tests/Baseline/hilti.types.struct.errors-method/output +++ b/tests/Baseline/hilti.types.struct.errors-method/output @@ -3,5 +3,4 @@ [error] <...>/errors-method.hlt:16:1: type X does not have a method 'y' [error] <...>/errors-method.hlt:17:1: X::s is not a method [error] <...>/errors-method.hlt:18:1: method lacks a type namespace -[error] <...>/errors-method.hlt:20:34: unknown ID 'self' [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.errors/output b/tests/Baseline/hilti.types.struct.errors/output index 05acb409e..ecb22ef45 100644 --- a/tests/Baseline/hilti.types.struct.errors/output +++ b/tests/Baseline/hilti.types.struct.errors/output @@ -1,4 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. [error] <...>/errors.hlt:14:14: type does not have field 'DoesNotExist' -[error] <...>/errors.hlt:15:1: field 's' is not &optional [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.finally-fail-2/output b/tests/Baseline/hilti.types.struct.finally-fail-2/output new file mode 100644 index 000000000..3edc80d00 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.finally-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] <...>/finally-fail.hlt:4:10-6:2: ~finally must have return type void +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.finally-fail-3/output b/tests/Baseline/hilti.types.struct.finally-fail-3/output new file mode 100644 index 000000000..b4b571321 --- /dev/null +++ b/tests/Baseline/hilti.types.struct.finally-fail-3/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] <...>/finally-fail.hlt:4:10-6:2: ~finally cannot take any parameters +[error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.finally-fail/output b/tests/Baseline/hilti.types.struct.finally-fail/output index 8e127c325..1474c7de9 100644 --- a/tests/Baseline/hilti.types.struct.finally-fail/output +++ b/tests/Baseline/hilti.types.struct.finally-fail/output @@ -1,5 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/finally-fail.hlt:10:10-14:2: ~finally must be a hook -[error] <...>/finally-fail.hlt:10:10-14:2: ~finally must have return type void -[error] <...>/finally-fail.hlt:10:10-14:2: ~finally cannot take any parameters +[error] <...>/finally-fail.hlt:8:10-10:2: ~finally must be a hook [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.struct.params-write-fail/output b/tests/Baseline/hilti.types.struct.params-write-fail/output index 701c20b87..915f026da 100644 --- a/tests/Baseline/hilti.types.struct.params-write-fail/output +++ b/tests/Baseline/hilti.types.struct.params-write-fail/output @@ -1,5 +1,4 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. [error] <...>/params-write-fail.hlt:17:5: cannot assign to expression: X = True -[error] <...>/params-write-fail.hlt:18:5: cannot resolve operator: add >[] -[error] <...>/params-write-fail.hlt:19:5: cannot assign to expression: (*Z).a = 42 +[error] <...>/params-write-fail.hlt:18:5: unsupported operator: add >[] [error] spicyc: aborting after errors diff --git a/tests/Baseline/hilti.types.tuple.index-not-unsigned-integer-fail/output b/tests/Baseline/hilti.types.tuple.index-not-unsigned-integer-fail/output index 345f2b10e..d497b0a89 100644 --- a/tests/Baseline/hilti.types.tuple.index-not-unsigned-integer-fail/output +++ b/tests/Baseline/hilti.types.tuple.index-not-unsigned-integer-fail/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/index-not-unsigned-integer-fail.hlt:12:14: cannot resolve operator: >[>] +[error] <...>/index-not-unsigned-integer-fail.hlt:12:14: unsupported operator: >[>] [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.types.vector.ctor-fail/output b/tests/Baseline/hilti.types.vector.ctor-fail/output index 35b83d2d9..b8cd32de7 100644 --- a/tests/Baseline/hilti.types.vector.ctor-fail/output +++ b/tests/Baseline/hilti.types.vector.ctor-fail/output @@ -1,3 +1,4 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. [error] <...>/ctor-fail.hlt:6:1: type mismatch in vector elements +[error] <...>/ctor-fail.hlt:6:1: vector elements have inconsistent types [error] hiltic: aborting after errors diff --git a/tests/Baseline/hilti.validation.const-violations/output b/tests/Baseline/hilti.validation.const-violations/output index 08c083dc2..111e4e3d7 100644 --- a/tests/Baseline/hilti.validation.const-violations/output +++ b/tests/Baseline/hilti.validation.const-violations/output @@ -1,8 +1,8 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/const-violations.hlt:22:5: cannot resolve operator: >>.push_back(>) +[error] <...>/const-violations.hlt:22:5: unsupported operator: >>.push_back(>) [error] <...>/const-violations.hlt:23:5: cannot assign to expression: v = v2 [error] <...>/const-violations.hlt:24:5: cannot assign to expression: v[5] = 1 -[error] <...>/const-violations.hlt:25:5: cannot assign to expression: (x, v) = ("X", v) +[error] <...>/const-violations.hlt:25:5: cannot assign to expression: v [error] <...>/const-violations.hlt:26:5: call does not match any function: p2(>>) [error] <...>/const-violations.hlt:26:5: candidate functions: [error] <...>/const-violations.hlt:26:5: - p2(>>) diff --git a/tests/Baseline/spicy.expressions.assignment-fail/output b/tests/Baseline/spicy.expressions.assignment-fail/output index 89a0670dc..67bfb694c 100644 --- a/tests/Baseline/spicy.expressions.assignment-fail/output +++ b/tests/Baseline/spicy.expressions.assignment-fail/output @@ -1,7 +1,7 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. [error] <...>/assignment-fail.spicy:8:1: cannot assign to expression: b"" = b"bytes" [error] <...>/assignment-fail.spicy:9:1: cannot coerce expression '"string"' of type 'string' to type 'bytes' -[error] <...>/assignment-fail.spicy:20:5: cannot assign to expression: (*a).x = b"bytes" +[error] <...>/assignment-fail.spicy:15:9: cannot coerce expression '"string"' of type 'string' to type 'bytes' +[error] <...>/assignment-fail.spicy:20:5: cannot assign to field of constant unit instance [error] <...>/assignment-fail.spicy:21:5: cannot coerce expression '"string"' of type 'string' to type 'bytes' -[error] <...>/assignment-fail.spicy:15:9: type mismatch for assignment, expected type bytes but got string [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.expressions.equal-ops-fail/output b/tests/Baseline/spicy.expressions.equal-ops-fail/output index 14a2de742..48ab9da45 100644 --- a/tests/Baseline/spicy.expressions.equal-ops-fail/output +++ b/tests/Baseline/spicy.expressions.equal-ops-fail/output @@ -1,4 +1,4 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/equal-ops-fail.spicy:11:5: units cannot be compared with == -[error] <...>/equal-ops-fail.spicy:12:5: units cannot be compared with != +[error] <...>/equal-ops-fail.spicy:11:5: unsupported operator: == +[error] <...>/equal-ops-fail.spicy:12:5: unsupported operator: != [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.expressions.list-comprehension-unit/output b/tests/Baseline/spicy.expressions.list-comprehension-unit/output new file mode 100644 index 000000000..edf4de151 --- /dev/null +++ b/tests/Baseline/spicy.expressions.list-comprehension-unit/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. +[] diff --git a/tests/Baseline/spicy.optimization.default-parser-functions/noopt.hlt b/tests/Baseline/spicy.optimization.default-parser-functions/noopt.hlt index 8ce8eebba..1dca73c18 100644 --- a/tests/Baseline/spicy.optimization.default-parser-functions/noopt.hlt +++ b/tests/Baseline/spicy.optimization.default-parser-functions/noopt.hlt @@ -1,8 +1,8 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. module foo { -import hilti; import spicy_rt; +import hilti; type P0 = struct { hook void __on_0x25_init(); @@ -61,11 +61,11 @@ public type P2 = struct { method tuple, int<64>, iterator> __parse_foo_P2_stage2(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe); } &on-heap; -const __feat%foo__P1%supports_filters = True; -const __feat%foo__P1%supports_sinks = True; -const __feat%foo__P2%supports_filters = True; -const __feat%foo__P2%supports_sinks = True; -const __feat%foo__P2%uses_random_access = True; +const bool __feat%foo__P1%supports_filters = True; +const bool __feat%foo__P1%supports_sinks = True; +const bool __feat%foo__P2%supports_filters = True; +const bool __feat%foo__P2%supports_sinks = True; +const bool __feat%foo__P2%uses_random_access = True; method method tuple, int<64>, iterator> foo::P0::__parse_stage1(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe) { local tuple, int<64>, iterator> __result; @@ -87,7 +87,7 @@ method method tuple, int<64>, iterator> foo::P0::__parse_st } method extern method view foo::P0::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -100,7 +100,7 @@ method extern method view foo::P0::parse1(inout value_ref data, } method extern method view foo::P0::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::P0)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -130,7 +130,7 @@ method method tuple, int<64>, iterator> foo::P1::__parse_st try { hilti::debugIndent("spicy"); (*self).__on_0x25_init(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( __feat%foo__P1%supports_filters ) @@ -178,7 +178,7 @@ method method tuple, int<64>, iterator> foo::P1::__parse_fo } method extern method view foo::P1::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -191,7 +191,7 @@ method extern method view foo::P1::parse1(inout value_ref data, } method extern method view foo::P1::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::P1)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -251,7 +251,7 @@ method method tuple, int<64>, iterator> foo::P2::__parse_st } - local auto filtered = Null; + local strong_ref filtered = Null; if ( __feat%foo__P2%supports_filters ) @@ -400,7 +400,7 @@ method method tuple, int<64>, iterator> foo::P2::__parse_fo } method extern method view foo::P2::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -417,7 +417,7 @@ method extern method view foo::P2::parse1(inout value_ref data, } method extern method view foo::P2::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::P2)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; diff --git a/tests/Baseline/spicy.optimization.default-parser-functions/opt.hlt b/tests/Baseline/spicy.optimization.default-parser-functions/opt.hlt index 851ce7be4..22e030104 100644 --- a/tests/Baseline/spicy.optimization.default-parser-functions/opt.hlt +++ b/tests/Baseline/spicy.optimization.default-parser-functions/opt.hlt @@ -1,8 +1,8 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. module foo { -import hilti; import spicy_rt; +import hilti; public type P1 = struct { spicy_rt::Parser __parser &static &internal &always-emit; @@ -26,18 +26,18 @@ public type P2 = struct { method tuple, int<64>, iterator> __parse_foo_P2_stage2(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe); } &on-heap; -const __feat%foo__P1%supports_filters = False; -const __feat%foo__P1%supports_sinks = False; -const __feat%foo__P2%supports_filters = True; -const __feat%foo__P2%supports_sinks = False; -const __feat%foo__P2%uses_random_access = False; +const bool __feat%foo__P1%supports_filters = False; +const bool __feat%foo__P1%supports_sinks = False; +const bool __feat%foo__P2%supports_filters = True; +const bool __feat%foo__P2%supports_sinks = False; +const bool __feat%foo__P2%uses_random_access = False; method method tuple, int<64>, iterator> foo::P1::__parse_stage1(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe) &always-emit { local tuple, int<64>, iterator> __result; try { hilti::debugIndent("spicy"); default(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo_P1_stage2(__data, __cur, __trim, __lah, __lahe); @@ -62,7 +62,7 @@ method method tuple, int<64>, iterator> foo::P1::__parse_fo } method extern method view foo::P1::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -75,7 +75,7 @@ method extern method view foo::P1::parse1(inout value_ref data, } method extern method view foo::P1::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::P1)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -116,7 +116,7 @@ method method tuple, int<64>, iterator> foo::P2::__parse_st try { hilti::debugIndent("spicy"); default(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( filtered = spicy_rt::filter_init(self, __data, __cur) ) { @@ -175,7 +175,7 @@ method method tuple, int<64>, iterator> foo::P2::__parse_fo } method extern method view foo::P2::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -188,7 +188,7 @@ method extern method view foo::P2::parse1(inout value_ref data, } method extern method view foo::P2::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::P2)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; diff --git a/tests/Baseline/spicy.optimization.feature_requirements/noopt.hlt b/tests/Baseline/spicy.optimization.feature_requirements/noopt.hlt index c149a3de5..e990a6dd6 100644 --- a/tests/Baseline/spicy.optimization.feature_requirements/noopt.hlt +++ b/tests/Baseline/spicy.optimization.feature_requirements/noopt.hlt @@ -1,8 +1,8 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. module foo { -import hilti; import spicy_rt; +import hilti; type X1 = struct { optional> __begin &internal &needed-by-feature="uses_random_access"; @@ -90,12 +90,12 @@ type X6 = struct { method extern view parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static; } &on-heap; -const __feat%foo__X1%uses_random_access = True; -const __feat%foo__X2%uses_random_access = True; -const __feat%foo__X3%is_filter = True; -const __feat%foo__X4%is_filter = True; -const __feat%foo__X5%supports_filters = True; -const __feat%foo__X5%supports_sinks = True; +const bool __feat%foo__X1%uses_random_access = True; +const bool __feat%foo__X2%uses_random_access = True; +const bool __feat%foo__X3%is_filter = True; +const bool __feat%foo__X4%is_filter = True; +const bool __feat%foo__X5%supports_filters = True; +const bool __feat%foo__X5%supports_sinks = True; method hook void foo::X1::__on_0x25_init() { cast>((*(*self).__position) - (*(*self).__begin)); @@ -187,7 +187,7 @@ method method tuple, int<64>, iterator> foo::X1::__parse_st } method extern method view foo::X1::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -204,7 +204,7 @@ method extern method view foo::X1::parse1(inout value_ref data, } method extern method view foo::X1::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::X1)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -323,7 +323,7 @@ method method tuple, int<64>, iterator> foo::X2::__parse_st } method extern method view foo::X2::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -340,7 +340,7 @@ method extern method view foo::X2::parse1(inout value_ref data, } method extern method view foo::X2::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::X2)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -401,7 +401,7 @@ method method tuple, int<64>, iterator> foo::X3::__parse_st } method extern method view foo::X3::parse1(inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -414,7 +414,7 @@ method extern method view foo::X3::parse1(inout value_ref data, } method extern method view foo::X3::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::X3)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -471,7 +471,7 @@ method method tuple, int<64>, iterator> foo::X4::__parse_st } method extern method view foo::X4::parse1(inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -484,7 +484,7 @@ method extern method view foo::X4::parse1(inout value_ref data, } method extern method view foo::X4::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::X4)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -522,7 +522,7 @@ method method tuple, int<64>, iterator> foo::X5::__parse_st try { hilti::debugIndent("spicy"); (*self).__on_0x25_init(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( __feat%foo__X5%supports_filters ) @@ -570,7 +570,7 @@ method method tuple, int<64>, iterator> foo::X5::__parse_fo } method extern method view foo::X5::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -583,7 +583,7 @@ method extern method view foo::X5::parse1(inout value_ref data, } method extern method view foo::X5::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::X5)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -614,7 +614,7 @@ init function void __register_foo_X5() { } method hook void foo::X6::__on_0x25_init() { - (*(*self).data).write(b"", Null, Null); + (*self).data.write(b"", Null, Null); } method method tuple, int<64>, iterator> foo::X6::__parse_stage1(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe) { @@ -639,7 +639,7 @@ method method tuple, int<64>, iterator> foo::X6::__parse_st } method extern method view foo::X6::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -652,7 +652,7 @@ method extern method view foo::X6::parse1(inout value_ref data, } method extern method view foo::X6::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::X6)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; diff --git a/tests/Baseline/spicy.optimization.feature_requirements/opt.hlt b/tests/Baseline/spicy.optimization.feature_requirements/opt.hlt index fa1e265ec..5d2454d87 100644 --- a/tests/Baseline/spicy.optimization.feature_requirements/opt.hlt +++ b/tests/Baseline/spicy.optimization.feature_requirements/opt.hlt @@ -1,8 +1,8 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. module foo { -import hilti; import spicy_rt; +import hilti; type X1 = struct { optional> __begin &internal &needed-by-feature="uses_random_access"; @@ -40,12 +40,12 @@ type X6 = struct { hook void __on_0x25_init(); } &on-heap; -const __feat%foo__X1%uses_random_access = True; -const __feat%foo__X2%uses_random_access = False; -const __feat%foo__X3%is_filter = False; -const __feat%foo__X4%is_filter = True; -const __feat%foo__X5%supports_filters = True; -const __feat%foo__X5%supports_sinks = False; +const bool __feat%foo__X1%uses_random_access = True; +const bool __feat%foo__X2%uses_random_access = False; +const bool __feat%foo__X3%is_filter = False; +const bool __feat%foo__X4%is_filter = True; +const bool __feat%foo__X5%supports_filters = True; +const bool __feat%foo__X5%supports_sinks = False; method hook void foo::X1::__on_0x25_init() { cast>((*(*self).__position) - (*(*self).__begin)); @@ -71,7 +71,7 @@ method method tuple, int<64>, iterator> foo::X3::__parse_st } method extern method view foo::X3::parse1(inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -84,7 +84,7 @@ method extern method view foo::X3::parse1(inout value_ref data, } method extern method view foo::X3::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::X3)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -139,7 +139,7 @@ method method tuple, int<64>, iterator> foo::X4::__parse_st } method extern method view foo::X4::parse1(inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -152,7 +152,7 @@ method extern method view foo::X4::parse1(inout value_ref data, } method extern method view foo::X4::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::X4)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -190,7 +190,7 @@ method method tuple, int<64>, iterator> foo::X5::__parse_st try { hilti::debugIndent("spicy"); (*self).__on_0x25_init(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( filtered = spicy_rt::filter_init(self, __data, __cur) ) { @@ -235,7 +235,7 @@ method method tuple, int<64>, iterator> foo::X5::__parse_fo } method extern method view foo::X5::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -248,7 +248,7 @@ method extern method view foo::X5::parse1(inout value_ref data, } method extern method view foo::X5::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::X5)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -279,7 +279,7 @@ init function void __register_foo_X5() { } method hook void foo::X6::__on_0x25_init() { - (*(*self).data).write(b"", Null, Null); + (*self).data.write(b"", Null, Null); } } diff --git a/tests/Baseline/spicy.optimization.unused-functions/noopt.hlt b/tests/Baseline/spicy.optimization.unused-functions/noopt.hlt index c379c7fe6..d7545885f 100644 --- a/tests/Baseline/spicy.optimization.unused-functions/noopt.hlt +++ b/tests/Baseline/spicy.optimization.unused-functions/noopt.hlt @@ -1,8 +1,8 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. module foo { -import hilti; import spicy_rt; +import hilti; type A = struct { hook void __on_0x25_init(); @@ -79,11 +79,11 @@ type F = struct { method extern view parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static &always-emit; } &on-heap; -const __feat%foo__B%supports_filters = True; -const __feat%foo__B%supports_sinks = True; -const __feat%foo__D%supports_filters = True; -const __feat%foo__D%supports_sinks = True; -const __feat%foo__F%is_filter = True; +const bool __feat%foo__B%supports_filters = True; +const bool __feat%foo__B%supports_sinks = True; +const bool __feat%foo__D%supports_filters = True; +const bool __feat%foo__D%supports_sinks = True; +const bool __feat%foo__F%is_filter = True; function void f1() { } @@ -111,7 +111,7 @@ method method tuple, int<64>, iterator> foo::A::__parse_sta } method extern method view foo::A::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -124,7 +124,7 @@ method extern method view foo::A::parse1(inout value_ref data, o } method extern method view foo::A::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::A)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -154,7 +154,7 @@ method method tuple, int<64>, iterator> foo::B::__parse_sta try { hilti::debugIndent("spicy"); (*self).__on_0x25_init(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( __feat%foo__B%supports_filters ) @@ -202,7 +202,7 @@ method method tuple, int<64>, iterator> foo::B::__parse_foo } method extern method view foo::B::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -215,7 +215,7 @@ method extern method view foo::B::parse1(inout value_ref data, o } method extern method view foo::B::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::B)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -265,7 +265,7 @@ method method tuple, int<64>, iterator> foo::C::__parse_sta } method extern method view foo::C::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -278,7 +278,7 @@ method extern method view foo::C::parse1(inout value_ref data, o } method extern method view foo::C::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::C)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -308,7 +308,7 @@ method method tuple, int<64>, iterator> foo::D::__parse_sta try { hilti::debugIndent("spicy"); (*self).__on_0x25_init(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( __feat%foo__D%supports_filters ) @@ -363,7 +363,7 @@ method method tuple, int<64>, iterator> foo::D::__parse_foo } method extern method view foo::D::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -376,7 +376,7 @@ method extern method view foo::D::parse1(inout value_ref data, o } method extern method view foo::D::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::D)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -434,7 +434,7 @@ method method tuple, int<64>, iterator> foo::F::__parse_sta } method extern method view foo::F::parse1(inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -447,7 +447,7 @@ method extern method view foo::F::parse1(inout value_ref data, o } method extern method view foo::F::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::F)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; diff --git a/tests/Baseline/spicy.optimization.unused-functions/opt.hlt b/tests/Baseline/spicy.optimization.unused-functions/opt.hlt index 55d62baff..ac212cc1f 100644 --- a/tests/Baseline/spicy.optimization.unused-functions/opt.hlt +++ b/tests/Baseline/spicy.optimization.unused-functions/opt.hlt @@ -1,8 +1,8 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. module foo { -import hilti; import spicy_rt; +import hilti; public type B = struct { spicy_rt::Parser __parser &static &internal &always-emit; @@ -32,18 +32,18 @@ type F = struct { method extern view parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static &always-emit; } &on-heap; -const __feat%foo__B%supports_filters = False; -const __feat%foo__B%supports_sinks = False; -const __feat%foo__D%supports_filters = False; -const __feat%foo__D%supports_sinks = False; -const __feat%foo__F%is_filter = False; +const bool __feat%foo__B%supports_filters = False; +const bool __feat%foo__B%supports_sinks = False; +const bool __feat%foo__D%supports_filters = False; +const bool __feat%foo__D%supports_sinks = False; +const bool __feat%foo__F%is_filter = False; method method tuple, int<64>, iterator> foo::B::__parse_stage1(inout value_ref __data, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe) &always-emit { local tuple, int<64>, iterator> __result; try { hilti::debugIndent("spicy"); default(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo_B_stage2(__data, __cur, __trim, __lah, __lahe); @@ -68,7 +68,7 @@ method method tuple, int<64>, iterator> foo::B::__parse_foo } method extern method view foo::B::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -81,7 +81,7 @@ method extern method view foo::B::parse1(inout value_ref data, o } method extern method view foo::B::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::B)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -135,7 +135,7 @@ method method tuple, int<64>, iterator> foo::D::__parse_sta try { hilti::debugIndent("spicy"); default(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo_D_stage2(__data, __cur, __trim, __lah, __lahe); @@ -167,7 +167,7 @@ method method tuple, int<64>, iterator> foo::D::__parse_foo } method extern method view foo::D::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -180,7 +180,7 @@ method extern method view foo::D::parse1(inout value_ref data, o } method extern method view foo::D::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::D)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -230,7 +230,7 @@ method method tuple, int<64>, iterator> foo::F::__parse_sta } method extern method view foo::F::parse1(inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -243,7 +243,7 @@ method extern method view foo::F::parse1(inout value_ref data, o } method extern method view foo::F::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static &always-emit { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::F)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; diff --git a/tests/Baseline/spicy.optimization.unused-types/noopt.hlt b/tests/Baseline/spicy.optimization.unused-types/noopt.hlt index a8e1eed68..b5b331296 100644 --- a/tests/Baseline/spicy.optimization.unused-types/noopt.hlt +++ b/tests/Baseline/spicy.optimization.unused-types/noopt.hlt @@ -1,8 +1,8 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. module foo { -import hilti; import spicy_rt; +import hilti; type Priv1 = struct { hook void __on_0x25_init(); @@ -142,12 +142,12 @@ public type Priv10 = struct { type Priv11 = enum { A = 0, B = 1, C = 2 }; type Priv12 = enum { A = 0, B = 1, C = 2 }; -const __feat%foo__Pub2%supports_filters = True; -const __feat%foo__Pub2%supports_sinks = True; -const __feat%foo__Pub3%supports_filters = True; -const __feat%foo__Pub3%supports_sinks = True; -const __feat%foo__Priv10%supports_filters = True; -const __feat%foo__Priv10%supports_sinks = True; +const bool __feat%foo__Pub2%supports_filters = True; +const bool __feat%foo__Pub2%supports_sinks = True; +const bool __feat%foo__Pub3%supports_filters = True; +const bool __feat%foo__Pub3%supports_sinks = True; +const bool __feat%foo__Priv10%supports_filters = True; +const bool __feat%foo__Priv10%supports_sinks = True; global Priv11 en; global Priv12 em = Priv12::A; @@ -172,7 +172,7 @@ method method tuple, int<64>, iterator> foo::Priv1::__parse } method extern method view foo::Priv1::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -185,7 +185,7 @@ method extern method view foo::Priv1::parse1(inout value_ref dat } method extern method view foo::Priv1::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Priv1)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -215,7 +215,7 @@ method method tuple, int<64>, iterator> foo::Pub2::__parse_ try { hilti::debugIndent("spicy"); (*self).__on_0x25_init(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( __feat%foo__Pub2%supports_filters ) @@ -263,7 +263,7 @@ method method tuple, int<64>, iterator> foo::Pub2::__parse_ } method extern method view foo::Pub2::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -276,7 +276,7 @@ method extern method view foo::Pub2::parse1(inout value_ref data } method extern method view foo::Pub2::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Pub2)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -326,7 +326,7 @@ method method tuple, int<64>, iterator> foo::Priv2::__parse } method extern method view foo::Priv2::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -339,7 +339,7 @@ method extern method view foo::Priv2::parse1(inout value_ref dat } method extern method view foo::Priv2::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Priv2)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -384,7 +384,7 @@ method method tuple, int<64>, iterator> foo::Priv3::__parse } method extern method view foo::Priv3::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -397,7 +397,7 @@ method extern method view foo::Priv3::parse1(inout value_ref dat } method extern method view foo::Priv3::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Priv3)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -455,7 +455,7 @@ method method tuple, int<64>, iterator> foo::Priv4::__parse } method extern method view foo::Priv4::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -468,7 +468,7 @@ method extern method view foo::Priv4::parse1(inout value_ref dat } method extern method view foo::Priv4::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Priv4)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -513,7 +513,7 @@ method method tuple, int<64>, iterator> foo::Priv5::__parse } method extern method view foo::Priv5::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -526,7 +526,7 @@ method extern method view foo::Priv5::parse1(inout value_ref dat } method extern method view foo::Priv5::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Priv5)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -571,7 +571,7 @@ method method tuple, int<64>, iterator> foo::Priv6::__parse } method extern method view foo::Priv6::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -584,7 +584,7 @@ method extern method view foo::Priv6::parse1(inout value_ref dat } method extern method view foo::Priv6::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Priv6)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -614,7 +614,7 @@ method method tuple, int<64>, iterator> foo::Pub3::__parse_ try { hilti::debugIndent("spicy"); (*self).__on_0x25_init(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( __feat%foo__Pub3%supports_filters ) @@ -675,7 +675,7 @@ method method tuple, int<64>, iterator> foo::Pub3::__parse_ } method extern method view foo::Pub3::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -688,7 +688,7 @@ method extern method view foo::Pub3::parse1(inout value_ref data } method extern method view foo::Pub3::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Pub3)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -723,7 +723,7 @@ method method tuple, int<64>, iterator> foo::Priv10::__pars try { hilti::debugIndent("spicy"); (*self).__on_0x25_init(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( __feat%foo__Priv10%supports_filters ) @@ -771,7 +771,7 @@ method method tuple, int<64>, iterator> foo::Priv10::__pars } method extern method view foo::Priv10::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -784,7 +784,7 @@ method extern method view foo::Priv10::parse1(inout value_ref da } method extern method view foo::Priv10::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Priv10)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; diff --git a/tests/Baseline/spicy.optimization.unused-types/opt.hlt b/tests/Baseline/spicy.optimization.unused-types/opt.hlt index ad7618be5..99cdc4421 100644 --- a/tests/Baseline/spicy.optimization.unused-types/opt.hlt +++ b/tests/Baseline/spicy.optimization.unused-types/opt.hlt @@ -1,8 +1,8 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. module foo { -import hilti; import spicy_rt; +import hilti; public type Pub2 = struct { spicy_rt::Parser __parser &static &internal &always-emit; @@ -44,12 +44,12 @@ public type Priv10 = struct { type Priv11 = enum { A = 0, B = 1, C = 2 }; type Priv12 = enum { A = 0, B = 1, C = 2 }; -const __feat%foo__Pub2%supports_filters = False; -const __feat%foo__Pub2%supports_sinks = False; -const __feat%foo__Pub3%supports_filters = False; -const __feat%foo__Pub3%supports_sinks = False; -const __feat%foo__Priv10%supports_filters = False; -const __feat%foo__Priv10%supports_sinks = False; +const bool __feat%foo__Pub2%supports_filters = False; +const bool __feat%foo__Pub2%supports_sinks = False; +const bool __feat%foo__Pub3%supports_filters = False; +const bool __feat%foo__Pub3%supports_sinks = False; +const bool __feat%foo__Priv10%supports_filters = False; +const bool __feat%foo__Priv10%supports_sinks = False; global Priv11 en; global Priv12 em = Priv12::A; @@ -59,7 +59,7 @@ method method tuple, int<64>, iterator> foo::Pub2::__parse_ try { hilti::debugIndent("spicy"); default(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo_Pub2_stage2(__data, __cur, __trim, __lah, __lahe); @@ -84,7 +84,7 @@ method method tuple, int<64>, iterator> foo::Pub2::__parse_ } method extern method view foo::Pub2::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -97,7 +97,7 @@ method extern method view foo::Pub2::parse1(inout value_ref data } method extern method view foo::Pub2::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Pub2)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -170,7 +170,7 @@ method method tuple, int<64>, iterator> foo::Pub3::__parse_ try { hilti::debugIndent("spicy"); default(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo_Pub3_stage2(__data, __cur, __trim, __lah, __lahe); @@ -208,7 +208,7 @@ method method tuple, int<64>, iterator> foo::Pub3::__parse_ } method extern method view foo::Pub3::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -221,7 +221,7 @@ method extern method view foo::Pub3::parse1(inout value_ref data } method extern method view foo::Pub3::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Pub3)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; @@ -256,7 +256,7 @@ method method tuple, int<64>, iterator> foo::Priv10::__pars try { hilti::debugIndent("spicy"); default(); - local auto filtered = Null; + local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo_Priv10_stage2(__data, __cur, __trim, __lah, __lahe); @@ -281,7 +281,7 @@ method method tuple, int<64>, iterator> foo::Priv10::__pars } method extern method view foo::Priv10::parse1(inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; local iterator lahead_end; @@ -294,7 +294,7 @@ method extern method view foo::Priv10::parse1(inout value_ref da } method extern method view foo::Priv10::parse3(inout value_ref gunit, inout value_ref data, optional> cur = Null, optional context) &static { - local auto unit = value_ref(default())value_ref(default()); + local value_ref unit = value_ref(default())value_ref(default()); spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(foo::Priv10)); local view ncur = cur ? (*cur) : cast>((*data)); local int<64> lahead = 0; diff --git a/tests/Baseline/spicy.rt.debug-trace/.stderr b/tests/Baseline/spicy.rt.debug-trace/.stderr index 00ec09b92..5b439add8 100644 --- a/tests/Baseline/spicy.rt.debug-trace/.stderr +++ b/tests/Baseline/spicy.rt.debug-trace/.stderr @@ -1,65 +1,65 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[hilti-trace] : Mini::Test::__parser = [$name="Mini::Test", $parse1=Mini::Test::parse1, $parse2=Mini::Test::parse2, $parse3=Mini::Test::parse3, $context_new=Null, $type_info=typeinfo(Mini::Test), $description="", $mime_types=vector(), $ports=vector()]; -[hilti-trace] : spicy_rt::registerParser(Mini::Test::__parser, hilti::linker_scope(), Null); -[hilti-trace] : local auto unit = value_ref(default())value_ref(default()); -[hilti-trace] : spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(Mini::Test)); -[hilti-trace] : local view ncur = cur ? (*cur) : cast>((*data)); -[hilti-trace] : local int<64> lahead = 0; -[hilti-trace] : local iterator lahead_end; -[hilti-trace] : # Begin parsing production: Unit: Mini_Test -> f1 f2 -[hilti-trace] : spicy_rt::printParserState("Mini::Test", data, ncur, lahead, lahead_end, "default", True); -[hilti-trace] : hilti::debug("spicy-verbose", "- parsing production: Unit: Mini_Test -> f1 f2"); -[hilti-trace] : hilti::debugIndent("spicy-verbose"); -[hilti-trace] : (ncur, lahead, lahead_end) = (*unit).__parse_stage1(data, ncur, True, lahead, lahead_end); -[hilti-trace] : local tuple, int<64>, iterator> __result; -[hilti-trace] : try { hilti::debug("spicy", "Mini::Test"); hilti::debugIndent("spicy"); (*self).__on_0x25_init(); local auto filtered = Null; if ( filtered = spicy_rt::filter_init(self, __data, __cur) ) { local value_ref filtered_data = filtered; (*self).__parse_Mini_Test_stage2(filtered_data, (*filtered_data), __trim, __lah, __lahe); __cur = __cur.advance(|__cur|); if ( __trim ) { hilti::debug("spicy-verbose", "- trimming input"); (*__data).trim(begin(__cur)); } __result = (__cur, __lah, __lahe); } if ( ! filtered ) __result = (*self).__parse_Mini_Test_stage2(__data, __cur, __trim, __lah, __lahe); } catch { default(); spicy_rt::filter_disconnect(self); default(); throw; } -[hilti-trace] : hilti::debug("spicy", "Mini::Test"); -[hilti-trace] : hilti::debugIndent("spicy"); -[hilti-trace] <...>/debug-trace.spicy:8:20-13:2: (*self).__on_0x25_init(); -[hilti-trace] <...>/debug-trace.spicy:9:18: hilti::print(self, True); -[hilti-trace] : local auto filtered = Null; -[hilti-trace] : if ( filtered = spicy_rt::filter_init(self, __data, __cur) ) { local value_ref filtered_data = filtered; (*self).__parse_Mini_Test_stage2(filtered_data, (*filtered_data), __trim, __lah, __lahe); __cur = __cur.advance(|__cur|); if ( __trim ) { hilti::debug("spicy-verbose", "- trimming input"); (*__data).trim(begin(__cur)); } __result = (__cur, __lah, __lahe); } -[hilti-trace] : if ( ! filtered ) { __result = (*self).__parse_Mini_Test_stage2(__data, __cur, __trim, __lah, __lahe); } -[hilti-trace] : __result = (*self).__parse_Mini_Test_stage2(__data, __cur, __trim, __lah, __lahe); -[hilti-trace] : local tuple, int<64>, iterator> __result; -[hilti-trace] : # Begin parsing production: Variable: f1 -> uint<32> -[hilti-trace] : spicy_rt::printParserState("Mini::Test", __data, __cur, __lah, __lahe, "default", __trim); -[hilti-trace] : hilti::debug("spicy-verbose", "- parsing production: Variable: f1 -> uint<32>"); -[hilti-trace] : hilti::debugIndent("spicy-verbose"); -[hilti-trace] : spicy_rt::waitForInput(__data, __cur, 4, "expecting 4 bytes for unpacking value", "<...>/debug-trace.spicy:10:9", (*self).__filters); -[hilti-trace] : ((*self).f1, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); -[hilti-trace] : if ( __trim ) { hilti::debug("spicy-verbose", "- trimming input"); (*__data).trim(begin(__cur)); } -[hilti-trace] : hilti::debug("spicy-verbose", "- trimming input"); -[hilti-trace] : (*__data).trim(begin(__cur)); -[hilti-trace] : hilti::debugDedent("spicy-verbose"); -[hilti-trace] : # End parsing production: Variable: f1 -> uint<32> -[hilti-trace] : hilti::debug("spicy", "f1 = %s" % (*self).f1); -[hilti-trace] : hilti::debug("spicy-verbose", "- setting field 'f1' to '%s'" % (*self).f1); -[hilti-trace] <...>/debug-trace.spicy:10:5: (*self).__on_f1((*self).f1); -[hilti-trace] <...>/debug-trace.spicy:10:18: hilti::print((*self).f1, True); -[hilti-trace] : # Begin parsing production: Variable: f2 -> uint<8> -[hilti-trace] : spicy_rt::printParserState("Mini::Test", __data, __cur, __lah, __lahe, "default", __trim); -[hilti-trace] : hilti::debug("spicy-verbose", "- parsing production: Variable: f2 -> uint<8>"); -[hilti-trace] : hilti::debugIndent("spicy-verbose"); -[hilti-trace] : spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/debug-trace.spicy:11:9", (*self).__filters); -[hilti-trace] : ((*self).f2, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); -[hilti-trace] : if ( __trim ) { hilti::debug("spicy-verbose", "- trimming input"); (*__data).trim(begin(__cur)); } -[hilti-trace] : hilti::debug("spicy-verbose", "- trimming input"); -[hilti-trace] : (*__data).trim(begin(__cur)); -[hilti-trace] : hilti::debugDedent("spicy-verbose"); -[hilti-trace] : # End parsing production: Variable: f2 -> uint<8> -[hilti-trace] : hilti::debug("spicy", "f2 = %s" % (*self).f2); -[hilti-trace] : hilti::debug("spicy-verbose", "- setting field 'f2' to '%s'" % (*self).f2); -[hilti-trace] <...>/debug-trace.spicy:11:5: (*self).__on_f2((*self).f2); -[hilti-trace] <...>/debug-trace.spicy:11:18: hilti::print((*self).f2, True); -[hilti-trace] <...>/debug-trace.spicy:8:20-13:2: (*self).__on_0x25_done(); -[hilti-trace] <...>/debug-trace.spicy:12:18: hilti::print(self, True); -[hilti-trace] : spicy_rt::filter_disconnect(self); -[hilti-trace] : hilti::debugDedent("spicy"); -[hilti-trace] : __result = (__cur, __lah, __lahe); -[hilti-trace] : return __result; -[hilti-trace] <...>/debug-trace.spicy:8:20-13:2: default(); -[hilti-trace] : return __result; -[hilti-trace] : hilti::debugDedent("spicy-verbose"); -[hilti-trace] : # End parsing production: Unit: Mini_Test -> f1 f2 -[hilti-trace] : return ncur; +[hilti-trace] : Mini::Test::__parser = [$name="Mini::Test", $parse1=Mini::Test::parse1, $parse2=Mini::Test::parse2, $parse3=Mini::Test::parse3, $context_new=Null, $type_info=typeinfo(Mini::Test), $description="", $mime_types=vector(), $ports=vector()]; +[hilti-trace] : spicy_rt::registerParser(Mini::Test::__parser, hilti::linker_scope(), Null); +[hilti-trace] : local value_ref unit = value_ref(default())value_ref(default()); +[hilti-trace] : spicy_rt::initializeParsedUnit(gunit, unit, typeinfo(Mini::Test)); +[hilti-trace] : local view ncur = cur ? (*cur) : cast>((*data)); +[hilti-trace] : local int<64> lahead = 0; +[hilti-trace] : local iterator lahead_end; +[hilti-trace] : # Begin parsing production: Unit: Mini_Test -> f1 f2 +[hilti-trace] : spicy_rt::printParserState("Mini::Test", data, ncur, lahead, lahead_end, "default", True); +[hilti-trace] : hilti::debug("spicy-verbose", "- parsing production: Unit: Mini_Test -> f1 f2"); +[hilti-trace] : hilti::debugIndent("spicy-verbose"); +[hilti-trace] : (ncur, lahead, lahead_end) = (*unit).__parse_stage1(data, ncur, True, lahead, lahead_end); +[hilti-trace] : local tuple, int<64>, iterator> __result; +[hilti-trace] : try { hilti::debug("spicy", "Mini::Test"); hilti::debugIndent("spicy"); (*self).__on_0x25_init(); local strong_ref filtered = Null; if ( filtered = spicy_rt::filter_init(self, __data, __cur) ) { local value_ref filtered_data = filtered; (*self).__parse_Mini_Test_stage2(filtered_data, (*filtered_data), __trim, __lah, __lahe); __cur = __cur.advance(|__cur|); if ( __trim ) { hilti::debug("spicy-verbose", "- trimming input"); (*__data).trim(begin(__cur)); } __result = (__cur, __lah, __lahe); } if ( ! filtered ) __result = (*self).__parse_Mini_Test_stage2(__data, __cur, __trim, __lah, __lahe); } catch { default(); spicy_rt::filter_disconnect(self); default(); throw; } +[hilti-trace] : hilti::debug("spicy", "Mini::Test"); +[hilti-trace] : hilti::debugIndent("spicy"); +[hilti-trace] <...>/debug-trace.spicy:8:20-13:2: (*self).__on_0x25_init(); +[hilti-trace] <...>/debug-trace.spicy:9:18: hilti::print(self, True); +[hilti-trace] : local strong_ref filtered = Null; +[hilti-trace] : if ( filtered = spicy_rt::filter_init(self, __data, __cur) ) { local value_ref filtered_data = filtered; (*self).__parse_Mini_Test_stage2(filtered_data, (*filtered_data), __trim, __lah, __lahe); __cur = __cur.advance(|__cur|); if ( __trim ) { hilti::debug("spicy-verbose", "- trimming input"); (*__data).trim(begin(__cur)); } __result = (__cur, __lah, __lahe); } +[hilti-trace] : if ( ! filtered ) { __result = (*self).__parse_Mini_Test_stage2(__data, __cur, __trim, __lah, __lahe); } +[hilti-trace] : __result = (*self).__parse_Mini_Test_stage2(__data, __cur, __trim, __lah, __lahe); +[hilti-trace] : local tuple, int<64>, iterator> __result; +[hilti-trace] : # Begin parsing production: Variable: f1 -> uint<32> +[hilti-trace] : spicy_rt::printParserState("Mini::Test", __data, __cur, __lah, __lahe, "default", __trim); +[hilti-trace] : hilti::debug("spicy-verbose", "- parsing production: Variable: f1 -> uint<32>"); +[hilti-trace] : hilti::debugIndent("spicy-verbose"); +[hilti-trace] : spicy_rt::waitForInput(__data, __cur, 4, "expecting 4 bytes for unpacking value", "<...>/debug-trace.spicy:10:9", (*self).__filters); +[hilti-trace] : ((*self).f1, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); +[hilti-trace] : if ( __trim ) { hilti::debug("spicy-verbose", "- trimming input"); (*__data).trim(begin(__cur)); } +[hilti-trace] : hilti::debug("spicy-verbose", "- trimming input"); +[hilti-trace] : (*__data).trim(begin(__cur)); +[hilti-trace] : hilti::debugDedent("spicy-verbose"); +[hilti-trace] : # End parsing production: Variable: f1 -> uint<32> +[hilti-trace] : hilti::debug("spicy", "f1 = %s" % (*self).f1); +[hilti-trace] : hilti::debug("spicy-verbose", "- setting field 'f1' to '%s'" % (*self).f1); +[hilti-trace] <...>/debug-trace.spicy:10:5: (*self).__on_f1((*self).f1); +[hilti-trace] <...>/debug-trace.spicy:10:18: hilti::print((*self).f1, True); +[hilti-trace] : # Begin parsing production: Variable: f2 -> uint<8> +[hilti-trace] : spicy_rt::printParserState("Mini::Test", __data, __cur, __lah, __lahe, "default", __trim); +[hilti-trace] : hilti::debug("spicy-verbose", "- parsing production: Variable: f2 -> uint<8>"); +[hilti-trace] : hilti::debugIndent("spicy-verbose"); +[hilti-trace] : spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/debug-trace.spicy:11:9", (*self).__filters); +[hilti-trace] : ((*self).f2, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); +[hilti-trace] : if ( __trim ) { hilti::debug("spicy-verbose", "- trimming input"); (*__data).trim(begin(__cur)); } +[hilti-trace] : hilti::debug("spicy-verbose", "- trimming input"); +[hilti-trace] : (*__data).trim(begin(__cur)); +[hilti-trace] : hilti::debugDedent("spicy-verbose"); +[hilti-trace] : # End parsing production: Variable: f2 -> uint<8> +[hilti-trace] : hilti::debug("spicy", "f2 = %s" % (*self).f2); +[hilti-trace] : hilti::debug("spicy-verbose", "- setting field 'f2' to '%s'" % (*self).f2); +[hilti-trace] <...>/debug-trace.spicy:11:5: (*self).__on_f2((*self).f2); +[hilti-trace] <...>/debug-trace.spicy:11:18: hilti::print((*self).f2, True); +[hilti-trace] <...>/debug-trace.spicy:8:20-13:2: (*self).__on_0x25_done(); +[hilti-trace] <...>/debug-trace.spicy:12:18: hilti::print(self, True); +[hilti-trace] : spicy_rt::filter_disconnect(self); +[hilti-trace] : hilti::debugDedent("spicy"); +[hilti-trace] : __result = (__cur, __lah, __lahe); +[hilti-trace] : return __result; +[hilti-trace] <...>/debug-trace.spicy:8:20-13:2: default(); +[hilti-trace] : return __result; +[hilti-trace] : hilti::debugDedent("spicy-verbose"); +[hilti-trace] : # End parsing production: Unit: Mini_Test -> f1 f2 +[hilti-trace] : return ncur; diff --git a/tests/Baseline/spicy.tools.preprocessor/output b/tests/Baseline/spicy.tools.preprocessor/output index 93866983e..5194d5ba1 100644 --- a/tests/Baseline/spicy.tools.preprocessor/output +++ b/tests/Baseline/spicy.tools.preprocessor/output @@ -1,5 +1,5 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. == spicyc - hilti::rt::print(std::string("have spicy version"), hilti::rt::Bool(true)); - hilti::rt::print(std::string("have spicy version >= 0.4"), hilti::rt::Bool(true)); - hilti::rt::print(std::string("not have spicy version >= 4"), hilti::rt::Bool(true)); + ::hilti::rt::print(std::string("have spicy version"), ::hilti::rt::Bool(true)); + ::hilti::rt::print(std::string("have spicy version >= 0.4"), ::hilti::rt::Bool(true)); + ::hilti::rt::print(std::string("not have spicy version >= 4"), ::hilti::rt::Bool(true)); diff --git a/tests/Baseline/spicy.tools.spicy-driver-library-path/output b/tests/Baseline/spicy.tools.spicy-driver-library-path/output index 118db8105..7834f02c0 100644 --- a/tests/Baseline/spicy.tools.spicy-driver-library-path/output +++ b/tests/Baseline/spicy.tools.spicy-driver-library-path/output @@ -3,5 +3,5 @@ [$i=1] [$i=1] [error] test.spicy:4:1: cannot import module 'Bar': cannot find file -[error] spicy-driver: aborting after errors +[error] spicy-driver: errors encountered during resolving [$i=1] diff --git a/tests/Baseline/spicy.tools.spicyc-hello-world/test.hlt b/tests/Baseline/spicy.tools.spicyc-hello-world/test.hlt index c5669f36e..87aa0a883 100644 --- a/tests/Baseline/spicy.tools.spicyc-hello-world/test.hlt +++ b/tests/Baseline/spicy.tools.spicyc-hello-world/test.hlt @@ -7,21 +7,85 @@ #include #include +namespace __hlt::type_info { namespace { + extern const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily; + extern const ::hilti::rt::TypeInfo __ti_hilti_BitOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder; + extern const ::hilti::rt::TypeInfo __ti_hilti_Captures; + extern const ::hilti::rt::TypeInfo __ti_hilti_Charset; + extern const ::hilti::rt::TypeInfo __ti_hilti_Exception; + extern const ::hilti::rt::TypeInfo __ti_hilti_MatchState; + extern const ::hilti::rt::TypeInfo __ti_hilti_Protocol; + extern const ::hilti::rt::TypeInfo __ti_hilti_RealType; + extern const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError; + extern const ::hilti::rt::TypeInfo __ti_hilti_Side; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_Backtrack; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_BitOrder; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_Direction; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_Filters; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_FindDirection; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_Forward; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_HiltiResumable; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_MIMEType; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_ParseError; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_ParsedUnit; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_Parser; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_ParserPort; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_Sink; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_SinkState; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_TypeInfo; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_UnitAlreadyConnected; + extern const ::hilti::rt::TypeInfo __ti_spicy_rt_UnitContext; + extern const ::hilti::rt::TypeInfo __ti_vectorx30spicy_rt_ParserPort; +} } + namespace __hlt::Foo { extern void __init_module(); extern void __register_module(); } +namespace __hlt::type_info { namespace { + const ::hilti::rt::TypeInfo __ti_hilti_AddressFamily = { "hilti::AddressFamily", "hilti::AddressFamily", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IPv4", 0 }, ::hilti::rt::type_info::enum_::Label{ "IPv6", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_BitOrder = { "hilti::BitOrder", "hilti::BitOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "LSB0", 0 }, ::hilti::rt::type_info::enum_::Label{ "MSB0", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_ByteOrder = { "hilti::ByteOrder", "hilti::ByteOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Little", 0 }, ::hilti::rt::type_info::enum_::Label{ "Big", 1 }, ::hilti::rt::type_info::enum_::Label{ "Network", 2 }, ::hilti::rt::type_info::enum_::Label{ "Host", 3 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Captures = { "hilti::Captures", "hilti::Captures", new ::hilti::rt::type_info::Vector(&::hilti::rt::type_info::bytes, ::hilti::rt::type_info::Vector::accessor<::hilti::rt::Bytes>()) }; + const ::hilti::rt::TypeInfo __ti_hilti_Charset = { "hilti::Charset", "hilti::Charset", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "ASCII", 0 }, ::hilti::rt::type_info::enum_::Label{ "UTF8", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Exception = { "hilti::Exception", "hilti::Exception", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_MatchState = { "hilti::MatchState", "hilti::MatchState", new ::hilti::rt::type_info::Struct(std::vector<::hilti::rt::type_info::struct_::Field>({})) }; + const ::hilti::rt::TypeInfo __ti_hilti_Protocol = { "hilti::Protocol", "hilti::Protocol", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "TCP", 0 }, ::hilti::rt::type_info::enum_::Label{ "UDP", 1 }, ::hilti::rt::type_info::enum_::Label{ "ICMP", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RealType = { "hilti::RealType", "hilti::RealType", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "IEEE754_Single", 0 }, ::hilti::rt::type_info::enum_::Label{ "IEEE754_Double", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_hilti_RuntimeError = { "hilti::RuntimeError", "hilti::RuntimeError", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_hilti_Side = { "hilti::Side", "hilti::Side", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Left", 0 }, ::hilti::rt::type_info::enum_::Label{ "Right", 1 }, ::hilti::rt::type_info::enum_::Label{ "Both", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_Backtrack = { "spicy_rt::Backtrack", "spicy_rt::Backtrack", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_BitOrder = { "spicy_rt::BitOrder", "spicy_rt::BitOrder", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "LSB0", 0 }, ::hilti::rt::type_info::enum_::Label{ "MSB0", 1 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_Direction = { "spicy_rt::Direction", "spicy_rt::Direction", new ::hilti::rt::type_info::Enum(std::vector<::hilti::rt::type_info::enum_::Label>({::hilti::rt::type_info::enum_::Label{ "Originator", 0 }, ::hilti::rt::type_info::enum_::Label{ "Responder", 1 }, ::hilti::rt::type_info::enum_::Label{ "Both", 2 }, ::hilti::rt::type_info::enum_::Label{ "Undef", -1 }})) }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_Filters = { "spicy_rt::Filters", "spicy_rt::Filters", new ::hilti::rt::type_info::Library() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_FindDirection = { "spicy_rt::FindDirection", "spicy_rt::FindDirection", new ::hilti::rt::type_info::Library() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_Forward = { "spicy_rt::Forward", "spicy_rt::Forward", new ::hilti::rt::type_info::Library() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_HiltiResumable = { "spicy_rt::HiltiResumable", "spicy_rt::HiltiResumable", new ::hilti::rt::type_info::Library() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_MIMEType = { "spicy_rt::MIMEType", "spicy_rt::MIMEType", new ::hilti::rt::type_info::Library() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_ParseError = { "spicy_rt::ParseError", "spicy_rt::ParseError", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_ParsedUnit = { "spicy_rt::ParsedUnit", "spicy_rt::ParsedUnit", new ::hilti::rt::type_info::Library() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_Parser = { "spicy_rt::Parser", "spicy_rt::Parser", new ::hilti::rt::type_info::Struct(std::vector<::hilti::rt::type_info::struct_::Field>({::hilti::rt::type_info::struct_::Field{ "name", &::hilti::rt::type_info::string, offsetof(::spicy::rt::Parser, name), false }, ::hilti::rt::type_info::struct_::Field{ "parse1", &::hilti::rt::type_info::any, offsetof(::spicy::rt::Parser, parse1), false }, ::hilti::rt::type_info::struct_::Field{ "parse2", &::hilti::rt::type_info::any, offsetof(::spicy::rt::Parser, parse2), false }, ::hilti::rt::type_info::struct_::Field{ "parse3", &::hilti::rt::type_info::any, offsetof(::spicy::rt::Parser, parse3), false }, ::hilti::rt::type_info::struct_::Field{ "context_new", &::hilti::rt::type_info::any, offsetof(::spicy::rt::Parser, context_new), false }, ::hilti::rt::type_info::struct_::Field{ "type_info", &type_info::__ti_spicy_rt_TypeInfo, offsetof(::spicy::rt::Parser, type_info), false }, ::hilti::rt::type_info::struct_::Field{ "description", &::hilti::rt::type_info::string, offsetof(::spicy::rt::Parser, description), false }, ::hilti::rt::type_info::struct_::Field{ "mime_types", &::hilti::rt::type_info::any, offsetof(::spicy::rt::Parser, mime_types), false }, ::hilti::rt::type_info::struct_::Field{ "ports", &type_info::__ti_vectorx30spicy_rt_ParserPort, offsetof(::spicy::rt::Parser, ports), false }})) }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_ParserPort = { "spicy_rt::ParserPort", "spicy_rt::ParserPort", new ::hilti::rt::type_info::Library() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_Sink = { "spicy_rt::Sink", "spicy_rt::Sink", new ::hilti::rt::type_info::Struct(std::vector<::hilti::rt::type_info::struct_::Field>({})) }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_SinkState = { "spicy_rt::SinkState", "spicy_rt::SinkState", new ::hilti::rt::type_info::Library() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_TypeInfo = { "spicy_rt::TypeInfo", "spicy_rt::TypeInfo", new ::hilti::rt::type_info::Library() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_UnitAlreadyConnected = { "spicy_rt::UnitAlreadyConnected", "spicy_rt::UnitAlreadyConnected", new ::hilti::rt::type_info::Exception() }; + const ::hilti::rt::TypeInfo __ti_spicy_rt_UnitContext = { "spicy_rt::UnitContext", "spicy_rt::UnitContext", new ::hilti::rt::type_info::Library() }; + const ::hilti::rt::TypeInfo __ti_vectorx30spicy_rt_ParserPort = { {}, "vector", new ::hilti::rt::type_info::Vector(&type_info::__ti_spicy_rt_ParserPort, ::hilti::rt::type_info::Vector::accessor<::spicy::rt::ParserPort>()) }; +} } + HILTI_PRE_INIT(__hlt::Foo::__register_module) extern void __hlt::Foo::__init_module() { __location__("<...>/spicyc-hello-world.spicy:8:1"); - hilti::rt::print(std::string("Hello, world!"), hilti::rt::Bool(true)); + ::hilti::rt::print(std::string("Hello, world!"), ::hilti::rt::Bool(true)); __location__("<...>/spicyc-hello-world.spicy:9:1"); - hilti::rt::printValues(std::make_tuple(std::string("Hello"), std::string("world!")), hilti::rt::Bool(true)); + ::hilti::rt::printValues(std::make_tuple(std::string("Hello"), std::string("world!")), ::hilti::rt::Bool(true)); } -extern void __hlt::Foo::__register_module() { hilti::rt::detail::registerModule({ "Foo", &__init_module, nullptr, nullptr}); } +extern void __hlt::Foo::__register_module() { ::hilti::rt::detail::registerModule({ "Foo", &__init_module, nullptr, nullptr}); } /* __HILTI_LINKER_V1__ {"module":"Foo","namespace":"__hlt::Foo","path":"<...>/spicyc-hello-world.spicy","version":1} diff --git a/tests/Baseline/spicy.types.bytes.parse-length-until-fail/output b/tests/Baseline/spicy.types.bytes.parse-length-until-fail/output index 28465f97b..8f220f978 100644 --- a/tests/Baseline/spicy.types.bytes.parse-length-until-fail/output +++ b/tests/Baseline/spicy.types.bytes.parse-length-until-fail/output @@ -1,2 +1,2 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until expression found (<...>/parse-length-until-fail.spicy:7:8) +[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until expression found (<...>/parse-length-until-fail.spicy:7:29) diff --git a/tests/Baseline/spicy.types.bytes.parse-until-error/output b/tests/Baseline/spicy.types.bytes.parse-until-error/output index 6d56562b5..455b7c1d8 100644 --- a/tests/Baseline/spicy.types.bytes.parse-until-error/output +++ b/tests/Baseline/spicy.types.bytes.parse-until-error/output @@ -1,2 +1,2 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until expression found (<...>/parse-until-error.spicy:8:10) +[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until expression found (<...>/parse-until-error.spicy:8:23) diff --git a/tests/Baseline/spicy.types.function.overloading/output b/tests/Baseline/spicy.types.function.overloading/output new file mode 100644 index 000000000..17565bb6b --- /dev/null +++ b/tests/Baseline/spicy.types.function.overloading/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. +int64, 42 +string, foo diff --git a/tests/Baseline/spicy.types.integer.parse-byte-order-from-parameter-fail/output b/tests/Baseline/spicy.types.integer.parse-byte-order-from-parameter-fail/output index 221abdcb4..b44fcf76b 100644 --- a/tests/Baseline/spicy.types.integer.parse-byte-order-from-parameter-fail/output +++ b/tests/Baseline/spicy.types.integer.parse-byte-order-from-parameter-fail/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/parse-byte-order-from-parameter-fail.spicy:12:17: type mismatch, expression has type 'string', but expected 'spicy::ByteOrder' +[error] <...>/parse-byte-order-from-parameter-fail.spicy:10:10-12:2: &byte-order expression must be of spicy::ByteOrder, but is of type string [error] spicy-driver: aborting after errors diff --git a/tests/Baseline/spicy.types.integer.wrong-cast/output b/tests/Baseline/spicy.types.integer.wrong-cast/output index bf2161257..c9b990c08 100644 --- a/tests/Baseline/spicy.types.integer.wrong-cast/output +++ b/tests/Baseline/spicy.types.integer.wrong-cast/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/wrong-cast.spicy:8:7: cannot resolve operator: cast<>>(>) +[error] <...>/wrong-cast.spicy:8:7: unsupported operator: cast<>>(>) [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.integer.wrong-int-mix/output b/tests/Baseline/spicy.types.integer.wrong-int-mix/output index 441431b31..f0aa93769 100644 --- a/tests/Baseline/spicy.types.integer.wrong-int-mix/output +++ b/tests/Baseline/spicy.types.integer.wrong-int-mix/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/wrong-int-mix.spicy:9:7: cannot resolve operator: > + > +[error] <...>/wrong-int-mix.spicy:9:7: unsupported operator: > + > [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output b/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output new file mode 100644 index 000000000..d2043cfbf --- /dev/null +++ b/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output @@ -0,0 +1,731 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[debug/ast-declarations] # [Spicy] a +[debug/ast-declarations] - Type "X" (a::X) +[debug/ast-declarations] - Expression "self" (a::X::self) +[debug/ast-declarations] - Expression "__dd" (a::X::__dd) +[debug/ast-declarations] - ImportedModule "spicy_rt" (spicy_rt) +[debug/ast-declarations] - ImportedModule "hilti" (hilti) +[debug/ast-declarations] # [Spicy] b +[debug/ast-declarations] - ImportedModule "a" (a) +[debug/ast-declarations] - UnitHook "a::X::x" (a::X::x) +[debug/ast-declarations] - Expression "__dd" (a::X::x::__dd) +[debug/ast-declarations] - ImportedModule "spicy_rt" (spicy_rt) +[debug/ast-declarations] - ImportedModule "hilti" (hilti) +[debug/ast-declarations] # [Spicy] spicy_rt +[debug/ast-declarations] - Type "ParseError" (spicy_rt::ParseError) +[debug/ast-declarations] - Type "Backtrack" (spicy_rt::Backtrack) +[debug/ast-declarations] - Type "UnitAlreadyConnected" (spicy_rt::UnitAlreadyConnected) +[debug/ast-declarations] - Type "SinkState" (spicy_rt::SinkState) +[debug/ast-declarations] - Type "ParsedUnit" (spicy_rt::ParsedUnit) +[debug/ast-declarations] - Type "TypeInfo" (spicy_rt::TypeInfo) +[debug/ast-declarations] - Type "Sink" (spicy_rt::Sink) +[debug/ast-declarations] - Expression "self" (spicy_rt::Sink::self) +[debug/ast-declarations] - Field "close" (spicy_rt::Sink::close) +[debug/ast-declarations] - Field "connect" (spicy_rt::Sink::connect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::Sink::connect::unit) +[debug/ast-declarations] - Field "connect_filter" (spicy_rt::Sink::connect_filter) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::Sink::connect_filter::unit) +[debug/ast-declarations] - Field "connect_mime_type" (spicy_rt::Sink::connect_mime_type) +[debug/ast-declarations] - Parameter "mime_type" (spicy_rt::Sink::connect_mime_type::mime_type) +[debug/ast-declarations] - Field "connect_mime_type" (spicy_rt::Sink::connect_mime_type) +[debug/ast-declarations] - Parameter "mime_type" (spicy_rt::Sink::connect_mime_type::mime_type) +[debug/ast-declarations] - Field "gap" (spicy_rt::Sink::gap) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::gap::seq) +[debug/ast-declarations] - Parameter "len" (spicy_rt::Sink::gap::len) +[debug/ast-declarations] - Field "sequence_number" (spicy_rt::Sink::sequence_number) +[debug/ast-declarations] - Field "set_auto_trim" (spicy_rt::Sink::set_auto_trim) +[debug/ast-declarations] - Parameter "enable" (spicy_rt::Sink::set_auto_trim::enable) +[debug/ast-declarations] - Field "set_initial_sequence_number" (spicy_rt::Sink::set_initial_sequence_number) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::set_initial_sequence_number::seq) +[debug/ast-declarations] - Field "set_policy" (spicy_rt::Sink::set_policy) +[debug/ast-declarations] - Parameter "policy" (spicy_rt::Sink::set_policy::policy) +[debug/ast-declarations] - Field "size" (spicy_rt::Sink::size) +[debug/ast-declarations] - Field "skip" (spicy_rt::Sink::skip) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::skip::seq) +[debug/ast-declarations] - Field "trim" (spicy_rt::Sink::trim) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::trim::seq) +[debug/ast-declarations] - Field "write" (spicy_rt::Sink::write) +[debug/ast-declarations] - Parameter "data" (spicy_rt::Sink::write::data) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::write::seq) +[debug/ast-declarations] - Parameter "len" (spicy_rt::Sink::write::len) +[debug/ast-declarations] - Type "HiltiResumable" (spicy_rt::HiltiResumable) +[debug/ast-declarations] - Type "Filters" (spicy_rt::Filters) +[debug/ast-declarations] - Type "Forward" (spicy_rt::Forward) +[debug/ast-declarations] - Function "filter_init" (spicy_rt::filter_init) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_init::unit) +[debug/ast-declarations] - Parameter "data" (spicy_rt::filter_init::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::filter_init::cur) +[debug/ast-declarations] - Function "filter_connect" (spicy_rt::filter_connect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_connect::unit) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_connect::filter) +[debug/ast-declarations] - Function "filter_disconnect" (spicy_rt::filter_disconnect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_disconnect::unit) +[debug/ast-declarations] - Function "filter_forward" (spicy_rt::filter_forward) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_forward::filter) +[debug/ast-declarations] - Parameter "b" (spicy_rt::filter_forward::b) +[debug/ast-declarations] - Function "filter_forward_eod" (spicy_rt::filter_forward_eod) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_forward_eod::filter) +[debug/ast-declarations] - Type "UnitContext" (spicy_rt::UnitContext) +[debug/ast-declarations] - Function "createContext" (spicy_rt::createContext) +[debug/ast-declarations] - Parameter "ctx" (spicy_rt::createContext::ctx) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::createContext::ti) +[debug/ast-declarations] - Function "setContext" (spicy_rt::setContext) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::setContext::unit) +[debug/ast-declarations] - Parameter "ctx" (spicy_rt::setContext::ctx) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::setContext::ti) +[debug/ast-declarations] - Type "Parser" (spicy_rt::Parser) +[debug/ast-declarations] - Expression "self" (spicy_rt::Parser::self) +[debug/ast-declarations] - Field "name" (spicy_rt::Parser::name) +[debug/ast-declarations] - Field "parse1" (spicy_rt::Parser::parse1) +[debug/ast-declarations] - Field "parse2" (spicy_rt::Parser::parse2) +[debug/ast-declarations] - Field "parse3" (spicy_rt::Parser::parse3) +[debug/ast-declarations] - Field "context_new" (spicy_rt::Parser::context_new) +[debug/ast-declarations] - Field "type_info" (spicy_rt::Parser::type_info) +[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 "Direction" (spicy_rt::Direction) +[debug/ast-declarations] - Constant "Originator" (spicy_rt::Direction::Originator) +[debug/ast-declarations] - Constant "Responder" (spicy_rt::Direction::Responder) +[debug/ast-declarations] - Constant "Both" (spicy_rt::Direction::Both) +[debug/ast-declarations] - Constant "Undef" (spicy_rt::Direction::Undef) +[debug/ast-declarations] - Type "FindDirection" (spicy_rt::FindDirection) +[debug/ast-declarations] - Type "MIMEType" (spicy_rt::MIMEType) +[debug/ast-declarations] - Type "ParserPort" (spicy_rt::ParserPort) +[debug/ast-declarations] - Function "registerParser" (spicy_rt::registerParser) +[debug/ast-declarations] - Parameter "parse_func" (spicy_rt::registerParser::parse_func) +[debug/ast-declarations] - Parameter "linker_scope" (spicy_rt::registerParser::linker_scope) +[debug/ast-declarations] - Parameter "instance" (spicy_rt::registerParser::instance) +[debug/ast-declarations] - Function "printParserState" (spicy_rt::printParserState) +[debug/ast-declarations] - Parameter "unit_id" (spicy_rt::printParserState::unit_id) +[debug/ast-declarations] - Parameter "data" (spicy_rt::printParserState::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::printParserState::cur) +[debug/ast-declarations] - Parameter "lahead" (spicy_rt::printParserState::lahead) +[debug/ast-declarations] - Parameter "lahead_end" (spicy_rt::printParserState::lahead_end) +[debug/ast-declarations] - Parameter "literal_mode" (spicy_rt::printParserState::literal_mode) +[debug/ast-declarations] - Parameter "trim" (spicy_rt::printParserState::trim) +[debug/ast-declarations] - Function "waitForInputOrEod" (spicy_rt::waitForInputOrEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInputOrEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInputOrEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInputOrEod::filters) +[debug/ast-declarations] - Function "waitForInputOrEod" (spicy_rt::waitForInputOrEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInputOrEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInputOrEod::cur) +[debug/ast-declarations] - Parameter "n" (spicy_rt::waitForInputOrEod::n) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInputOrEod::filters) +[debug/ast-declarations] - Function "waitForInput" (spicy_rt::waitForInput) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInput::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInput::cur) +[debug/ast-declarations] - Parameter "error_msg" (spicy_rt::waitForInput::error_msg) +[debug/ast-declarations] - Parameter "location" (spicy_rt::waitForInput::location) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInput::filters) +[debug/ast-declarations] - Function "waitForInput" (spicy_rt::waitForInput) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInput::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInput::cur) +[debug/ast-declarations] - Parameter "n" (spicy_rt::waitForInput::n) +[debug/ast-declarations] - Parameter "error_msg" (spicy_rt::waitForInput::error_msg) +[debug/ast-declarations] - Parameter "location" (spicy_rt::waitForInput::location) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInput::filters) +[debug/ast-declarations] - Function "waitForEod" (spicy_rt::waitForEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForEod::filters) +[debug/ast-declarations] - Function "atEod" (spicy_rt::atEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::atEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::atEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::atEod::filters) +[debug/ast-declarations] - Function "unit_find" (spicy_rt::unit_find) +[debug/ast-declarations] - Parameter "begin_" (spicy_rt::unit_find::begin_) +[debug/ast-declarations] - Parameter "end_" (spicy_rt::unit_find::end_) +[debug/ast-declarations] - Parameter "i" (spicy_rt::unit_find::i) +[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) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::initializeParsedUnit::ti) +[debug/ast-declarations] # [Spicy] hilti +[debug/ast-declarations] - Type "BitOrder" (hilti::BitOrder) +[debug/ast-declarations] - Constant "LSB0" (hilti::BitOrder::LSB0) +[debug/ast-declarations] - Constant "MSB0" (hilti::BitOrder::MSB0) +[debug/ast-declarations] - Constant "Undef" (hilti::BitOrder::Undef) +[debug/ast-declarations] - Type "ByteOrder" (hilti::ByteOrder) +[debug/ast-declarations] - Constant "Little" (hilti::ByteOrder::Little) +[debug/ast-declarations] - Constant "Big" (hilti::ByteOrder::Big) +[debug/ast-declarations] - Constant "Network" (hilti::ByteOrder::Network) +[debug/ast-declarations] - Constant "Host" (hilti::ByteOrder::Host) +[debug/ast-declarations] - Constant "Undef" (hilti::ByteOrder::Undef) +[debug/ast-declarations] - Type "Side" (hilti::Side) +[debug/ast-declarations] - Constant "Left" (hilti::Side::Left) +[debug/ast-declarations] - Constant "Right" (hilti::Side::Right) +[debug/ast-declarations] - Constant "Both" (hilti::Side::Both) +[debug/ast-declarations] - Constant "Undef" (hilti::Side::Undef) +[debug/ast-declarations] - Type "AddressFamily" (hilti::AddressFamily) +[debug/ast-declarations] - Constant "IPv4" (hilti::AddressFamily::IPv4) +[debug/ast-declarations] - Constant "IPv6" (hilti::AddressFamily::IPv6) +[debug/ast-declarations] - Constant "Undef" (hilti::AddressFamily::Undef) +[debug/ast-declarations] - Type "RealType" (hilti::RealType) +[debug/ast-declarations] - Constant "IEEE754_Single" (hilti::RealType::IEEE754_Single) +[debug/ast-declarations] - Constant "IEEE754_Double" (hilti::RealType::IEEE754_Double) +[debug/ast-declarations] - Constant "Undef" (hilti::RealType::Undef) +[debug/ast-declarations] - Type "Protocol" (hilti::Protocol) +[debug/ast-declarations] - Constant "TCP" (hilti::Protocol::TCP) +[debug/ast-declarations] - Constant "UDP" (hilti::Protocol::UDP) +[debug/ast-declarations] - Constant "ICMP" (hilti::Protocol::ICMP) +[debug/ast-declarations] - Constant "Undef" (hilti::Protocol::Undef) +[debug/ast-declarations] - Type "Charset" (hilti::Charset) +[debug/ast-declarations] - Constant "ASCII" (hilti::Charset::ASCII) +[debug/ast-declarations] - Constant "UTF8" (hilti::Charset::UTF8) +[debug/ast-declarations] - Constant "Undef" (hilti::Charset::Undef) +[debug/ast-declarations] - Type "Captures" (hilti::Captures) +[debug/ast-declarations] - Type "MatchState" (hilti::MatchState) +[debug/ast-declarations] - Expression "self" (hilti::MatchState::self) +[debug/ast-declarations] - Field "captures" (hilti::MatchState::captures) +[debug/ast-declarations] - Parameter "data" (hilti::MatchState::captures::data) +[debug/ast-declarations] - Function "print" (hilti::print) +[debug/ast-declarations] - Parameter "obj" (hilti::print::obj) +[debug/ast-declarations] - Parameter "newline" (hilti::print::newline) +[debug/ast-declarations] - Function "printValues" (hilti::printValues) +[debug/ast-declarations] - Parameter "t" (hilti::printValues::t) +[debug/ast-declarations] - Parameter "newline" (hilti::printValues::newline) +[debug/ast-declarations] - Function "debug" (hilti::debug) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debug::dbg_stream) +[debug/ast-declarations] - Parameter "obj" (hilti::debug::obj) +[debug/ast-declarations] - Function "debugIndent" (hilti::debugIndent) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debugIndent::dbg_stream) +[debug/ast-declarations] - Function "debugDedent" (hilti::debugDedent) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debugDedent::dbg_stream) +[debug/ast-declarations] - Function "current_time" (hilti::current_time) +[debug/ast-declarations] - Function "mktime" (hilti::mktime) +[debug/ast-declarations] - Parameter "y" (hilti::mktime::y) +[debug/ast-declarations] - Parameter "m" (hilti::mktime::m) +[debug/ast-declarations] - Parameter "d" (hilti::mktime::d) +[debug/ast-declarations] - Parameter "H" (hilti::mktime::H) +[debug/ast-declarations] - Parameter "M" (hilti::mktime::M) +[debug/ast-declarations] - Parameter "S" (hilti::mktime::S) +[debug/ast-declarations] - Function "abort" (hilti::abort) +[debug/ast-declarations] - Function "linker_scope" (hilti::linker_scope) +[debug/ast-declarations] - Type "Exception" (hilti::Exception) +[debug/ast-declarations] - Type "RuntimeError" (hilti::RuntimeError) +[debug/ast-declarations] # [HILTI] a +[debug/ast-declarations] - Type "X" (a::X) +[debug/ast-declarations] - Expression "self" (a::X::self) +[debug/ast-declarations] - Field "x" (a::X::x) +[debug/ast-declarations] - Field "__on_x" (a::X::__on_x) +[debug/ast-declarations] - Parameter "__dd" (a::X::__on_x::__dd) +[debug/ast-declarations] - Field "__on_0x25_init" (a::X::__on_0x25_init) +[debug/ast-declarations] - Field "__on_0x25_done" (a::X::__on_0x25_done) +[debug/ast-declarations] - Field "__on_0x25_error" (a::X::__on_0x25_error) +[debug/ast-declarations] - Field "__str__" (a::X::__str__) +[debug/ast-declarations] - Field "__on_0x25_finally" (a::X::__on_0x25_finally) +[debug/ast-declarations] - Field "__on_0x25_gap" (a::X::__on_0x25_gap) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_gap::seq) +[debug/ast-declarations] - Parameter "len" (a::X::__on_0x25_gap::len) +[debug/ast-declarations] - Field "__on_0x25_overlap" (a::X::__on_0x25_overlap) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_overlap::seq) +[debug/ast-declarations] - Parameter "old" (a::X::__on_0x25_overlap::old) +[debug/ast-declarations] - Parameter "new_" (a::X::__on_0x25_overlap::new_) +[debug/ast-declarations] - Field "__on_0x25_skipped" (a::X::__on_0x25_skipped) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_skipped::seq) +[debug/ast-declarations] - Field "__on_0x25_undelivered" (a::X::__on_0x25_undelivered) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_undelivered::seq) +[debug/ast-declarations] - Parameter "data" (a::X::__on_0x25_undelivered::data) +[debug/ast-declarations] - Field "__parser" (a::X::__parser) +[debug/ast-declarations] - Field "__sink" (a::X::__sink) +[debug/ast-declarations] - Field "__filters" (a::X::__filters) +[debug/ast-declarations] - Field "__parse_stage1" (a::X::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_stage1::__lahe) +[debug/ast-declarations] - Field "parse1" (a::X::parse1) +[debug/ast-declarations] - Parameter "data" (a::X::parse1::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse1::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse1::context) +[debug/ast-declarations] - Field "parse2" (a::X::parse2) +[debug/ast-declarations] - Parameter "unit" (a::X::parse2::unit) +[debug/ast-declarations] - Parameter "data" (a::X::parse2::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse2::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse2::context) +[debug/ast-declarations] - Field "parse3" (a::X::parse3) +[debug/ast-declarations] - Parameter "gunit" (a::X::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (a::X::parse3::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse3::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse3::context) +[debug/ast-declarations] - Field "__parse_a_X_stage2" (a::X::__parse_a_X_stage2) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_a_X_stage2::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_a_X_stage2::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_a_X_stage2::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_a_X_stage2::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_a_X_stage2::__lahe) +[debug/ast-declarations] - ImportedModule "spicy_rt" (spicy_rt) +[debug/ast-declarations] - ImportedModule "hilti" (hilti) +[debug/ast-declarations] - Constant "__feat%a__X%supports_filters" (a::__feat%a__X%supports_filters) +[debug/ast-declarations] - Constant "__feat%a__X%supports_sinks" (a::__feat%a__X%supports_sinks) +[debug/ast-declarations] - Function "a::X::__parse_stage1" (a::X::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_stage1::__lahe) +[debug/ast-declarations] - LocalVariable "__result" (a::X::__parse_stage1::__result) +[debug/ast-declarations] - LocalVariable "filtered" (a::X::__parse_stage1::filtered) +[debug/ast-declarations] - LocalVariable "filtered_data" (a::X::__parse_stage1::filtered_data) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_a_X_stage2::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_a_X_stage2::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_a_X_stage2::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_a_X_stage2::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_a_X_stage2::__lahe) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_a_X_stage2::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_a_X_stage2::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_a_X_stage2::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_a_X_stage2::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_a_X_stage2::__lahe) +[debug/ast-declarations] - Function "a::X::__parse_a_X_stage2" (a::X::__parse_a_X_stage2) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_a_X_stage2::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_a_X_stage2::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_a_X_stage2::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_a_X_stage2::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_a_X_stage2::__lahe) +[debug/ast-declarations] - LocalVariable "__result" (a::X::__parse_a_X_stage2::__result) +[debug/ast-declarations] - Constant "Little" (hilti::ByteOrder::Little) +[debug/ast-declarations] - Constant "Big" (hilti::ByteOrder::Big) +[debug/ast-declarations] - Constant "Network" (hilti::ByteOrder::Network) +[debug/ast-declarations] - Constant "Host" (hilti::ByteOrder::Host) +[debug/ast-declarations] - Constant "Undef" (hilti::ByteOrder::Undef) +[debug/ast-declarations] - Parameter "__dd" (a::X::__on_x::__dd) +[debug/ast-declarations] - Function "a::X::parse1" (a::X::parse1) +[debug/ast-declarations] - Parameter "data" (a::X::parse1::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse1::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse1::context) +[debug/ast-declarations] - LocalVariable "unit" (a::X::parse1::unit) +[debug/ast-declarations] - LocalVariable "ncur" (a::X::parse1::ncur) +[debug/ast-declarations] - LocalVariable "lahead" (a::X::parse1::lahead) +[debug/ast-declarations] - LocalVariable "lahead_end" (a::X::parse1::lahead_end) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_stage1::__lahe) +[debug/ast-declarations] - Function "a::X::parse3" (a::X::parse3) +[debug/ast-declarations] - Parameter "gunit" (a::X::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (a::X::parse3::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse3::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse3::context) +[debug/ast-declarations] - LocalVariable "unit" (a::X::parse3::unit) +[debug/ast-declarations] - Expression "self" (a::X::self) +[debug/ast-declarations] - Field "x" (a::X::x) +[debug/ast-declarations] - Field "__on_x" (a::X::__on_x) +[debug/ast-declarations] - Parameter "__dd" (a::X::__on_x::__dd) +[debug/ast-declarations] - Field "__on_0x25_init" (a::X::__on_0x25_init) +[debug/ast-declarations] - Field "__on_0x25_done" (a::X::__on_0x25_done) +[debug/ast-declarations] - Field "__on_0x25_error" (a::X::__on_0x25_error) +[debug/ast-declarations] - Field "__str__" (a::X::__str__) +[debug/ast-declarations] - Field "__on_0x25_finally" (a::X::__on_0x25_finally) +[debug/ast-declarations] - Field "__on_0x25_gap" (a::X::__on_0x25_gap) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_gap::seq) +[debug/ast-declarations] - Parameter "len" (a::X::__on_0x25_gap::len) +[debug/ast-declarations] - Field "__on_0x25_overlap" (a::X::__on_0x25_overlap) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_overlap::seq) +[debug/ast-declarations] - Parameter "old" (a::X::__on_0x25_overlap::old) +[debug/ast-declarations] - Parameter "new_" (a::X::__on_0x25_overlap::new_) +[debug/ast-declarations] - Field "__on_0x25_skipped" (a::X::__on_0x25_skipped) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_skipped::seq) +[debug/ast-declarations] - Field "__on_0x25_undelivered" (a::X::__on_0x25_undelivered) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_undelivered::seq) +[debug/ast-declarations] - Parameter "data" (a::X::__on_0x25_undelivered::data) +[debug/ast-declarations] - Field "__parser" (a::X::__parser) +[debug/ast-declarations] - Field "__sink" (a::X::__sink) +[debug/ast-declarations] - Field "__filters" (a::X::__filters) +[debug/ast-declarations] - Field "__parse_stage1" (a::X::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_stage1::__lahe) +[debug/ast-declarations] - Field "parse1" (a::X::parse1) +[debug/ast-declarations] - Parameter "data" (a::X::parse1::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse1::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse1::context) +[debug/ast-declarations] - Field "parse2" (a::X::parse2) +[debug/ast-declarations] - Parameter "unit" (a::X::parse2::unit) +[debug/ast-declarations] - Parameter "data" (a::X::parse2::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse2::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse2::context) +[debug/ast-declarations] - Field "parse3" (a::X::parse3) +[debug/ast-declarations] - Parameter "gunit" (a::X::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (a::X::parse3::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse3::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse3::context) +[debug/ast-declarations] - Field "__parse_a_X_stage2" (a::X::__parse_a_X_stage2) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_a_X_stage2::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_a_X_stage2::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_a_X_stage2::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_a_X_stage2::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_a_X_stage2::__lahe) +[debug/ast-declarations] - LocalVariable "ncur" (a::X::parse3::ncur) +[debug/ast-declarations] - LocalVariable "lahead" (a::X::parse3::lahead) +[debug/ast-declarations] - LocalVariable "lahead_end" (a::X::parse3::lahead_end) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_stage1::__lahe) +[debug/ast-declarations] - Function "a::X::parse2" (a::X::parse2) +[debug/ast-declarations] - Parameter "unit" (a::X::parse2::unit) +[debug/ast-declarations] - Parameter "data" (a::X::parse2::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse2::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse2::context) +[debug/ast-declarations] - LocalVariable "ncur" (a::X::parse2::ncur) +[debug/ast-declarations] - LocalVariable "lahead" (a::X::parse2::lahead) +[debug/ast-declarations] - LocalVariable "lahead_end" (a::X::parse2::lahead_end) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_stage1::__lahe) +[debug/ast-declarations] - Function "__register_a_X" (a::__register_a_X) +[debug/ast-declarations] - Expression "self" (a::__register_a_X::::self) +[debug/ast-declarations] - Field "name" (a::__register_a_X::::name) +[debug/ast-declarations] - Field "parse1" (a::__register_a_X::::parse1) +[debug/ast-declarations] - Parameter "data" (a::X::parse1::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse1::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse1::context) +[debug/ast-declarations] - Field "parse2" (a::__register_a_X::::parse2) +[debug/ast-declarations] - Parameter "unit" (a::X::parse2::unit) +[debug/ast-declarations] - Parameter "data" (a::X::parse2::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse2::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse2::context) +[debug/ast-declarations] - Field "parse3" (a::__register_a_X::::parse3) +[debug/ast-declarations] - Parameter "gunit" (a::X::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (a::X::parse3::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse3::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse3::context) +[debug/ast-declarations] - Field "context_new" (a::__register_a_X::::context_new) +[debug/ast-declarations] - Field "type_info" (a::__register_a_X::::type_info) +[debug/ast-declarations] - Field "description" (a::__register_a_X::::description) +[debug/ast-declarations] - Field "mime_types" (a::__register_a_X::::mime_types) +[debug/ast-declarations] - Field "ports" (a::__register_a_X::::ports) +[debug/ast-declarations] - Expression "self" (a::X::self) +[debug/ast-declarations] - Field "x" (a::X::x) +[debug/ast-declarations] - Field "__on_x" (a::X::__on_x) +[debug/ast-declarations] - Parameter "__dd" (a::X::__on_x::__dd) +[debug/ast-declarations] - Field "__on_0x25_init" (a::X::__on_0x25_init) +[debug/ast-declarations] - Field "__on_0x25_done" (a::X::__on_0x25_done) +[debug/ast-declarations] - Field "__on_0x25_error" (a::X::__on_0x25_error) +[debug/ast-declarations] - Field "__str__" (a::X::__str__) +[debug/ast-declarations] - Field "__on_0x25_finally" (a::X::__on_0x25_finally) +[debug/ast-declarations] - Field "__on_0x25_gap" (a::X::__on_0x25_gap) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_gap::seq) +[debug/ast-declarations] - Parameter "len" (a::X::__on_0x25_gap::len) +[debug/ast-declarations] - Field "__on_0x25_overlap" (a::X::__on_0x25_overlap) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_overlap::seq) +[debug/ast-declarations] - Parameter "old" (a::X::__on_0x25_overlap::old) +[debug/ast-declarations] - Parameter "new_" (a::X::__on_0x25_overlap::new_) +[debug/ast-declarations] - Field "__on_0x25_skipped" (a::X::__on_0x25_skipped) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_skipped::seq) +[debug/ast-declarations] - Field "__on_0x25_undelivered" (a::X::__on_0x25_undelivered) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_undelivered::seq) +[debug/ast-declarations] - Parameter "data" (a::X::__on_0x25_undelivered::data) +[debug/ast-declarations] - Field "__parser" (a::X::__parser) +[debug/ast-declarations] - Field "__sink" (a::X::__sink) +[debug/ast-declarations] - Field "__filters" (a::X::__filters) +[debug/ast-declarations] - Field "__parse_stage1" (a::X::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_stage1::__lahe) +[debug/ast-declarations] - Field "parse1" (a::X::parse1) +[debug/ast-declarations] - Parameter "data" (a::X::parse1::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse1::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse1::context) +[debug/ast-declarations] - Field "parse2" (a::X::parse2) +[debug/ast-declarations] - Parameter "unit" (a::X::parse2::unit) +[debug/ast-declarations] - Parameter "data" (a::X::parse2::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse2::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse2::context) +[debug/ast-declarations] - Field "parse3" (a::X::parse3) +[debug/ast-declarations] - Parameter "gunit" (a::X::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (a::X::parse3::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse3::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse3::context) +[debug/ast-declarations] - Field "__parse_a_X_stage2" (a::X::__parse_a_X_stage2) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_a_X_stage2::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_a_X_stage2::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_a_X_stage2::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_a_X_stage2::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_a_X_stage2::__lahe) +[debug/ast-declarations] - Expression "self" (a::X::self) +[debug/ast-declarations] - Field "x" (a::X::x) +[debug/ast-declarations] - Field "__on_x" (a::X::__on_x) +[debug/ast-declarations] - Parameter "__dd" (a::X::__on_x::__dd) +[debug/ast-declarations] - Field "__on_0x25_init" (a::X::__on_0x25_init) +[debug/ast-declarations] - Field "__on_0x25_done" (a::X::__on_0x25_done) +[debug/ast-declarations] - Field "__on_0x25_error" (a::X::__on_0x25_error) +[debug/ast-declarations] - Field "__str__" (a::X::__str__) +[debug/ast-declarations] - Field "__on_0x25_finally" (a::X::__on_0x25_finally) +[debug/ast-declarations] - Field "__on_0x25_gap" (a::X::__on_0x25_gap) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_gap::seq) +[debug/ast-declarations] - Parameter "len" (a::X::__on_0x25_gap::len) +[debug/ast-declarations] - Field "__on_0x25_overlap" (a::X::__on_0x25_overlap) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_overlap::seq) +[debug/ast-declarations] - Parameter "old" (a::X::__on_0x25_overlap::old) +[debug/ast-declarations] - Parameter "new_" (a::X::__on_0x25_overlap::new_) +[debug/ast-declarations] - Field "__on_0x25_skipped" (a::X::__on_0x25_skipped) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_skipped::seq) +[debug/ast-declarations] - Field "__on_0x25_undelivered" (a::X::__on_0x25_undelivered) +[debug/ast-declarations] - Parameter "seq" (a::X::__on_0x25_undelivered::seq) +[debug/ast-declarations] - Parameter "data" (a::X::__on_0x25_undelivered::data) +[debug/ast-declarations] - Field "__parser" (a::X::__parser) +[debug/ast-declarations] - Field "__sink" (a::X::__sink) +[debug/ast-declarations] - Field "__filters" (a::X::__filters) +[debug/ast-declarations] - Field "__parse_stage1" (a::X::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_stage1::__lahe) +[debug/ast-declarations] - Field "parse1" (a::X::parse1) +[debug/ast-declarations] - Parameter "data" (a::X::parse1::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse1::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse1::context) +[debug/ast-declarations] - Field "parse2" (a::X::parse2) +[debug/ast-declarations] - Parameter "unit" (a::X::parse2::unit) +[debug/ast-declarations] - Parameter "data" (a::X::parse2::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse2::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse2::context) +[debug/ast-declarations] - Field "parse3" (a::X::parse3) +[debug/ast-declarations] - Parameter "gunit" (a::X::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (a::X::parse3::data) +[debug/ast-declarations] - Parameter "cur" (a::X::parse3::cur) +[debug/ast-declarations] - Parameter "context" (a::X::parse3::context) +[debug/ast-declarations] - Field "__parse_a_X_stage2" (a::X::__parse_a_X_stage2) +[debug/ast-declarations] - Parameter "__data" (a::X::__parse_a_X_stage2::__data) +[debug/ast-declarations] - Parameter "__cur" (a::X::__parse_a_X_stage2::__cur) +[debug/ast-declarations] - Parameter "__trim" (a::X::__parse_a_X_stage2::__trim) +[debug/ast-declarations] - Parameter "__lah" (a::X::__parse_a_X_stage2::__lah) +[debug/ast-declarations] - Parameter "__lahe" (a::X::__parse_a_X_stage2::__lahe) +[debug/ast-declarations] # [HILTI] b +[debug/ast-declarations] - ImportedModule "a" (a) +[debug/ast-declarations] - Function "a::X::__on_x" (a::X::__on_x) +[debug/ast-declarations] - Parameter "__dd" (a::X::__on_x::__dd) +[debug/ast-declarations] - ImportedModule "spicy_rt" (spicy_rt) +[debug/ast-declarations] - ImportedModule "hilti" (hilti) +[debug/ast-declarations] # [HILTI] hilti +[debug/ast-declarations] - Type "BitOrder" (hilti::BitOrder) +[debug/ast-declarations] - Constant "LSB0" (hilti::BitOrder::LSB0) +[debug/ast-declarations] - Constant "MSB0" (hilti::BitOrder::MSB0) +[debug/ast-declarations] - Constant "Undef" (hilti::BitOrder::Undef) +[debug/ast-declarations] - Type "ByteOrder" (hilti::ByteOrder) +[debug/ast-declarations] - Constant "Little" (hilti::ByteOrder::Little) +[debug/ast-declarations] - Constant "Big" (hilti::ByteOrder::Big) +[debug/ast-declarations] - Constant "Network" (hilti::ByteOrder::Network) +[debug/ast-declarations] - Constant "Host" (hilti::ByteOrder::Host) +[debug/ast-declarations] - Constant "Undef" (hilti::ByteOrder::Undef) +[debug/ast-declarations] - Type "Side" (hilti::Side) +[debug/ast-declarations] - Constant "Left" (hilti::Side::Left) +[debug/ast-declarations] - Constant "Right" (hilti::Side::Right) +[debug/ast-declarations] - Constant "Both" (hilti::Side::Both) +[debug/ast-declarations] - Constant "Undef" (hilti::Side::Undef) +[debug/ast-declarations] - Type "AddressFamily" (hilti::AddressFamily) +[debug/ast-declarations] - Constant "IPv4" (hilti::AddressFamily::IPv4) +[debug/ast-declarations] - Constant "IPv6" (hilti::AddressFamily::IPv6) +[debug/ast-declarations] - Constant "Undef" (hilti::AddressFamily::Undef) +[debug/ast-declarations] - Type "RealType" (hilti::RealType) +[debug/ast-declarations] - Constant "IEEE754_Single" (hilti::RealType::IEEE754_Single) +[debug/ast-declarations] - Constant "IEEE754_Double" (hilti::RealType::IEEE754_Double) +[debug/ast-declarations] - Constant "Undef" (hilti::RealType::Undef) +[debug/ast-declarations] - Type "Protocol" (hilti::Protocol) +[debug/ast-declarations] - Constant "TCP" (hilti::Protocol::TCP) +[debug/ast-declarations] - Constant "UDP" (hilti::Protocol::UDP) +[debug/ast-declarations] - Constant "ICMP" (hilti::Protocol::ICMP) +[debug/ast-declarations] - Constant "Undef" (hilti::Protocol::Undef) +[debug/ast-declarations] - Type "Charset" (hilti::Charset) +[debug/ast-declarations] - Constant "ASCII" (hilti::Charset::ASCII) +[debug/ast-declarations] - Constant "UTF8" (hilti::Charset::UTF8) +[debug/ast-declarations] - Constant "Undef" (hilti::Charset::Undef) +[debug/ast-declarations] - Type "Captures" (hilti::Captures) +[debug/ast-declarations] - Type "MatchState" (hilti::MatchState) +[debug/ast-declarations] - Expression "self" (hilti::MatchState::self) +[debug/ast-declarations] - Field "captures" (hilti::MatchState::captures) +[debug/ast-declarations] - Parameter "data" (hilti::MatchState::captures::data) +[debug/ast-declarations] - Function "print" (hilti::print) +[debug/ast-declarations] - Parameter "obj" (hilti::print::obj) +[debug/ast-declarations] - Parameter "newline" (hilti::print::newline) +[debug/ast-declarations] - Function "printValues" (hilti::printValues) +[debug/ast-declarations] - Parameter "t" (hilti::printValues::t) +[debug/ast-declarations] - Parameter "newline" (hilti::printValues::newline) +[debug/ast-declarations] - Function "debug" (hilti::debug) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debug::dbg_stream) +[debug/ast-declarations] - Parameter "obj" (hilti::debug::obj) +[debug/ast-declarations] - Function "debugIndent" (hilti::debugIndent) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debugIndent::dbg_stream) +[debug/ast-declarations] - Function "debugDedent" (hilti::debugDedent) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debugDedent::dbg_stream) +[debug/ast-declarations] - Function "current_time" (hilti::current_time) +[debug/ast-declarations] - Function "mktime" (hilti::mktime) +[debug/ast-declarations] - Parameter "y" (hilti::mktime::y) +[debug/ast-declarations] - Parameter "m" (hilti::mktime::m) +[debug/ast-declarations] - Parameter "d" (hilti::mktime::d) +[debug/ast-declarations] - Parameter "H" (hilti::mktime::H) +[debug/ast-declarations] - Parameter "M" (hilti::mktime::M) +[debug/ast-declarations] - Parameter "S" (hilti::mktime::S) +[debug/ast-declarations] - Function "abort" (hilti::abort) +[debug/ast-declarations] - Function "linker_scope" (hilti::linker_scope) +[debug/ast-declarations] - Type "Exception" (hilti::Exception) +[debug/ast-declarations] - Type "RuntimeError" (hilti::RuntimeError) +[debug/ast-declarations] # [HILTI] spicy_rt +[debug/ast-declarations] - Type "ParseError" (spicy_rt::ParseError) +[debug/ast-declarations] - Type "Backtrack" (spicy_rt::Backtrack) +[debug/ast-declarations] - Type "UnitAlreadyConnected" (spicy_rt::UnitAlreadyConnected) +[debug/ast-declarations] - Type "SinkState" (spicy_rt::SinkState) +[debug/ast-declarations] - Type "ParsedUnit" (spicy_rt::ParsedUnit) +[debug/ast-declarations] - Type "TypeInfo" (spicy_rt::TypeInfo) +[debug/ast-declarations] - Type "Sink" (spicy_rt::Sink) +[debug/ast-declarations] - Expression "self" (spicy_rt::Sink::self) +[debug/ast-declarations] - Field "close" (spicy_rt::Sink::close) +[debug/ast-declarations] - Field "connect" (spicy_rt::Sink::connect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::Sink::connect::unit) +[debug/ast-declarations] - Field "connect_filter" (spicy_rt::Sink::connect_filter) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::Sink::connect_filter::unit) +[debug/ast-declarations] - Field "connect_mime_type" (spicy_rt::Sink::connect_mime_type) +[debug/ast-declarations] - Parameter "mime_type" (spicy_rt::Sink::connect_mime_type::mime_type) +[debug/ast-declarations] - Field "connect_mime_type" (spicy_rt::Sink::connect_mime_type) +[debug/ast-declarations] - Parameter "mime_type" (spicy_rt::Sink::connect_mime_type::mime_type) +[debug/ast-declarations] - Field "gap" (spicy_rt::Sink::gap) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::gap::seq) +[debug/ast-declarations] - Parameter "len" (spicy_rt::Sink::gap::len) +[debug/ast-declarations] - Field "sequence_number" (spicy_rt::Sink::sequence_number) +[debug/ast-declarations] - Field "set_auto_trim" (spicy_rt::Sink::set_auto_trim) +[debug/ast-declarations] - Parameter "enable" (spicy_rt::Sink::set_auto_trim::enable) +[debug/ast-declarations] - Field "set_initial_sequence_number" (spicy_rt::Sink::set_initial_sequence_number) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::set_initial_sequence_number::seq) +[debug/ast-declarations] - Field "set_policy" (spicy_rt::Sink::set_policy) +[debug/ast-declarations] - Parameter "policy" (spicy_rt::Sink::set_policy::policy) +[debug/ast-declarations] - Field "size" (spicy_rt::Sink::size) +[debug/ast-declarations] - Field "skip" (spicy_rt::Sink::skip) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::skip::seq) +[debug/ast-declarations] - Field "trim" (spicy_rt::Sink::trim) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::trim::seq) +[debug/ast-declarations] - Field "write" (spicy_rt::Sink::write) +[debug/ast-declarations] - Parameter "data" (spicy_rt::Sink::write::data) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::write::seq) +[debug/ast-declarations] - Parameter "len" (spicy_rt::Sink::write::len) +[debug/ast-declarations] - Type "HiltiResumable" (spicy_rt::HiltiResumable) +[debug/ast-declarations] - Type "Filters" (spicy_rt::Filters) +[debug/ast-declarations] - Type "Forward" (spicy_rt::Forward) +[debug/ast-declarations] - Function "filter_init" (spicy_rt::filter_init) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_init::unit) +[debug/ast-declarations] - Parameter "data" (spicy_rt::filter_init::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::filter_init::cur) +[debug/ast-declarations] - Function "filter_connect" (spicy_rt::filter_connect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_connect::unit) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_connect::filter) +[debug/ast-declarations] - Function "filter_disconnect" (spicy_rt::filter_disconnect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_disconnect::unit) +[debug/ast-declarations] - Function "filter_forward" (spicy_rt::filter_forward) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_forward::filter) +[debug/ast-declarations] - Parameter "b" (spicy_rt::filter_forward::b) +[debug/ast-declarations] - Function "filter_forward_eod" (spicy_rt::filter_forward_eod) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_forward_eod::filter) +[debug/ast-declarations] - Type "UnitContext" (spicy_rt::UnitContext) +[debug/ast-declarations] - Function "createContext" (spicy_rt::createContext) +[debug/ast-declarations] - Parameter "ctx" (spicy_rt::createContext::ctx) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::createContext::ti) +[debug/ast-declarations] - Function "setContext" (spicy_rt::setContext) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::setContext::unit) +[debug/ast-declarations] - Parameter "ctx" (spicy_rt::setContext::ctx) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::setContext::ti) +[debug/ast-declarations] - Type "Parser" (spicy_rt::Parser) +[debug/ast-declarations] - Expression "self" (spicy_rt::Parser::self) +[debug/ast-declarations] - Field "name" (spicy_rt::Parser::name) +[debug/ast-declarations] - Field "parse1" (spicy_rt::Parser::parse1) +[debug/ast-declarations] - Field "parse2" (spicy_rt::Parser::parse2) +[debug/ast-declarations] - Field "parse3" (spicy_rt::Parser::parse3) +[debug/ast-declarations] - Field "context_new" (spicy_rt::Parser::context_new) +[debug/ast-declarations] - Field "type_info" (spicy_rt::Parser::type_info) +[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 "Direction" (spicy_rt::Direction) +[debug/ast-declarations] - Constant "Originator" (spicy_rt::Direction::Originator) +[debug/ast-declarations] - Constant "Responder" (spicy_rt::Direction::Responder) +[debug/ast-declarations] - Constant "Both" (spicy_rt::Direction::Both) +[debug/ast-declarations] - Constant "Undef" (spicy_rt::Direction::Undef) +[debug/ast-declarations] - Type "FindDirection" (spicy_rt::FindDirection) +[debug/ast-declarations] - Type "MIMEType" (spicy_rt::MIMEType) +[debug/ast-declarations] - Type "ParserPort" (spicy_rt::ParserPort) +[debug/ast-declarations] - Function "registerParser" (spicy_rt::registerParser) +[debug/ast-declarations] - Parameter "parse_func" (spicy_rt::registerParser::parse_func) +[debug/ast-declarations] - Parameter "linker_scope" (spicy_rt::registerParser::linker_scope) +[debug/ast-declarations] - Parameter "instance" (spicy_rt::registerParser::instance) +[debug/ast-declarations] - Function "printParserState" (spicy_rt::printParserState) +[debug/ast-declarations] - Parameter "unit_id" (spicy_rt::printParserState::unit_id) +[debug/ast-declarations] - Parameter "data" (spicy_rt::printParserState::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::printParserState::cur) +[debug/ast-declarations] - Parameter "lahead" (spicy_rt::printParserState::lahead) +[debug/ast-declarations] - Parameter "lahead_end" (spicy_rt::printParserState::lahead_end) +[debug/ast-declarations] - Parameter "literal_mode" (spicy_rt::printParserState::literal_mode) +[debug/ast-declarations] - Parameter "trim" (spicy_rt::printParserState::trim) +[debug/ast-declarations] - Function "waitForInputOrEod" (spicy_rt::waitForInputOrEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInputOrEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInputOrEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInputOrEod::filters) +[debug/ast-declarations] - Function "waitForInputOrEod" (spicy_rt::waitForInputOrEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInputOrEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInputOrEod::cur) +[debug/ast-declarations] - Parameter "n" (spicy_rt::waitForInputOrEod::n) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInputOrEod::filters) +[debug/ast-declarations] - Function "waitForInput" (spicy_rt::waitForInput) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInput::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInput::cur) +[debug/ast-declarations] - Parameter "error_msg" (spicy_rt::waitForInput::error_msg) +[debug/ast-declarations] - Parameter "location" (spicy_rt::waitForInput::location) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInput::filters) +[debug/ast-declarations] - Function "waitForInput" (spicy_rt::waitForInput) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInput::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInput::cur) +[debug/ast-declarations] - Parameter "n" (spicy_rt::waitForInput::n) +[debug/ast-declarations] - Parameter "error_msg" (spicy_rt::waitForInput::error_msg) +[debug/ast-declarations] - Parameter "location" (spicy_rt::waitForInput::location) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInput::filters) +[debug/ast-declarations] - Function "waitForEod" (spicy_rt::waitForEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForEod::filters) +[debug/ast-declarations] - Function "atEod" (spicy_rt::atEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::atEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::atEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::atEod::filters) +[debug/ast-declarations] - Function "unit_find" (spicy_rt::unit_find) +[debug/ast-declarations] - Parameter "begin_" (spicy_rt::unit_find::begin_) +[debug/ast-declarations] - Parameter "end_" (spicy_rt::unit_find::end_) +[debug/ast-declarations] - Parameter "i" (spicy_rt::unit_find::i) +[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) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::initializeParsedUnit::ti) diff --git a/tests/Baseline/spicy.types.unit.canonical-ids/output b/tests/Baseline/spicy.types.unit.canonical-ids/output new file mode 100644 index 000000000..93dee4ac2 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.canonical-ids/output @@ -0,0 +1,649 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[debug/ast-declarations] # [Spicy] DNS +[debug/ast-declarations] - Type "Label" (DNS::Label) +[debug/ast-declarations] - Expression "self" (DNS::Label::self) +[debug/ast-declarations] - Expression "__dd" (DNS::Label::__dd) +[debug/ast-declarations] - Type "Pointer" (DNS::Pointer) +[debug/ast-declarations] - Expression "self" (DNS::Pointer::self) +[debug/ast-declarations] - Expression "__dd" (DNS::Pointer::__dd) +[debug/ast-declarations] - ImportedModule "spicy_rt" (spicy_rt) +[debug/ast-declarations] - ImportedModule "hilti" (hilti) +[debug/ast-declarations] # [Spicy] spicy_rt +[debug/ast-declarations] - Type "ParseError" (spicy_rt::ParseError) +[debug/ast-declarations] - Type "Backtrack" (spicy_rt::Backtrack) +[debug/ast-declarations] - Type "UnitAlreadyConnected" (spicy_rt::UnitAlreadyConnected) +[debug/ast-declarations] - Type "SinkState" (spicy_rt::SinkState) +[debug/ast-declarations] - Type "ParsedUnit" (spicy_rt::ParsedUnit) +[debug/ast-declarations] - Type "TypeInfo" (spicy_rt::TypeInfo) +[debug/ast-declarations] - Type "Sink" (spicy_rt::Sink) +[debug/ast-declarations] - Expression "self" (spicy_rt::Sink::self) +[debug/ast-declarations] - Field "close" (spicy_rt::Sink::close) +[debug/ast-declarations] - Field "connect" (spicy_rt::Sink::connect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::Sink::connect::unit) +[debug/ast-declarations] - Field "connect_filter" (spicy_rt::Sink::connect_filter) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::Sink::connect_filter::unit) +[debug/ast-declarations] - Field "connect_mime_type" (spicy_rt::Sink::connect_mime_type) +[debug/ast-declarations] - Parameter "mime_type" (spicy_rt::Sink::connect_mime_type::mime_type) +[debug/ast-declarations] - Field "connect_mime_type" (spicy_rt::Sink::connect_mime_type) +[debug/ast-declarations] - Parameter "mime_type" (spicy_rt::Sink::connect_mime_type::mime_type) +[debug/ast-declarations] - Field "gap" (spicy_rt::Sink::gap) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::gap::seq) +[debug/ast-declarations] - Parameter "len" (spicy_rt::Sink::gap::len) +[debug/ast-declarations] - Field "sequence_number" (spicy_rt::Sink::sequence_number) +[debug/ast-declarations] - Field "set_auto_trim" (spicy_rt::Sink::set_auto_trim) +[debug/ast-declarations] - Parameter "enable" (spicy_rt::Sink::set_auto_trim::enable) +[debug/ast-declarations] - Field "set_initial_sequence_number" (spicy_rt::Sink::set_initial_sequence_number) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::set_initial_sequence_number::seq) +[debug/ast-declarations] - Field "set_policy" (spicy_rt::Sink::set_policy) +[debug/ast-declarations] - Parameter "policy" (spicy_rt::Sink::set_policy::policy) +[debug/ast-declarations] - Field "size" (spicy_rt::Sink::size) +[debug/ast-declarations] - Field "skip" (spicy_rt::Sink::skip) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::skip::seq) +[debug/ast-declarations] - Field "trim" (spicy_rt::Sink::trim) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::trim::seq) +[debug/ast-declarations] - Field "write" (spicy_rt::Sink::write) +[debug/ast-declarations] - Parameter "data" (spicy_rt::Sink::write::data) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::write::seq) +[debug/ast-declarations] - Parameter "len" (spicy_rt::Sink::write::len) +[debug/ast-declarations] - Type "HiltiResumable" (spicy_rt::HiltiResumable) +[debug/ast-declarations] - Type "Filters" (spicy_rt::Filters) +[debug/ast-declarations] - Type "Forward" (spicy_rt::Forward) +[debug/ast-declarations] - Function "filter_init" (spicy_rt::filter_init) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_init::unit) +[debug/ast-declarations] - Parameter "data" (spicy_rt::filter_init::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::filter_init::cur) +[debug/ast-declarations] - Function "filter_connect" (spicy_rt::filter_connect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_connect::unit) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_connect::filter) +[debug/ast-declarations] - Function "filter_disconnect" (spicy_rt::filter_disconnect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_disconnect::unit) +[debug/ast-declarations] - Function "filter_forward" (spicy_rt::filter_forward) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_forward::filter) +[debug/ast-declarations] - Parameter "b" (spicy_rt::filter_forward::b) +[debug/ast-declarations] - Function "filter_forward_eod" (spicy_rt::filter_forward_eod) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_forward_eod::filter) +[debug/ast-declarations] - Type "UnitContext" (spicy_rt::UnitContext) +[debug/ast-declarations] - Function "createContext" (spicy_rt::createContext) +[debug/ast-declarations] - Parameter "ctx" (spicy_rt::createContext::ctx) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::createContext::ti) +[debug/ast-declarations] - Function "setContext" (spicy_rt::setContext) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::setContext::unit) +[debug/ast-declarations] - Parameter "ctx" (spicy_rt::setContext::ctx) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::setContext::ti) +[debug/ast-declarations] - Type "Parser" (spicy_rt::Parser) +[debug/ast-declarations] - Expression "self" (spicy_rt::Parser::self) +[debug/ast-declarations] - Field "name" (spicy_rt::Parser::name) +[debug/ast-declarations] - Field "parse1" (spicy_rt::Parser::parse1) +[debug/ast-declarations] - Field "parse2" (spicy_rt::Parser::parse2) +[debug/ast-declarations] - Field "parse3" (spicy_rt::Parser::parse3) +[debug/ast-declarations] - Field "context_new" (spicy_rt::Parser::context_new) +[debug/ast-declarations] - Field "type_info" (spicy_rt::Parser::type_info) +[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 "Direction" (spicy_rt::Direction) +[debug/ast-declarations] - Constant "Originator" (spicy_rt::Direction::Originator) +[debug/ast-declarations] - Constant "Responder" (spicy_rt::Direction::Responder) +[debug/ast-declarations] - Constant "Both" (spicy_rt::Direction::Both) +[debug/ast-declarations] - Constant "Undef" (spicy_rt::Direction::Undef) +[debug/ast-declarations] - Type "FindDirection" (spicy_rt::FindDirection) +[debug/ast-declarations] - Type "MIMEType" (spicy_rt::MIMEType) +[debug/ast-declarations] - Type "ParserPort" (spicy_rt::ParserPort) +[debug/ast-declarations] - Function "registerParser" (spicy_rt::registerParser) +[debug/ast-declarations] - Parameter "parse_func" (spicy_rt::registerParser::parse_func) +[debug/ast-declarations] - Parameter "linker_scope" (spicy_rt::registerParser::linker_scope) +[debug/ast-declarations] - Parameter "instance" (spicy_rt::registerParser::instance) +[debug/ast-declarations] - Function "printParserState" (spicy_rt::printParserState) +[debug/ast-declarations] - Parameter "unit_id" (spicy_rt::printParserState::unit_id) +[debug/ast-declarations] - Parameter "data" (spicy_rt::printParserState::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::printParserState::cur) +[debug/ast-declarations] - Parameter "lahead" (spicy_rt::printParserState::lahead) +[debug/ast-declarations] - Parameter "lahead_end" (spicy_rt::printParserState::lahead_end) +[debug/ast-declarations] - Parameter "literal_mode" (spicy_rt::printParserState::literal_mode) +[debug/ast-declarations] - Parameter "trim" (spicy_rt::printParserState::trim) +[debug/ast-declarations] - Function "waitForInputOrEod" (spicy_rt::waitForInputOrEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInputOrEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInputOrEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInputOrEod::filters) +[debug/ast-declarations] - Function "waitForInputOrEod" (spicy_rt::waitForInputOrEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInputOrEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInputOrEod::cur) +[debug/ast-declarations] - Parameter "n" (spicy_rt::waitForInputOrEod::n) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInputOrEod::filters) +[debug/ast-declarations] - Function "waitForInput" (spicy_rt::waitForInput) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInput::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInput::cur) +[debug/ast-declarations] - Parameter "error_msg" (spicy_rt::waitForInput::error_msg) +[debug/ast-declarations] - Parameter "location" (spicy_rt::waitForInput::location) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInput::filters) +[debug/ast-declarations] - Function "waitForInput" (spicy_rt::waitForInput) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInput::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInput::cur) +[debug/ast-declarations] - Parameter "n" (spicy_rt::waitForInput::n) +[debug/ast-declarations] - Parameter "error_msg" (spicy_rt::waitForInput::error_msg) +[debug/ast-declarations] - Parameter "location" (spicy_rt::waitForInput::location) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInput::filters) +[debug/ast-declarations] - Function "waitForEod" (spicy_rt::waitForEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForEod::filters) +[debug/ast-declarations] - Function "atEod" (spicy_rt::atEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::atEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::atEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::atEod::filters) +[debug/ast-declarations] - Function "unit_find" (spicy_rt::unit_find) +[debug/ast-declarations] - Parameter "begin_" (spicy_rt::unit_find::begin_) +[debug/ast-declarations] - Parameter "end_" (spicy_rt::unit_find::end_) +[debug/ast-declarations] - Parameter "i" (spicy_rt::unit_find::i) +[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) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::initializeParsedUnit::ti) +[debug/ast-declarations] # [Spicy] hilti +[debug/ast-declarations] - Type "BitOrder" (hilti::BitOrder) +[debug/ast-declarations] - Constant "LSB0" (hilti::BitOrder::LSB0) +[debug/ast-declarations] - Constant "MSB0" (hilti::BitOrder::MSB0) +[debug/ast-declarations] - Constant "Undef" (hilti::BitOrder::Undef) +[debug/ast-declarations] - Type "ByteOrder" (hilti::ByteOrder) +[debug/ast-declarations] - Constant "Little" (hilti::ByteOrder::Little) +[debug/ast-declarations] - Constant "Big" (hilti::ByteOrder::Big) +[debug/ast-declarations] - Constant "Network" (hilti::ByteOrder::Network) +[debug/ast-declarations] - Constant "Host" (hilti::ByteOrder::Host) +[debug/ast-declarations] - Constant "Undef" (hilti::ByteOrder::Undef) +[debug/ast-declarations] - Type "Side" (hilti::Side) +[debug/ast-declarations] - Constant "Left" (hilti::Side::Left) +[debug/ast-declarations] - Constant "Right" (hilti::Side::Right) +[debug/ast-declarations] - Constant "Both" (hilti::Side::Both) +[debug/ast-declarations] - Constant "Undef" (hilti::Side::Undef) +[debug/ast-declarations] - Type "AddressFamily" (hilti::AddressFamily) +[debug/ast-declarations] - Constant "IPv4" (hilti::AddressFamily::IPv4) +[debug/ast-declarations] - Constant "IPv6" (hilti::AddressFamily::IPv6) +[debug/ast-declarations] - Constant "Undef" (hilti::AddressFamily::Undef) +[debug/ast-declarations] - Type "RealType" (hilti::RealType) +[debug/ast-declarations] - Constant "IEEE754_Single" (hilti::RealType::IEEE754_Single) +[debug/ast-declarations] - Constant "IEEE754_Double" (hilti::RealType::IEEE754_Double) +[debug/ast-declarations] - Constant "Undef" (hilti::RealType::Undef) +[debug/ast-declarations] - Type "Protocol" (hilti::Protocol) +[debug/ast-declarations] - Constant "TCP" (hilti::Protocol::TCP) +[debug/ast-declarations] - Constant "UDP" (hilti::Protocol::UDP) +[debug/ast-declarations] - Constant "ICMP" (hilti::Protocol::ICMP) +[debug/ast-declarations] - Constant "Undef" (hilti::Protocol::Undef) +[debug/ast-declarations] - Type "Charset" (hilti::Charset) +[debug/ast-declarations] - Constant "ASCII" (hilti::Charset::ASCII) +[debug/ast-declarations] - Constant "UTF8" (hilti::Charset::UTF8) +[debug/ast-declarations] - Constant "Undef" (hilti::Charset::Undef) +[debug/ast-declarations] - Type "Captures" (hilti::Captures) +[debug/ast-declarations] - Type "MatchState" (hilti::MatchState) +[debug/ast-declarations] - Expression "self" (hilti::MatchState::self) +[debug/ast-declarations] - Field "captures" (hilti::MatchState::captures) +[debug/ast-declarations] - Parameter "data" (hilti::MatchState::captures::data) +[debug/ast-declarations] - Function "print" (hilti::print) +[debug/ast-declarations] - Parameter "obj" (hilti::print::obj) +[debug/ast-declarations] - Parameter "newline" (hilti::print::newline) +[debug/ast-declarations] - Function "printValues" (hilti::printValues) +[debug/ast-declarations] - Parameter "t" (hilti::printValues::t) +[debug/ast-declarations] - Parameter "newline" (hilti::printValues::newline) +[debug/ast-declarations] - Function "debug" (hilti::debug) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debug::dbg_stream) +[debug/ast-declarations] - Parameter "obj" (hilti::debug::obj) +[debug/ast-declarations] - Function "debugIndent" (hilti::debugIndent) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debugIndent::dbg_stream) +[debug/ast-declarations] - Function "debugDedent" (hilti::debugDedent) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debugDedent::dbg_stream) +[debug/ast-declarations] - Function "current_time" (hilti::current_time) +[debug/ast-declarations] - Function "mktime" (hilti::mktime) +[debug/ast-declarations] - Parameter "y" (hilti::mktime::y) +[debug/ast-declarations] - Parameter "m" (hilti::mktime::m) +[debug/ast-declarations] - Parameter "d" (hilti::mktime::d) +[debug/ast-declarations] - Parameter "H" (hilti::mktime::H) +[debug/ast-declarations] - Parameter "M" (hilti::mktime::M) +[debug/ast-declarations] - Parameter "S" (hilti::mktime::S) +[debug/ast-declarations] - Function "abort" (hilti::abort) +[debug/ast-declarations] - Function "linker_scope" (hilti::linker_scope) +[debug/ast-declarations] - Type "Exception" (hilti::Exception) +[debug/ast-declarations] - Type "RuntimeError" (hilti::RuntimeError) +[debug/ast-declarations] # [HILTI] DNS +[debug/ast-declarations] - Type "Label" (DNS::Label) +[debug/ast-declarations] - Expression "self" (DNS::Label::self) +[debug/ast-declarations] - Field "ptr" (DNS::Label::ptr) +[debug/ast-declarations] - Field "__on_ptr" (DNS::Label::__on_ptr) +[debug/ast-declarations] - Parameter "__dd" (DNS::Label::__on_ptr::__dd) +[debug/ast-declarations] - Field "__on_0x25_init" (DNS::Label::__on_0x25_init) +[debug/ast-declarations] - Field "__on_0x25_done" (DNS::Label::__on_0x25_done) +[debug/ast-declarations] - Field "__on_0x25_error" (DNS::Label::__on_0x25_error) +[debug/ast-declarations] - Field "__str__" (DNS::Label::__str__) +[debug/ast-declarations] - Field "__on_0x25_finally" (DNS::Label::__on_0x25_finally) +[debug/ast-declarations] - Field "__parse_stage1" (DNS::Label::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (DNS::Label::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Label::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Label::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Label::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Label::__parse_stage1::__lahe) +[debug/ast-declarations] - Field "parse1" (DNS::Label::parse1) +[debug/ast-declarations] - Parameter "data" (DNS::Label::parse1::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Label::parse1::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Label::parse1::context) +[debug/ast-declarations] - Field "parse2" (DNS::Label::parse2) +[debug/ast-declarations] - Parameter "unit" (DNS::Label::parse2::unit) +[debug/ast-declarations] - Parameter "data" (DNS::Label::parse2::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Label::parse2::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Label::parse2::context) +[debug/ast-declarations] - Field "parse3" (DNS::Label::parse3) +[debug/ast-declarations] - Parameter "gunit" (DNS::Label::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (DNS::Label::parse3::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Label::parse3::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Label::parse3::context) +[debug/ast-declarations] - Type "Pointer" (DNS::Pointer) +[debug/ast-declarations] - Expression "self" (DNS::Pointer::self) +[debug/ast-declarations] - Field "name" (DNS::Pointer::name) +[debug/ast-declarations] - Field "__on_name" (DNS::Pointer::__on_name) +[debug/ast-declarations] - Parameter "__dd" (DNS::Pointer::__on_name::__dd) +[debug/ast-declarations] - Field "__on_0x25_init" (DNS::Pointer::__on_0x25_init) +[debug/ast-declarations] - Field "__on_0x25_done" (DNS::Pointer::__on_0x25_done) +[debug/ast-declarations] - Field "__on_0x25_error" (DNS::Pointer::__on_0x25_error) +[debug/ast-declarations] - Field "__str__" (DNS::Pointer::__str__) +[debug/ast-declarations] - Field "__on_0x25_finally" (DNS::Pointer::__on_0x25_finally) +[debug/ast-declarations] - Field "__parse_stage1" (DNS::Pointer::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (DNS::Pointer::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Pointer::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Pointer::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Pointer::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Pointer::__parse_stage1::__lahe) +[debug/ast-declarations] - Field "parse1" (DNS::Pointer::parse1) +[debug/ast-declarations] - Parameter "data" (DNS::Pointer::parse1::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Pointer::parse1::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Pointer::parse1::context) +[debug/ast-declarations] - Field "parse2" (DNS::Pointer::parse2) +[debug/ast-declarations] - Parameter "unit" (DNS::Pointer::parse2::unit) +[debug/ast-declarations] - Parameter "data" (DNS::Pointer::parse2::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Pointer::parse2::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Pointer::parse2::context) +[debug/ast-declarations] - Field "parse3" (DNS::Pointer::parse3) +[debug/ast-declarations] - Parameter "gunit" (DNS::Pointer::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (DNS::Pointer::parse3::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Pointer::parse3::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Pointer::parse3::context) +[debug/ast-declarations] - ImportedModule "spicy_rt" (spicy_rt) +[debug/ast-declarations] - ImportedModule "hilti" (hilti) +[debug/ast-declarations] - Function "DNS::Label::__parse_stage1" (DNS::Label::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (DNS::Label::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Label::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Label::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Label::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Label::__parse_stage1::__lahe) +[debug/ast-declarations] - LocalVariable "__result" (DNS::Label::__parse_stage1::__result) +[debug/ast-declarations] - Parameter "__data" (DNS::Pointer::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Pointer::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Pointer::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Pointer::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Pointer::__parse_stage1::__lahe) +[debug/ast-declarations] - Parameter "__dd" (DNS::Label::__on_ptr::__dd) +[debug/ast-declarations] - Function "DNS::Label::parse1" (DNS::Label::parse1) +[debug/ast-declarations] - Parameter "data" (DNS::Label::parse1::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Label::parse1::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Label::parse1::context) +[debug/ast-declarations] - LocalVariable "unit" (DNS::Label::parse1::unit) +[debug/ast-declarations] - LocalVariable "ncur" (DNS::Label::parse1::ncur) +[debug/ast-declarations] - LocalVariable "lahead" (DNS::Label::parse1::lahead) +[debug/ast-declarations] - LocalVariable "lahead_end" (DNS::Label::parse1::lahead_end) +[debug/ast-declarations] - Parameter "__data" (DNS::Label::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Label::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Label::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Label::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Label::__parse_stage1::__lahe) +[debug/ast-declarations] - Function "DNS::Label::parse3" (DNS::Label::parse3) +[debug/ast-declarations] - Parameter "gunit" (DNS::Label::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (DNS::Label::parse3::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Label::parse3::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Label::parse3::context) +[debug/ast-declarations] - LocalVariable "unit" (DNS::Label::parse3::unit) +[debug/ast-declarations] - Expression "self" (DNS::Label::self) +[debug/ast-declarations] - Field "ptr" (DNS::Label::ptr) +[debug/ast-declarations] - Field "__on_ptr" (DNS::Label::__on_ptr) +[debug/ast-declarations] - Parameter "__dd" (DNS::Label::__on_ptr::__dd) +[debug/ast-declarations] - Field "__on_0x25_init" (DNS::Label::__on_0x25_init) +[debug/ast-declarations] - Field "__on_0x25_done" (DNS::Label::__on_0x25_done) +[debug/ast-declarations] - Field "__on_0x25_error" (DNS::Label::__on_0x25_error) +[debug/ast-declarations] - Field "__str__" (DNS::Label::__str__) +[debug/ast-declarations] - Field "__on_0x25_finally" (DNS::Label::__on_0x25_finally) +[debug/ast-declarations] - Field "__parse_stage1" (DNS::Label::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (DNS::Label::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Label::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Label::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Label::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Label::__parse_stage1::__lahe) +[debug/ast-declarations] - Field "parse1" (DNS::Label::parse1) +[debug/ast-declarations] - Parameter "data" (DNS::Label::parse1::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Label::parse1::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Label::parse1::context) +[debug/ast-declarations] - Field "parse2" (DNS::Label::parse2) +[debug/ast-declarations] - Parameter "unit" (DNS::Label::parse2::unit) +[debug/ast-declarations] - Parameter "data" (DNS::Label::parse2::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Label::parse2::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Label::parse2::context) +[debug/ast-declarations] - Field "parse3" (DNS::Label::parse3) +[debug/ast-declarations] - Parameter "gunit" (DNS::Label::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (DNS::Label::parse3::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Label::parse3::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Label::parse3::context) +[debug/ast-declarations] - LocalVariable "ncur" (DNS::Label::parse3::ncur) +[debug/ast-declarations] - LocalVariable "lahead" (DNS::Label::parse3::lahead) +[debug/ast-declarations] - LocalVariable "lahead_end" (DNS::Label::parse3::lahead_end) +[debug/ast-declarations] - Parameter "__data" (DNS::Label::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Label::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Label::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Label::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Label::__parse_stage1::__lahe) +[debug/ast-declarations] - Function "DNS::Label::parse2" (DNS::Label::parse2) +[debug/ast-declarations] - Parameter "unit" (DNS::Label::parse2::unit) +[debug/ast-declarations] - Parameter "data" (DNS::Label::parse2::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Label::parse2::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Label::parse2::context) +[debug/ast-declarations] - LocalVariable "ncur" (DNS::Label::parse2::ncur) +[debug/ast-declarations] - LocalVariable "lahead" (DNS::Label::parse2::lahead) +[debug/ast-declarations] - LocalVariable "lahead_end" (DNS::Label::parse2::lahead_end) +[debug/ast-declarations] - Parameter "__data" (DNS::Label::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Label::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Label::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Label::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Label::__parse_stage1::__lahe) +[debug/ast-declarations] - Function "DNS::Pointer::__parse_stage1" (DNS::Pointer::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (DNS::Pointer::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Pointer::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Pointer::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Pointer::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Pointer::__parse_stage1::__lahe) +[debug/ast-declarations] - LocalVariable "__result" (DNS::Pointer::__parse_stage1::__result) +[debug/ast-declarations] - Parameter "__data" (DNS::Label::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Label::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Label::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Label::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Label::__parse_stage1::__lahe) +[debug/ast-declarations] - Parameter "__dd" (DNS::Pointer::__on_name::__dd) +[debug/ast-declarations] - Function "DNS::Pointer::parse1" (DNS::Pointer::parse1) +[debug/ast-declarations] - Parameter "data" (DNS::Pointer::parse1::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Pointer::parse1::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Pointer::parse1::context) +[debug/ast-declarations] - LocalVariable "unit" (DNS::Pointer::parse1::unit) +[debug/ast-declarations] - LocalVariable "ncur" (DNS::Pointer::parse1::ncur) +[debug/ast-declarations] - LocalVariable "lahead" (DNS::Pointer::parse1::lahead) +[debug/ast-declarations] - LocalVariable "lahead_end" (DNS::Pointer::parse1::lahead_end) +[debug/ast-declarations] - Parameter "__data" (DNS::Pointer::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Pointer::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Pointer::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Pointer::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Pointer::__parse_stage1::__lahe) +[debug/ast-declarations] - Function "DNS::Pointer::parse3" (DNS::Pointer::parse3) +[debug/ast-declarations] - Parameter "gunit" (DNS::Pointer::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (DNS::Pointer::parse3::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Pointer::parse3::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Pointer::parse3::context) +[debug/ast-declarations] - LocalVariable "unit" (DNS::Pointer::parse3::unit) +[debug/ast-declarations] - Expression "self" (DNS::Pointer::self) +[debug/ast-declarations] - Field "name" (DNS::Pointer::name) +[debug/ast-declarations] - Field "__on_name" (DNS::Pointer::__on_name) +[debug/ast-declarations] - Parameter "__dd" (DNS::Pointer::__on_name::__dd) +[debug/ast-declarations] - Field "__on_0x25_init" (DNS::Pointer::__on_0x25_init) +[debug/ast-declarations] - Field "__on_0x25_done" (DNS::Pointer::__on_0x25_done) +[debug/ast-declarations] - Field "__on_0x25_error" (DNS::Pointer::__on_0x25_error) +[debug/ast-declarations] - Field "__str__" (DNS::Pointer::__str__) +[debug/ast-declarations] - Field "__on_0x25_finally" (DNS::Pointer::__on_0x25_finally) +[debug/ast-declarations] - Field "__parse_stage1" (DNS::Pointer::__parse_stage1) +[debug/ast-declarations] - Parameter "__data" (DNS::Pointer::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Pointer::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Pointer::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Pointer::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Pointer::__parse_stage1::__lahe) +[debug/ast-declarations] - Field "parse1" (DNS::Pointer::parse1) +[debug/ast-declarations] - Parameter "data" (DNS::Pointer::parse1::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Pointer::parse1::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Pointer::parse1::context) +[debug/ast-declarations] - Field "parse2" (DNS::Pointer::parse2) +[debug/ast-declarations] - Parameter "unit" (DNS::Pointer::parse2::unit) +[debug/ast-declarations] - Parameter "data" (DNS::Pointer::parse2::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Pointer::parse2::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Pointer::parse2::context) +[debug/ast-declarations] - Field "parse3" (DNS::Pointer::parse3) +[debug/ast-declarations] - Parameter "gunit" (DNS::Pointer::parse3::gunit) +[debug/ast-declarations] - Parameter "data" (DNS::Pointer::parse3::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Pointer::parse3::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Pointer::parse3::context) +[debug/ast-declarations] - LocalVariable "ncur" (DNS::Pointer::parse3::ncur) +[debug/ast-declarations] - LocalVariable "lahead" (DNS::Pointer::parse3::lahead) +[debug/ast-declarations] - LocalVariable "lahead_end" (DNS::Pointer::parse3::lahead_end) +[debug/ast-declarations] - Parameter "__data" (DNS::Pointer::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Pointer::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Pointer::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Pointer::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Pointer::__parse_stage1::__lahe) +[debug/ast-declarations] - Function "DNS::Pointer::parse2" (DNS::Pointer::parse2) +[debug/ast-declarations] - Parameter "unit" (DNS::Pointer::parse2::unit) +[debug/ast-declarations] - Parameter "data" (DNS::Pointer::parse2::data) +[debug/ast-declarations] - Parameter "cur" (DNS::Pointer::parse2::cur) +[debug/ast-declarations] - Parameter "context" (DNS::Pointer::parse2::context) +[debug/ast-declarations] - LocalVariable "ncur" (DNS::Pointer::parse2::ncur) +[debug/ast-declarations] - LocalVariable "lahead" (DNS::Pointer::parse2::lahead) +[debug/ast-declarations] - LocalVariable "lahead_end" (DNS::Pointer::parse2::lahead_end) +[debug/ast-declarations] - Parameter "__data" (DNS::Pointer::__parse_stage1::__data) +[debug/ast-declarations] - Parameter "__cur" (DNS::Pointer::__parse_stage1::__cur) +[debug/ast-declarations] - Parameter "__trim" (DNS::Pointer::__parse_stage1::__trim) +[debug/ast-declarations] - Parameter "__lah" (DNS::Pointer::__parse_stage1::__lah) +[debug/ast-declarations] - Parameter "__lahe" (DNS::Pointer::__parse_stage1::__lahe) +[debug/ast-declarations] # [HILTI] hilti +[debug/ast-declarations] - Type "BitOrder" (hilti::BitOrder) +[debug/ast-declarations] - Constant "LSB0" (hilti::BitOrder::LSB0) +[debug/ast-declarations] - Constant "MSB0" (hilti::BitOrder::MSB0) +[debug/ast-declarations] - Constant "Undef" (hilti::BitOrder::Undef) +[debug/ast-declarations] - Type "ByteOrder" (hilti::ByteOrder) +[debug/ast-declarations] - Constant "Little" (hilti::ByteOrder::Little) +[debug/ast-declarations] - Constant "Big" (hilti::ByteOrder::Big) +[debug/ast-declarations] - Constant "Network" (hilti::ByteOrder::Network) +[debug/ast-declarations] - Constant "Host" (hilti::ByteOrder::Host) +[debug/ast-declarations] - Constant "Undef" (hilti::ByteOrder::Undef) +[debug/ast-declarations] - Type "Side" (hilti::Side) +[debug/ast-declarations] - Constant "Left" (hilti::Side::Left) +[debug/ast-declarations] - Constant "Right" (hilti::Side::Right) +[debug/ast-declarations] - Constant "Both" (hilti::Side::Both) +[debug/ast-declarations] - Constant "Undef" (hilti::Side::Undef) +[debug/ast-declarations] - Type "AddressFamily" (hilti::AddressFamily) +[debug/ast-declarations] - Constant "IPv4" (hilti::AddressFamily::IPv4) +[debug/ast-declarations] - Constant "IPv6" (hilti::AddressFamily::IPv6) +[debug/ast-declarations] - Constant "Undef" (hilti::AddressFamily::Undef) +[debug/ast-declarations] - Type "RealType" (hilti::RealType) +[debug/ast-declarations] - Constant "IEEE754_Single" (hilti::RealType::IEEE754_Single) +[debug/ast-declarations] - Constant "IEEE754_Double" (hilti::RealType::IEEE754_Double) +[debug/ast-declarations] - Constant "Undef" (hilti::RealType::Undef) +[debug/ast-declarations] - Type "Protocol" (hilti::Protocol) +[debug/ast-declarations] - Constant "TCP" (hilti::Protocol::TCP) +[debug/ast-declarations] - Constant "UDP" (hilti::Protocol::UDP) +[debug/ast-declarations] - Constant "ICMP" (hilti::Protocol::ICMP) +[debug/ast-declarations] - Constant "Undef" (hilti::Protocol::Undef) +[debug/ast-declarations] - Type "Charset" (hilti::Charset) +[debug/ast-declarations] - Constant "ASCII" (hilti::Charset::ASCII) +[debug/ast-declarations] - Constant "UTF8" (hilti::Charset::UTF8) +[debug/ast-declarations] - Constant "Undef" (hilti::Charset::Undef) +[debug/ast-declarations] - Type "Captures" (hilti::Captures) +[debug/ast-declarations] - Type "MatchState" (hilti::MatchState) +[debug/ast-declarations] - Expression "self" (hilti::MatchState::self) +[debug/ast-declarations] - Field "captures" (hilti::MatchState::captures) +[debug/ast-declarations] - Parameter "data" (hilti::MatchState::captures::data) +[debug/ast-declarations] - Function "print" (hilti::print) +[debug/ast-declarations] - Parameter "obj" (hilti::print::obj) +[debug/ast-declarations] - Parameter "newline" (hilti::print::newline) +[debug/ast-declarations] - Function "printValues" (hilti::printValues) +[debug/ast-declarations] - Parameter "t" (hilti::printValues::t) +[debug/ast-declarations] - Parameter "newline" (hilti::printValues::newline) +[debug/ast-declarations] - Function "debug" (hilti::debug) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debug::dbg_stream) +[debug/ast-declarations] - Parameter "obj" (hilti::debug::obj) +[debug/ast-declarations] - Function "debugIndent" (hilti::debugIndent) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debugIndent::dbg_stream) +[debug/ast-declarations] - Function "debugDedent" (hilti::debugDedent) +[debug/ast-declarations] - Parameter "dbg_stream" (hilti::debugDedent::dbg_stream) +[debug/ast-declarations] - Function "current_time" (hilti::current_time) +[debug/ast-declarations] - Function "mktime" (hilti::mktime) +[debug/ast-declarations] - Parameter "y" (hilti::mktime::y) +[debug/ast-declarations] - Parameter "m" (hilti::mktime::m) +[debug/ast-declarations] - Parameter "d" (hilti::mktime::d) +[debug/ast-declarations] - Parameter "H" (hilti::mktime::H) +[debug/ast-declarations] - Parameter "M" (hilti::mktime::M) +[debug/ast-declarations] - Parameter "S" (hilti::mktime::S) +[debug/ast-declarations] - Function "abort" (hilti::abort) +[debug/ast-declarations] - Function "linker_scope" (hilti::linker_scope) +[debug/ast-declarations] - Type "Exception" (hilti::Exception) +[debug/ast-declarations] - Type "RuntimeError" (hilti::RuntimeError) +[debug/ast-declarations] # [HILTI] spicy_rt +[debug/ast-declarations] - Type "ParseError" (spicy_rt::ParseError) +[debug/ast-declarations] - Type "Backtrack" (spicy_rt::Backtrack) +[debug/ast-declarations] - Type "UnitAlreadyConnected" (spicy_rt::UnitAlreadyConnected) +[debug/ast-declarations] - Type "SinkState" (spicy_rt::SinkState) +[debug/ast-declarations] - Type "ParsedUnit" (spicy_rt::ParsedUnit) +[debug/ast-declarations] - Type "TypeInfo" (spicy_rt::TypeInfo) +[debug/ast-declarations] - Type "Sink" (spicy_rt::Sink) +[debug/ast-declarations] - Expression "self" (spicy_rt::Sink::self) +[debug/ast-declarations] - Field "close" (spicy_rt::Sink::close) +[debug/ast-declarations] - Field "connect" (spicy_rt::Sink::connect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::Sink::connect::unit) +[debug/ast-declarations] - Field "connect_filter" (spicy_rt::Sink::connect_filter) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::Sink::connect_filter::unit) +[debug/ast-declarations] - Field "connect_mime_type" (spicy_rt::Sink::connect_mime_type) +[debug/ast-declarations] - Parameter "mime_type" (spicy_rt::Sink::connect_mime_type::mime_type) +[debug/ast-declarations] - Field "connect_mime_type" (spicy_rt::Sink::connect_mime_type) +[debug/ast-declarations] - Parameter "mime_type" (spicy_rt::Sink::connect_mime_type::mime_type) +[debug/ast-declarations] - Field "gap" (spicy_rt::Sink::gap) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::gap::seq) +[debug/ast-declarations] - Parameter "len" (spicy_rt::Sink::gap::len) +[debug/ast-declarations] - Field "sequence_number" (spicy_rt::Sink::sequence_number) +[debug/ast-declarations] - Field "set_auto_trim" (spicy_rt::Sink::set_auto_trim) +[debug/ast-declarations] - Parameter "enable" (spicy_rt::Sink::set_auto_trim::enable) +[debug/ast-declarations] - Field "set_initial_sequence_number" (spicy_rt::Sink::set_initial_sequence_number) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::set_initial_sequence_number::seq) +[debug/ast-declarations] - Field "set_policy" (spicy_rt::Sink::set_policy) +[debug/ast-declarations] - Parameter "policy" (spicy_rt::Sink::set_policy::policy) +[debug/ast-declarations] - Field "size" (spicy_rt::Sink::size) +[debug/ast-declarations] - Field "skip" (spicy_rt::Sink::skip) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::skip::seq) +[debug/ast-declarations] - Field "trim" (spicy_rt::Sink::trim) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::trim::seq) +[debug/ast-declarations] - Field "write" (spicy_rt::Sink::write) +[debug/ast-declarations] - Parameter "data" (spicy_rt::Sink::write::data) +[debug/ast-declarations] - Parameter "seq" (spicy_rt::Sink::write::seq) +[debug/ast-declarations] - Parameter "len" (spicy_rt::Sink::write::len) +[debug/ast-declarations] - Type "HiltiResumable" (spicy_rt::HiltiResumable) +[debug/ast-declarations] - Type "Filters" (spicy_rt::Filters) +[debug/ast-declarations] - Type "Forward" (spicy_rt::Forward) +[debug/ast-declarations] - Function "filter_init" (spicy_rt::filter_init) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_init::unit) +[debug/ast-declarations] - Parameter "data" (spicy_rt::filter_init::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::filter_init::cur) +[debug/ast-declarations] - Function "filter_connect" (spicy_rt::filter_connect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_connect::unit) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_connect::filter) +[debug/ast-declarations] - Function "filter_disconnect" (spicy_rt::filter_disconnect) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::filter_disconnect::unit) +[debug/ast-declarations] - Function "filter_forward" (spicy_rt::filter_forward) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_forward::filter) +[debug/ast-declarations] - Parameter "b" (spicy_rt::filter_forward::b) +[debug/ast-declarations] - Function "filter_forward_eod" (spicy_rt::filter_forward_eod) +[debug/ast-declarations] - Parameter "filter" (spicy_rt::filter_forward_eod::filter) +[debug/ast-declarations] - Type "UnitContext" (spicy_rt::UnitContext) +[debug/ast-declarations] - Function "createContext" (spicy_rt::createContext) +[debug/ast-declarations] - Parameter "ctx" (spicy_rt::createContext::ctx) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::createContext::ti) +[debug/ast-declarations] - Function "setContext" (spicy_rt::setContext) +[debug/ast-declarations] - Parameter "unit" (spicy_rt::setContext::unit) +[debug/ast-declarations] - Parameter "ctx" (spicy_rt::setContext::ctx) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::setContext::ti) +[debug/ast-declarations] - Type "Parser" (spicy_rt::Parser) +[debug/ast-declarations] - Expression "self" (spicy_rt::Parser::self) +[debug/ast-declarations] - Field "name" (spicy_rt::Parser::name) +[debug/ast-declarations] - Field "parse1" (spicy_rt::Parser::parse1) +[debug/ast-declarations] - Field "parse2" (spicy_rt::Parser::parse2) +[debug/ast-declarations] - Field "parse3" (spicy_rt::Parser::parse3) +[debug/ast-declarations] - Field "context_new" (spicy_rt::Parser::context_new) +[debug/ast-declarations] - Field "type_info" (spicy_rt::Parser::type_info) +[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 "Direction" (spicy_rt::Direction) +[debug/ast-declarations] - Constant "Originator" (spicy_rt::Direction::Originator) +[debug/ast-declarations] - Constant "Responder" (spicy_rt::Direction::Responder) +[debug/ast-declarations] - Constant "Both" (spicy_rt::Direction::Both) +[debug/ast-declarations] - Constant "Undef" (spicy_rt::Direction::Undef) +[debug/ast-declarations] - Type "FindDirection" (spicy_rt::FindDirection) +[debug/ast-declarations] - Type "MIMEType" (spicy_rt::MIMEType) +[debug/ast-declarations] - Type "ParserPort" (spicy_rt::ParserPort) +[debug/ast-declarations] - Function "registerParser" (spicy_rt::registerParser) +[debug/ast-declarations] - Parameter "parse_func" (spicy_rt::registerParser::parse_func) +[debug/ast-declarations] - Parameter "linker_scope" (spicy_rt::registerParser::linker_scope) +[debug/ast-declarations] - Parameter "instance" (spicy_rt::registerParser::instance) +[debug/ast-declarations] - Function "printParserState" (spicy_rt::printParserState) +[debug/ast-declarations] - Parameter "unit_id" (spicy_rt::printParserState::unit_id) +[debug/ast-declarations] - Parameter "data" (spicy_rt::printParserState::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::printParserState::cur) +[debug/ast-declarations] - Parameter "lahead" (spicy_rt::printParserState::lahead) +[debug/ast-declarations] - Parameter "lahead_end" (spicy_rt::printParserState::lahead_end) +[debug/ast-declarations] - Parameter "literal_mode" (spicy_rt::printParserState::literal_mode) +[debug/ast-declarations] - Parameter "trim" (spicy_rt::printParserState::trim) +[debug/ast-declarations] - Function "waitForInputOrEod" (spicy_rt::waitForInputOrEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInputOrEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInputOrEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInputOrEod::filters) +[debug/ast-declarations] - Function "waitForInputOrEod" (spicy_rt::waitForInputOrEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInputOrEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInputOrEod::cur) +[debug/ast-declarations] - Parameter "n" (spicy_rt::waitForInputOrEod::n) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInputOrEod::filters) +[debug/ast-declarations] - Function "waitForInput" (spicy_rt::waitForInput) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInput::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInput::cur) +[debug/ast-declarations] - Parameter "error_msg" (spicy_rt::waitForInput::error_msg) +[debug/ast-declarations] - Parameter "location" (spicy_rt::waitForInput::location) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInput::filters) +[debug/ast-declarations] - Function "waitForInput" (spicy_rt::waitForInput) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForInput::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForInput::cur) +[debug/ast-declarations] - Parameter "n" (spicy_rt::waitForInput::n) +[debug/ast-declarations] - Parameter "error_msg" (spicy_rt::waitForInput::error_msg) +[debug/ast-declarations] - Parameter "location" (spicy_rt::waitForInput::location) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForInput::filters) +[debug/ast-declarations] - Function "waitForEod" (spicy_rt::waitForEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::waitForEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::waitForEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::waitForEod::filters) +[debug/ast-declarations] - Function "atEod" (spicy_rt::atEod) +[debug/ast-declarations] - Parameter "data" (spicy_rt::atEod::data) +[debug/ast-declarations] - Parameter "cur" (spicy_rt::atEod::cur) +[debug/ast-declarations] - Parameter "filters" (spicy_rt::atEod::filters) +[debug/ast-declarations] - Function "unit_find" (spicy_rt::unit_find) +[debug/ast-declarations] - Parameter "begin_" (spicy_rt::unit_find::begin_) +[debug/ast-declarations] - Parameter "end_" (spicy_rt::unit_find::end_) +[debug/ast-declarations] - Parameter "i" (spicy_rt::unit_find::i) +[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) +[debug/ast-declarations] - Parameter "ti" (spicy_rt::initializeParsedUnit::ti) diff --git a/tests/Baseline/spicy.types.unit.context-fail/output b/tests/Baseline/spicy.types.unit.context-fail/output index a51e50297..ea2d42121 100644 --- a/tests/Baseline/spicy.types.unit.context-fail/output +++ b/tests/Baseline/spicy.types.unit.context-fail/output @@ -1,5 +1,6 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. [error] <...>/context-fail.spicy:9:5: only public units can have %context +[error] <...>/context-fail.spicy:12:17-15:2: unit cannot have more than one %context [error] <...>/context-fail.spicy:13:5: %context requires an argument [error] <...>/context-fail.spicy:14:5: %context requires a type [error] <...>/context-fail.spicy:17:17-20:2: unit cannot have more than one %context diff --git a/tests/Baseline/spicy.types.unit.dollardollar-fail/output b/tests/Baseline/spicy.types.unit.dollardollar-fail/output index 2e4e15bf8..0b31e2ed2 100644 --- a/tests/Baseline/spicy.types.unit.dollardollar-fail/output +++ b/tests/Baseline/spicy.types.unit.dollardollar-fail/output @@ -1,5 +1,5 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/dollardollar-fail.spicy:12:27: $$ is not available in this hook -[error] <...>/dollardollar-fail.spicy:8:22: $$ is not available in this hook -[error] <...>/dollardollar-fail.spicy:9:20: $$ is not available in this hook +[error] <...>/dollardollar-fail.spicy:8:22: $$ is not available in this context +[error] <...>/dollardollar-fail.spicy:9:20: $$ is not available in this context +[error] <...>/dollardollar-fail.spicy:12:27: $$ is not available in this context [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.field-default-value-fail-2/output b/tests/Baseline/spicy.types.unit.field-default-value-fail-2/output index 903f0ef91..356f6acb7 100644 --- a/tests/Baseline/spicy.types.unit.field-default-value-fail-2/output +++ b/tests/Baseline/spicy.types.unit.field-default-value-fail-2/output @@ -1,3 +1,4 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. [error] <...>/field-default-value-fail.spicy:5:3: cannot coerce default expression to type 'bytes' +[error] <...>/field-default-value-fail.spicy:2:12-6:3: type mismatch for &default expression, expecting type bytes [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.field-hook-fail/output b/tests/Baseline/spicy.types.unit.field-hook-fail/output index 63ee2a707..c7c2ab660 100644 --- a/tests/Baseline/spicy.types.unit.field-hook-fail/output +++ b/tests/Baseline/spicy.types.unit.field-hook-fail/output @@ -1,4 +1,5 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[error] <...>/field-hook-fail.spicy:7:12: 'foreach' hook can only be used with containers [error] <...>/field-hook-fail.spicy:7:12: foreach can only be used with containers [error] <...>/field-hook-fail.spicy:9:17: foreach can only be used with containers [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.field-max-size/output b/tests/Baseline/spicy.types.unit.field-max-size/output index 361b3afa2..4caafb90f 100644 --- a/tests/Baseline/spicy.types.unit.field-max-size/output +++ b/tests/Baseline/spicy.types.unit.field-max-size/output @@ -4,4 +4,4 @@ done, [$xs=b"\x01\x00"] error, [$xs=(not set)] [error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: parsing not done within &max-size bytes (<...>/field-max-size.spicy:15:40) error, [$xs=b"\x01\x01\x01"] -[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until-including expression found (<...>/field-max-size.spicy:15:9) +[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until-including expression found (<...>/field-max-size.spicy:15:32) diff --git a/tests/Baseline/spicy.types.unit.hooks-fail/output b/tests/Baseline/spicy.types.unit.hooks-fail/output index ac7d7a0bc..d047749ed 100644 --- a/tests/Baseline/spicy.types.unit.hooks-fail/output +++ b/tests/Baseline/spicy.types.unit.hooks-fail/output @@ -1,22 +1,23 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/hooks-fail.spicy:9:7: hook '%init' does not take any parameters -[error] <...>/hooks-fail.spicy:10:7: hook ID cannot be scoped -[error] <...>/hooks-fail.spicy:12:7: signature for hook must be: %gap(seq: uint64, len: uint64) -[error] <...>/hooks-fail.spicy:12:7: cannot use hook '%gap', unit type does not support sinks because it is not public -[error] <...>/hooks-fail.spicy:13:7: signature for hook must be: %skipped(seq: uint64) -[error] <...>/hooks-fail.spicy:13:7: cannot use hook '%skipped', unit type does not support sinks because it is not public -[error] <...>/hooks-fail.spicy:14:7: signature for hook must be: %overlap(seq: uint64, old: bytes, new_: bytes) -[error] <...>/hooks-fail.spicy:14:7: cannot use hook '%overlap', unit type does not support sinks because it is not public -[error] <...>/hooks-fail.spicy:15:7: signature for hook must be: %undelivered(seq: uint64, data: bytes) -[error] <...>/hooks-fail.spicy:15:7: cannot use hook '%undelivered', unit type does not support sinks because it is not public -[error] <...>/hooks-fail.spicy:17:7: no field 'x1' in unit type -[error] <...>/hooks-fail.spicy:18:7: unknown hook '%x2' -[error] <...>/hooks-fail.spicy:19:7: cannot use paths in hooks; trigger on the top-level field instead -[error] <...>/hooks-fail.spicy:23:1: hook '%init' does not take any parameters -[error] <...>/hooks-fail.spicy:24:1: signature for hook must be: %gap(seq: uint64, len: uint64) -[error] <...>/hooks-fail.spicy:24:1: cannot use hook '%gap', unit type does not support sinks because it is not public -[error] <...>/hooks-fail.spicy:25:1: no field 'y1' in unit type -[error] <...>/hooks-fail.spicy:26:1: unknown hook '%y2' -[error] <...>/hooks-fail.spicy:27:1: cannot use paths in hooks; trigger on the top-level field instead -[error] <...>/hooks-fail.spicy:28:1: unknown unit type +[error] <...>/hooks-fail.spicy:9:13: hook '%init' does not take any parameters +[error] <...>/hooks-fail.spicy:10:16: hook ID cannot be scoped +[error] <...>/hooks-fail.spicy:10:16: no field 'bar' in unit type +[error] <...>/hooks-fail.spicy:12:13: signature for hook must be: %gap(seq: uint64, len: uint64) +[error] <...>/hooks-fail.spicy:12:13: cannot use hook '%gap', unit type does not support sinks because it is not public +[error] <...>/hooks-fail.spicy:13:17: signature for hook must be: %skipped(seq: uint64) +[error] <...>/hooks-fail.spicy:13:17: cannot use hook '%skipped', unit type does not support sinks because it is not public +[error] <...>/hooks-fail.spicy:14:17: signature for hook must be: %overlap(seq: uint64, old: bytes, new_: bytes) +[error] <...>/hooks-fail.spicy:14:17: cannot use hook '%overlap', unit type does not support sinks because it is not public +[error] <...>/hooks-fail.spicy:15:21: signature for hook must be: %undelivered(seq: uint64, data: bytes) +[error] <...>/hooks-fail.spicy:15:21: cannot use hook '%undelivered', unit type does not support sinks because it is not public +[error] <...>/hooks-fail.spicy:17:10: no field 'x1' in unit type +[error] <...>/hooks-fail.spicy:18:11: unknown hook '%x2' +[error] <...>/hooks-fail.spicy:19:13: cannot use paths in hooks; trigger on the top-level field instead +[error] <...>/hooks-fail.spicy:23:15: hook '%init' does not take any parameters +[error] <...>/hooks-fail.spicy:24:15: signature for hook must be: %gap(seq: uint64, len: uint64) +[error] <...>/hooks-fail.spicy:24:15: cannot use hook '%gap', unit type does not support sinks because it is not public +[error] <...>/hooks-fail.spicy:25:12: no field 'y1' in unit type +[error] <...>/hooks-fail.spicy:26:13: unknown hook '%y2' +[error] <...>/hooks-fail.spicy:27:15: cannot use paths in hooks; trigger on the top-level field instead +[error] <...>/hooks-fail.spicy:28:11: unknown ID 'XXXX' [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.params-write-fail/output b/tests/Baseline/spicy.types.unit.params-write-fail/output index b4b8482af..81ed8a2f1 100644 --- a/tests/Baseline/spicy.types.unit.params-write-fail/output +++ b/tests/Baseline/spicy.types.unit.params-write-fail/output @@ -1,4 +1,4 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. [error] <...>/params-write-fail.spicy:16:9: cannot assign to expression: x = False -[error] <...>/params-write-fail.spicy:17:9: cannot assign to expression: (*y).a = 42 +[error] <...>/params-write-fail.spicy:17:9: cannot assign to field of constant unit instance [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.properties-fail/output b/tests/Baseline/spicy.types.unit.properties-fail/output index 4d9623818..6179f4c8c 100644 --- a/tests/Baseline/spicy.types.unit.properties-fail/output +++ b/tests/Baseline/spicy.types.unit.properties-fail/output @@ -1,4 +1,6 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[error] <...>/properties-fail.spicy:8:19-19:2: attribute '&byte-order' requires an expression +[error] <...>/properties-fail.spicy:19:3: &byte-order requires an expression [error] <...>/properties-fail.spicy:9:5: %port requires an argument [error] <...>/properties-fail.spicy:10:5: %port requires a port as its argument [error] <...>/properties-fail.spicy:11:5: %description requires an argument @@ -9,5 +11,4 @@ [error] <...>/properties-fail.spicy:16:5: %mime-type requires a string argument [error] <...>/properties-fail.spicy:17:5: %mime-type argument must follow "main/sub" form [error] <...>/properties-fail.spicy:18:5: unknown property '%unknown' -[error] <...>/properties-fail.spicy:19:3: &byte-order requires an expression [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.switch-convert-mixed/output b/tests/Baseline/spicy.types.unit.switch-convert-mixed/output new file mode 100644 index 000000000..f1cb88b3b --- /dev/null +++ b/tests/Baseline/spicy.types.unit.switch-convert-mixed/output @@ -0,0 +1,7 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +in x:1, 1 +in x:2, 1 +[$a=b"\x01", $x=1] +in x:1, 2 +in x:2, 2 +[$a=b"\x02", $x=2] diff --git a/tests/Baseline/spicy.types.unit.switch-wrong-case/output b/tests/Baseline/spicy.types.unit.switch-wrong-case/output index 869740dbd..e9a271ed0 100644 --- a/tests/Baseline/spicy.types.unit.switch-wrong-case/output +++ b/tests/Baseline/spicy.types.unit.switch-wrong-case/output @@ -1,3 +1,3 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. -[error] <...>/switch-wrong-case.spicy:13:12: cannot resolve operator: == > +[error] <...>/switch-wrong-case.spicy:13:12: unsupported operator: == > [error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.unit-max-size/output b/tests/Baseline/spicy.types.unit.unit-max-size/output index 7438a3a56..2f9cb8f96 100644 --- a/tests/Baseline/spicy.types.unit.unit-max-size/output +++ b/tests/Baseline/spicy.types.unit.unit-max-size/output @@ -5,4 +5,4 @@ done, [$xs=b"\x01\x01\x00"] error, [$xs=b"\x01\x01\x00"] [error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: parsing not done within &max-size bytes (<...>/unit-max-size.spicy:19:3) error, [$xs=b"\x01\x01\x01"] -[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until-including expression found (<...>/unit-max-size.spicy:15:9) +[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until-including expression found (<...>/unit-max-size.spicy:15:32) diff --git a/tests/Baseline/spicy.types.unit.void-until/output b/tests/Baseline/spicy.types.unit.void-until/output index 674581717..fae6d526c 100644 --- a/tests/Baseline/spicy.types.unit.void-until/output +++ b/tests/Baseline/spicy.types.unit.void-until/output @@ -3,5 +3,5 @@ hook! [$x=b"123"] hook! [$x=b"123"] -[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until expression found (<...>/void-until.spicy:15:7) -[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until expression found (<...>/void-until.spicy:15:7) +[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until expression found (<...>/void-until.spicy:15:12) +[error] terminating with uncaught exception of type spicy::rt::ParseError: parse error: end-of-data reached before &until expression found (<...>/void-until.spicy:15:12) diff --git a/tests/Scripts/canonify-hiltic b/tests/Scripts/canonify-hiltic index a3720d063..308ae309a 100755 --- a/tests/Scripts/canonify-hiltic +++ b/tests/Scripts/canonify-hiltic @@ -18,4 +18,5 @@ cat \ | sed 's#libc++abi.dylib: ##g' \ | sed 's#\(Compiled by HILTI version\) .*#\1 X.X.X#g' \ | sed 's#__hlto_library_version = .*#__hlto_library_version = <...>#g' \ - | sed 's#__hlto_scope = .*#__hlto_scope = <...>#g' + | sed 's#__hlto_scope = .*#__hlto_scope = <...>#g' \ + | sed 's/\[\(@.:\)[0-9a-f]\{1,\}\]/[\1XXX]/g' diff --git a/tests/Scripts/canonify-spicy-debug b/tests/Scripts/canonify-spicy-debug index 536bca7e4..27d2aae80 100755 --- a/tests/Scripts/canonify-spicy-debug +++ b/tests/Scripts/canonify-spicy-debug @@ -1,6 +1,6 @@ #! /bin/sh # -# Canonifies spicy/spicy-verbose debug output for baseline diffing. +# Canonifies spicy-verbose debug output for baseline diffing. cat \ | sed 's/stream=0x[0-9a-z]\{2,\} /stream=0xXXXXXXXX /g' \ diff --git a/tests/Scripts/cxx-link b/tests/Scripts/cxx-link index b1e6118ee..898010b07 100755 --- a/tests/Scripts/cxx-link +++ b/tests/Scripts/cxx-link @@ -5,4 +5,4 @@ cxx=$(hilti-config --cxx) ldflags=$(hilti-config --ldflags ${debug}) -exec ${cxx} ${ldflags} $@ +exec ${cxx} $@ ${ldflags} diff --git a/tests/hilti/ast/imported-id.hlt b/tests/hilti/ast/imported-id.hlt index 751d01da2..e55b7d3c4 100644 --- a/tests/hilti/ast/imported-id.hlt +++ b/tests/hilti/ast/imported-id.hlt @@ -1,5 +1,5 @@ # @TEST-GROUP: no-jit -# @TEST-EXEC: ${HILTIC} -c -D compiler,resolver,ast-scopes,ast-final foo.hlt >output 2>&1 +# @TEST-EXEC: ${HILTIC} -c -D compiler,resolver,ast-final foo.hlt >output 2>&1 # @TEST-EXEC: btest-diff output @TEST-START-FILE foo.hlt diff --git a/tests/hilti/codegen/imported-types.hlt b/tests/hilti/codegen/imported-types.hlt new file mode 100644 index 000000000..ded80831e --- /dev/null +++ b/tests/hilti/codegen/imported-types.hlt @@ -0,0 +1,20 @@ +# @TEST-EXEC: hiltic -j a.hlt %INPUT +# @TEST-DOC: Check that our C++ code fully prototypes typed imported from another module. + +module B { + +import A; + +public type Bar = struct(strong_ref a) {}; + +} + +@TEST-START-FILE a.hlt + +module A { + +type Foo = struct {}; + +} + +@TEST-END-FILE diff --git a/tests/hilti/codegen/type-info.hlt b/tests/hilti/codegen/type-info.hlt index 8cb800b36..6c175a139 100644 --- a/tests/hilti/codegen/type-info.hlt +++ b/tests/hilti/codegen/type-info.hlt @@ -2,9 +2,10 @@ module Test { +import hilti; + type Enum = enum { A = 1, B = 2, C = 4 }; type Exception = exception; -type MatchState = __library_type("hilti::rt::regexp::MatchState"); type TypeInfo = __library_type("const hilti::rt::TypeInfo*"); type Struct = struct { @@ -12,7 +13,12 @@ type Struct = struct { int<64> i; }; -type Union = union { +# Name the union type with leading 'A' so that the codegen prints out the union +# first. That ensures that the C++-side order of typedefs works. Normally, we'd +# declare the union &on-heap instead to make it independent of the order, +# however, our C++ code in type-info.cc then wouldn't easily be able to +# distinguish the resulting value_ref from the value_ref. +type A_Union = union { string s, int<64> i }; @@ -28,7 +34,7 @@ public type TestTypes = struct { exception ex; # method void test(); # Can't test - won't be included into type info. interval in_; - MatchState li; + hilti::MatchState li; map, string> ma; iterator, string>> mai; net ne; @@ -51,7 +57,7 @@ public type TestTypes = struct { Struct stru; time ti; tuple, string, c: bool> tu; - Union un; + A_Union un; uint<8> ui8; uint<16> ui16; uint<32> ui32; @@ -85,11 +91,11 @@ public function tuple, strong_ref, TypeInfo> ma S.s = "string"; S.i = 42; - local Union U; + local A_Union U; U.i = 42; local re = /abc/ &nosub; - local MatchState MS = re.token_matcher(); + local hilti::MatchState MS = re.token_matcher(); local Exception E; local TestTypes x = [ @@ -203,12 +209,16 @@ void __check_eq(const T& x, const U& y, std::string loc) { // "TypesInit". struct VisitorTypesInit { std::set seen; - static inline const int ExepectedVisitorsSeen = 41; // all (43) minus void and function + static inline const int ExepectedVisitorsSeen = 40; // all (43) 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. void testStruct(const type_info::Value& v) { auto s = type_info::value::auxType(v)->iterate(v); + if ( s.empty() ) + // We get here for hilti::MatchState + return; + auto i = s.begin(); auto fv = i->second; CHECK_EQ(i->first.name, "s"); diff --git a/tests/hilti/optimization/unimplemented_hook.hlt b/tests/hilti/optimization/unimplemented_hook.hlt index 099a92f03..8dac792da 100644 --- a/tests/hilti/optimization/unimplemented_hook.hlt +++ b/tests/hilti/optimization/unimplemented_hook.hlt @@ -13,7 +13,7 @@ declare public hook void global_unimplemented_void(); declare public hook void global_implemented(); declare public hook optional> global_unimplemented_int64(); -function hook void global_implemented() {} +hook void global_implemented() {} type X = struct { hook void implemented(); # Called, implemented hook. @@ -22,7 +22,7 @@ type X = struct { hook optional> unimplemented_int64(); # Called, unimplemented hook. }; -method hook void X::implemented() {} +hook void X::implemented() {} global_implemented(); diff --git a/tests/hilti/statements/try-across-hooks.hlt b/tests/hilti/statements/try-across-hooks.hlt index 0748f0389..ea317ff1c 100644 --- a/tests/hilti/statements/try-across-hooks.hlt +++ b/tests/hilti/statements/try-across-hooks.hlt @@ -11,7 +11,7 @@ public type E = exception; declare public hook void f1(); -public function hook void f1() &priority=5 { hilti::print("Foo: f1"); } +public hook void f1() &priority=5 { hilti::print("Foo: f1"); } } @@ -24,7 +24,7 @@ module Bar { import hilti; import Foo; -function hook void Foo::f1() { hilti::print("Bar: f1"); throw Foo::E("Bar::f1"); } +hook void Foo::f1() { hilti::print("Bar: f1"); throw Foo::E("Bar::f1"); } } diff --git a/tests/hilti/types/function/hook-functions-across-units.hlt b/tests/hilti/types/function/hook-functions-across-units.hlt index 55fffb3e0..b89e19786 100644 --- a/tests/hilti/types/function/hook-functions-across-units.hlt +++ b/tests/hilti/types/function/hook-functions-across-units.hlt @@ -11,7 +11,7 @@ declare public hook void f1(); declare public hook void f2(); declare public hook void f3(); -public function hook void f1() { hilti::print("Foo: f1"); } +public hook void f1() { hilti::print("Foo: f1"); } } @@ -24,8 +24,8 @@ module Bar { import hilti; import Foo; -function hook void Foo::f1() { hilti::print("Bar: f1"); } -function hook void Foo::f2() { hilti::print("Bar: f2"); } +hook void Foo::f1() { hilti::print("Bar: f1"); } +hook void Foo::f2() { hilti::print("Bar: f2"); } } diff --git a/tests/hilti/types/function/hook-functions.hlt b/tests/hilti/types/function/hook-functions.hlt index 484f05f29..417df148e 100644 --- a/tests/hilti/types/function/hook-functions.hlt +++ b/tests/hilti/types/function/hook-functions.hlt @@ -5,27 +5,27 @@ module Foo { import hilti; -function hook void f1(string s) { +hook void f1(string s) { hilti::print("f1.a %s" % s); } -function hook void f1(string s) { +hook void f1(string s) { hilti::print("f1.b %s" % s); } -function hook void f1(string s) { +hook void f1(string s) { hilti::print("f1.c %s" % s); } -function hook optional> f2() { +hook optional> f2() { return Null; } -function hook optional> f2() { +hook optional> f2() { return 42; } -function hook optional> f2() { +hook optional> f2() { return Null; } diff --git a/tests/hilti/types/function/hook-methods-across-units.hlt b/tests/hilti/types/function/hook-methods-across-units.hlt index 1636337a2..c8586c977 100644 --- a/tests/hilti/types/function/hook-methods-across-units.hlt +++ b/tests/hilti/types/function/hook-methods-across-units.hlt @@ -13,7 +13,7 @@ public type X = struct { hook void f3(); }; -method hook void X::f1() { +hook void X::f1() { hilti::print("Foo: f1"); } @@ -28,11 +28,11 @@ module Bar { import hilti; import Foo; -method hook void Foo::X::f1() { +hook void Foo::X::f1() { hilti::print("Bar: f1"); } -method hook void Foo::X::f2() { +hook void Foo::X::f2() { hilti::print("Bar: f2"); } diff --git a/tests/hilti/types/function/hook-methods.hlt b/tests/hilti/types/function/hook-methods.hlt index fe86d524a..f3b622562 100644 --- a/tests/hilti/types/function/hook-methods.hlt +++ b/tests/hilti/types/function/hook-methods.hlt @@ -12,28 +12,28 @@ type X = struct { hook optional> f2(); }; -method hook void X::f1(string s) { +hook void X::f1(string s) { hilti::print("f1 %s %s" % (self.m, s)); } -method hook void X::f1(string s) { +hook void X::f1(string s) { hilti::print("f2 %s %s" % (self.m, s)); } -method hook void X::f1(string s) { +hook void X::f1(string s) { hilti::print("f3 %s %s" % (self.m, s)); } -method hook optional> X::f2() { +hook optional> X::f2() { return Null; } -method hook optional> X::f2() { +hook optional> X::f2() { hilti::print(("in f2:", self.m)); return 42; } -method hook optional> X::f2() { +hook optional> X::f2() { return Null; } diff --git a/tests/hilti/types/function/hook-priorities-fail.hlt b/tests/hilti/types/function/hook-priorities-fail.hlt index 13cfb2ea8..6d05ccf03 100644 --- a/tests/hilti/types/function/hook-priorities-fail.hlt +++ b/tests/hilti/types/function/hook-priorities-fail.hlt @@ -7,8 +7,8 @@ import hilti; declare public hook void f(); -function hook void f() &priority="foo" { } -function hook void f() &priority { } +hook void f() &priority="foo" { } +hook void f() &priority { } function void g() &priority=42 { } } diff --git a/tests/hilti/types/function/hook-priorities.hlt b/tests/hilti/types/function/hook-priorities.hlt index 9a41dac6b..d13ec25b1 100644 --- a/tests/hilti/types/function/hook-priorities.hlt +++ b/tests/hilti/types/function/hook-priorities.hlt @@ -9,7 +9,7 @@ import hilti; declare public hook void f(); -public function hook void f() { hilti::print("f with priority 0"); } +public hook void f() { hilti::print("f with priority 0"); } } @@ -22,8 +22,8 @@ module Bar { import hilti; import Foo; -public function hook void Foo::f() &priority=-5 { hilti::print("f with priority -5"); } -public function hook void Foo::f() &priority=15 { hilti::print("f with priority 15"); } +public hook void Foo::f() &priority=-5 { hilti::print("f with priority -5"); } +public hook void Foo::f() &priority=15 { hilti::print("f with priority 15"); } } @@ -36,6 +36,6 @@ import Foo; Foo::f(); -public function hook void Foo::f() &priority=10 { hilti::print("f with priority 10"); } +public hook void Foo::f() &priority=10 { hilti::print("f with priority 10"); } } diff --git a/tests/hilti/types/function/overloading.hlt b/tests/hilti/types/function/overloading.hlt new file mode 100644 index 000000000..446112b98 --- /dev/null +++ b/tests/hilti/types/function/overloading.hlt @@ -0,0 +1,14 @@ +# @TEST-EXEC: hiltic -j %INPUT >output +# @TEST-EXEC: btest-diff output + +module Foo { + +import hilti; + +function void foo(int<64> x) { hilti::print(("int64", x)); } +function void foo(string x) { hilti::print(("string", x)); } + +foo(42); +foo("foo"); + +} diff --git a/tests/hilti/types/integer/on-heap.hlt b/tests/hilti/types/integer/on-heap.hlt index 660e579e2..0c8803fe6 100644 --- a/tests/hilti/types/integer/on-heap.hlt +++ b/tests/hilti/types/integer/on-heap.hlt @@ -1,5 +1,5 @@ # @TEST-EXEC: ${HILTIC} -j %INPUT >output -# @TEST-EXEC: ${HILTIC} -c %INPUT | grep -q 'hilti::rt::ValueReference> x' +# @TEST-EXEC: ${HILTIC} -c %INPUT | grep -q '::hilti::rt::ValueReference<::hilti::rt::integer::safe> x' # @TEST-EXEC: btest-diff output # # Note: From inside HILTI, it's not easily visible if an integer diff --git a/tests/hilti/types/optional/ops.hlt b/tests/hilti/types/optional/ops.hlt index a4b2134dd..572df8c09 100644 --- a/tests/hilti/types/optional/ops.hlt +++ b/tests/hilti/types/optional/ops.hlt @@ -7,7 +7,7 @@ import hilti; global optional o; -assert-exception *o; +assert-exception *o == ""; o = "123"; assert o; assert *o == "123"; diff --git a/tests/hilti/types/struct/canonical-ids.hlt b/tests/hilti/types/struct/canonical-ids.hlt new file mode 100644 index 000000000..4edea8144 --- /dev/null +++ b/tests/hilti/types/struct/canonical-ids.hlt @@ -0,0 +1,21 @@ +# @TEST-EXEC: hiltic -D ast-declarations -p %INPUT 2>output >/dev/null +# @TEST-EXEC: btest-diff output +# +# @TEST-DOC: Records internal canonical IDs for an example struct. + +module Foo { + +type S = struct { + method void test(string A, real B); +}; + +method void S::test(string A, real B) {} + +global auto s1 = new S(); +s1.test("a", 3); + +# Anonymous struct ctor. +[$foo=42, $bar="string"]; + + +} diff --git a/tests/hilti/types/struct/errors-method.hlt b/tests/hilti/types/struct/errors-method.hlt index d13e2daac..7ce5ab709 100644 --- a/tests/hilti/types/struct/errors-method.hlt +++ b/tests/hilti/types/struct/errors-method.hlt @@ -17,6 +17,14 @@ method void X::y(string s) { } method void X::s(string s) { } method void x(string s) { } +} + +@TEST-START-NEXT + +module Foo { + +import hilti; + function void x() { hilti::print(self); } } diff --git a/tests/hilti/types/struct/errors.hlt b/tests/hilti/types/struct/errors.hlt index 56c00c008..b6963e08d 100644 --- a/tests/hilti/types/struct/errors.hlt +++ b/tests/hilti/types/struct/errors.hlt @@ -12,6 +12,21 @@ type X = struct { global X x; hilti::print(x.DoesNotExist); + +} + +@TEST-START-NEXT + +module Foo { + +import hilti; + +type X = struct { + string s; +}; + +global X x; + unset x.s; } diff --git a/tests/hilti/types/struct/finally-fail.hlt b/tests/hilti/types/struct/finally-fail.hlt index 3d3e62ed3..fadbfb587 100644 --- a/tests/hilti/types/struct/finally-fail.hlt +++ b/tests/hilti/types/struct/finally-fail.hlt @@ -5,11 +5,27 @@ module Foo { -import hilti; - type X = struct { method void ~finally(); +}; + +} + +@TEST-START-NEXT + +module Foo { + +type X = struct { hook optional ~finally(); +}; + +} + +@TEST-START-NEXT + +module Foo { + +type X = struct { hook void ~finally(string s); }; diff --git a/tests/hilti/types/struct/finally.hlt b/tests/hilti/types/struct/finally.hlt index 856390326..4cb4bbc9f 100644 --- a/tests/hilti/types/struct/finally.hlt +++ b/tests/hilti/types/struct/finally.hlt @@ -13,11 +13,11 @@ type X = struct(string name) { hook void ~finally(); }; -method hook void X::~finally() { +hook void X::~finally() { hilti::print("%s: finally 1!" % name); } -method hook void X::~finally() { +hook void X::~finally() { hilti::print("%s: finally 2!" % name); } diff --git a/tests/hilti/types/struct/on-heap-chained.hlt b/tests/hilti/types/struct/on-heap-chained.hlt new file mode 100644 index 000000000..bbff22e62 --- /dev/null +++ b/tests/hilti/types/struct/on-heap-chained.hlt @@ -0,0 +1,25 @@ +# @TEST-EXEC: ${HILTIC} -j %INPUT >output +# +# @TEST-DOC: Ensure deref chains on &on-heap objects work. +# +# No output check, we just ensure this compiles (this used to have constness issue). + +module Foo { + +type X = struct { + method void bar(); +} &on-heap; + +type Y = struct { + X x; + method void foo(); +} &on-heap; + +method void X::bar() { +} + +method void Y::foo() { + (*self.x).bar(); +} + +} diff --git a/tests/hilti/types/struct/string-conversion.hlt b/tests/hilti/types/struct/string-conversion.hlt index adb90f71e..d8beb1877 100644 --- a/tests/hilti/types/struct/string-conversion.hlt +++ b/tests/hilti/types/struct/string-conversion.hlt @@ -19,7 +19,7 @@ type Bar = struct { hook optional __str__(); # not implemented }; -method hook optional Foo::__str__() { +hook optional Foo::__str__() { return self.x + "|" + self.y; } diff --git a/tests/hilti/types/tuple/assign-element.hlt b/tests/hilti/types/tuple/assign-element.hlt index c7c82ac78..78f093e76 100644 --- a/tests/hilti/types/tuple/assign-element.hlt +++ b/tests/hilti/types/tuple/assign-element.hlt @@ -21,6 +21,26 @@ t2.x = new X; t2.x.s = "foo"; t2.y = 42; +hilti::print(t2); + assert t2.x.s == "foo"; assert t2.y == 42; + +type X2 = struct { + addr a; + bytes b; + stream s; + }; + +global X2 x2; +(x2.a, x2.b) = (1.2.3.4, b"xxx"); +assert x2.a == 1.2.3.4; +assert x2.b == b"xxx"; + +global t = (1.2.3.4, b"xxx", b"YYY"); +(x2.a, x2.b, x2.s) = t; +assert x2.a == 1.2.3.4; +assert x2.b == b"xxx"; +assert x2.s == b"YYY"; + } diff --git a/tests/spicy/expressions/list-comprehension-unit.spicy b/tests/spicy/expressions/list-comprehension-unit.spicy new file mode 100644 index 000000000..a02c89b60 --- /dev/null +++ b/tests/spicy/expressions/list-comprehension-unit.spicy @@ -0,0 +1,13 @@ +# @TEST-EXEC: spicyc -j %INPUT >output +# @TEST-EXEC: btest-diff output +# +# @TEST-DOC: List comprehension with a container of units, to ensure &on-heap is treated correctly. + +module X; + +type Bar = unit { + data: bytes &eod; +}; + +global x: vector; +print [i.data for i in x]; diff --git a/tests/spicy/types/bytes/attr-validity.spicy b/tests/spicy/types/bytes/attr-validity.spicy index 73d17c200..d753a8260 100644 --- a/tests/spicy/types/bytes/attr-validity.spicy +++ b/tests/spicy/types/bytes/attr-validity.spicy @@ -11,4 +11,5 @@ public type X = unit { : bytes &until=b"1" &until-including=b"1"; : bytes &parse-from=self.input() &parse-at=self.input(); : bytes &parse-from=self.input(); + %random-access; }; diff --git a/tests/spicy/types/function/overloading.spicy b/tests/spicy/types/function/overloading.spicy new file mode 100644 index 000000000..32025b319 --- /dev/null +++ b/tests/spicy/types/function/overloading.spicy @@ -0,0 +1,10 @@ +# @TEST-EXEC: spicyc -j %INPUT >output +# @TEST-EXEC: btest-diff output + +module Foo; + +function foo(x: int64) { print "int64", x; } +function foo(x: string) { print "string", x; } + +foo(42); +foo("foo"); diff --git a/tests/spicy/types/unit/canonical-ids-with-import.spicy b/tests/spicy/types/unit/canonical-ids-with-import.spicy new file mode 100644 index 000000000..e1fcee274 --- /dev/null +++ b/tests/spicy/types/unit/canonical-ids-with-import.spicy @@ -0,0 +1,20 @@ +# @TEST-EXEC: spicyc -D ast-declarations -p %INPUT a.spicy 2>output >/dev/null +# @TEST-EXEC: btest-diff output +# +# @TEST-DOC: Records internal canonical IDs for an example where we import a unit from another file. + +module b; + +import a; + +on a::X::x { print "from b", $$; } + +@TEST-START-FILE a.spicy + +module a; + +public type X = unit { + x: uint8; +}; + +@TEST-END-FILE diff --git a/tests/spicy/types/unit/canonical-ids.spicy b/tests/spicy/types/unit/canonical-ids.spicy new file mode 100644 index 000000000..f91007920 --- /dev/null +++ b/tests/spicy/types/unit/canonical-ids.spicy @@ -0,0 +1,16 @@ +# @TEST-EXEC: spicyc -D ast-declarations -p %INPUT 2>output >/dev/null +# @TEST-EXEC: btest-diff output +# +# @TEST-DOC: Records internal canonical IDs for a sufficiently complex example. +# +# We reuse the cycle.spicy code here. + +module DNS; + +type Label = unit() { + ptr: Pointer; +}; + +type Pointer = unit() { + name: Label; + }; diff --git a/tests/spicy/types/unit/coercion.spicy b/tests/spicy/types/unit/coercion.spicy new file mode 100644 index 000000000..187d6c395 --- /dev/null +++ b/tests/spicy/types/unit/coercion.spicy @@ -0,0 +1,9 @@ +# @TEST-EXEC: spicyc -p %INPUT +# @TEST-DOC: Check that unit T coerces to T&. + +module testing; + +public type U = unit {}; + +global x: U; +global y: U& = x; diff --git a/tests/spicy/types/unit/cycle.spicy b/tests/spicy/types/unit/cycle.spicy new file mode 100644 index 000000000..dedcb6645 --- /dev/null +++ b/tests/spicy/types/unit/cycle.spicy @@ -0,0 +1,15 @@ +# @TEST-EXEC: spicyc -j %INPUT +# +# @TEST-DOC: Checks for succesful compilation of a cycle unit relationship. +# +# This is a regression test, getting this to compile can be tricky. + +module DNS; + +type Label = unit() { + ptr: Pointer; +}; + +type Pointer = unit() { + name: Label; + }; diff --git a/tests/spicy/types/unit/switch-convert-mixed.spicy b/tests/spicy/types/unit/switch-convert-mixed.spicy new file mode 100644 index 000000000..134a98780 --- /dev/null +++ b/tests/spicy/types/unit/switch-convert-mixed.spicy @@ -0,0 +1,23 @@ +# @TEST-EXEC: ${SPICYC} %INPUT -j -o %INPUT.hlto +# @TEST-EXEC: printf "\001\000\001" | spicy-driver %INPUT.hlto >output 2>&1 +# @TEST-EXEC: printf "\002\000\000\000\002" | spicy-driver %INPUT.hlto >>output 2>&1 +# @TEST-EXEC: btest-diff output +# +# @TEST-DOC: Test hooks on switch fields that have the same name and item types, but different parse types. + +module Mini; + +public type test = unit { + a: bytes &size=1; + + switch ( self.a ) { + b"\x01" -> x: uint16 &convert=cast($$); + b"\x02" -> x: uint32; + }; + + on x { print "in x:1", $$; } + + on %done { print self; } +}; + +on test::x { print "in x:2", $$; } diff --git a/tests/spicy/types/vector/size-from-self.spicy b/tests/spicy/types/vector/size-from-self.spicy new file mode 100644 index 000000000..7ac146e49 --- /dev/null +++ b/tests/spicy/types/vector/size-from-self.spicy @@ -0,0 +1,18 @@ +# @TEST-EXEC: spicyc -j %INPUT +# +# @TEST-DOC: Checks for succesful compilation of parsing a vector with size derived through `self`. +# +# This is a regression test, getting this to compile can be tricky. + +module DNS; + +public type Message = unit { + header: Header; + question: Question[self.header.qdcount]; +}; + +type Header = unit { + qdcount: uint16; +}; + +type Question = unit { };