Skip to content

Commit 628b631

Browse files
committed
WIP
1 parent a3a4469 commit 628b631

File tree

16 files changed

+160
-49
lines changed

16 files changed

+160
-49
lines changed

error.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3856,10 +3856,24 @@ inspect_frozen_obj(VALUE obj, VALUE mesg, int recur)
38563856
return mesg;
38573857
}
38583858

3859+
static void
3860+
str_check_chilled(VALUE str)
3861+
{
3862+
if (FL_TEST_RAW(str, FL_USER3)) { // STR_CHILLED
3863+
FL_UNSET_RAW(str, FL_USER3| FL_FREEZE); // STR_CHILLED
3864+
rb_warning("literal string will be frozen in the future");
3865+
}
3866+
}
3867+
38593868
void
38603869
rb_error_frozen_object(VALUE frozen_obj)
38613870
{
38623871
rb_yjit_lazy_push_frame(GET_EC()->cfp->pc);
3872+
/* rb_bug("test"); */
3873+
if (RB_TYPE_P(frozen_obj, T_STRING) && RB_FL_TEST_RAW(frozen_obj, FL_USER3)) {
3874+
str_check_chilled(frozen_obj);
3875+
return;
3876+
}
38633877
VALUE debug_info;
38643878
const ID created_info = id_debug_created_info;
38653879
VALUE mesg = rb_sprintf("can't modify frozen %"PRIsVALUE": ",

ext/objspace/objspace_dump.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,8 @@ dump_object(VALUE obj, struct dump_config *dc)
476476
dump_append(dc, ", \"embedded\":true");
477477
if (FL_TEST(obj, RSTRING_FSTR))
478478
dump_append(dc, ", \"fstring\":true");
479+
if (FL_TEST(obj, STR_CHILLED))
480+
dump_append(dc, ", \"chilled\":true");
479481
if (STR_SHARED_P(obj))
480482
dump_append(dc, ", \"shared\":true");
481483
else

include/ruby/internal/fl_type.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,9 @@ static inline void
916916
RB_OBJ_FREEZE_RAW(VALUE obj)
917917
{
918918
RB_FL_SET_RAW(obj, RUBY_FL_FREEZE);
919+
if (RB_TYPE_P(obj, T_STRING)) {
920+
RB_FL_UNSET_RAW(obj, FL_USER3);
921+
}
919922
}
920923

921924
RUBY_SYMBOL_EXPORT_BEGIN

include/ruby/internal/intern/error.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,6 @@ RBIMPL_ATTR_NONNULL(())
190190
*/
191191
void rb_error_frozen(const char *what);
192192

193-
RBIMPL_ATTR_NORETURN()
194193
/**
195194
* Identical to rb_error_frozen(), except it takes arbitrary Ruby object
196195
* instead of C's string.

internal/string.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#define STR_NOEMBED FL_USER1
1919
#define STR_SHARED FL_USER2 /* = ELTS_SHARED */
20+
#define STR_CHILLED FL_USER3
2021

2122
#ifdef rb_fstring_cstr
2223
# undef rb_fstring_cstr

object.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,10 @@ rb_obj_clone_setup(VALUE obj, VALUE clone, VALUE kwfreeze)
502502
case Qnil:
503503
rb_funcall(clone, id_init_clone, 1, obj);
504504
RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_FREEZE;
505-
if (RB_OBJ_FROZEN(obj)) {
505+
if (RB_TYPE_P(obj, T_STRING) && FL_TEST_RAW(obj, FL_USER3)) { // STR_CHILLED
506+
RBASIC(clone)->flags |= RBASIC(obj)->flags & FL_USER3; // STR_CHILLED
507+
}
508+
else if (RB_OBJ_FROZEN(obj)) {
506509
rb_shape_t * next_shape = rb_shape_transition_shape_frozen(clone);
507510
if (!rb_shape_obj_too_complex(clone) && next_shape->type == SHAPE_OBJ_TOO_COMPLEX) {
508511
rb_evict_ivars_to_hash(clone);

prism_compile.c

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -820,11 +820,19 @@ pm_interpolated_node_compile(pm_node_list_t *parts, rb_iseq_t *iseq, NODE dummy_
820820
current_string = rb_enc_str_new(NULL, 0, scope_node->encoding);
821821
}
822822

823-
if (ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
824-
ADD_INSN1(ret, &dummy_line_node, putobject, rb_str_freeze(current_string));
825-
}
826-
else {
823+
switch (ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
824+
case -1: // unspecified
825+
ADD_INSN1(ret, &dummy_line_node, putchilledstring, rb_str_freeze(current_string));
826+
break;
827+
case 0: // disabled
827828
ADD_INSN1(ret, &dummy_line_node, putstring, rb_str_freeze(current_string));
829+
break;
830+
case 1: // enabled
831+
ADD_INSN1(ret, &dummy_line_node, putobject, rb_str_freeze(current_string));
832+
break;
833+
default:
834+
rb_bug("invalid frozen_string_literal");
835+
break;
828836
}
829837

830838
current_string = Qnil;
@@ -842,11 +850,19 @@ pm_interpolated_node_compile(pm_node_list_t *parts, rb_iseq_t *iseq, NODE dummy_
842850
if (RTEST(current_string)) {
843851
current_string = rb_fstring(current_string);
844852

845-
if (ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
846-
ADD_INSN1(ret, &dummy_line_node, putobject, current_string);
847-
}
848-
else {
853+
switch (ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
854+
case -1: // unspecified
855+
ADD_INSN1(ret, &dummy_line_node, putchilledstring, current_string);
856+
break;
857+
case 0: // disabled
849858
ADD_INSN1(ret, &dummy_line_node, putstring, current_string);
859+
break;
860+
case 1: // enabled
861+
ADD_INSN1(ret, &dummy_line_node, putobject, current_string);
862+
break;
863+
default:
864+
rb_bug("invalid frozen_string_literal");
865+
break;
850866
}
851867

852868
current_string = Qnil;
@@ -4019,7 +4035,7 @@ pm_opt_aref_with_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
40194035
((const pm_arguments_node_t *) node->arguments)->arguments.size == 1 &&
40204036
PM_NODE_TYPE_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_NODE) &&
40214037
node->block == NULL &&
4022-
!ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal &&
4038+
!frozen_string_literal_p(iseq) &&
40234039
ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
40244040
);
40254041
}
@@ -4038,7 +4054,7 @@ pm_opt_aset_with_p(const rb_iseq_t *iseq, const pm_call_node_t *node)
40384054
((const pm_arguments_node_t *) node->arguments)->arguments.size == 2 &&
40394055
PM_NODE_TYPE_P(((const pm_arguments_node_t *) node->arguments)->arguments.nodes[0], PM_STRING_NODE) &&
40404056
node->block == NULL &&
4041-
!ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal &&
4057+
!frozen_string_literal_p(iseq) &&
40424058
ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction
40434059
);
40444060
}
@@ -7958,9 +7974,13 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
79587974
if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_FROZEN) || ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
79597975
PUSH_INSN1(ret, location, putobject, value);
79607976
}
7961-
else {
7977+
else if (PM_NODE_FLAG_P(node, PM_STRING_FLAGS_MUTABLE)) {
79627978
PUSH_INSN1(ret, location, putstring, value);
79637979
}
7980+
else {
7981+
// TODO: need a flag for explicit false
7982+
PUSH_INSN1(ret, location, putchilledstring, value);
7983+
}
79647984
}
79657985
return;
79667986
}

ractor.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2986,7 +2986,7 @@ static int
29862986
frozen_shareable_p(VALUE obj, bool *made_shareable)
29872987
{
29882988
if (!RB_TYPE_P(obj, T_DATA)) {
2989-
return true;
2989+
return !(RB_TYPE_P(obj, T_STRING) && FL_TEST_RAW(obj, FL_USER3));
29902990
}
29912991
else if (RTYPEDDATA_P(obj)) {
29922992
const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
@@ -3014,7 +3014,7 @@ make_shareable_check_shareable(VALUE obj)
30143014
if (rb_ractor_shareable_p(obj)) {
30153015
return traverse_skip;
30163016
}
3017-
else if (!frozen_shareable_p(obj, &made_shareable)) {
3017+
else if (!frozen_shareable_p(obj, &made_shareable) && !RB_TYPE_P(obj, T_STRING)) {
30183018
if (made_shareable) {
30193019
return traverse_skip;
30203020
}

spec/ruby/core/kernel/eval_spec.rb

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -350,9 +350,11 @@ class EvalSpecs
350350
end
351351

352352
it "allows a magic encoding comment and a subsequent frozen_string_literal magic comment" do
353+
frozen_string_default = "test".frozen?
354+
353355
code = <<CODE.b
354356
# encoding: UTF-8
355-
# frozen_string_literal: true
357+
# frozen_string_literal: #{!frozen_string_default}
356358
class EvalSpecs
357359
Vπstring = "frozen"
358360
end
@@ -362,7 +364,7 @@ class EvalSpecs
362364
EvalSpecs.constants(false).should include(:"Vπstring")
363365
EvalSpecs::Vπstring.should == "frozen"
364366
EvalSpecs::Vπstring.encoding.should == Encoding::UTF_8
365-
EvalSpecs::Vπstring.frozen?.should be_true
367+
EvalSpecs::Vπstring.frozen?.should == !frozen_string_default
366368
end
367369

368370
it "allows a magic encoding comment and a frozen_string_literal magic comment on the same line in emacs style" do
@@ -381,8 +383,9 @@ class EvalSpecs
381383
end
382384

383385
it "ignores the magic encoding comment if it is after a frozen_string_literal magic comment" do
386+
frozen_string_default = "test".frozen?
384387
code = <<CODE.b
385-
# frozen_string_literal: true
388+
# frozen_string_literal: #{!frozen_string_default}
386389
# encoding: UTF-8
387390
class EvalSpecs
388391
Vπfrozen_first = "frozen"
@@ -396,24 +399,24 @@ class EvalSpecs
396399
value = EvalSpecs.const_get(binary_constant)
397400
value.should == "frozen"
398401
value.encoding.should == Encoding::BINARY
399-
value.frozen?.should be_true
402+
value.frozen?.should == !frozen_string_default
400403
end
401404

402405
it "ignores the frozen_string_literal magic comment if it appears after a token and warns if $VERBOSE is true" do
403-
default_frozen_string_literal = "test".frozen?
406+
frozen_string_default = "test".frozen?
404407
code = <<CODE
405408
some_token_before_magic_comment = :anything
406-
# frozen_string_literal: true
409+
# frozen_string_literal: #{!frozen_string_default}
407410
class EvalSpecs
408411
Vπstring_not_frozen = "not frozen"
409412
end
410413
CODE
411414
-> { eval(code) }.should complain(/warning: [`']frozen_string_literal' is ignored after any tokens/, verbose: true)
412-
EvalSpecs::Vπstring_not_frozen.frozen?.should == default_frozen_string_literal
415+
EvalSpecs::Vπstring_not_frozen.frozen?.should == frozen_string_default
413416
EvalSpecs.send :remove_const, :Vπstring_not_frozen
414417

415418
-> { eval(code) }.should_not complain(verbose: false)
416-
EvalSpecs::Vπstring_not_frozen.frozen?.should == default_frozen_string_literal
419+
EvalSpecs::Vπstring_not_frozen.frozen?.should == frozen_string_default
417420
EvalSpecs.send :remove_const, :Vπstring_not_frozen
418421
end
419422
end

spec/ruby/core/string/uplus_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
end
1515

1616
it 'returns self if the String is not frozen' do
17-
input = 'foo'
17+
input = 'foo'.dup
1818
output = +input
1919

2020
output.equal?(input).should == true

0 commit comments

Comments
 (0)