From: merch-redmine@... Date: 2021-01-19T22:34:32+00:00 Subject: [ruby-core:102175] [Ruby master Bug#17563] FrozenError raised from Module#const_set when receiver is not frozen Issue #17563 has been updated by jeremyevans0 (Jeremy Evans). Assignee changed from jeremyevans0 (Jeremy Evans) to nobu (Nobuyoshi Nakada) Using `ivar_set` seems reasonable to me, but I'd like to get @nobu's input as to whether this is acceptable. ---------------------------------------- Bug #17563: FrozenError raised from Module#const_set when receiver is not frozen https://bugs.ruby-lang.org/issues/17563#change-90024 * Author: ryannevell (Ryan Nevell) * Status: Open * Priority: Normal * Assignee: nobu (Nobuyoshi Nakada) * ruby -v: ruby 3.1.0dev (2021-01-19T16:58:26Z master a8dc5156e1) [x86_64-darwin20] * Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN ---------------------------------------- The following code executed without error on Ruby 2.7.1 and many earlier 2.* versions. The behavior has changed on Ruby 3.0.0 and now raises a Frozen Error: ``` % ruby -e 'Module.new.const_set(:Foo, Class.new.freeze)' -e:1:in `const_set': can't modify frozen #>: # (FrozenError) from -e:1:in `
' ``` It seems that const_set is attempting to modify the (frozen) object being passed in, not just the receiver of the const_set call. This seems to only happen if that object is a Module or Class. This may be due to this change to rb_const_set. On 2.7.1, no action was taken if `parental_path` was `nil`: ``` c int parental_path_permanent; VALUE parental_path = classname(klass, &parental_path_permanent); if (!NIL_P(parental_path)) { if (parental_path_permanent && !val_path_permanent) { set_namespace_path(val, build_const_path(parental_path, id)); } else if (!parental_path_permanent && NIL_P(val_path)) { rb_ivar_set(val, tmp_classpath, build_const_path(parental_path, id)); } } ``` On 3.0, a temporary class path is created, and then it goes on to call `rb_ivar_set` on `val`, which will error out if `val.frozen? == true`: ``` c int parental_path_permanent; VALUE parental_path = classname(klass, &parental_path_permanent); if (NIL_P(parental_path)) { int throwaway; parental_path = rb_tmp_class_path(klass, &throwaway, make_temporary_path); } if (parental_path_permanent && !val_path_permanent) { set_namespace_path(val, build_const_path(parental_path, id)); } else if (!parental_path_permanent && NIL_P(val_path)) { rb_ivar_set(val, tmp_classpath, build_const_path(parental_path, id)); } ``` -- https://bugs.ruby-lang.org/ Unsubscribe: