Actions
Bug #17563
closedFrozenError raised from Module#const_set when receiver is not frozen
Description
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 #<Class:#<Class:0x00007fe883886698>>: #<Class:0x00007fe883886698> (FrozenError)
from -e:1:in `<main>'
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
:
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
:
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));
}
Actions
Like0
Like0Like0Like0Like0Like0