Skip to content

Commit 07228b0

Browse files
shugoclaude
authored andcommitted
Register clones with their superclass's subclass list
Since a2531ba, class_associate_super() maintains subclass lists only for direct T_CLASS -> T_CLASS links. When a class that includes or prepends a module is cloned, rb_mod_init_copy() connects the clone (or its origin) to a T_ICLASS, so the clone was never added to its real superclass's subclass list and Class#subclasses did not return it. Register the clone with its superclass explicitly after the chain is built. rb_class_superclass() can be used because the superclass table is already up to date at this point and the superclass always exists (the root class cannot be cloned). Clones of classes without modules are registered by class_associate_super() as before. [Backport #22190] Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 41d79e8 commit 07228b0

2 files changed

Lines changed: 21 additions & 0 deletions

File tree

class.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,13 @@ rb_mod_init_copy(VALUE clone, VALUE orig)
11281128
rb_class_update_superclasses(clone);
11291129
}
11301130

1131+
if (RB_TYPE_P(clone, T_CLASS)) {
1132+
VALUE super = RCLASS_SUPER(clone);
1133+
if (super && RB_TYPE_P(super, T_ICLASS)) {
1134+
class_switch_superclass(rb_class_superclass(clone), clone);
1135+
}
1136+
}
1137+
11311138
return clone;
11321139
}
11331140

test/ruby/test_class.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,20 @@ def test_subclasses
820820
end
821821
end
822822

823+
def test_subclasses_includes_clone
824+
c = Class.new
825+
with_include = Class.new(c) { include Module.new }
826+
with_prepend = Class.new(c) { prepend Module.new }
827+
plain = Class.new(c)
828+
829+
bug22190 = '[ruby-core:126038] [Bug #22190]'
830+
clones = [with_include, with_prepend, plain].flat_map {|k| [k.clone, k.dup]}
831+
subclasses = c.subclasses
832+
clones.each do |k|
833+
assert_equal(1, subclasses.count(k), "#{bug22190} expected #{k.inspect} to appear in subclasses exactly once")
834+
end
835+
end
836+
823837
def test_attached_object
824838
c = Class.new
825839
sc = c.singleton_class

0 commit comments

Comments
 (0)