Go Home Page
The Go Programming Language

Source file src/pkg/exp/eval/stmt.go

// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package eval

import (
    "big"
    "log"
    "go/ast"
    "go/token"
)

const (
    returnPC = ^uint(0)
    badPC    = ^uint(1)
)

/*
 * Statement compiler
 */

type stmtCompiler struct {
    *blockCompiler
    pos token.Position
    // This statement's label, or nil if it is not labeled.
    stmtLabel *label
}

func (a *stmtCompiler) diag(format string, args ...interface{}) {
    a.diagAt(&a.pos, format, args)
}

/*
 * Flow checker
 */

type flowEnt struct {
    // Whether this flow entry is conditional.  If true, flow can
    // continue to the next PC.
    cond bool
    // True if this will terminate flow (e.g., a return statement).
    // cond must be false and jumps must be nil if this is true.
    term bool
    // PC's that can be reached from this flow entry.
    jumps []*uint
    // Whether this flow entry has been visited by reachesEnd.
    visited bool
}

type flowBlock struct {
    // If this is a goto, the target label.
    target string
    // The inner-most block containing definitions.
    block *block
    // The numVars from each block leading to the root of the
    // scope, starting at block.
    numVars []int
}

type flowBuf struct {
    cb *codeBuf
    // ents is a map from PC's to flow entries.  Any PC missing
    // from this map is assumed to reach only PC+1.
    ents map[uint]*flowEnt
    // gotos is a map from goto positions to information on the
    // block at the point of the goto.
    gotos map[*token.Position]*flowBlock
    // labels is a map from label name to information on the block
    // at the point of the label.  labels are tracked by name,
    // since mutliple labels at the same PC can have different
    // blocks.
    labels map[string]*flowBlock
}

func newFlowBuf(cb *codeBuf) *flowBuf {
    return &flowBuf{cb, make(map[uint]*flowEnt), make(map[*token.Position]*flowBlock), make(map[string]*flowBlock)}
}

// put creates a flow control point for the next PC in the code buffer.
// This should be done before pushing the instruction into the code buffer.
func (f *flowBuf) put(cond bool, term bool, jumps []*uint) {
    pc := f.cb.nextPC()
    if ent, ok := f.ents[pc]; ok {
        log.Crashf("Flow entry already exists at PC %d: %+v", pc, ent)
    }
    f.ents[pc] = &flowEnt{cond, term, jumps, false}
}

// putTerm creates a flow control point at the next PC that
// unconditionally terminates execution.
func (f *flowBuf) putTerm() { f.put(false, true, nil) }

// put1 creates a flow control point at the next PC that jumps to one
// PC and, if cond is true, can also continue to the PC following the
// next PC.
func (f *flowBuf) put1(cond bool, jumpPC *uint) {
    f.put(cond, false, []*uint{jumpPC})
}

func newFlowBlock(target string, b *block) *flowBlock {
    // Find the inner-most block containing definitions
    for b.numVars == 0 && b.outer != nil && b.outer.scope == b.scope {
        b = b.outer
    }

    // Count parents leading to the root of the scope
    n := 0
    for bp := b; bp.scope == b.scope; bp = bp.outer {
        n++
    }

    // Capture numVars from each block to the root of the scope
    numVars := make([]int, n)
    i := 0
    for bp := b; i < n; bp = bp.outer {
        numVars[i] = bp.numVars
        i++
    }

    return &flowBlock{target, b, numVars}
}

// putGoto captures the block at a goto statement.  This should be
// called in addition to putting a flow control point.
func (f *flowBuf) putGoto(pos token.Position, target string, b *block) {
    f.gotos[&pos] = newFlowBlock(target, b)
}

// putLabel captures the block at a label.
func (f *flowBuf) putLabel(name string, b *block) {
    f.labels[name] = newFlowBlock("", b)
}

// reachesEnd returns true if the end of f's code buffer can be
// reached from the given program counter.  Error reporting is the
// caller's responsibility.
func (f *flowBuf) reachesEnd(pc uint) bool {
    endPC := f.cb.nextPC()
    if pc > endPC {
        log.Crashf("Reached bad PC %d past end PC %d", pc, endPC)
    }

    for ; pc < endPC; pc++ {
        ent, ok := f.ents[pc]
        if !ok {
            continue
        }

        if ent.visited {
            return false
        }
        ent.visited = true

        if ent.term {
            return false
        }

        // If anything can reach the end, we can reach the end
        // from pc.
        for _, j := range ent.jumps {
            if f.reachesEnd(*j) {
                return true
            }
        }
        // If the jump was conditional, we can reach the next
        // PC, so try reaching the end from it.
        if ent.cond {
            continue
        }
        return false
    }
    return true
}

// gotosObeyScopes returns true if no goto statement causes any
// variables to come into scope that were not in scope at the point of
// the goto.  Reports any errors using the given compiler.
func (f *flowBuf) gotosObeyScopes(a *compiler) {
    for pos, src := range f.gotos {
        tgt := f.labels[src.target]

        // The target block must be a parent of this block
        numVars := src.numVars
        b := src.block
        for len(numVars) > 0 && b != tgt.block {
            b = b.outer
            numVars = numVars[1:]
        }
        if b != tgt.block {
            // We jumped into a deeper block
            a.diagAt(pos, "goto causes variables to come into scope")
            return
        }

        // There must be no variables in the target block that
        // did not exist at the jump
        tgtNumVars := tgt.numVars
        for i := range numVars {
            if tgtNumVars[i] > numVars[i] {
                a.diagAt(pos, "goto causes variables to come into scope")
                return
            }
        }
    }
}

/*
 * Statement generation helpers
 */

func (a *stmtCompiler) defineVar(ident *ast.Ident, t Type) *Variable {
    v, prev := a.block.DefineVar(ident.Name(), ident.Pos(), t)
    if prev != nil {
        // TODO(austin) It's silly that we have to capture
        // Pos() in a variable.
        pos := prev.Pos()
        if pos.IsValid() {
            a.diagAt(ident, "variable %s redeclared in this block\n\tprevious declaration at %s", ident.Name(), &pos)
        } else {
            a.diagAt(ident, "variable %s redeclared in this block", ident.Name())
        }
        return nil
    }

    // Initialize the variable
    index := v.Index
    if v.Index >= 0 {
        a.push(func(v *Thread) { v.f.Vars[index] = t.Zero() })
    }
    return v
}

// TODO(austin) Move doAssign to here

/*
 * Statement compiler
 */

func (a *stmtCompiler) compile(s ast.Stmt) {
    if a.block.inner != nil {
        log.Crash("Child scope still entered")
    }

    notimpl := false
    switch s := s.(type) {
    case *ast.BadStmt:
        // Error already reported by parser.
        a.silentErrors++

    case *ast.DeclStmt:
        a.compileDeclStmt(s)

    case *ast.EmptyStmt:
        // Do nothing.

    case *ast.LabeledStmt:
        a.compileLabeledStmt(s)

    case *ast.ExprStmt:
        a.compileExprStmt(s)

    case *ast.IncDecStmt:
        a.compileIncDecStmt(s)

    case *ast.AssignStmt:
        a.compileAssignStmt(s)

    case *ast.GoStmt:
        notimpl = true

    case *ast.DeferStmt:
        notimpl = true

    case *ast.ReturnStmt:
        a.compileReturnStmt(s)

    case *ast.BranchStmt:
        a.compileBranchStmt(s)

    case *ast.BlockStmt:
        a.compileBlockStmt(s)

    case *ast.IfStmt:
        a.compileIfStmt(s)

    case *ast.CaseClause:
        a.diag("case clause outside switch")

    case *ast.SwitchStmt:
        a.compileSwitchStmt(s)

    case *ast.TypeCaseClause:
        notimpl = true

    case *ast.TypeSwitchStmt:
        notimpl = true

    case *ast.CommClause:
        notimpl = true

    case *ast.SelectStmt:
        notimpl = true

    case *ast.ForStmt:
        a.compileForStmt(s)

    case *ast.RangeStmt:
        notimpl = true

    default:
        log.Crashf("unexpected ast node type %T", s)
    }

    if notimpl {
        a.diag("%T statment node not implemented", s)
    }

    if a.block.inner != nil {
        log.Crash("Forgot to exit child scope")
    }
}

func (a *stmtCompiler) compileDeclStmt(s *ast.DeclStmt) {
    switch decl := s.Decl.(type) {
    case *ast.BadDecl:
        // Do nothing.  Already reported by parser.
        a.silentErrors++

    case *ast.FuncDecl:
        if !a.block.global {
            log.Crash("FuncDecl at statement level")
        }

    case *ast.GenDecl:
        if decl.Tok == token.IMPORT && !a.block.global {
            log.Crash("import at statement level")
        }

    default:
        log.Crashf("Unexpected Decl type %T", s.Decl)
    }
    a.compileDecl(s.Decl)
}

