Repository navigation
Templates, Absolute Qualifiers, Scala Parity - #112
Merged
Merged
Conversation
Port the Scala absolute-name delta to the AST and the parser:
- fpp_ast: ExprKind::Ident { value, is_absolute } (Scala ExprIdent) and
QualIdent::Unqualified(Unqualified { name, is_absolute }) (Scala
QualIdent.Unqualified). QualIdent::is_absolute (Qualified delegates to its
qualifier), QualIdent::to_ident_list, QualIdentNodeList with
qual_ident_node_list::{split, qualifier, name}, QualIdent::from_node_list,
Expr::get_ident_list_opt, Expr::get_is_absolute_opt and
TypeName::get_is_absolute_opt. Display and Serialize print the leading dot
of an absolute name. Every level of a qualified identifier gets its own
span (A.B in A.B.C covers A.B only). Remove the unused QualIdentKind and
the Rust-only write_parts.
- fpp_parser: optional leading dot in identExpr, qualIdent,
portInstanceIdentifier and tlmChannelIdentifier (interface_instance_member
is inlined into the last two, on a new qual_ident_node_list), and dispatch
on Dot in type_name, tlm_packet_member and expr_primary.
- DefComponentInstance.base_id is a required Expr, as in Scala and the spec;
the parser requires `base id` and the analysis no longer defaults it to 0.
- QualifiedName::from(&QualIdent) and the constant-use names in
BasicUseAnalyzer/UseAnalyzer come from to_ident_list/get_ident_list_opt,
which fixes the reordered qualifiers of 4+ part qualified identifiers and
3+ part dotted expressions.
- fpp_query: the syntactic template look-up honours is_absolute.
- Absolute names resolve like relative ones in fpp_analysis for now.
Tests: copy Scala's fpp-syntax syntax.fpp, state-machine.fpp and
template.fpp (the Rust-only template cases move to template-forms.fpp),
port the ParserSpec groups with absolute names plus def component instance,
add spec_loc four_part_name_{ok,path_error}, dependency tests for a 3-part
constant use and a 4-part type use, and an fpp_query absolute template
look-up test. The SM refs choice_cycle, cycle_and_types and choice_* change
order with the node ids (hash-ordered sets). Regenerate the Python mirror.
The renderer prints the Scala headline and the note "symbol is defined here" at the definition; every construction site passes the bare reason (the sites that built the prefix themselves no longer do), and the sites that pointed the note at the definition's name point it at the definition, as Scala's symbol.getLoc does.
Scala Map/Set-backed collections whose iteration order is observable use insertion order (IndexMap/IndexSet): exact parity with Scala's immutable Map/Set up to four entries, deterministic beyond.
Span::tu_location, get_dir_path (the current directory for stdin), get_relative_path (joined and normalized lexically, without reading the file system), compare and compare_opt mirror Scala's Location. The shared path normalization now matches java.nio.file.Path.normalize at the root and for a leading .. of a relative path.
A redefinition error now points at the redefining definition and the previous one through each symbol's location, the start of its definition, in the generic name-symbol map and in the module path of EnterSymbols alike. This drops the Rust-only SymbolInterface::redefined_loc.
ComponentInstance::from_def resolves its component through Analysis::get_component, and the interface imports in CheckInterfaceDefs and CheckComponentDefs go through Analysis::get_interface_symbol and Analysis::get_interface, so a module named where a component or an interface is required is reported as 'invalid use of symbol M: not a component symbol' / 'not a interface symbol' instead of being dropped. Analysis::get_interface and Analysis::is_general_port move into analysis.rs next to the Scala helpers they mirror; get_interface now returns SemanticResult<Option<Interface>>. check_for_duplicate_parameter reports at the formal parameter (which starts at a 'ref' keyword), and CheckPortDefs calls it instead of an inline copy.
A failed interface import is reported at the import specifier as 'failed to import interface', with the error that caused it attached as notes, as Scala's InterfaceImport prints it. InterfaceImplements attaches its cause the same way, through the new Diagnostic::span_note_diagnostic. PassiveAsync lists the import specifiers outermost first, the async input port note points at the port definition and says 'port definition is here', and the init-specifier, component-instance, state-machine instance, duplicate-parameter and overlapping-id-range messages use Scala's wording.
ExpandTemplates walks the body of a template where the template is expanded as well as where it is defined, threading the template definition it is inside of, so a model with an expansion before an invalid template reports the error the expansion meets first. The list driver is now transform_list over trans_unit, and the separate walk over a template body is gone.
Type Display prints bool, 'array A', 'enum E', 'struct S', an unsized
anonymous array as 'array of T' and an anonymous struct as '{ a: T }' in
stored member order. Value Display prints the kind of a primitive value
('1: U8'), floats as Java's Double.toString, enum constants as '(X,0): E',
and the elements and members of aggregates. Anonymous struct value members
are kept in insertion order, so they print in the order Scala prints them.
A diagnostic now follows the include and expansion locations of each location in the chain, as Location.toString does, so an error inside an expansion whose specifier sits in an included file also reports where that file is included. The flat include_spans and expand_spans stay for the Python bindings.
…ators and stuck lists A bare deployment or external keyword reached a bump that asserted on the next keyword; dispatch now looks ahead. TokenSet's last token kind is SHIFT_RIGHT, so recovery at << or >> no longer trips the debug assertion. A member list iteration that consumes nothing now bumps the token or leaves the list at a foreign closing delimiter instead of looping until the step limit. Add a robustness test that places every token in every member position and checks the parse is panic-free and lossless.
CheckSpecLocs and BuildSpecLocMap resolve a location specifier path with Span::get_relative_path, as Scala's Location.getRelativePath does, so a path with . or .. components names the same file as its normalized form. A specifier whose symbol is absolute (leading dot) is keyed without the enclosing module prefix. CheckSpecLocs is split into check_path, keyed by the enclosing scope names, and check_dictionary_specifier, keyed by the qualified name, as in Scala. Ports spec_loc/shadowed_ok and shadowed_error, and adds Rust-only tests for normalized paths.
Analysis::get_interface_instance and Analysis::get_interface now live in analysis.rs next to their symbol helpers and return SemanticResult, built on get_interface_instance_symbol / get_interface_symbol as in Analysis.scala: a module named as a connection endpoint, topology port, implemented interface, interface template argument or interface parameter type is rejected, and a topology used before it is resolved reports 'this topology could not be resolved before it was used here'. CheckTopologyInstances resolves instance and import specifiers through get_interface_instance_symbol, so 'instance M' / 'import M' naming a module is rejected. Port the Scala delta 4a8aa9e85: get_represented_component_instance reports at the use under the use's name with the note at the represented definition, and TlmChannelIdentifier resolves a bound instance template parameter. Port tlm_packets/template_arg_channel and template_arg_topology.
PortInstanceType::show takes the analysis and prints a defined port as the qualified name of its definition, as Scala's Type.show(a) does, so a connection between two port types with the same unqualified name reads 'cannot connect port types M.P and N.P'. The Display impl keeps Scala's toString (the unqualified name, or 'serial'). Connection::check_types and check_serial_with_typed_input take the analysis to pass it through.
CheckDictionaryDefs visits module members, component members and the members of template expansions, as Scala's ModuleAnalyzer and ComponentAnalyzer do, so it no longer reaches definitions inside state machines. A dictionary constant whose type is an alias type is rejected, as in Scala, where an alias type is neither an integer nor a floating-point type.
ConstructDictionaryMap is a visitor, as in Scala: it visits module members and the members of template expansions in model order, constructs the dictionary of each deployment topology it reaches, and builds each telemetry packet set from its packet specifiers. A topology copied by an expansion is visited at the expansion and a topology in an included file at the include, so the first error is the one Scala reports.
EnterSymbols and EnterTemplateExpansionSymbols (renamed from EnterTemplateSymbols) now share the module entering of Scala's EnterModuleSymbols: a module is looked up by name in the current scope, re-opened if it is a module, and created and entered into every name group otherwise, with update_map recording its parent. After merging an expansion, EnterTemplateExpansionSymbols walks the expansion members again with that base, as Scala does, and an already entered expansion is walked in its own scope.
Per the collections policy, PortInterface's port maps, the component dictionaries and state machine instance map, an interface's import map and the component instance map are insertion-ordered IndexMaps, matching the iteration order of Scala's immutable Maps for up to four entries. The port_order bookkeeping that emulated this for two walks goes away. So check_dictionary_names reports a duplicate name at the later definition, the passive-component, data-product and id-range checks report the first offender in definition order, and instances with equal base ids are compared in definition order.
LexedStr kept none of fpp_lexer's errors, so unclosed strings, malformed numbers and bad line continuations parsed cleanly and fpp-format rewrote them. Each lexer error is now attached to the token that reported it and reported first, as 'invalid token: MSG'. A character that cannot begin a token is reported with its unicode value (and the tab note), and the parser skips it like whitespace, as the Scala lexer does. Port the Scala lexer test inputs.
CheckExprTypes checks numeric operands with convert_node_to_numeric / convert_to_numeric, reporting 'cannot convert T to a numeric type' as Scala does. A unary minus has the numeric type its operand converts to (Integer for an enum or alias operand), and an arithmetic expression has the type its common type converts to (spec, Arithmetic and Concatenation). A shift operand must itself be an integer or enum type, so an alias operand is rejected. An expression that fails to type check gets no type, and EvalConstantExprs does not evaluate it. Type::is_int, is_float, is_primitive, is_numeric, is_convertible_to_numeric, is_promotable_to_array and array_size no longer look through alias types, as Scala's AliasType inherits the base answers; has_numeric_members keeps the look-through because the spec defines it on the underlying type.
CheckTemplateUses visits only translation unit and module members, as in Scala: it looks up each module by name in the current nested scope, pushes the module scope, and resolves the template name of each expansion specifier. It no longer walks every definition through BasicUseAnalyzer.
Component dictionary ids and opcodes stay 128-bit integers. Where the next default id or opcode, or the end of an instance's id range, would not fit, the analysis reports an error at the element or base id instead of overflowing. Parameter default opcodes follow the previous opcode as the spec says; a test pins that there is no truncation to 32 bits.
Analysis::common_type, common_type_of_pair and common_type_of_types mirror Scala's three commonType helpers: a list of elements reports at the element before the one that fails, with the two types in Scala's order, and every failure is a TypeMismatch 'cannot compute common type of T1 and T2'. Analysis::convert_types reports 'cannot convert T1 to T2'; CheckExprTypes uses it for array, enum, struct and parameter defaults, telemetry limits (check_limit_expr), event throttle intervals (check_throttle) and template constant arguments. FinalizeConstantExprs uses the same helpers and reports its conversion failures as TypeMismatch.
CheckTopologyDefs is a tree visitor, as in Scala: it resolves each topology where the tree reaches it (translation units in input order, members in order, an included topology at its include site, an expanded topology at its expansion site), resolving the topologies it imports first through their symbols, and it handles direct and pattern connection graphs and the telemetry packet set deployment check as it visits the members, in source order. CheckTopologyInstances visits the topologies a topology imports right after the topology itself. Per the collections policy the topology direct-instance maps, the transitive import set, the port map, the pattern map, the connection graph maps, the matched-numbering instance connection map and the template expansion map keep insertion order; connection lists keep the most recent connection first, as Scala's do. Pattern targets keep every written target. CheckTemplateInterfaceArgs checks the expansions in entry order and the arguments in parameter order.
TemplateExpansion::params is now a map from TemplateExpansion::ParamKey, a name group paired with a parameter name, to the bound argument, as in Scala. It is an insertion-ordered map, so the arguments are still analyzed in the order the parameters are declared.
TlmPacketSet::packet_map and the dictionary entry maps are insertion-ordered, as Scala's immutable maps are for small dictionaries. Duplicate packet names are reported in model order, the channels of a packet set are checked in the order of the instance names and then of the component's channel map, and a channel used in several packets is reported as used in the last one in model order.
A special port whose implied use resolves to another kind of symbol (a module Fw.Time, say) is reported as 'invalid use of symbol Time: not a port symbol' with a note at the definition, like the general-port case. A matched port that names no port of the component is reported through PortInterface::lookup_port_instance as InvalidPortInstanceId, the error Scala's getPortInstance produces. The port_instance, port_matching, record and spec_init test lists follow tests.sh, with the Rust-only tests after them.
fpp_lexer: - Treat CR LF as a newline everywhere (a lone CR stays invalid). - Lex signed exponents (1e-10, 1.5E+3) as in fetchFraction; an identifier character after a float literal is an 'invalid literal number' error that ends the literal instead of being swallowed into it. - A digit must be below the base (15a, 0x1g end the literal), and 0x with no hex digit is an 'invalid literal number' error. - Report every invalid character as a lexer error with the code point, the embedded-tab notes, the '$' hint and the '<' / '>' messages, one error per character. - Report an unclosed string at its opening quote; form feed and an escaped newline in a single-line string stay rejected, as the spec requires. - Keep the keywords in one sorted table so that keywords display with their FPP spelling (U32, F32, string), and display EOF as 'end of input'. - Port util/Chars.scala as chars.rs. fpp_parser cursor: - Lex the whole file up front and emit its lexer errors before any syntax error of the file. - Interpret string literals: \c becomes c, a multi-line string drops the newline after the opening quotes and at most the first line's indent of spaces on each line, and CR LF becomes LF. - '->' absorbs the newlines after it; '<<' and '>>' do not; a line continuation ends a run of newlines. - Normalize 0X to 0x, trim annotations like Java's String.trim, and remove the panics on a pre-annotation at end of input and on non-ASCII text in unclosed or indented strings. - Locate 'expected' errors at the offending token (an end of line where it starts, end of input at the end of the file), keep the expected list at end of input, and give the initial span the include chain. - LitString.inner_span covers the raw text between the quotes. Tests: port the Scala LexerSpec (cursor/lexer_spec.rs with the Scala inputs in fpp_parser/tests/lexer), the full illegal-character.fpp, numeric-shift and a CRLF fixture, and lexer and cursor unit tests for each fix.
GeneralPortInstance construction gets its size the way Scala's private PortInstance.getArraySize does: Analysis::get_array_size for a given size, 1 when there is none.
Conflict in fpp_diagram/src/lower_sm.rs: both sides rewrote the SpanKey doc that named cmp_span; kept the parity/f-tools wording, which also says what Span::compare compares beyond the key.
…where their values are read CheckExprTypes no longer converts the base id, queue size, stack size, priority and CPU affinity of a component instance or the port numbers of a connection to a numeric type. ComponentInstance::from_def_component_instance and Endpoint::from_ast check the type right before reading each value, in fpp-check's order, so a component definition error or a passive-component attribute error comes first as it does there.
A type use is recorded in the use-def map under its type name, qualified identifier and name, all with the same span. handle_references now sorts the locations and drops repeats.
…omponent A component instance exists only once its component is in the component map, so the dictionary entry and used-symbol walks need no skip for a missing component.
It creates its directory link with std::os::unix::fs::symlink.
…ror in Error.scala
…field names
The variants and the arms of From<SemanticError> for Diagnostic follow the
declaration order of SemanticError in Error.scala, with the state machine
errors grouped. The shared 'previous occurrence' and 'port matching' notes go
through prev_loc_note and matching_loc_note, as printPrevLoc and
printMatchingLoc do. Fields take the Scala names: UndefinedSymbol.symbol_kind,
InvalidSymbol.name, InvalidPortInstanceId.symbol_kind, InvalidPortNumber.size,
DuplicateSignal.sig_name, ChoiceTypeMismatch.{to_loc1,to1,to_loc2,to2},
CallSiteTypeMismatch.{te_ty,site_ty} and InterfaceImplements.err.
InvalidQualifier carries the qualifier name and the bare message, and renders
the headline and the definition note itself. No output changes.
The methods of Analysis follow the order of the Analysis case class, then the functions of its companion object follow in their order, with ComponentPrefix after them. The Rust-only helpers for scopes, template expansions and the passes come last. Apart from a doc comment on invalid_symbol, a pure move.
Each fpp_analysis/tests/<dir>/test.rs runs the tests ported from fpp-check in tests.sh order, then the tests with no Scala counterpart after one marker comment, each with a line saying what it covers. The mod blocks of integration.rs follow the fpp-check/test directory listing, and template/test.rs carries run.sh's note on the unconnected port report. The unused disabled-tests feature is gone.
The directories are renamed from cycles, enums, structs and types, the mod blocks use raw identifiers where the name is a keyword, and the tests named after keywords are r#enum, r#struct and r#type. Only the paths in the refs change.
The header comments of the overflow, opcode and implements fixtures say what each test checks; the line counts are unchanged, so the refs are too.
…n from module_analyzer Every use analysis runs on Analysis, so UseAnalysisPass and visit_template_arg fix the state to it. The passes import visit_expansion from module_analyzer, where it is defined, and analyzer no longer re-exports it.
…vate doc link The test analyzes its model with the pipeline the other helpers use. ImpliedUse::annotate_emitted_errors no longer links to the private errors::annotate_emitted_errors, so cargo doc -p fpp_analysis is warning-free.
…mlinked-root LSP tests on Unix only Endpoint::from_ast reports a port number that does not have a numeric type as an error; it returns None only for an instance that is unresolved or failed to check. The two workspace tests that open a project through a symbolic link create it with std::os::unix::fs::symlink.
Brings in #101-#111 on top of the Scala parity work. Conflict resolutions: - fpp_analysis: the Scala-ordered errors.rs/analysis.rs and the split semantics files are kept. #106 is re-applied inside them: InvalidTlmPacketSet is back to Scala's (loc, name, msg) shape and the new TlmPacketSetChannelsNotCovered / TlmPacketSetChannelsUsedAndOmitted variants sit next to it. check_channel_usage keeps Scala's per-channel scan in dictionary order to decide which error comes first, then lists every channel failing that check. The packet and packet set errors stay at the specifier, where Scala reports them (#106 moved them to the name). get_uncovered_channels(_for_node) serve the LSP quick fix. #105 makes partial_topology_map crate-private. #103 and #107 were already ported from the same Scala tests; one copy of each is kept. - fpp_lsp_parser: #102's expect() in place of bump() on top of the lookahead dispatch, so the parser neither asserts nor changes acceptance. - fpp_lsp_server: the code action and its tests next to ours. - fpp_parser/src/error.rs: ours (the include cycle diagnostic #111 touched is now IncludeError::Cycle). - fpp_python: master's nested-union generator, macros and runtime, with IndexSet support and our comment fixes on top; all generated files are regenerated with `make -C fpp_python codegen` (the stubs now live in python/fpp/{__init__,ast}.pyi). Tests follow the nested names and our API shape (19 symbol variants, TemplateArgKind and ScopeOwner unions, Display-based type reprs). - Cargo.lock: master's, updated by cargo for our dependencies.
SemanticError::UndefinedSymbol renders as Scala's "symbol NAME is not defined" with the note "looking for a KIND here". The state machine uses resolve through the same error, so they change with it. The refs, the fpp_diagram check for a cascade of undefined names, and the Python diagnostics tests and doc example follow.
The .gitignore note names the checked-in stubs under python/fpp/, and the visitor module compares its traversal with Python's ast.NodeVisitor.
The packet and packet set diagnostics point at the specifier's name, as on master, for a narrower span in the editor. The line is the one Scala reports.
# Conflicts: # Cargo.lock # fpp_analysis/src/analysis.rs # fpp_analysis/src/errors.rs # fpp_analysis/src/passes/check_expr_types.rs # fpp_analysis/src/passes/check_topology_defs.rs # fpp_analysis/src/passes/finalize_constant_exprs.rs # fpp_analysis/src/semantics/tlm_packet_set.rs # fpp_analysis/tests/connection_pattern/undef_source_and_target.ref.txt # fpp_analysis/tests/constant/absolute_undef.ref.txt # fpp_analysis/tests/dictionary_implied_uses/member_of_constant.ref.txt # fpp_analysis/tests/dictionary_implied_uses/missing_user_data_size.ref.txt # fpp_analysis/tests/enum/rep_type_string_size_scope.ref.txt # fpp_analysis/tests/enum/undef_constant_1.ref.txt # fpp_analysis/tests/enum/undef_constant_2.ref.txt # fpp_analysis/tests/expr/sizeof_string_fw_store_type_not_defined.ref.txt # fpp_analysis/tests/port_instance/implied_port_undef_member.ref.txt # fpp_analysis/tests/port_instance/shadowed_time_get.ref.txt # fpp_analysis/tests/port_instance/undef_command_recv.ref.txt # fpp_analysis/tests/port_instance/undef_command_reg.ref.txt # fpp_analysis/tests/port_instance/undef_command_resp.ref.txt # fpp_analysis/tests/port_instance/undef_event.ref.txt # fpp_analysis/tests/port_instance/undef_param_get.ref.txt # fpp_analysis/tests/port_instance/undef_param_set.ref.txt # fpp_analysis/tests/port_instance/undef_priority_and_implied_port.ref.txt # fpp_analysis/tests/port_instance/undef_product_recv.ref.txt # fpp_analysis/tests/port_instance/undef_product_request.ref.txt # fpp_analysis/tests/port_instance/undef_product_send.ref.txt # fpp_analysis/tests/port_instance/undef_size_and_port.ref.txt # fpp_analysis/tests/port_instance/undef_telemetry.ref.txt # fpp_analysis/tests/port_instance/undef_text_event.ref.txt # fpp_analysis/tests/port_instance/undef_time_get.ref.txt # fpp_analysis/tests/redef/port.ref.txt # fpp_analysis/tests/state_machine/types/action_undef_type.ref.txt # fpp_analysis/tests/state_machine/types/array_undef_type.ref.txt # fpp_analysis/tests/state_machine/types/guard_undef_type.ref.txt # fpp_analysis/tests/state_machine/types/signal_undef_type.ref.txt # fpp_analysis/tests/state_machine/types/struct_undef_type.ref.txt # fpp_analysis/tests/state_machine/undef/absolute_state_error.ref.txt # fpp_analysis/tests/state_machine_defs/nested_type_undef.ref.txt # fpp_analysis/tests/template/expand_absolute_global_template.ref.txt # fpp_analysis/tests/template/expand_absolute_undefined.ref.txt # fpp_analysis/tests/template/expand_in_include.ref.txt # fpp_analysis/tests/template/type_arg_string.ref.txt # fpp_analysis/tests/template/type_param_implied_use.ref.txt # fpp_analysis/tests/tlm_packets/channel_order_ids.ref.txt # fpp_analysis/tests/tlm_packets/channel_order_instances.ref.txt # fpp_analysis/tests/tlm_packets/duplicate_packet_name_explicit_ids.ref.txt # fpp_analysis/tests/tlm_packets/test.rs # fpp_analysis/tests/tlm_packets/topology_order_a.ref.txt # fpp_analysis/tests/tlm_packets/topology_order_include.ref.txt # fpp_analysis/tests/tlm_packets/topology_order_template.ref.txt # fpp_analysis/tests/tlm_packets/used_channel_order.ref.txt # fpp_analysis/tests/type/string_length_shadowed.ref.txt # fpp_analysis/tests/type/string_missing_default_size_constant.ref.txt # fpp_analysis/tests/type/string_missing_fw_size_store_type.ref.txt # fpp_analysis/tests/type/string_size_type_shadowed.ref.txt # fpp_lsp_parser/src/grammar/topology.rs # fpp_lsp_parser/src/grammar/types.rs # fpp_lsp_server/src/workspace_tests.rs # fpp_python/fpp.pyi # fpp_python/tests/test_api_contracts.py # fpp_python/tests/test_templates.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR brings in the FPP Templates branch we have finally merged into FPP Scala main. It also brings in absolute qualfied identifiers and puts the implementation and diagnostics much closer in-line with Scala.
All tests fpp-check tests are now fully represented (including template tests).
For the implementation internals, the analyzer pattern is now similar to the Scala version where we can mixin analyzers to decend into different definitions.
Scala divergences:
Opus 5.5 has found the following divergences between Rust/Scala. Some of these are Scala divergences from the spec and others are implementation details:
Spec over Scala (policy 3):
ledger §2.2).
typeoption-alias-bug.md).sizeof(string size N)range-checks N; Scala does not.Integer(Type-Checking.adoc, "Arithmetic and Concatenation"); Scala gives it the
common type, so e.g.
(q * q) >> 2withqof an alias type is accepted hereand rejected by Scala (
template/alias_param_binop_shift_ok).and a same-named system collide (Scoping-of-Names.adoc); Scala accepts
(
redef/module_system).order; Scala accepts it when the state machine comes first
(
type/state_machine_not_a_type).truncation (
param/default_opcode_after_32_bits); Scala truncates.i128 (policy 2): values past the i128 range are reported ("arithmetic result is
too large to represent", the default id/opcode overflow tests); Scala's BigInt
accepts them. An explicit id or opcode at the i128 maximum is reported even when
no later default needs it.
Where Scala crashes (InternalError/MatchError), Rust reports a spec-based error
(non-numeric instance attributes and port numbers, a special port whose implied
use names a module,
string size "abc"-style cases where Rust has a check).Collections above four entries: Scala iterates in hash order, Rust in insertion
order, so a "first of many" error can name a different element.
Diagnostics: wording differs where the meaning is the same; Rust spans may cover
more context; extra diagnostics after the first are allowed (policy 1),
including a few identical repeats (an unresolvable template interface argument
used several times is reported once per use).
ComponentInstance::filestays relative to the source file (ledger §2.4).Scala Parity
Scala
fpp-checksuite (724 test rows, every Scala test is in the Rust suite):