| // 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 walk |
| |
| import ( |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/ir" |
| ) |
| |
| // The result of walkStmt MUST be assigned back to n, e.g. |
| // n.Left = walkStmt(n.Left) |
| func walkStmt(n ir.Node) ir.Node { |
| if n == nil { |
| return n |
| } |
| |
| ir.SetPos(n) |
| |
| walkStmtList(n.Init()) |
| |
| switch n.Op() { |
| default: |
| if n.Op() == ir.ONAME { |
| n := n.(*ir.Name) |
| base.Errorf("%v is not a top level statement", n.Sym()) |
| } else { |
| base.Errorf("%v is not a top level statement", n.Op()) |
| } |
| ir.Dump("nottop", n) |
| return n |
| |
| case ir.OAS, |
| ir.OASOP, |
| ir.OAS2, |
| ir.OAS2DOTTYPE, |
| ir.OAS2RECV, |
| ir.OAS2FUNC, |
| ir.OAS2MAPR, |
| ir.OCLOSE, |
| ir.OCOPY, |
| ir.OCALLINTER, |
| ir.OCALL, |
| ir.OCALLFUNC, |
| ir.ODELETE, |
| ir.OSEND, |
| ir.OPRINT, |
| ir.OPRINTN, |
| ir.OPANIC, |
| ir.ORECOVERFP, |
| ir.OGETG: |
| if n.Typecheck() == 0 { |
| base.Fatalf("missing typecheck: %+v", n) |
| } |
| init := ir.TakeInit(n) |
| n = walkExpr(n, &init) |
| if n.Op() == ir.ONAME { |
| // copy rewrote to a statement list and a temp for the length. |
| // Throw away the temp to avoid plain values as statements. |
| n = ir.NewBlockStmt(n.Pos(), init) |
| init = nil |
| } |
| if len(init) > 0 { |
| switch n.Op() { |
| case ir.OAS, ir.OAS2, ir.OBLOCK: |
| n.(ir.InitNode).PtrInit().Prepend(init...) |
| |
| default: |
| init.Append(n) |
| n = ir.NewBlockStmt(n.Pos(), init) |
| } |
| } |
| return n |
| |
| // special case for a receive where we throw away |
| // the value received. |
| case ir.ORECV: |
| n := n.(*ir.UnaryExpr) |
| return walkRecv(n) |
| |
| case ir.OBREAK, |
| ir.OCONTINUE, |
| ir.OFALL, |
| ir.OGOTO, |
| ir.OLABEL, |
| ir.ODCL, |
| ir.ODCLCONST, |
| ir.ODCLTYPE, |
| ir.OCHECKNIL, |
| ir.OVARDEF, |
| ir.OVARKILL, |
| ir.OVARLIVE: |
| return n |
| |
| case ir.OBLOCK: |
| n := n.(*ir.BlockStmt) |
| walkStmtList(n.List) |
| return n |
| |
| case ir.OCASE: |
| base.Errorf("case statement out of place") |
| panic("unreachable") |
| |
| case ir.ODEFER: |
| n := n.(*ir.GoDeferStmt) |
| ir.CurFunc.SetHasDefer(true) |
| ir.CurFunc.NumDefers++ |
| if ir.CurFunc.NumDefers > maxOpenDefers { |
| // Don't allow open-coded defers if there are more than |
| // 8 defers in the function, since we use a single |
| // byte to record active defers. |
| ir.CurFunc.SetOpenCodedDeferDisallowed(true) |
| } |
| if n.Esc() != ir.EscNever { |
| // If n.Esc is not EscNever, then this defer occurs in a loop, |
| // so open-coded defers cannot be used in this function. |
| ir.CurFunc.SetOpenCodedDeferDisallowed(true) |
| } |
| fallthrough |
| case ir.OGO: |
| n := n.(*ir.GoDeferStmt) |
| return walkGoDefer(n) |
| |
| case ir.OFOR, ir.OFORUNTIL: |
| n := n.(*ir.ForStmt) |
| return walkFor(n) |
| |
| case ir.OIF: |
| n := n.(*ir.IfStmt) |
| return walkIf(n) |
| |
| case ir.ORETURN: |
| n := n.(*ir.ReturnStmt) |
| return walkReturn(n) |
| |
| case ir.OTAILCALL: |
| n := n.(*ir.TailCallStmt) |
| |
| var init ir.Nodes |
| n.Call.X = walkExpr(n.Call.X, &init) |
| |
| if len(init) > 0 { |
| init.Append(n) |
| return ir.NewBlockStmt(n.Pos(), init) |
| } |
| return n |
| |
| case ir.OINLMARK: |
| n := n.(*ir.InlineMarkStmt) |
| return n |
| |
| case ir.OSELECT: |
| n := n.(*ir.SelectStmt) |
| walkSelect(n) |
| return n |
| |
| case ir.OSWITCH: |
| n := n.(*ir.SwitchStmt) |
| walkSwitch(n) |
| return n |
| |
| case ir.ORANGE: |
| n := n.(*ir.RangeStmt) |
| return walkRange(n) |
| } |
| |
| // No return! Each case must return (or panic), |
| // to avoid confusion about what gets returned |
| // in the presence of type assertions. |
| } |
| |
| func walkStmtList(s []ir.Node) { |
| for i := range s { |
| s[i] = walkStmt(s[i]) |
| } |
| } |
| |
| // walkFor walks an OFOR or OFORUNTIL node. |
| func walkFor(n *ir.ForStmt) ir.Node { |
| if n.Cond != nil { |
| init := ir.TakeInit(n.Cond) |
| walkStmtList(init) |
| n.Cond = walkExpr(n.Cond, &init) |
| n.Cond = ir.InitExpr(init, n.Cond) |
| } |
| |
| n.Post = walkStmt(n.Post) |
| if n.Op() == ir.OFORUNTIL { |
| walkStmtList(n.Late) |
| } |
| walkStmtList(n.Body) |
| return n |
| } |
| |
| // validGoDeferCall reports whether call is a valid call to appear in |
| // a go or defer statement; that is, whether it's a regular function |
| // call without arguments or results. |
| func validGoDeferCall(call ir.Node) bool { |
| if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC && len(call.KeepAlive) == 0 { |
| sig := call.X.Type() |
| return sig.NumParams()+sig.NumResults() == 0 |
| } |
| return false |
| } |
| |
| // walkGoDefer walks an OGO or ODEFER node. |
| func walkGoDefer(n *ir.GoDeferStmt) ir.Node { |
| if !validGoDeferCall(n.Call) { |
| base.FatalfAt(n.Pos(), "invalid %v call: %v", n.Op(), n.Call) |
| } |
| |
| var init ir.Nodes |
| |
| call := n.Call.(*ir.CallExpr) |
| call.X = walkExpr(call.X, &init) |
| |
| if len(init) > 0 { |
| init.Append(n) |
| return ir.NewBlockStmt(n.Pos(), init) |
| } |
| return n |
| } |
| |
| // walkIf walks an OIF node. |
| func walkIf(n *ir.IfStmt) ir.Node { |
| n.Cond = walkExpr(n.Cond, n.PtrInit()) |
| walkStmtList(n.Body) |
| walkStmtList(n.Else) |
| return n |
| } |