The lazy loading of error_highlight, did_you_mean, and syntax_suggest
claimed the load by an atomic exchange before the load started. So a
caller in another Ractor or thread during the load returned the
undecorated message without waiting for the load to complete.
rs = 2.times.map do
Ractor.new do
nil.upcase
rescue => e
e.detailed_message(highlight: false)
end
end
rs.each { puts it.value, "" }
# One of them lacks the snippet by error_highlight
Load the gems with rb_autoload_load instead of rb_require_string, as a
constant reference would. The autoload's own synchronization makes
concurrent callers wait until the load is done. The flag to load the
gems only once is no longer needed: rb_autoload_load does nothing once
the gems are loaded, and whether to re-dispatch is now decided by whether
the load has just put a decorator above Exception#detailed_message.
This also makes the gems loaded through RubyGems' Kernel#require, so a
newer version of the gems installed by the user is activated, as it was
before they were lazily loaded.
[Bug #22442]
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Once rb_len_t widens this changes the `long *` output parameters of rb_range_beg_len(), rb_str_subpos(), rb_arithmetic_sequence_beg_len_step(), ruby_enc_find_basename() and ruby_enc_find_extname(), so extensions passing `long *` to them will need to be updated. The extension guides list these signatures verbatim and follow along.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rb_warn_deprecated_to_remove_at warns only when Warning[:deprecated]
is enabled, so a deprecation scheduled to warn by default in a later
version needed a hand-written rb_warn and a separate commit to switch.
rb_warn_to_remove_at emits the same message with rb_warn and no
category. rb_warn_scheduled_deprecation(X.Y, X.Z, ...) selects
between the two at compile time by RUBY_API_VERSION_CODE: gated by
Warning[:deprecated] before X.Y, shown by default from X.Y, and a
compile error in RUBY_DEBUG builds at X.Z, like the existing removal
check. ruby/version.h is now included unconditionally and RUBY_VERSION_SINCE
and the version-string macros are always defined for that, while the
compile-time checks and RBIMPL_TODO stay RUBY_DEBUG only. Both
functions are exported so that the new -test-/warning extension can
exercise both phases.
Use it for the two alias deprecations in rb_alias, which are scheduled
to warn by default in 4.2 and to be removed in 4.3.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
When two Ractors fail an assertion at nearly the same time both write into
the same stream, and the first report -- the interesting one -- is cut
short by the second:
rs = 100.times.map { Ractor.new { sleep rand(1..3); Ractor.fail_assert } }
produced a 25-line report ending in "Crashed while printing bug report",
every time.
Take a claim before writing. The first thread through writes its report
and aborts the process; a later one waits for that instead of writing its
own. The claiming thread is let through again, so the existing
crash-while-reporting path still works. The wait is bounded, so a writer
that hangs still ends the process as a crash rather than a hang.
rb_assert_failure_detail() writes a report without going through
report_bug(), so it needs the same claim.
The example above now produces one complete report.
[Bug #21146]
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The pattern of allocating an empty hash with `rb_hash_new()` and
then appending to it is relatively wasteful, because every time
we go over the hash capacity, we'll have to `realloc`, rehash etc.
It may even get worse if https://github.com/ruby/ruby/pull/18345
lands, as right now an empty hash starts with a capacity of 8,
but it may very well start with a capacity of 2.
Hence in cases where the final hash size is easy to predict, we
should use `rb_hash_new_capa()`.
rb_require_string delegates requires from non-main Ractors to the
main Ractor via rb_ractor_require, so the main-Ractor guard here was
unnecessary. Drop it and claim the load with an atomic exchange,
because Ractors run in parallel and the plain bool flag was only safe
under that guard. did_you_mean already keeps its state Ractor-safe,
so suggestions now appear in non-main Ractors as well.
Suggested by John Hawthorn.
require_decoration_gems is called while an exception is being displayed,
so $! holds that exception. rb_protect does not preserve errinfo across
the call, and the previous code cleared it to nil whenever a require
raised, losing the exception being shown. Save errinfo before the
requires and restore it afterward so $! is left untouched whether or not
a require fails.
The decoration gems are loaded lazily on the first error display, which
in a pre-forking server happens after fork in each child. That loads
their code outside copy-on-write shared memory and busts method caches
at runtime when they prepend Exception#detailed_message.
Process.warmup, called before the first fork by such servers, now loads
them eagerly so the prepended decorators land in shared memory and the
cache invalidation happens during boot.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
[Feature #21951]
Defer loading these gems from boot to the first error display. They
only decorate Exception#detailed_message, so requiring them at boot is
unnecessary overhead. `ruby -e1` with user-installed gems drops from
114.3ms to 30.2ms on Apple M1 Pro, matching the --disable-* flags for
the three gems.
exc_detailed_message forces the autoload entries once on the first
call, only in the main Ractor, and then re-dispatches to pick up the
detailed_message decorators the gems prepend. When the first call
arrives through an already-prepended decorator, re-dispatching is
skipped to avoid decorating twice. The gems are loaded with the
C-level require so that displaying an error neither invokes nor
depends on Kernel#require monkeypatches.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Set RUBY_TYPED_THREAD_SAFE_FREE on TypedData types whose dfree function
is trivially safe, and only frees its own memory
Types not yet marked as THREAD_SAFE_FREE:
- id2ref_tbl_type: sets the process-global id2ref_tbl to NULL.
- mutex_data_type: unlinks itself from another thread's keeping_mutexes list.
- autoload_data_type: deletes nodes from a shared intrusive list.
- autoload_const_type: deletes itself from a shared list.
- rb_cont_data_type: mutates the shared fiber pool and the global first_jit_cont list.
- rb_fiber_data_type: delegates to cont_free.
- FiberPoolDataType: mutates shared fiber pool free-lists.
- ractor_data_type: reconfigures global VM event-hook flags and call caches.
- exported_object_registry: frees a global table under the VM lock.
- rb_box_data_type (Box::Entry): unlinks classext from other live class and module objects.
- box_ext_cleanup_type: dereferences another String object and calls unlink.
- monitor_data_type: uses the default free, so the flag has no effect.
Co-authored-by: Luke Gruber <luke.gruber@shopify.com>
Rename for consistency with ISEQ_BODY naming convention. Remove the
separate rb_cfp_has_iseq/rb_cfp_has_pc wrappers — CFP_ISEQ and CFP_PC
return NULL for missing values, which works directly in if conditions.
Per tenderlove's review, every cfp->iseq read should go through an
accessor that accounts for JITFrame. Rename the field to _iseq so
direct access causes a compile error, and rename the accessors to
drop the zjit prefix (rb_cfp_iseq, rb_cfp_has_iseq, rb_cfp_pc,
rb_cfp_has_pc).
When rb_bug() is called (e.g. from a ZJIT Rust panic), the crash report
file could be truncated and left nearly empty due to two issues:
1. The crash report file was opened with default (buffered) I/O. If
rb_vm_bugreport() triggered a secondary crash, buffered output was
lost. Fix by setting the file to unbuffered with setvbuf().
2. After rb_bug() wrote the crash report, die() called abort() which
triggered our custom SIGABRT handler. The handler re-opened the same
crash report file with "w" mode, truncating the report that was
already written. Fix by resetting SIGABRT to SIG_DFL before abort().
With the following code:
```ruby
object = []
object.singleton_class
object.freeze
object.instance_variable_set(:@a, 42)
```
The previous error message was:
```
can't modify frozen #<Class:#<Array:0x00000631d1308f78>>: []
```
With this change, the error message is:
```
can't modify frozen Array: []
```
Since we show the inspect value of the affected object, I think
including the singleton class instead of the actual class if it
exists makes the error message harder to understand.
The `p->field = rb_gc_location(p->field)` isn't ideal because it means all
references are rewritten on compaction, regardless of whether the referenced
object has moved. This isn't good for caches nor for Copy-on-Write.
`rb_gc_mark_and_move` avoid needless writes, and most of the time allow to
have a single function for both marking and updating references.
Original issue: https://github.com/ruby/rdoc/issues/1128
The problem is caused by the `# :stopdoc:` directive in `bundled_gems.rb`,
which's scope covers the redefinition of `LoadError`.
Since the goal of `# :stopdoc:` is to hide the documentation of `Gem::BUNDLED_GEMS`,
we can use `# :nodoc:` on it instead.
The man page is clear that every `va_start` call MUST be succeeded by
the corresponding `va_end` call.
So `rb_raise` can't call `rb_exc_raise` before `va_end`, otherwise
`va_end` is never called.
Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
* Use FL_USER0 for ELTS_SHARED
This makes space in RString for two bits for chilled strings.
* Mark strings returned by `Symbol#to_s` as chilled
[Feature #20350]
`STR_CHILLED` now spans on two user flags. If one bit is set it
marks a chilled string literal, if it's the other it marks a
`Symbol#to_s` chilled string.
Since it's not possible, and doesn't make much sense to include
debug info when `--debug-frozen-string-literal` is set, we can't
include allocation source, but we can safely include the symbol
name in the warning message, making it much easier to find the source
of the issue.
Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
---------
Co-authored-by: Étienne Barrié <etienne.barrie@gmail.com>
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
Previously, it always used stderr. Slight shuffle of the first line
of the crash due to reusing code from rb_bug():
```diff
-Assertion Failed: /ruby/object.c:649:rb_obj_itself:false
+/ruby/object.c:649: Assertion Failed: rb_obj_itself:false
```
Tested locally to confirm that it writes to the file given with
RUBY_CRASH_REPORT. Follow-up for [Feature #19790].
to show unused block warning strictly.
```ruby
class C
def f = nil
end
class D
def f = yield
end
[C.new, D.new].each{|obj| obj.f{}}
```
In this case, `D#f` accepts a block. However `C#f` doesn't
accept a block. There are some cases passing a block with
`obj.f{}` where `obj` is `C` or `D`. To avoid warnings on
such cases, "unused block warning" will be warned only if
there is not same name which accepts a block.
On the above example, `C.new.f{}` doesn't show any warnings
because there is a same name `D#f` which accepts a block.
We call this default behavior as "relax mode".
`strict_unused_block` new warning category changes from
"relax mode" to "strict mode", we don't check same name
methods and `C.new.f{}` will be warned.
[Feature #15554]