From d94ec9786eb02e54328ee379f3a7b6c1898a50f2 Mon Sep 17 00:00:00 2001 From: dearblue Date: Sat, 7 Feb 2026 15:32:11 +0900 Subject: [PATCH] Fixes identity for proc object Previously, the identity of the proc object was verified solely based on the identity of irep. This patch makes the behavior consistent with CRuby. The reason I noticed this issue was that when adding multiple proc objects with the same irep to a set object, only one was added. ```ruby p Set.new(Array.new(3) { -> {} }).size # => 3 (Ruby 4.0) # => 1 (mruby without this patch) ``` If the block scope is the same, there is only one in CRuby as well. However, in CRuby, the result of `Proc#to_s` is not affected by the block scope, so it has been changed to be based on the object's address. The reason no test for `Proc#to_s` was added is that I couldn't determine whether it should be based on `Proc#hash` or the object's address. ```ruby b = [] t = 3 while t > 0 b << -> {} t -= 1 end p Set.new(b).size # => 1 (Ruby 4.0 and mruby) p b[0].to_s == b[1].to_s # => false (Ruby 4.0) # => true (mruby without this patch) ``` --- mrbgems/mruby-proc-ext/src/proc.c | 2 +- src/proc.c | 3 ++- test/t/proc.rb | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c index 7d9bb21dd..e9fff09db 100644 --- a/mrbgems/mruby-proc-ext/src/proc.c +++ b/mrbgems/mruby-proc-ext/src/proc.c @@ -104,7 +104,7 @@ proc_inspect(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); mrb_value str = mrb_str_new_lit(mrb, "#body.irep; diff --git a/src/proc.c b/src/proc.c index ed0a2d6b9..5395aeefc 100644 --- a/src/proc.c +++ b/src/proc.c @@ -358,6 +358,7 @@ mrb_proc_eql(mrb_state *mrb, mrb_value self, mrb_value other) } else if (MRB_PROC_CFUNC_P(p2)) return FALSE; else if (p1->body.irep != p2->body.irep) return FALSE; + else if (MRB_PROC_ENV(p1) != MRB_PROC_ENV(p2)) return FALSE; return TRUE; } @@ -371,7 +372,7 @@ static mrb_value proc_hash(mrb_state *mrb, mrb_value self) { const struct RProc *p = mrb_proc_ptr(self); - return mrb_int_value(mrb, (mrb_int)(((intptr_t)p->body.irep)^MRB_TT_PROC)); + return mrb_int_value(mrb, (mrb_int)((intptr_t)p->body.irep^((intptr_t)MRB_PROC_ENV(p)>>2)^MRB_TT_PROC)); } /* 15.3.1.2.6 */ diff --git a/test/t/proc.rb b/test/t/proc.rb index b17b21e8c..417213dc2 100644 --- a/test/t/proc.rb +++ b/test/t/proc.rb @@ -178,3 +178,21 @@ assert('Creation of a proc through the block of a method') do m{ break }.call end end + +assert('identity check for proc object') do + b = [] + t = 2 + while t > 0 + b << ->{} + t -= 1 + end + assert_true b[0] == b[1] + assert_equal b[0].hash, b[1].hash + + b = [] + 2.times { + b << ->{} + } + assert_false b[0] == b[1] + assert_not_equal b[0].hash, b[1].hash +end