//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   20 Jun 06  Brian Frank  Creation
//    1 Oct 06  Brian Frank  Port from Java to Fan
//

**
** CheckParamDefs is used to process all the parameter default
** expressions for all the methods.  What we are looking for is
** default expressions which use default expressions before it
** which require us to insert a store instruction.
**
class CheckParamDefs : CompilerStep
{

  new make(Compiler compiler)
    : super(compiler)
  {
  }

  override Void run()
  {
    walk(compiler, VisitDepth.slotDef)
  }

  override Void visitMethodDef(MethodDef m)
  {
    // unless there aren't any defaults don't bother
    params := m.paramDefs
    num := params.size
    if (num == 0 || params[-1].def == null)
      return

    // if a def expr calculates a local used after it,
    // then we need to insert a store (local set)
    num.times |Int i|
    {
      if (params[i].def == null) return
      used := usedInSuccDef(params, i)
      if (used != null)
      {
        // handle error case of foo(x := x)
        if (used == params[i])
        {
          err("Param default '$used.name' cannot access itself", used.loc)
          return
        }

        param := params[i]
        var   := m.vars[i]
        loc   := param.loc

        if (!param.name.equals(var.name)) throw err("invalid state", loc)

        param.def = BinaryExpr.makeAssign(LocalVarExpr(loc, var), param.def, true)
      }
    }
  }

  ParamDef? usedInSuccDef(ParamDef[] params, Int index)
  {
    this.name = params[index].name
    for (i:=index; i<params.size; ++i)
    {
      this.used = false
      params[i].def.walk(this)
      if (this.used) return params[i]
    }
    return null
  }

  override Expr visitExpr(Expr expr)
  {
    if (expr.id === ExprId.localVar)
    {
      local := (LocalVarExpr)expr
      if (name == local.var.name) used = true
    }
    return expr
  }

  Str? name
  Bool used

}