AIでruby-signatureを生成する

# はじめに
 タイトルはお約束の釣りでただし、AIはAbstract Interpretationですって奴です。

ruby-signatureを抽象実行で生成しようってのがうまくいきそう(うまくいったとは言っていない)のでまとめてみます。

## ruby-signatureって
ruby-signatureはRubyのライブラリや組み込みクラス・メソッド等の戻り値の型情報を記述するための記法と型のデータベースです。[ここ](https://github.com/ruby/ruby-signature) にあります。
型情報は手で書くことを想定しているわけですが、自動的に生成出来れば色々便利でしょう。

## mmcについて
[mmc](https://github.com/miura1729/mruby-meta-circular)は私が作っているRubyからCに変換するコンパイラです。中に抽象実行をおこなって型を推定するエンジンを持っています。抽象実行とかそういうのは書かないので[ここ](https://dev.to/miura1729/-2376)を見てください。

# Enumerableの型を解析する
おそらくとりあえず一番むつかしいのはEnumerableじゃないかと思われます。これはModuleでそのままではインスタンスを作ることもできません。また、どのクラスにincludeされるかで結果の型が変ってきます。そのため、型変数を含んだ型情報になります。
Enumerableの正解は[こちら]( https://github.com/ruby/ruby-signature/blob/master/stdlib/builtin/enumerable.rbs
)になります。

## 型変数の導入

これまで実際の値を用意できないと抽象実行出来なかったのですが、型変数(TypeVariable)というクラスを用意します。このクラスは任意のメソッドを呼び出したら、戻り値として別の型変数を用意して、呼び出されたメソッド名とその戻りの型を記録しておきます。また、引数にブロックやProcオブジェクトがあった場合は、引数として別の型変数を渡してProcオブジェクトを抽象実行します。この辺のコードはこちらになります。method_missingとして実装しています。

```ruby
    define_inf_rule_method :missing, TypeVariable do |infer, inst, node, tup|
      rec = inst.inreg[0].type[tup][0]
      name = inst.inreg[1].type[tup][0]
      block = inst.inreg[3].type[tup][0]
      if block.is_a?(ProcType) then
        make_intype(infer, inst, node, tup) do |intype, argc|
          #      intype = inst.inreg.map {|reg| reg.flush_type(tup)[tup] || []}
          intype[0] = [block.slf]
          ele = rec.sub_type_var[:ele] ? rec.sub_type_var[:ele] : TypeVarType.new(TypeVariable)
          rec.sub_type_var[:ele] = ele
          intype[1] = [ele]
          intype[2] = inst.inreg[3].type[tup]
          ntup = infer.typetupletab.get_tupple_id(intype, block, tup)
          irepssa = block.irep
          infer.inference_block(irepssa, intype, ntup, argc, block)
          inst.outreg[0].add_same irepssa.retreg
          inst.outreg[0].flush_type(tup, ntup)
        end
      end

      ret = rec.sub_type_var[:ret] || TypeVarType.new(TypeVariable)
      rec.using_method[name] = [inst.inreg.map {|r| r.type[tup]}, ret]
      rec.sub_type_var[:ret] = ret
      inst.outreg[0].add_type ret, tup
      nil
    end
```

## 入力プログラム
Enumerableモジュールを型解析するための入力プログラムはこんな感じです

```ruby
class TypeVariable
  include Enumerable
end

class Foo<TypeVariable
end

MTypeInf::inference_main {
  slf = Foo.new
  blk = lambda {|a| TypeVariable.new}
  slf.collect(&blk)
  blk = lambda {|a| TypeVariable.new}
  slf.any?(&blk)
  blk = lambda {|a| TypeVariable.new}
  slf.detect(TypeVariable.new, &blk)
  blk = lambda {|a| TypeVariable.new}
  slf.each_with_index(&blk)
  blk = lambda {|a| TypeVariable.new}
  slf.entries(&blk)
}
```
さすがにEnumerableは単独でどう頑張ってもインスタンス化できないので、型変数クラスにincludeしています。あとは、引数をすべて型変数クラスのインスタンスを渡してやればOKです。引数の数が分かれば自動生成も可能でしょう。

## 表示
解析結果を表示する場所です

```ruby
        if cls == TypeVariable then
          acls = cls.ancestors[1]
          mblk = clsobj.method.values[0]
          sreg = mblk.regtab[0]
          slftype = sreg.type.values[0][0]
          tvpara = slftype.sub_type_var.map {|name, ty| ty }
          interface = slftype.using_method.map {|name, ty|
            name.val
          }
          mess << "#{acls.class} #{acls} #{tvpara} #{interface} \n"
        else
```
TypeVariableがselfになっているメソッドはEnumerableなどのModuleです。この場合は型変数型でメソッド呼び出しの度に集めたメソッド情報とその戻り値の型情報をダンプしています。

##　結果
これでこんな感じの出力になります

```ruby
Module Enumerable [TV1, TV4] [:each]
 Instance variables

 methodes
  collect (TV0,  (Object, TV1, NilClass) -> TV2 ) -> Array<TV2>
  method_missing (TV0, Symbol(:each),  (TV0, TV1, Proc<>, Proc<>) -> Array<TV1> ) -> Array<TV1>|TV4
  any? (TV0,  (Object, TV1, NilClass) -> TV5 ) -> TrueClass|FalseClass
  detect (TV0, TV1,  (Object, TV1, NilClass) -> TV8 ) -> TV1
  each_with_index (TV0,  (Object, TV1, Fixnum, NilClass) -> TV10
   (Object, TV1, Fixnum, NilClass) -> TV12 ) -> TV0
  entries (TV0, ) -> Array<TV1>
```
まだまだ不完全ですけど、結構いい線行っているのではいでしょうか？　(ひいき目です)

お手本はこんな感じ

```
module Enumerable[unchecked out Elem, out Return]: _Each[Elem, Return]

  def `any?`: () -> bool
            | () { (Elem arg0) -> untyped } -> bool

  def collect: [U] () { (Elem arg0) -> U } -> ::Array[U]
             | () -> ::Enumerator[Elem, Return]

  def detect: (?Proc ifnone) { (Elem arg0) -> untyped } -> Elem?
            | (?Proc ifnone) -> ::Enumerator[Elem, Return]

  def each_with_index: () { (Elem arg0, Integer arg1) -> untyped } -> ::Enumerable[Elem, Return]
                     | () -> ::Enumerator[[ Elem, Integer ], Return]

  def each_with_object: [U] (U arg0) { (Elem arg0, untyped arg1) -> untyped } -> U
                      | [U] (U arg0) -> ::Enumerator[[ Elem, U ], Return]

```