// 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 ( "bignum"; "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 ...) { 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.Value, 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.Value, &pos) } else { a.diagAt(ident, "variable %s redeclared in this block", ident.Value) } 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.Value, 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.Value, &pos) } else { a.diagAt(d.Name, "identifier %s redeclared in this block", d.Name.Value) } } 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.Value]; if ok { if l.resolved.IsValid() { a.diag("label %s redeclared in this block\n\tprevious declaration at %s", s.Label.Value, &l.resolved) } } else { pc := badPC; l = &label{name: s.Label.Value, 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() *bignum.Integer { return bignum.Int(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.Value]; 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.Value { 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.Value) } 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.Value]; if !ok { pc := badPC; l = &label{name: s.Label.Value, 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].Value, 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].Value, 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); }