func (a *stmtCompiler) compileVarDecl(decl *ast.GenDecl) {
    for _, spec := range decl.Specs {
        spec := spec.(*ast.ValueSpec)
        if spec.Values == nil {
            // Declaration without assignment
            if spec.Type == nil {
                // Parser should have caught
                log.Crash("Type and Values nil")
            }
            t := a.compileType(a.block, spec.Type)
            // Define placeholders even if type compile failed
            for _, n := range spec.Names {
                a.defineVar(n, t)
            }
        } else {
            // Declaration with assignment
            lhs := make([]ast.Expr, len(spec.Names))
            for i, n := range spec.Names {
                lhs[i] = n
            }
            a.doAssign(lhs, spec.Values, decl.Tok, spec.Type)
        }
    }
}

func (a *stmtCompiler) compileDecl(decl ast.Decl) {
    switch d := decl.(type) {
    case *ast.BadDecl:
        // Do nothing.  Already reported by parser.
        a.silentErrors++

    case *ast.FuncDecl:
        decl := a.compileFuncType(a.block, d.Type)
        if decl == nil {
            return
        }
        // Declare and initialize v before compiling func
        // so that body can refer to itself.
        c, prev := a.block.DefineConst(d.Name.Name(), a.pos, decl.Type, decl.Type.Zero())
        if prev != nil {
            pos := prev.Pos()
            if pos.IsValid() {
                a.diagAt(d.Name, "identifier %s redeclared in this block\n\tprevious declaration at %s", d.Name.Name(), &pos)
            } else {
                a.diagAt(d.Name, "identifier %s redeclared in this block", d.Name.Name())
            }
        }
        fn := a.compileFunc(a.block, decl, d.Body)
        if c == nil || fn == nil {
            return
        }
        var zeroThread Thread
        c.Value.(FuncValue).Set(nil, fn(&zeroThread))

    case *ast.GenDecl:
        switch d.Tok {
        case token.IMPORT:
            log.Crashf("%v not implemented", d.Tok)
        case token.CONST:
            log.Crashf("%v not implemented", d.Tok)
        case token.TYPE:
            a.compileTypeDecl(a.block, d)
        case token.VAR:
            a.compileVarDecl(d)
        }

    default:
        log.Crashf("Unexpected Decl type %T", decl)
    }
}

func (a *stmtCompiler) compileLabeledStmt(s *ast.LabeledStmt) {
    // Define label
    l, ok := a.labels[s.Label.Name()]
    if ok {
        if l.resolved.IsValid() {
            a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Name(), &l.resolved)
        }
    } else {
        pc := badPC
        l = &label{name: s.Label.Name(), gotoPC: &pc}
        a.labels[l.name] = l
    }
    l.desc = "regular label"
    l.resolved = s.Pos()

    // Set goto PC
    *l.gotoPC = a.nextPC()

    // Define flow entry so we can check for jumps over declarations.
    a.flow.putLabel(l.name, a.block)

    // Compile the statement.  Reuse our stmtCompiler for simplicity.
    sc := &stmtCompiler{a.blockCompiler, s.Stmt.Pos(), l}
    sc.compile(s.Stmt)
}

func (a *stmtCompiler) compileExprStmt(s *ast.ExprStmt) {
    bc := a.enterChild()
    defer bc.exit()

    e := a.compileExpr(bc.block, false, s.X)
    if e == nil {
        return
    }

    if e.exec == nil {
        a.diag("%s cannot be used as expression statement", e.desc)
        return
    }

    a.push(e.exec)
}

func (a *stmtCompiler) compileIncDecStmt(s *ast.IncDecStmt) {
    // Create temporary block for extractEffect
    bc := a.enterChild()
    defer bc.exit()

    l := a.compileExpr(bc.block, false, s.X)
    if l == nil {
        return
    }

    if l.evalAddr == nil {
        l.diag("cannot assign to %s", l.desc)
        return
    }
    if !(l.t.isInteger() || l.t.isFloat()) {
        l.diagOpType(s.Tok, l.t)
        return
    }

    var op token.Token
    var desc string
    switch s.Tok {
    case token.INC:
        op = token.ADD
        desc = "increment statement"
    case token.DEC:
        op = token.SUB
        desc = "decrement statement"
    default:
        log.Crashf("Unexpected IncDec token %v", s.Tok)
    }

    effect, l := l.extractEffect(bc.block, desc)

    one := l.newExpr(IdealIntType, "constant")
    one.pos = s.Pos()
    one.eval = func() *big.Int { return big.NewInt(1) }

    binop := l.compileBinaryExpr(op, l, one)
    if binop == nil {
        return
    }

    assign := a.compileAssign(s.Pos(), bc.block, l.t, []*expr{binop}, "", "")
    if assign == nil {
        log.Crashf("compileAssign type check failed")
    }

    lf := l.evalAddr
    a.push(func(v *Thread) {
        effect(v)
        assign(lf(v), v)
    })
}

