diff --git a/.rdoc_options b/.rdoc_options
index 38dca221112c53..591ddf58897595 100644
--- a/.rdoc_options
+++ b/.rdoc_options
@@ -10,6 +10,7 @@ rdoc_include:
exclude:
- \.gemspec\z
+- lib/set/subclass_compatible.rb
generator_name: aliki
diff --git a/defs/jit.mk b/defs/jit.mk
index e893098ca26486..42b56c4cd928b6 100644
--- a/defs/jit.mk
+++ b/defs/jit.mk
@@ -1,4 +1,9 @@
# Make recipes that deal with the rust code of YJIT and ZJIT.
+#
+# $(gnumake_recursive) adds the '+' prefix to pass down GNU make's
+# jobserver resources to cargo/rustc as rust-lang.org recommends.
+# Without it, certain make version trigger a warning. It does not
+# add the prefix when `make --dry-run` so dry runs are indeed dry.
ifneq ($(JIT_CARGO_SUPPORT),no)
@@ -25,7 +30,7 @@ $(RUST_LIB): $(srcdir)/ruby.rs
elif [ '$(YJIT_SUPPORT)' != no ]; then \
echo 'building YJIT ($(JIT_CARGO_SUPPORT) mode)'; \
fi
- +$(Q)CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \
+ $(gnumake_recursive)$(Q)CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \
CARGO_TERM_PROGRESS_WHEN='never' \
MACOSX_DEPLOYMENT_TARGET=11.0 \
$(CARGO) $(CARGO_VERBOSE) build --manifest-path '$(top_srcdir)/Cargo.toml' $(CARGO_BUILD_ARGS)
@@ -34,7 +39,7 @@ else ifneq ($(strip $(RLIB_DIR)),) # combo build
$(RUST_LIB): $(srcdir)/ruby.rs
$(ECHO) 'building $(@F)'
- $(Q) $(RUSTC) --edition=2024 \
+ $(gnumake_recursive)$(Q) $(RUSTC) --edition=2024 \
'-L$(@D)' \
--extern=yjit \
--extern=zjit \
@@ -50,7 +55,7 @@ $(YJIT_RLIB): $(JIT_RLIB)
$(ZJIT_RLIB): $(JIT_RLIB)
$(JIT_RLIB):
$(ECHO) 'building $(@F)'
- $(Q) $(RUSTC) --crate-name=jit \
+ $(gnumake_recursive)$(Q) $(RUSTC) --crate-name=jit \
--edition=2024 \
$(JIT_RUST_FLAGS) \
'--out-dir=$(@D)' \
diff --git a/gc.c b/gc.c
index 5a14cb3675032d..d229c1f5aba254 100644
--- a/gc.c
+++ b/gc.c
@@ -1537,34 +1537,26 @@ os_obj_of(VALUE of)
* Ruby process. If module is specified, calls the block
* for only those classes or modules that match (or are a subclass of)
* module. Returns the number of objects found. Immediate
- * objects (Fixnums, Symbols
- * true, false, and nil) are
- * never returned. In the example below, #each_object returns both
- * the numbers we defined and several constants defined in the Math
- * module.
+ * objects (such as Fixnums, static Symbols
+ * true, false and nil) are
+ * never returned.
*
* If no block is given, an enumerator is returned instead.
*
- * a = 102.7
- * b = 95 # Won't be returned
- * c = 12345678987654321
- * count = ObjectSpace.each_object(Numeric) {|x| p x }
+ * Job = Class.new
+ * jobs = [Job.new, Job.new]
+ * count = ObjectSpace.each_object(Job) {|x| p x }
* puts "Total count: #{count}"
*
* produces:
*
- * 12345678987654321
- * 102.7
- * 2.71828182845905
- * 3.14159265358979
- * 2.22044604925031e-16
- * 1.7976931348623157e+308
- * 2.2250738585072e-308
- * Total count: 7
+ * #
+ * #
+ * Total count: 2
*
- * Due to a current known Ractor implementation issue, this method will not yield
- * Ractor-unshareable objects in multi-Ractor mode (when
- * Ractor.new has been called within the process at least once).
+ * Due to a current Ractor implementation issue, this method does not yield
+ * Ractor-unshareable objects when the process is in multi-Ractor mode. Multi-ractor
+ * mode is enabled when Ractor.new has been called for the first time.
* See https://bugs.ruby-lang.org/issues/19387 for more information.
*
* a = 12345678987654321 # shareable
diff --git a/include/ruby/internal/ctype.h b/include/ruby/internal/ctype.h
index 0f7ca6c51635d0..93e92801fcc5a3 100644
--- a/include/ruby/internal/ctype.h
+++ b/include/ruby/internal/ctype.h
@@ -498,7 +498,7 @@ RBIMPL_ATTR_ARTIFICIAL()
* Our own locale-insensitive version of `tolower(3)`.
*
* @param[in] c Byte in question to convert.
- * @retval c The byte is not listed in in IEEE 1003.1 section
+ * @retval c The byte is not listed in IEEE 1003.1 section
* 7.3.1.1 "upper".
* @retval otherwise Byte converted using the map defined in IEEE 1003.1
* section 7.3.1 "tolower".
diff --git a/include/ruby/internal/symbol.h b/include/ruby/internal/symbol.h
index 569bf215a2df85..858c5e290f6463 100644
--- a/include/ruby/internal/symbol.h
+++ b/include/ruby/internal/symbol.h
@@ -187,7 +187,7 @@ ID rb_check_id(volatile VALUE *namep);
*
* :FIXME: Can anyone tell us what is the difference between this one and
* rb_intern_str()? As far as @shyouhei reads the implementation it seems what
- * rb_to_id() does is is just waste some CPU time, then call rb_intern_str().
+ * rb_to_id() does is just waste some CPU time, then call rb_intern_str().
* He hopes he is wrong.
*/
ID rb_to_id(VALUE str);
diff --git a/ractor_sync.c b/ractor_sync.c
index 57ae13e88de50f..8c7c144c3fda97 100644
--- a/ractor_sync.c
+++ b/ractor_sync.c
@@ -699,7 +699,12 @@ ractor_sync_free(rb_ractor_t *r)
static size_t
ractor_sync_memsize(const rb_ractor_t *r)
{
- return st_table_size(r->sync.ports);
+ if (r->sync.ports) {
+ return st_table_size(r->sync.ports);
+ }
+ else {
+ return 0;
+ }
}
static void
diff --git a/set.c b/set.c
index 6d200b5dfa1495..734a6ecaea2107 100644
--- a/set.c
+++ b/set.c
@@ -495,11 +495,11 @@ set_initialize_with_block(RB_BLOCK_CALL_FUNC_ARGLIST(i, set))
* If a block is given, the elements of enum are preprocessed by the
* given block.
*
- * Set.new([1, 2]) #=> #
- * Set.new([1, 2, 1]) #=> #
- * Set.new([1, 'c', :s]) #=> #
- * Set.new(1..5) #=> #
- * Set.new([1, 2, 3]) { |x| x * x } #=> #
+ * Set.new([1, 2]) #=> Set[1, 2]
+ * Set.new([1, 2, 1]) #=> Set[1, 2]
+ * Set.new([1, 'c', :s]) #=> Set[1, "c", :s]
+ * Set.new(1..5) #=> Set[1, 2, 3, 4, 5]
+ * Set.new([1, 2, 3]) { |x| x * x } #=> Set[1, 4, 9]
*/
static VALUE
set_i_initialize(int argc, VALUE *argv, VALUE set)
@@ -595,11 +595,11 @@ set_inspect(VALUE set, VALUE dummy, int recur)
* Returns a new string containing the set entries:
*
* s = Set.new
- * s.inspect # => "#"
+ * s.inspect # => "Set[]"
* s.add(1)
- * s.inspect # => "#"
+ * s.inspect # => "Set[1]"
* s.add(2)
- * s.inspect # => "#"
+ * s.inspect # => "Set[1, 2]"
*
* Related: see {Methods for Converting}[rdoc-ref:Set@Methods+for+Converting].
*/
@@ -650,11 +650,11 @@ set_i_to_a(VALUE set)
* call-seq:
* to_set(klass = Set, *args, &block) -> self or new_set
*
- * Returns self if receiver is an instance of +Set+ and no arguments or
- * block are given. Otherwise, converts the set to another with
- * klass.new(self, *args, &block).
+ * Without arguments, returns +self+ (for duck-typing in methods that
+ * accept "set, or set-convertible" arguments).
*
- * In subclasses, returns `klass.new(self, *args, &block)` unless overridden.
+ * A form with arguments is _deprecated_. It converts the set to another
+ * with klass.new(self, *args, &block).
*/
static VALUE
set_i_to_set(int argc, VALUE *argv, VALUE set)
@@ -700,9 +700,9 @@ set_i_join(int argc, VALUE *argv, VALUE set)
* Adds the given object to the set and returns self. Use `merge` to
* add many elements at once.
*
- * Set[1, 2].add(3) #=> #
- * Set[1, 2].add([3, 4]) #=> #
- * Set[1, 2].add(2) #=> #
+ * Set[1, 2].add(3) #=> Set[1, 2, 3]
+ * Set[1, 2].add([3, 4]) #=> Set[1, 2, [3, 4]]
+ * Set[1, 2].add(2) #=> Set[1, 2]
*/
static VALUE
set_i_add(VALUE set, VALUE item)
@@ -726,8 +726,8 @@ set_i_add(VALUE set, VALUE item)
* Adds the given object to the set and returns self. If the object is
* already in the set, returns nil.
*
- * Set[1, 2].add?(3) #=> #
- * Set[1, 2].add?([3, 4]) #=> #
+ * Set[1, 2].add?(3) #=> Set[1, 2, 3]
+ * Set[1, 2].add?([3, 4]) #=> Set[1, 2, [3, 4]]
* Set[1, 2].add?(2) #=> nil
*/
static VALUE
@@ -856,9 +856,9 @@ set_classify_i(st_data_t key, st_data_t tmp)
*
* files = Set.new(Dir.glob("*.rb"))
* hash = files.classify { |f| File.mtime(f).year }
- * hash #=> {2000 => #,
- * # 2001 => #,
- * # 2002 => #}
+ * hash #=> {2000 => Set["a.rb", "b.rb"],
+ * # 2001 => Set["c.rb", "d.rb", "e.rb"],
+ * # 2002 => Set["f.rb"]}
*
* Returns an enumerator if no block is given.
*/
@@ -959,10 +959,10 @@ static void set_merge_enum_into(VALUE set, VALUE arg);
*
* numbers = Set[1, 3, 4, 6, 9, 10, 11]
* set = numbers.divide { |i,j| (i - j).abs == 1 }
- * set #=> #,
- * # #,
- * # #}>
- * # #,
+ * set #=> Set[Set[1],
+ * # Set[3, 4],
+ * # Set[6],
+ * # Set[9, 10, 11]]
*
* Returns an enumerator if no block is given.
*/
@@ -993,9 +993,9 @@ set_clear_i(st_data_t key, st_data_t dummy)
*
* Removes all elements and returns self.
*
- * set = Set[1, 'c', :s] #=> #
- * set.clear #=> #
- * set #=> #
+ * set = Set[1, 'c', :s] #=> Set[1, "c", :s]
+ * set.clear #=> Set[]
+ * set #=> Set[]
*/
static VALUE
set_i_clear(VALUE set)
@@ -1043,8 +1043,8 @@ set_intersection_block(RB_BLOCK_CALL_FUNC_ARGLIST(i, data))
* Returns a new set containing elements common to the set and the given
* enumerable object.
*
- * Set[1, 3, 5] & Set[3, 2, 1] #=> #
- * Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> #
+ * Set[1, 3, 5] & Set[3, 2, 1] #=> Set[3, 1]
+ * Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> Set["a", "b"]
*/
static VALUE
set_i_intersection(VALUE set, VALUE other)
@@ -1285,8 +1285,8 @@ set_xor_i(st_data_t key, st_data_t data)
* given enumerable object. (set ^ enum) is equivalent to
* ((set | enum) - (set & enum)).
*
- * Set[1, 2] ^ Set[2, 3] #=> #
- * Set[1, 'b', 'c'] ^ ['b', 'd'] #=> #
+ * Set[1, 2] ^ Set[2, 3] #=> Set[3, 1]
+ * Set[1, 'b', 'c'] ^ ['b', 'd'] #=> Set["d", 1, "c"]
*/
static VALUE
set_i_xor(VALUE set, VALUE other)
@@ -1312,8 +1312,8 @@ set_i_xor(VALUE set, VALUE other)
* Returns a new set built by merging the set and the elements of the
* given enumerable object.
*
- * Set[1, 2, 3] | Set[2, 4, 5] #=> #
- * Set[1, 5, 'z'] | (1..6) #=> #
+ * Set[1, 2, 3] | Set[2, 4, 5] #=> Set[1, 2, 3, 4, 5]
+ * Set[1, 5, 'z'] | (1..6) #=> Set[1, 5, "z", 2, 3, 4, 6]
*/
static VALUE
set_i_union(VALUE set, VALUE other)
@@ -1371,8 +1371,8 @@ set_i_subtract(VALUE set, VALUE other)
* Returns a new set built by duplicating the set, removing every
* element that appears in the given enumerable object.
*
- * Set[1, 3, 5] - Set[1, 5] #=> #
- * Set['a', 'b', 'z'] - ['a', 'c'] #=> #
+ * Set[1, 3, 5] - Set[1, 5] #=> Set[3]
+ * Set['a', 'b', 'z'] - ['a', 'c'] #=> Set["b", "z"]
*/
static VALUE
set_i_difference(VALUE set, VALUE other)
@@ -1488,9 +1488,9 @@ set_i_select(VALUE set)
* Replaces the contents of the set with the contents of the given
* enumerable object and returns self.
*
- * set = Set[1, 'c', :s] #=> #
- * set.replace([1, 2]) #=> #
- * set #=> #
+ * set = Set[1, 'c', :s] #=> Set[1, "c", :s]
+ * set.replace([1, 2]) #=> Set[1, 2]
+ * set #=> Set[1, 2]
*/
static VALUE
set_i_replace(VALUE set, VALUE other)
@@ -1766,7 +1766,7 @@ set_i_disjoint(VALUE set, VALUE other)
* set <=> other -> -1, 0, 1, or nil
*
* Returns 0 if the set are equal, -1 / 1 if the set is a
- * proper subset / superset of the given set, or or nil if
+ * proper subset / superset of the given set, or nil if
* they both have unique elements.
*/
static VALUE
@@ -1992,10 +1992,10 @@ rb_set_size(VALUE set)
* duplicates. It is a hybrid of Array's intuitive inter-operation
* facilities and Hash's fast lookup.
*
- * Set is easy to use with Enumerable objects (implementing `each`).
+ * Set is easy to use with Enumerable objects (implementing #each).
* Most of the initializer methods and binary operators accept generic
* Enumerable objects besides sets and arrays. An Enumerable object
- * can be converted to Set using the `to_set` method.
+ * can be converted to Set using the +to_set+ method.
*
* Set uses a data structure similar to Hash for storage, except that
* it only has keys and no values.
@@ -2019,11 +2019,11 @@ rb_set_size(VALUE set)
*
* == Example
*
- * s1 = Set[1, 2] #=> #
- * s2 = [1, 2].to_set #=> #
+ * s1 = Set[1, 2] #=> Set[1, 2]
+ * s2 = [1, 2].to_set #=> Set[1, 2]
* s1 == s2 #=> true
- * s1.add("foo") #=> #
- * s1.merge([2, 6]) #=> #
+ * s1.add("foo") #=> Set[1, 2, "foo"]
+ * s1.merge([2, 6]) #=> Set[1, 2, "foo", 6]
* s1.subset?(s2) #=> false
* s2.subset?(s1) #=> true
*
@@ -2031,9 +2031,39 @@ rb_set_size(VALUE set)
*
* - Akinori MUSHA (current maintainer)
*
- * == What's Here
+ * == Inheriting from \Set
*
- * First, what's elsewhere. \Class \Set:
+ * Before Ruby 4.0 (released December 2025), \Set had a different, less
+ * efficient implementation. It was reimplemented in C, and the behavior
+ * of some of the core methods were adjusted.
+ *
+ * To keep backward compatibility, when a class is inherited from \Set,
+ * additional module +Set::SubclassCompatible+ is included, which makes
+ * the inherited class behavior, as well as internal method names,
+ * closer to what it was before Ruby 4.0.
+ *
+ * It can be easily seen, for example, in the #inspect method behavior:
+ *
+ * p Set[1, 2, 3]
+ * # prints "Set[1, 2, 3]"
+ *
+ * class MySet < Set
+ * end
+ * p MySet[1, 2, 3]
+ * # prints "#", like it was in Ruby 3.4
+ *
+ * For new code, if backward compatibility is not necessary,
+ * it is recommended to instead inherit from +Set::CoreSet+, which
+ * avoids including the "compatibility" layer:
+ *
+ * class MyCoreSet < Set::CoreSet
+ * end
+ * p MyCoreSet[1, 2, 3]
+ * # prints "MyCoreSet[1, 2, 3]"
+ *
+ * == Set's methods
+ *
+ * First, what's elsewhere. \Class \Set:
*
* - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
* - Includes {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here],
@@ -2045,16 +2075,15 @@ rb_set_size(VALUE set)
*
* Here, class \Set provides methods that are useful for:
*
- * - {Creating an Array}[rdoc-ref:Array@Methods+for+Creating+an+Array]
* - {Creating a Set}[rdoc-ref:Set@Methods+for+Creating+a+Set]
* - {Set Operations}[rdoc-ref:Set@Methods+for+Set+Operations]
- * - {Comparing}[rdoc-ref:Array@Methods+for+Comparing]
- * - {Querying}[rdoc-ref:Array@Methods+for+Querying]
- * - {Assigning}[rdoc-ref:Array@Methods+for+Assigning]
- * - {Deleting}[rdoc-ref:Array@Methods+for+Deleting]
- * - {Converting}[rdoc-ref:Array@Methods+for+Converting]
- * - {Iterating}[rdoc-ref:Array@Methods+for+Iterating]
- * - {And more....}[rdoc-ref:Array@Other+Methods]
+ * - {Comparing}[rdoc-ref:Set@Methods+for+Comparing]
+ * - {Querying}[rdoc-ref:Set@Methods+for+Querying]
+ * - {Assigning}[rdoc-ref:Set@Methods+for+Assigning]
+ * - {Deleting}[rdoc-ref:Set@Methods+for+Deleting]
+ * - {Converting}[rdoc-ref:Set@Methods+for+Converting]
+ * - {Iterating}[rdoc-ref:Set@Methods+for+Iterating]
+ * - {And more....}[rdoc-ref:Set@Other+Methods]
*
* === Methods for Creating a \Set
*
diff --git a/yjit/yjit.mk b/yjit/yjit.mk
index 22d256fee78c14..21fd96514bdca9 100644
--- a/yjit/yjit.mk
+++ b/yjit/yjit.mk
@@ -18,14 +18,14 @@ ifneq ($(strip $(YJIT_LIBS)),)
yjit-libs: $(BUILD_YJIT_LIBS)
$(BUILD_YJIT_LIBS): $(YJIT_SRC_FILES)
$(ECHO) 'building Rust YJIT (release mode)'
- $(Q) $(RUSTC) $(YJIT_RUSTC_ARGS)
+ $(gnumake_recursive)$(Q) $(RUSTC) $(YJIT_RUSTC_ARGS)
else ifneq ($(strip $(RLIB_DIR)),) # combo build
# Absolute path to avoid VPATH ambiguity
YJIT_RLIB = $(TOP_BUILD_DIR)/$(RLIB_DIR)/libyjit.rlib
$(YJIT_RLIB): $(YJIT_SRC_FILES)
$(ECHO) 'building $(@F)'
- $(Q) $(RUSTC) '-L$(@D)' --extern=jit $(YJIT_RUSTC_ARGS)
+ $(gnumake_recursive)$(Q) $(RUSTC) '-L$(@D)' --extern=jit $(YJIT_RUSTC_ARGS)
$(RUST_LIB): $(YJIT_RLIB)
endif # ifneq ($(strip $(YJIT_LIBS)),)
diff --git a/zjit/zjit.mk b/zjit/zjit.mk
index 2116775a91a182..58b45d87872d94 100644
--- a/zjit/zjit.mk
+++ b/zjit/zjit.mk
@@ -22,14 +22,14 @@ BUILD_ZJIT_LIBS = $(TOP_BUILD_DIR)/$(ZJIT_LIBS)
ifneq ($(strip $(ZJIT_LIBS)),)
$(BUILD_ZJIT_LIBS): $(ZJIT_SRC_FILES)
$(ECHO) 'building Rust ZJIT (release mode)'
- $(Q) $(RUSTC) $(ZJIT_RUSTC_ARGS)
+ $(gnumake_recursive)$(Q) $(RUSTC) $(ZJIT_RUSTC_ARGS)
else ifneq ($(strip $(RLIB_DIR)),) # combo build
# Absolute path to avoid VPATH ambiguity
ZJIT_RLIB = $(TOP_BUILD_DIR)/$(RLIB_DIR)/libzjit.rlib
$(ZJIT_RLIB): $(ZJIT_SRC_FILES)
$(ECHO) 'building $(@F)'
- $(Q) $(RUSTC) '-L$(@D)' --extern=jit $(ZJIT_RUSTC_ARGS)
+ $(gnumake_recursive)$(Q) $(RUSTC) '-L$(@D)' --extern=jit $(ZJIT_RUSTC_ARGS)
$(RUST_LIB): $(ZJIT_RLIB)
endif # ifneq ($(strip $(ZJIT_LIBS)),)