mirror of
https://github.com/golang/go
synced 2024-11-23 20:00:04 -07:00
6113db0bb4
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>
191 lines
4.9 KiB
Go
191 lines
4.9 KiB
Go
// errorcheck -0 -m -l
|
|
|
|
// Copyright 2015 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.
|
|
|
|
// Test, using compiler diagnostic flags, that the escape analysis is working.
|
|
// Compiles but does not run. Inlining is disabled.
|
|
// Registerization is disabled too (-N), which should
|
|
// have no effect on escape analysis.
|
|
|
|
package main
|
|
|
|
import "fmt"
|
|
|
|
func main() {
|
|
// Just run test over and over again. This main func is just for
|
|
// convenience; if test were the main func, we could also trigger
|
|
// the panic just by running the program over and over again
|
|
// (sometimes it takes 1 time, sometimes it takes ~4,000+).
|
|
for iter := 0; ; iter++ {
|
|
if iter%50 == 0 {
|
|
fmt.Println(iter) // ERROR "iter escapes to heap$" "... argument does not escape$"
|
|
}
|
|
test1(iter)
|
|
test2(iter)
|
|
test3(iter)
|
|
test4(iter)
|
|
test5(iter)
|
|
test6(iter)
|
|
}
|
|
}
|
|
|
|
func test1(iter int) {
|
|
|
|
const maxI = 500
|
|
m := make(map[int][]int) // ERROR "make\(map\[int\]\[\]int\) escapes to heap$"
|
|
|
|
// The panic seems to be triggered when m is modified inside a
|
|
// closure that is both recursively called and reassigned to in a
|
|
// loop.
|
|
|
|
// Cause of bug -- escape of closure failed to escape (shared) data structures
|
|
// of map. Assign to fn declared outside of loop triggers escape of closure.
|
|
// Heap -> stack pointer eventually causes badness when stack reallocation
|
|
// occurs.
|
|
|
|
var fn func() // ERROR "moved to heap: fn$"
|
|
for i := 0; i < maxI; i++ { // ERROR "moved to heap: i$"
|
|
// var fn func() // this makes it work, because fn stays off heap
|
|
j := 0 // ERROR "moved to heap: j$"
|
|
fn = func() { // ERROR "func literal escapes to heap$"
|
|
m[i] = append(m[i], 0)
|
|
if j < 25 {
|
|
j++
|
|
fn()
|
|
}
|
|
}
|
|
fn()
|
|
}
|
|
|
|
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$" "fmt.Sprintf\(.*\) escapes to heap"
|
|
}
|
|
}
|
|
|
|
func test2(iter int) {
|
|
|
|
const maxI = 500
|
|
m := make(map[int][]int) // ERROR "make\(map\[int\]\[\]int\) does not escape$"
|
|
|
|
// var fn func()
|
|
for i := 0; i < maxI; i++ {
|
|
var fn func() // this makes it work, because fn stays off heap
|
|
j := 0
|
|
fn = func() { // ERROR "func literal does not escape$"
|
|
m[i] = append(m[i], 0)
|
|
if j < 25 {
|
|
j++
|
|
fn()
|
|
}
|
|
}
|
|
fn()
|
|
}
|
|
|
|
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$" "fmt.Sprintf\(.*\) escapes to heap"
|
|
}
|
|
}
|
|
|
|
func test3(iter int) {
|
|
|
|
const maxI = 500
|
|
var x int // ERROR "moved to heap: x$"
|
|
m := &x
|
|
|
|
var fn func() // ERROR "moved to heap: fn$"
|
|
for i := 0; i < maxI; i++ {
|
|
// var fn func() // this makes it work, because fn stays off heap
|
|
j := 0 // ERROR "moved to heap: j$"
|
|
fn = func() { // ERROR "func literal escapes to heap$"
|
|
if j < 100 {
|
|
j++
|
|
fn()
|
|
} else {
|
|
*m = *m + 1
|
|
}
|
|
}
|
|
fn()
|
|
}
|
|
|
|
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$" "fmt.Sprintf\(.*\) escapes to heap"
|
|
}
|
|
}
|
|
|
|
func test4(iter int) {
|
|
|
|
const maxI = 500
|
|
var x int
|
|
m := &x
|
|
|
|
// var fn func()
|
|
for i := 0; i < maxI; i++ {
|
|
var fn func() // this makes it work, because fn stays off heap
|
|
j := 0
|
|
fn = func() { // ERROR "func literal does not escape$"
|
|
if j < 100 {
|
|
j++
|
|
fn()
|
|
} else {
|
|
*m = *m + 1
|
|
}
|
|
}
|
|
fn()
|
|
}
|
|
|
|
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$" "fmt.Sprintf\(.*\) escapes to heap"
|
|
}
|
|
}
|
|
|
|
type str struct {
|
|
m *int
|
|
}
|
|
|
|
func recur1(j int, s *str) { // ERROR "s does not escape"
|
|
if j < 100 {
|
|
j++
|
|
recur1(j, s)
|
|
} else {
|
|
*s.m++
|
|
}
|
|
}
|
|
|
|
func test5(iter int) {
|
|
|
|
const maxI = 500
|
|
var x int // ERROR "moved to heap: x$"
|
|
m := &x
|
|
|
|
var fn *str
|
|
for i := 0; i < maxI; i++ {
|
|
// var fn *str // this makes it work, because fn stays off heap
|
|
fn = &str{m} // ERROR "&str{...} escapes to heap"
|
|
recur1(0, fn)
|
|
}
|
|
|
|
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$" "fmt.Sprintf\(.*\) escapes to heap"
|
|
}
|
|
}
|
|
|
|
func test6(iter int) {
|
|
|
|
const maxI = 500
|
|
var x int
|
|
m := &x
|
|
|
|
// var fn *str
|
|
for i := 0; i < maxI; i++ {
|
|
var fn *str // this makes it work, because fn stays off heap
|
|
fn = &str{m} // ERROR "&str{...} does not escape"
|
|
recur1(0, fn)
|
|
}
|
|
|
|
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$" "fmt.Sprintf\(.*\) escapes to heap"
|
|
}
|
|
}
|