[Edit: I saw @sawa's answer after completing mine. I was right: it's a bug!]
That different results are obtained when a literal empty hash is double-splatted and an empty hash that is the value of a variable is double-splatted, seems to me to be prima facia evidence that it's due to a bug in Ruby. To understand why the bug may exist, consider first the reason for passing a double-splatted hash to a method.
Suppose we define a method with some keyword arguments:
def my_method(x, a: 'cat', b: 'dog')
[x, a, b]
end
my_method(1)
#=> [1, "cat", "dog"]
The defaults apply for the two keyword arguments. Now try:
my_method(1, a: 2)
#=> [1, 2, "dog"]
Now lets use a double-splatted hash.
h = { a: 2, b: 3 }
my_method(1, **h)
#=> [1, 2, 3]
This works the same with required keyword arguments (Ruby 2.1+).
def my_method(x, a:, b:)
[x, a, b]
end
my_method(1, **h)
#=> [1, 2, 3]
However, to use a double-splatted hash as an argument, the hash cannot contain keys that are not listed as arguments in the method definition.
def my_method(x, a:)
[x, a]
end
h = { a: 2, b: 3 }
my_method(1, **h)
#=> ArgumentError: unknown keyword: b
The question therefore arises: can a double-splatted empty hash be passed as an argument, considering that all the hash's keys (none) are included as arguments in the method definition (which case it would have no effect)? Let's try it.
def my_method(x)
[x]
end
my_method(1, **{})
#=> [1]
Yes!
h = {}
my_method(1, **h)
#=> ArgumentError: wrong number of arguments (given 2, expected 1)
No!
That makes no sense. So assuming this is a bug, how might it have arisen? I suspect it may have to do with an optimization of Ruby, as suggested by the OP. It the empty hash is a literal, one could deal with it earlier in Ruby's code than if it were the value of variable. I'm guessing that whoever wrote the earlier code answered "yes" to the question I posed above, and whoever wrote the latter code answered "no", or failed to consider the case of an empty hash at that point.
If this bug theory is not shot down, the OP or someone else should report it.