func (a *stmtCompiler) doAssign(lhs []ast.Expr, rhs []ast.Expr, tok token.Token, declTypeExpr ast.Expr) {
    nerr := a.numError()

    // Compile right side first so we have the types when
    // compiling the left side and so we don't see definitions
    // made on the left side.
    rs := make([]*expr, len(rhs))
    for i, re := range rhs {
        rs[i] = a.compileExpr(a.block, false, re)
    }

    errOp := "assignment"
    if tok == token.DEFINE || tok == token.VAR {
        errOp = "declaration"
    }
    ac, ok := a.checkAssign(a.pos, rs, errOp, "value")
    ac.allowMapForms(len(lhs))

    // If this is a definition and the LHS is too big, we won't be
    // able to produce the usual error message because we can't
    // begin to infer the types of the LHS.
    if (tok == token.DEFINE || tok == token.VAR) && len(lhs) > len(ac.rmt.Elems) {
        a.diag("not enough values for definition")
    }

    // Compile left type if there is one
    var declType Type
    if declTypeExpr != nil {
        declType = a.compileType(a.block, declTypeExpr)
    }

    // Compile left side
    ls := make([]*expr, len(lhs))
    nDefs := 0
    for i, le := range lhs {
        // If this is a definition, get the identifier and its type
        var ident *ast.Ident
        var lt Type
        switch tok {
        case token.DEFINE:
            // Check that it's an identifier
            ident, ok = le.(*ast.Ident)
            if !ok {
                a.diagAt(le, "left side of := must be a name")
                // Suppress new defitions errors
                nDefs++
                continue
            }

            // Is this simply an assignment?
            if _, ok := a.block.defs[ident.Name()]; ok {
                ident = nil
                break
            }
            nDefs++

        case token.VAR:
            ident = le.(*ast.Ident)
        }

        // If it's a definition, get or infer its type.
        if ident != nil {
            // Compute the identifier's type from the RHS
            // type.  We use the computed MultiType so we
            // don't have to worry about unpacking.
            switch {
            case declTypeExpr != nil:
                // We have a declaration type, use it.
                // If declType is nil, we gave an
                // error when we compiled it.
                lt = declType

            case i >= len(ac.rmt.Elems):
                // Define a placeholder.  We already
                // gave the "not enough" error above.
                lt = nil

            case ac.rmt.Elems[i] == nil:
                // We gave the error when we compiled
                // the RHS.
                lt = nil

            case ac.rmt.Elems[i].isIdeal():
                // If the type is absent and the
                // corresponding expression is a
                // constant expression of ideal
                // integer or ideal float type, the
                // type of the declared variable is
                // int or float respectively.
                switch {
                case ac.rmt.Elems[i].isInteger():
                    lt = IntType
                case ac.rmt.Elems[i].isFloat():
                    lt = FloatType
                default:
                    log.Crashf("unexpected ideal type %v", rs[i].t)
                }

            default:
                lt = ac.rmt.Elems[i]
            }
        }

        // If it's a definition, define the identifier
        if ident != nil {
            if a.defineVar(ident, lt) == nil {
                continue
            }
        }

        // Compile LHS
        ls[i] = a.compileExpr(a.block, false, le)
        if ls[i] == nil {
            continue
        }

        if ls[i].evalMapValue != nil {
            // Map indexes are not generally addressable,
            // but they are assignable.
            //
            // TODO(austin) Now that the expression
            // compiler uses semantic values, this might
            // be easier to implement as a function call.
            sub := ls[i]
            ls[i] = ls[i].newExpr(sub.t, sub.desc)
            ls[i].evalMapValue = sub.evalMapValue
            mvf := sub.evalMapValue
            et := sub.t
            ls[i].evalAddr = func(t *Thread) Value {
                m, k := mvf(t)
                e := m.Elem(t, k)
                if e == nil {
                    e = et.Zero()
                    m.SetElem(t, k, e)
                }
                return e
            }
        } else if ls[i].evalAddr == nil {
            ls[i].diag("cannot assign to %s", ls[i].desc)
            continue
        }
    }

    // A short variable declaration may redeclare variables
    // provided they were originally declared in the same block
    // with the same type, and at least one of the variables is
    // new.
    if tok == token.DEFINE && nDefs == 0 {
        a.diag("at least one new variable must be declared")
        return
    }

    // If there have been errors, our arrays are full of nil's so
    // get out of here now.
    if nerr != a.numError() {
        return
    }

    // Check for 'a[x] = r, ok'
    if len(ls) == 1 && len(rs) == 2 && ls[0].evalMapValue != nil {
        a.diag("a[x] = r, ok form not implemented")
        return
    }

    // Create assigner
    var lt Type
    n := len(lhs)
    if n == 1 {
        lt = ls[0].t
    } else {
        lts := make([]Type, len(ls))
        for i, l := range ls {
            if l != nil {
                lts[i] = l.t
            }
        }
        lt = NewMultiType(lts)
    }
    bc := a.enterChild()
    defer bc.exit()
    assign := ac.compile(bc.block, lt)
    if assign == nil {
        return
    }

    // Compile
    if n == 1 {
        // Don't need temporaries and can avoid []Value.
        lf := ls[0].evalAddr
        a.push(func(t *Thread) { assign(lf(t), t) })
    } else if tok == token.VAR || (tok == token.DEFINE && nDefs == n) {
        // Don't need temporaries
        lfs := make([]func(*Thread) Value, n)
        for i, l := range ls {
            lfs[i] = l.evalAddr
        }
        a.push(func(t *Thread) {
            dest := make([]Value, n)
            for i, lf := range lfs {
                dest[i] = lf(t)
            }
            assign(multiV(dest), t)
        })
    } else {
        // Need temporaries
        lmt := lt.(*MultiType)
        lfs := make([]func(*Thread) Value, n)
        for i, l := range ls {
            lfs[i] = l.evalAddr
        }
        a.push(func(t *Thread) {
            temp := lmt.Zero().(multiV)
            assign(temp, t)
            // Copy to destination
            for i := 0; i < n; i++ {
                // TODO(austin) Need to evaluate LHS
                // before RHS
                lfs[i](t).Assign(t, temp[i])
            }
        })
    }
}

