summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNobuyoshi Nakada <nobu@ruby-lang.org>2020-12-19 20:42:58 +0900
committerNobuyoshi Nakada <nobu@ruby-lang.org>2020-12-23 13:50:42 +0900
commit7a094146e6ef38453a7e475450d90a9c83ea2277 (patch)
treedae133999b6504425ff1c9082974d813b7404b54
parentf14aaa2b1282d6dea9fad3d050e65828f1e269e1 (diff)
Changed shareable literal semantics [Feature #17397]
When `literal`, check if the literal about to be assigned to a constant is ractor-shareable, otherwise raise `Ractor::Error` at runtime instead of `SyntaxError`.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/3950
-rw-r--r--parse.y157
-rw-r--r--ractor.c11
-rw-r--r--test/ruby/test_parse.rb12
-rw-r--r--vm.c8
4 files changed, 149 insertions, 39 deletions
diff --git a/parse.y b/parse.y
index b3f07c33c2..50d24dfef7 100644
--- a/parse.y
+++ b/parse.y
@@ -11008,16 +11008,61 @@ mark_lvar_used(struct parser_params *p, NODE *rhs)
}
}
+static NODE *
+const_decl_path(struct parser_params *p, NODE **dest)
+{
+ NODE *n = *dest;
+ if (nd_type(n) != NODE_CALL) {
+ const YYLTYPE *loc = &n->nd_loc;
+ VALUE path;
+ if (n->nd_vid) {
+ path = rb_id2str(n->nd_vid);
+ }
+ else {
+ n = n->nd_else;
+ path = rb_ary_new();
+ for (; n && nd_type(n) == NODE_COLON2; n = n->nd_head) {
+ rb_ary_push(path, rb_id2str(n->nd_mid));
+ }
+ if (n && nd_type(n) == NODE_CONST) {
+ // Const::Name
+ rb_ary_push(path, rb_id2str(n->nd_vid));
+ }
+ else if (n && nd_type(n) == NODE_COLON3) {
+ // ::Const::Name
+ rb_ary_push(path, rb_str_new(0, 0));
+ }
+ else {
+ // expression::Name
+ rb_ary_push(path, rb_str_new_cstr("..."));
+ }
+ path = rb_ary_join(rb_ary_reverse(path), rb_str_new_cstr("::"));
+ path = rb_fstring(path);
+ }
+ *dest = n = NEW_LIT(path, loc);
+ }
+ return n;
+}
+
extern VALUE rb_mRubyVMFrozenCore;
static NODE *
-shareable_literal_node(struct parser_params *p, NODE *value, const YYLTYPE *loc)
+make_shareable_node(struct parser_params *p, NODE *value, const YYLTYPE *loc)
{
NODE *fcore = NEW_LIT(rb_mRubyVMFrozenCore, loc);
return NEW_CALL(fcore, rb_intern("make_shareable"),
NEW_LIST(value, loc), loc);
}
+static NODE *
+ensure_shareable_node(struct parser_params *p, NODE **dest, NODE *value, const YYLTYPE *loc)
+{
+ NODE *fcore = NEW_LIT(rb_mRubyVMFrozenCore, loc);
+ NODE *args = NEW_LIST(value, loc);
+ args = list_append(p, args, const_decl_path(p, dest));
+ return NEW_CALL(fcore, rb_intern("ensure_shareable"), args, loc);
+}
+
static int is_static_content(NODE *node);
static VALUE
@@ -11039,15 +11084,19 @@ shareable_literal_value(NODE *node)
}
}
+#ifndef SHAREABLE_BARE_EXPRESSION
+#define SHAREABLE_BARE_EXPRESSION 0
+#endif
+
VALUE rb_ractor_make_shareable(VALUE obj);
static NODE *
shareable_literal_constant(struct parser_params *p, enum shareability shareable,
- NODE *value, const YYLTYPE *loc, size_t level)
+ NODE **dest, NODE *value, const YYLTYPE *loc, size_t level)
{
# define shareable_literal_constant_next(n) \
- shareable_literal_constant(p, shareable, (n), &(n)->nd_loc, level+1)
- VALUE lit;
+ shareable_literal_constant(p, shareable, dest, (n), &(n)->nd_loc, level+1)
+ VALUE lit = Qnil;
if (!value) return 0;
enum node_type type = nd_type(value);
@@ -11056,44 +11105,52 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable,
case NODE_FALSE:
case NODE_NIL:
case NODE_LIT:
+ return value;
+
case NODE_DSTR:
- break;
+ if (shareable == shareable_literal) {
+ value = NEW_CALL(value, idUMinus, 0, loc);
+ }
+ return value;
case NODE_STR:
lit = rb_fstring(value->nd_lit);
nd_set_type(value, NODE_LIT);
RB_OBJ_WRITE(p->ast, &value->nd_lit, lit);
- break;
+ return value;
case NODE_ZLIST:
+ lit = rb_ary_new();
+ OBJ_FREEZE_RAW(lit);
nd_set_type(value, NODE_LIT);
- RB_OBJ_WRITE(p->ast, &value->nd_lit, rb_ary_new());
- break;
+ RB_OBJ_WRITE(p->ast, &value->nd_lit, lit);
+ return value;
case NODE_LIST:
lit = rb_ary_new();
for (NODE *n = value; n; n = n->nd_next) {
NODE *elt = n->nd_head;
- if (elt && !(elt = shareable_literal_constant_next(elt))) {
- if (lit) {
+ if (elt) {
+ elt = shareable_literal_constant_next(elt);
+ if (elt) {
+ n->nd_head = elt;
+ }
+ else if (RTEST(lit)) {
rb_ary_clear(lit);
lit = Qfalse;
}
}
- if (lit) {
+ if (RTEST(lit)) {
VALUE e = shareable_literal_value(elt);
if (e != Qundef) {
rb_ary_push(lit, e);
}
else {
rb_ary_clear(lit);
- lit = Qfalse;
+ lit = Qnil; /* make shareable at runtime */
}
}
}
- if (!lit) return 0;
- nd_set_type(value, NODE_LIT);
- RB_OBJ_WRITE(p->ast, &value->nd_lit, rb_ractor_make_shareable(lit));
break;
case NODE_HASH:
@@ -11102,14 +11159,27 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable,
for (NODE *n = value->nd_head; n; n = n->nd_next->nd_next) {
NODE *key = n->nd_head;
NODE *val = n->nd_next->nd_head;
- if ((key && !(key = shareable_literal_constant_next(key))) ||
- (val && !(val = shareable_literal_constant_next(val)))) {
- if (lit) {
+ if (key) {
+ key = shareable_literal_constant_next(key);
+ if (key) {
+ n->nd_head = key;
+ }
+ else if (RTEST(lit)) {
rb_hash_clear(lit);
lit = Qfalse;
}
}
- if (lit) {
+ if (val) {
+ val = shareable_literal_constant_next(val);
+ if (val) {
+ n->nd_next->nd_head = val;
+ }
+ else if (RTEST(lit)) {
+ rb_hash_clear(lit);
+ lit = Qfalse;
+ }
+ }
+ if (RTEST(lit)) {
VALUE k = shareable_literal_value(key);
VALUE v = shareable_literal_value(val);
if (k != Qundef && v != Qundef) {
@@ -11117,26 +11187,39 @@ shareable_literal_constant(struct parser_params *p, enum shareability shareable,
}
else {
rb_hash_clear(lit);
- lit = Qfalse;
+ lit = Qnil; /* make shareable at runtime */
}
}
}
- if (!lit) return 0;
- nd_set_type(value, NODE_LIT);
- RB_OBJ_WRITE(p->ast, &value->nd_lit, rb_ractor_make_shareable(lit));
break;
default:
- if (shareable == shareable_literal && level > 0)
- yyerror1(loc, "unshareable expression");
+ if (shareable == shareable_literal &&
+ (SHAREABLE_BARE_EXPRESSION || level > 0)) {
+ return ensure_shareable_node(p, dest, value, loc);
+ }
return 0;
}
+
+ /* Array or Hash */
+ if (!lit) return 0;
+ if (NIL_P(lit)) {
+ // if shareable_literal, all elements should have been ensured
+ // as shareable
+ value = make_shareable_node(p, value, loc);
+ }
+ else {
+ nd_set_type(value, NODE_LIT);
+ RB_OBJ_WRITE(p->ast, &value->nd_lit, rb_ractor_make_shareable(lit));
+ }
+
return value;
# undef shareable_literal_constant_next
}
static NODE *
-shareable_constant_value(struct parser_params *p, NODE *value, enum shareability shareable, const YYLTYPE *loc)
+shareable_constant_value(struct parser_params *p, enum shareability shareable,
+ NODE *lhs, NODE *value, const YYLTYPE *loc)
{
if (!value) return 0;
switch (shareable) {
@@ -11144,18 +11227,24 @@ shareable_constant_value(struct parser_params *p, NODE *value, enum shareability
return value;
case shareable_literal:
+ {
+ NODE *lit = shareable_literal_constant(p, shareable, &lhs, value, loc, 0);
+ if (lit) return lit;
+ return value;
+ }
+ break;
+
case shareable_everything:
{
- NODE *lit = shareable_literal_constant(p, shareable, value, loc, 0);
+ NODE *lit = shareable_literal_constant(p, shareable, &lhs, value, loc, 0);
if (lit) return lit;
+ return make_shareable_node(p, value, loc);
}
break;
default:
UNREACHABLE_RETURN(0);
}
-
- return shareable_literal_node(p, value, loc);
}
static NODE *
@@ -11165,7 +11254,7 @@ node_assign(struct parser_params *p, NODE *lhs, NODE *rhs, struct lex_context ct
switch (nd_type(lhs)) {
case NODE_CDECL:
- rhs = shareable_constant_value(p, rhs, ctxt.shareable_constant_value, loc);
+ rhs = shareable_constant_value(p, ctxt.shareable_constant_value, lhs, rhs, loc);
/* fallthru */
case NODE_GASGN:
@@ -12110,7 +12199,7 @@ new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_c
}
}
if (op == tOROP) {
- rhs = shareable_constant_value(p, rhs, shareable, &rhs->nd_loc);
+ rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc);
lhs->nd_value = rhs;
nd_set_loc(lhs, loc);
asgn = NEW_OP_ASGN_OR(gettable(p, vid, &lhs_loc), lhs, loc);
@@ -12125,7 +12214,7 @@ new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_c
}
else if (op == tANDOP) {
if (shareable) {
- rhs = shareable_constant_value(p, rhs, shareable, &rhs->nd_loc);
+ rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc);
}
lhs->nd_value = rhs;
nd_set_loc(lhs, loc);
@@ -12135,7 +12224,7 @@ new_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct lex_c
asgn = lhs;
rhs = NEW_CALL(gettable(p, vid, &lhs_loc), op, NEW_LIST(rhs, &rhs->nd_loc), loc);
if (shareable) {
- rhs = shareable_constant_value(p, rhs, shareable, &rhs->nd_loc);
+ rhs = shareable_constant_value(p, shareable, lhs, rhs, &rhs->nd_loc);
}
asgn->nd_value = rhs;
nd_set_loc(asgn, loc);
@@ -12182,7 +12271,7 @@ new_const_op_assign(struct parser_params *p, NODE *lhs, ID op, NODE *rhs, struct
NODE *asgn;
if (lhs) {
- rhs = shareable_constant_value(p, rhs, ctxt.shareable_constant_value, loc);
+ rhs = shareable_constant_value(p, ctxt.shareable_constant_value, lhs, rhs, loc);
asgn = NEW_OP_CDECL(lhs, op, rhs, loc);
}
else {
diff --git a/ractor.c b/ractor.c
index 7675a09832..484b5c78b9 100644
--- a/ractor.c
+++ b/ractor.c
@@ -2442,6 +2442,17 @@ rb_ractor_make_shareable_copy(VALUE obj)
return copy;
}
+VALUE
+rb_ractor_ensure_shareable(VALUE obj, VALUE name)
+{
+ if (!RTEST(rb_ractor_shareable_p(obj))) {
+ VALUE message = rb_sprintf("cannot assign unshareable object to %"PRIsVALUE,
+ name);
+ rb_exc_raise(rb_exc_new_str(rb_eRactorError, message));
+ }
+ return obj;
+}
+
static enum obj_traverse_iterator_result
shareable_p_enter(VALUE obj)
{
diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb
index ecacdebc70..0a1e3edc66 100644
--- a/test/ruby/test_parse.rb
+++ b/test/ruby/test_parse.rb
@@ -1213,11 +1213,13 @@ x = __ENCODING__
assert_equal([1], a[0])
assert_send([Ractor, :shareable?, a[0]])
- assert_syntax_error("#{<<~"begin;"}\n#{<<~'end;'}", /unshareable expression/)
- begin;
- # shareable_constant_value: literal
- C = ["Not " + "shareable"]
- end;
+ assert_raise_with_message(Ractor::Error, /unshareable/) do
+ Class.new.class_eval("#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ # shareable_constant_value: literal
+ C = ["Not " + "shareable"]
+ end;
+ end
end
=begin
diff --git a/vm.c b/vm.c
index 3e775f8a5f..a30ba9c046 100644
--- a/vm.c
+++ b/vm.c
@@ -995,6 +995,7 @@ collect_outer_variable_names(ID id, VALUE val, void *ptr)
}
VALUE rb_ractor_make_shareable(VALUE obj);
+VALUE rb_ractor_ensure_shareable(VALUE obj, VALUE name);
static const rb_env_t *
env_copy(const VALUE *src_ep, VALUE read_only_variables)
@@ -3181,6 +3182,12 @@ m_core_make_shareable(VALUE recv, VALUE obj)
}
static VALUE
+m_core_ensure_shareable(VALUE recv, VALUE obj, VALUE name)
+{
+ return rb_ractor_ensure_shareable(obj, name);
+}
+
+static VALUE
core_hash_merge_kwd(VALUE hash, VALUE kw)
{
rb_hash_foreach(rb_to_hash_type(kw), kwmerge_i, hash);
@@ -3345,6 +3352,7 @@ Init_VM(void)
rb_define_method_id(klass, idProc, f_proc, 0);
rb_define_method_id(klass, idLambda, f_lambda, 0);
rb_define_method(klass, "make_shareable", m_core_make_shareable, 1);
+ rb_define_method(klass, "ensure_shareable", m_core_ensure_shareable, 2);
rb_obj_freeze(fcore);
RBASIC_CLEAR_CLASS(klass);
rb_obj_freeze(klass);