コンパイラ作成(62) 引数のチェック

今回の目標

関数コール時の引数の型・個数をチェックするよ。

// 誤った関数コール
extern int add(int a, int b);

int main()
{
   int a = 5, b = 12, c;
   c = add(a,"must be int");
   printf("%d + %d = %d\n",a,b,c);
}
// 誤った関数コール
extern int add(int a, int b);

int main()
{
   int a = 5, b = 12, c;
   c = add(a);
   printf("%d + %d = %d\n",a,b,c);
}

前々回、関数テーブルにちゃんと引数の型の情報を入れるようにしたんで、それを使ってチェックするよ。

codegen_func
  # 関数コールのコード生成
  def codegen_func operand
    rettype = "int"
    f = @functions[operand[0].str]
    if @numuseregs != 0 then
      if @numuseregs % 2 == 1 then codegen "  sub  rsp, 8" end
      (0...@numuseregs).each do |i| codegen "  push #{@regs64[i]}" end
    end
    if f != nil then
      if operand.size - 2 != f[1].size then
        perror "wrong number of parameters"
      end
    end
    (0...operand.size-2).each do |i|
      save = @numuseregs
      @numuseregs = i
      type = codegen_el operand[i+2]
      @numuseregs = save
      if f != nil then
        if type != f[1][i] then perror "incompatible type parameter" end
      end
      if type == "int"
        codegen "  mov  #{@regs32[i]}, eax"
      elsif type == "char*"
        codegen "  mov  #{@regs64[i]}, rax"
      else
        perror
      end
    end
    codegen "  call " + operand[0].str
    if f != nil then
      rettype = f[0]
    end
    if @numuseregs != 0 then
      (0...@numuseregs).reverse_each do |i| codegen "  pop  #{@regs64[i]}" end
      if @numuseregs % 2 == 1 then codegen "  add  rsp, 8" end
    end
    return rettype
  end

関数宣言がされてるときだけチェックしてる。されてないときにワーニングを出すべきかな。

動作テスト

2つ続けてテスト。

~/myc$ myc err31.myc
err31.myc:7:28 error: incompatible type parameter
~/myc$ myc err32.myc
err32.myc:7:14 error: wrong number of parameters
~/myc$

できたよ。このところ関数まわりとchar*まわりを頑張ってきたけど飽きたなあ。次回は別の事しよう。