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
5package walk
6
7import (
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)
15func 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
174func walkStmtList(s []ir.Node) {
175	for i := range s {
176		s[i] = walkStmt(s[i])
177	}
178}
179
180// walkFor walks an OFOR node.
181func 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.
197func 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.
206func 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.
224func 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