mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
This flag is only needed to prevent the directClosureCall optimization in walkCall, when called for walkGoDefer. But walkGoDefer don't need to call walkCall: at this point in the compile, the call expression isn't a real call anymore. Instead, we just need to walkExpr on the function expression. Change-Id: I8a5176cfe1bff53700cbd21ed1b479ebd9a839ad Reviewed-on: https://go-review.googlesource.com/c/go/+/330271 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Trust: Matthew Dempsky <mdempsky@google.com>
243 lines
4.9 KiB
Go
243 lines
4.9 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package walk
|
|
|
|
import (
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/ir"
|
|
)
|
|
|
|
// The result of walkStmt MUST be assigned back to n, e.g.
|
|
// n.Left = walkStmt(n.Left)
|
|
func walkStmt(n ir.Node) ir.Node {
|
|
if n == nil {
|
|
return n
|
|
}
|
|
|
|
ir.SetPos(n)
|
|
|
|
walkStmtList(n.Init())
|
|
|
|
switch n.Op() {
|
|
default:
|
|
if n.Op() == ir.ONAME {
|
|
n := n.(*ir.Name)
|
|
base.Errorf("%v is not a top level statement", n.Sym())
|
|
} else {
|
|
base.Errorf("%v is not a top level statement", n.Op())
|
|
}
|
|
ir.Dump("nottop", n)
|
|
return n
|
|
|
|
case ir.OAS,
|
|
ir.OASOP,
|
|
ir.OAS2,
|
|
ir.OAS2DOTTYPE,
|
|
ir.OAS2RECV,
|
|
ir.OAS2FUNC,
|
|
ir.OAS2MAPR,
|
|
ir.OCLOSE,
|
|
ir.OCOPY,
|
|
ir.OCALLMETH,
|
|
ir.OCALLINTER,
|
|
ir.OCALL,
|
|
ir.OCALLFUNC,
|
|
ir.ODELETE,
|
|
ir.OSEND,
|
|
ir.OPRINT,
|
|
ir.OPRINTN,
|
|
ir.OPANIC,
|
|
ir.ORECOVER,
|
|
ir.OGETG:
|
|
if n.Typecheck() == 0 {
|
|
base.Fatalf("missing typecheck: %+v", n)
|
|
}
|
|
init := ir.TakeInit(n)
|
|
n = walkExpr(n, &init)
|
|
if n.Op() == ir.ONAME {
|
|
// copy rewrote to a statement list and a temp for the length.
|
|
// Throw away the temp to avoid plain values as statements.
|
|
n = ir.NewBlockStmt(n.Pos(), init)
|
|
init = nil
|
|
}
|
|
if len(init) > 0 {
|
|
switch n.Op() {
|
|
case ir.OAS, ir.OAS2, ir.OBLOCK:
|
|
n.(ir.InitNode).PtrInit().Prepend(init...)
|
|
|
|
default:
|
|
init.Append(n)
|
|
n = ir.NewBlockStmt(n.Pos(), init)
|
|
}
|
|
}
|
|
return n
|
|
|
|
// special case for a receive where we throw away
|
|
// the value received.
|
|
case ir.ORECV:
|
|
n := n.(*ir.UnaryExpr)
|
|
return walkRecv(n)
|
|
|
|
case ir.OBREAK,
|
|
ir.OCONTINUE,
|
|
ir.OFALL,
|
|
ir.OGOTO,
|
|
ir.OLABEL,
|
|
ir.ODCL,
|
|
ir.ODCLCONST,
|
|
ir.ODCLTYPE,
|
|
ir.OCHECKNIL,
|
|
ir.OVARDEF,
|
|
ir.OVARKILL,
|
|
ir.OVARLIVE:
|
|
return n
|
|
|
|
case ir.OBLOCK:
|
|
n := n.(*ir.BlockStmt)
|
|
walkStmtList(n.List)
|
|
return n
|
|
|
|
case ir.OCASE:
|
|
base.Errorf("case statement out of place")
|
|
panic("unreachable")
|
|
|
|
case ir.ODEFER:
|
|
n := n.(*ir.GoDeferStmt)
|
|
ir.CurFunc.SetHasDefer(true)
|
|
ir.CurFunc.NumDefers++
|
|
if ir.CurFunc.NumDefers > maxOpenDefers {
|
|
// Don't allow open-coded defers if there are more than
|
|
// 8 defers in the function, since we use a single
|
|
// byte to record active defers.
|
|
ir.CurFunc.SetOpenCodedDeferDisallowed(true)
|
|
}
|
|
if n.Esc() != ir.EscNever {
|
|
// If n.Esc is not EscNever, then this defer occurs in a loop,
|
|
// so open-coded defers cannot be used in this function.
|
|
ir.CurFunc.SetOpenCodedDeferDisallowed(true)
|
|
}
|
|
fallthrough
|
|
case ir.OGO:
|
|
n := n.(*ir.GoDeferStmt)
|
|
return walkGoDefer(n)
|
|
|
|
case ir.OFOR, ir.OFORUNTIL:
|
|
n := n.(*ir.ForStmt)
|
|
return walkFor(n)
|
|
|
|
case ir.OIF:
|
|
n := n.(*ir.IfStmt)
|
|
return walkIf(n)
|
|
|
|
case ir.ORETURN:
|
|
n := n.(*ir.ReturnStmt)
|
|
return walkReturn(n)
|
|
|
|
case ir.OTAILCALL:
|
|
n := n.(*ir.TailCallStmt)
|
|
return n
|
|
|
|
case ir.OINLMARK:
|
|
n := n.(*ir.InlineMarkStmt)
|
|
return n
|
|
|
|
case ir.OSELECT:
|
|
n := n.(*ir.SelectStmt)
|
|
walkSelect(n)
|
|
return n
|
|
|
|
case ir.OSWITCH:
|
|
n := n.(*ir.SwitchStmt)
|
|
walkSwitch(n)
|
|
return n
|
|
|
|
case ir.ORANGE:
|
|
n := n.(*ir.RangeStmt)
|
|
return walkRange(n)
|
|
}
|
|
|
|
// No return! Each case must return (or panic),
|
|
// to avoid confusion about what gets returned
|
|
// in the presence of type assertions.
|
|
}
|
|
|
|
func walkStmtList(s []ir.Node) {
|
|
for i := range s {
|
|
s[i] = walkStmt(s[i])
|
|
}
|
|
}
|
|
|
|
// walkFor walks an OFOR or OFORUNTIL node.
|
|
func walkFor(n *ir.ForStmt) ir.Node {
|
|
if n.Cond != nil {
|
|
init := ir.TakeInit(n.Cond)
|
|
walkStmtList(init)
|
|
n.Cond = walkExpr(n.Cond, &init)
|
|
n.Cond = ir.InitExpr(init, n.Cond)
|
|
}
|
|
|
|
n.Post = walkStmt(n.Post)
|
|
if n.Op() == ir.OFORUNTIL {
|
|
walkStmtList(n.Late)
|
|
}
|
|
walkStmtList(n.Body)
|
|
return n
|
|
}
|
|
|
|
// validGoDeferCall reports whether call is a valid call to appear in
|
|
// a go or defer statement; that is, whether it's a regular function
|
|
// call without arguments or results.
|
|
func validGoDeferCall(call ir.Node) bool {
|
|
if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC && len(call.KeepAlive) == 0 {
|
|
sig := call.X.Type()
|
|
return sig.NumParams()+sig.NumResults() == 0
|
|
}
|
|
return false
|
|
}
|
|
|
|
// walkGoDefer walks an OGO or ODEFER node.
|
|
func walkGoDefer(n *ir.GoDeferStmt) ir.Node {
|
|
if !validGoDeferCall(n.Call) {
|
|
base.FatalfAt(n.Pos(), "invalid %v call: %v", n.Op(), n.Call)
|
|
}
|
|
|
|
var init ir.Nodes
|
|
|
|
call := n.Call.(*ir.CallExpr)
|
|
call.X = walkExpr(call.X, &init)
|
|
|
|
if len(init) > 0 {
|
|
init.Append(n)
|
|
return ir.NewBlockStmt(n.Pos(), init)
|
|
}
|
|
return n
|
|
}
|
|
|
|
// walkIf walks an OIF node.
|
|
func walkIf(n *ir.IfStmt) ir.Node {
|
|
n.Cond = walkExpr(n.Cond, n.PtrInit())
|
|
walkStmtList(n.Body)
|
|
walkStmtList(n.Else)
|
|
return n
|
|
}
|
|
|
|
// undoVariadic turns a call to a variadic function of the form
|
|
//
|
|
// f(a, b, []T{c, d, e}...)
|
|
//
|
|
// back into
|
|
//
|
|
// f(a, b, c, d, e)
|
|
//
|
|
func undoVariadic(call *ir.CallExpr) {
|
|
if call.IsDDD {
|
|
last := len(call.Args) - 1
|
|
if va := call.Args[last]; va.Op() == ir.OSLICELIT {
|
|
va := va.(*ir.CompLitExpr)
|
|
call.Args = append(call.Args[:last], va.List...)
|
|
call.IsDDD = false
|
|
}
|
|
}
|
|
}
|