Issue #19394 has been updated by nagachika (Tomoyuki Chikanaga).
Backport changed from 3.0: UNKNOWN, 3.1: REQUIRED, 3.2: REQUIRED to 3.0: UNKNOWN, 3.1:
REQUIRED, 3.2: DONE
Merged
https://github.com/ruby/ruby/pull/7888
----------------------------------------
Bug #19394: cvars in instance of cloned class point to source class's cvars even after
class_variable_set on clone
https://bugs.ruby-lang.org/issues/19394#change-103732
* Author: jamescdavis (James Davis)
* Status: Closed
* Priority: Normal
* ruby -v: ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [x86_64-darwin21]
* Backport: 3.0: UNKNOWN, 3.1: REQUIRED, 3.2: DONE
----------------------------------------
This unexpected change in behavior happens between Ruby 3.0.x and 3.1.x. In Ruby >=
3.1, when a class with a cvar is cloned (or duped), the cvar in instances of the cloned
class continues to point to the source class’s cvar after the clone has its cvar updated
with `class_variable_set`. In Ruby < 3.1, the cloned class instance points to the
updated cvar, as expected.
It seems likely that this is a bug in the [cvar
cache](https://bugs.ruby-lang.org/issues/17763) introduced in Ruby 3.1.
Repro:
```rb
class Foo
@@bar = 'bar'
def print_bar
puts "#{self.class.name} (from instance): #{@@bar} #{@@bar.object_id}"
end
end
foo_bar = Foo.class_variable_get(:@@bar)
puts "Foo (class_variable_get): #{foo_bar} #{foo_bar.object_id}"
Foo.new.print_bar
FooClone = Foo.clone
FooClone.class_variable_set(:@@bar, 'bar_clone')
foo_clone_bar = FooClone.class_variable_get(:@@bar)
puts "FooClone (class_variable_get): #{foo_clone_bar}
#{foo_clone_bar.object_id}"
FooClone.new.print_bar
```
Ruby 3.0.5:
```
Foo (class_variable_get): bar 60
Foo (from instance): bar 60
FooClone (class_variable_get): bar_clone 80
FooClone (from instance): bar_clone 80
```
Ruby 3.1.3, 3.2.0:
```
Foo (class_variable_get): bar 60
Foo (from instance): bar 60
FooClone (class_variable_get): bar_clone 80
FooClone (from instance): bar 60
```
Something similar happens when there are multiple clones and a cvar that the source class
does not have defined is set on the clones. In this case, the cvars in instances of the
clones all point to the first clone’s cvar.
Repro:
```rb
class Foo
def print_bar
puts "#{self.class.name} (from instance): #{@@bar} #{@@bar.object_id}"
end
end
Foo1 = Foo.clone
Foo2 = Foo.clone
Foo3 = Foo.clone
Foo1.class_variable_set(:@@bar, 'bar1')
Foo2.class_variable_set(:@@bar, 'bar2')
Foo3.class_variable_set(:@@bar, 'bar3')
foo1_bar = Foo1.class_variable_get(:@@bar)
foo2_bar = Foo2.class_variable_get(:@@bar)
foo3_bar = Foo3.class_variable_get(:@@bar)
puts "Foo1 (class_variable_get): #{foo1_bar} #{foo1_bar.object_id}"
puts "Foo2 (class_variable_get): #{foo2_bar} #{foo2_bar.object_id}"
puts "Foo3 (class_variable_get): #{foo3_bar} #{foo3_bar.object_id}"
Foo1.new.print_bar
Foo2.new.print_bar
Foo3.new.print_bar
```
Ruby 3.0.5:
```
Foo1 (class_variable_get): bar1 60
Foo2 (class_variable_get): bar2 80
Foo3 (class_variable_get): bar3 100
Foo1 (from instance): bar1 60
Foo2 (from instance): bar2 80
Foo3 (from instance): bar3 100
```
Ruby 3.1.3, 3.2.0:
```
Foo1 (class_variable_get): bar1 60
Foo2 (class_variable_get): bar2 80
Foo3 (class_variable_get): bar3 100
Foo1 (from instance): bar1 60
Foo2 (from instance): bar1 60
Foo3 (from instance): bar1 60
```
--
https://bugs.ruby-lang.org/