The return and receiver class analyses ask the bytecode IR how a body leaves its frame and where the values it
returns come from (tools/bc2cpp/bytecode_ir_returns.rb, ADR 0503).
program = BytecodeIR.for(irep)
program.exits # [Exit(index, source, kind)], kind :value :block_return :nil :self :true :false :break :stop
program.exit?(:value) # any `RETURN R(a)`
BytecodeIR.nested_bodies(irep) # every block and lambda below irep, depth first (Irep#tree)
BytecodeIR.nested_exits(irep, :block_return) # [[child, Exit]]: the `return`s inside blocks
BytecodeIR.nested_exit?(irep, :break)
BytecodeIR.return_sources(irep, index, reg) # [[writer, reg] | :nil] or nilreturn_sources is reaching_definitions over handler edges with the body's own nested-block writes opaque. A
LOADNIL, and the fall-through of a RAISEIF of the register (vm.c raises unless it is nil), are nil sources. The
answer is nil when a path is unaccounted for: the method's entry value reaches, an op outside the audited write list
(the rescue handler's EXCEPT and RESCUE among them) is crossed, a callee frame may overwrite the register, or the
state budget runs out.
Who reads them:
- FIXNUM_RETURN_PROOF, ARRAY_RETURN_PROOF and RETCLASS_SELF_CALL_SUPPORT (
codegen_return_analysis.rb): the exits of every candidate body,return_sourcesfor the class proof andwrite_dominates?for the Array proof's hops. - DECLARED_RETURN_CHECK (
codegen_declared_returns.rb): every exit and nestedreturnof a declared method. - The exact-class return table (
codegen_return_classes.rb): the exits of a body and of its blocks, joined over the exact-class flow; the bodies that call a tracked name come from one per-op index. module_function_copy_self_safe?andblock_carrying_callers_of(codegen_receiver_facts.rb).
What stays a linear walk, and why:
straightline_return_reg?finds the textually preceding writer withwalk_writersbecausetrace_new_targetandproven_array_source, which prove that writer, scan the same way; the IR only checks that the writer dominates (write_dominates?).rescued_exception_receiver?follows a handler's exception register back to itsEXCEPT, a handler entry the normal-flow walk refuses at.receiver_trace_originnames the producers that reach an unresolved receiver, for a diagnostic comment. At a join it reports every producer asjoin:a.b(BytecodeIR reaching definitions). A query the IR refuses, such as a receiver inside a rescue region, is labelledunproven.<nearest textual writer>: the label is a single writer, not a complete set.
Run ruby scripts/bc2cpp/ir_facts_check.rb for the host-side regression checks.