github.com/bir3/gocompiler@v0.3.205/src/cmd/compile/internal/walk/stmt.go (about)

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