Issue #21039 has been reported by Eregon (Benoit Daloze).
----------------------------------------
Bug #21039: Ractor.make_shareable breaks block semantics (seeing updated captured variables) of existing blocks
https://bugs.ruby-lang.org/issues/21039
* Author: Eregon (Benoit Daloze)
* Status: Open
* Assignee: ko1 (Koichi Sasada)
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
```ruby
def make_counter
count = 0
nil.instance_exec do
[-> { count }, -> { count += 1 }]
end
end
get, increment = make_counter
reader = Thread.new {
sleep 0.01
loop do
p get.call
sleep 0.1
end
}
writer = Thread.new {
loop do
increment.call
sleep 0.1
end
}
ractor_thread = Thread.new {
sleep 1
Ractor.make_shareable(get)
}
sleep 2
```
This prints:
```
1
2
3
4
5
6
7
8
9
10
10
10
10
10
10
10
10
10
10
10
```
But it should print 1..20, and indeed it does when commenting out the `Ractor.make_shareable(get)`.
This shows a given block/Proc instance is concurrently broken by `Ractor.make_shareable`, IOW Ractor is breaking fundamental Ruby semantics of blocks and their captured/outer variables or "environment".
It's expected that `Ractor.make_shareable` can `freeze` objects and that may cause some FrozenError, but here it's not a FrozenError, it's wrong values being read.
I think what should happen instead is that `Ractor.make_shareable` should create a new Proc and mutate that.
However, if the Proc is inside some other object and not just directly the argument, that wouldn't work (like `Ractor.make_shareable([get])`).
So I think one fix would to be to only accept Procs for `Ractor.make_shareable(obj, copy: true)`.
FWIW that currently doesn't allow Procs, it gives `<internal:ractor>:828:in 'Ractor.make_shareable': allocator undefined for Proc (TypeError)`.
I think the only other way, and I think it would be a far better way would be to not support making Procs shareable with `Ractor.make_shareable`.
Instead it could be some new method like `isolated { ... }` or `Proc.isolated { ... }` or `Proc.snapshot_outer_variables { ... }` or so, only accepting a literal block (to avoid mutating/breaking an existing block), and that would snapshot outer variables (or require no outer variables like Ractor.new's block, or maybe even do `Ractor.make_shareable(copy: true)` on outer variables) and possibly also set `self` since that's anyway needed.
That would make such blocks with different semantics explicit, which would fix the problem of breaking the intention of who wrote that block and whoever read that code, expecting normal Ruby block semantics, which includes seeing updated outer variables.
Related: #21033 https://bugs.ruby-lang.org/issues/18243#note-5
Extracted from https://bugs.ruby-lang.org/issues/21033#note-14
--
https://bugs.ruby-lang.org/
Issue #20205 has been reported by byroot (Jean Boussier).
----------------------------------------
Feature #20205: Enable `frozen_string_literal` by default
https://bugs.ruby-lang.org/issues/20205
* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Context
The `frozen_string_literal: true` pragma was introduced in Ruby 2.3, and as far as I'm aware the plan was initially to make it the default for Ruby 3.0, but this plan was abandoned because it would be too much of a breaking change without any real further notice.
According to Matz, he still wishes to enable `frozen_string_literal` by default in the future, but a reasonable migration plan is required.
The main issue is backward compatibility, flipping the switch immediately would break a lot of code, so there must be some deprecation period.
The usual the path forward for this kind of change is to emit deprecation warnings one of multiple versions in advance.
One example of that was the Ruby 2.7 keyword argument deprecation. It was quite verbose, and some users were initially annoyed, but I think the community pulled through it and I don't seem to hear much about it anymore.
So for frozen string literals, the first step would be to start warning when a string that would be frozen in the future is mutated.
### Deprecation Warning Implementation
I implemented a quick proof of concept with @etienne in https://github.com/Shopify/ruby/pull/549
In short:
- Files with `# frozen_string_literal: true` or `# frozen_string_literal: false` don't change in behavior at all.
- Files with no `# frozen_string_literal` comment are compiled to use `putchilledstring` opcode instead of regular `putstring`.
- This opcode mark the string with a user flag, when these strings are mutated, a warning is issued.
Currently the proof of concept issue the warning at the mutation location, which in some case can make locating where the string was allocated a bit hard.
But it is possible to improve it so the message also include the location at which the literal string was allocated, and learning from the keyword argument warning experience,
we can record which warnings were already issued to avoid spamming users with duplicated warnings.
As currently implemented, there is almost no overhead. If we modify the implementation to record the literal location,
we'd incur a small memory overhead for each literal string in a file without an explicit `frozen_string_literal` pragma.
But I believe we could do it in a way that has no overhead if `Warning[:deprecated] = false`.
### Timeline
The migration would happen in 3 steps, each step can potentially last multiple releases. e.g. `R0` could be `3.4`, `R1` be `3.7` and `R2` be `4.0`.
I don't have a strong opinion on the pace.
- Release `R0`: introduce the deprecation warning (only if deprecation warnings enabled).
- Release `R1`: make the deprecation warning show up regardless of verbosity level.
- Release `R2`: make string literals frozen by default.
### Impact
Given that `rubocop` is quite popular in the community and it has enforced the usage of `# frozen_string_literal: true` for years now,
I suspect a large part of the actively maintained codebases in the wild wouldn't see any warnings.
And with recent versions of `minitest` enabling deprecation warnings by default (and [potentially RSpec too](https://github.com/rspec/rspec-core/issues/2867))
the few that didn't migrate will likely be made compatible quickly.
The real problem of course are the less actively developed libraries and applications. For such cases, any codebase can remain compatible by setting `RUBYOPT="--disable=frozen_string_literal"`,
and so even after `R2` release. The flag would never be removed any legacy codebase can continue upgrading Ruby without changing a single line of cod by just flipping this flag.
### Workflow for library maintainers
As a library maintainer, fixing the deprecation warnings can be as simple as prepending `# frozen_string_literal: false` at the top of all their source files, and this will keep working forever.
Alternatively they can of course make their code compatible with frozen string literals.
Code that is frozen string literal compatible doesn't need to explicitly declare it. Only code that need it turned of need to do so.
### Workflow for application owners
For application owners, the workflow is the same than for libraries.
However if they depend on a gem that hasn't updated, or that they can't upgrade it, they can run their application with `RUBYOPT="--disable=frozen_string_literal"` and it will keep working forever.
Any user running into an incompatibility issue can set `RUBYOPT="--disable=frozen_string_literal"` forever, even in `4.x`, the only thing changing is the default value.
And any application for which all dependencies have been made fully frozen string literal compatible can set `RUBYOPT="--enable=frozen_string_literal"` and start immediately removing magic comment from their codebase.
--
https://bugs.ruby-lang.org/
Issue #19473 has been reported by Eregon (Benoit Daloze).
----------------------------------------
Bug #19473: can't be called from trap context (ThreadError) is too limiting
https://bugs.ruby-lang.org/issues/19473
* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Simple reproducer:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
-e:1:in `synchronize': can't be called from trap context (ThreadError)
from -e:1:in `block in <main>'
from -e:1:in `kill'
from -e:1:in `<main>'
```
Expected behavior:
```
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
truffleruby 22.3.1, like ruby 3.0.3, GraalVM CE Native [x86_64-linux]
:OK
$ ruby -ve 'm=Mutex.new; trap(:HUP) { m.synchronize { p :OK } }; Process.kill :HUP, Process.pid; sleep 0.1'
jruby 9.4.0.0 (3.1.0) 2022-11-23 95c0ec159f OpenJDK 64-Bit Server VM 17.0.6+10 on 17.0.6+10 +jit [x86_64-linux]
:OK
```
This exception is highly problematic, for instance it breaks `Timeout.timeout` in `trap`:
https://github.com/ruby/timeout/issues/17#issuecomment-1142035939
I suppose this behavior is because *sometimes* it's problematic to lock a Mutex in trap, e.g., if it's already locked by the main thread/fiber.
However, that's just one case, not all, so we should not raise an exception early like that.
There seems to be no valid reason to prevent *all* `Mutex#synchronize` in `trap`.
After all, if the Mutex for instance is only used in `trap`, it's well-defined AFAIK.
For instance a given trap handler does not seem executed concurrently:
```
$ ruby -ve 'trap(:HUP) { puts "in trap\n"+caller.join("\n")+"\n\n"; sleep 0.1 }; pid = Process.pid; Process.wait fork { 20.times { Process.kill :HUP, pid } }; sleep 1'
ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
in trap
-e:1:in `wait'
-e:1:in `<main>'
in trap
-e:1:in `wait'
-e:1:in `<main>'
in trap
-e:1:in `wait'
-e:1:in `<main>'
in trap
-e:1:in `wait'
-e:1:in `<main>'
in trap
-e:1:in `wait'
-e:1:in `<main>'
in trap
-e:1:in `wait'
-e:1:in `<main>'
```
And if the trap handler using the Mutex is never called while the Mutex is held by the main thread/fiber, there is also no problem.
--
https://bugs.ruby-lang.org/
Issue #21029 has been reported by qnighy (Masaki Hara).
----------------------------------------
Bug #21029: Prism behavior for `defined? (;x)` differs
https://bugs.ruby-lang.org/issues/21029
* Author: qnighy (Masaki Hara)
* Status: Open
* ruby -v: ruby 3.5.0dev (2025-01-11T03:21:57Z master 1b3037081e) +PRISM [x86_64-linux]
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
Prism has a different behavior for `(;expr)` when used in `defined?` predicate:
```console
% ./miniruby --parser=prism -e "p defined? (;x)"
nil
% ./miniruby --parser=parse.y -e "p defined? (;x)"
"expression"
```
Although not a significant difference, aligning either of them with the other would be better.
--
https://bugs.ruby-lang.org/
Issue #20405 has been reported by nobu (Nobuyoshi Nakada).
----------------------------------------
Feature #20405: Inline comments
https://bugs.ruby-lang.org/issues/20405
* Author: nobu (Nobuyoshi Nakada)
* Status: Open
----------------------------------------
I propose a new inline comment syntax.
```ruby
p (| This is a comment (| and nested one |) /:|) (:|) #=> :|
```
--
https://bugs.ruby-lang.org/
Issue #19154 has been reported by fxn (Xavier Noria).
----------------------------------------
Bug #19154: Specify require and autoload guarantees in ractors
https://bugs.ruby-lang.org/issues/19154
* Author: fxn (Xavier Noria)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.0preview3 (2022-11-27) [x86_64-darwin22]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Given a file `c.rb`:
```ruby
class C
end
```
the following script:
```ruby
r1 = Ractor.new do
require './c.rb'
end
r2 = Ractor.new do
require './c.rb'
end
r1.take
r2.take
```
raises:
```
% ruby -v foo.rb
ruby 3.2.0preview3 (2022-11-27) [x86_64-darwin22]
foo.rb:1: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
#<Thread:0x000000010fee2928 run> terminated with exception (report_on_exception is true):
#<Thread:0x00000001102acfe0 run> terminated with exception (report_on_exception is true):
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:164:in `ensure in require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from <internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:167:in `require'
from foo.rb:6:in `block in <main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:37:in `require'<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:164:in `ensure in require': : can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from <internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:167:in `require'
can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError) from foo.rb:2:in `block in <main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:37:in `require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from foo.rb:2:in `block in <main>'
from foo.rb:6:in `block in <main>'
<internal:ractor>:698:in `take': thrown by remote Ractor. (Ractor::RemoteError)
from foo.rb:9:in `<main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:164:in `ensure in require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from <internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:167:in `require'
from foo.rb:2:in `block in <main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:37:in `require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from foo.rb:2:in `block in <main>'
```
Would it be possible to have documentation about their interaction?
This is important also to understand autoloading within ractors, since constant references may trigger `require` calls.
--
https://bugs.ruby-lang.org/
Issue #21024 has been reported by jprokop (Jarek Prokop).
----------------------------------------
Bug #21024: Ruby including <cstdbool> generates compilation warning with GCC 15, header is deprecated in C++17,
https://bugs.ruby-lang.org/issues/21024
* Author: jprokop (Jarek Prokop)
* Status: Open
* ruby -v: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [x86_64-linux]
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN
----------------------------------------
C++ compiler in GCC 15 now generates a warning when trying to include `<cstdbool>`.
This file is being included when compiling with C++ compiler from `include/ruby/internal/stdbool.h`.
In C++, true, false and bool are keywords so including headers for boolean definitions should not be necessary.
Alternatively, just including <stdbool.h> should be sufficient as that is what the `#include <cstdbool>` results in with GCC.
Simplest fix is just deleting the lines that are including the file (see attached patch and comments 2 and 3 from downstream issue linked in the bottom section "Additional info").
Considering the wide array of compilers Ruby aims to support to be compiled with I wanted to make an issue here first to make sure
there isn't a supported compiler that wouldn't agree with this approach.
Reproduction:
Obtain GCC 15, for Fedora Rawhide there is currently a repo of maintainer, I use it in Fedora Rawhide container (podman is replaceable for docker here):
```
host$ podman run -it --rm registry.fedoraproject.org/fedora:rawhide bash
container# echo "[gcc_15]
name=pre-release GCC 15 Repo
baseurl=https://fedorapeople.org/~dmalcolm/gcc/gcc-15-mass-prebuild/$basear…
type=rpm-md
skip_if_unavailable=True
gpgcheck=0
repo_gpgcheck=0
enabled=1
enabled_metadata=1" > /etc/yum.repos.d/gcc_15.repo
container# dnf install --assumeyes g++ ruby-devel
```
^ should install `g++-15.0.0` and `ruby.h` needed for the reproducer.
Note: that baseurl is a custom space of a Fedora maintainer.
Have a file named for example `main.cpp`:
``` cpp
#include <ruby.h>
int main(void) {
return 0;
}
```
Compile with GCC 15
```
$ g++ main.cpp
```
Current behavior:
Compiling throws warning:
```
$ g++ main.cpp
In file included from /usr/include/ruby/internal/stdbool.h:30,
from /usr/include/ruby/backward/2/bool.h:22,
from /usr/include/ruby/defines.h:74,
from /usr/include/ruby/ruby.h:25,
from /usr/include/ruby.h:38,
from reproducer.cpp:1:
/usr/include/c++/15/cstdbool:48:6: warning: #warning "<cstdbool> is deprecated in C++17, remove the #include" [-Wcpp]
48 | # warning "<cstdbool> is deprecated in C++17, remove the #include"
| ^~~~~~~
```
Expected:
Compilation throws no warnings and compiles the executable:
```
$ g++ reproducer.cpp
$ echo $?
0
```
Additional info:
```
$ g++ --version
g++ (GCC) 15.0.0 20241203 (Red Hat 15.0.0-0)
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```
This was met during recompilation of libdnf5 that includes `-Werror` in their compilation flags,
See downstream discussion: https://bugzilla.redhat.com/show_bug.cgi?id=2336567
Considering the inclusion of the <cstdbool> throws me up to 5 years ago back ( https://github.com/ruby/ruby/blob/9e6e39c3512f7a962c44dc3729c98a0f8be90341/… ), I assume this warning will be the case with older rubies as well as newest master when using GCC 15.
---Files--------------------------------
0001-Stop-including-cstdbool-in-include-ruby-internal-std.patch (869 Bytes)
--
https://bugs.ruby-lang.org/
Issue #20968 has been reported by koic (Koichi ITO).
----------------------------------------
Bug #20968: `Array#fetch_values` unexpected method name in stack trace
https://bugs.ruby-lang.org/issues/20968
* Author: koic (Koichi ITO)
* Status: Open
* ruby -v: ruby 3.4.0dev (2024-12-19T04:44:56Z master 2783868de2) +PRISM [x86_64-darwin23]
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
It seems that the current Ruby implementation is displaying unexpected method name in stack trace.
## Expected
Similar to `Hash#fetch_values`, the method name `Array#fetch_values` is expected to be displayed in the stack trace.
```console
$ ruby -e '{k: 42}.fetch_values(:unknown)'
-e:1:in 'Hash#fetch_values': key not found: :unknown (KeyError)
from -e:1:in '<main>'
$ ruby -e '[1].fetch_values(42)'
-e:1:in 'Array#fetch_values': index 42 outside of array bounds: -1...1 (IndexError)
from -e:1:in '<main>'
```
## Actual
The stack trace displays the `Array#fetch` method, which user is not aware of, along with the `<internal.array>` stack trace.
```console
$ ruby -e '[1].fetch_values(42)'
<internal:array>:211:in 'Array#fetch': index 42 outside of array bounds: -1...1 (IndexError)
from <internal:array>:211:in 'block in Array#fetch_values'
from <internal:array>:211:in 'Array#map!'
from <internal:array>:211:in 'Array#fetch_values'
from -e:1:in '<main>'
```
It likely requires an approach such as implementing it in C, as suggested in https://github.com/ruby/ruby/pull/11555.
--
https://bugs.ruby-lang.org/
Issue #20714 has been reported by Earlopain (A S).
----------------------------------------
Bug #20714: Handle optional dependencies in `bundled_gems.rb`
https://bugs.ruby-lang.org/issues/20714
* Author: Earlopain (A S)
* Status: Open
* ruby -v: 3.3.5
* Backport: 3.1: UNKNOWN, 3.2: UNKNOWN, 3.3: UNKNOWN
----------------------------------------
I've encountered a few places around bundled gems where the library doesn't care if the gem is available, but will still provide some functionallity if it is.
The way to accomplish that right now seems to be by setting `$VERBOSE = nil` and resetting it later again to not bother the user with the warning about the gem. However, this has the effect of silencing the warning about other gems as well, that may not be prepared about the bundling.
From `ruby/reline` for example: https://github.com/ruby/reline/blob/c90f08f7e308d2f1cdd7cfaf9939fe45ce546fd…
Or the `logging` gem: https://github.com/TwP/logging/blob/df41715364f7eb8c65098cd3c3316677ef1f378…
I propose to simply delay the warning to the next require. GitHub PR at ...
--
https://bugs.ruby-lang.org/