From ebfdf8ae433c0258bb25b38ea12eb633e1363ca2 Mon Sep 17 00:00:00 2001 From: Pete Kinnecom Date: Thu, 7 May 2026 15:49:26 +0000 Subject: [PATCH] Fix Class inherited hook ordering In CRuby the inherited method is invoked before Class.new yields to the block. An "already initialized error" exception is also removed since the inherited method is now invoked before initialization and can set instance variables on a class. --- src/class.c | 6 +----- test/t/class.rb | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/class.c b/src/class.c index 07a045838..7b216a516 100644 --- a/src/class.c +++ b/src/class.c @@ -3077,10 +3077,6 @@ mrb_class_initialize(mrb_state *mrb, mrb_value obj) { struct RClass *c = mrb_class_ptr(obj); - if (c->iv) { - mrb_raise(mrb, E_TYPE_ERROR, "already initialized class"); - } - mrb_value a, b; mrb_get_args(mrb, "|C&", &a, &b); if (!mrb_nil_p(b)) { @@ -3099,6 +3095,7 @@ mrb_class_new_class(mrb_state *mrb, mrb_value cv) super = mrb_obj_value(mrb->object_class); } mrb_value new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super))); + mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class)); mrb_sym mid = MRB_SYM(initialize); if (mrb_func_basic_p(mrb, new_class, mid, mrb_class_initialize)) { mrb_class_initialize(mrb, new_class); @@ -3106,7 +3103,6 @@ mrb_class_new_class(mrb_state *mrb, mrb_value cv) else { mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk); } - mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class)); return new_class; } diff --git a/test/t/class.rb b/test/t/class.rb index 65eb5edd4..fcdb7a88b 100644 --- a/test/t/class.rb +++ b/test/t/class.rb @@ -503,3 +503,39 @@ assert('module with extended callback') do assert_true BarBeingExtended.respond_to?(:answer) assert_equal 42, BarBeingExtended.answer end + +assert("inherited hook runs before block body") do + class A + def self.values + @values ||= [] + end + + def self.inherited(mod) + mod.values << 1 + end + end + + klass = Class.new(A) do + self.values << 2 + end + + assert_equal [1, 2], klass.values +end + +assert("inherited hook runs before class body") do + class A + def self.values + @values ||= [] + end + + def self.inherited(mod) + mod.values << 1 + end + end + + class B < A + self.values << 2 + end + + assert_equal [1, 2], B.values +end