summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Newton <kddnewton@gmail.com>2024-03-14 21:37:16 -0400
committergit <svn-admin@ruby-lang.org>2024-03-15 12:31:26 +0000
commitc45ad17fa1269aa882ed170760a5603a814d7c36 (patch)
treef40c85565e30f56d3587aeb11fab8bb2f710d250
parent59785680ddda63d2f5a9ab02f35d3c4b43fc3013 (diff)
[ruby/prism] Shareable constant nodes
https://github.com/ruby/prism/commit/473cfed6d0
-rw-r--r--lib/prism/translation/parser/compiler.rb5
-rw-r--r--lib/prism/translation/ripper.rb5
-rw-r--r--lib/prism/translation/ruby_parser.rb5
-rw-r--r--prism/config.yml33
-rw-r--r--prism/parser.h13
-rw-r--r--prism/prism.c114
-rw-r--r--prism/templates/src/diagnostic.c.erb1
-rw-r--r--test/prism/location_test.rb12
8 files changed, 172 insertions, 16 deletions
diff --git a/lib/prism/translation/parser/compiler.rb b/lib/prism/translation/parser/compiler.rb
index 50c47c068e..4eb2c4a8da 100644
--- a/lib/prism/translation/parser/compiler.rb
+++ b/lib/prism/translation/parser/compiler.rb
@@ -1423,6 +1423,11 @@ module Prism
builder.self(token(node.location))
end
+ # A shareable constant.
+ def visit_shareable_constant_node(node)
+ visit(node.write)
+ end
+
# class << self; end
# ^^^^^^^^^^^^^^^^^^
def visit_singleton_class_node(node)
diff --git a/lib/prism/translation/ripper.rb b/lib/prism/translation/ripper.rb
index 9f269f9eb8..3c06f6a40d 100644
--- a/lib/prism/translation/ripper.rb
+++ b/lib/prism/translation/ripper.rb
@@ -2858,6 +2858,11 @@ module Prism
on_var_ref(on_kw("self"))
end
+ # A shareable constant.
+ def visit_shareable_constant_node(node)
+ visit(node.write)
+ end
+
# class << self; end
# ^^^^^^^^^^^^^^^^^^
def visit_singleton_class_node(node)
diff --git a/lib/prism/translation/ruby_parser.rb b/lib/prism/translation/ruby_parser.rb
index 4664061841..108e6c6928 100644
--- a/lib/prism/translation/ruby_parser.rb
+++ b/lib/prism/translation/ruby_parser.rb
@@ -1274,6 +1274,11 @@ module Prism
s(node, :self)
end
+ # A shareable constant.
+ def visit_shareable_constant_node(node)
+ visit(node.write)
+ end
+
# class << self; end
# ^^^^^^^^^^^^^^^^^^
def visit_singleton_class_node(node)
diff --git a/prism/config.yml b/prism/config.yml
index 5f0741bce3..ab4aeae7d7 100644
--- a/prism/config.yml
+++ b/prism/config.yml
@@ -248,6 +248,7 @@ warnings:
- INTEGER_IN_FLIP_FLOP
- INVALID_CHARACTER
- INVALID_NUMBERED_REFERENCE
+ - INVALID_SHAREABLE_CONSTANT_VALUE
- KEYWORD_EOL
- LITERAL_IN_CONDITION_DEFAULT
- LITERAL_IN_CONDITION_VERBOSE
@@ -667,6 +668,15 @@ flags:
- name: FORCED_US_ASCII_ENCODING
comment: "internal bytes forced the encoding to US-ASCII"
comment: Flags for regular expression and match last line nodes.
+ - name: ShareableConstantNodeFlags
+ values:
+ - name: LITERAL
+ comment: "constant writes that should be modified with shareable constant value literal"
+ - name: EXPERIMENTAL_EVERYTHING
+ comment: "constant writes that should be modified with shareable constant value experimental everything"
+ - name: EXPERIMENTAL_COPY
+ comment: "constant writes that should be modified with shareable constant value experimental copy"
+ comment: Flags for shareable constant nodes.
- name: StringFlags
values:
- name: FORCED_UTF8_ENCODING
@@ -3063,6 +3073,29 @@ nodes:
self
^^^^
+ - name: ShareableConstantNode
+ fields:
+ - name: flags
+ type: flags
+ kind: ShareableConstantNodeFlags
+ - name: write
+ type: node
+ kind:
+ - ConstantWriteNode
+ - ConstantAndWriteNode
+ - ConstantOrWriteNode
+ - ConstantOperatorWriteNode
+ - ConstantPathWriteNode
+ - ConstantPathAndWriteNode
+ - ConstantPathOrWriteNode
+ - ConstantPathOperatorWriteNode
+ comment: The constant write that should be modified with the shareability state.
+ comment: |
+ This node wraps a constant write to indicate that when the value is written, it should have its shareability state modified.
+
+ # shareable_constant_value: literal
+ C = { a: 1 }
+ ^^^^^^^^^^^^
- name: SingletonClassNode
fields:
- name: locals
diff --git a/prism/parser.h b/prism/parser.h
index 619358c5ba..b685fa377d 100644
--- a/prism/parser.h
+++ b/prism/parser.h
@@ -448,6 +448,13 @@ typedef struct {
void (*callback)(void *data, pm_parser_t *parser, pm_token_t *token);
} pm_lex_callback_t;
+/** The type of shareable constant value that can be set. */
+typedef uint8_t pm_shareable_constant_value_t;
+static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_NONE = 0x0;
+static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_LITERAL = 0x1;
+static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING = 0x2;
+static const pm_shareable_constant_value_t PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY = 0x4;
+
/**
* This struct represents a node in a linked list of scopes. Some scopes can see
* into their parent scopes, while others cannot.
@@ -488,6 +495,12 @@ typedef struct pm_scope {
int8_t numbered_parameters;
/**
+ * The current state of constant shareability for this scope. This is
+ * changed by magic shareable_constant_value comments.
+ */
+ pm_shareable_constant_value_t shareable_constant;
+
+ /**
* A boolean indicating whether or not this scope can see into its parent.
* If closed is true, then the scope cannot see into its parent.
*/
diff --git a/prism/prism.c b/prism/prism.c
index a492eaa329..96435a38b1 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -5732,6 +5732,25 @@ pm_self_node_create(pm_parser_t *parser, const pm_token_t *token) {
}
/**
+ * Allocate and initialize a new ShareableConstantNode node.
+ */
+static pm_shareable_constant_node_t *
+pm_shareable_constant_node_create(pm_parser_t *parser, pm_node_t *write, pm_shareable_constant_value_t value) {
+ pm_shareable_constant_node_t *node = PM_ALLOC_NODE(parser, pm_shareable_constant_node_t);
+
+ *node = (pm_shareable_constant_node_t) {
+ {
+ .type = PM_SHAREABLE_CONSTANT_NODE,
+ .flags = (pm_node_flags_t) value,
+ .location = PM_LOCATION_NODE_VALUE(write)
+ },
+ .write = write
+ };
+
+ return node;
+}
+
+/**
* Allocate a new SingletonClassNode node.
*/
static pm_singleton_class_node_t *
@@ -6745,6 +6764,7 @@ pm_parser_scope_push(pm_parser_t *parser, bool closed) {
.locals = { 0 },
.parameters = PM_SCOPE_PARAMETERS_NONE,
.numbered_parameters = PM_SCOPE_NUMBERED_PARAMETERS_NONE,
+ .shareable_constant = (closed || parser->current_scope == NULL) ? PM_SCOPE_SHAREABLE_CONSTANT_NONE : parser->current_scope->shareable_constant,
.closed = closed
};
@@ -6791,6 +6811,27 @@ pm_parser_scope_forwarding_keywords_check(pm_parser_t *parser, const pm_token_t
}
/**
+ * Get the current state of constant shareability.
+ */
+static inline pm_shareable_constant_value_t
+pm_parser_scope_shareable_constant_get(pm_parser_t *parser) {
+ return parser->current_scope->shareable_constant;
+}
+
+/**
+ * Set the current state of constant shareability. We'll set it on all of the
+ * open scopes so that reads are quick.
+ */
+static void
+pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constant_value_t shareable_constant) {
+ pm_scope_t *scope = parser->current_scope;
+
+ do {
+ scope->shareable_constant = shareable_constant;
+ } while (!scope->closed && (scope = scope->previous) != NULL);
+}
+
+/**
* Save the current param name as the return value and set it to the given
* constant id.
*/
@@ -7345,6 +7386,28 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
}
}
+ // If we have hit a ractor pragma, attempt to lex that.
+ uint32_t value_length = (uint32_t) (value_end - value_start);
+ if (key_length == 24 && pm_strncasecmp(key_source, (const uint8_t *) "shareable_constant_value", 24) == 0) {
+ if (value_length == 4 && pm_strncasecmp(value_start, (const uint8_t *) "none", 4) == 0) {
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_NONE);
+ } else if (value_length == 7 && pm_strncasecmp(value_start, (const uint8_t *) "literal", 7) == 0) {
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_LITERAL);
+ } else if (value_length == 23 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_everything", 23) == 0) {
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_EVERYTHING);
+ } else if (value_length == 17 && pm_strncasecmp(value_start, (const uint8_t *) "experimental_copy", 17) == 0) {
+ pm_parser_scope_shareable_constant_set(parser, PM_SCOPE_SHAREABLE_CONSTANT_EXPERIMENTAL_COPY);
+ } else {
+ PM_PARSER_WARN_TOKEN_FORMAT(
+ parser,
+ parser->current,
+ PM_WARN_INVALID_SHAREABLE_CONSTANT_VALUE,
+ (int) value_length,
+ (const char *) value_start
+ );
+ }
+ }
+
// When we're done, we want to free the string in case we had to
// allocate memory for it.
pm_string_free(&key);
@@ -11992,6 +12055,21 @@ parse_target_validate(pm_parser_t *parser, pm_node_t *target) {
}
/**
+ * Potentially wrap a constant write node in a shareable constant node depending
+ * on the current state.
+ */
+static pm_node_t *
+parse_shareable_constant_write(pm_parser_t *parser, pm_node_t *write) {
+ pm_shareable_constant_value_t shareable_constant = pm_parser_scope_shareable_constant_get(parser);
+
+ if (shareable_constant != PM_SCOPE_SHAREABLE_CONSTANT_NONE) {
+ return (pm_node_t *) pm_shareable_constant_node_create(parser, write, shareable_constant);
+ }
+
+ return write;
+}
+
+/**
* Convert the given node into a valid write node.
*/
static pm_node_t *
@@ -12005,15 +12083,17 @@ parse_write(pm_parser_t *parser, pm_node_t *target, pm_token_t *operator, pm_nod
pm_node_destroy(parser, target);
return (pm_node_t *) node;
}
- case PM_CONSTANT_PATH_NODE:
- return (pm_node_t *) pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value);
+ case PM_CONSTANT_PATH_NODE: {
+ pm_node_t *node = (pm_node_t *) pm_constant_path_write_node_create(parser, (pm_constant_path_node_t *) target, operator, value);
+ return parse_shareable_constant_write(parser, node);
+ }
case PM_CONSTANT_READ_NODE: {
- pm_constant_write_node_t *node = pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value);
+ pm_node_t *node = (pm_node_t *) pm_constant_write_node_create(parser, (pm_constant_read_node_t *) target, operator, value);
if (context_def_p(parser)) {
- pm_parser_err_node(parser, (pm_node_t *) node, PM_ERR_WRITE_TARGET_IN_METHOD);
+ pm_parser_err_node(parser, node, PM_ERR_WRITE_TARGET_IN_METHOD);
}
pm_node_destroy(parser, target);
- return (pm_node_t *) node;
+ return parse_shareable_constant_write(parser, node);
}
case PM_BACK_REFERENCE_READ_NODE:
case PM_NUMBERED_REFERENCE_READ_NODE:
@@ -17954,16 +18034,18 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
parser_lex(parser);
pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
- return (pm_node_t *) pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value);
+ pm_node_t *write = (pm_node_t *) pm_constant_path_and_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value);
+
+ return parse_shareable_constant_write(parser, write);
}
case PM_CONSTANT_READ_NODE: {
parser_lex(parser);
pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ);
- pm_node_t *result = (pm_node_t *) pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value);
+ pm_node_t *write = (pm_node_t *) pm_constant_and_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value);
pm_node_destroy(parser, node);
- return result;
+ return parse_shareable_constant_write(parser, write);
}
case PM_INSTANCE_VARIABLE_READ_NODE: {
parser_lex(parser);
@@ -18065,16 +18147,18 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
parser_lex(parser);
pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
- return (pm_node_t *) pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value);
+ pm_node_t *write = (pm_node_t *) pm_constant_path_or_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value);
+
+ return parse_shareable_constant_write(parser, write);
}
case PM_CONSTANT_READ_NODE: {
parser_lex(parser);
pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ);
- pm_node_t *result = (pm_node_t *) pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value);
+ pm_node_t *write = (pm_node_t *) pm_constant_or_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value);
pm_node_destroy(parser, node);
- return result;
+ return parse_shareable_constant_write(parser, write);
}
case PM_INSTANCE_VARIABLE_READ_NODE: {
parser_lex(parser);
@@ -18186,16 +18270,18 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
parser_lex(parser);
pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
- return (pm_node_t *) pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value);
+ pm_node_t *write = (pm_node_t *) pm_constant_path_operator_write_node_create(parser, (pm_constant_path_node_t *) node, &token, value);
+
+ return parse_shareable_constant_write(parser, write);
}
case PM_CONSTANT_READ_NODE: {
parser_lex(parser);
pm_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR);
- pm_node_t *result = (pm_node_t *) pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value);
+ pm_node_t *write = (pm_node_t *) pm_constant_operator_write_node_create(parser, (pm_constant_read_node_t *) node, &token, value);
pm_node_destroy(parser, node);
- return result;
+ return parse_shareable_constant_write(parser, write);
}
case PM_INSTANCE_VARIABLE_READ_NODE: {
parser_lex(parser);
diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb
index 2a14c0dcb3..1c3114479e 100644
--- a/prism/templates/src/diagnostic.c.erb
+++ b/prism/templates/src/diagnostic.c.erb
@@ -327,6 +327,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_WARN_FLOAT_OUT_OF_RANGE] = { "Float %.*s%s out of range", PM_WARNING_LEVEL_VERBOSE },
[PM_WARN_INTEGER_IN_FLIP_FLOP] = { "integer literal in flip-flop", PM_WARNING_LEVEL_DEFAULT },
[PM_WARN_INVALID_CHARACTER] = { "invalid character syntax; use %s%s%s", PM_WARNING_LEVEL_DEFAULT },
+ [PM_WARN_INVALID_SHAREABLE_CONSTANT_VALUE] = { "invalid value for shareable_constant_value: %.*s", PM_WARNING_LEVEL_VERBOSE },
[PM_WARN_INVALID_NUMBERED_REFERENCE] = { "'%.*s' is too big for a number variable, always nil", PM_WARNING_LEVEL_DEFAULT },
[PM_WARN_KEYWORD_EOL] = { "`%.*s` at the end of line without an expression", PM_WARNING_LEVEL_VERBOSE },
[PM_WARN_LITERAL_IN_CONDITION_DEFAULT] = { "%sliteral in %s", PM_WARNING_LEVEL_DEFAULT },
diff --git a/test/prism/location_test.rb b/test/prism/location_test.rb
index 46ec7f4e3f..c7ce248b56 100644
--- a/test/prism/location_test.rb
+++ b/test/prism/location_test.rb
@@ -781,6 +781,15 @@ module Prism
assert_location(SelfNode, "self")
end
+ def test_ShareableConstantNode
+ source = <<~RUBY
+ # shareable_constant_value: literal
+ C = { foo: 1 }
+ RUBY
+
+ assert_location(ShareableConstantNode, source, 36...50)
+ end
+
def test_SingletonClassNode
assert_location(SingletonClassNode, "class << self; end")
end
@@ -915,8 +924,7 @@ module Prism
def assert_location(kind, source, expected = 0...source.length, **options)
result = Prism.parse(source, **options)
- assert_equal [], result.comments
- assert_equal [], result.errors
+ assert result.success?
node = result.value.statements.body.last
node = yield node if block_given?