Skip to content

Thread::Mutex and three siblings answer their bare name, not the qualified one #759

Open

Description

@tannevaled

Measured

24 nested core classes, asked for #name and #to_s, against ruby 4.0.5 on
main @44b5067. 7 differ, in two distinct kinds.

Kind A — four classes carry their bare name instead of the qualified one

constant #name ruby 4.0.5 #name rbgo
Thread::Mutex "Thread::Mutex" "Mutex"
Thread::Queue "Thread::Queue" "Queue"
Thread::SizedQueue "Thread::SizedQueue" "SizedQueue"
Thread::ConditionVariable "Thread::ConditionVariable" "ConditionVariable"

In MRI these are defined under Thread and additionally reachable at top
level, so the qualified spelling is the class's own name. rbgo has it the other
way round: the constant resolves under Thread:: but the class answers the bare
name, so every message that names the class is wrong.

That is observable well beyond #name:

Thread::Mutex.new.to_s     # ruby "#<Thread::Mutex:0x…>"   rbgo "#<Mutex:0x…>"
Thread::Mutex.new.inspect  # same divergence
raise TypeError            # any message built from the class name

It also silently defeats shape tests written against the class name. #756's
Kernel#to_s fix first used self.ToS() == "#<" + classOf(self).name + ">" as
its "nothing better to say" test, and that test skipped Thread::Mutex
entirely
— the value renders as #<Thread::Mutex> while its class answers
"Mutex", so the two never matched and the row kept coming out without an
address. The rule had to be made structural instead. A defect that makes
correct code fail an identity check is worth more than its own row.

Kind B — three constants are absent

Enumerator::Generator   ruby  Enumerator::Generator      rbgo  NameError
Random::Formatter       ruby  Random::Formatter          rbgo  NameError
Ractor                  ruby  Ractor                     rbgo  NameError

Enumerator::Generator is the interesting one: rbgo prints that name —
Enumerator.new { }.inspect gives #<Enumerator: #<Enumerator::Generator>:each>
— while the constant does not exist. So the name is a literal in a format string
rather than a class's own, and the two cannot be kept in step.

(Ractor being absent is a feature gap, not a naming one, and Random::Formatter
is a stdlib module; listing them here only because the same probe found them.)

The 17 that already agree

Thread::Backtrace, Enumerator::{Lazy,Yielder,Product,ArithmeticSequence},
Struct, Process::Status, Encoding::{Converter,CompatibilityError},
IO::Buffer, ObjectSpace::{WeakMap,WeakKeyMap}, Comparable,
Math::DomainError, Errno::ENOENT, File::Stat, Data — so the nesting
machinery is right in general, and Kind A is four specific registrations.

Reproducing

%w[Thread::Mutex Thread::Queue Thread::SizedQueue Thread::ConditionVariable].each do |n|
  c = Object.const_get(n)
  puts "#{n}: name=#{c.name.inspect} to_s=#{c.to_s.inspect}"
end

Found while measuring #756.

🤖 Generated with Claude Code

Activity

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