@@ -637,6 +637,152 @@ def test_warn_frozen_error_warns_on_instance_variable_writes
637637 RUBY
638638 end
639639
640+ def test_warn_frozen_error_make_shareable_tolerates_unshareable_typed_data
641+ assert_ractor ( <<~'RUBY' )
642+ old = Ractor.warn_frozen_error
643+ begin
644+ Ractor.warn_frozen_error = true
645+ h = Hash.new(Mutex.new)
646+ assert_nothing_raised { Ractor.make_shareable(h) }
647+ assert_equal false, h.frozen?
648+ ensure
649+ Ractor.warn_frozen_error = old
650+ end
651+ RUBY
652+ end
653+
654+ def test_warn_frozen_error_make_shareable_handles_proc_cycles
655+ assert_ractor ( <<~'RUBY' )
656+ old = Ractor.warn_frozen_error
657+ begin
658+ Ractor.warn_frozen_error = true
659+ obj = Object.new
660+ pr = proc { obj }
661+ obj.instance_variable_set(:@proc, pr)
662+
663+ assert_nothing_raised { Ractor.make_shareable(obj) }
664+ assert_same obj, pr.call
665+ ensure
666+ Ractor.warn_frozen_error = old
667+ end
668+ RUBY
669+ end
670+
671+ def test_warn_frozen_error_marks_shareable_proc_without_freezing
672+ assert_ractor ( <<~'RUBY' )
673+ old = Ractor.warn_frozen_error
674+ begin
675+ Ractor.warn_frozen_error = true
676+ pr = Ractor.shareable_proc { :ok }
677+
678+ assert_equal false, Ractor.shareable?(pr)
679+ assert_equal false, pr.frozen?
680+ assert_equal :ok, pr.call
681+
682+ assert_warning(/would raise FrozenError.*Proc/) do
683+ pr.instance_variable_set(:@mutated, true)
684+ end
685+ assert_equal true, pr.instance_variable_get(:@mutated)
686+ ensure
687+ Ractor.warn_frozen_error = old
688+ end
689+ RUBY
690+ end
691+
692+ def test_warn_frozen_error_marks_shareable_lambda_without_freezing
693+ assert_ractor ( <<~'RUBY' )
694+ old = Ractor.warn_frozen_error
695+ begin
696+ Ractor.warn_frozen_error = true
697+ pr = Ractor.shareable_lambda { :ok }
698+
699+ assert_equal false, Ractor.shareable?(pr)
700+ assert_equal false, pr.frozen?
701+ assert_equal true, pr.lambda?
702+ assert_equal :ok, pr.call
703+
704+ assert_warning(/would raise FrozenError.*Proc/) do
705+ pr.instance_variable_set(:@mutated, true)
706+ end
707+ assert_equal true, pr.instance_variable_get(:@mutated)
708+ ensure
709+ Ractor.warn_frozen_error = old
710+ end
711+ RUBY
712+ end
713+
714+ def test_warn_frozen_error_preserves_shareable_proc_outer_variables
715+ assert_ractor ( <<~'RUBY' )
716+ old = Ractor.warn_frozen_error
717+ begin
718+ Ractor.warn_frozen_error = true
719+ ary = []
720+ pr = nil
721+
722+ assert_warning(/cannot make a shareable Proc because it can refer unshareable object.*variable 'ary'/) do
723+ pr = Ractor.shareable_proc { ary << :called; ary }
724+ end
725+
726+ assert_equal [:called], pr.call
727+ assert_equal false, ary.frozen?
728+ assert_warning(/would raise FrozenError.*Array/) do
729+ ary << :mutated
730+ end
731+ assert_equal [:called, :mutated], ary
732+ ensure
733+ Ractor.warn_frozen_error = old
734+ end
735+ RUBY
736+ end
737+
738+ def test_warn_frozen_error_warns_but_preserves_reassigned_outer_variables
739+ assert_ractor ( <<~'RUBY' )
740+ old = Ractor.warn_frozen_error
741+ begin
742+ Ractor.warn_frozen_error = true
743+ counter = 0
744+ pr = nil
745+
746+ assert_warning(/can not make a Proc shareable because it accesses outer variables \(counter\)/) do
747+ pr = Ractor.shareable_proc { counter += 1 }
748+ end
749+
750+ assert_equal 1, pr.call
751+ assert_equal 2, pr.call
752+ ensure
753+ Ractor.warn_frozen_error = old
754+ end
755+ RUBY
756+ end
757+
758+ def test_warn_frozen_error_marks_shareable_proc_self_without_freezing
759+ assert_ractor ( <<~'RUBY' )
760+ old = Ractor.warn_frozen_error
761+ begin
762+ Ractor.warn_frozen_error = true
763+ replacement_self = Object.new
764+ nested = []
765+ replacement_self.instance_variable_set(:@nested, nested)
766+ pr = Ractor.shareable_proc(self: replacement_self) { self }
767+
768+ assert_same replacement_self, pr.call
769+ assert_equal false, replacement_self.frozen?
770+ assert_equal false, nested.frozen?
771+
772+ assert_warning(/would raise FrozenError.*Object/) do
773+ replacement_self.instance_variable_set(:@mutated, true)
774+ end
775+ assert_equal true, replacement_self.instance_variable_get(:@mutated)
776+ assert_warning(/would raise FrozenError.*Array/) do
777+ nested << :mutated
778+ end
779+ assert_equal [:mutated], nested
780+ ensure
781+ Ractor.warn_frozen_error = old
782+ end
783+ RUBY
784+ end
785+
640786 def test_warn_frozen_error_off_uses_normal_make_shareable_freezing
641787 assert_ractor ( <<~'RUBY' )
642788 old = Ractor.warn_frozen_error
@@ -646,6 +792,10 @@ def test_warn_frozen_error_off_uses_normal_make_shareable_freezing
646792 Ractor.make_shareable(ary)
647793 assert_equal true, ary.frozen?
648794 assert_raise(FrozenError) { ary << :mutated }
795+
796+ pr = Ractor.shareable_proc { :ok }
797+ assert_equal true, pr.frozen?
798+ assert_raise(FrozenError) { pr.instance_variable_set(:@mutated, true) }
649799 ensure
650800 Ractor.warn_frozen_error = old
651801 end
0 commit comments