]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.regabi] cmd/compile: convert OPANIC argument to interface{} during typecheck
authorMatthew Dempsky <mdempsky@google.com>
Mon, 18 Jan 2021 00:14:48 +0000 (16:14 -0800)
committerMatthew Dempsky <mdempsky@google.com>
Mon, 18 Jan 2021 05:55:08 +0000 (05:55 +0000)
Currently, typecheck leaves arguments to OPANIC as their original
type. This CL changes it to insert implicit OCONVIFACE operations to
convert arguments to `interface{}` like how any other function call
would be handled.

No immediate benefits, other than getting to remove a tiny bit of
special-case logic in order.go's handling of OPANICs. Instead, the
generic code path for handling OCONVIFACE is used, if necessary.
Longer term, this should be marginally helpful for #43753, as it
reduces the number of cases where we need values to be addressable for
runtime calls.

However, this does require adding some hacks to appease existing
tests:

1. We need yet another kludge in inline budgeting, to ensure that
reflect.flag.mustBe stays inlinable for cmd/compile/internal/test's
TestIntendedInlining.

2. Since the OCONVIFACE expressions are now being introduced during
typecheck, they're now visible to escape analysis. So expressions like
"panic(1)" are now seen as "panic(interface{}(1))", and escape
analysis warns that the "interface{}(1)" escapes to the heap. These
have always escaped to heap, just now we're accurately reporting about
it.

