From: "nagachika (Tomoyuki Chikanaga) via ruby-core" <ruby-core@...>
Date: 2023-07-01T05:19:16+00:00
Subject: [ruby-core:114061] [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 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/

 ______________________________________________
 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/