Feature #16153

Updated by Dan0042 (Daniel DeLorme) about 4 years ago

Freezing objects can give us a nice performance boost, but freezing previously non-frozen objects is a backward-incompatible change which is hard to handle because the place where the object is mutated can be far from where it was frozen, and tests might not cover the cases of frozen input vs non-frozen input. frozen. 

 I propose adding a flag which gives us a migration path for freezing objects. For purposes of discussion I will call this flag "eventually_frozen". It would act as a pseudo-frozen change the behavior of the frozen flag where so that mutating the object would result in a warning instead of an error. It would also change the return value of `Object#frozen?` so So code like `obj = obj.dup if obj.frozen?` and `+str` would work as expected to remove the warning. expected. Note that eventually_frozen strings cannot be deduplicated, as they are in reality mutable. 

 This way it would be possible for Symbol#to_s (and many others) to return an eventually_frozen string in 2.7 which gives apps and gems time to migrate, before finally becoming a frozen deduplicated string in 3.0. This might could even open up a migration path for eventually using `frozen_string_literal:true` as default. For example if it was possible to add `frozen_string_literal:eventual` to all files in a project (or as a global switch), project, we could run that in production to discover where to fix things, and then change it to `frozen_string_literal:true` for a bug-free performance boost. 

 Proposed changes: 
 * Object#freeze(eventual:false) 
    * if `eventual` keyword is false, false 
       * set frozen=true and eventually_frozen=false 
    * if `eventual` keyword is true, true 
       * set frozen=true and eventually_frozen=true UNLESS frozen flag is already true 
 * String#+@ String#-@ 
    * if eventually_frozen is true, create a duplicate deduplicate the string with eventually_frozen=false as if it was non-frozen 
 * Object#frozen?(eventual:true) 
    * if `eventual` is false 
       * return true (frozen==true and eventually_frozen==false) 
    * if `eventual` keyword and eventually_frozen flag are both is true 
       * return (frozen==true) 
 * rb_check_frozen 
    * if eventually_frozen is true  
       * output warning 
    * if eventually_frozen flag is false and frozen is true 
       * raise error