1
0
mirror of https://github.com/golang/go synced 2024-11-12 03:00:22 -07:00

Revert "cmd/compile: rewrite f(g()) for multi-value g() during typecheck"

This reverts commit d96b7fbf98.

Reason for revert: broke noopt and longtest builders.

Change-Id: Ifaec64d817c4336cb255a2e9db00526b7bc5606a
Reviewed-on: https://go-review.googlesource.com/c/164757
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Matthew Dempsky 2019-03-01 05:49:48 +00:00
parent 13d24b685a
commit 38642b9fce
13 changed files with 267 additions and 157 deletions

View File

@ -1604,6 +1604,13 @@ func (e *EscState) esccall(call *Node, parent *Node) {
} }
argList := call.List argList := call.List
if argList.Len() == 1 {
arg := argList.First()
if arg.Type.IsFuncArgStruct() { // f(g())
argList = e.nodeEscState(arg).Retval
}
}
args := argList.Slice() args := argList.Slice()
if indirect { if indirect {

View File

@ -1404,11 +1404,14 @@ func (n *Node) exprfmt(s fmt.State, prec int, mode fmtMode) {
} }
mode.Fprintf(s, "sliceheader{%v,%v,%v}", n.Left, n.List.First(), n.List.Second()) mode.Fprintf(s, "sliceheader{%v,%v,%v}", n.Left, n.List.First(), n.List.Second())
case OCOMPLEX, OCOPY: case OCOPY:
if n.Left != nil { mode.Fprintf(s, "%#v(%v, %v)", n.Op, n.Left, n.Right)
mode.Fprintf(s, "%#v(%v, %v)", n.Op, n.Left, n.Right)
case OCOMPLEX:
if n.List.Len() == 1 {
mode.Fprintf(s, "%#v(%v)", n.Op, n.List.First())
} else { } else {
mode.Fprintf(s, "%#v(%.v)", n.Op, n.List) mode.Fprintf(s, "%#v(%v, %v)", n.Op, n.Left, n.Right)
} }
case OCONV, case OCONV,
@ -1537,8 +1540,6 @@ func (n *Node) nodefmt(s fmt.State, flag FmtFlag, mode fmtMode) {
if flag&FmtLong != 0 && t != nil { if flag&FmtLong != 0 && t != nil {
if t.Etype == TNIL { if t.Etype == TNIL {
fmt.Fprint(s, "nil") fmt.Fprint(s, "nil")
} else if n.Op == ONAME && n.Name.AutoTemp() {
mode.Fprintf(s, "%v value", t)
} else { } else {
mode.Fprintf(s, "%v (type %v)", n, t) mode.Fprintf(s, "%v (type %v)", n, t)
} }

View File

@ -1387,8 +1387,7 @@ func (w *exportWriter) localIdent(s *types.Sym, v int32) {
return return
} }
// TODO(mdempsky): Fix autotmp hack. if i := strings.LastIndex(name, "."); i >= 0 {
if i := strings.LastIndex(name, "."); i >= 0 && !strings.HasPrefix(name, ".autotmp_") {
Fatalf("unexpected dot in identifier: %v", name) Fatalf("unexpected dot in identifier: %v", name)
} }

View File

@ -14,9 +14,6 @@ import (
// the name, normally "pkg.init", is altered to "pkg.init.0". // the name, normally "pkg.init", is altered to "pkg.init.0".
var renameinitgen int var renameinitgen int
// Dummy function for autotmps generated during typechecking.
var dummyInitFn = nod(ODCLFUNC, nil, nil)
func renameinit() *types.Sym { func renameinit() *types.Sym {
s := lookupN("init.", renameinitgen) s := lookupN("init.", renameinitgen)
renameinitgen++ renameinitgen++
@ -117,12 +114,6 @@ func fninit(n []*Node) {
initsym := lookup("init") initsym := lookup("init")
fn := dclfunc(initsym, nod(OTFUNC, nil, nil)) fn := dclfunc(initsym, nod(OTFUNC, nil, nil))
for _, dcl := range dummyInitFn.Func.Dcl {
dcl.Name.Curfn = fn
}
fn.Func.Dcl = append(fn.Func.Dcl, dummyInitFn.Func.Dcl...)
dummyInitFn = nil
// (3) // (3)
a := nod(OIF, nil, nil) a := nod(OIF, nil, nil)
a.Left = nod(OGT, gatevar, nodintconst(1)) a.Left = nod(OGT, gatevar, nodintconst(1))

View File

@ -589,13 +589,24 @@ func inlnode(n *Node, maxCost int32) *Node {
} }
inlnodelist(n.List, maxCost) inlnodelist(n.List, maxCost)
if n.Op == OBLOCK { switch n.Op {
case OBLOCK:
for _, n2 := range n.List.Slice() { for _, n2 := range n.List.Slice() {
if n2.Op == OINLCALL { if n2.Op == OINLCALL {
inlconv2stmt(n2) inlconv2stmt(n2)
} }
} }
} else {
case ORETURN, OCALLFUNC, OCALLMETH, OCALLINTER, OAPPEND, OCOMPLEX:
// if we just replaced arg in f(arg()) or return arg with an inlined call
// and arg returns multiple values, glue as list
if n.List.Len() == 1 && n.List.First().Op == OINLCALL && n.List.First().Rlist.Len() > 1 {
n.List.Set(inlconv2list(n.List.First()))
break
}
fallthrough
default:
s := n.List.Slice() s := n.List.Slice()
for i1, n1 := range s { for i1, n1 := range s {
if n1 != nil && n1.Op == OINLCALL { if n1 != nil && n1.Op == OINLCALL {
@ -1005,6 +1016,9 @@ func mkinlcall(n, fn *Node, maxCost int32) *Node {
// to pass as a slice. // to pass as a slice.
numvals := n.List.Len() numvals := n.List.Len()
if numvals == 1 && n.List.First().Type.IsFuncArgStruct() {
numvals = n.List.First().Type.NumFields()
}
x := as.List.Len() x := as.List.Len()
for as.List.Len() < numvals { for as.List.Len() < numvals {

View File

@ -380,12 +380,66 @@ func (o *Order) init(n *Node) {
n.Ninit.Set(nil) n.Ninit.Set(nil)
} }
// Ismulticall reports whether the list l is f() for a multi-value function.
// Such an f() could appear as the lone argument to a multi-arg function.
func ismulticall(l Nodes) bool {
// one arg only
if l.Len() != 1 {
return false
}
n := l.First()
// must be call
switch n.Op {
default:
return false
case OCALLFUNC, OCALLMETH, OCALLINTER:
// call must return multiple values
return n.Left.Type.NumResults() > 1
}
}
// copyRet emits t1, t2, ... = n, where n is a function call,
// and then returns the list t1, t2, ....
func (o *Order) copyRet(n *Node) []*Node {
if !n.Type.IsFuncArgStruct() {
Fatalf("copyret %v %d", n.Type, n.Left.Type.NumResults())
}
slice := n.Type.Fields().Slice()
l1 := make([]*Node, len(slice))
l2 := make([]*Node, len(slice))
for i, t := range slice {
tmp := temp(t.Type)
l1[i] = tmp
l2[i] = tmp
}
as := nod(OAS2, nil, nil)
as.List.Set(l1)
as.Rlist.Set1(n)
as = typecheck(as, ctxStmt)
o.stmt(as)
return l2
}
// callArgs orders the list of call arguments *l.
func (o *Order) callArgs(l *Nodes) {
if ismulticall(*l) {
// return f() where f() is multiple values.
l.Set(o.copyRet(l.First()))
} else {
o.exprList(*l)
}
}
// call orders the call expression n. // call orders the call expression n.
// n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY. // n.Op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
func (o *Order) call(n *Node) { func (o *Order) call(n *Node) {
n.Left = o.expr(n.Left, nil) n.Left = o.expr(n.Left, nil)
n.Right = o.expr(n.Right, nil) // ODDDARG temp n.Right = o.expr(n.Right, nil) // ODDDARG temp
o.exprList(n.List) o.callArgs(&n.List)
if n.Op != OCALLFUNC { if n.Op != OCALLFUNC {
return return
@ -757,7 +811,7 @@ func (o *Order) stmt(n *Node) {
o.cleanTemp(t) o.cleanTemp(t)
case ORETURN: case ORETURN:
o.exprList(n.List) o.callArgs(&n.List)
o.out = append(o.out, n) o.out = append(o.out, n)
// Special: clean case temporaries in each block entry. // Special: clean case temporaries in each block entry.
@ -1120,7 +1174,7 @@ func (o *Order) expr(n, lhs *Node) *Node {
n.List.SetFirst(o.expr(n.List.First(), nil)) // order x n.List.SetFirst(o.expr(n.List.First(), nil)) // order x
n.List.Second().Left = o.expr(n.List.Second().Left, nil) // order y n.List.Second().Left = o.expr(n.List.Second().Left, nil) // order y
} else { } else {
o.exprList(n.List) o.callArgs(&n.List)
} }
if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.List.First()) { if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.List.First()) {

View File

@ -1318,7 +1318,11 @@ func typecheck1(n *Node, top int) (res *Node) {
return n return n
} }
typecheckargs(n) if n.List.Len() == 1 && !n.IsDDD() {
n.List.SetFirst(typecheck(n.List.First(), ctxExpr|ctxMultiOK))
} else {
typecheckslice(n.List.Slice(), ctxExpr)
}
t := l.Type t := l.Type
if t == nil { if t == nil {
n.Type = nil n.Type = nil
@ -1512,24 +1516,51 @@ func typecheck1(n *Node, top int) (res *Node) {
case OCOMPLEX: case OCOMPLEX:
ok |= ctxExpr ok |= ctxExpr
typecheckargs(n) var r *Node
if !twoarg(n) { var l *Node
n.Type = nil if n.List.Len() == 1 {
return n typecheckslice(n.List.Slice(), ctxMultiOK)
if n.List.First().Op != OCALLFUNC && n.List.First().Op != OCALLMETH {
yyerror("invalid operation: complex expects two arguments")
n.Type = nil
return n
}
t := n.List.First().Left.Type
if !t.IsKind(TFUNC) {
// Bail. This error will be reported elsewhere.
return n
}
if t.NumResults() != 2 {
yyerror("invalid operation: complex expects two arguments, %v returns %d results", n.List.First(), t.NumResults())
n.Type = nil
return n
}
t = n.List.First().Type
l = asNode(t.Field(0).Nname)
r = asNode(t.Field(1).Nname)
} else {
if !twoarg(n) {
n.Type = nil
return n
}
n.Left = typecheck(n.Left, ctxExpr)
n.Right = typecheck(n.Right, ctxExpr)
l = n.Left
r = n.Right
if l.Type == nil || r.Type == nil {
n.Type = nil
return n
}
l, r = defaultlit2(l, r, false)
if l.Type == nil || r.Type == nil {
n.Type = nil
return n
}
n.Left = l
n.Right = r
} }
l := n.Left
r := n.Right
if l.Type == nil || r.Type == nil {
n.Type = nil
return n
}
l, r = defaultlit2(l, r, false)
if l.Type == nil || r.Type == nil {
n.Type = nil
return n
}
n.Left = l
n.Right = r
if !types.Identical(l.Type, r.Type) { if !types.Identical(l.Type, r.Type) {
yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type) yyerror("invalid operation: %v (mismatched types %v and %v)", n, l.Type, r.Type)
@ -1591,8 +1622,6 @@ func typecheck1(n *Node, top int) (res *Node) {
ok |= ctxStmt ok |= ctxStmt
case ODELETE: case ODELETE:
ok |= ctxStmt
typecheckargs(n)
args := n.List args := n.List
if args.Len() == 0 { if args.Len() == 0 {
yyerror("missing arguments to delete") yyerror("missing arguments to delete")
@ -1612,6 +1641,8 @@ func typecheck1(n *Node, top int) (res *Node) {
return n return n
} }
ok |= ctxStmt
typecheckslice(args.Slice(), ctxExpr)
l := args.First() l := args.First()
r := args.Second() r := args.Second()
if l.Type != nil && !l.Type.IsMap() { if l.Type != nil && !l.Type.IsMap() {
@ -1624,7 +1655,6 @@ func typecheck1(n *Node, top int) (res *Node) {
case OAPPEND: case OAPPEND:
ok |= ctxExpr ok |= ctxExpr
typecheckargs(n)
args := n.List args := n.List
if args.Len() == 0 { if args.Len() == 0 {
yyerror("missing arguments to append") yyerror("missing arguments to append")
@ -1632,12 +1662,25 @@ func typecheck1(n *Node, top int) (res *Node) {
return n return n
} }
if args.Len() == 1 && !n.IsDDD() {
args.SetFirst(typecheck(args.First(), ctxExpr|ctxMultiOK))
} else {
typecheckslice(args.Slice(), ctxExpr)
}
t := args.First().Type t := args.First().Type
if t == nil { if t == nil {
n.Type = nil n.Type = nil
return n return n
} }
// Unpack multiple-return result before type-checking.
var funarg *types.Type
if t.IsFuncArgStruct() {
funarg = t
t = t.Field(0).Type
}
n.Type = t n.Type = t
if !t.IsSlice() { if !t.IsSlice() {
if Isconst(args.First(), CTNIL) { if Isconst(args.First(), CTNIL) {
@ -1673,23 +1716,44 @@ func typecheck1(n *Node, top int) (res *Node) {
break break
} }
as := args.Slice()[1:] if funarg != nil {
for i, n := range as { for _, t := range funarg.FieldSlice()[1:] {
if n.Type == nil { if assignop(t.Type, n.Type.Elem(), nil) == 0 {
continue yyerror("cannot append %v value to []%v", t.Type, n.Type.Elem())
}
}
} else {
as := args.Slice()[1:]
for i, n := range as {
if n.Type == nil {
continue
}
as[i] = assignconv(n, t.Elem(), "append")
checkwidth(as[i].Type) // ensure width is calculated for backend
} }
as[i] = assignconv(n, t.Elem(), "append")
checkwidth(as[i].Type) // ensure width is calculated for backend
} }
case OCOPY: case OCOPY:
ok |= ctxStmt | ctxExpr ok |= ctxStmt | ctxExpr
typecheckargs(n) args := n.List
if !twoarg(n) { if args.Len() < 2 {
yyerror("missing arguments to copy")
n.Type = nil n.Type = nil
return n return n
} }
if args.Len() > 2 {
yyerror("too many arguments to copy")
n.Type = nil
return n
}
n.Left = args.First()
n.Right = args.Second()
n.List.Set(nil)
n.Type = types.Types[TINT] n.Type = types.Types[TINT]
n.Left = typecheck(n.Left, ctxExpr)
n.Right = typecheck(n.Right, ctxExpr)
if n.Left.Type == nil || n.Right.Type == nil { if n.Left.Type == nil || n.Right.Type == nil {
n.Type = nil n.Type = nil
return n return n
@ -2085,7 +2149,11 @@ func typecheck1(n *Node, top int) (res *Node) {
case ORETURN: case ORETURN:
ok |= ctxStmt ok |= ctxStmt
typecheckargs(n) if n.List.Len() == 1 {
typecheckslice(n.List.Slice(), ctxExpr|ctxMultiOK)
} else {
typecheckslice(n.List.Slice(), ctxExpr)
}
if Curfn == nil { if Curfn == nil {
yyerror("return outside function") yyerror("return outside function")
n.Type = nil n.Type = nil
@ -2189,51 +2257,6 @@ func typecheck1(n *Node, top int) (res *Node) {
return n return n
} }
func typecheckargs(n *Node) {
if n.List.Len() != 1 || n.IsDDD() {
typecheckslice(n.List.Slice(), ctxExpr)
return
}
typecheckslice(n.List.Slice(), ctxExpr|ctxMultiOK)
t := n.List.First().Type
if t == nil || !t.IsFuncArgStruct() {
return
}
// Rewrite f(g()) into t1, t2, ... = g(); f(t1, t2, ...).
// Save n as n.Orig for fmt.go.
if n.Orig == n {
n.Orig = n.sepcopy()
}
as := nod(OAS2, nil, nil)
as.Rlist.AppendNodes(&n.List)
// If we're outside of function context, then this call will
// be executed during the generated init function. However,
// init.go hasn't yet created it. Instead, associate the
// temporary variables with dummyInitFn for now, and init.go
// will reassociate them later when it's appropriate.
static := Curfn == nil
if static {
Curfn = dummyInitFn
}
for _, f := range t.FieldSlice() {
t := temp(f.Type)
as.Ninit.Append(nod(ODCL, t, nil))
as.List.Append(t)
n.List.Append(t)
}
if static {
Curfn = nil
}
as = typecheck(as, ctxStmt)
n.Ninit.Append(as)
}
func checksliceindex(l *Node, r *Node, tp *types.Type) bool { func checksliceindex(l *Node, r *Node, tp *types.Type) bool {
t := r.Type t := r.Type
if t == nil { if t == nil {
@ -2373,15 +2396,24 @@ func twoarg(n *Node) bool {
if n.Left != nil { if n.Left != nil {
return true return true
} }
if n.List.Len() != 2 { if n.List.Len() == 0 {
if n.List.Len() < 2 { yyerror("missing argument to %v - %v", n.Op, n)
yyerror("not enough arguments in call to %v", n)
} else {
yyerror("too many arguments in call to %v", n)
}
return false return false
} }
n.Left = n.List.First() n.Left = n.List.First()
if n.List.Len() == 1 {
yyerror("missing argument to %v - %v", n.Op, n)
n.List.Set(nil)
return false
}
if n.List.Len() > 2 {
yyerror("too many arguments to %v - %v", n.Op, n)
n.List.Set(nil)
return false
}
n.Right = n.List.Second() n.Right = n.List.Second()
n.List.Set(nil) n.List.Set(nil)
return true return true
@ -2641,6 +2673,8 @@ func hasddd(t *types.Type) bool {
// typecheck assignment: type list = expression list // typecheck assignment: type list = expression list
func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes, desc func() string) { func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes, desc func() string) {
var t *types.Type var t *types.Type
var n1 int
var n2 int
var i int var i int
lno := lineno lno := lineno
@ -2653,10 +2687,57 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes,
var n *Node var n *Node
if nl.Len() == 1 { if nl.Len() == 1 {
n = nl.First() n = nl.First()
if n.Type != nil && n.Type.IsFuncArgStruct() {
if !hasddd(tstruct) {
n1 := tstruct.NumFields()
n2 := n.Type.NumFields()
if n2 > n1 {
goto toomany
}
if n2 < n1 {
goto notenough
}
}
lfs := tstruct.FieldSlice()
rfs := n.Type.FieldSlice()
var why string
for i, tl := range lfs {
if tl.IsDDD() {
for _, tn := range rfs[i:] {
if assignop(tn.Type, tl.Type.Elem(), &why) == 0 {
if call != nil {
yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type.Elem(), call, why)
} else {
yyerror("cannot use %v as type %v in %s%s", tn.Type, tl.Type.Elem(), desc(), why)
}
}
}
return
}
if i >= len(rfs) {
goto notenough
}
tn := rfs[i]
if assignop(tn.Type, tl.Type, &why) == 0 {
if call != nil {
yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type, call, why)
} else {
yyerror("cannot use %v as type %v in %s%s", tn.Type, tl.Type, desc(), why)
}
}
}
if len(rfs) > len(lfs) {
goto toomany
}
return
}
} }
n1 := tstruct.NumFields() n1 = tstruct.NumFields()
n2 := nl.Len() n2 = nl.Len()
if !hasddd(tstruct) { if !hasddd(tstruct) {
if n2 > n1 { if n2 > n1 {
goto toomany goto toomany
@ -2698,7 +2779,6 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *types.Type, nl Nodes,
return return
} }
// TODO(mdempsky): Make into ... call with implicit slice.
for ; i < nl.Len(); i++ { for ; i < nl.Len(); i++ {
n = nl.Index(i) n = nl.Index(i)
setlineno(n) setlineno(n)
@ -2806,8 +2886,14 @@ func (nl Nodes) retsigerr(isddd bool) string {
} }
var typeStrings []string var typeStrings []string
for _, n := range nl.Slice() { if nl.Len() == 1 && nl.First().Type != nil && nl.First().Type.IsFuncArgStruct() {
typeStrings = append(typeStrings, sigrepr(n.Type)) for _, f := range nl.First().Type.Fields().Slice() {
typeStrings = append(typeStrings, sigrepr(f.Type))
}
} else {
for _, n := range nl.Slice() {
typeStrings = append(typeStrings, sigrepr(n.Type))
}
} }
ddd := "" ddd := ""

View File

@ -49,10 +49,10 @@ func main() {
_ = complex(f64, F64) // ERROR "complex" _ = complex(f64, F64) // ERROR "complex"
_ = complex(F64, f64) // ERROR "complex" _ = complex(F64, f64) // ERROR "complex"
_ = complex(F1()) // ERROR "not enough arguments" _ = complex(F1()) // ERROR "expects two arguments.*returns 1"
_ = complex(F3()) // ERROR "too many arguments" _ = complex(F3()) // ERROR "expects two arguments.*returns 3"
_ = complex() // ERROR "not enough arguments" _ = complex() // ERROR "missing argument"
c128 = complex(f32, f32) // ERROR "cannot use" c128 = complex(f32, f32) // ERROR "cannot use"
c64 = complex(f64, f64) // ERROR "cannot use" c64 = complex(f64, f64) // ERROR "cannot use"

View File

@ -14,7 +14,7 @@ func main() {
si := make([]int, 8) si := make([]int, 8)
sf := make([]float64, 8) sf := make([]float64, 8)
_ = copy() // ERROR "not enough arguments" _ = copy() // ERROR "missing arguments"
_ = copy(1, 2, 3) // ERROR "too many arguments" _ = copy(1, 2, 3) // ERROR "too many arguments"
_ = copy(si, "hi") // ERROR "have different element types.*int.*string" _ = copy(si, "hi") // ERROR "have different element types.*int.*string"

View File

@ -1,38 +0,0 @@
// run
// Copyright 2018 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
import (
"fmt"
)
func f(a []byte) ([]byte, []byte) {
return a, []byte("abc")
}
func g(a []byte) ([]byte, string) {
return a, "abc"
}
func h(m map[int]int) (map[int]int, int) {
return m, 0
}
func main() {
a := []byte{1, 2, 3}
n := copy(f(a))
fmt.Println(n, a)
b := []byte{1, 2, 3}
n = copy(f(b))
fmt.Println(n, b)
m := map[int]int{0: 0}
fmt.Println(len(m))
delete(h(m))
fmt.Println(len(m))
}

View File

@ -1,4 +0,0 @@
3 [97 98 99]
3 [97 98 99]
1
0

View File

@ -6,4 +6,4 @@
package main package main
const A = complex(0()) // ERROR "cannot call non-function" "const initializer .* is not a constant" "not enough arguments" const A = complex(0()) // ERROR "cannot call non-function" "const initializer .* is not a constant"

View File

@ -13,6 +13,6 @@ func f() (_, _ []int) { return }
func g() (x []int, y float64) { return } func g() (x []int, y float64) { return }
func main() { func main() {
_ = append(f()) // ERROR "cannot use \[\]int value as type int in append" _ = append(f()) // ERROR "cannot append \[\]int value to \[\]int"
_ = append(g()) // ERROR "cannot use float64 value as type int in append" _ = append(g()) // ERROR "cannot append float64 value to \[\]int"
} }