var assignOpToOp = map[token.Token]token.Token{
    token.ADD_ASSIGN: token.ADD,
    token.SUB_ASSIGN: token.SUB,
    token.MUL_ASSIGN: token.MUL,
    token.QUO_ASSIGN: token.QUO,
    token.REM_ASSIGN: token.REM,

    token.AND_ASSIGN:     token.AND,
    token.OR_ASSIGN:      token.OR,
    token.XOR_ASSIGN:     token.XOR,
    token.SHL_ASSIGN:     token.SHL,
    token.SHR_ASSIGN:     token.SHR,
    token.AND_NOT_ASSIGN: token.AND_NOT,
}

func (a *stmtCompiler) doAssignOp(s *ast.AssignStmt) {
    if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
        a.diag("tuple assignment cannot be combined with an arithmetic operation")
        return
    }

    // Create temporary block for extractEffect
    bc := a.enterChild()
    defer bc.exit()

    l := a.compileExpr(bc.block, false, s.Lhs[0])
    r := a.compileExpr(bc.block, false, s.Rhs[0])
    if l == nil || r == nil {
        return
    }

    if l.evalAddr == nil {
        l.diag("cannot assign to %s", l.desc)
        return
    }

    effect, l := l.extractEffect(bc.block, "operator-assignment")

    binop := r.compileBinaryExpr(assignOpToOp[s.Tok], l, r)
    if binop == nil {
        return
    }

    assign := a.compileAssign(s.Pos(), bc.block, l.t, []*expr{binop}, "assignment", "value")
    if assign == nil {
        log.Crashf("compileAssign type check failed")
    }

    lf := l.evalAddr
    a.push(func(t *Thread) {
        effect(t)
        assign(lf(t), t)
    })
}

func (a *stmtCompiler) compileAssignStmt(s *ast.AssignStmt) {
    switch s.Tok {
    case token.ASSIGN, token.DEFINE:
        a.doAssign(s.Lhs, s.Rhs, s.Tok, nil)

    default:
        a.doAssignOp(s)
    }
}

