mirror of
https://github.com/golang/go
synced 2024-11-13 18:10:24 -07:00
abefcac10a
For most nodes (e.g., OPTRLIT, OMAKESLICE, OCONVIFACE), escape analysis prints "escapes to heap" or "does not escape" to indicate whether that node's allocation can be heap or stack allocated. These messages are also emitted for OADDR, even though OADDR does not actually allocate anything itself. Moreover, it's redundant because escape analysis already prints "moved to heap" diagnostics when an OADDR node like "&x" causes x to require heap allocation. Because OADDR nodes don't allocate memory, my escape analysis rewrite doesn't naturally emit the "escapes to heap" / "does not escape" diagnostics for them. It's also non-trivial to replicate the exact semantics esc.go uses for OADDR. Since there are so many of these messages, I'm disabling them in this CL by themselves. I modified esc.go to suppress the Warnl calls without any other behavior changes, and then used a shell script to automatically remove any ERROR messages mentioned by run.go in "missing error" or "no match for" lines. Fixes #16300. Updates #23109. Change-Id: I3993e2743c3ff83ccd0893f4e73b366ff8871a57 Reviewed-on: https://go-review.googlesource.com/c/go/+/170319 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com> Reviewed-by: David Chase <drchase@google.com>
191 lines
4.8 KiB
Go
191 lines
4.8 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$" "main ... 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$" "test1 ... argument does not escape$"
|
|
}
|
|
}
|
|
|
|
func test2(iter int) {
|
|
|
|
const maxI = 500
|
|
m := make(map[int][]int) // ERROR "test2 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 "test2 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$" "test2 ... argument does not escape$"
|
|
}
|
|
}
|
|
|
|
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$" "test3 ... argument does not escape$"
|
|
}
|
|
}
|
|
|
|
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 "test4 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$" "test4 ... argument does not escape$"
|
|
}
|
|
}
|
|
|
|
type str struct {
|
|
m *int
|
|
}
|
|
|
|
func recur1(j int, s *str) { // ERROR "recur1 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 literal 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$" "test5 ... argument does not escape$"
|
|
}
|
|
}
|
|
|
|
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 literal 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$" "test6 ... argument does not escape$"
|
|
}
|
|
}
|