Ruby 3.0で Module#include の挙動が変わってる

社内で Rubyのしくみの読書会をやっていて、6章の「メソッド探索と定数探索」を読みながらコードを動かしている最中に気がついた。確か以下の記述の検証をしていたときだと思う。

モジュールをインクルードすると、 Rubyはクラスの スーパークラスのリンクトリストに、 そのモジュールのコピーを挿入する

PatShaughnessy. Rubyのしくみ Ruby Under a Microscope (p.150). Kindle 版.

Ruby 2.6.6

$ irb
irb(main):002:0> module M
irb(main):003:1> def hello; end
irb(main):004:1> end
=> :hello
irb(main):005:0> class A
irb(main):006:1> include M
irb(main):007:1> end
=> A
irb(main):008:0> a = A.new
=> #<A:0x00007ffc5f950ef8>
irb(main):009:0> module M2
irb(main):010:1> def foo; end
irb(main):011:1> end
=> :foo
irb(main):012:0> module M
irb(main):013:1> include M2
irb(main):014:1> end
=> M
irb(main):015:0> a.foo
Traceback (most recent call last):
        4: from /Users/ikaruga777/.asdf/installs/ruby/2.6.6/bin/irb:23:in `<main>'
        3: from /Users/ikaruga777/.asdf/installs/ruby/2.6.6/bin/irb:23:in `load'
        2: from /Users/ikaruga777/.asdf/installs/ruby/2.6.6/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
        1: from (irb):15
NoMethodError (undefined method `foo' for #<A:0x00007ffc5f950ef8>)
Did you mean?  for
irb(main):016:0> a.class.ancestors
=> [A, M, Object, Kernel, BasicObject]
irb(main):017:0>

Ruby 3.0.1

irb(main):003:1* module M
irb(main):004:1*   def hello; end
irb(main):005:0> end
=> :hello
irb(main):006:1* class A
irb(main):007:1*   include M
irb(main):008:0> end
=> A
irb(main):009:0> a = A.new
=> #<A:0x00007ffc8a17a460>
irb(main):010:1* module M2
irb(main):011:1*   def foo; end
irb(main):012:0> end
=> :foo
irb(main):013:1* module M
irb(main):014:1*   include M2
irb(main):015:0> end
=> M
irb(main):016:0> a.foo
=> nil
irb(main):017:0> a.class.ancestors
=> [A, M, M2, Object, PP::ObjectMixin, Kernel, BasicObject]
irb(main):018:0>

2.x まではモジュールをインクルードしたクラスの定義後に、インクルードされたモジュールに対して更にモジュールをインクルードしても継承チェーンは反映されなかった。しかし、3.0.0 からは継承チェーンに後からインクルードしたモジュールが反映されるようになる。

ここらへんをみて納得した。