func (a *stmtCompiler) compileReturnStmt(s *ast.ReturnStmt) {
    if a.fnType == nil {
        a.diag("cannot return at the top level")
        return
    }

    if len(s.Results) == 0 && (len(a.fnType.Out) == 0 || a.outVarsNamed) {
        // Simple case.  Simply exit from the function.
        a.flow.putTerm()
        a.push(func(v *Thread) { v.pc = returnPC })
        return
    }

    bc := a.enterChild()
    defer bc.exit()

    // Compile expressions
    bad := false
    rs := make([]*expr, len(s.Results))
    for i, re := range s.Results {
        rs[i] = a.compileExpr(bc.block, false, re)
        if rs[i] == nil {
            bad = true
        }
    }
    if bad {
        return
    }

    // Create assigner

    // However, if the expression list in the "return" statement
    // is a single call to a multi-valued function, the values
    // returned from the called function will be returned from
    // this one.
    assign := a.compileAssign(s.Pos(), bc.block, NewMultiType(a.fnType.Out), rs, "return", "value")

    // XXX(Spec) "The result types of the current function and the
    // called function must match."  Match is fuzzy.  It should
    // say that they must be assignment compatible.

    // Compile
    start := len(a.fnType.In)
    nout := len(a.fnType.Out)
    a.flow.putTerm()
    a.push(func(t *Thread) {
        assign(multiV(t.f.Vars[start:start+nout]), t)
        t.pc = returnPC
    })
}

func (a *stmtCompiler) findLexicalLabel(name *ast.Ident, pred func(*label) bool, errOp, errCtx string) *label {
    bc := a.blockCompiler
    for ; bc != nil; bc = bc.parent {
        if bc.label == nil {
            continue
        }
        l := bc.label
        if name == nil && pred(l) {
            return l
        }
        if name != nil && l.name == name.Name() {
            if !pred(l) {
                a.diag("cannot %s to %s %s", errOp, l.desc, l.name)
                return nil
            }
            return l
        }
    }
    if name == nil {
        a.diag("%s outside %s", errOp, errCtx)
    } else {
        a.diag("%s label %s not defined", errOp, name.Name())
    }
    return nil
}

func (a *stmtCompiler) compileBranchStmt(s *ast.BranchStmt) {
    var pc *uint

    switch s.Tok {
    case token.BREAK:
        l := a.findLexicalLabel(s.Label, func(l *label) bool { return l.breakPC != nil }, "break", "for loop, switch, or select")
        if l == nil {
            return
        }
        pc = l.breakPC

    case token.CONTINUE:
        l := a.findLexicalLabel(s.Label, func(l *label) bool { return l.continuePC != nil }, "continue", "for loop")
        if l == nil {
            return
        }
        pc = l.continuePC

    case token.GOTO:
        l, ok := a.labels[s.Label.Name()]
        if !ok {
            pc := badPC
            l = &label{name: s.Label.Name(), desc: "unresolved label", gotoPC: &pc, used: s.Pos()}
            a.labels[l.name] = l
        }

        pc = l.gotoPC
        a.flow.putGoto(s.Pos(), l.name, a.block)

    case token.FALLTHROUGH:
        a.diag("fallthrough outside switch")
        return

    default:
        log.Crash("Unexpected branch token %v", s.Tok)
    }

    a.flow.put1(false, pc)
    a.push(func(v *Thread) { v.pc = *pc })
}

func (a *stmtCompiler) compileBlockStmt(s *ast.BlockStmt) {
    bc := a.enterChild()
    bc.compileStmts(s)
    bc.exit()
}

func (a *stmtCompiler) compileIfStmt(s *ast.IfStmt) {
    // The scope of any variables declared by [the init] statement
    // extends to the end of the "if" statement and the variables
    // are initialized once before the statement is entered.
    //
    // XXX(Spec) What this really wants to say is that there's an
    // implicit scope wrapping every if, for, and switch
    // statement.  This is subtly different from what it actually
    // says when there's a non-block else clause, because that
    // else claus has to execute in a scope that is *not* the
    // surrounding scope.
    bc := a.enterChild()
    defer bc.exit()

    // Compile init statement, if any
    if s.Init != nil {
        bc.compileStmt(s.Init)
    }

    elsePC := badPC
    endPC := badPC

    // Compile condition, if any.  If there is no condition, we
    // fall through to the body.
    if s.Cond != nil {
        e := bc.compileExpr(bc.block, false, s.Cond)
        switch {
        case e == nil:
            // Error reported by compileExpr
        case !e.t.isBoolean():
            e.diag("'if' condition must be boolean\n\t%v", e.t)
        default:
            eval := e.asBool()
            a.flow.put1(true, &elsePC)
            a.push(func(t *Thread) {
                if !eval(t) {
                    t.pc = elsePC
                }
            })
        }
    }

    // Compile body
    body := bc.enterChild()
    body.compileStmts(s.Body)
    body.exit()

    // Compile else
    if s.Else != nil {
        // Skip over else if we executed the body
        a.flow.put1(false, &endPC)
        a.push(func(v *Thread) { v.pc = endPC })
        elsePC = a.nextPC()
        bc.compileStmt(s.Else)
    } else {
        elsePC = a.nextPC()
    }
    endPC = a.nextPC()
}

