diff --git a/doc/syntax/pattern_matching.rdoc b/doc/syntax/pattern_matching.rdoc
index c43919ba14e5f4..06aae26d498e82 100644
--- a/doc/syntax/pattern_matching.rdoc
+++ b/doc/syntax/pattern_matching.rdoc
@@ -253,11 +253,11 @@ The "rest" part of a pattern also can be bound to a variable:
case {a: 1, b: 2}
in {a: } | Array
+ # ^ SyntaxError (variable capture in alternative pattern)
"matched: #{a}"
else
"not matched"
end
- # SyntaxError (illegal variable in alternative pattern (a))
Variables that start with _ are the only exclusions from this rule:
diff --git a/ext/json/parser/extconf.rb b/ext/json/parser/extconf.rb
index cda385767c3cc7..079b37382d7bc3 100644
--- a/ext/json/parser/extconf.rb
+++ b/ext/json/parser/extconf.rb
@@ -6,7 +6,6 @@
have_func("rb_str_to_interned_str", "ruby.h") # RUBY_VERSION >= 3.0
have_func("rb_hash_new_capa", "ruby.h") # RUBY_VERSION >= 3.2
have_func("rb_hash_bulk_insert", "ruby.h") # Missing on TruffleRuby
-have_func("strnlen", "string.h") # Missing on Solaris 10
append_cflags("-std=c99")
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
index 9df04ce0079fad..2bf3ae0eb38dd6 100644
--- a/ext/json/parser/parser.c
+++ b/ext/json/parser/parser.c
@@ -296,15 +296,6 @@ static void rvalue_stack_eagerly_release(VALUE handle)
}
}
-
-#ifndef HAVE_STRNLEN
-static size_t strnlen(const char *s, size_t maxlen)
-{
- char *p;
- return ((p = memchr(s, '\0', maxlen)) ? p - s : maxlen);
-}
-#endif
-
static int convert_UTF32_to_UTF8(char *buf, uint32_t ch)
{
int len = 1;
diff --git a/gc.rb b/gc.rb
index f5e62eed6058fa..f944c653b5b467 100644
--- a/gc.rb
+++ b/gc.rb
@@ -193,7 +193,10 @@ def self.count
# - +:time+:
# The total time spent in garbage collections (in milliseconds).
# - +:heap_allocated_pages+:
- # The total number of +:heap_eden_pages+ + +:heap_tomb_pages+.
+ # The total number of allocated pages.
+ # - +:heap_empty_pages+:
+ # - +:heap_allocated_pages+:
+ # The number of pages with no live objects, and that could be released to the system.
# - +:heap_sorted_length+:
# The number of pages that can fit into the buffer that holds references to all pages.
# - +:heap_allocatable_pages+:
@@ -210,8 +213,6 @@ def self.count
# The total number of objects marked in the last \GC.
# - +:heap_eden_pages+:
# The total number of pages which contain at least one live slot.
- # - +:heap_tomb_pages+:
- # The total number of pages which do not contain any live slots.
# - +:total_allocated_pages+:
# The cumulative number of pages allocated since application start.
# - +:total_freed_pages+:
@@ -374,11 +375,6 @@ def self.stat hash_or_key = nil
# The number of pages in the eden heap.
# - +:heap_eden_slots+:
# The total number of slots in all of the pages in the eden heap.
- # - +:heap_tomb_pages+:
- # The number of pages in the tomb heap. The tomb heap only contains pages
- # that do not have any live objects.
- # - +:heap_tomb_slots+:
- # The total number of slots in all of the pages in the tomb heap.
# - +:total_allocated_pages+:
# The total number of pages that have been allocated in the heap.
# - +:total_freed_pages+:
diff --git a/lib/time.rb b/lib/time.rb
index a0fc7dfbf5d300..14bfe4c8fbc43b 100644
--- a/lib/time.rb
+++ b/lib/time.rb
@@ -80,7 +80,7 @@ class << Time
#
# You must require 'time' to use this method.
#
- def zone_offset(zone, year=self.now.year)
+ def zone_offset(zone, year=nil)
off = nil
zone = zone.upcase
if /\A([+-])(\d\d)(:?)(\d\d)(?:\3(\d\d))?\z/ =~ zone
@@ -89,10 +89,13 @@ def zone_offset(zone, year=self.now.year)
off = zone.to_i * 3600
elsif ZoneOffset.include?(zone)
off = ZoneOffset[zone] * 3600
- elsif ((t = self.local(year, 1, 1)).zone.upcase == zone rescue false)
- off = t.utc_offset
- elsif ((t = self.local(year, 7, 1)).zone.upcase == zone rescue false)
- off = t.utc_offset
+ else
+ year ||= self.now.year
+ if ((t = self.local(year, 1, 1)).zone.upcase == zone rescue false)
+ off = t.utc_offset
+ elsif ((t = self.local(year, 7, 1)).zone.upcase == zone rescue false)
+ off = t.utc_offset
+ end
end
off
end
diff --git a/prism_compile.c b/prism_compile.c
index 811ce4781696e7..d3c66691a8325b 100644
--- a/prism_compile.c
+++ b/prism_compile.c
@@ -2288,7 +2288,7 @@ pm_compile_index_control_flow_write_node(rb_iseq_t *iseq, const pm_node_t *node,
// A forward declaration because this is the recursive function that handles
// compiling a pattern. It can be reentered by nesting patterns, as in the case
// of arrays or hashes.
-static int pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool in_alternation_pattern, bool use_deconstructed_cache, unsigned int base_index);
+static int pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index);
/**
* This function generates the code to set up the error string and error_p
@@ -2394,10 +2394,10 @@ pm_compile_pattern_eqq_error(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const
* label.
*/
static int
-pm_compile_pattern_match(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *unmatched_label, bool in_single_pattern, bool in_alternation_pattern, bool use_deconstructed_cache, unsigned int base_index)
+pm_compile_pattern_match(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *unmatched_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index)
{
LABEL *matched_label = NEW_LABEL(pm_node_line_number(scope_node->parser, node));
- CHECK(pm_compile_pattern(iseq, scope_node, node, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index));
+ CHECK(pm_compile_pattern(iseq, scope_node, node, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
PUSH_LABEL(ret, matched_label);
return COMPILE_OK;
}
@@ -2544,7 +2544,7 @@ pm_compile_pattern_error_handler(rb_iseq_t *iseq, const pm_scope_node_t *scope_n
* Compile a pattern matching expression.
*/
static int
-pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool in_alternation_pattern, bool use_deconstructed_cache, unsigned int base_index)
+pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t *node, LINK_ANCHOR *const ret, LABEL *matched_label, LABEL *unmatched_label, bool in_single_pattern, bool use_deconstructed_cache, unsigned int base_index)
{
const pm_node_location_t location = PM_NODE_START_LOCATION(scope_node->parser, node);
@@ -2604,7 +2604,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
PUSH_INSN(ret, location, dup);
PUSH_INSN1(ret, location, putobject, INT2FIX(index));
PUSH_SEND(ret, location, idAREF, INT2FIX(1));
- CHECK(pm_compile_pattern_match(iseq, scope_node, required, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, required, ret, match_failed_label, in_single_pattern, false, base_index + 1));
}
if (cast->rest != NULL) {
@@ -2617,7 +2617,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
PUSH_SEND(ret, location, idMINUS, INT2FIX(1));
PUSH_INSN1(ret, location, setn, INT2FIX(4));
PUSH_SEND(ret, location, idAREF, INT2FIX(2));
- CHECK(pm_compile_pattern_match(iseq, scope_node, ((const pm_splat_node_t *) cast->rest)->expression, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, ((const pm_splat_node_t *) cast->rest)->expression, ret, match_failed_label, in_single_pattern, false, base_index + 1));
}
else if (posts_size > 0) {
PUSH_INSN(ret, location, dup);
@@ -2637,7 +2637,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
PUSH_INSN1(ret, location, topn, INT2FIX(3));
PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
PUSH_SEND(ret, location, idAREF, INT2FIX(1));
- CHECK(pm_compile_pattern_match(iseq, scope_node, post, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, post, ret, match_failed_label, in_single_pattern, false, base_index + 1));
}
PUSH_INSN(ret, location, pop);
@@ -2734,7 +2734,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
}
PUSH_SEND(ret, location, idAREF, INT2FIX(1));
- CHECK(pm_compile_pattern_match(iseq, scope_node, cast->requireds.nodes[index], ret, next_loop_label, in_single_pattern, in_alternation_pattern, false, base_index + 4));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, cast->requireds.nodes[index], ret, next_loop_label, in_single_pattern, false, base_index + 4));
}
const pm_splat_node_t *left = cast->left;
@@ -2744,7 +2744,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
PUSH_INSN1(ret, location, putobject, INT2FIX(0));
PUSH_INSN1(ret, location, topn, INT2FIX(2));
PUSH_SEND(ret, location, idAREF, INT2FIX(2));
- CHECK(pm_compile_pattern_match(iseq, scope_node, left->expression, ret, find_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 4));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, left->expression, ret, find_failed_label, in_single_pattern, false, base_index + 4));
}
RUBY_ASSERT(PM_NODE_TYPE_P(cast->right, PM_SPLAT_NODE));
@@ -2757,7 +2757,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
PUSH_SEND(ret, location, idPLUS, INT2FIX(1));
PUSH_INSN1(ret, location, topn, INT2FIX(3));
PUSH_SEND(ret, location, idAREF, INT2FIX(2));
- pm_compile_pattern_match(iseq, scope_node, right->expression, ret, find_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 4);
+ pm_compile_pattern_match(iseq, scope_node, right->expression, ret, find_failed_label, in_single_pattern, false, base_index + 4);
}
PUSH_INSNL(ret, location, jump, find_succeeded_label);
@@ -2936,7 +2936,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
value = ((const pm_implicit_node_t *) value)->value;
}
- CHECK(pm_compile_pattern_match(iseq, scope_node, value, match_values, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, value, match_values, match_failed_label, in_single_pattern, false, base_index + 1));
}
PUSH_SEQ(ret, match_values);
@@ -2964,7 +2964,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
case PM_ASSOC_SPLAT_NODE: {
const pm_assoc_splat_node_t *splat = (const pm_assoc_splat_node_t *) cast->rest;
PUSH_INSN(ret, location, dup);
- pm_compile_pattern_match(iseq, scope_node, splat->value, ret, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index + 1);
+ pm_compile_pattern_match(iseq, scope_node, splat->value, ret, match_failed_label, in_single_pattern, false, base_index + 1);
break;
}
default:
@@ -3009,8 +3009,8 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
LABEL *match_failed_label = NEW_LABEL(location.line);
PUSH_INSN(ret, location, dup);
- CHECK(pm_compile_pattern_match(iseq, scope_node, cast->value, ret, match_failed_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index + 1));
- CHECK(pm_compile_pattern(iseq, scope_node, (const pm_node_t *) cast->target, ret, matched_label, match_failed_label, in_single_pattern, in_alternation_pattern, false, base_index));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, cast->value, ret, match_failed_label, in_single_pattern, use_deconstructed_cache, base_index + 1));
+ CHECK(pm_compile_pattern(iseq, scope_node, (const pm_node_t *) cast->target, ret, matched_label, match_failed_label, in_single_pattern, false, base_index));
PUSH_INSN(ret, location, putnil);
PUSH_LABEL(ret, match_failed_label);
@@ -3026,20 +3026,6 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
const pm_local_variable_target_node_t *cast = (const pm_local_variable_target_node_t *) node;
pm_local_index_t index = pm_lookup_local_index(iseq, scope_node, cast->name, cast->depth);
- // If this local variable is being written from within an alternation
- // pattern, then it cannot actually be added to the local table since
- // it's ambiguous which value should be used. So instead we indicate
- // this with a compile error.
- if (in_alternation_pattern) {
- ID id = pm_constant_id_lookup(scope_node, cast->name);
- const char *name = rb_id2name(id);
-
- if (name && strlen(name) > 0 && name[0] != '_') {
- COMPILE_ERROR(iseq, location.line, "illegal variable in alternative pattern (%"PRIsVALUE")", rb_id2str(id));
- return COMPILE_NG;
- }
- }
-
PUSH_SETLOCAL(ret, location, index.index, index.level);
PUSH_INSNL(ret, location, jump, matched_label);
break;
@@ -3055,7 +3041,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
// First, we're going to attempt to match against the left pattern. If
// that pattern matches, then we'll skip matching the right pattern.
PUSH_INSN(ret, location, dup);
- CHECK(pm_compile_pattern(iseq, scope_node, cast->left, ret, matched_left_label, unmatched_left_label, in_single_pattern, true, use_deconstructed_cache, base_index + 1));
+ CHECK(pm_compile_pattern(iseq, scope_node, cast->left, ret, matched_left_label, unmatched_left_label, in_single_pattern, use_deconstructed_cache, base_index + 1));
// If we get here, then we matched on the left pattern. In this case we
// should pop out the duplicate value that we preemptively added to
@@ -3068,7 +3054,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
// If we get here, then we didn't match on the left pattern. In this
// case we attempt to match against the right pattern.
PUSH_LABEL(ret, unmatched_left_label);
- CHECK(pm_compile_pattern(iseq, scope_node, cast->right, ret, matched_label, unmatched_label, in_single_pattern, true, use_deconstructed_cache, base_index));
+ CHECK(pm_compile_pattern(iseq, scope_node, cast->right, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
break;
}
case PM_PARENTHESES_NODE:
@@ -3076,7 +3062,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
// they do nothing since they can only wrap individual expressions and
// not groups. In this case we'll recurse back into this same function
// with the body of the parentheses.
- return pm_compile_pattern(iseq, scope_node, ((const pm_parentheses_node_t *) node)->body, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index);
+ return pm_compile_pattern(iseq, scope_node, ((const pm_parentheses_node_t *) node)->body, ret, matched_label, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index);
case PM_PINNED_EXPRESSION_NODE:
// Pinned expressions are a way to match against the value of an
// expression that should be evaluated at runtime. This looks like:
@@ -3137,7 +3123,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
// looks like: foo in ^@bar. To compile these, we compile the variable
// that they hold.
const pm_pinned_variable_node_t *cast = (const pm_pinned_variable_node_t *) node;
- CHECK(pm_compile_pattern(iseq, scope_node, cast->variable, ret, matched_label, unmatched_label, in_single_pattern, in_alternation_pattern, true, base_index));
+ CHECK(pm_compile_pattern(iseq, scope_node, cast->variable, ret, matched_label, unmatched_label, in_single_pattern, true, base_index));
break;
}
case PM_IF_NODE:
@@ -3171,7 +3157,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
statement = cast->statements->body.nodes[0];
}
- CHECK(pm_compile_pattern_match(iseq, scope_node, statement, ret, unmatched_label, in_single_pattern, in_alternation_pattern, use_deconstructed_cache, base_index));
+ CHECK(pm_compile_pattern_match(iseq, scope_node, statement, ret, unmatched_label, in_single_pattern, use_deconstructed_cache, base_index));
PM_COMPILE_NOT_POPPED(predicate);
if (in_single_pattern) {
@@ -7878,7 +7864,7 @@ pm_compile_case_match_node(rb_iseq_t *iseq, const pm_case_match_node_t *node, co
LABEL *next_pattern_label = NEW_LABEL(pattern_location.line);
PUSH_INSN(cond_seq, pattern_location, dup);
- pm_compile_pattern(iseq, scope_node, in_node->pattern, cond_seq, body_label, next_pattern_label, in_single_pattern, false, true, 2);
+ pm_compile_pattern(iseq, scope_node, in_node->pattern, cond_seq, body_label, next_pattern_label, in_single_pattern, true, 2);
PUSH_LABEL(cond_seq, next_pattern_label);
LABEL_UNREMOVABLE(next_pattern_label);
}
@@ -8126,7 +8112,7 @@ pm_compile_match_required_node(rb_iseq_t *iseq, const pm_match_required_node_t *
// through the in_single_pattern parameter. We also indicate that the
// value to compare against is 2 slots from the top of the stack (the
// base_index parameter).
- pm_compile_pattern(iseq, scope_node, node->pattern, ret, matched_label, unmatched_label, true, false, true, 2);
+ pm_compile_pattern(iseq, scope_node, node->pattern, ret, matched_label, unmatched_label, true, true, 2);
// If the pattern did not match the value, then we're going to compile
// in our error handler code. This will determine which error to raise
@@ -9815,7 +9801,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
LABEL *matched_label = NEW_LABEL(location.line);
LABEL *unmatched_label = NEW_LABEL(location.line);
LABEL *done_label = NEW_LABEL(location.line);
- pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, false, false, true, 2);
+ pm_compile_pattern(iseq, scope_node, cast->pattern, ret, matched_label, unmatched_label, false, true, 2);
// If the pattern did not match, then compile the necessary instructions
// to handle pushing false onto the stack, then jump to the end.
diff --git a/string.c b/string.c
index 5c4e15c4a10384..827555d9e0faab 100644
--- a/string.c
+++ b/string.c
@@ -1935,8 +1935,8 @@ str_duplicate_setup_embed(VALUE klass, VALUE str, VALUE dup)
long len = RSTRING_LEN(str);
RUBY_ASSERT(STR_EMBED_P(dup));
- RUBY_ASSERT(str_embed_capa(dup) >= len + 1);
- MEMCPY(RSTRING(dup)->as.embed.ary, RSTRING(str)->as.embed.ary, char, len + 1);
+ RUBY_ASSERT(str_embed_capa(dup) >= len + TERM_LEN(str));
+ MEMCPY(RSTRING(dup)->as.embed.ary, RSTRING(str)->as.embed.ary, char, len + TERM_LEN(str));
STR_SET_LEN(dup, RSTRING_LEN(str));
return str_duplicate_setup_encoding(str, dup, flags);
}
diff --git a/test/prism/ruby/parser_test.rb b/test/prism/ruby/parser_test.rb
index 1629c36b382b53..648c44e77a6573 100644
--- a/test/prism/ruby/parser_test.rb
+++ b/test/prism/ruby/parser_test.rb
@@ -172,7 +172,6 @@ def test_non_prism_builder_class_deprecated
if RUBY_VERSION >= "3.3"
def test_current_parser_for_current_ruby
major, minor = current_major_minor.split(".")
- return if major == "3" && minor == "5" # TODO: Remove once ruby-dev becomes 4.0
# Let's just hope there never is a Ruby 3.10 or similar
expected = major.to_i * 10 + minor.to_i
assert_equal(expected, Translation::ParserCurrent.new.version)
diff --git a/test/prism/test_helper.rb b/test/prism/test_helper.rb
index c03f70b2cdab18..42555738cf318f 100644
--- a/test/prism/test_helper.rb
+++ b/test/prism/test_helper.rb
@@ -256,7 +256,6 @@ def current_major_minor
if RUBY_VERSION >= "3.3.0"
def test_all_syntax_versions_present
- return if RUBY_VERSION.start_with?("3.5") # TODO: Remove once ruby-dev becomes 4.0
assert_include(SYNTAX_VERSIONS, current_major_minor)
end
end
diff --git a/tool/format-release b/tool/format-release
index 927a35d58c23de..b7ad74a095c6fd 100755
--- a/tool/format-release
+++ b/tool/format-release
@@ -30,10 +30,9 @@ class Tarball
def gz?; @url.end_with?('.gz'); end
def zip?; @url.end_with?('.zip'); end
- def bz2?; @url.end_with?('.bz2'); end
def xz?; @url.end_with?('.xz'); end
- def ext; @url[/(?:zip|tar\.(?:gz|bz2|xz))\z/]; end
+ def ext; @url[/(?:zip|tar\.(?:gz|xz))\z/]; end
def to_md
<