Issue #17321 has been reported by ufuk (Ufuk Kayserilioglu).
----------------------------------------
Bug #17321: Having a singleton class makes cloning imperfect
https://bugs.ruby-lang.org/issues/17321
* Author: ufuk (Ufuk Kayserilioglu)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.0.0dev (2020-11-11T09:11:09Z master fa3670e6e4) [x86_64-darwin19]
* Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN
----------------------------------------
## Problem
---
Running the following reproduction script:
```ruby
class Foo
def self.foo; end
end
def report(klass, name)
puts " #{name}.instance_methods(false): #{klass.instance_methods(false)}"
end
def clone_and_compare(obj)
cln = obj.clone
report(obj.singleton_class, "obj.singleton_class")
report(cln.singleton_class, "cln.singleton_class")
report(obj.singleton_class.singleton_class, "obj.singleton_class.singleton_class")
report(cln.singleton_class.singleton_class, "cln.singleton_class.singleton_class")
end
puts "## Case 1"
obj = Foo.new
clone_and_compare(obj)
puts "## Case 2"
obj = Foo.new
obj.singleton_class
clone_and_compare(obj)
puts "## Case 3"
obj = Foo.new
obj.singleton_class.singleton_class.send(:define_method, :method_on_s2) {}
clone_and_compare(obj)
```
gives the following output:
```
## Case 1
obj.singleton_class.instance_methods(false): []
cln.singleton_class.instance_methods(false): []
obj.singleton_class.singleton_class.instance_methods(false): []
cln.singleton_class.singleton_class.instance_methods(false): []
## Case 2
obj.singleton_class.instance_methods(false): []
cln.singleton_class.instance_methods(false): []
obj.singleton_class.singleton_class.instance_methods(false): []
cln.singleton_class.singleton_class.instance_methods(false): [:foo]
## Case 3
obj.singleton_class.instance_methods(false): []
cln.singleton_class.instance_methods(false): []
obj.singleton_class.singleton_class.instance_methods(false): [:method_on_s2]
cln.singleton_class.singleton_class.instance_methods(false): [:method_on_s2]
```
`Case 2` is surprising, because the cloned object has different contents to the original.
It is surprising that `clone.singleton_class.singleton_class` has the method `:foo` whereas
`obj.singleton_class.singleton_class` does not have any methods.
`Case 3` suggests, however, that `clone.singleton_class.singleton_class` should have the
same methods as the ones on `obj.singleton_class.singleton_class`.
This reproduction script gives the same output all the way from Ruby 2.0 up to Ruby-HEAD:
https://wandbox.org/permlink/hRM9OMgtd6nuscRz
## Fix
---
@alanwu and me have put together a PR that makes `Case 2` output identical to `Case 1` output:
https://github.com/ruby/ruby/pull/3761
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>