func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) {
    // Create implicit scope around switch
    bc := a.enterChild()
    defer bc.exit()

    // Compile init statement, if any
    if s.Init != nil {
        bc.compileStmt(s.Init)
    }

    // Compile condition, if any, and extract its effects
    var cond *expr
    condbc := bc.enterChild()
    if s.Tag != nil {
        e := condbc.compileExpr(condbc.block, false, s.Tag)
        if e != nil {
            var effect func(*Thread)
            effect, cond = e.extractEffect(condbc.block, "switch")
            a.push(effect)
        }
    }

    // Count cases
    ncases := 0
    hasDefault := false
    for _, c := range s.Body.List {
        clause, ok := c.(*ast.CaseClause)
        if !ok {
            a.diagAt(clause, "switch statement must contain case clauses")
            continue
        }
        if clause.Values == nil {
            if hasDefault {
                a.diagAt(clause, "switch statement contains more than one default case")
            }
            hasDefault = true
        } else {
            ncases += len(clause.Values)
        }
    }

    // Compile case expressions
    cases := make([]func(*Thread) bool, ncases)
    i := 0
    for _, c := range s.Body.List {
        clause, ok := c.(*ast.CaseClause)
        if !ok {
            continue
        }
        for _, v := range clause.Values {
            e := condbc.compileExpr(condbc.block, false, v)
            switch {
            case e == nil:
                // Error reported by compileExpr
            case cond == nil && !e.t.isBoolean():
                a.diagAt(v, "'case' condition must be boolean")
            case cond == nil:
                cases[i] = e.asBool()
            case cond != nil:
                // Create comparison
                // TOOD(austin) This produces bad error messages
                compare := e.compileBinaryExpr(token.EQL, cond, e)
                if compare != nil {
                    cases[i] = compare.asBool()
                }
            }
            i++
        }
    }

    // Emit condition
    casePCs := make([]*uint, ncases+1)
    endPC := badPC

    a.flow.put(false, false, casePCs)
    a.push(func(t *Thread) {
        for i, c := range cases {
            if c(t) {
                t.pc = *casePCs[i]
                return
            }
        }
        t.pc = *casePCs[ncases]
    })
    condbc.exit()

    // Compile cases
    i = 0
    for _, c := range s.Body.List {
        clause, ok := c.(*ast.CaseClause)
        if !ok {
            continue
        }

        // Save jump PC's
        pc := a.nextPC()
        if clause.Values != nil {
            for _ = range clause.Values {
                casePCs[i] = &pc
                i++
            }
        } else {
            // Default clause
            casePCs[ncases] = &pc
        }

        // Compile body
        fall := false
        for j, s := range clause.Body {
            if br, ok := s.(*ast.BranchStmt); ok && br.Tok == token.FALLTHROUGH {
                // println("Found fallthrough");
                // It may be used only as the final
                // non-empty statement in a case or
                // default clause in an expression
                // "switch" statement.
                for _, s2 := range clause.Body[j+1:] {
                    // XXX(Spec) 6g also considers
                    // empty blocks to be empty
                    // statements.
                    if _, ok := s2.(*ast.EmptyStmt); !ok {
                        a.diagAt(s, "fallthrough statement must be final statement in case")
                        break
                    }
                }
                fall = true
            } else {
                bc.compileStmt(s)
            }
        }
        // Jump out of switch, unless there was a fallthrough
        if !fall {
            a.flow.put1(false, &endPC)
            a.push(func(v *Thread) { v.pc = endPC })
        }
    }

    // Get end PC
    endPC = a.nextPC()
    if !hasDefault {
        casePCs[ncases] = &endPC
    }
}

