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  		n.Call.Fun = walkExpr(n.Call.Fun, &init)
   143  
   144  		if len(init) > 0 {
   145  			init.Append(n)
   146  			return ir.NewBlockStmt(n.Pos(), init)
   147  		}
   148  		return n
   149  
   150  	case ir.OINLMARK:
   151  		n := n.(*ir.InlineMarkStmt)
   152  		return n
   153  
   154  	case ir.OSELECT:
   155  		n := n.(*ir.SelectStmt)
   156  		walkSelect(n)
   157  		return n
   158  
   159  	case ir.OSWITCH:
   160  		n := n.(*ir.SwitchStmt)
   161  		walkSwitch(n)
   162  		return n
   163  
   164  	case ir.ORANGE:
   165  		n := n.(*ir.RangeStmt)
   166  		return walkRange(n)
   167  	}
   168  
   169  	// No return! Each case must return (or panic),
   170  	// to avoid confusion about what gets returned
   171  	// in the presence of type assertions.
   172  }
   173  
   174  func walkStmtList(s []ir.Node) {
   175  	for i := range s {
   176  		s[i] = walkStmt(s[i])
   177  	}
   178  }
   179  
   180  // walkFor walks an OFOR node.
   181  func walkFor(n *ir.ForStmt) ir.Node {
   182  	if n.Cond != nil {
   183  		init := ir.TakeInit(n.Cond)
   184  		walkStmtList(init)
   185  		n.Cond = walkExpr(n.Cond, &init)
   186  		n.Cond = ir.InitExpr(init, n.Cond)
   187  	}
   188  
   189  	n.Post = walkStmt(n.Post)
   190  	walkStmtList(n.Body)
   191  	return n
   192  }
   193  
   194  // validGoDeferCall reports whether call is a valid call to appear in
   195  // a go or defer statement; that is, whether it's a regular function
   196  // call without arguments or results.
   197  func validGoDeferCall(call ir.Node) bool {
   198  	if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC && len(call.KeepAlive) == 0 {
   199  		sig := call.Fun.Type()
   200  		return sig.NumParams()+sig.NumResults() == 0
   201  	}
   202  	return false
   203  }
   204  
   205  // walkGoDefer walks an OGO or ODEFER node.
   206  func walkGoDefer(n *ir.GoDeferStmt) ir.Node {
   207  	if !validGoDeferCall(n.Call) {
   208  		base.FatalfAt(n.Pos(), "invalid %v call: %v", n.Op(), n.Call)
   209  	}
   210  
   211  	var init ir.Nodes
   212  
   213  	call := n.Call.(*ir.CallExpr)
   214  	call.Fun = walkExpr(call.Fun, &init)
   215  
   216  	if len(init) > 0 {
   217  		init.Append(n)
   218  		return ir.NewBlockStmt(n.Pos(), init)
   219  	}
   220  	return n
   221  }
   222  
   223  // walkIf walks an OIF node.
   224  func walkIf(n *ir.IfStmt) ir.Node {
   225  	n.Cond = walkExpr(n.Cond, n.PtrInit())
   226  	walkStmtList(n.Body)
   227  	walkStmtList(n.Else)
   228  	return n
   229  }
   230  

View as plain text