diff options
author | Kevin Newton <[email protected]> | 2024-03-14 21:37:16 -0400 |
---|---|---|
committer | git <[email protected]> | 2024-03-15 12:31:26 +0000 |
commit | c45ad17fa1269aa882ed170760a5603a814d7c36 (patch) | |
tree | f40c85565e30f56d3587aeb11fab8bb2f710d250 | |
parent | 59785680ddda63d2f5a9ab02f35d3c4b43fc3013 (diff) |
[ruby/prism] Shareable constant nodes
https://github.com/ruby/prism/commit/473cfed6d0
-rw-r--r-- | lib/prism/translation/parser/compiler.rb | 5 | ||||
-rw-r--r-- | lib/prism/translation/ripper.rb | 5 | ||||
-rw-r--r-- | lib/prism/translation/ruby_parser.rb | 5 | ||||
-rw-r--r-- | prism/config.yml | 33 | ||||
-rw-r--r-- | prism/parser.h | 13 | ||||
-rw-r--r-- | prism/prism.c | 114 | ||||
-rw-r--r-- | prism/templates/src/diagnostic.c.erb | 1 | ||||
-rw-r--r-- | test/prism/location_test.rb | 12 |
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? |