https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112020-01-11T00:31:51ZRuby Issue Tracking SystemRuby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=837642020-01-11T00:31:51Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>Thank you for the report! I cannot reproduce the issue by a simple config.ru:</p>
<pre><code>require "rack/oauth2"
use Rack::OAuth2::Server::Resource::Bearer, 'The API' do |request|
request.access_token
end
</code></pre>
<p>Adding <code>p args</code> into the definition of Rack's <code>use</code> shows only <code>["The API"]</code>. So I'd like to try it with the source code of gitlab. I have no idea at all about gitlab, so could you tell me how to reproduce it?</p> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=837652020-01-11T00:52:32Zioquatix (Samuel Williams)samuel@oriontransfer.net
<ul></ul><p>We cannot reproduce this.</p>
<p>Can you make some script to reproduce this in isolation?</p>
<p>Including Gemfile and Gemfile.lock details.</p> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=837662020-01-11T01:21:45Zioquatix (Samuel Williams)samuel@oriontransfer.net
<ul></ul><p><a href="https://github.com/ruby-grape/grape/blob/d58dc0ab7a0b51625217deedd8110d1030be7cf7/lib/grape/middleware/stack.rb#L80-L84" class="external">https://github.com/ruby-grape/grape/blob/d58dc0ab7a0b51625217deedd8110d1030be7cf7/lib/grape/middleware/stack.rb#L80-L84</a> is probably responsible for the failure.</p> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=837672020-01-11T01:28:09Zioquatix (Samuel Williams)samuel@oriontransfer.net
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Rejected</i></li><li><strong>Assignee</strong> set to <i>ioquatix (Samuel Williams)</i></li></ul><p><a href="https://github.com/ruby-grape/grape/issues/1967" class="external">https://github.com/ruby-grape/grape/issues/1967</a></p> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=837882020-01-11T23:44:59Zioquatix (Samuel Williams)samuel@oriontransfer.net
<ul><li><strong>Status</strong> changed from <i>Rejected</i> to <i>Open</i></li></ul><p>On Ruby 2.7.0:</p>
<pre><code>irb(main):020:-> x = [1, 2, ->{}]; puts(*x, &x.pop)
1
2
=> nil
irb(main):021:-> x = [1, 2, ->{}]; puts(*x, &x.last)
1
2
#<Proc:0x0000562763a56398 (irb):21 (lambda)>
=> nil
</code></pre>
<p>This seems like buggy behaviour related to the original issue.</p> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=837892020-01-11T23:59:46Zjeremyevans0 (Jeremy Evans)merch-redmine@jeremyevans.net
<ul></ul><p>ioquatix (Samuel Williams) wrote:</p>
<blockquote>
<p>On Ruby 2.7.0:</p>
<pre><code>irb(main):020:-> x = [1, 2, ->{}]; puts(*x, &x.pop)
1
2
=> nil
irb(main):021:-> x = [1, 2, ->{}]; puts(*x, &x.last)
1
2
#<Proc:0x0000562763a56398 (irb):21 (lambda)>
=> nil
</code></pre>
<p>This seems like buggy behaviour related to the original issue.</p>
</blockquote>
<p>I'm not sure if that behavior is buggy. puts ignores a passed block, so the behavior seems expected.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">a</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">b</span><span class="p">)</span>
<span class="nb">p</span> <span class="p">[</span><span class="n">args</span><span class="p">,</span> <span class="n">b</span><span class="p">]</span>
<span class="k">end</span>
<span class="n">x</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="o">-></span><span class="p">{}];</span> <span class="n">a</span><span class="p">(</span><span class="o">*</span><span class="n">x</span><span class="p">,</span> <span class="o">&</span><span class="n">x</span><span class="p">.</span><span class="nf">pop</span><span class="p">)</span>
<span class="c1"># => [[1, 2], #<Proc:0x00001100c1362518 (irb):4 (lambda)>]</span>
<span class="n">x</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="o">-></span><span class="p">{}];</span> <span class="n">a</span><span class="p">(</span><span class="o">*</span><span class="n">x</span><span class="p">,</span> <span class="o">&</span><span class="n">x</span><span class="p">.</span><span class="nf">last</span><span class="p">)</span>
<span class="c1"># => [[1, 2, #<Proc:0x000011004f2b0ba0 (irb):5 (lambda)>], #<Proc:0x000011004f2b0ba0 (irb):5 (lambda)>]</span>
</code></pre>
<p>Same results with Ruby 1.9.</p> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=837902020-01-12T00:12:58Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>Ruby 2.7 partially changed the behavior. Coped from my comment: <a href="https://github.com/ruby-grape/grape/issues/1967#issuecomment-573366122" class="external">https://github.com/ruby-grape/grape/issues/1967#issuecomment-573366122</a></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># in 2.6 or before</span>
<span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="o">-></span> <span class="p">{}];</span> <span class="n">foo</span><span class="p">(</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">args</span><span class="p">.</span><span class="nf">pop</span><span class="p">)</span> <span class="c1">#=> passes [1, 2] (bug; [1, 2, ->{}] is expected)</span>
<span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="o">-></span> <span class="p">{}];</span> <span class="n">foo</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">args</span><span class="p">.</span><span class="nf">pop</span><span class="p">)</span> <span class="c1">#=> passes [0, 1, 2] (bug; [0, 1, 2, ->{}] is expected)</span>
<span class="c1"># in 2.7</span>
<span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="o">-></span> <span class="p">{}];</span> <span class="n">foo</span><span class="p">(</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">args</span><span class="p">.</span><span class="nf">pop</span><span class="p">)</span> <span class="c1">#=> passes [1, 2] (bug; [1, 2, ->{}] is expected)</span>
<span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="o">-></span> <span class="p">{}];</span> <span class="n">foo</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">args</span><span class="p">.</span><span class="nf">pop</span><span class="p">)</span> <span class="c1">#=> passes [0, 1, 2, ->{}] (good)</span>
</code></pre>
<p>So, there are two issues.</p>
<ul>
<li>The issue that this ticket says is caused by a behavior change of 2.7, and ruby-grape's change will fix the issue <a href="https://github.com/ruby-grape/grape/commit/dec3e1ff5dbf3215a714565e62b12bd2ef6b0ddb" class="external">https://github.com/ruby-grape/grape/commit/dec3e1ff5dbf3215a714565e62b12bd2ef6b0ddb</a>
</li>
<li>The behavior of <code>args = [1, 2, -> {}]; foo( *args, &args.pop)</code> should pass <code>[1, 2, ->{}]</code>, and we should fix the bug on the ruby interpreter.</li>
</ul> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=837922020-01-12T00:52:34Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul><li><strong>Related to</strong> <i><a class="issue tracker-1 status-5 priority-4 priority-default closed" href="/issues/16504">Bug #16504</a>: `foo(*args, &args.pop)` should pass all elements of args</i> added</li></ul> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=837962020-01-12T13:40:18Zsawa (Tsuyoshi Sawada)
<ul><li><strong>Subject</strong> changed from <i>Argument added both to splat and last &block argument</i> to <i>Argument is added to both splat and last &block argument</i></li><li><strong>Description</strong> updated (<a title="View differences" href="/journals/83796/diff?detail_id=56145">diff</a>)</li></ul> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=838022020-01-12T16:38:30ZEregon (Benoit Daloze)
<ul></ul><p>mame (Yusuke Endoh) wrote:</p>
<blockquote>
<ul>
<li>The behavior of <code>args = [1, 2, -> {}]; foo( *args, &args.pop)</code> should pass <code>[1, 2, ->{}]</code>, and we should fix the bug on the ruby interpreter.</li>
</ul>
</blockquote>
<p>Why is that better than the previous behavior?<br>
Changing that will be more incompatible.</p> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=838032020-01-12T16:45:59ZEregon (Benoit Daloze)
<ul></ul><p>FWIW the 2.6 behavior is detailed in ruby/spec:<br>
<a href="https://github.com/ruby/spec/blob/84d606aa8e85a8fef6521b3402dae612d04288c4/language/send_spec.rb#L424-L445" class="external">https://github.com/ruby/spec/blob/84d606aa8e85a8fef6521b3402dae612d04288c4/language/send_spec.rb#L424-L445</a><br>
From commit<br>
<a href="https://github.com/ruby/spec/commit/01992ab93dd893d9e8bf79db9f5ff7d250952097" class="external">https://github.com/ruby/spec/commit/01992ab93dd893d9e8bf79db9f5ff7d250952097</a><br>
which shows RubyGems used to rely on the 2.6 behavior.</p>
<p>What I want to say is existing code relies on 2.6 behavior.<br>
It's probably good to always evaluate positional arguments before the block argument for consistency, but it's an incompatible change.</p> Ruby master - Bug #16500: Argument is added to both splat and last &block argumenthttps://bugs.ruby-lang.org/issues/16500?journal_id=862392020-06-18T15:19:53Zjeremyevans (Jeremy Evans)code@jeremyevans.net
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Closed</i></li></ul><p>Applied in changeset <a class="changeset" title="Dup splat array in certain cases where there is a block argument This makes: ```ruby args = [..." href="https://bugs.ruby-lang.org/projects/ruby-master/repository/git/revisions/aae8223c7076483f1f1641181088790b2f3a66dd">git|aae8223c7076483f1f1641181088790b2f3a66dd</a>.</p>
<hr>
<p>Dup splat array in certain cases where there is a block argument</p>
<p>This makes:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"> <span class="n">args</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="o">-></span> <span class="p">{}];</span> <span class="n">foo</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">args</span><span class="p">.</span><span class="nf">pop</span><span class="p">)</span>
</code></pre>
<p>call <code>foo</code> with 1, 2, and the lambda, in addition to passing the<br>
lambda as a block. This is different from the previous behavior,<br>
which passed the lambda as a block but not as a regular argument,<br>
which goes against the expected left-to-right evaluation order.</p>
<p>This is how Ruby already compiled arguments if using leading<br>
arguments, trailing arguments, or keywords in the same call.</p>
<p>This works by disabling the optimization that skipped duplicating<br>
the array during the splat (splatarray instruction argument<br>
switches from false to true). In the above example, the splat<br>
call duplicates the array. I've tested and cases where a<br>
local variable or symbol are used do not duplicate the array,<br>
so I don't expect this to decrease the performance of most Ruby<br>
programs. However, programs such as:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"> <span class="n">foo</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">bar</span><span class="p">)</span>
</code></pre>
<p>could see a decrease in performance, if <code>bar</code> is a method call<br>
and not a local variable.</p>
<p>This is not a perfect solution, there are ways to get around<br>
this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"> <span class="n">args</span> <span class="o">=</span> <span class="no">Struct</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:a</span><span class="p">).</span><span class="nf">new</span><span class="p">([</span><span class="ss">:x</span><span class="p">,</span> <span class="ss">:y</span><span class="p">])</span>
<span class="k">def</span> <span class="nc">args</span><span class="o">.</span><span class="nf">to_a</span><span class="p">;</span> <span class="n">a</span><span class="p">;</span> <span class="k">end</span>
<span class="k">def</span> <span class="nc">args</span><span class="o">.</span><span class="nf">to_proc</span><span class="p">;</span> <span class="n">a</span><span class="p">.</span><span class="nf">pop</span><span class="p">;</span> <span class="o">-></span><span class="p">{};</span> <span class="k">end</span>
<span class="n">foo</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">&</span><span class="n">args</span><span class="p">)</span>
<span class="c1"># calls foo with 1 argument (:x)</span>
<span class="c1"># not 2 arguments (:x and :y)</span>
</code></pre>
<p>A perfect solution would require completely disabling the<br>
optimization.</p>
<p>Fixes [Bug <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: `foo(*args, &args.pop)` should pass all elements of args (Closed)" href="https://bugs.ruby-lang.org/issues/16504">#16504</a>]<br>
Fixes [Bug <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Argument is added to both splat and last &block argument (Closed)" href="https://bugs.ruby-lang.org/issues/16500">#16500</a>]</p>