Do not write back a frozen $LOADED_FEATURES while rebuilding its index

A frozen array is never shared, so the check for user modifications
always fires and rb_ary_replace() raises FrozenError before
rb_provide_feature() can report the freeze.  Appending to
$LOADED_FEATURES before freezing it is enough to reach this.  Under
RUBY_BOX=1 every box reaches it, because a box starts with an empty
index snapshot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hiroshi SHIBATA 2026-09-07 11:22:17 +09:00
parent 033fdefd80
commit c9cfbc3f04
Notes: git 2026-11-06 08:05:25 +00:00
2 changed files with 10 additions and 2 deletions

7
load.c
View File

@ -395,8 +395,11 @@ get_loaded_features_index(const rb_box_t *box)
rb_ary_store(features, i, as_str);
features_index_add(box, as_str, INT2FIX(i));
}
/* The user modified $LOADED_FEATURES, so we should restore the changes. */
if (!rb_ary_shared_with_p(features, box->loaded_features)) {
/* The user modified $LOADED_FEATURES, so we should restore the changes.
* A frozen array is never shared, so this check always fires for one.
* Let rb_provide_feature() report the freeze instead. */
if (!OBJ_FROZEN(box->loaded_features) &&
!rb_ary_shared_with_p(features, box->loaded_features)) {
rb_ary_replace(box->loaded_features, features);
}
reset_loaded_features_snapshot(box);

View File

@ -537,6 +537,11 @@ class TestRequire < Test::Unit::TestCase
bug3756)
end
def test_frozen_loaded_features_while_rebuilding_index
assert_in_out_err(['-e', '$LOADED_FEATURES << "dummy.rb"; $LOADED_FEATURES.freeze; require "erb"'], "",
[], /\$LOADED_FEATURES is frozen; cannot append feature \(RuntimeError\)$/)
end
def test_race_exception
bug5754 = '[ruby-core:41618]'
path = nil