-
-
Notifications
You must be signed in to change notification settings - Fork 943
Closed
Milestone
Description
Summary
When a module uses def initialize(...) + super(...) and is prepended on a Struct subclass, JRuby incorrectly warns "Passing only keyword arguments to Struct#initialize will behave differently from Ruby 3.2" even though only positional arguments are being passed. For 1-member Structs, it crashes with a ClassCastException.
CRuby 3.4 produces no warnings and no crashes for identical code.
Environment
- JRuby 10.0.2.0 (3.4.2)
- OpenJDK 24.0.2+12-FR
Reproduction
#!/usr/bin/env ruby
# Reproduction: JRuby incorrectly handles `...` forwarding through `super` to Struct#initialize.
#
# Expected behavior (CRuby 3.4): no warnings, no crashes.
# Actual behavior (JRuby 10.0.2.0):
# - 1-member Struct: ClassCastException crash
# - 2-member Struct: works fine
# - 3+-member Struct: spurious "keyword arguments" warning
#
# Also affects `super(*args, **kwargs)` forwarding (3+ members warn).
$VERBOSE = true
module Wrapper
def initialize(...)
super(...)
end
end
# --- Case 1: 1-member Struct crashes ---
puts "--- Case 1: 1-member Struct ---"
begin
klass1 = Class.new(Struct.new(:a)) { prepend Wrapper }
klass1.new("x")
puts "OK"
rescue => e
puts "CRASH: #{e.class}: #{e.message}"
end
# --- Case 2: 2-member Struct works ---
puts "--- Case 2: 2-member Struct ---"
klass2 = Class.new(Struct.new(:a, :b)) { prepend Wrapper }
klass2.new("x", "y")
puts "OK"
# --- Case 3: 3-member Struct warns ---
puts "--- Case 3: 3-member Struct ---"
klass3 = Class.new(Struct.new(:a, :b, :c)) { prepend Wrapper }
klass3.new("x", "y", "z")
puts "OK"
# --- Case 4: bare `super` in def initialize(...) also triggers ---
puts "--- Case 4: bare super variant ---"
module WrapperBare
def initialize(...)
super
end
end
klass4 = Class.new(Struct.new(:a, :b, :c)) { prepend WrapperBare }
klass4.new("x", "y", "z")
puts "OK"
# --- Case 5: super(*args, **kwargs) also triggers ---
puts "--- Case 5: super(*args, **kwargs) ---"
module WrapperKwargs
def initialize(*args, **kwargs)
super(*args, **kwargs)
end
end
klass5 = Class.new(Struct.new(:a, :b, :c)) { include WrapperKwargs }
klass5.new("x", "y", "z")
puts "OK"
# --- Workaround: *args, &block without **kwargs does not warn ---
puts "--- Workaround: *args, &block ---"
module WrapperFixed
def initialize(*args, &block)
super(*args, &block)
end
end
klass6 = Class.new(Struct.new(:a, :b, :c)) { prepend WrapperFixed }
klass6.new("x", "y", "z")
puts "OK (no warning)"Run this script on JRuby and MRI to observe the difference in behavior.
Behavior by member count
| Members | Behavior | Expected |
|---|---|---|
| 1 | ClassCastException crash | No crash, no warning |
| 2 | Works fine | Works fine |
| 3+ | Spurious keyword argument warning | No warning |
Also affects
def initialize(...) + super(bare super, no parens)def initialize(*args, **kwargs) + super(*args, **kwargs)in included modules
Workaround
Using ruby2_keywords def initialize(*args, &block) + super(*args, &block) instead of def initialize(...) + super(...) avoids the bug.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels