Bug #19132
closed`**` を引数に指定すると no anonymous keyword rest parameter になる
Added by tommy (Masahiro Tomita) almost 3 years ago. Updated almost 3 years ago.
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) almost 3 years ago
          
          
        
        
          
            Actions
          
          #1
            [ruby-dev:51197]
          Updated by shugo (Shugo Maeda) almost 3 years ago
          
          
        
        
          
            Actions
          
          #1
            [ruby-dev:51197]
        
      
      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) almost 3 years ago
          
          
        
        
          
            Actions
          
          #2
            [ruby-dev:51198]
          Updated by shugo (Shugo Maeda) almost 3 years ago
          
          
        
        
          
            Actions
          
          #2
            [ruby-dev:51198]
        
      
      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を修正した方がいい気がするのですが、どうするのがいいでしょう。
        
           Updated by nobu (Nobuyoshi Nakada) almost 3 years ago
          
          
        
        
          
            Actions
          
          #3
          Updated by nobu (Nobuyoshi Nakada) almost 3 years ago
          
          
        
        
          
            Actions
          
          #3
        
      
      - 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) almost 3 years ago
          
          
        
        
          
            Actions
          
          #4
            [ruby-dev:51199]
          Updated by nobu (Nobuyoshi Nakada) almost 3 years ago
          
          
        
        
          
            Actions
          
          #4
            [ruby-dev:51199]
        
      
      :**に統一でいいのではないでしょうか。
        
           Updated by shugo (Shugo Maeda) almost 3 years ago
          
          
        
        
          
            Actions
          
          #5
            [ruby-dev:51200]
          Updated by shugo (Shugo Maeda) almost 3 years ago
          
          
        
        
          
            Actions
          
          #5
            [ruby-dev:51200]
        
      
      nobu (Nobuyoshi Nakada) wrote in #note-4:
:**に統一でいいのではないでしょうか。
Rubyレベルで公開されている情報が :** なので私もそちらに合わせる方がよいように思います。
        
           Updated by shugo (Shugo Maeda) almost 3 years ago
          
          
        
        
          
            Actions
          
          #6
            [ruby-dev:51201]
          Updated by shugo (Shugo Maeda) almost 3 years ago
          
          
        
        
          
            Actions
          
          #6
            [ruby-dev:51201]
        
      
      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) almost 3 years ago
          
          
        
        
          
            Actions
          
          #7
            [ruby-dev:51202]
          Updated by yui-knk (Kaneko Yuichiro) almost 3 years ago
          
          
        
        
          
            Actions
          
          #7
            [ruby-dev:51202]
        
      
      - 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)))
        
           Updated by shugo (Shugo Maeda) almost 3 years ago
          
          
        
        
          
            Actions
          
          #8
          Updated by shugo (Shugo Maeda) almost 3 years ago
          
          
        
        
          
            Actions
          
          #8
        
      
      - Status changed from Open to Closed
Applied in changeset git|ddd62fadaf91418cd259593285bc59358fb0b166.
Allow anonymous keyword rest parameter with other keyword parameters
Fixes [Bug #19132]