diff --git a/spicy/toolchain/include/ast/declarations/hook.h b/spicy/toolchain/include/ast/declarations/hook.h index ffe3e0ac4..44f0bbcb8 100644 --- a/spicy/toolchain/include/ast/declarations/hook.h +++ b/spicy/toolchain/include/ast/declarations/hook.h @@ -2,10 +2,7 @@ #pragma once -#include -#include #include -#include #include #include @@ -42,12 +39,16 @@ enum class Type { /** `foreach` hook for containers, executing for each element added. */ ForEach, + + /** `%error` hook executing when an error has occurred processing the field. */ + Error, }; namespace detail { constexpr hilti::util::enum_::Value Types[] = { {Type::Standard, "standard"}, {Type::ForEach, "foreach"}, + {Type::Error, "error"}, }; } // namespace detail @@ -81,6 +82,8 @@ class Hook : public Declaration { hook::Type hookType() const { if ( attributes()->has("foreach") ) return hook::Type::ForEach; + else if ( attributes()->has("%error") ) + return hook::Type::Error; else return hook::Type::Standard; } diff --git a/spicy/toolchain/include/ast/declarations/unit-hook.h b/spicy/toolchain/include/ast/declarations/unit-hook.h index aeb60f64c..f61cf826a 100644 --- a/spicy/toolchain/include/ast/declarations/unit-hook.h +++ b/spicy/toolchain/include/ast/declarations/unit-hook.h @@ -2,8 +2,6 @@ #pragma once -#include -#include #include #include diff --git a/spicy/toolchain/src/compiler/codegen/codegen.cc b/spicy/toolchain/src/compiler/codegen/codegen.cc index ec8da5e31..84b5969c6 100644 --- a/spicy/toolchain/src/compiler/codegen/codegen.cc +++ b/spicy/toolchain/src/compiler/codegen/codegen.cc @@ -631,6 +631,10 @@ hilti::declaration::Function* CodeGen::compileHook(const type::Unit& unit, const builder()->parameter("__dd", field->ddType()->type()->elementType()->type(), hilti::parameter::Kind::In)); params.push_back(builder()->parameter("__stop", builder()->typeBool(), hilti::parameter::Kind::InOut)); } + else if ( type == declaration::hook::Type::Error ) { + if ( params.empty() ) + params.push_back(builder()->parameter("__excpt", builder()->typeString(), hilti::parameter::Kind::In)); + } else if ( original_field_type ) { params.push_back(builder()->parameter("__dd", field->itemType()->type(), hilti::parameter::Kind::In)); @@ -652,10 +656,20 @@ hilti::declaration::Function* CodeGen::compileHook(const type::Unit& unit, const hid = "__str__"; } else { + std::string postfix; + + switch ( type ) { + case declaration::hook::Type::Standard: break; + case declaration::hook::Type::Error: postfix = "_error"; break; + case declaration::hook::Type::ForEach: postfix = "_foreach"; break; + } + + hid = fmt("__on_%s%s", id.local(), postfix); result = builder()->qualifiedType(builder()->typeVoid(), hilti::Constness::Const); - hid = fmt("__on_%s%s", id.local(), (type == declaration::hook::Type::ForEach ? "_foreach" : "")); } + assert(! hid.empty()); + if ( ! id.namespace_().empty() ) hid = fmt("%s::%s", id.namespace_(), hid); diff --git a/spicy/toolchain/src/compiler/codegen/parser-builder.cc b/spicy/toolchain/src/compiler/codegen/parser-builder.cc index e97bdebf6..45411920f 100644 --- a/spicy/toolchain/src/compiler/codegen/parser-builder.cc +++ b/spicy/toolchain/src/compiler/codegen/parser-builder.cc @@ -578,6 +578,24 @@ struct ProductionVisitor : public production::Visitor { // Parse production + std::optional, Builder::TryProxy>> try_; + + if ( is_field_owner ) { + bool has_error_hook = false; + for ( const auto* h : field->hooks() ) { + if ( h->hookType() == declaration::hook::Type::Error ) + has_error_hook = true; + } + + if ( has_error_hook || ! field->isAnonymous() ) { + // Wrap field parsing into try-block for per-field %error hook, + // which may be attached directly or provided externally for + // named fields. + try_ = builder()->addTry(); + pushBuilder(try_->first); + } + } + builder()->setLocation(p->location()); Expression* pre_container_offset = nullptr; @@ -664,6 +682,20 @@ struct ProductionVisitor : public production::Visitor { path_tracker.reset(); } + if ( try_ ) { + popBuilder(); // per-field try-block + + auto catch_ = + try_->second.addCatch(builder()->parameter("__except", builder()->typeName("hilti::SystemException"))); + + pushBuilder(catch_, [&]() { + auto what = builder()->call("hilti::exception_what", {builder()->id("__except")}); + builder()->addMemberCall(state().self, ID(fmt("__on_%s_error", field->id().local())), {what}, + field->meta()); + builder()->addRethrow(); + }); + } + // Top of stack will now have the final value for the field. Expression* stop = builder()->bool_(false); diff --git a/spicy/toolchain/src/compiler/codegen/unit-builder.cc b/spicy/toolchain/src/compiler/codegen/unit-builder.cc index bbc60fd8f..8c20c08ad 100644 --- a/spicy/toolchain/src/compiler/codegen/unit-builder.cc +++ b/spicy/toolchain/src/compiler/codegen/unit-builder.cc @@ -77,6 +77,7 @@ struct FieldBuilder : public visitor::PreOrder { if ( f->emitHook() ) { add_hook_declaration(f, declaration::hook::Type::Standard); + add_hook_declaration(f, declaration::hook::Type::Error); if ( f->isContainer() ) add_hook_declaration(f, declaration::hook::Type::ForEach); diff --git a/spicy/toolchain/src/compiler/parser/parser.yy b/spicy/toolchain/src/compiler/parser/parser.yy index e7e528bee..f232f242c 100644 --- a/spicy/toolchain/src/compiler/parser/parser.yy +++ b/spicy/toolchain/src/compiler/parser/parser.yy @@ -35,7 +35,7 @@ namespace spicy { namespace detail { class Parser; } } %verbose %glr-parser -%expect 94 +%expect 100 %expect-rr 168 %{ @@ -902,6 +902,7 @@ unit_hook_attribute : FOREACH { $$ = builder->attribute("foreach", __loc__); } | PRIORITY '=' expr { $$ = builder->attribute("&priority", std::move($3), __loc__); } | PROPERTY { $$ = builder->attribute($1, __loc__); } + | attribute { $$ = std::move($1); } unit_switch : SWITCH opt_unit_switch_expr '{' unit_switch_cases '}' opt_attributes opt_unit_field_condition ';' { $$ = builder->typeUnitItemSwitch(std::move($2), std::move($4), std::move($7), {}, std::move($6), __loc__); } diff --git a/spicy/toolchain/src/compiler/resolver.cc b/spicy/toolchain/src/compiler/resolver.cc index 60ac9996d..1c02758a6 100644 --- a/spicy/toolchain/src/compiler/resolver.cc +++ b/spicy/toolchain/src/compiler/resolver.cc @@ -131,9 +131,7 @@ struct VisitorPass2 : visitor::MutatingPostOrder { } void operator()(declaration::UnitHook* n) final { - if ( ! n->fullyQualifiedID() ) { - if ( auto m = n->parent() ) - n->setFullyQualifiedID(m->id() + n->id()); // global scope + if ( n->hook()->hookType() == declaration::hook::Type::Error ) { } } diff --git a/spicy/toolchain/src/compiler/validator.cc b/spicy/toolchain/src/compiler/validator.cc index aa05d9b20..3a790412b 100644 --- a/spicy/toolchain/src/compiler/validator.cc +++ b/spicy/toolchain/src/compiler/validator.cc @@ -479,16 +479,22 @@ struct VisitorPost : visitor::PreOrder, hilti::validator::VisitorMixIn { } void operator()(spicy::declaration::Hook* n) final { - for ( const auto* attr : n->attributes()->attributes() ) { - if ( attr->tag() == "foreach" ) { + int count = 0; + switch ( n->hookType() ) { + case declaration::hook::Type::ForEach: if ( auto field = n->parent(); field && ! field->isContainer() ) error("'foreach' can only be used with containers", n); - } - else if ( attr->tag() == "%debug" || attr->tag() == "&priority" ) - continue; - else - error(fmt("unknown hook type '%s'", attr->tag()), n); + + ++count; + break; + + case declaration::hook::Type::Error: ++count; break; + + default: break; } + + if ( count > 1 ) + error("hook cannot have be both types, 'foreach' and '%error'", n); } void operator()(spicy::type::unit::item::UnitHook* n) final { @@ -1018,6 +1024,12 @@ struct VisitorPost : visitor::PreOrder, hilti::validator::VisitorMixIn { id_readable), n, location); } + + else if ( hook->hookType() == declaration::hook::Type::Error && ! params.empty() ) { + if ( params.size() != 1 || ! hilti::type::same(params[0]->type()->type(), builder()->typeString()) ) + error("%error hook must only take a string parameter", n, location); + } + else { if ( auto i = unit->itemByName(ID(id)); ! i ) error(fmt("no field '%s' in unit type", id), n, location); diff --git a/tests/Baseline/spicy.optimization.default-parser-functions/log b/tests/Baseline/spicy.optimization.default-parser-functions/log index aa9cee054..95f7d4e6e 100644 --- a/tests/Baseline/spicy.optimization.default-parser-functions/log +++ b/tests/Baseline/spicy.optimization.default-parser-functions/log @@ -273,6 +273,10 @@ [debug/optimizer] [] statement::If "if ( False ) { }" -> null [debug/optimizer] [] statement::If "if ( False ) { }" -> null [debug/optimizer] [] statement::If "if ( False ) { }" -> null +[debug/optimizer] [] statement::Try "try { # "<...>/default-parser-functions.spicy:17:8-17:12" # Begin parsing production: Variable: x -> uint<8> spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:17:8-17:12", Null); ((*self).x, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); if ( __trim ) (*__data).trim(begin(__cur)); # End parsing production: Variable: x -> uint<8> (*self).__error = __error; __error = (*self).__error; } catch ( hilti::SystemException __except ) { throw; }" -> replacing rethrowing try/catch with just the block +[debug/optimizer] [] statement::Try "try { # "<...>/default-parser-functions.spicy:17:8-17:12" # Begin parsing production: Variable: x -> uint<8> spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:17:8-17:12", Null); ((*self).x, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); if ( __trim ) (*__data).trim(begin(__cur)); # End parsing production: Variable: x -> uint<8> (*self).__error = __error; __error = (*self).__error; } catch ( hilti::SystemException __except ) { throw; }" -> statement::Block "{ # "<...>/default-parser-functions.spicy:17:8-17:12" # Begin parsing production: Variable: x -> uint<8> spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:17:8-17:12", Null); ((*self).x, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); if ( __trim ) (*__data).trim(begin(__cur)); # End parsing production: Variable: x -> uint<8> (*self).__error = __error; __error = (*self).__error; }" +[debug/optimizer] [] statement::Try "try { # "<...>/default-parser-functions.spicy:18:8-18:12" # Begin parsing production: Variable: y -> uint<8> spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:18:8-18:12", Null); ((*self).y, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); if ( __trim ) (*__data).trim(begin(__cur)); # End parsing production: Variable: y -> uint<8> (*self).__error = __error; (*self).__on_y((*self).y); __error = (*self).__error; } catch ( hilti::SystemException __except ) { throw; }" -> replacing rethrowing try/catch with just the block +[debug/optimizer] [] statement::Try "try { # "<...>/default-parser-functions.spicy:18:8-18:12" # Begin parsing production: Variable: y -> uint<8> spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:18:8-18:12", Null); ((*self).y, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); if ( __trim ) (*__data).trim(begin(__cur)); # End parsing production: Variable: y -> uint<8> (*self).__error = __error; (*self).__on_y((*self).y); __error = (*self).__error; } catch ( hilti::SystemException __except ) { throw; }" -> statement::Block "{ # "<...>/default-parser-functions.spicy:18:8-18:12" # Begin parsing production: Variable: y -> uint<8> spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:18:8-18:12", Null); ((*self).y, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); if ( __trim ) (*__data).trim(begin(__cur)); # End parsing production: Variable: y -> uint<8> (*self).__error = __error; (*self).__on_y((*self).y); __error = (*self).__error; }" [debug/optimizer] [] statement::Try "try { hilti::debugIndent("spicy"); local iterator __begin = begin(__cur); local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo__P1_stage2(__data, __begin, __cur, __trim, __lah, __lahe, __error); } catch ( hilti::SystemException __except ) { throw; }" -> replacing rethrowing try/catch with just the block [debug/optimizer] [] statement::Try "try { hilti::debugIndent("spicy"); local iterator __begin = begin(__cur); local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo__P1_stage2(__data, __begin, __cur, __trim, __lah, __lahe, __error); } catch ( hilti::SystemException __except ) { throw; }" -> statement::Block "{ hilti::debugIndent("spicy"); local iterator __begin = begin(__cur); local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo__P1_stage2(__data, __begin, __cur, __trim, __lah, __lahe, __error); }" [debug/optimizer] [default-parser-functions.spicy:10:1-21:2] declaration::Constant "const bool __feat%foo@@P0%supports_filters = False;" -> disabled feature 'supports_filters' of type 'foo::P0' since it is not used @@ -370,8 +374,14 @@ [debug/optimizer] [default-parser-functions.spicy:16:18-21:1] statement::Expression "default();" -> removing default statement [debug/optimizer] [default-parser-functions.spicy:16:18-21:1] statement::Expression "default();" -> removing default statement [debug/optimizer] [default-parser-functions.spicy:17:5-17:13] declaration::Field "hook void __on_x(uint<8> __dd);" -> null +[debug/optimizer] [default-parser-functions.spicy:17:5-17:13] declaration::Field "hook void __on_x_error(string __excpt);" -> null [debug/optimizer] [default-parser-functions.spicy:17:5-17:13] operator_::struct_::MemberCall "(*self).__on_x((*self).x)" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) +[debug/optimizer] [default-parser-functions.spicy:17:5-17:13] operator_::struct_::MemberCall "(*self).__on_x_error(hilti::exception_what(__except))" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) [debug/optimizer] [default-parser-functions.spicy:17:5-17:13] statement::Expression "default();" -> removing default statement +[debug/optimizer] [default-parser-functions.spicy:17:5-17:13] statement::Expression "default();" -> removing default statement +[debug/optimizer] [default-parser-functions.spicy:18:5-18:15] declaration::Field "hook void __on_y_error(string __excpt);" -> null +[debug/optimizer] [default-parser-functions.spicy:18:5-18:15] operator_::struct_::MemberCall "(*self).__on_y_error(hilti::exception_what(__except))" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) +[debug/optimizer] [default-parser-functions.spicy:18:5-18:15] statement::Expression "default();" -> removing default statement [debug/optimizer] [spicy_rt.hlt:28:5-28:76] declaration::Field "method void connect_mime_type(string mime_type, string scope) &internal;" -> null (removing unused member) [debug/optimizer] [spicy_rt.hlt:29:5-29:75] declaration::Field "method void connect_mime_type(bytes mime_type, string scope) &internal;" -> null (removing unused member) [debug/optimizer] removing field for unused method foo::P0::__on_0x25_confirmed @@ -416,6 +426,8 @@ [debug/optimizer] removing field for unused method foo::P2::__on_0x25_synced [debug/optimizer] removing field for unused method foo::P2::__on_0x25_undelivered [debug/optimizer] removing field for unused method foo::P2::__on_x +[debug/optimizer] removing field for unused method foo::P2::__on_x_error +[debug/optimizer] removing field for unused method foo::P2::__on_y_error [debug/optimizer] removing field for unused method foo::P2::__str__ [debug/optimizer] removing field for unused method struct_3::parse1 [debug/optimizer] removing field for unused method struct_3::parse2 diff --git a/tests/Baseline/spicy.optimization.default-parser-functions/noopt.hlt b/tests/Baseline/spicy.optimization.default-parser-functions/noopt.hlt index bdc7eeab0..920eade4f 100644 --- a/tests/Baseline/spicy.optimization.default-parser-functions/noopt.hlt +++ b/tests/Baseline/spicy.optimization.default-parser-functions/noopt.hlt @@ -72,7 +72,9 @@ public type P2 = struct { strong_ref __filters &internal &needed-by-feature="supports_filters"; optional __error &always-emit &internal; hook void __on_x(uint<8> __dd); + hook void __on_x_error(string __excpt); hook void __on_y(uint<8> __dd); + hook void __on_y_error(string __excpt); hook void __on_0x25_init(); hook void __on_0x25_done(); hook void __on_0x25_error(string __except); @@ -732,33 +734,40 @@ method method tuple, int<64>, const iterator, optiona method method tuple, int<64>, const iterator, optional> foo::P2::__parse_foo__P2_stage2(inout value_ref __data, iterator __begin, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error) { # "<...>/default-parser-functions.spicy:16:18-21:1" local tuple, int<64>, const iterator, optional> __result; - # "<...>/default-parser-functions.spicy:17:8-17:12" + try { + # "<...>/default-parser-functions.spicy:17:8-17:12" - # Begin parsing production: Variable: x -> uint<8> - spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:17:8-17:12", ::__feat%foo@@P2%supports_filters ? (*self).__filters : Null); - ((*self).x, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); + # Begin parsing production: Variable: x -> uint<8> + spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:17:8-17:12", ::__feat%foo@@P2%supports_filters ? (*self).__filters : Null); + ((*self).x, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); - if ( __trim ) - (*__data).trim(begin(__cur)); + if ( __trim ) + (*__data).trim(begin(__cur)); - # End parsing production: Variable: x -> uint<8> + # End parsing production: Variable: x -> uint<8> - (*self).__error = __error; + (*self).__error = __error; - if ( ::__feat%foo@@P2%uses_random_access || ::__feat%foo@@P2%uses_offset ) - (*self).__position_update = Null; + if ( ::__feat%foo@@P2%uses_random_access || ::__feat%foo@@P2%uses_offset ) + (*self).__position_update = Null; - (*self).__on_x((*self).x); + (*self).__on_x((*self).x); - if ( ::__feat%foo@@P2%uses_random_access || ::__feat%foo@@P2%uses_offset ) + if ( ::__feat%foo@@P2%uses_random_access || ::__feat%foo@@P2%uses_offset ) - if ( (*self).__position_update ) { - __cur = __cur.advance((*(*self).__position_update)); - (*self).__position_update = Null; - } + if ( (*self).__position_update ) { + __cur = __cur.advance((*(*self).__position_update)); + (*self).__position_update = Null; + } - __error = (*self).__error; + __error = (*self).__error; + } + catch ( hilti::SystemException __except ) { + (*self).__on_x_error(hilti::exception_what(__except)); + throw; + } + if ( ::__feat%foo@@P2%uses_random_access ) (*self).__begin = __begin; @@ -767,33 +776,40 @@ method method tuple, int<64>, const iterator, optiona if ( ::__feat%foo@@P2%uses_offset ) (*self).__offset = cast>(begin(__cur).offset() - __begin.offset()); - # "<...>/default-parser-functions.spicy:18:8-18:12" + try { + # "<...>/default-parser-functions.spicy:18:8-18:12" - # Begin parsing production: Variable: y -> uint<8> - spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:18:8-18:12", ::__feat%foo@@P2%supports_filters ? (*self).__filters : Null); - ((*self).y, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); + # Begin parsing production: Variable: y -> uint<8> + spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:18:8-18:12", ::__feat%foo@@P2%supports_filters ? (*self).__filters : Null); + ((*self).y, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); - if ( __trim ) - (*__data).trim(begin(__cur)); + if ( __trim ) + (*__data).trim(begin(__cur)); - # End parsing production: Variable: y -> uint<8> + # End parsing production: Variable: y -> uint<8> - (*self).__error = __error; + (*self).__error = __error; - if ( ::__feat%foo@@P2%uses_random_access || ::__feat%foo@@P2%uses_offset ) - (*self).__position_update = Null; + if ( ::__feat%foo@@P2%uses_random_access || ::__feat%foo@@P2%uses_offset ) + (*self).__position_update = Null; - (*self).__on_y((*self).y); + (*self).__on_y((*self).y); - if ( ::__feat%foo@@P2%uses_random_access || ::__feat%foo@@P2%uses_offset ) + if ( ::__feat%foo@@P2%uses_random_access || ::__feat%foo@@P2%uses_offset ) - if ( (*self).__position_update ) { - __cur = __cur.advance((*(*self).__position_update)); - (*self).__position_update = Null; - } + if ( (*self).__position_update ) { + __cur = __cur.advance((*(*self).__position_update)); + (*self).__position_update = Null; + } - __error = (*self).__error; + __error = (*self).__error; + } + catch ( hilti::SystemException __except ) { + (*self).__on_y_error(hilti::exception_what(__except)); + throw; + } + if ( ::__feat%foo@@P2%uses_random_access ) (*self).__begin = __begin; diff --git a/tests/Baseline/spicy.optimization.default-parser-functions/opt.hlt b/tests/Baseline/spicy.optimization.default-parser-functions/opt.hlt index 980664ae7..b0ae88da2 100644 --- a/tests/Baseline/spicy.optimization.default-parser-functions/opt.hlt +++ b/tests/Baseline/spicy.optimization.default-parser-functions/opt.hlt @@ -170,31 +170,36 @@ method method tuple, int<64>, const iterator, optiona method method tuple, int<64>, const iterator, optional> foo::P2::__parse_foo__P2_stage2(inout value_ref __data, iterator __begin, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error) { # "<...>/default-parser-functions.spicy:16:18-21:1" local tuple, int<64>, const iterator, optional> __result; - # "<...>/default-parser-functions.spicy:17:8-17:12" + { + # "<...>/default-parser-functions.spicy:17:8-17:12" - # Begin parsing production: Variable: x -> uint<8> - spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:17:8-17:12", Null); - ((*self).x, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); + # Begin parsing production: Variable: x -> uint<8> + spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:17:8-17:12", Null); + ((*self).x, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); - if ( __trim ) - (*__data).trim(begin(__cur)); + if ( __trim ) + (*__data).trim(begin(__cur)); - # End parsing production: Variable: x -> uint<8> + # End parsing production: Variable: x -> uint<8> + } - # "<...>/default-parser-functions.spicy:18:8-18:12" + { + # "<...>/default-parser-functions.spicy:18:8-18:12" - # Begin parsing production: Variable: y -> uint<8> - spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:18:8-18:12", Null); - ((*self).y, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); + # Begin parsing production: Variable: y -> uint<8> + spicy_rt::waitForInput(__data, __cur, 1, "expecting 1 bytes for unpacking value", "<...>/default-parser-functions.spicy:18:8-18:12", Null); + ((*self).y, __cur) = (*unpack>((__cur, hilti::ByteOrder::Network))); - if ( __trim ) - (*__data).trim(begin(__cur)); + if ( __trim ) + (*__data).trim(begin(__cur)); - # End parsing production: Variable: y -> uint<8> + # End parsing production: Variable: y -> uint<8> + + (*self).__error = __error; + (*self).__on_y((*self).y); + __error = (*self).__error; + } - (*self).__error = __error; - (*self).__on_y((*self).y); - __error = (*self).__error; hilti::debugDedent("spicy"); __result = (__cur, __lah, __lahe, __error); return __result; diff --git a/tests/Baseline/spicy.optimization.unused-types/log b/tests/Baseline/spicy.optimization.unused-types/log index 1e58b9a6b..ae82b22b6 100644 --- a/tests/Baseline/spicy.optimization.unused-types/log +++ b/tests/Baseline/spicy.optimization.unused-types/log @@ -788,6 +788,8 @@ [debug/optimizer] [] statement::If "if ( False ) { }" -> null [debug/optimizer] [] statement::If "if ( False ) { }" -> null [debug/optimizer] [] statement::If "if ( False ) { }" -> null +[debug/optimizer] [] statement::Try "try { # "<...>/unused-types.spicy:28:14-28:20" # Begin parsing production: Unit: foo__Priv6_2 -> (*self).x = default(); (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); # End parsing production: Unit: foo__Priv6_2 -> (*self).__error = __error; __error = (*self).__error; } catch ( hilti::SystemException __except ) { throw; }" -> replacing rethrowing try/catch with just the block +[debug/optimizer] [] statement::Try "try { # "<...>/unused-types.spicy:28:14-28:20" # Begin parsing production: Unit: foo__Priv6_2 -> (*self).x = default(); (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); # End parsing production: Unit: foo__Priv6_2 -> (*self).__error = __error; __error = (*self).__error; } catch ( hilti::SystemException __except ) { throw; }" -> statement::Block "{ # "<...>/unused-types.spicy:28:14-28:20" # Begin parsing production: Unit: foo__Priv6_2 -> (*self).x = default(); (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); # End parsing production: Unit: foo__Priv6_2 -> (*self).__error = __error; __error = (*self).__error; }" [debug/optimizer] [] statement::Try "try { hilti::debugIndent("spicy"); local iterator __begin = begin(__cur); local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo__Priv10_stage2(__data, __begin, __cur, __trim, __lah, __lahe, __error); } catch ( hilti::SystemException __except ) { throw; }" -> replacing rethrowing try/catch with just the block [debug/optimizer] [] statement::Try "try { hilti::debugIndent("spicy"); local iterator __begin = begin(__cur); local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo__Priv10_stage2(__data, __begin, __cur, __trim, __lah, __lahe, __error); } catch ( hilti::SystemException __except ) { throw; }" -> statement::Block "{ hilti::debugIndent("spicy"); local iterator __begin = begin(__cur); local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo__Priv10_stage2(__data, __begin, __cur, __trim, __lah, __lahe, __error); }" [debug/optimizer] [] statement::Try "try { hilti::debugIndent("spicy"); local iterator __begin = begin(__cur); local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo__Priv5_stage2(__data, __begin, __cur, __trim, __lah, __lahe, __error); } catch ( hilti::SystemException __except ) { throw; }" -> replacing rethrowing try/catch with just the block @@ -978,7 +980,7 @@ [debug/optimizer] [unused-types.spicy:21:14-24:1] declaration::Function "method extern method view foo::Priv4::parse1(inout value_ref __data, optional> __cur = Null, optional __context) &needed-by-feature="is_filter" &needed-by-feature="supports_sinks" &static { # "<...>/unused-types.spicy:21:14-24:1" local value_ref __unit = value_ref(default()); local view __ncur = __cur ? (*__cur) : cast>((*__data)); local int<64> __lahead = 0; local iterator __lahead_end; local optional __error = Null; # "<...>/unused-types.spicy:21:14-24:1" # Begin parsing production: Unit: foo__Priv4 -> Resolved_6 Resolved_7 (__ncur, __lahead, __lahead_end, __error) = (*__unit).__parse_stage1(__data, begin(__ncur), __ncur, True, __lahead, __lahead_end, __error); # End parsing production: Unit: foo__Priv4 -> Resolved_6 Resolved_7 if ( __error ) throw exception("successful synchronization never confirmed: %s" % (hilti::exception_what((*__error)))); return __ncur; }" -> null (removing declaration for unused function) [debug/optimizer] [unused-types.spicy:21:14-24:1] declaration::Function "method extern method view foo::Priv4::parse2(inout value_ref __unit, inout value_ref __data, optional> __cur = Null, optional __context) &needed-by-feature="is_filter" &needed-by-feature="supports_sinks" &static { # "<...>/unused-types.spicy:21:14-24:1" local view __ncur = __cur ? (*__cur) : cast>((*__data)); local int<64> __lahead = 0; local iterator __lahead_end; local optional __error = Null; # "<...>/unused-types.spicy:21:14-24:1" # Begin parsing production: Unit: foo__Priv4 -> Resolved_6 Resolved_7 (__ncur, __lahead, __lahead_end, __error) = (*__unit).__parse_stage1(__data, begin(__ncur), __ncur, True, __lahead, __lahead_end, __error); # End parsing production: Unit: foo__Priv4 -> Resolved_6 Resolved_7 if ( __error ) throw exception("successful synchronization never confirmed: %s" % (hilti::exception_what((*__error)))); return __ncur; }" -> null (removing declaration for unused function) [debug/optimizer] [unused-types.spicy:21:14-24:1] declaration::Function "method extern method view foo::Priv4::parse3(inout value_ref __gunit, inout value_ref __data, optional> __cur = Null, optional __context) &needed-by-feature="is_filter" &needed-by-feature="supports_sinks" &static { # "<...>/unused-types.spicy:21:14-24:1" local value_ref __unit = value_ref(default()); spicy_rt::initializeParsedUnit((*__gunit), __unit, typeinfo(foo::Priv4)); local view __ncur = __cur ? (*__cur) : cast>((*__data)); local int<64> __lahead = 0; local iterator __lahead_end; local optional __error = Null; # "<...>/unused-types.spicy:21:14-24:1" # Begin parsing production: Unit: foo__Priv4 -> Resolved_6 Resolved_7 (__ncur, __lahead, __lahead_end, __error) = (*__unit).__parse_stage1(__data, begin(__ncur), __ncur, True, __lahead, __lahead_end, __error); # End parsing production: Unit: foo__Priv4 -> Resolved_6 Resolved_7 if ( __error ) throw exception("successful synchronization never confirmed: %s" % (hilti::exception_what((*__error)))); return __ncur; }" -> null (removing declaration for unused function) -[debug/optimizer] [unused-types.spicy:21:14-24:1] declaration::Function "method method tuple, int<64>, const iterator, optional> foo::Priv4::__parse_foo__Priv4_stage2(inout value_ref __data, iterator __begin, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error) { # "<...>/unused-types.spicy:21:14-24:1" local tuple, int<64>, const iterator, optional> __result; local value_ref __transient__anon; # "<...>/unused-types.spicy:19:14-19:20" # Begin parsing production: Unit: foo__Priv2_2 -> __transient__anon = default(); (__cur, __lah, __lahe, __error) = (*__transient__anon).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); # End parsing production: Unit: foo__Priv2_2 -> # "<...>/unused-types.spicy:20:14-20:20" # Begin parsing production: Unit: foo__Priv3_2 -> (*self).x = default(); (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); # End parsing production: Unit: foo__Priv3_2 -> (*self).__error = __error; default(); __error = (*self).__error; (*self).__error = __error; default(); __error = (*self).__error; hilti::debugDedent("spicy"); __result = (__cur, __lah, __lahe, __error); return __result; }" -> null (removing declaration for unused function) +[debug/optimizer] [unused-types.spicy:21:14-24:1] declaration::Function "method method tuple, int<64>, const iterator, optional> foo::Priv4::__parse_foo__Priv4_stage2(inout value_ref __data, iterator __begin, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error) { # "<...>/unused-types.spicy:21:14-24:1" local tuple, int<64>, const iterator, optional> __result; local value_ref __transient__anon; # "<...>/unused-types.spicy:19:14-19:20" # Begin parsing production: Unit: foo__Priv2_2 -> __transient__anon = default(); (__cur, __lah, __lahe, __error) = (*__transient__anon).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); # End parsing production: Unit: foo__Priv2_2 -> try { # "<...>/unused-types.spicy:20:14-20:20" # Begin parsing production: Unit: foo__Priv3_2 -> (*self).x = default(); (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); # End parsing production: Unit: foo__Priv3_2 -> (*self).__error = __error; default(); __error = (*self).__error; } catch ( hilti::SystemException __except ) { default(); throw; } (*self).__error = __error; default(); __error = (*self).__error; hilti::debugDedent("spicy"); __result = (__cur, __lah, __lahe, __error); return __result; }" -> null (removing declaration for unused function) [debug/optimizer] [unused-types.spicy:21:14-24:1] declaration::Function "method method tuple, int<64>, const iterator, optional> foo::Priv4::__parse_stage1(inout value_ref __data, iterator __begin, copy view __cur, copy bool __trim, copy int<64> __lah, copy iterator __lahe, copy optional __error) { # "<...>/unused-types.spicy:21:14-24:1" local tuple, int<64>, const iterator, optional> __result; try { hilti::debugIndent("spicy"); local iterator __begin = begin(__cur); (*self).__error = __error; default(); __error = (*self).__error; local strong_ref filtered = Null; if ( ! filtered ) __result = (*self).__parse_foo__Priv4_stage2(__data, __begin, __cur, __trim, __lah, __lahe, __error); } catch ( hilti::SystemException __except ) { default(); (*self).__error = __error; default(); __error = (*self).__error; throw; } (*self).__error = __error; default(); __error = (*self).__error; return __result; }" -> null (removing declaration for unused function) [debug/optimizer] [unused-types.spicy:21:14-24:1] operator_::struct_::MemberCall "(*self).__on_0x25_done()" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) [debug/optimizer] [unused-types.spicy:21:14-24:1] operator_::struct_::MemberCall "(*self).__on_0x25_error(hilti::exception_what(__except))" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) @@ -986,7 +988,9 @@ [debug/optimizer] [unused-types.spicy:21:14-24:1] operator_::struct_::MemberCall "(*self).__on_0x25_finally()" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) [debug/optimizer] [unused-types.spicy:21:14-24:1] operator_::struct_::MemberCall "(*self).__on_0x25_init()" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) [debug/optimizer] [unused-types.spicy:23:5-23:13] declaration::Field "hook void __on_x(value_ref __dd);" -> null +[debug/optimizer] [unused-types.spicy:23:5-23:13] declaration::Field "hook void __on_x_error(string __excpt);" -> null [debug/optimizer] [unused-types.spicy:23:5-23:13] operator_::struct_::MemberCall "(*self).__on_x((*self).x)" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) +[debug/optimizer] [unused-types.spicy:23:5-23:13] operator_::struct_::MemberCall "(*self).__on_x_error(hilti::exception_what(__except))" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) [debug/optimizer] [unused-types.spicy:27:14-27:20] declaration::Field "hook optional __str__();" -> null [debug/optimizer] [unused-types.spicy:27:14-27:20] declaration::Field "hook void __on_0x25_confirmed() &needed-by-feature="synchronization";" -> null [debug/optimizer] [unused-types.spicy:27:14-27:20] declaration::Field "hook void __on_0x25_done();" -> null @@ -1066,7 +1070,10 @@ [debug/optimizer] [unused-types.spicy:29:20-32:1] statement::Expression "default();" -> removing default statement [debug/optimizer] [unused-types.spicy:29:20-32:1] statement::Expression "default();" -> removing default statement [debug/optimizer] [unused-types.spicy:31:5-31:13] declaration::Field "hook void __on_x(value_ref __dd);" -> null +[debug/optimizer] [unused-types.spicy:31:5-31:13] declaration::Field "hook void __on_x_error(string __excpt);" -> null [debug/optimizer] [unused-types.spicy:31:5-31:13] operator_::struct_::MemberCall "(*self).__on_x((*self).x)" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) +[debug/optimizer] [unused-types.spicy:31:5-31:13] operator_::struct_::MemberCall "(*self).__on_x_error(hilti::exception_what(__except))" -> expression::Ctor "default()" (replacing call to unimplemented method with default value) +[debug/optimizer] [unused-types.spicy:31:5-31:13] statement::Expression "default();" -> removing default statement [debug/optimizer] [unused-types.spicy:31:5-31:13] statement::Expression "default();" -> removing default statement [debug/optimizer] [unused-types.spicy:35:1-35:30] declaration::Type "type Priv7 = enum { A = 0, B = 1, C = 2 };" -> null (removing unused type) [debug/optimizer] [unused-types.spicy:43:22-46:1] declaration::Field "hook optional __str__();" -> null @@ -1172,6 +1179,7 @@ [debug/optimizer] removing field for unused method foo::Priv4::__on_0x25_synced [debug/optimizer] removing field for unused method foo::Priv4::__on_0x25_undelivered [debug/optimizer] removing field for unused method foo::Priv4::__on_x +[debug/optimizer] removing field for unused method foo::Priv4::__on_x_error [debug/optimizer] removing field for unused method foo::Priv4::__parse_stage1 [debug/optimizer] removing field for unused method foo::Priv4::__str__ [debug/optimizer] removing field for unused method foo::Priv4::parse1 @@ -1235,6 +1243,7 @@ [debug/optimizer] removing field for unused method foo::Pub3::__on_0x25_synced [debug/optimizer] removing field for unused method foo::Pub3::__on_0x25_undelivered [debug/optimizer] removing field for unused method foo::Pub3::__on_x +[debug/optimizer] removing field for unused method foo::Pub3::__on_x_error [debug/optimizer] removing field for unused method foo::Pub3::__str__ [debug/optimizer] removing field for unused method struct_10::parse1 [debug/optimizer] removing field for unused method struct_10::parse2 diff --git a/tests/Baseline/spicy.optimization.unused-types/noopt.hlt b/tests/Baseline/spicy.optimization.unused-types/noopt.hlt index de0b445e6..aaee81bfb 100644 --- a/tests/Baseline/spicy.optimization.unused-types/noopt.hlt +++ b/tests/Baseline/spicy.optimization.unused-types/noopt.hlt @@ -128,6 +128,7 @@ type Priv4 = struct { strong_ref __filters &internal &needed-by-feature="supports_filters"; optional __error &always-emit &internal; hook void __on_x(value_ref __dd); + hook void __on_x_error(string __excpt); hook void __on_0x25_init(); hook void __on_0x25_done(); hook void __on_0x25_error(string __except); @@ -215,6 +216,7 @@ public type Pub3 = struct { strong_ref __filters &internal &needed-by-feature="supports_filters"; optional __error &always-emit &internal; hook void __on_x(value_ref __dd); + hook void __on_x_error(string __excpt); hook void __on_0x25_init(); hook void __on_0x25_done(); hook void __on_0x25_error(string __except); @@ -1462,29 +1464,36 @@ method method tuple, int<64>, const iterator, optiona if ( ::__feat%foo@@Priv4%uses_offset ) (*self).__offset = cast>(begin(__cur).offset() - __begin.offset()); - # "<...>/unused-types.spicy:20:14-20:20" + try { + # "<...>/unused-types.spicy:20:14-20:20" - # Begin parsing production: Unit: foo__Priv3_2 -> - (*self).x = default(); - (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); - # End parsing production: Unit: foo__Priv3_2 -> + # Begin parsing production: Unit: foo__Priv3_2 -> + (*self).x = default(); + (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); + # End parsing production: Unit: foo__Priv3_2 -> - (*self).__error = __error; + (*self).__error = __error; - if ( ::__feat%foo@@Priv4%uses_random_access || ::__feat%foo@@Priv4%uses_offset ) - (*self).__position_update = Null; + if ( ::__feat%foo@@Priv4%uses_random_access || ::__feat%foo@@Priv4%uses_offset ) + (*self).__position_update = Null; - (*self).__on_x((*self).x); + (*self).__on_x((*self).x); - if ( ::__feat%foo@@Priv4%uses_random_access || ::__feat%foo@@Priv4%uses_offset ) + if ( ::__feat%foo@@Priv4%uses_random_access || ::__feat%foo@@Priv4%uses_offset ) - if ( (*self).__position_update ) { - __cur = __cur.advance((*(*self).__position_update)); - (*self).__position_update = Null; - } + if ( (*self).__position_update ) { + __cur = __cur.advance((*(*self).__position_update)); + (*self).__position_update = Null; + } - __error = (*self).__error; + __error = (*self).__error; + } + catch ( hilti::SystemException __except ) { + (*self).__on_x_error(hilti::exception_what(__except)); + throw; + } + if ( ::__feat%foo@@Priv4%uses_random_access ) (*self).__begin = __begin; @@ -2246,29 +2255,36 @@ method method tuple, int<64>, const iterator, optiona if ( ::__feat%foo@@Pub3%uses_offset ) (*self).__offset = cast>(begin(__cur).offset() - __begin.offset()); - # "<...>/unused-types.spicy:28:14-28:20" + try { + # "<...>/unused-types.spicy:28:14-28:20" - # Begin parsing production: Unit: foo__Priv6_2 -> - (*self).x = default(); - (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); - # End parsing production: Unit: foo__Priv6_2 -> + # Begin parsing production: Unit: foo__Priv6_2 -> + (*self).x = default(); + (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); + # End parsing production: Unit: foo__Priv6_2 -> - (*self).__error = __error; + (*self).__error = __error; - if ( ::__feat%foo@@Pub3%uses_random_access || ::__feat%foo@@Pub3%uses_offset ) - (*self).__position_update = Null; + if ( ::__feat%foo@@Pub3%uses_random_access || ::__feat%foo@@Pub3%uses_offset ) + (*self).__position_update = Null; - (*self).__on_x((*self).x); + (*self).__on_x((*self).x); - if ( ::__feat%foo@@Pub3%uses_random_access || ::__feat%foo@@Pub3%uses_offset ) + if ( ::__feat%foo@@Pub3%uses_random_access || ::__feat%foo@@Pub3%uses_offset ) - if ( (*self).__position_update ) { - __cur = __cur.advance((*(*self).__position_update)); - (*self).__position_update = Null; - } + if ( (*self).__position_update ) { + __cur = __cur.advance((*(*self).__position_update)); + (*self).__position_update = Null; + } - __error = (*self).__error; + __error = (*self).__error; + } + catch ( hilti::SystemException __except ) { + (*self).__on_x_error(hilti::exception_what(__except)); + throw; + } + if ( ::__feat%foo@@Pub3%uses_random_access ) (*self).__begin = __begin; diff --git a/tests/Baseline/spicy.optimization.unused-types/opt.hlt b/tests/Baseline/spicy.optimization.unused-types/opt.hlt index 5a2db4ba3..7b44d11ac 100644 --- a/tests/Baseline/spicy.optimization.unused-types/opt.hlt +++ b/tests/Baseline/spicy.optimization.unused-types/opt.hlt @@ -306,12 +306,14 @@ method method tuple, int<64>, const iterator, optiona (__cur, __lah, __lahe, __error) = (*__transient__anon_2).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); # End parsing production: Unit: foo__Priv5_2 -> - # "<...>/unused-types.spicy:28:14-28:20" + { + # "<...>/unused-types.spicy:28:14-28:20" - # Begin parsing production: Unit: foo__Priv6_2 -> - (*self).x = default(); - (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); - # End parsing production: Unit: foo__Priv6_2 -> + # Begin parsing production: Unit: foo__Priv6_2 -> + (*self).x = default(); + (__cur, __lah, __lahe, __error) = (*(*self).x).__parse_stage1(__data, __begin, __cur, __trim, __lah, __lahe, __error); + # End parsing production: Unit: foo__Priv6_2 -> + } hilti::debugDedent("spicy"); __result = (__cur, __lah, __lahe, __error); diff --git a/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output b/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output index b51599e8b..8bf47bd96 100644 --- a/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output +++ b/tests/Baseline/spicy.types.unit.canonical-ids-with-import/output @@ -420,6 +420,8 @@ [debug/ast-declarations] - Field "x" (a::x_3) [debug/ast-declarations] - Field "__on_x" (a::__on_x) [debug/ast-declarations] - Parameter "__dd" (a::__dd_2) +[debug/ast-declarations] - Field "__on_x_error" (a::__on_x_error) +[debug/ast-declarations] - Parameter "__excpt" (a::__excpt) [debug/ast-declarations] - Field "__on_0x25_init" (a::__on_0x25_init) [debug/ast-declarations] - Field "__on_0x25_done" (a::__on_0x25_done) [debug/ast-declarations] - Field "__on_0x25_error" (a::__on_0x25_error) @@ -513,6 +515,7 @@ [debug/ast-declarations] - Parameter "__lahe" (a::__lahe_4) [debug/ast-declarations] - Parameter "__error" (a::__error_5) [debug/ast-declarations] - LocalVariable "__result" (a::__result_2) +[debug/ast-declarations] - Parameter "__except" (a::__except_3) [debug/ast-declarations] - Function "a::X::parse1" (a::a::X::parse1) [debug/ast-declarations] - Parameter "__data" (a::__data_8) [debug/ast-declarations] - Parameter "__cur" (a::__cur_8) diff --git a/tests/Baseline/spicy.types.unit.canonical-ids/output b/tests/Baseline/spicy.types.unit.canonical-ids/output index 40209046f..99a4d34c5 100644 --- a/tests/Baseline/spicy.types.unit.canonical-ids/output +++ b/tests/Baseline/spicy.types.unit.canonical-ids/output @@ -415,6 +415,8 @@ [debug/ast-declarations] - Field "ptr" (DNS::ptr_3) [debug/ast-declarations] - Field "__on_ptr" (DNS::__on_ptr) [debug/ast-declarations] - Parameter "__dd" (DNS::__dd_3) +[debug/ast-declarations] - Field "__on_ptr_error" (DNS::__on_ptr_error) +[debug/ast-declarations] - Parameter "__excpt" (DNS::__excpt) [debug/ast-declarations] - Field "__on_0x25_init" (DNS::__on_0x25_init) [debug/ast-declarations] - Field "__on_0x25_done" (DNS::__on_0x25_done) [debug/ast-declarations] - Field "__on_0x25_error" (DNS::__on_0x25_error) @@ -481,6 +483,8 @@ [debug/ast-declarations] - Field "name" (DNS::name_3) [debug/ast-declarations] - Field "__on_name" (DNS::__on_name) [debug/ast-declarations] - Parameter "__dd" (DNS::__dd_4) +[debug/ast-declarations] - Field "__on_name_error" (DNS::__on_name_error) +[debug/ast-declarations] - Parameter "__excpt" (DNS::__excpt_2) [debug/ast-declarations] - Field "__on_0x25_init" (DNS::__on_0x25_init_2) [debug/ast-declarations] - Field "__on_0x25_done" (DNS::__on_0x25_done_2) [debug/ast-declarations] - Field "__on_0x25_error" (DNS::__on_0x25_error_2) @@ -574,6 +578,7 @@ [debug/ast-declarations] - Parameter "__lahe" (DNS::__lahe_6) [debug/ast-declarations] - Parameter "__error" (DNS::__error_8) [debug/ast-declarations] - LocalVariable "__result" (DNS::__result_2) +[debug/ast-declarations] - Parameter "__except" (DNS::__except_4) [debug/ast-declarations] - Function "DNS::Label::parse1" (DNS::DNS::Label::parse1) [debug/ast-declarations] - Parameter "__data" (DNS::__data_13) [debug/ast-declarations] - Parameter "__cur" (DNS::__cur_13) @@ -647,7 +652,7 @@ [debug/ast-declarations] - LocalVariable "__offset1" (DNS::__offset1_2) [debug/ast-declarations] - LocalVariable "__filtered_data" (DNS::__filtered_data_2) [debug/ast-declarations] - LocalVariable "__offset2" (DNS::__offset2_2) -[debug/ast-declarations] - Parameter "__except" (DNS::__except_4) +[debug/ast-declarations] - Parameter "__except" (DNS::__except_5) [debug/ast-declarations] - Function "DNS::Pointer::__parse_DNS__Pointer_2_stage2" (DNS::DNS::Pointer::__parse_DNS__Pointer_2_stage2) [debug/ast-declarations] - Parameter "__data" (DNS::__data_17) [debug/ast-declarations] - Parameter "__begin" (DNS::__begin_12) @@ -657,6 +662,7 @@ [debug/ast-declarations] - Parameter "__lahe" (DNS::__lahe_8) [debug/ast-declarations] - Parameter "__error" (DNS::__error_13) [debug/ast-declarations] - LocalVariable "__result" (DNS::__result_4) +[debug/ast-declarations] - Parameter "__except" (DNS::__except_6) [debug/ast-declarations] - Function "DNS::Pointer::parse1" (DNS::DNS::Pointer::parse1) [debug/ast-declarations] - Parameter "__data" (DNS::__data_18) [debug/ast-declarations] - Parameter "__cur" (DNS::__cur_18) diff --git a/tests/Baseline/spicy.types.unit.error-hook-field-fail/output b/tests/Baseline/spicy.types.unit.error-hook-field-fail/output new file mode 100644 index 000000000..6b20bfcc8 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.error-hook-field-fail/output @@ -0,0 +1,4 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +[error] <...>/error-hook-field-fail.spicy:11:12-12:1: %error hook must only take a string parameter +[error] <...>/error-hook-field-fail.spicy:14:12-15:1: %error hook must only take a string parameter +[error] spicyc: aborting after errors diff --git a/tests/Baseline/spicy.types.unit.error-hook-field/output b/tests/Baseline/spicy.types.unit.error-hook-field/output new file mode 100644 index 000000000..ee0a2e583 --- /dev/null +++ b/tests/Baseline/spicy.types.unit.error-hook-field/output @@ -0,0 +1,29 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +--- +field A standard +unit %done, [$a=b"A", $b=b"B", $c=b"C"] +--- +[error] terminating with uncaught exception of type spicy::rt::ParseError: expected bytes literal "B" but input starts with "x" (<...>/error-hook-field.spicy:21:8-21:11) +field A standard +field B %error, attached, [$a=b"A", $b=(not set), $c=(not set)] +field B %error, external, [$a=b"A", $b=(not set), $c=(not set)] +field B %error, external, expected bytes literal "B" but input starts with "x" (<...>/error-hook-field.spicy:21:8-21:11), [$a=b"A", $b=(not set), $c=(not set)] +field B %error, inside unit, [$a=b"A", $b=(not set), $c=(not set)] +field B %error, inside unit, expected bytes literal "B" but input starts with "x" (<...>/error-hook-field.spicy:21:8-21:11), [$a=b"A", $b=(not set), $c=(not set)] +unit %error, [$a=b"A", $b=(not set), $c=(not set)] +unit %error, external, expected bytes literal "B" but input starts with "x" (<...>/error-hook-field.spicy:21:8-21:11), [$a=b"A", $b=(not set), $c=(not set)] +--- +[error] terminating with uncaught exception of type spicy::rt::ParseError: expected bytes literal "C" but input starts with "x" (<...>/error-hook-field.spicy:22:8-22:11) +field A standard +unit %error, [$a=b"A", $b=b"B", $c=(not set)] +unit %error, external, [$a=b"A", $b=b"B", $c=(not set)] +unit %error, external, expected bytes literal "C" but input starts with "x" (<...>/error-hook-field.spicy:22:8-22:11), [$a=b"A", $b=b"B", $c=(not set)] +=== +--- +unit %done, [$foo=[$a=b"A", $b=b"B"], $c=b"C"] +--- +field A %error +unit %done, [$foo=[$a=(not set), $b=(not set)], $c=b"C"] +--- +[error] terminating with uncaught exception of type spicy::rt::ParseError: expected bytes literal "B" but input starts with "x" (<...>/error-hook-field.spicy:66:8-66:11) +unit %error, [$foo=[$a=b"A", $b=(not set)], $c=(not set)] diff --git a/tests/Baseline/spicy.types.unit.hooks-across-imports/.stderr b/tests/Baseline/spicy.types.unit.hooks-across-imports/.stderr index 18f4f68c7..9e156c95a 100644 --- a/tests/Baseline/spicy.types.unit.hooks-across-imports/.stderr +++ b/tests/Baseline/spicy.types.unit.hooks-across-imports/.stderr @@ -32,39 +32,39 @@ [debug/ast-stats] - type::bytes::Iterator: 179 [debug/ast-stats] - type::operand_list::Operand: 1257 [debug/ast-stats] - type::stream::Iterator: 141 -[debug/ast-stats] garbage collected 8966 nodes in 14 rounds, 21095 left retained -[debug/ast-stats] garbage collected 2310 nodes in 16 rounds, 24911 left retained -[debug/ast-stats] garbage collected 2346 nodes in 8 rounds, 26329 left retained -[debug/ast-stats] garbage collected 4594 nodes in 11 rounds, 27547 left retained -[debug/ast-stats] garbage collected 412 nodes in 3 rounds, 27547 left retained +[debug/ast-stats] garbage collected 9044 nodes in 14 rounds, 21359 left retained +[debug/ast-stats] garbage collected 2418 nodes in 16 rounds, 25283 left retained +[debug/ast-stats] garbage collected 2346 nodes in 8 rounds, 26701 left retained +[debug/ast-stats] garbage collected 4762 nodes in 11 rounds, 27919 left retained +[debug/ast-stats] garbage collected 412 nodes in 3 rounds, 27919 left retained [debug/ast-stats] # [HILTI] AST statistics: [debug/ast-stats] - # AST rounds 5 [debug/ast-stats] - max tree depth: 27 -[debug/ast-stats] - # context declarations: 254 +[debug/ast-stats] - # context declarations: 260 [debug/ast-stats] - # context types: 55 [debug/ast-stats] - # context modules: 8 -[debug/ast-stats] - # nodes reachable in AST: 18587 -[debug/ast-stats] - # nodes live: 27547 -[debug/ast-stats] - # nodes retained: 27547 +[debug/ast-stats] - # nodes reachable in AST: 18875 +[debug/ast-stats] - # nodes live: 27919 +[debug/ast-stats] - # nodes retained: 27919 [debug/ast-stats] - # nodes live > 1%: [debug/ast-stats] - Attribute: 301 -[debug/ast-stats] - AttributeSet: 765 -[debug/ast-stats] - QualifiedType: 9116 -[debug/ast-stats] - declaration::Parameter: 399 -[debug/ast-stats] - expression::Ctor: 770 -[debug/ast-stats] - expression::Member: 314 -[debug/ast-stats] - expression::Name: 992 +[debug/ast-stats] - AttributeSet: 783 +[debug/ast-stats] - QualifiedType: 9218 +[debug/ast-stats] - declaration::Parameter: 411 +[debug/ast-stats] - expression::Ctor: 782 +[debug/ast-stats] - expression::Member: 320 +[debug/ast-stats] - expression::Name: 1010 [debug/ast-stats] - type::Bool: 338 -[debug/ast-stats] - type::Member: 542 -[debug/ast-stats] - type::Name: 522 -[debug/ast-stats] - type::OperandList: 798 +[debug/ast-stats] - type::Member: 554 +[debug/ast-stats] - type::Name: 534 +[debug/ast-stats] - type::OperandList: 810 [debug/ast-stats] - type::Optional: 465 -[debug/ast-stats] - type::String: 441 -[debug/ast-stats] - type::Tuple: 324 +[debug/ast-stats] - type::String: 465 +[debug/ast-stats] - type::Tuple: 336 [debug/ast-stats] - type::UnsignedInteger: 1695 -[debug/ast-stats] - type::operand_list::Operand: 1557 +[debug/ast-stats] - type::operand_list::Operand: 1581 [debug/ast-stats] - type::stream::Iterator: 1016 [debug/ast-stats] - type::stream::View: 550 -[debug/ast-stats] - type::tuple::Element: 673 -[debug/ast-stats] garbage collected 32191 nodes in 18 rounds, 0 left retained +[debug/ast-stats] - type::tuple::Element: 685 +[debug/ast-stats] garbage collected 32587 nodes in 18 rounds, 0 left retained [debug/ast-stats] garbage collected 0 nodes in 1 round, 0 left retained diff --git a/tests/Baseline/spicy.types.unit.skip-field/skips.txt b/tests/Baseline/spicy.types.unit.skip-field/skips.txt index 8b8c562ec..01eee0a48 100644 --- a/tests/Baseline/spicy.types.unit.skip-field/skips.txt +++ b/tests/Baseline/spicy.types.unit.skip-field/skips.txt @@ -1,9 +1,9 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. - # Begin parsing production: Skip: a_2 -> skip: a + # Begin parsing production: Skip: a_2 -> skip: a # Begin parsing production: Skip: _anon_6 -> skip: _anon - # Begin parsing production: Skip: c_2 -> skip: c + # Begin parsing production: Skip: c_2 -> skip: c # Begin parsing production: Skip: _anon_2_2 -> skip: _anon_2 # Begin parsing production: Skip: _anon_4_2 -> skip: _anon_4 # Begin parsing production: Skip: _anon_5_2 -> skip: _anon_5 # Begin parsing production: Skip: _anon_7_2 -> skip: _anon_7 - # Begin parsing production: Skip: eod_2 -> skip: eod + # Begin parsing production: Skip: eod_2 -> skip: eod diff --git a/tests/Baseline/spicy.types.unit.sub-unit/.stderr b/tests/Baseline/spicy.types.unit.sub-unit/.stderr index 064693d47..9133b606e 100644 --- a/tests/Baseline/spicy.types.unit.sub-unit/.stderr +++ b/tests/Baseline/spicy.types.unit.sub-unit/.stderr @@ -31,40 +31,40 @@ [debug/ast-stats] - type::bytes::Iterator: 148 [debug/ast-stats] - type::operand_list::Operand: 1257 [debug/ast-stats] - type::stream::Iterator: 141 -[debug/ast-stats] garbage collected 5080 nodes in 12 rounds, 17788 left retained -[debug/ast-stats] garbage collected 2884 nodes in 16 rounds, 20614 left retained -[debug/ast-stats] garbage collected 1582 nodes in 8 rounds, 21511 left retained -[debug/ast-stats] garbage collected 3397 nodes in 11 rounds, 22304 left retained -[debug/ast-stats] garbage collected 286 nodes in 3 rounds, 22304 left retained +[debug/ast-stats] garbage collected 5119 nodes in 12 rounds, 17920 left retained +[debug/ast-stats] garbage collected 2938 nodes in 16 rounds, 20800 left retained +[debug/ast-stats] garbage collected 1582 nodes in 8 rounds, 21697 left retained +[debug/ast-stats] garbage collected 3481 nodes in 11 rounds, 22490 left retained +[debug/ast-stats] garbage collected 286 nodes in 3 rounds, 22490 left retained [debug/ast-stats] # [HILTI] AST statistics: [debug/ast-stats] - # AST rounds 5 [debug/ast-stats] - max tree depth: 27 -[debug/ast-stats] - # context declarations: 189 +[debug/ast-stats] - # context declarations: 192 [debug/ast-stats] - # context types: 49 [debug/ast-stats] - # context modules: 6 -[debug/ast-stats] - # nodes reachable in AST: 14254 -[debug/ast-stats] - # nodes live: 22304 -[debug/ast-stats] - # nodes retained: 22304 +[debug/ast-stats] - # nodes reachable in AST: 14398 +[debug/ast-stats] - # nodes live: 22490 +[debug/ast-stats] - # nodes retained: 22490 [debug/ast-stats] - # nodes live > 1%: [debug/ast-stats] - Attribute: 251 -[debug/ast-stats] - AttributeSet: 593 -[debug/ast-stats] - QualifiedType: 7347 +[debug/ast-stats] - AttributeSet: 602 +[debug/ast-stats] - QualifiedType: 7398 [debug/ast-stats] - ctor::String: 290 -[debug/ast-stats] - declaration::Parameter: 307 -[debug/ast-stats] - expression::Ctor: 695 -[debug/ast-stats] - expression::Name: 736 +[debug/ast-stats] - declaration::Parameter: 313 +[debug/ast-stats] - expression::Ctor: 701 +[debug/ast-stats] - expression::Name: 745 [debug/ast-stats] - type::Bool: 279 -[debug/ast-stats] - type::Member: 394 -[debug/ast-stats] - type::Name: 382 -[debug/ast-stats] - type::OperandList: 752 +[debug/ast-stats] - type::Member: 400 +[debug/ast-stats] - type::Name: 388 +[debug/ast-stats] - type::OperandList: 758 [debug/ast-stats] - type::Optional: 339 -[debug/ast-stats] - type::String: 536 -[debug/ast-stats] - type::Tuple: 269 +[debug/ast-stats] - type::String: 548 +[debug/ast-stats] - type::Tuple: 275 [debug/ast-stats] - type::UnsignedInteger: 1290 -[debug/ast-stats] - type::Void: 253 -[debug/ast-stats] - type::operand_list::Operand: 1451 +[debug/ast-stats] - type::Void: 262 +[debug/ast-stats] - type::operand_list::Operand: 1463 [debug/ast-stats] - type::stream::Iterator: 763 [debug/ast-stats] - type::stream::View: 417 -[debug/ast-stats] - type::tuple::Element: 600 -[debug/ast-stats] garbage collected 23630 nodes in 18 rounds, 0 left retained +[debug/ast-stats] - type::tuple::Element: 606 +[debug/ast-stats] garbage collected 23828 nodes in 18 rounds, 0 left retained [debug/ast-stats] garbage collected 0 nodes in 1 round, 0 left retained diff --git a/tests/spicy/types/unit/error-hook-field-fail.spicy b/tests/spicy/types/unit/error-hook-field-fail.spicy new file mode 100644 index 000000000..c38e00ae7 --- /dev/null +++ b/tests/spicy/types/unit/error-hook-field-fail.spicy @@ -0,0 +1,15 @@ +# @TEST-EXEC-FAIL: spicyc -j -d -o test.hlto %INPUT 2>output +# +# @TEST-EXEC: btest-diff output + +module Mini; + +public type Test1 = unit { + x: b"X"; +}; + +on Test1::x(msg: bool) %error { # wrong parameter type +} + +on Test1::x(msg: string, x: bool) %error { # wrong parameter number +} diff --git a/tests/spicy/types/unit/error-hook-field.spicy b/tests/spicy/types/unit/error-hook-field.spicy new file mode 100644 index 000000000..0f9dbfab1 --- /dev/null +++ b/tests/spicy/types/unit/error-hook-field.spicy @@ -0,0 +1,67 @@ +# @TEST-EXEC: spicyc -j -d -o test.hlto %INPUT +# +# @TEST-EXEC: echo ABC | spicy-driver -p Mini::Test1 test.hlto 2>&1 | sort >>output +# @TEST-EXEC: echo AxC | (spicy-driver -p Mini::Test1 test.hlto 2>&1; true) | sort >>output +# @TEST-EXEC: echo ABx | (spicy-driver -p Mini::Test1 test.hlto 2>&1; true) | sort >>output +# +# @TEST-EXEC: echo === >>output +# +# @TEST-EXEC: echo ABC | spicy-driver -p Mini::Test2 test.hlto 2>&1 | sort >>output +# @TEST-EXEC: echo C | spicy-driver -p Mini::Test2 test.hlto 2>&1 | sort >>output +# @TEST-EXEC: echo AxC | (spicy-driver -p Mini::Test2 test.hlto 2>&1; true) | sort >>output +# +# @TEST-EXEC: btest-diff output + +module Mini; + +public type Test1 = unit { + on %init { print "---"; } + + a: b"A" { print "field A standard"; } + b: b"B" %error { print "field B %error, attached", self; } + c: b"C"; + + # TODO: These don't trigger yet + on b %error { + print "field B %error, inside unit", self; + } + + on b(msg: string) %error { + print "field B %error, inside unit", msg, self; + } + + on %error { print "unit %error", self; } + on %done { print "unit %done", self; } +}; + +on Test1::b %error { + print "field B %error, external", self; +} + +on Test1::b(msg: string) %error { + print "field B %error, external", msg, self; +} + +on Test1::c %error { + print "unit %error, external", self; +} + +on Test1::%error(msg: string) { + print "unit %error, external", msg, self; +} + +# Test that backtracks only inside one field's error handler. +public type Test2 = unit { + on %init { print "---"; } + + foo: Foo &try; + c: b"C"; + + on %error { print "unit %error", self; } + on %done { print "unit %done", self; } +}; + +type Foo = unit { + a: b"A" %error { print "field A %error"; self.backtrack(); } + b: b"B"; +};