summaryrefslogtreecommitdiff
path: root/usr/austin/eval/stmt.go
diff options
context:
space:
mode:
Diffstat (limited to 'usr/austin/eval/stmt.go')
-rw-r--r--usr/austin/eval/stmt.go1313
1 files changed, 0 insertions, 1313 deletions
diff --git a/usr/austin/eval/stmt.go b/usr/austin/eval/stmt.go
deleted file mode 100644
index 9ec6fb83d..000000000
--- a/usr/austin/eval/stmt.go
+++ /dev/null
@@ -1,1313 +0,0 @@
-// 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:len(numVars)];
- }
- 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:len(clause.Body)] {
- // 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);
-}