Skip to content

Support RITE0400 bytecode and ensure handling - #90

Merged
udzura merged 3 commits into
masterfrom
codex/rite0400-exception-handling
Sep 13, 2026
Merged

udzura merged 3 commits into
masterfrom
codex/rite0400-exception-handling

Conversation

@udzura

@udzura udzura commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Split from #89 with only the two commits that introduce RITE0400 decoding and ensure exception propagation.

  • Decode mruby 4.0 opcode numbering and implement its newly added opcodes
  • Reject non-RITE0400 bytecode, compile tests with the RITE0400 compiler
  • Track catch handler ranges so the innermost matching rescue/ensure is selected
  • Let exceptions raised from an ensure clause reach an outer handler

Excluded from #89: the ENTER &nil block-rejection change, removal of 3.x binary examples, and documentation updates.

Validated with cargo test --package mrubyedge.

bash0C7 and others added 3 commits September 13, 2026 14:38
mruby 4.0 renumbered the opcode table from index 36 and added thirteen
opcodes, so a 4.0 chunk cannot be decoded with the 3.x numbering and a 3.x
chunk cannot be decoded with this one. rite::load now refuses any header
that does not say 04, and the tests compile their Ruby with
mruby-compiler2, which emits it.

The table and the opcodes have to land together: 4.0's codegen reaches
TDEF, SSEND0 and the RET* family for ordinary Ruby, so the existing suite
would hit unimplemented arms if either half came first. The same goes for
three paths 4.0's codegen walks that 3.3's did not:

- a rescue handler opens with GETGV $! rather than EXCEPT, and reading a
  global that was never assigned answered an internal error instead of nil
- a single begin/ensure emits three overlapping catch handlers, so the
  table has to keep each handler's range and be searched the way vm.c's
  catch_handler_find does, at the raise, innermost first
- the ensure path runs RESCUE against the nil that EXCEPT leaves behind,
  where the VM assumed an exception

MATCHERR raises NoMatchingPatternError, which the prelude now defines
under StandardError.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FfoC7uMVr3d3VaxWFnaPFT
consume_ensure_block stashed every error into vm.exception and carried on to
the RAISEIF it stops at, so op_jmpuw returned Ok, the run loop never started
unwinding, and the catch handler search was skipped. The exception surfaced
as a Rust error out of vm.run() instead of being rescued by the Ruby around
it. mruby aborts the clause at the raise.

Real exceptions now leave as Err, which is the ingress the run loop already
has for them. Error::Break and Error::BlockReturn keep the old handling:
they are control flow, not exceptions, and the Break arm reads a breadcrumb
that the ensure clause never pushed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FfoC7uMVr3d3VaxWFnaPFT
Use the released crates.io package for RITE0400 test compilation.

Co-authored-by: Codex <codex@openai.com>
@udzura
udzura merged commit a7423bd into master Sep 13, 2026
5 checks passed
@udzura
udzura deleted the codex/rite0400-exception-handling branch September 13, 2026 05:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant