Bug #16642
closedSplatted empty hash literal produces frozen hash object
Description
When splatting an empty hash literal, internally it's optimized using a global frozen hash object, but this implementation detail can leak into the ruby code outside:
ruby2_keywords def foo(*a) a.last end
h = foo(**{})
h[1] = 2
# can't modify frozen Hash: {} (FrozenError)
I think this can be considered a bug?
Files
Updated by jeremyevans0 (Jeremy Evans) about 4 years ago
I agree it is a bug. I'm not sure it is worth fixing. Basically, the reason behind it is the parser doesn't separate hash compilation from keyword argument compilation, and the optimization for **{}
as the only keyword argument ends up applying to hashes as well. It is simple to remove the optimization, but it will result in worse performance for keyword arguments using **{}
(which can be used to avoid keyword argument warnings in Ruby 2.7). Separate parsing and/or compilation of keyword arguments and hashes will fix this, and it is on my todo list.
Updated by jeremyevans0 (Jeremy Evans) about 4 years ago
Actually, looks like I didn't read the bug report closely enough. This is a different issue, and suggests that we should either recognize the frozen hash in the ruby2_keywords
case and dup it, or remove the optimization.
The issue I was referring to in my last comment is the fact that {**{}}.frozen?
is true, due to the same optimization.
Updated by jeremyevans0 (Jeremy Evans) about 4 years ago
- File ruby2_keywords-empty-kw-splat-16642.patch ruby2_keywords-empty-kw-splat-16642.patch added
- Status changed from Open to Closed
- ruby -v set to ruby 2.7.1p37 (2020-02-20) [x86_64-openbsd6.6]
- Backport changed from 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN to 2.5: DONTNEED, 2.6: DONTNEED, 2.7: REQUIRED
This was already fixed in master at f8a8f055123bc81fc13fa295b936504196df0da4, which changed it so ruby2_keywords does not pass empty keyword splats as hashes. Passing empty keyword splats as hashes is not needed in Ruby 3, it is only needed in Ruby 2.7 to avoid a final positional hash from being treated as keywords in a later method call.
Attached is a patch that fixes this in Ruby 2.7.
Updated by naruse (Yui NARUSE) about 4 years ago
- Backport changed from 2.5: DONTNEED, 2.6: DONTNEED, 2.7: REQUIRED to 2.5: DONTNEED, 2.6: DONTNEED, 2.7: DONE
ruby_2_7 commit:7804720c631d309b2f19457e703ecfb47a59589f.