(Also, unfortunately fmt.go hides implicit conversions by default in
diagnostics messages, so instead of reporting "interface{}(1) escapes
to heap", it actually reports "1 escapes to heap", which is
confusing. However, this confusing messaging also isn't new.)

Change-Id: Icedf60e1d2e464e219441b8d1233a313770272af
Reviewed-on: https://go-review.googlesource.com/c/go/+/284412
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>

src/cmd/compile/internal/inline/inl.go
src/cmd/compile/internal/typecheck/func.go
src/cmd/compile/internal/walk/order.go
test/closure3.dir/main.go
test/escape2.go
test/escape2n.go
test/escape4.go
test/fixedbugs/issue13799.go
test/fixedbugs/issue7921.go

index 143fbe9efe1b9e68b6d32703987fa383d7821b27..aa194ebab2b6e1897209a94aabfeb8e3891cd628 100644 (file)
@@ -346,6 +346,13 @@ func (v *hairyVisitor) doNode(n ir.Node) error {
                v.budget -= v.extraCallCost
 
        case ir.OPANIC:
+               n := n.(*ir.UnaryExpr)
+               if n.X.Op() == ir.OCONVIFACE && n.X.(*ir.ConvExpr).Implicit() {
+                       // Hack to keep reflect.flag.mustBe inlinable for TestIntendedInlining.
+                       // Before CL 284412, these conversions were introduced later in the
+                       // compiler, so they didn't count against inlining budget.
+                       v.budget++
+               }
                v.budget -= inlineExtraPanicCost
 
        case ir.ORECOVER:
index c832d9700f37057caf1a165e13310db885e2be29..b576590d4d92b1081153b2b1edd9e9e4fe1b88cc 100644 (file)
@@ -896,7 +896,7 @@ func tcNew(n *ir.UnaryExpr) ir.Node {
 // tcPanic typechecks an OPANIC node.
 func tcPanic(n *ir.UnaryExpr) ir.Node {
        n.X = Expr(n.X)
-       n.X = DefaultLit(n.X, types.Types[types.TINTER])
+       n.X = AssignConv(n.X, types.Types[types.TINTER], "argument to panic")
        if n.X.Type() == nil {
                n.SetType(nil)
                return n
index e1e9f168bbc4b88fa1f6988a67a2b1ca644e2f3b..fe0b6a0eff4ec9ff15b28840f5c033a4efd2daca 100644 (file)
@@ -768,14 +768,12 @@ func (o *orderState) stmt(n ir.Node) {
                orderBlock(&n.Else, o.free)
                o.out = append(o.out, n)
 
-       // Special: argument will be converted to interface using convT2E
-       // so make sure it is an addressable temporary.
        case ir.OPANIC:
                n := n.(*ir.UnaryExpr)
                t := o.markTemp()
                n.X = o.expr(n.X, nil)
-               if !n.X.Type().IsInterface() {
-                       n.X = o.addrTemp(n.X)
+               if !n.X.Type().IsEmptyInterface() {
+                       base.FatalfAt(n.Pos(), "bad argument to panic: %L", n.X)
                }
                o.out = append(o.out, n)
                o.cleanTemp(t)
index 5694673f1ec64bece849d7473362fb4296f03ff7..e8e1e99860f5c1ae28b273ad448b74eca6bde713 100644 (file)
@@ -285,5 +285,5 @@ func main() {
 
 //go:noinline
 func ppanic(s string) { // ERROR "leaking param: s"
-       panic(s)
+       panic(s) // ERROR "s escapes to heap"
 }
index 5c6eb559faf0667b37433d7d6db1bce091439892..b9b723d8666862195d9bc090eda8608ba0bf607e 100644 (file)
@@ -1547,7 +1547,7 @@ func foo153(v interface{}) *int { // ERROR "v does not escape"
        case int: // ERROR "moved to heap: x$"
                return &x
        }
-       panic(0)
+       panic(0) // ERROR "0 escapes to heap"
 }
 
 // issue 8185 - &result escaping into result
index 46e58f85661a7ab6e25a019c11d15a64c9da1c79..7c8208aa73ca11afb097cd1a0fb9e68922dc8d89 100644 (file)
@@ -1547,7 +1547,7 @@ func foo153(v interface{}) *int { // ERROR "v does not escape"
        case int: // ERROR "moved to heap: x$"
                return &x
        }
-       panic(0)
+       panic(0) // ERROR "0 escapes to heap"
 }
 
 // issue 8185 - &result escaping into result
index a4a9c14a3e0cc211eff199b93d131b8eddb28469..4e50231bf959b1a36534ba6db412da28ac0ef392 100644 (file)
@@ -35,14 +35,14 @@ func f1() {
 func f2() {} // ERROR "can inline f2"
 
 // No inline for recover; panic now allowed to inline.
-func f3() { panic(1) } // ERROR "can inline f3"
+func f3() { panic(1) } // ERROR "can inline f3" "1 escapes to heap"
 func f4() { recover() }
 
 func f5() *byte {
        type T struct {
                x [1]byte
        }
-       t := new(T)    // ERROR "new.T. escapes to heap"
+       t := new(T) // ERROR "new.T. escapes to heap"
        return &t.x[0]
 }
 
@@ -52,6 +52,6 @@ func f6() *byte {
                        y byte
                }
        }
-       t := new(T)   // ERROR "new.T. escapes to heap"
+       t := new(T) // ERROR "new.T. escapes to heap"
        return &t.x.y
 }
index fbdd4c32bc85a85fd9aa405962d36810776988ec..c8ecfc54e4464c3b01bc44b562d99cb4d720f2be 100644 (file)
@@ -60,7 +60,7 @@ func test1(iter int) {
        }
 
        if len(m) != maxI {
-               panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
+               panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap"
        }
 }
 
@@ -84,7 +84,7 @@ func test2(iter int) {
        }
 
        if len(m) != maxI {
-               panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
+               panic(fmt.Sprintf("iter %d: maxI = %d, len(m) = %d", iter, maxI, len(m))) // ERROR "iter escapes to heap$" "len\(m\) escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap"
        }
 }
 
@@ -110,7 +110,7 @@ func test3(iter int) {
        }
 
        if *m != maxI {
-               panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
+               panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap"
        }
 }
 
@@ -136,7 +136,7 @@ func test4(iter int) {
        }
 
        if *m != maxI {
-               panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
+               panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap"
        }
 }
 
@@ -167,7 +167,7 @@ func test5(iter int) {
        }
 
        if *m != maxI {
-               panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
+               panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap"
        }
 }
 
@@ -185,6 +185,6 @@ func test6(iter int) {
        }
 
        if *m != maxI {
-               panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$"
+               panic(fmt.Sprintf("iter %d: maxI = %d, *m = %d", iter, maxI, *m)) // ERROR "\*m escapes to heap$" "iter escapes to heap$" "maxI escapes to heap$" "... argument does not escape$" "fmt.Sprintf\(.*\) escapes to heap"
        }
 }
index a4e7b246d491130f52068d444e0e9d0c8dfdcbe1..65be4b5bbee8d1e73443fbfb6859d76655fb903d 100644 (file)
@@ -41,7 +41,7 @@ func bufferNoEscape3(xs []string) string { // ERROR "xs does not escape$"
 
 func bufferNoEscape4() []byte {
        var b bytes.Buffer
-       b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m\]$" "inlining call to bytes.\(\*Buffer\).Grow$"
+       b.Grow(64) // ERROR "bufferNoEscape4 ignoring self-assignment in bytes.b.buf = bytes.b.buf\[:bytes.m\]$" "inlining call to bytes.\(\*Buffer\).Grow$" "string\(.*\) escapes to heap"
        useBuffer(&b)
        return b.Bytes() // ERROR "inlining call to bytes.\(\*Buffer\).Bytes$"
 }