Source file src/cmd/compile/internal/walk/stmt.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package walk
     6  
     7  import (
     8  	"cmd/compile/internal/base"
     9  	"cmd/compile/internal/ir"
    10  )
    11  
    12  // The result of walkStmt MUST be assigned back to n, e.g.
    13  // 	n.Left = walkStmt(n.Left)
    14  func walkStmt(n ir.Node) ir.Node {
    15  	if n == nil {
    16  		return n
    17  	}
    18  
    19  	ir.SetPos(n)
    20  
    21  	walkStmtList(n.Init())
    22  
    23  	switch n.Op() {
    24  	default:
    25  		if n.Op() == ir.ONAME {
    26  			n := n.(*ir.Name)
    27  			base.Errorf("%v is not a top level statement", n.Sym())
    28  		} else {
    29  			base.Errorf("%v is not a top level statement", n.Op())
    30  		}
    31  		ir.Dump("nottop", n)
    32  		return n
    33  
    34  	case ir.OAS,
    35  		ir.OASOP,
    36  		ir.OAS2,
    37  		ir.OAS2DOTTYPE,
    38  		ir.OAS2RECV,
    39  		ir.OAS2FUNC,
    40  		ir.OAS2MAPR,
    41  		ir.OCLOSE,
    42  		ir.OCOPY,
    43  		ir.OCALLINTER,
    44  		ir.OCALL,
    45  		ir.OCALLFUNC,
    46  		ir.ODELETE,
    47  		ir.OSEND,
    48  		ir.OPRINT,
    49  		ir.OPRINTN,
    50  		ir.OPANIC,
    51  		ir.ORECOVERFP,
    52  		ir.OGETG:
    53  		if n.Typecheck() == 0 {
    54  			base.Fatalf("missing typecheck: %+v", n)
    55  		}
    56  		init := ir.TakeInit(n)
    57  		n = walkExpr(n, &init)
    58  		if n.Op() == ir.ONAME {
    59  			// copy rewrote to a statement list and a temp for the length.
    60  			// Throw away the temp to avoid plain values as statements.
    61  			n = ir.NewBlockStmt(n.Pos(), init)
    62  			init = nil
    63  		}
    64  		if len(init) > 0 {
    65  			switch n.Op() {
    66  			case ir.OAS, ir.OAS2, ir.OBLOCK:
    67  				n.(ir.InitNode).PtrInit().Prepend(init...)
    68  
    69  			default:
    70  				init.Append(n)
    71  				n = ir.NewBlockStmt(n.Pos(), init)
    72  			}
    73  		}
    74  		return n
    75  
    76  	// special case for a receive where we throw away
    77  	// the value received.
    78  	case ir.ORECV:
    79  		n := n.(*ir.UnaryExpr)
    80  		return walkRecv(n)
    81  
    82  	case ir.OBREAK,
    83  		ir.OCONTINUE,
    84  		ir.OFALL,
    85  		ir.OGOTO,
    86  		ir.OLABEL,
    87  		ir.ODCL,
    88  		ir.ODCLCONST,
    89  		ir.ODCLTYPE,
    90  		ir.OCHECKNIL,
    91  		ir.OVARDEF,
    92  		ir.OVARKILL,
    93  		ir.OVARLIVE:
    94  		return n
    95  
    96  	case ir.OBLOCK:
    97  		n := n.(*ir.BlockStmt)
    98  		walkStmtList(n.List)
    99  		return n
   100  
   101  	case ir.OCASE:
   102  		base.Errorf("case statement out of place")
   103  		panic("unreachable")
   104  
   105  	case ir.ODEFER:
   106  		n := n.(*ir.GoDeferStmt)
   107  		ir.CurFunc.SetHasDefer(true)
   108  		ir.CurFunc.NumDefers++
   109  		if ir.CurFunc.NumDefers > maxOpenDefers {
   110  			// Don't allow open-coded defers if there are more than
   111  			// 8 defers in the function, since we use a single
   112  			// byte to record active defers.
   113  			ir.CurFunc.SetOpenCodedDeferDisallowed(true)
   114  		}
   115  		if n.Esc() != ir.EscNever {
   116  			// If n.Esc is not EscNever, then this defer occurs in a loop,
   117  			// so open-coded defers cannot be used in this function.
   118  			ir.CurFunc.SetOpenCodedDeferDisallowed(true)
   119  		}
   120  		fallthrough
   121  	case ir.OGO:
   122  		n := n.(*ir.GoDeferStmt)
   123  		return walkGoDefer(n)
   124  
   125  	case ir.OFOR, ir.OFORUNTIL:
   126  		n := n.(*ir.ForStmt)
   127  		return walkFor(n)
   128  
   129  	case ir.OIF:
   130  		n := n.(*ir.IfStmt)
   131  		return walkIf(n)
   132  
   133  	case ir.ORETURN:
   134  		n := n.(*ir.ReturnStmt)
   135  		return walkReturn(n)
   136  
   137  	case ir.OTAILCALL:
   138  		n := n.(*ir.TailCallStmt)
   139  
   140  		var init ir.Nodes
   141  		n.Call.X = walkExpr(n.Call.X, &init)
   142  
   143  		if len(init) > 0 {
   144  			init.Append(n)
   145  			return ir.NewBlockStmt(n.Pos(), init)
   146  		}
   147  		return n
   148  
   149  	case ir.OINLMARK:
   150  		n := n.(*ir.InlineMarkStmt)
   151  		return n
   152  
   153  	case ir.OSELECT:
   154  		n := n.(*ir.SelectStmt)
   155  		walkSelect(n)
   156  		return n
   157  
   158  	case ir.OSWITCH:
   159  		n := n.(*ir.SwitchStmt)
   160  		walkSwitch(n)
   161  		return n
   162  
   163  	case ir.ORANGE:
   164  		n := n.(*ir.RangeStmt)
   165  		return walkRange(n)
   166  	}
   167  
   168  	// No return! Each case must return (or panic),
   169  	// to avoid confusion about what gets returned
   170  	// in the presence of type assertions.
   171  }
   172  
   173  func walkStmtList(s []ir.Node) {
   174  	for i := range s {
   175  		s[i] = walkStmt(s[i])
   176  	}
   177  }
   178  
   179  // walkFor walks an OFOR or OFORUNTIL node.
   180  func walkFor(n *ir.ForStmt) ir.Node {
   181  	if n.Cond != nil {
   182  		init := ir.TakeInit(n.Cond)
   183  		walkStmtList(init)
   184  		n.Cond = walkExpr(n.Cond, &init)
   185  		n.Cond = ir.InitExpr(init, n.Cond)
   186  	}
   187  
   188  	n.Post = walkStmt(n.Post)
   189  	if n.Op() == ir.OFORUNTIL {
   190  		walkStmtList(n.Late)
   191  	}
   192  	walkStmtList(n.Body)
   193  	return n
   194  }
   195  
   196  // validGoDeferCall reports whether call is a valid call to appear in
   197  // a go or defer statement; that is, whether it's a regular function
   198  // call without arguments or results.
   199  func validGoDeferCall(call ir.Node) bool {
   200  	if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC && len(call.KeepAlive) == 0 {
   201  		sig := call.X.Type()
   202  		return sig.NumParams()+sig.NumResults() == 0
   203  	}
   204  	return false
   205  }
   206  
   207  // walkGoDefer walks an OGO or ODEFER node.
   208  func walkGoDefer(n *ir.GoDeferStmt) ir.Node {
   209  	if !validGoDeferCall(n.Call) {
   210  		base.FatalfAt(n.Pos(), "invalid %v call: %v", n.Op(), n.Call)
   211  	}
   212  
   213  	var init ir.Nodes
   214  
   215  	call := n.Call.(*ir.CallExpr)
   216  	call.X = walkExpr(call.X, &init)
   217  
   218  	if len(init) > 0 {
   219  		init.Append(n)
   220  		return ir.NewBlockStmt(n.Pos(), init)
   221  	}
   222  	return n
   223  }
   224  
   225  // walkIf walks an OIF node.
   226  func walkIf(n *ir.IfStmt) ir.Node {
   227  	n.Cond = walkExpr(n.Cond, n.PtrInit())
   228  	walkStmtList(n.Body)
   229  	walkStmtList(n.Else)
   230  	return n
   231  }
   232  

View as plain text