Skip to content

rb_warn diagnostics never fire: $VERBOSE is unset in vm.globals while Ruby reads it as false #641

Closed

Description

@tannevaled

VM.rbWarn is the VM's rb_warn equivalent: it writes to the current $stderr
unless $VERBOSE is nil, which is the gate MRI applies. But
vm.globals["$VERBOSE"] starts unset, so object.IsNil sees a Go nil and
the helper returns early. Reading $VERBOSE from Ruby still answers false —
it comes from a different path — so nothing looks wrong.

The effect is that every rb_warn-class diagnostic this VM implements is
silent until a program assigns $VERBOSE itself. MRI prints them at the default
$VERBOSE == false.

Reproduction:

# v.rb
module MM; K = 1; K = 2; end
p :done
$ ruby v.rb
v.rb:1: warning: already initialized constant MM::K
v.rb:1: warning: previous definition of K was here
:done

$ rbgo v.rb
:done

$VERBOSE itself reads correctly on both:

$ ruby -e 'p $VERBOSE'   # false
$ rbgo  -e 'p $VERBOSE'  # false

Assigning it makes the warnings appear, which confirms the gate rather than the
call site is at fault:

$ rbgo -e '$VERBOSE = false; module MM; K = 1; K = 2; end'
warning: already initialized constant MM::K

Callers affected today (all of them correct code that simply never fires):

The fix belongs in VM initialisation (give $VERBOSE its false default in
vm.globals, where the reader already agrees), not in any individual warning.
Doing that will turn on a class of warnings across the suite at once, so it
wants its own measured change rather than riding along with a conformance PR.

🤖 Generated with Claude Code

Activity

  1. tannevaled commented on Sep 26, 2026

    @tannevaled
    ContributorAuthor

    Confirmed live on main @8f2a019, and here is the witness that makes it
    unmistakable.
    A census of all 2206 *_spec.rb under language/+core/ puts
    82 examples across 57 files behind this one gate — the largest single cause in
    the campaign.

    $ ruby -e 'X=1; X=2'
    -e:1: warning: already initialized constant X
    -e:1: warning: previous definition of X was here
    
    $ rbgo -e 'X=1; X=2'
    (nothing)
    
    $ rbgo -e '$VERBOSE=false; X=1; X=2'
    warning: already initialized constant X          <-- it FIRES
    

    Assigning $VERBOSE the value it already reads turns the warnings on. The
    Ruby-visible read synthesises false, so the gate is invisible from inside the
    language:

    $ ruby -e 'p $VERBOSE.nil?'   # false
    $ rbgo -e 'p $VERBOSE.nil?'   # false   <-- identical, on both engines
    

    So there are two representations of one value, and the gate reads the one nobody
    writes
    : rbWarn tests object.IsNil(vm.globals["$VERBOSE"]), and that map entry
    is never seeded at bootstrap, while the Ruby-level accessor synthesises false on
    read. No Ruby-level probe can distinguish the two — which is why this survived so
    long and why it is worth writing down as a shape, not just a fix.

    Two companions that must land with it, or the examples still fail:

    • rbWarn omits the warnUplevelPrefix(0) that rbWarn1 applies, so messages
      lose the -e:1: prefix MRI always emits (visible above);
    • the previous definition of X was here second line is never emitted at all —
      a missing example, not a formatting difference.

    Both are tracked in #699.

    A separate sub-finding from the same census, worth fixing in the same pass: the
    given block not used warning goes through two different helpers in one file —
    builtins.go:3106 uses rbWarning1 (which requires $VERBOSE == true) while
    :4428/:4456 use rbWarn1 (default level). MRI uses rb_warn for all of them,
    so count/all?/any?/none?/one?/rindex and the rest of Enumerable stay
    silent while find_index warns.

    Owning file internal/vm/io_class.go:1133 plus the bootstrap seeding. Note that
    PR #697 is currently open against io_class.go, so this wants to land after it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions