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

View as plain text