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  //
    14  //	n.Left = walkStmt(n.Left)
    15  func walkStmt(n ir.Node) ir.Node {
    16  	if n == nil {
    17  		return n
    18  	}
    19  
    20  	ir.SetPos(n)
    21  
    22  	walkStmtList(n.Init())
    23  
    24  	switch n.Op() {
    25  	default:
    26  		if n.Op() == ir.ONAME {
    27  			n := n.(*ir.Name)
    28  			base.Errorf("%v is not a top level statement", n.Sym())
    29  		} else {
    30  			base.Errorf("%v is not a top level statement", n.Op())
    31  		}
    32  		ir.Dump("nottop", n)
    33  		return n
    34  
    35  	case ir.OAS,
    36  		ir.OASOP,
    37  		ir.OAS2,
    38  		ir.OAS2DOTTYPE,
    39  		ir.OAS2RECV,
    40  		ir.OAS2FUNC,
    41  		ir.OAS2MAPR,
    42  		ir.OCLEAR,
    43  		ir.OCLOSE,
    44  		ir.OCOPY,
    45  		ir.OCALLINTER,
    46  		ir.OCALL,
    47  		ir.OCALLFUNC,
    48  		ir.ODELETE,
    49  		ir.OSEND,
    50  		ir.OPRINT,
    51  		ir.OPRINTLN,
    52  		ir.OPANIC,
    53  		ir.ORECOVERFP,
    54  		ir.OGETG:
    55  		if n.Typecheck() == 0 {
    56  			base.Fatalf("missing typecheck: %+v", n)
    57  		}
    58  
    59  		init := ir.TakeInit(n)
    60  		n = walkExpr(n, &init)
    61  		if n.Op() == ir.ONAME {
    62  			// copy rewrote to a statement list and a temp for the length.
    63  			// Throw away the temp to avoid plain values as statements.
    64  			n = ir.NewBlockStmt(n.Pos(), init)
    65  			init = nil
    66  		}
    67  		if len(init) > 0 {
    68  			switch n.Op() {
    69  			case ir.OAS, ir.OAS2, ir.OBLOCK:
    70  				n.(ir.InitNode).PtrInit().Prepend(init...)
    71  
    72  			default:
    73  				init.Append(n)
    74  				n = ir.NewBlockStmt(n.Pos(), init)
    75  			}
    76  		}
    77  		return n
    78  
    79  	// special case for a receive where we throw away
    80  	// the value received.
    81  	case ir.ORECV:
    82  		n := n.(*ir.UnaryExpr)
    83  		return walkRecv(n)
    84  
    85  	case ir.OBREAK,
    86  		ir.OCONTINUE,
    87  		ir.OFALL,
    88  		ir.OGOTO,
    89  		ir.OLABEL,
    90  		ir.OJUMPTABLE,
    91  		ir.OINTERFACESWITCH,
    92  		ir.ODCL,
    93  		ir.OCHECKNIL:
    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 || n.DeferAt != nil {
   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  			// Also don't allow if we need to use deferprocat.
   114  			ir.CurFunc.SetOpenCodedDeferDisallowed(true)
   115  		}
   116  		if n.Esc() != ir.EscNever {
   117  			// If n.Esc is not EscNever, then this defer occurs in a loop,
   118  			// so open-coded defers cannot be used in this function.
   119  			ir.CurFunc.SetOpenCodedDeferDisallowed(true)
   120  		}
   121  		fallthrough
   122  	case ir.OGO:
   123  		n := n.(*ir.GoDeferStmt)
   124  		return walkGoDefer(n)
   125  
   126  	case ir.OFOR:
   127  		n := n.(*ir.ForStmt)
   128  		return walkFor(n)
   129  
   130  	case ir.OIF:
   131  		n := n.(*ir.IfStmt)
   132  		return walkIf(n)
   133  
   134  	case ir.ORETURN:
   135  		n := n.(*ir.ReturnStmt)
   136  		return walkReturn(n)
   137  
   138  	case ir.OTAILCALL:
   139  		n := n.(*ir.TailCallStmt)
   140  
   141  		var init ir.Nodes
   142  		call := n.Call.(*ir.CallExpr)
   143  		call.Fun = walkExpr(call.Fun, &init)
   144  
   145  		if len(init) > 0 {
   146  			init.Append(n)
   147  			return ir.NewBlockStmt(n.Pos(), init)
   148  		}
   149  		return n
   150  
   151  	case ir.OINLMARK:
   152  		n := n.(*ir.InlineMarkStmt)
   153  		return n
   154  
   155  	case ir.OSELECT:
   156  		n := n.(*ir.SelectStmt)
   157  		walkSelect(n)
   158  		return n
   159  
   160  	case ir.OSWITCH:
   161  		n := n.(*ir.SwitchStmt)
   162  		walkSwitch(n)
   163  		return n
   164  
   165  	case ir.ORANGE:
   166  		n := n.(*ir.RangeStmt)
   167  		return walkRange(n)
   168  	}
   169  
   170  	// No return! Each case must return (or panic),
   171  	// to avoid confusion about what gets returned
   172  	// in the presence of type assertions.
   173  }
   174  
   175  func walkStmtList(s []ir.Node) {
   176  	for i := range s {
   177  		s[i] = walkStmt(s[i])
   178  	}
   179  }
   180  
   181  // walkFor walks an OFOR node.
   182  func walkFor(n *ir.ForStmt) ir.Node {
   183  	if n.Cond != nil {
   184  		init := ir.TakeInit(n.Cond)
   185  		walkStmtList(init)
   186  		n.Cond = walkExpr(n.Cond, &init)
   187  		n.Cond = ir.InitExpr(init, n.Cond)
   188  	}
   189  
   190  	n.Post = walkStmt(n.Post)
   191  	walkStmtList(n.Body)
   192  	return n
   193  }
   194  
   195  // validGoDeferCall reports whether call is a valid call to appear in
   196  // a go or defer statement; that is, whether it's a regular function
   197  // call without arguments or results.
   198  func validGoDeferCall(call ir.Node) bool {
   199  	if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC && len(call.KeepAlive) == 0 {
   200  		sig := call.Fun.Type()
   201  		return sig.NumParams()+sig.NumResults() == 0
   202  	}
   203  	return false
   204  }
   205  
   206  // walkGoDefer walks an OGO or ODEFER node.
   207  func walkGoDefer(n *ir.GoDeferStmt) ir.Node {
   208  	if !validGoDeferCall(n.Call) {
   209  		base.FatalfAt(n.Pos(), "invalid %v call: %v", n.Op(), n.Call)
   210  	}
   211  
   212  	var init ir.Nodes
   213  
   214  	call := n.Call.(*ir.CallExpr)
   215  	call.Fun = walkExpr(call.Fun, &init)
   216  
   217  	if len(init) > 0 {
   218  		init.Append(n)
   219  		return ir.NewBlockStmt(n.Pos(), init)
   220  	}
   221  	return n
   222  }
   223  
   224  // walkIf walks an OIF node.
   225  func walkIf(n *ir.IfStmt) ir.Node {
   226  	n.Cond = walkExpr(n.Cond, n.PtrInit())
   227  	walkStmtList(n.Body)
   228  	walkStmtList(n.Else)
   229  	return n
   230  }
   231  

View as plain text