func (a *stmtCompiler) compileForStmt(s *ast.ForStmt) {
    // Wrap the entire for in a block.
    bc := a.enterChild()
    defer bc.exit()

    // Compile init statement, if any
    if s.Init != nil {
        bc.compileStmt(s.Init)
    }

    bodyPC := badPC
    postPC := badPC
    checkPC := badPC
    endPC := badPC

    // Jump to condition check.  We generate slightly less code by
    // placing the condition check after the body.
    a.flow.put1(false, &checkPC)
    a.push(func(v *Thread) { v.pc = checkPC })

    // Compile body
    bodyPC = a.nextPC()
    body := bc.enterChild()
    if a.stmtLabel != nil {
        body.label = a.stmtLabel
    } else {
        body.label = &label{resolved: s.Pos()}
    }
    body.label.desc = "for loop"
    body.label.breakPC = &endPC
    body.label.continuePC = &postPC
    body.compileStmts(s.Body)
    body.exit()

    // Compile post, if any
    postPC = a.nextPC()
    if s.Post != nil {
        // TODO(austin) Does the parser disallow short
        // declarations in s.Post?
        bc.compileStmt(s.Post)
    }

    // Compile condition check, if any
    checkPC = a.nextPC()
    if s.Cond == nil {
        // If the condition is absent, it is equivalent to true.
        a.flow.put1(false, &bodyPC)
        a.push(func(v *Thread) { v.pc = bodyPC })
    } else {
        e := bc.compileExpr(bc.block, false, s.Cond)
        switch {
        case e == nil:
            // Error reported by compileExpr
        case !e.t.isBoolean():
            a.diag("'for' condition must be boolean\n\t%v", e.t)
        default:
            eval := e.asBool()
            a.flow.put1(true, &bodyPC)
            a.push(func(t *Thread) {
                if eval(t) {
                    t.pc = bodyPC
                }
            })
        }
    }

    endPC = a.nextPC()
}

/*
 * Block compiler
 */

func (a *blockCompiler) compileStmt(s ast.Stmt) {
    sc := &stmtCompiler{a, s.Pos(), nil}
    sc.compile(s)
}

func (a *blockCompiler) compileStmts(block *ast.BlockStmt) {
    for _, sub := range block.List {
        a.compileStmt(sub)
    }
}

func (a *blockCompiler) enterChild() *blockCompiler {
    block := a.block.enterChild()
    return &blockCompiler{
        funcCompiler: a.funcCompiler,
        block:        block,
        parent:       a,
    }
}

func (a *blockCompiler) exit() { a.block.exit() }

/*
 * Function compiler
 */

func (a *compiler) compileFunc(b *block, decl *FuncDecl, body *ast.BlockStmt) func(*Thread) Func {
    // Create body scope
    //
    // The scope of a parameter or result is the body of the
    // corresponding function.
    bodyScope := b.ChildScope()
    defer bodyScope.exit()
    for i, t := range decl.Type.In {
        if decl.InNames[i] != nil {
            bodyScope.DefineVar(decl.InNames[i].Name(), decl.InNames[i].Pos(), t)
        } else {
            bodyScope.DefineTemp(t)
        }
    }
    for i, t := range decl.Type.Out {
        if decl.OutNames[i] != nil {
            bodyScope.DefineVar(decl.OutNames[i].Name(), decl.OutNames[i].Pos(), t)
        } else {
            bodyScope.DefineTemp(t)
        }
    }

    // Create block context
    cb := newCodeBuf()
    fc := &funcCompiler{
        compiler:     a,
        fnType:       decl.Type,
        outVarsNamed: len(decl.OutNames) > 0 && decl.OutNames[0] != nil,
        codeBuf:      cb,
        flow:         newFlowBuf(cb),
        labels:       make(map[string]*label),
    }
    bc := &blockCompiler{
        funcCompiler: fc,
        block:        bodyScope.block,
    }

    // Compile body
    nerr := a.numError()
    bc.compileStmts(body)
    fc.checkLabels()
    if nerr != a.numError() {
        return nil
    }

    // Check that the body returned if necessary.  We only check
    // this if there were no errors compiling the body.
    if len(decl.Type.Out) > 0 && fc.flow.reachesEnd(0) {
        // XXX(Spec) Not specified.
        a.diagAt(&body.Rbrace, "function ends without a return statement")
        return nil
    }

    code := fc.get()
    maxVars := bodyScope.maxVars
    return func(t *Thread) Func { return &evalFunc{t.f, maxVars, code} }
}

// Checks that labels were resolved and that all jumps obey scoping
// rules.  Reports an error and set fc.err if any check fails.
func (a *funcCompiler) checkLabels() {
    nerr := a.numError()
    for _, l := range a.labels {
        if !l.resolved.IsValid() {
            a.diagAt(&l.used, "label %s not defined", l.name)
        }
    }
    if nerr != a.numError() {
        // Don't check scopes if we have unresolved labels
        return
    }

    // Executing the "goto" statement must not cause any variables
    // to come into scope that were not already in scope at the
    // point of the goto.
    a.flow.gotosObeyScopes(a.compiler)
}