Commit Graph
103255 Commits
Author SHA1 Message Date
Edouard CHINandYaroslav Markin cc57f9c818 Include ivar name and class name in the Ractor isolation error message
- ### Problem

  When a Ractor isolation error happens due to setting an ivar on a
  class, the message doesn't include the name of the ivar and neither
  the name of the class. It makes it difficult to track down where the
  isolation problem comes from.

  I'd like the message to be similar as what we get when a Ractor
  access an unshareable ivar.

  This is the current behaviour

  ```ruby
  class Foo
    def self.foo
      @foo ||= "test"
    end
  end

  Ractor.new do
    Foo.foo # can not set instance variables of classes/modules by non-main Ractors
  end.join

  Foo.foo

  Ractor.new do
    Foo.foo # can not get unshareable values from instance variables of classes/modules from non-main Ractors (@foo from Foo)
  end.join
  ```

  ### Solution

  Modify the error message to include the ivar name and the class/module name.

Co-authored-by: Yaroslav Markin <yaroslav@markin.net>
2026-10-22 17:51:23 -04:00
Luke Gruber 7a39a7a957 Fix retire GC for Ractors holding on to empty pages
There was a condition that only a heap with > 1 page can move an empty
page from free_pages to empty_pages. This was even true during a retire
GC (Ractor#join), so running a retire GC couldn't release these actually
empty pages back to the page pool. This meant that when a terminated Ractor
got absorbed, 768KB (12 heap pages) came with it, even if those pages were
empty (this assumes the Ractor had a page for every heap).

Since Ractors can now have 0 pages and the main global GC trigger is
zombie pages (and not retired Ractors), we need to count retired Ractors
with 0 pages towards this zombie pages limit. For now, the floor is
HEAP_COUNT (12 pages), but this can be changed if it's too high.
2026-10-22 17:28:30 -04:00
ShiaandClaude Opus 5.5 8ff4ae135d Store the members directly in Data.new with positional arguments
Data.new turned positional arguments into a keyword Hash and called
initialize with it. When initialize is the default Data#initialize and
every member is given, that call can only store the members and freeze
the instance, so do that directly. A TracePoint on c_call no longer sees
Data#initialize for such calls.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 16:24:10 +02:00
Matt Valentine-House 0d83a93784 Unshare arrays only when they become shareable
obj_traverse_i called rb_ary_cancel_sharing on every array it visited,
before the traversal knew whether the object graph was shareable.

So Ractor.shareable? modified frozen arrays even when it returned false.

This commit moves rb_ary_cancel_sharing into mark_shareable, which
runs only after the traversal succeeds.

[Bug #22460]
2026-10-22 13:01:48 +01:00
Hiroshi SHIBATAandClaude Opus 5.5 9fa6602683 Define SIZEOF_RB_LEN_T for mswin64 with GNU make
4e746b14fd added it only to Makefile.sub, so GNU make builds kept a 32-bit rb_len_t, differing from nmake builds, and failed the static assertion in yjit.c with YJIT enabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:09:07 +09:00
5f2bc22560 [ruby/syntax_suggest] Prefer Ruby::Prism, the parser built into the
interpreter
(https://github.com/ruby/syntax_suggest/pull/266)

* Prefer Ruby::Prism, the parser built into the interpreter

Ruby 4.1 exposes the prism parser built into the interpreter as
Ruby::Prism, and the prism gem becomes a bundled gem
(https://bugs.ruby-lang.org/issues/22379).

syntax_suggest is loaded when a script fails to parse, so it should not
depend on the prism gem, which may be absent from the Gemfile under
bundler.  On Ruby 4.1+ use Ruby::Prism; on older Rubies keep requiring
the prism gem.

Every reference to Prism in syntax_suggest is lexically inside the
SyntaxSuggest module, so defining SyntaxSuggest::Prism is enough and
the call sites are untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Update lib/syntax_suggest/api.rb

* Update CHANGELOG.md

* Apply suggestion from @Earlopain

---------

https://github.com/ruby/syntax_suggest/commit/e11b8842ff

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Earlopain <14981592+Earlopain@users.noreply.github.com>
2026-10-22 08:02:58 +00:00
Nobuyoshi Nakada faefcb1105 [Bug #22461] Fix dangling pointers after associating encoding
`rb_enc_associate` may re-allocate the string buffer because of the
terminator length change.
2026-10-22 10:46:22 +09:00
Max Bernstein f4c7ad48ce ZJIT: Inline ISEQs marked :always_inline
Add a new `Primitive.attr :always_inline`. Force inline ISEQs with this
attribute. This is helpful for e.g. `BasicObject#!=`, which is otherwise
megamorphic, and very small.

Other JITs also have a "always inline small methods" policy but I didn't
want to wade into that yet.
2026-10-21 20:48:24 -04:00
Max Bernstein 95d41e7a9a Add other iseq attrs to disasm, error messages
Some other attributes got forgotten so make sure to add them.
2026-10-21 20:48:24 -04:00
BurdetteLamar 1d802519b7 [DOC] Make bitwise methods doc visible 2026-10-22 08:52:40 +09:00
John Hawthorn f609897fa4 Add Ractor#alive?
Matches Thread#alive? and Fiber#alive?. Returns false once the ractor's
result has been recorded. Reads sync.legacy under the ractor lock, as
Ractor#monitor does, so it cannot race with termination.

[Feature #22441]
2026-10-21 16:48:38 -07:00
John Hawthorn 2500ab0668 Don't call rb_add_method with the VM lock held when refining
Defining a method in a refinement makes rb_method_entry_make (VM lock held)
add an entry to the refined class by calling rb_add_method. rb_add_method must
be called without the VM lock: it asserts that, and emits warnings, which can
run Ruby code, after its critical section. With a second Ractor around that
tripped ASSERT_vm_unlocking and called rb_warn with the lock held (e.g.
refine(Object) { def respond_to? }).

Add the entry with rb_method_entry_make directly and let it store its warnings
in the caller's struct, so the outermost rb_add_method emits them after
unlocking. Every warning is still emitted; the 'object_id' and '__send__'
ones, which used to be emitted twice, now once.
2026-10-21 16:40:46 -07:00
Koichi SasadaandClaude Opus 5.5 5771bd7a39 Read another Ractor's constant entries under the VM lock
rb_const_remove deleted the entry from the constant table without the VM
lock and then freed it, while a Ractor that does not own the class looks
the table up under the VM lock but reads the entry after releasing it.
Reading a constant while its owner removes it could crash in the table
lookup or return garbage (an Integer, Float or false that was never
assigned).

Delete under the VM lock, and have the readers that may not own the class
copy the entry while holding it (rb_const_lookup_copy).  Class#dup and
the singleton class clone walk the original's table under the lock for
the same reason; the Class#dup check collects the candidates under the
lock and runs rb_ractor_shareable_p after it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 05:49:20 +09:00
John Hawthorn b4ae8a7189 Skip Dir.scan device specs when readdir reports DT_REG 2026-10-21 12:01:58 -07:00
Max Bernstein 178043df56 ZJIT: Remove unused reasons and counters 2026-10-21 14:40:42 -04:00
Max Bernstein 5a11098754 ZJIT: Don't profile splatkw instruction
We don't use the profiles anymore.
2026-10-21 14:40:42 -04:00
Max Bernstein 3f0b6b911d ZJIT: Inline NilClass/Hash check in splatkw
Don't try and guess from profiles; nil check is cheap. Helps Rails activerecord/relation/delegation.rb:

    def create(model, ...)
      relation_class_for(model).new(model, ...)
    end

Depending on what keywords are passed (none, some), the hash in splatkw
can either be nil or a Hash. Just check for both cases. The `to_hash`
case is probably very rare.
2026-10-21 14:40:42 -04:00
Yusuke EndohandClaude Opus 5.5 053f8da910 Always use the no-inline rb_current_ec()
Under the M:N thread scheduler, a Ruby thread in a non-main Ractor may be
resumed on a different native thread after it blocks. The C compiler
assumes that the native thread never changes within a function, so it may
compute the address of ruby_current_ec once (`__tls_get_addr` in
libruby.so) and reuse it after the switch. The address then refers to the
TLS of the previous native thread, which leads to a SEGV or to using
another thread's ec.

    8.times.map do
      Ractor.new do
        50.times do
          ",,".split(/(,)/) { sleep 0.001 }
        end
        :ok
      end
    end.map(&:value)
    #=> [BUG] Segmentation fault at 0x0000000000000030 (with Clang 21)

arm64 and ppc64le already use the out-of-line rb_current_ec()
(RB_THREAD_CURRENT_EC_NOINLINE), which prevents the compiler from caching
the address. Use it on all platforms, and remove the macro.

[Bug #22453]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:26 +09:00
Yusuke EndohandClaude Opus 5.5 574524d570 Define RUBY_NT_SERIAL regardless of RB_THREAD_CURRENT_EC_NOINLINE
ruby_nt_serial and RUBY_NT_SERIAL were declared only when
RB_THREAD_CURRENT_EC_NOINLINE is not defined, so RUBY_DEBUG_LOG() did not
show the native thread serial on arm64 and ppc64le. ruby_nt_serial is not
related to the way ruby_current_ec is accessed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:27:26 +09:00
Nobuyoshi Nakada de04d82a87 Add conversions between rb_len_t and Fixnum 2026-10-21 23:55:00 +09:00
Kevin Newton 8eff6a2b7d [ruby/prism] Bump to v2.0.0
https://github.com/ruby/prism/commit/e2727368fd
2026-10-21 14:45:46 +00:00
Nobuyoshi Nakada 5c52c2a37b [Bug #22458] Fix out-of-bound in Time#strftime 2026-10-21 20:19:58 +09:00
Shourya Singh cfa50b0093 [DOC] Fix outdated results in examples
Some example results in the docs no longer match what Ruby prints:

* Array#flatten and Array#flatten!: Set#inspect now shows Set[6, 7]
* Module: Mod.constants also includes Math::DomainError
* Module#const_get and Module#const_set: Floats are shown with full
  precision
* Complex#imag: sin(PI/4) is 0.7071067811865475
* String#<<: missing # in #<Encoding:UTF-8>
* Enumerable#inject: typo #=>>
2026-10-21 17:52:28 +09:00
Burdette Lamar 536dc6f98b [DOC] Doc for Range class (#19427) 2026-10-21 16:29:43 +09:00
Yusuke EndohandClaude Opus 5.5 6b60cb45f9 Unpoison zombies before clearing FL_FINALIZE at shutdown
A Ractor's zombies with finalizers are poisoned and moved to the main
objspace when the Ractor terminates.  If the process exits before they
are finalized, rb_gc_impl_shutdown_call_finalizer_i touches them and
ASAN reports use-after-poison.

```
8.times.map do
  Ractor.new do
    10.times do
      4.times { ObjectSpace.define_finalizer(Object.new, proc { sleep 0.0005 }) }
      GC.start
      sleep 0.0001
    end
  end
end.each(&:join)
#=> ERROR: AddressSanitizer: use-after-poison (sometimes)
```

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 15:59:41 +09:00
Hiroshi SHIBATAandClaude Opus 5.5 6a81db9d5f Keep capture group indices in long
They index the match registers, whose count is an int, rather than a String, so rb_len_t does not apply. Only the length of a group name given as a String stays in rb_len_t.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:37:54 +09:00
Burdette Lamar fa2fee5a1d [DOC] Tweaks for String#tr (#19433) 2026-10-21 11:17:32 +09:00
Koichi SasadaandClaude Opus 5.5 7467d784eb Let an interrupted M:N io wait handle the interrupt and wait again
thread_sched_wait_events() returned thread_sched_wait_event when an
interrupt was already pending at entry, without looking at the fd, and
thread_io_wait() reported that as readiness: under RUBY_MN_THREADS=2,
IO#wait_readable on a pipe nobody wrote to returned truthy when a trap
arrived just before the wait.

A wakeup for a masked interrupt (a trap arriving inside a trap handler)
was reported as a timeout, so wait_readable(0.3) in a trap handler
returned nil at once.

Add thread_sched_wait_interrupted for both "stopped for an interrupt"
cases.  thread_io_wait() now runs the whole wait under one tag, handles
the interrupt and waits again on the M:N scheduler until its deadline,
and treats a wakeup without an event before the deadline the same way.
At the deadline it probes the fd once.  rb_thread_io_blocking_call()
retries func for every M:N outcome but unhandled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:08:38 +09:00
Koichi SasadaandClaude Opus 5.5 6e6f03b9bd Test zero-timeout probes at EOF while another thread wants the GVL
Covers the previous two commits: wait_readable(0) and
IO.select([io], nil, nil, 0) on an io at EOF must always report it,
also while another thread keeps asking for the GVL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:08:38 +09:00
Koichi SasadaandClaude Opus 5.5 d42ae65308 Don't report a skipped select as a timeout in do_select
Same as the previous commit, for the select(2) path used by IO.select
(and by thread_io_wait without poll).  When an interrupt was pending,
select was skipped with result 0, which wait_retryable takes as a
timeout, so IO.select([io], nil, nil, 0) could return nil for an io at
EOF.  With another thread looping on Thread.pass, 137-153 of about 10^6
such calls returned nil; 0 with this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:08:38 +09:00
Koichi SasadaandClaude Opus 5.5 5beeb48f7b Don't report a skipped ppoll as a timeout in thread_io_wait
wait_for_single_fd_blocking_region skips ppoll when an interrupt is
already pending, but left result 0, which wait_retryable reads as a
timeout.  With a zero timeout the deadline has passed, so it does not
retry: IO#wait_readable(0) returns nil even for a socket at EOF.  This
happens about once per 10^4 probes whenever another thread is around
(timer interrupt).

Net::HTTP#begin_transport probes a kept-alive connection with
wait_readable(0) && eof?, so it reused a connection the server had
already closed and the next non-idempotent request raised EOFError
(spec/ruby/library/net-http/http/request_spec.rb:71 on trunk_asan).

Report the skip as EINTR so the probe polls again after the interrupt
is handled.  The same pattern remains in do_select.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:08:38 +09:00
Kevin Newton 957a081892 [ruby/prism] Ship an official C++ interface
The C++ interface is intentionally minimal: it provides only a
couple of niceties in order to largely stay out of the way. It is
mostly used to provide:

* RAII for opaque resources
* Safe up and down casting for nodes
* string_view for various resources
* Iteration over resource lists

Note that if consumers want to use it, they must be on C++17 at
minimum.

https://github.com/ruby/prism/commit/57b8dbe0e9
2026-10-20 22:15:29 +00:00
Kevin Newton 85513912fa [ruby/prism] Use slice!(-n,n) instead of pop(n) to fix ractors
https://github.com/ruby/prism/commit/bda3cffb2a
2026-10-20 21:37:42 +00:00
Kevin Newton ac6ce58177 [ruby/prism] Fix cygwin
https://github.com/ruby/prism/commit/8adb1555ce
2026-10-20 21:33:59 +00:00
Kevin Newton 4f6a7e0fa9 [ruby/prism] Serialize using post-order traversal
This makes it so that you do not have to recurse when serializing
and deserializing, making this safe for deep trees.

https://github.com/ruby/prism/commit/ab92f0eb4b
2026-10-20 21:02:33 +00:00
XrXr c21fb62f50 ZJIT: Add macOS --with-gmp build test for ZJIT test binary
This is so the build failure in <https://github.com/ruby/ruby/pull/19138>
is covered on CI.
2026-10-20 16:48:11 -04:00
Nobuyoshi Nakada b01ca2f752 [ruby/prism] Fix printf format attribute on ucrt64-mingw gcc 16.3
`__MINGW_PRINTF_FORMAT` is defined in stdio.h.

```
prism/buffer.c: In function 'pm_buffer_append_format':
prism/buffer.c:114:5: warning: function 'pm_buffer_append_format' might be a candidate for 'gnu_printf' format attribute [-Wsuggest-attribute=format]
  114 |     int result = vsnprintf(NULL, 0, format, arguments);
      |     ^~~
prism/buffer.c:123:9: warning: function 'pm_buffer_append_format' might be a candidate for 'gnu_printf' format attribute [-Wsuggest-attribute=format]
  123 |         vsnprintf(buffer->value + cursor, length, format, arguments);
      |         ^~~~~~~~~
```

https://github.com/ruby/prism/commit/72da47973e
2026-10-20 20:29:18 +00:00
Kevin Newton 2564ebb3c9 [ruby/prism] Expose pm_node_list_append and pm_parser_constant_insert
https://github.com/ruby/prism/commit/4896c9f025
2026-10-20 20:14:48 +00:00
Kevin Newton 568ff20b0d [PRISM] Update to handle pm_string_t being just a view 2026-10-20 16:00:01 -04:00
Kevin Newton 8f4f3fdd8d [ruby/prism] Make strings just a view into memory
We do not want strings to have the ability or the contract of
having to be freed. This makes the API simpler and keeps us away
from dynamic allocation.

https://github.com/ruby/prism/commit/c9b06230ec
2026-10-20 16:00:01 -04:00
Erik Berlin c71329d429 [ruby/io-console] Keep IO#isatty an alias of IO#tty? on JRuby
The C extension defines both tty? and isatty in IO::platform_tty, so
they stay the same method once io/console is loaded. The JRuby
backends' PlatformTty only defined tty?, so after
https://github.com/ruby/io-console/commit/f686b8305 loading
io/console left IO#isatty as the original method, which ruby/spec's
"IO#isatty is an alias of IO#tty?" checks.

https://github.com/ruby/io-console/commit/77d675c69e
2026-10-20 19:12:05 +00:00
Kevin Newton 0fc3e6a8ae [ruby/prism] Avoid allocating when comparing magic comments
https://github.com/ruby/prism/commit/dd8b706c61
2026-10-20 19:07:47 +00:00
Kevin Newton 660b1f0bcf [ruby/prism] Default options start line to 1
https://github.com/ruby/prism/commit/770e381997
2026-10-20 17:25:17 +00:00
Kevin Newton 0848a99be4 [PRISM] Use new constant iteration APIs 2026-10-20 13:20:25 -04:00
Kevin Newton 01ef241436 [ruby/prism] Add index and at APIs for constants
So that consumers don't unintentionally encode the fact that a
constant id is actually 1 + its index in the constants table.

Ideally this makes it so that we can change the underlying
implementation if we want to.

https://github.com/ruby/prism/commit/3a7f3cb6d5
2026-10-20 13:20:25 -04:00
Kevin Newton 16130709da [PRISM] Use the new iteration APIs for errors and warnings 2026-10-20 12:28:27 -04:00
Kevin Newton f25e997399 [ruby/prism] Add a first/next API for our iterables
This is as opposed to the *_each APIs we have been using, because
that adds the overhead of a passed around callback.

https://github.com/ruby/prism/commit/671ddb0022
2026-10-20 12:28:27 -04:00
Kevin Newton c11cc418fd [PRISM] Use pm_visit_result_t when visiting for pm_node_find 2026-10-20 12:09:38 -04:00
Kevin Newton 3357fecc4b [ruby/prism] Expand visit function to return an enum
When visiting, sometimes you want to stop the whole walk, not just
stop at a certain level. For example when you find a particular node.

https://github.com/ruby/prism/commit/8dccefe1cc
2026-10-20 12:09:38 -04:00
Taketo Takashima 3d3d1b0329 [ruby/ipaddr] Add IPAddr#to_bits
Returns the binary representation of the address value as a string of
"0" and "1" characters, right adjusted to the address size (32
characters for IPv4, 128 for IPv6).

https://github.com/ruby/ipaddr/commit/63beaa749e
2026-10-20 12:32:51 +00:00