mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Symbol#to_s now returns a frozen string.
[Feature #22137] It has returned a chilled string, hence emitting deprecation warnings for two versions.
This commit is contained in:
1 parent
3782b63b79
commit
2c574ca7e2
16 files changed
+42
-63
Notes:
git
2026-07-10 08:02:32 +00:00
No files matched your search
@@ -58,6 +58,10 @@ Note: We're only listing outstanding class updates.
|
||||
* A deprecated behavior, `Set#to_set`, `Range#to_set`, and
|
||||
`Enumerable#to_set` accepting arguments, was removed. [[Feature #21390]]
|
||||
|
||||
* Symbol
|
||||
|
||||
* `Symbol#to_s` now returns a frozen string. [[Feature #22137]]
|
||||
|
||||
## Stdlib updates
|
||||
|
||||
### The following bundled gems are added.
|
||||
@@ -211,6 +215,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
|
||||
[Feature #21861]: https://bugs.ruby-lang.org/issues/21861
|
||||
[Feature #21932]: https://bugs.ruby-lang.org/issues/21932
|
||||
[Feature #21981]: https://bugs.ruby-lang.org/issues/21981
|
||||
[Feature #22137]: https://bugs.ruby-lang.org/issues/22137
|
||||
[Feature #22139]: https://bugs.ruby-lang.org/issues/22139
|
||||
[PR #17201]: https://github.com/ruby/ruby/pull/17201
|
||||
[RubyGems-v4.0.4]: https://github.com/rubygems/rubygems/releases/tag/v4.0.4
|
||||
|
||||
@@ -4271,15 +4271,6 @@ rb_warn_unchilled_literal(VALUE obj)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rb_warn_unchilled_symbol_to_s(VALUE obj)
|
||||
{
|
||||
rb_category_warn(
|
||||
RB_WARN_CATEGORY_DEPRECATED,
|
||||
"string returned by :%s.to_s will be frozen in the future", RSTRING_PTR(obj)
|
||||
);
|
||||
}
|
||||
|
||||
#undef rb_check_frozen
|
||||
void
|
||||
rb_check_frozen(VALUE obj)
|
||||
|
||||
@@ -11,8 +11,15 @@ bug_str_capacity(VALUE klass, VALUE str)
|
||||
return LONG2FIX(rb_str_capacity(str));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
bug_str_new_shared(VALUE klass, VALUE str)
|
||||
{
|
||||
return rb_str_new_shared(str);
|
||||
}
|
||||
|
||||
void
|
||||
Init_string_capacity(VALUE klass)
|
||||
{
|
||||
rb_define_singleton_method(klass, "capacity", bug_str_capacity, 1);
|
||||
rb_define_singleton_method(klass, "rb_str_new_shared", bug_str_new_shared, 1);
|
||||
}
|
||||
@@ -258,7 +258,7 @@ rb_check_frozen_inline(VALUE obj)
|
||||
|
||||
/* ref: internal CHILLED_STRING_P()
|
||||
This is an implementation detail subject to change. */
|
||||
if (RB_UNLIKELY(RB_TYPE_P(obj, T_STRING) && FL_TEST_RAW(obj, RUBY_FL_USER2 | RUBY_FL_USER3))) { // STR_CHILLED
|
||||
if (RB_UNLIKELY(RB_TYPE_P(obj, T_STRING) && FL_TEST_RAW(obj, RUBY_FL_USER2))) { // STR_CHILLED
|
||||
rb_str_modify(obj);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-14
@@ -18,9 +18,7 @@
|
||||
|
||||
#define STR_SHARED FL_USER0 /* = ELTS_SHARED */
|
||||
#define STR_NOEMBED FL_USER1
|
||||
#define STR_CHILLED (FL_USER2 | FL_USER3)
|
||||
#define STR_CHILLED_LITERAL FL_USER2
|
||||
#define STR_CHILLED_SYMBOL_TO_S FL_USER3
|
||||
#define STR_CHILLED FL_USER2
|
||||
#define STR_FAKESTR FL_USER19
|
||||
|
||||
enum ruby_rstring_private_flags {
|
||||
@@ -103,7 +101,6 @@ VALUE rb_str_casecmp(VALUE str1, VALUE str2);
|
||||
|
||||
/* error.c */
|
||||
void rb_warn_unchilled_literal(VALUE str);
|
||||
void rb_warn_unchilled_symbol_to_s(VALUE str);
|
||||
|
||||
static inline bool STR_EMBED_P(VALUE str);
|
||||
static inline bool STR_SHARED_P(VALUE str);
|
||||
@@ -173,17 +170,9 @@ CHILLED_STRING_P(VALUE obj)
|
||||
static inline void
|
||||
CHILLED_STRING_MUTATED(VALUE str)
|
||||
{
|
||||
VALUE chilled_reason = RB_FL_TEST_RAW(str, STR_CHILLED);
|
||||
FL_UNSET_RAW(str, STR_CHILLED);
|
||||
switch (chilled_reason) {
|
||||
case STR_CHILLED_SYMBOL_TO_S:
|
||||
rb_warn_unchilled_symbol_to_s(str);
|
||||
break;
|
||||
case STR_CHILLED_LITERAL:
|
||||
if (RB_FL_TEST_RAW(str, STR_CHILLED)) {
|
||||
FL_UNSET_RAW(str, STR_CHILLED);
|
||||
rb_warn_unchilled_literal(str);
|
||||
break;
|
||||
default:
|
||||
rb_bug("RString was chilled for multiple reasons");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,9 @@ describe "chilled String" do
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
guard -> { ruby_version_is "3.4"..."4.1" and !:test.to_s.equal?(:test.to_s) } do
|
||||
describe "chilled strings returned by Symbol#to_s" do
|
||||
|
||||
describe "#frozen?" do
|
||||
|
||||
@@ -16,7 +16,7 @@ describe "Symbol#to_s" do
|
||||
symbol.to_s.encoding.should == Encoding::US_ASCII
|
||||
end
|
||||
|
||||
ruby_version_is "3.4" do
|
||||
ruby_version_is "3.4"..."4.1" do
|
||||
it "warns about mutating returned string" do
|
||||
-> { :bad!.to_s.upcase! }.should complain(/warning: string returned by :bad!.to_s will be frozen in the future/)
|
||||
end
|
||||
@@ -29,4 +29,10 @@ describe "Symbol#to_s" do
|
||||
Warning[:deprecated] = deprecated
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "4.1" do
|
||||
it "returns a frozen string" do
|
||||
:test.to_s.frozen?.should == true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -90,12 +90,9 @@ VALUE rb_cSymbol;
|
||||
* The string is not embedded. When a string is embedded, the contents
|
||||
* follow the header. When a string is not embedded, the contents is
|
||||
* on a separately allocated buffer.
|
||||
* 2: STR_CHILLED_LITERAL (will be frozen in a future version)
|
||||
* 2: STR_CHILLED (will be frozen in a future version)
|
||||
* The string was allocated as a literal in a file without an explicit `frozen_string_literal` comment.
|
||||
* It emits a deprecation warning when mutated for the first time.
|
||||
* 3: STR_CHILLED_SYMBOL_TO_S (will be frozen in a future version)
|
||||
* The string was allocated by the `Symbol#to_s` method.
|
||||
* It emits a deprecation warning when mutated for the first time.
|
||||
* 4: STR_PRECOMPUTED_HASH
|
||||
* The string is embedded and has its precomputed hashcode stored
|
||||
* after the terminator.
|
||||
@@ -2035,7 +2032,7 @@ rb_ec_str_resurrect(struct rb_execution_context_struct *ec, VALUE str, bool chil
|
||||
str_duplicate_setup_heap(klass, str, new_str);
|
||||
}
|
||||
if (chilled) {
|
||||
FL_SET_RAW(new_str, STR_CHILLED_LITERAL);
|
||||
FL_SET_RAW(new_str, STR_CHILLED);
|
||||
}
|
||||
return new_str;
|
||||
}
|
||||
@@ -2046,7 +2043,7 @@ rb_str_with_debug_created_info(VALUE str, VALUE path, int line)
|
||||
VALUE debug_info = rb_ary_new_from_args(2, path, INT2FIX(line));
|
||||
if (OBJ_FROZEN_RAW(str)) str = rb_str_dup(str);
|
||||
rb_ivar_set(str, id_debug_created_info, rb_ary_freeze(debug_info));
|
||||
FL_SET_RAW(str, STR_CHILLED_LITERAL);
|
||||
FL_SET_RAW(str, STR_CHILLED);
|
||||
return rb_str_freeze(str);
|
||||
}
|
||||
|
||||
@@ -12441,9 +12438,7 @@ sym_inspect(VALUE sym)
|
||||
VALUE
|
||||
rb_sym_to_s(VALUE sym)
|
||||
{
|
||||
VALUE str = str_new_shared(rb_cString, rb_sym2str(sym));
|
||||
FL_SET_RAW(str, STR_CHILLED_SYMBOL_TO_S);
|
||||
return str;
|
||||
return rb_sym2str(sym);
|
||||
}
|
||||
|
||||
VALUE
|
||||
|
||||
@@ -2,32 +2,20 @@ class Symbol
|
||||
# call-seq:
|
||||
# to_s -> string
|
||||
#
|
||||
# Returns a string representation of +self+ (not including the leading colon):
|
||||
#
|
||||
# :foo.to_s # => "foo"
|
||||
#
|
||||
# Related: Symbol#inspect, Symbol#name.
|
||||
def to_s
|
||||
Primitive.attr! :leaf
|
||||
Primitive.cexpr! 'rb_sym_to_s(self)'
|
||||
end
|
||||
|
||||
alias id2name to_s
|
||||
|
||||
# call-seq:
|
||||
# name -> string
|
||||
#
|
||||
# Returns a frozen string representation of +self+ (not including the leading colon):
|
||||
#
|
||||
# :foo.name # => "foo"
|
||||
# :foo.to_s # => "foo"
|
||||
# :foo.name.frozen? # => true
|
||||
#
|
||||
# Related: Symbol#to_s, Symbol#inspect.
|
||||
def name
|
||||
# Related: Symbol#inspect
|
||||
def to_s
|
||||
Primitive.attr! :leaf
|
||||
Primitive.cexpr! 'rb_sym2str(self)'
|
||||
end
|
||||
|
||||
alias id2name to_s
|
||||
alias name to_s
|
||||
|
||||
# call-seq:
|
||||
# empty? -> true or false
|
||||
#
|
||||
|
||||
@@ -12,8 +12,8 @@ class Test_StringCapacity < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_capacity_shared
|
||||
sym = ("a" * pool_slot_size(0)).to_sym
|
||||
assert_equal 0, capa(sym.to_s)
|
||||
source = ("a" * pool_slot_size(0)).freeze
|
||||
assert_equal 0, capa(Bug::String.rb_str_new_shared(source))
|
||||
end
|
||||
|
||||
def test_capacity_normal
|
||||
|
||||
@@ -91,8 +91,7 @@ module RubyTimelineTool
|
||||
StringFlags = FlagsConverter.new({
|
||||
STR_SHARED: FL_USER_N(0),
|
||||
RSTRING_NOEMBED: FL_USER_N(1),
|
||||
STR_CHILLED_LITERAL: FL_USER_N(2),
|
||||
STR_CHILLED_SYMBOL_TO_S: FL_USER_N(3),
|
||||
STR_CHILLED: FL_USER_N(2),
|
||||
RSTRING_FSTR: FL_USER_N(17),
|
||||
})
|
||||
|
||||
|
||||
+1
-1
@@ -2008,7 +2008,7 @@ rb_obj_freeze_inline(VALUE x)
|
||||
if (RB_FL_ABLE(x)) {
|
||||
RB_FL_SET_RAW(x, RUBY_FL_FREEZE);
|
||||
if (TYPE(x) == T_STRING) {
|
||||
RB_FL_UNSET_RAW(x, FL_USER2 | FL_USER3); // STR_CHILLED
|
||||
RB_FL_UNSET_RAW(x, FL_USER2); // STR_CHILLED
|
||||
}
|
||||
|
||||
// rb_obj_freeze_inline(String)
|
||||
|
||||
@@ -6188,9 +6188,6 @@ vm_objtostring(struct rb_control_frame_struct *reg_cfp, VALUE recv, CALL_DATA cd
|
||||
switch (type) {
|
||||
case T_SYMBOL:
|
||||
if (check_method_basic_definition(cme)) {
|
||||
// rb_sym_to_s() allocates a mutable string, but since we are only
|
||||
// going to use this string for interpolation, it's fine to use the
|
||||
// frozen string.
|
||||
return rb_sym2str(recv);
|
||||
}
|
||||
break;
|
||||
|
||||
Generated
+1
-1
@@ -679,7 +679,7 @@ pub struct rb_call_data {
|
||||
pub ci: *const rb_callinfo,
|
||||
pub cc: *const rb_callcache,
|
||||
}
|
||||
pub const RSTRING_CHILLED: ruby_rstring_private_flags = 49152;
|
||||
pub const RSTRING_CHILLED: ruby_rstring_private_flags = 16384;
|
||||
pub type ruby_rstring_private_flags = u32;
|
||||
pub const RHASH_PASS_AS_KEYWORDS: ruby_rhash_flags = 8192;
|
||||
pub const RHASH_PROC_DEFAULT: ruby_rhash_flags = 16384;
|
||||
|
||||
Generated
+1
-1
@@ -1577,7 +1577,7 @@ pub struct rb_call_data {
|
||||
pub ci: *const rb_callinfo,
|
||||
pub cc: *const rb_callcache,
|
||||
}
|
||||
pub const RSTRING_CHILLED: ruby_rstring_private_flags = 49152;
|
||||
pub const RSTRING_CHILLED: ruby_rstring_private_flags = 16384;
|
||||
pub type ruby_rstring_private_flags = u32;
|
||||
pub const RHASH_PASS_AS_KEYWORDS: ruby_rhash_flags = 8192;
|
||||
pub const RHASH_PROC_DEFAULT: ruby_rhash_flags = 16384;
|
||||
|
||||
@@ -5230,7 +5230,7 @@ pub(crate) mod hir_build_tests {
|
||||
let iseq = crate::cruby::with_rubyvm(|| get_instance_method_iseq("Symbol", "name"));
|
||||
let function = iseq_to_hir(iseq).unwrap();
|
||||
assert_snapshot!(hir_string_function(&function), @"
|
||||
fn name@<internal:symbol>:
|
||||
fn to_s@<internal:symbol>:
|
||||
bb1():
|
||||
EntryPoint interpreter
|
||||
v1:BasicObject = LoadSelf
|
||||
|
||||
Reference in new issue
Block a user