diff options
Diffstat (limited to 'test/live.go')
-rw-r--r-- | test/live.go | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/test/live.go b/test/live.go new file mode 100644 index 000000000..b4cced47e --- /dev/null +++ b/test/live.go @@ -0,0 +1,624 @@ +// errorcheck -0 -l -live + +// Copyright 2014 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. + +// liveness tests with inlining disabled. +// see also live2.go. + +package main + +func f1() { + var x *int + print(&x) // ERROR "live at call to printpointer: x$" + print(&x) // ERROR "live at call to printpointer: x$" +} + +func f2(b bool) { + if b { + print(0) // nothing live here + return + } + var x *int + print(&x) // ERROR "live at call to printpointer: x$" + print(&x) // ERROR "live at call to printpointer: x$" +} + +func f3(b bool) { + // Because x and y are ambiguously live, they appear + // live throughout the function, to avoid being poisoned + // in GODEBUG=gcdead=1 mode. + + print(0) // ERROR "live at call to printint: x y$" + if b == false { + print(0) // ERROR "live at call to printint: x y$" + return + } + + if b { + var x *int + print(&x) // ERROR "live at call to printpointer: x y$" + print(&x) // ERROR "live at call to printpointer: x y$" + } else { + var y *int + print(&y) // ERROR "live at call to printpointer: x y$" + print(&y) // ERROR "live at call to printpointer: x y$" + } + print(0) // ERROR "live at call to printint: x y$" "x \(type \*int\) is ambiguously live" "y \(type \*int\) is ambiguously live" +} + +// The old algorithm treated x as live on all code that +// could flow to a return statement, so it included the +// function entry and code above the declaration of x +// but would not include an indirect use of x in an infinite loop. +// Check that these cases are handled correctly. + +func f4(b1, b2 bool) { // x not live here + if b2 { + print(0) // x not live here + return + } + var z **int + x := new(int) + *x = 42 + z = &x + print(**z) // ERROR "live at call to printint: x z$" + if b2 { + print(1) // ERROR "live at call to printint: x$" + return + } + for { + print(**z) // ERROR "live at call to printint: x z$" + } +} + +func f5(b1 bool) { + var z **int + if b1 { + x := new(int) + *x = 42 + z = &x + } else { + y := new(int) + *y = 54 + z = &y + } + print(**z) // ERROR "live at call to printint: x y$" "x \(type \*int\) is ambiguously live" "y \(type \*int\) is ambiguously live" +} + +// confusion about the _ result used to cause spurious "live at entry to f6: _". + +func f6() (_, y string) { + y = "hello" + return +} + +// confusion about addressed results used to cause "live at entry to f7: x". + +func f7() (x string) { + _ = &x + x = "hello" + return +} + +// ignoring block returns used to cause "live at entry to f8: x, y". + +func f8() (x, y string) { + return g8() +} + +func g8() (string, string) + +// ignoring block assignments used to cause "live at entry to f9: x" +// issue 7205 + +var i9 interface{} + +func f9() bool { + g8() + x := i9 + return x != 99 +} + +// liveness formerly confused by UNDEF followed by RET, +// leading to "live at entry to f10: ~r1" (unnamed result). + +func f10() string { + panic(1) +} + +// liveness formerly confused by select, thinking runtime.selectgo +// can return to next instruction; it always jumps elsewhere. +// note that you have to use at least two cases in the select +// to get a true select; smaller selects compile to optimized helper functions. + +var c chan *int +var b bool + +// this used to have a spurious "live at entry to f11a: ~r0" +func f11a() *int { + select { // ERROR "live at call to selectgo: autotmp" + case <-c: // ERROR "live at call to selectrecv: autotmp" + return nil + case <-c: // ERROR "live at call to selectrecv: autotmp" + return nil + } +} + +func f11b() *int { + p := new(int) + if b { + // At this point p is dead: the code here cannot + // get to the bottom of the function. + // This used to have a spurious "live at call to printint: p". + print(1) // nothing live here! + select { // ERROR "live at call to selectgo: autotmp" + case <-c: // ERROR "live at call to selectrecv: autotmp" + return nil + case <-c: // ERROR "live at call to selectrecv: autotmp" + return nil + } + } + println(*p) + return nil +} + +func f11c() *int { + p := new(int) + if b { + // Unlike previous, the cases in this select fall through, + // so we can get to the println, so p is not dead. + print(1) // ERROR "live at call to printint: p" + select { // ERROR "live at call to newselect: p" "live at call to selectgo: autotmp.* p" + case <-c: // ERROR "live at call to selectrecv: autotmp.* p" + case <-c: // ERROR "live at call to selectrecv: autotmp.* p" + } + } + println(*p) + return nil +} + +// similarly, select{} does not fall through. +// this used to have a spurious "live at entry to f12: ~r0". + +func f12() *int { + if b { + select{} + } else { + return nil + } +} + +// incorrectly placed VARDEF annotations can cause missing liveness annotations. +// this used to be missing the fact that s is live during the call to g13 (because it is +// needed for the call to h13). + +func f13() { + s := "hello" + s = h13(s, g13(s)) // ERROR "live at call to g13: s" +} + +func g13(string) string +func h13(string, string) string + +// more incorrectly placed VARDEF. + +func f14() { + x := g14() + print(&x) // ERROR "live at call to printpointer: x" +} + +func g14() string + +func f15() { + var x string + _ = &x + x = g15() // ERROR "live at call to g15: x" + print(x) // ERROR "live at call to printstring: x" +} + +func g15() string + +// Checking that various temporaries do not persist or cause +// ambiguously live values that must be zeroed. +// The exact temporary names are inconsequential but we are +// trying to check that there is only one at any given site, +// and also that none show up in "ambiguously live" messages. + +var m map[string]int + +func f16() { + if b { + delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$" + } + delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$" + delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$" +} + +var m2s map[string]*byte +var m2 map[[2]string]*byte +var x2 [2]string +var bp *byte + +func f17a() { + // value temporary only + if b { + m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$" + } + m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$" + m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$" +} + +func f17b() { + // key temporary only + if b { + m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$" + } + m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$" + m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$" +} + +func f17c() { + // key and value temporaries + if b { + m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" + } + m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" + m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" +} + +func g18() [2]string + +func f18() { + // key temporary for mapaccess. + // temporary introduced by orderexpr. + var z *byte + if b { + z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + } + z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + print(z) +} + +var ch chan *byte + +func f19() { + // dest temporary for channel receive. + var z *byte + + if b { + z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$" + } + z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$" + z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$" + print(z) +} + +func f20() { + // src temporary for channel send + if b { + ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$" + } + ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$" + ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$" +} + +func f21() { + // key temporary for mapaccess using array literal key. + var z *byte + if b { + z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + } + z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + print(z) +} + +func f23() { + // key temporary for two-result map access using array literal key. + var z *byte + var ok bool + if b { + z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$" + } + z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$" + z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$" + print(z, ok) +} + +func f24() { + // key temporary for map access using array literal key. + // value temporary too. + if b { + m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" + } + m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" + m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" +} + +// defer should not cause spurious ambiguously live variables + +func f25(b bool) { + defer g25() + if b { + return + } + var x string + _ = &x + x = g15() // ERROR "live at call to g15: x" + print(x) // ERROR "live at call to printstring: x" +} // ERROR "live at call to deferreturn: x" + +func g25() + +// non-escaping ... slices passed to function call should die on return, +// so that the temporaries do not stack and do not cause ambiguously +// live variables. + +func f26(b bool) { + if b { + print26(1,2,3) // ERROR "live at call to print26: autotmp_[0-9]+$" + } + print26(4,5,6) // ERROR "live at call to print26: autotmp_[0-9]+$" + print26(7,8,9) // ERROR "live at call to print26: autotmp_[0-9]+$" + println() +} + +//go:noescape +func print26(...interface{}) + +// non-escaping closures passed to function call should die on return + +func f27(b bool) { + x := 0 + if b { + call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$" + } + call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$" + call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$" + println() +} + +// but defer does escape to later execution in the function + +func f27defer(b bool) { + x := 0 + if b { + defer call27(func() {x++}) // ERROR "live at call to deferproc: autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+$" + } + defer call27(func() {x++}) // ERROR "live at call to deferproc: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$" "ambiguously live" + println() // ERROR "live at call to printnl: autotmp_[0-9]+ autotmp_[0-9]+$" +} // ERROR "live at call to deferreturn: autotmp_[0-9]+ autotmp_[0-9]+$" + +// and newproc (go) escapes to the heap + +func f27go(b bool) { + x := 0 + if b { + go call27(func() {x++}) // ERROR "live at call to new: &x" "live at call to newproc: &x$" + } + go call27(func() {x++}) // ERROR "live at call to new: &x" + println() +} + +//go:noescape +func call27(func()) + +// concatstring slice should die on return + +var s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 string + +func f28(b bool) { + if b { + print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$" + } + print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$" + print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$" +} + +// map iterator should die on end of range loop + +func f29(b bool) { + if b { + for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$" + print(k) // ERROR "live at call to printstring: autotmp_[0-9]+$" + } + } + for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$" + print(k) // ERROR "live at call to printstring: autotmp_[0-9]+$" + } + for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$" + print(k) // ERROR "live at call to printstring: autotmp_[0-9]+$" + } +} + +// copy of array of pointers should die at end of range loop + +var ptrarr [10]*int + +func f30(b bool) { + // two live temps during print(p): + // the copy of ptrarr and the internal iterator pointer. + if b { + for _, p := range ptrarr { + print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$" + } + } + for _, p := range ptrarr { + print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$" + } + for _, p := range ptrarr { + print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$" + } +} + +// conversion to interface should not leave temporary behind + +func f31(b1, b2, b3 bool) { + if b1 { + g31("a") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to g31: autotmp_[0-9]+$" + } + if b2 { + h31("b") // ERROR "live at call to new: autotmp_[0-9]+$" "live at call to convT2E: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to h31: autotmp_[0-9]+$" + } + if b3 { + panic("asdf") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to panic: autotmp_[0-9]+$" + } + print(b3) +} + +func g31(interface{}) +func h31(...interface{}) + +// non-escaping partial functions passed to function call should die on return + +type T32 int + +func (t *T32) Inc() { // ERROR "live at entry" + *t++ +} + +var t32 T32 + +func f32(b bool) { + if b { + call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$" + } + call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$" + call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$" +} + +//go:noescape +func call32(func()) + +// temporaries introduced during if conditions and && || expressions +// should die once the condition has been acted upon. + +var m33 map[interface{}]int + +func f33() { + if m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + println() + return + } else { + println() + } + println() +} + +func f34() { + if m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + println() + return + } + println() +} + +func f35() { + if m33[nil] == 0 && m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + println() + return + } + println() +} + +func f36() { + if m33[nil] == 0 || m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + println() + return + } + println() +} + +func f37() { + if (m33[nil] == 0 || m33[nil] == 0) && m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + println() + return + } + println() +} + +// select temps should disappear in the case bodies + +var c38 chan string + +func fc38() chan string +func fi38(int) *string +func fb38() *bool + +func f38(b bool) { + // we don't care what temps are printed on the lines with output. + // we care that the println lines have no live variables + // and therefore no output. + if b { + select { // ERROR "live at call" + case <-fc38(): // ERROR "live at call" + println() + case fc38() <- *fi38(1): // ERROR "live at call" + println() + case *fi38(2) = <-fc38(): // ERROR "live at call" + println() + case *fi38(3), *fb38() = <-fc38(): // ERROR "live at call" + println() + } + println() + } + println() +} + +// issue 8097: mishandling of x = x during return. + +func f39() (x []int) { + x = []int{1} + println() // ERROR "live at call to printnl: x" + return x +} + +func f39a() (x []int) { + x = []int{1} + println() // ERROR "live at call to printnl: x" + return +} + +func f39b() (x [10]*int) { + x = [10]*int{new(int)} // ERROR "live at call to new: x" + println() // ERROR "live at call to printnl: x" + return x +} + +func f39c() (x [10]*int) { + x = [10]*int{new(int)} // ERROR "live at call to new: x" + println() // ERROR "live at call to printnl: x" + return +} + +// issue 8142: lost 'addrtaken' bit on inlined variables. +// no inlining in this test, so just checking that non-inlined works. + +type T40 struct { + m map[int]int +} + +func newT40() *T40 { + ret := T40{ // ERROR "live at call to makemap: &ret" + make(map[int]int), + } + return &ret +} + +func bad40() { + t := newT40() + println() + _ = t +} + +func good40() { + ret := T40{ // ERROR "live at call to makemap: ret" + make(map[int]int), + } + t := &ret + println() // ERROR "live at call to printnl: ret" + _ = t +} |