Project

General

Profile

Actions

Bug #19132

closed

`**` を引数に指定すると no anonymous keyword rest parameter になる

Added by tommy (Masahiro Tomita) about 2 years ago. Updated about 2 years ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 3.2.0preview3 (2022-11-14) [arm64-darwin21]
[ruby-dev:51196]

Description

Ruby 3.2.0-preview3 で次のスクリプトを実行するとエラーになります。

def hoge(a, *, k: nil, **)
  foo(*, **)
end
% ruby hoge.rb   
hoge.rb:2: no anonymous keyword rest parameter

次のようにキーワードパラメータが ** だけであればエラーになりませんでした。

def hoge(a, *, **)
  foo(*, **)
end

Updated by shugo (Shugo Maeda) about 2 years ago

new_args_tail()でkw_argsがある時にANON_KEYWORD_REST_IDがローカル変数のテーブルから削られてしまうようです。
f_kwrestの値をinternal_idからANON_KEYWORD_REST_IDに変えると動くようになりました。

https://github.com/ruby/ruby/pull/6743

内部的な変数名が変わってtest/ruby/test_ast.rbが失敗するようになったのでその部分は対処しましたが、この修正方法だと他にも問題があるかもしれません。

Updated by shugo (Shugo Maeda) about 2 years ago

shugo (Shugo Maeda) wrote in #note-1:

内部的な変数名が変わってtest/ruby/test_ast.rbが失敗するようになったのでその部分は対処しましたが、この修正方法だと他にも問題があるかもしれません。

案の定RBSのテストで失敗したのでANON_KEYWORD_REST_IDを内部IDに変えてみたのですが、今度はUnboundMethod#parametersなどで :** が返ることを期待しているテストがコケるようになってしまいました。

https://github.com/ruby/ruby/pull/6743#issuecomment-1317787459

内部IDか :** のどちらかに統一して、必要ならこれらのテストかRBSを修正した方がいい気がするのですが、どうするのがいいでしょう。

Actions #3

Updated by nobu (Nobuyoshi Nakada) about 2 years ago

  • Backport changed from 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN to 2.7: DONTNEED, 3.0: DONTNEED, 3.1: DONTNEED

Updated by nobu (Nobuyoshi Nakada) about 2 years ago

:**に統一でいいのではないでしょうか。

Updated by shugo (Shugo Maeda) about 2 years ago

nobu (Nobuyoshi Nakada) wrote in #note-4:

:**に統一でいいのではないでしょうか。

Rubyレベルで公開されている情報が :** なので私もそちらに合わせる方がよいように思います。

Updated by shugo (Shugo Maeda) about 2 years ago

shugo (Shugo Maeda) wrote in #note-5:

nobu (Nobuyoshi Nakada) wrote in #note-4:

:**に統一でいいのではないでしょうか。

Rubyレベルで公開されている情報が :** なので私もそちらに合わせる方がよいように思います。

上記の修正でRBSのテストがこけるのを調べてみたのですが、RubyVM::ASTのテストが通るように以下の修正をしたせいでした。

--- a/ast.c
+++ b/ast.c
@@ -348,6 +348,7 @@ static VALUE
 var_name(ID id)
 {
     if (!id) return Qnil;
+    if (!rb_is_local_id(id)) return Qnil;
     if (!rb_id2str(id)) return Qnil;
     return ID2SYM(id);
 }

変数名が ** の時に名前なし扱いするためでしたが、これが def foo(...) とかで定義される * なども隠してしまっていたようです。
** の時だけ隠すという修正もできますが、UnboundMethod#parametersなどでは ** という変数名が見えているので、RubyVM::ASTも合わせた方がいいように思います。

Updated by yui-knk (Kaneko Yuichiro) about 2 years ago

  • UnboundMethod#parametersなどでは ** という変数名が見えている
  • DVAR@1:6-1:8 nil で **を表現するよりは、kwrest: (DVAR@1:6-1:8 :**)で表現したほうが理解がしやすい

という点からRubyVM::AST側でも ** が取得できるというふうに修正し test/ruby/test_ast.rb のexpectationを更新するのがよいと思います。

また調べたところ、議論になっているtest caseが追加されたのが https://github.com/ruby/ruby/commit/fa41a7b2608#diff-7db75141987f9cf709ba4cb68969d3bc97b0de8dfe175d845df2f3de2b91cae6R320 で、 Add support for anonymous rest and keyword rest argument forwarding (https://github.com/ruby/ruby/commit/f53dfab95c3) のほうがあとであることもあり、expectationを更新することに前向きです。

anonymousでない場合:

pp RubyVM::AbstractSyntaxTree.parse("def a(**a) end")

#=> (SCOPE@1:0-1:14
 tbl: []
 args: nil
 body:
   (DEFN@1:0-1:14
    mid: :a
    body:
      (SCOPE@1:0-1:14
       tbl: [:a]
       args:
         (ARGS@1:6-1:9
          pre_num: 0
          pre_init: nil
          opt: nil
          first_post: nil
          post_num: 0
          post_init: nil
          rest: nil
          kw: nil
          kwrest: (DVAR@1:6-1:9 :a)
          block: nil)
       body: nil)))

anonymousで、**を隠す場合:

pp RubyVM::AbstractSyntaxTree.parse("def a(**) end")

#=> (SCOPE@1:0-1:13
 tbl: []
 args: nil
 body:
   (DEFN@1:0-1:13
    mid: :a
    body:
      (SCOPE@1:0-1:13
       tbl: [nil]
       args:
         (ARGS@1:6-1:8
          pre_num: 0
          pre_init: nil
          opt: nil
          first_post: nil
          post_num: 0
          post_init: nil
          rest: nil
          kw: nil
          kwrest: (DVAR@1:6-1:8 nil)
          block: nil)
       body: nil)))

anonymousで、**を隠さない場合:

pp RubyVM::AbstractSyntaxTree.parse("def a(**) end")
#=> (SCOPE@1:0-1:13
 tbl: []
 args: nil
 body:
   (DEFN@1:0-1:13
    mid: :a
    body:
      (SCOPE@1:0-1:13
       tbl: [:**]
       args:
         (ARGS@1:6-1:8
          pre_num: 0
          pre_init: nil
          opt: nil
          first_post: nil
          post_num: 0
          post_init: nil
          rest: nil
          kw: nil
          kwrest: (DVAR@1:6-1:8 :**)
          block: nil)
       body: nil)))
Actions #8

Updated by shugo (Shugo Maeda) about 2 years ago

  • Status changed from Open to Closed

Applied in changeset git|ddd62fadaf91418cd259593285bc59358fb0b166.


Allow anonymous keyword rest parameter with other keyword parameters

Fixes [Bug #19132]

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0