mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
cmd/compile: fix bad order of evaluation for multi-value f()(g()) calls
The compiler use to compile f()(g()) as: t1, t2 := g() f()(t1, t2) That violates the Go spec, since when "..., all function calls, ... are evaluated in lexical left-to-right order" This PR fixes the bug by compiling f()(g()) as: t0 := f() t1, t2 := g() t0(t1, t2) to make "f()" to be evaluated before "g()". Fixes #50672 Change-Id: I6a766f3dfc7347d10f8fa3a151f6a5ea79bcf818 Reviewed-on: https://go-review.googlesource.com/c/go/+/392834 TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
parent
e0ae8540ab
commit
7b314d27ce
@ -162,6 +162,7 @@ func transformCall(n *ir.CallExpr) {
|
|||||||
ir.SetPos(n)
|
ir.SetPos(n)
|
||||||
// n.Type() can be nil for calls with no return value
|
// n.Type() can be nil for calls with no return value
|
||||||
assert(n.Typecheck() == 1)
|
assert(n.Typecheck() == 1)
|
||||||
|
typecheck.RewriteNonNameCall(n)
|
||||||
transformArgs(n)
|
transformArgs(n)
|
||||||
l := n.X
|
l := n.X
|
||||||
t := l.Type()
|
t := l.Type()
|
||||||
|
@ -734,35 +734,40 @@ func IndexConst(n ir.Node) int64 {
|
|||||||
return ir.IntVal(types.Types[types.TINT], v)
|
return ir.IntVal(types.Types[types.TINT], v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// callOrChan reports whether n is a call or channel operation.
|
||||||
|
func callOrChan(n ir.Node) bool {
|
||||||
|
switch n.Op() {
|
||||||
|
case ir.OAPPEND,
|
||||||
|
ir.OCALL,
|
||||||
|
ir.OCALLFUNC,
|
||||||
|
ir.OCALLINTER,
|
||||||
|
ir.OCALLMETH,
|
||||||
|
ir.OCAP,
|
||||||
|
ir.OCLOSE,
|
||||||
|
ir.OCOMPLEX,
|
||||||
|
ir.OCOPY,
|
||||||
|
ir.ODELETE,
|
||||||
|
ir.OIMAG,
|
||||||
|
ir.OLEN,
|
||||||
|
ir.OMAKE,
|
||||||
|
ir.ONEW,
|
||||||
|
ir.OPANIC,
|
||||||
|
ir.OPRINT,
|
||||||
|
ir.OPRINTN,
|
||||||
|
ir.OREAL,
|
||||||
|
ir.ORECOVER,
|
||||||
|
ir.ORECV,
|
||||||
|
ir.OUNSAFEADD,
|
||||||
|
ir.OUNSAFESLICE:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// anyCallOrChan reports whether n contains any calls or channel operations.
|
// anyCallOrChan reports whether n contains any calls or channel operations.
|
||||||
func anyCallOrChan(n ir.Node) bool {
|
func anyCallOrChan(n ir.Node) bool {
|
||||||
return ir.Any(n, func(n ir.Node) bool {
|
return ir.Any(n, func(n ir.Node) bool {
|
||||||
switch n.Op() {
|
return callOrChan(n)
|
||||||
case ir.OAPPEND,
|
|
||||||
ir.OCALL,
|
|
||||||
ir.OCALLFUNC,
|
|
||||||
ir.OCALLINTER,
|
|
||||||
ir.OCALLMETH,
|
|
||||||
ir.OCAP,
|
|
||||||
ir.OCLOSE,
|
|
||||||
ir.OCOMPLEX,
|
|
||||||
ir.OCOPY,
|
|
||||||
ir.ODELETE,
|
|
||||||
ir.OIMAG,
|
|
||||||
ir.OLEN,
|
|
||||||
ir.OMAKE,
|
|
||||||
ir.ONEW,
|
|
||||||
ir.OPANIC,
|
|
||||||
ir.OPRINT,
|
|
||||||
ir.OPRINTN,
|
|
||||||
ir.OREAL,
|
|
||||||
ir.ORECOVER,
|
|
||||||
ir.ORECV,
|
|
||||||
ir.OUNSAFEADD,
|
|
||||||
ir.OUNSAFESLICE:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,6 +343,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node {
|
|||||||
return tcConv(n)
|
return tcConv(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RewriteNonNameCall(n)
|
||||||
typecheckargs(n)
|
typecheckargs(n)
|
||||||
t := l.Type()
|
t := l.Type()
|
||||||
if t == nil {
|
if t == nil {
|
||||||
|
@ -867,6 +867,42 @@ func typecheckargs(n ir.InitNode) {
|
|||||||
RewriteMultiValueCall(n, list[0])
|
RewriteMultiValueCall(n, list[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RewriteNonNameCall replaces non-Name call expressions with temps,
|
||||||
|
// rewriting f()(...) to t0 := f(); t0(...).
|
||||||
|
func RewriteNonNameCall(n *ir.CallExpr) {
|
||||||
|
np := &n.X
|
||||||
|
if inst, ok := (*np).(*ir.InstExpr); ok && inst.Op() == ir.OFUNCINST {
|
||||||
|
np = &inst.X
|
||||||
|
}
|
||||||
|
if dot, ok := (*np).(*ir.SelectorExpr); ok && (dot.Op() == ir.ODOTMETH || dot.Op() == ir.ODOTINTER || dot.Op() == ir.OMETHVALUE) {
|
||||||
|
np = &dot.X // peel away method selector
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for side effects in the callee expression.
|
||||||
|
// We explicitly special case new(T) though, because it doesn't have
|
||||||
|
// observable side effects, and keeping it in place allows better escape analysis.
|
||||||
|
if !ir.Any(*np, func(n ir.Node) bool { return n.Op() != ir.ONEW && callOrChan(n) }) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// See comment (1) in RewriteMultiValueCall.
|
||||||
|
static := ir.CurFunc == nil
|
||||||
|
if static {
|
||||||
|
ir.CurFunc = InitTodoFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp := Temp((*np).Type())
|
||||||
|
as := ir.NewAssignStmt(base.Pos, tmp, *np)
|
||||||
|
as.Def = true
|
||||||
|
*np = tmp
|
||||||
|
|
||||||
|
if static {
|
||||||
|
ir.CurFunc = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n.PtrInit().Append(Stmt(as))
|
||||||
|
}
|
||||||
|
|
||||||
// RewriteMultiValueCall rewrites multi-valued f() to use temporaries,
|
// RewriteMultiValueCall rewrites multi-valued f() to use temporaries,
|
||||||
// so the backend wouldn't need to worry about tuple-valued expressions.
|
// so the backend wouldn't need to worry about tuple-valued expressions.
|
||||||
func RewriteMultiValueCall(n ir.InitNode, call ir.Node) {
|
func RewriteMultiValueCall(n ir.InitNode, call ir.Node) {
|
||||||
@ -874,7 +910,7 @@ func RewriteMultiValueCall(n ir.InitNode, call ir.Node) {
|
|||||||
// be executed during the generated init function. However,
|
// be executed during the generated init function. However,
|
||||||
// init.go hasn't yet created it. Instead, associate the
|
// init.go hasn't yet created it. Instead, associate the
|
||||||
// temporary variables with InitTodoFunc for now, and init.go
|
// temporary variables with InitTodoFunc for now, and init.go
|
||||||
// will reassociate them later when it's appropriate.
|
// will reassociate them later when it's appropriate. (1)
|
||||||
static := ir.CurFunc == nil
|
static := ir.CurFunc == nil
|
||||||
if static {
|
if static {
|
||||||
ir.CurFunc = InitTodoFunc
|
ir.CurFunc = InitTodoFunc
|
||||||
|
105
test/fixedbugs/issue50672.go
Normal file
105
test/fixedbugs/issue50672.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2022 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 main
|
||||||
|
|
||||||
|
var ok = false
|
||||||
|
|
||||||
|
func f() func(int, int) int {
|
||||||
|
ok = true
|
||||||
|
return func(int, int) int { return 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
func g() (int, int) {
|
||||||
|
if !ok {
|
||||||
|
panic("FAIL")
|
||||||
|
}
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = f()(g())
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f1()
|
||||||
|
f2()
|
||||||
|
f3()
|
||||||
|
f4()
|
||||||
|
}
|
||||||
|
|
||||||
|
func f1() {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
f := func() func(int, int) {
|
||||||
|
ok = true
|
||||||
|
return func(int, int) {}
|
||||||
|
}
|
||||||
|
g := func() (int, int) {
|
||||||
|
if !ok {
|
||||||
|
panic("FAIL")
|
||||||
|
}
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
f()(g())
|
||||||
|
}
|
||||||
|
|
||||||
|
type S struct{}
|
||||||
|
|
||||||
|
func (S) f(int, int) {}
|
||||||
|
|
||||||
|
func f2() {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
f := func() S {
|
||||||
|
ok = true
|
||||||
|
return S{}
|
||||||
|
}
|
||||||
|
g := func() (int, int) {
|
||||||
|
if !ok {
|
||||||
|
panic("FAIL")
|
||||||
|
}
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
f().f(g())
|
||||||
|
}
|
||||||
|
|
||||||
|
func f3() {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
f := func() []func(int, int) {
|
||||||
|
ok = true
|
||||||
|
return []func(int, int){func(int, int) {}}
|
||||||
|
}
|
||||||
|
g := func() (int, int) {
|
||||||
|
if !ok {
|
||||||
|
panic("FAIL")
|
||||||
|
}
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
f()[0](g())
|
||||||
|
}
|
||||||
|
|
||||||
|
type G[T any] struct{}
|
||||||
|
|
||||||
|
func (G[T]) f(int, int) {}
|
||||||
|
|
||||||
|
func f4() {
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
f := func() G[int] {
|
||||||
|
ok = true
|
||||||
|
return G[int]{}
|
||||||
|
}
|
||||||
|
g := func() (int, int) {
|
||||||
|
if !ok {
|
||||||
|
panic("FAIL")
|
||||||
|
}
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
f().f(g())
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user