From: "eileencodes (Eileen Uchitelle) via ruby-core" Date: 2023-01-31T19:52:34+00:00 Subject: [ruby-core:112149] [Ruby master Bug#19394] cvars in instance of cloned class point to source class's cvars even after class_variable_set on clone Issue #19394 has been updated by eileencodes (Eileen Uchitelle). I will take a look, thanks for the ping Jean. ---------------------------------------- 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-101586 * Author: jamescdavis (James Davis) * Status: Open * Priority: Normal * ruby -v: ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [x86_64-darwin21] * Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- 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/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/