diff --git a/go/ssa/interp/interp_test.go b/go/ssa/interp/interp_test.go index 23d6424546..e04ec066ca 100644 --- a/go/ssa/interp/interp_test.go +++ b/go/ssa/interp/interp_test.go @@ -135,14 +135,18 @@ var gorootTestTests = []string{ // These are files in go.tools/go/ssa/interp/testdata/. var testdataTests = []string{ "boundmeth.go", + "complit.go", "coverage.go", + "defer.go", "fieldprom.go", "ifaceconv.go", "ifaceprom.go", "initorder.go", "methprom.go", "mrvchain.go", + "range.go", "recover.go", + "static.go", "callstack.go", } diff --git a/go/ssa/interp/testdata/complit.go b/go/ssa/interp/testdata/complit.go new file mode 100644 index 0000000000..c44fc0068e --- /dev/null +++ b/go/ssa/interp/testdata/complit.go @@ -0,0 +1,84 @@ +package main + +// Tests of composite literals. + +import "fmt" + +// Map literals. +func init() { + type M map[int]int + m1 := []*M{{1: 1}, &M{2: 2}} + want := "map[1:1] map[2:2]" + if got := fmt.Sprint(*m1[0], *m1[1]); got != want { + panic(got) + } + m2 := []M{{1: 1}, M{2: 2}} + if got := fmt.Sprint(m2[0], m2[1]); got != want { + panic(got) + } +} + +// Nonliteral keys in composite literal. +func init() { + const zero int = 1 + var v = []int{1 + zero: 42} + if x := fmt.Sprint(v); x != "[0 0 42]" { + panic(x) + } +} + +// Test for in-place initialization. +func init() { + // struct + type S struct { + a, b int + } + s := S{1, 2} + s = S{b: 3} + if s.a != 0 { + panic("s.a != 0") + } + if s.b != 3 { + panic("s.b != 3") + } + s = S{} + if s.a != 0 { + panic("s.a != 0") + } + if s.b != 0 { + panic("s.b != 0") + } + + // array + type A [4]int + a := A{2, 4, 6, 8} + a = A{1: 6, 2: 4} + if a[0] != 0 { + panic("a[0] != 0") + } + if a[1] != 6 { + panic("a[1] != 6") + } + if a[2] != 4 { + panic("a[2] != 4") + } + if a[3] != 0 { + panic("a[3] != 0") + } + a = A{} + if a[0] != 0 { + panic("a[0] != 0") + } + if a[1] != 0 { + panic("a[1] != 0") + } + if a[2] != 0 { + panic("a[2] != 0") + } + if a[3] != 0 { + panic("a[3] != 0") + } +} + +func main() { +} diff --git a/go/ssa/interp/testdata/coverage.go b/go/ssa/interp/testdata/coverage.go index ed6e9a83ff..ca65643d1e 100644 --- a/go/ssa/interp/testdata/coverage.go +++ b/go/ssa/interp/testdata/coverage.go @@ -1,11 +1,8 @@ // This interpreter test is designed to run very quickly yet provide // some coverage of a broad selection of constructs. -// TODO(adonovan): more. // // Validate this file with 'go run' after editing. // TODO(adonovan): break this into small files organized by theme. -// TODO(adonovan): move static tests (sanity checks) of ssa -// construction into ssa/testdata. package main @@ -14,17 +11,6 @@ import ( "reflect" ) -const zero int = 1 - -var v = []int{1 + zero: 42} - -// Nonliteral keys in composite literal. -func init() { - if x := fmt.Sprint(v); x != "[0 0 42]" { - panic(x) - } -} - func init() { // Call of variadic function with (implicit) empty slice. if x := fmt.Sprint(); x != "" { @@ -126,40 +112,6 @@ func init() { } } -// Range over string. -func init() { - if x := len("Hello, 世界"); x != 13 { // bytes - panic(x) - } - var indices []int - var runes []rune - for i, r := range "Hello, 世界" { - runes = append(runes, r) - indices = append(indices, i) - } - if x := fmt.Sprint(runes); x != "[72 101 108 108 111 44 32 19990 30028]" { - panic(x) - } - if x := fmt.Sprint(indices); x != "[0 1 2 3 4 5 6 7 10]" { - panic(x) - } - s := "" - for _, r := range runes { - s = fmt.Sprintf("%s%c", s, r) - } - if s != "Hello, 世界" { - panic(s) - } - - var x int - for range "Hello, 世界" { - x++ - } - if x != len(indices) { - panic(x) - } -} - func main() { print() // legal @@ -289,25 +241,6 @@ func main() { _ = map[int]*struct{}{0: {}} } -// A blocking select (sans "default:") cannot fall through. -// Regression test for issue 7022. -func bug7022() int { - var c1, c2 chan int - select { - case <-c1: - return 123 - case <-c2: - return 456 - } -} - -// Parens should not prevent intrinsic treatment of built-ins. -// (Regression test for a crash.) -func init() { - _ = (new)(int) - _ = (make)([]int, 0) -} - type mybool bool func (mybool) f() {} @@ -350,26 +283,6 @@ func init() { } } -var order []int - -func create(x int) int { - order = append(order, x) - return x -} - -var c = create(b + 1) -var a, b = create(1), create(2) - -// Initialization order of package-level value specs. -func init() { - if x := fmt.Sprint(order); x != "[1 2 3]" { - panic(x) - } - if c != 3 { - panic(c) - } -} - // Shifts. func init() { var i int64 = 1 @@ -408,15 +321,16 @@ func init() { // An I->I type-assert fails iff the value is nil. func init() { - // TODO(adonovan): temporarily disabled; see comment at bottom of file. - // defer func() { - // r := fmt.Sprint(recover()) - // if r != "interface conversion: interface is nil, not main.I" { - // panic("I->I type assertion succeeded for nil value") - // } - // }() - // var x I - // _ = x.(I) + defer func() { + r := fmt.Sprint(recover()) + // Exact error varies by toolchain. + if r != "runtime error: interface conversion: interface is nil, not main.I" && + r != "interface conversion: interface is nil, not main.I" { + panic("I->I type assertion succeeded for nil value") + } + }() + var x I + _ = x.(I) } ////////////////////////////////////////////////////////////////////// @@ -488,54 +402,6 @@ func init() { multipleLabels() } -//////////////////////////////////////////////////////////////////////// -// Defer - -func deferMutatesResults(noArgReturn bool) (a, b int) { - defer func() { - if a != 1 || b != 2 { - panic(fmt.Sprint(a, b)) - } - a, b = 3, 4 - }() - if noArgReturn { - a, b = 1, 2 - return - } - return 1, 2 -} - -func init() { - a, b := deferMutatesResults(true) - if a != 3 || b != 4 { - panic(fmt.Sprint(a, b)) - } - a, b = deferMutatesResults(false) - if a != 3 || b != 4 { - panic(fmt.Sprint(a, b)) - } -} - -// We concatenate init blocks to make a single function, but we must -// run defers at the end of each block, not the combined function. -var deferCount = 0 - -func init() { - deferCount = 1 - defer func() { - deferCount++ - }() - // defer runs HERE -} - -func init() { - // Strictly speaking the spec says deferCount may be 0 or 2 - // since the relative order of init blocks is unspecified. - if deferCount != 2 { - panic(deferCount) // defer call has not run! - } -} - func init() { // Struct equivalence ignores blank fields. type s struct{ x, _, z int } @@ -566,21 +432,6 @@ func init() { _ = i == j // interface comparison recurses on types } -// Composite literals - -func init() { - type M map[int]int - m1 := []*M{{1: 1}, &M{2: 2}} - want := "map[1:1] map[2:2]" - if got := fmt.Sprint(*m1[0], *m1[1]); got != want { - panic(got) - } - m2 := []M{{1: 1}, M{2: 2}} - if got := fmt.Sprint(m2[0], m2[1]); got != want { - panic(got) - } -} - func init() { // Regression test for SSA renaming bug. var ints []int @@ -594,42 +445,6 @@ func init() { } } -func init() { - // Regression test for issue 6806. - ch := make(chan int) - select { - case n, _ := <-ch: - _ = n - default: - // The default case disables the simplification of - // select to a simple receive statement. - } - - // TODO(adonovan, gri) enable again once we accept issue 8189. - // value,ok-form receive where TypeOf(ok) is a named boolean. - // type mybool bool - // var x int - // var y mybool - // select { - // case x, y = <-ch: - // default: - // // The default case disables the simplification of - // // select to a simple receive statement. - // } - - // TODO(adonovan, gri) remove once we accept issue 8189. - var x int - var y bool - select { - case x, y = <-ch: - default: - // The default case disables the simplification of - // select to a simple receive statement. - } - _ = x - _ = y -} - // Regression test for issue 6949: // []byte("foo") is not a constant since it allocates memory. func init() { @@ -655,33 +470,14 @@ func init() { panic(got) } max := 3 - if v[2] == 42 { - max = 2 + if "a"[0] == 'a' { + max = 2 // max is non-constant, even in SSA form } if got := lenCapLoHi(s[1:2:max]); got != [4]int{1, 1, 1, 1} { panic(got) } } -// Regression test for issue 7840 (covered by SSA sanity checker). -func bug7840() bool { - // This creates a single-predecessor block with a φ-node. - return false && a == 0 && a == 0 -} - -// Regression test for range of pointer to named array type. -func init() { - type intarr [3]int - ia := intarr{1, 2, 3} - var count int - for _, x := range &ia { - count += x - } - if count != 6 { - panic(count) - } -} - // Test that a nice error is issue by indirection wrappers. func init() { var ptr *T @@ -698,56 +494,3 @@ func init() { i.f() panic("unreachable") } - -// Test for in-place initialization. -func init() { - // struct - type S struct { - a, b int - } - s := S{1, 2} - s = S{b: 3} - if s.a != 0 { - panic("s.a != 0") - } - if s.b != 3 { - panic("s.b != 3") - } - s = S{} - if s.a != 0 { - panic("s.a != 0") - } - if s.b != 0 { - panic("s.b != 0") - } - - // array - type A [4]int - a := A{2, 4, 6, 8} - a = A{1: 6, 2: 4} - if a[0] != 0 { - panic("a[0] != 0") - } - if a[1] != 6 { - panic("a[1] != 6") - } - if a[2] != 4 { - panic("a[2] != 4") - } - if a[3] != 0 { - panic("a[3] != 0") - } - a = A{} - if a[0] != 0 { - panic("a[0] != 0") - } - if a[1] != 0 { - panic("a[1] != 0") - } - if a[2] != 0 { - panic("a[2] != 0") - } - if a[3] != 0 { - panic("a[3] != 0") - } -} diff --git a/go/ssa/interp/testdata/defer.go b/go/ssa/interp/testdata/defer.go new file mode 100644 index 0000000000..f5bae6c3f4 --- /dev/null +++ b/go/ssa/interp/testdata/defer.go @@ -0,0 +1,53 @@ +package main + +// Tests of defer. (Deferred recover() belongs is recover.go.) + +import "fmt" + +func deferMutatesResults(noArgReturn bool) (a, b int) { + defer func() { + if a != 1 || b != 2 { + panic(fmt.Sprint(a, b)) + } + a, b = 3, 4 + }() + if noArgReturn { + a, b = 1, 2 + return + } + return 1, 2 +} + +func init() { + a, b := deferMutatesResults(true) + if a != 3 || b != 4 { + panic(fmt.Sprint(a, b)) + } + a, b = deferMutatesResults(false) + if a != 3 || b != 4 { + panic(fmt.Sprint(a, b)) + } +} + +// We concatenate init blocks to make a single function, but we must +// run defers at the end of each block, not the combined function. +var deferCount = 0 + +func init() { + deferCount = 1 + defer func() { + deferCount++ + }() + // defer runs HERE +} + +func init() { + // Strictly speaking the spec says deferCount may be 0 or 2 + // since the relative order of init blocks is unspecified. + if deferCount != 2 { + panic(deferCount) // defer call has not run! + } +} + +func main() { +} diff --git a/go/ssa/interp/testdata/initorder.go b/go/ssa/interp/testdata/initorder.go index b351ae5dd1..0f26bed695 100644 --- a/go/ssa/interp/testdata/initorder.go +++ b/go/ssa/interp/testdata/initorder.go @@ -1,5 +1,7 @@ package main +import "fmt" + // Test of initialization order of package-level vars. var counter int @@ -38,3 +40,28 @@ var order = makeOrder() var a, b = next(), next() var c, d = next2() var e, f = next(), next() + +// ------------------------------------------------------------------------ + +var order2 []string + +func create(x int, name string) int { + order2 = append(order2, name) + return x +} + +var C = create(B+1, "C") +var A, B = create(1, "A"), create(2, "B") + +// Initialization order of package-level value specs. +func init() { + x := fmt.Sprint(order2) + // Result varies by toolchain. This is a spec bug. + if x != "[B C A]" && // gc + x != "[A B C]" { // go/types + panic(x) + } + if C != 3 { + panic(c) + } +} diff --git a/go/ssa/interp/testdata/range.go b/go/ssa/interp/testdata/range.go new file mode 100644 index 0000000000..da8a421e62 --- /dev/null +++ b/go/ssa/interp/testdata/range.go @@ -0,0 +1,55 @@ +package main + +// Tests of range loops. + +import "fmt" + +// Range over string. +func init() { + if x := len("Hello, 世界"); x != 13 { // bytes + panic(x) + } + var indices []int + var runes []rune + for i, r := range "Hello, 世界" { + runes = append(runes, r) + indices = append(indices, i) + } + if x := fmt.Sprint(runes); x != "[72 101 108 108 111 44 32 19990 30028]" { + panic(x) + } + if x := fmt.Sprint(indices); x != "[0 1 2 3 4 5 6 7 10]" { + panic(x) + } + s := "" + for _, r := range runes { + s = fmt.Sprintf("%s%c", s, r) + } + if s != "Hello, 世界" { + panic(s) + } + + var x int + for range "Hello, 世界" { + x++ + } + if x != len(indices) { + panic(x) + } +} + +// Regression test for range of pointer to named array type. +func init() { + type intarr [3]int + ia := intarr{1, 2, 3} + var count int + for _, x := range &ia { + count += x + } + if count != 6 { + panic(count) + } +} + +func main() { +} diff --git a/go/ssa/interp/testdata/static.go b/go/ssa/interp/testdata/static.go new file mode 100644 index 0000000000..b115513c63 --- /dev/null +++ b/go/ssa/interp/testdata/static.go @@ -0,0 +1,58 @@ +package main + +// Static tests of SSA builder (via the sanity checker). +// Dynamic semantics are not exercised. + +func init() { + // Regression test for issue 6806. + ch := make(chan int) + select { + case n, _ := <-ch: + _ = n + default: + // The default case disables the simplification of + // select to a simple receive statement. + } + + // value,ok-form receive where TypeOf(ok) is a named boolean. + type mybool bool + var x int + var y mybool + select { + case x, y = <-ch: + default: + // The default case disables the simplification of + // select to a simple receive statement. + } + _ = x + _ = y +} + +var a int + +// Regression test for issue 7840 (covered by SSA sanity checker). +func bug7840() bool { + // This creates a single-predecessor block with a φ-node. + return false && a == 0 && a == 0 +} + +// A blocking select (sans "default:") cannot fall through. +// Regression test for issue 7022. +func bug7022() int { + var c1, c2 chan int + select { + case <-c1: + return 123 + case <-c2: + return 456 + } +} + +// Parens should not prevent intrinsic treatment of built-ins. +// (Regression test for a crash.) +func init() { + _ = (new)(int) + _ = (make)([]int, 0) +} + +func main() {}