Project

General

Profile

Actions

Bug #19169

closed

Kernel#freeze doesn't propagate to singleton class when the singleton class has prepended modules

Bug #19169: Kernel#freeze doesn't propagate to singleton class when the singleton class has prepended modules

Added by alanwu (Alan Wu) almost 3 years ago. Updated almost 3 years ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 3.2.0dev (2022-12-01T16:50:48Z master 9da2a5204f) [arm64-darwin22]
[ruby-core:111138]

Description

a = []
singleton = a.singleton_class
a.freeze
p singleton.frozen? # => true

a = []
singleton = a.singleton_class.tap { |klass| klass.prepend(Module.new) }
a.freeze
p singleton.frozen? # => false

It's because of this function:

void
rb_freeze_singleton_class(VALUE x)
{
    /* should not propagate to meta-meta-class, and so on */
    if (!(RBASIC(x)->flags & FL_SINGLETON)) {
        VALUE klass = RBASIC_CLASS(x);
        if (klass && (klass = RCLASS_ORIGIN(klass)) != 0 &&
            FL_TEST(klass, (FL_SINGLETON|FL_FREEZE)) == FL_SINGLETON) {
            OBJ_FREEZE_RAW(klass);
        }
    }
}

It freezes the origin class of the singleton class, but I'm not sure why.
When there is no prepended modules RCLASS_ORIGIN(klass) just returns klass.
It's old code, so maybe the meaning of RCLASS_ORIGIN has changed?

Practically speaking not a big deal because each time one calls .singleton_class
it copies the frozen bit of the attached object to the singleton class.

Updated by byroot (Jean Boussier) almost 3 years ago Actions #1 [ruby-core:111145]

I ran it against older rubies, and it seems like it has been behaving like this since prepend was introduced.

It seem to work on TruffleRuby, but not JRuby.

Updated by alanwu (Alan Wu) almost 3 years ago Actions #2

  • Status changed from Open to Closed

Applied in changeset git|bb8afd7265af41a75e8889774aa26f157f146380.


Freeze singleton class, not its origin

Previously, when we froze an object, we froze
RCLASS_ORIGIN(object.singleton_class), which didn't freeze
object.singleton_class when it has some prepended modules.

Origin iclass are internal objects and users can't interact with
them through Kernel#freeze?, Kernel#freeze, or any mutation method
that checks the frozen status. So we shouldn't touch the origin
iclasses when the frozen status should be visible.

[Bug #19169]

Actions

Also available in: PDF Atom