diff options
author | Étienne Barrié <[email protected]> | 2023-12-01 11:33:00 +0100 |
---|---|---|
committer | Jean Boussier <[email protected]> | 2024-03-19 09:26:49 +0100 |
commit | 12be40ae6be78ac41e8e3f3c313cc6f63e7fa6c4 (patch) | |
tree | f6b81fac770da6b705557623224dbf9b9c2d2847 /test/ruby/test_string.rb | |
parent | 86b15316a748a579dd4fd4df42b6db42accebdc2 (diff) |
Implement chilled strings
[Feature #20205]
As a path toward enabling frozen string literals by default in the future,
this commit introduce "chilled strings". From a user perspective chilled
strings pretend to be frozen, but on the first attempt to mutate them,
they lose their frozen status and emit a warning rather than to raise a
`FrozenError`.
Implementation wise, `rb_compile_option_struct.frozen_string_literal` is
no longer a boolean but a tri-state of `enabled/disabled/unset`.
When code is compiled with frozen string literals neither explictly enabled
or disabled, string literals are compiled with a new `putchilledstring`
instruction. This instruction is identical to `putstring` except it marks
the String with the `STR_CHILLED (FL_USER3)` and `FL_FREEZE` flags.
Chilled strings have the `FL_FREEZE` flag as to minimize the need to check
for chilled strings across the codebase, and to improve compatibility with
C extensions.
Notes:
- `String#freeze`: clears the chilled flag.
- `String#-@`: acts as if the string was mutable.
- `String#+@`: acts as if the string was mutable.
- `String#clone`: copies the chilled flag.
Co-authored-by: Jean Boussier <[email protected]>
Diffstat (limited to 'test/ruby/test_string.rb')
-rw-r--r-- | test/ruby/test_string.rb | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 58e1af2b61..3f1b91a6a9 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -3610,6 +3610,39 @@ CODE assert_bytesplice_raise(ArgumentError, S("hello"), 0..-1, "bye", 0, 3) end + def test_chilled_string + chilled_string = eval('"chilled"') + + # Chilled strings pretend to be frozen + assert_predicate chilled_string, :frozen? + + assert_not_predicate chilled_string.dup, :frozen? + assert_predicate chilled_string.clone, :frozen? + + # @+ treat the original string as frozen + assert_not_predicate +chilled_string, :frozen? + assert_not_same chilled_string, +chilled_string + + # @- the the original string as mutable + assert_predicate -chilled_string, :frozen? + assert_not_same chilled_string, -chilled_string + end + + def test_chilled_string_setivar + String.class_eval <<~RUBY, __FILE__, __LINE__ + 1 + def setivar! + @ivar = 42 + @ivar + end + RUBY + chilled_string = eval('"chilled"') + begin + assert_equal 42, chilled_string.setivar! + ensure + String.undef_method(:setivar!) + end + end + private def assert_bytesplice_result(expected, s, *args) |