diff options
Diffstat (limited to 'go/ssa/interp/testdata')
-rw-r--r-- | go/ssa/interp/testdata/boundmeth.go | 3 | ||||
-rw-r--r-- | go/ssa/interp/testdata/convert.go | 9 | ||||
-rw-r--r-- | go/ssa/interp/testdata/deepequal.go | 93 | ||||
-rw-r--r-- | go/ssa/interp/testdata/fixedbugs/issue52342.go | 17 | ||||
-rw-r--r-- | go/ssa/interp/testdata/fixedbugs/issue52835.go | 27 | ||||
-rw-r--r-- | go/ssa/interp/testdata/fixedbugs/issue55086.go | 132 | ||||
-rw-r--r-- | go/ssa/interp/testdata/slice2array.go | 92 | ||||
-rw-r--r-- | go/ssa/interp/testdata/slice2arrayptr.go | 2 | ||||
-rw-r--r-- | go/ssa/interp/testdata/src/encoding/encoding.go | 15 | ||||
-rw-r--r-- | go/ssa/interp/testdata/src/log/log.go | 8 | ||||
-rw-r--r-- | go/ssa/interp/testdata/src/reflect/deepequal.go | 109 | ||||
-rw-r--r-- | go/ssa/interp/testdata/src/reflect/reflect.go | 13 | ||||
-rw-r--r-- | go/ssa/interp/testdata/typeassert.go | 32 | ||||
-rw-r--r-- | go/ssa/interp/testdata/width32.go | 42 | ||||
-rw-r--r-- | go/ssa/interp/testdata/zeros.go | 45 |
15 files changed, 637 insertions, 2 deletions
diff --git a/go/ssa/interp/testdata/boundmeth.go b/go/ssa/interp/testdata/boundmeth.go index 69937f9d3..47b940685 100644 --- a/go/ssa/interp/testdata/boundmeth.go +++ b/go/ssa/interp/testdata/boundmeth.go @@ -123,7 +123,8 @@ func nilInterfaceMethodValue() { r := fmt.Sprint(recover()) // runtime panic string varies across toolchains if r != "interface conversion: interface is nil, not error" && - r != "runtime error: invalid memory address or nil pointer dereference" { + r != "runtime error: invalid memory address or nil pointer dereference" && + r != "method value: interface is nil" { panic("want runtime panic from nil interface method value, got " + r) } }() diff --git a/go/ssa/interp/testdata/convert.go b/go/ssa/interp/testdata/convert.go index 0dcf13bdd..76310405f 100644 --- a/go/ssa/interp/testdata/convert.go +++ b/go/ssa/interp/testdata/convert.go @@ -22,6 +22,15 @@ func main() { }, "runtime error: negative shift amount", ) + wantPanic( + func() { + const maxInt32 = 1<<31 - 1 + var idx int64 = maxInt32*2 + 8 + x := make([]int, 16) + _ = x[idx] + }, + "runtime error: runtime error: index out of range [4294967302] with length 16", + ) } func wantPanic(fn func(), s string) { diff --git a/go/ssa/interp/testdata/deepequal.go b/go/ssa/interp/testdata/deepequal.go new file mode 100644 index 000000000..4fad2d657 --- /dev/null +++ b/go/ssa/interp/testdata/deepequal.go @@ -0,0 +1,93 @@ +// This interpreter test is designed to test the test copy of DeepEqual. +// +// Validate this file with 'go run' after editing. + +package main + +import "reflect" + +func assert(cond bool) { + if !cond { + panic("failed") + } +} + +type X int +type Y struct { + y *Y + z [3]int +} + +var ( + a = []int{0, 1, 2, 3} + b = []X{0, 1, 2, 3} + c = map[int]string{0: "zero", 1: "one"} + d = map[X]string{0: "zero", 1: "one"} + e = &Y{} + f = (*Y)(nil) + g = &Y{y: e} + h *Y +) + +func init() { + h = &Y{} // h->h + h.y = h +} + +func main() { + assert(reflect.DeepEqual(nil, nil)) + assert(reflect.DeepEqual((*int)(nil), (*int)(nil))) + assert(!reflect.DeepEqual(nil, (*int)(nil))) + + assert(reflect.DeepEqual(0, 0)) + assert(!reflect.DeepEqual(0, int64(0))) + + assert(!reflect.DeepEqual("", 0)) + + assert(reflect.DeepEqual(a, []int{0, 1, 2, 3})) + assert(!reflect.DeepEqual(a, []int{0, 1, 2})) + assert(!reflect.DeepEqual(a, []int{0, 1, 0, 3})) + + assert(reflect.DeepEqual(b, []X{0, 1, 2, 3})) + assert(!reflect.DeepEqual(b, []X{0, 1, 0, 3})) + + assert(reflect.DeepEqual(c, map[int]string{0: "zero", 1: "one"})) + assert(!reflect.DeepEqual(c, map[int]string{0: "zero", 1: "one", 2: "two"})) + assert(!reflect.DeepEqual(c, map[int]string{1: "one", 2: "two"})) + assert(!reflect.DeepEqual(c, map[int]string{1: "one"})) + + assert(reflect.DeepEqual(d, map[X]string{0: "zero", 1: "one"})) + assert(!reflect.DeepEqual(d, map[int]string{0: "zero", 1: "one"})) + + assert(reflect.DeepEqual(e, &Y{})) + assert(reflect.DeepEqual(e, &Y{z: [3]int{0, 0, 0}})) + assert(!reflect.DeepEqual(e, &Y{z: [3]int{0, 1, 0}})) + + assert(reflect.DeepEqual(f, (*Y)(nil))) + assert(!reflect.DeepEqual(f, nil)) + + // eq_h -> eq_h. Pointer structure and elements are equal so DeepEqual. + eq_h := &Y{} + eq_h.y = eq_h + assert(reflect.DeepEqual(h, eq_h)) + + // deepeq_h->h->h. Pointed to elem of (deepeq_h, h) are (h,h). (h,h) are deep equal so h and deepeq_h are DeepEqual. + deepeq_h := &Y{} + deepeq_h.y = h + assert(reflect.DeepEqual(h, deepeq_h)) + + distinct := []interface{}{a, b, c, d, e, f, g, h} + for x := range distinct { + for y := range distinct { + assert((x == y) == reflect.DeepEqual(distinct[x], distinct[y])) + } + } + + // anonymous struct types. + assert(reflect.DeepEqual(struct{}{}, struct{}{})) + assert(reflect.DeepEqual(struct{ x int }{1}, struct{ x int }{1})) + assert(!reflect.DeepEqual(struct{ x int }{}, struct{ x int }{5})) + assert(!reflect.DeepEqual(struct{ x, y int }{0, 1}, struct{ x int }{0})) + assert(reflect.DeepEqual(struct{ x, y int }{2, 3}, struct{ x, y int }{2, 3})) + assert(!reflect.DeepEqual(struct{ x, y int }{4, 5}, struct{ x, y int }{4, 6})) +} diff --git a/go/ssa/interp/testdata/fixedbugs/issue52342.go b/go/ssa/interp/testdata/fixedbugs/issue52342.go new file mode 100644 index 000000000..2e1cc63cf --- /dev/null +++ b/go/ssa/interp/testdata/fixedbugs/issue52342.go @@ -0,0 +1,17 @@ +package main + +func main() { + var d byte + + d = 1 + d <<= 256 + if d != 0 { + panic(d) + } + + d = 1 + d >>= 256 + if d != 0 { + panic(d) + } +} diff --git a/go/ssa/interp/testdata/fixedbugs/issue52835.go b/go/ssa/interp/testdata/fixedbugs/issue52835.go new file mode 100644 index 000000000..f1d99abb7 --- /dev/null +++ b/go/ssa/interp/testdata/fixedbugs/issue52835.go @@ -0,0 +1,27 @@ +package main + +var called bool + +type I interface { + Foo() +} + +type A struct{} + +func (a A) Foo() { + called = true +} + +func lambda[X I]() func() func() { + return func() func() { + var x X + return x.Foo + } +} + +func main() { + lambda[A]()()() + if !called { + panic(called) + } +} diff --git a/go/ssa/interp/testdata/fixedbugs/issue55086.go b/go/ssa/interp/testdata/fixedbugs/issue55086.go new file mode 100644 index 000000000..84c81e91a --- /dev/null +++ b/go/ssa/interp/testdata/fixedbugs/issue55086.go @@ -0,0 +1,132 @@ +// Copyright 2022 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. + +package main + +func a() (r string) { + s := "initial" + var p *struct{ i int } + defer func() { + recover() + r = s + }() + + s, p.i = "set", 2 // s must be set before p.i panics + return "unreachable" +} + +func b() (r string) { + s := "initial" + fn := func() []int { panic("") } + defer func() { + recover() + r = s + }() + + s, fn()[0] = "set", 2 // fn() panics before any assignment occurs + return "unreachable" +} + +func c() (r string) { + s := "initial" + var p map[int]int + defer func() { + recover() + r = s + }() + + s, p[0] = "set", 2 //s must be set before p[0] index panics" + return "unreachable" +} + +func d() (r string) { + s := "initial" + var p map[int]int + defer func() { + recover() + r = s + }() + fn := func() int { panic("") } + + s, p[0] = "set", fn() // fn() panics before s is set + return "unreachable" +} + +func e() (r string) { + s := "initial" + p := map[int]int{} + defer func() { + recover() + r = s + }() + fn := func() int { panic("") } + + s, p[fn()] = "set", 0 // fn() panics before any assignment occurs + return "unreachable" +} + +func f() (r string) { + s := "initial" + p := []int{} + defer func() { + recover() + r = s + }() + + s, p[1] = "set", 0 // p[1] panics after s is set + return "unreachable" +} + +func g() (r string) { + s := "initial" + p := map[any]any{} + defer func() { + recover() + r = s + }() + var i any = func() {} + s, p[i] = "set", 0 // p[i] panics after s is set + return "unreachable" +} + +func h() (r string) { + fail := false + defer func() { + recover() + if fail { + r = "fail" + } else { + r = "success" + } + }() + + type T struct{ f int } + var p *struct{ *T } + + // The implicit "p.T" operand should be evaluated in phase 1 (and panic), + // before the "fail = true" assignment in phase 2. + fail, p.f = true, 0 + return "unreachable" +} + +func main() { + for _, test := range []struct { + fn func() string + want string + desc string + }{ + {a, "set", "s must be set before p.i panics"}, + {b, "initial", "p() panics before s is set"}, + {c, "set", "s must be set before p[0] index panics"}, + {d, "initial", "fn() panics before s is set"}, + {e, "initial", "fn() panics before s is set"}, + {f, "set", "p[1] panics after s is set"}, + {g, "set", "p[i] panics after s is set"}, + {h, "success", "p.T panics before fail is set"}, + } { + if test.fn() != test.want { + panic(test.desc) + } + } +} diff --git a/go/ssa/interp/testdata/slice2array.go b/go/ssa/interp/testdata/slice2array.go new file mode 100644 index 000000000..84e6b7330 --- /dev/null +++ b/go/ssa/interp/testdata/slice2array.go @@ -0,0 +1,92 @@ +// Copyright 2022 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 for slice to array conversion introduced in go1.20 +// See: https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer + +package main + +func main() { + s := make([]byte, 3, 4) + s[0], s[1], s[2] = 2, 3, 5 + a := ([2]byte)(s) + s[0] = 7 + + if a != [2]byte{2, 3} { + panic("converted from non-nil slice to array") + } + + { + var s []int + a := ([0]int)(s) + if a != [0]int{} { + panic("zero len array is not equal") + } + } + + if emptyToEmptyDoesNotPanic() { + panic("no panic expected from emptyToEmptyDoesNotPanic()") + } + if !threeToFourDoesPanic() { + panic("panic expected from threeToFourDoesPanic()") + } + + if !fourPanicsWhileOneDoesNot[[4]int]() { + panic("panic expected from fourPanicsWhileOneDoesNot[[4]int]()") + } + if fourPanicsWhileOneDoesNot[[1]int]() { + panic("no panic expected from fourPanicsWhileOneDoesNot[[1]int]()") + } + + if !fourPanicsWhileZeroDoesNot[[4]int]() { + panic("panic expected from fourPanicsWhileZeroDoesNot[[4]int]()") + } + if fourPanicsWhileZeroDoesNot[[0]int]() { + panic("no panic expected from fourPanicsWhileZeroDoesNot[[0]int]()") + } +} + +func emptyToEmptyDoesNotPanic() (raised bool) { + defer func() { + if e := recover(); e != nil { + raised = true + } + }() + var s []int + _ = ([0]int)(s) + return false +} + +func threeToFourDoesPanic() (raised bool) { + defer func() { + if e := recover(); e != nil { + raised = true + } + }() + s := make([]int, 3, 5) + _ = ([4]int)(s) + return false +} + +func fourPanicsWhileOneDoesNot[T [1]int | [4]int]() (raised bool) { + defer func() { + if e := recover(); e != nil { + raised = true + } + }() + s := make([]int, 3, 5) + _ = T(s) + return false +} + +func fourPanicsWhileZeroDoesNot[T [0]int | [4]int]() (raised bool) { + defer func() { + if e := recover(); e != nil { + raised = true + } + }() + var s []int + _ = T(s) + return false +} diff --git a/go/ssa/interp/testdata/slice2arrayptr.go b/go/ssa/interp/testdata/slice2arrayptr.go index ff2d9b55c..d9d8804d3 100644 --- a/go/ssa/interp/testdata/slice2arrayptr.go +++ b/go/ssa/interp/testdata/slice2arrayptr.go @@ -32,6 +32,8 @@ func main() { }, "runtime error: array length is greater than slice length", ) + + f() } type arr [2]int diff --git a/go/ssa/interp/testdata/src/encoding/encoding.go b/go/ssa/interp/testdata/src/encoding/encoding.go new file mode 100644 index 000000000..73e9de494 --- /dev/null +++ b/go/ssa/interp/testdata/src/encoding/encoding.go @@ -0,0 +1,15 @@ +package encoding + +type BinaryMarshaler interface { + MarshalBinary() (data []byte, err error) +} +type BinaryUnmarshaler interface { + UnmarshalBinary(data []byte) error +} + +type TextMarshaler interface { + MarshalText() (text []byte, err error) +} +type TextUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/go/ssa/interp/testdata/src/log/log.go b/go/ssa/interp/testdata/src/log/log.go index 8897c1d21..9a57e8c1c 100644 --- a/go/ssa/interp/testdata/src/log/log.go +++ b/go/ssa/interp/testdata/src/log/log.go @@ -8,8 +8,16 @@ import ( func Println(v ...interface{}) { fmt.Println(v...) } +func Printf(format string, v ...interface{}) { + fmt.Printf(format, v...) +} func Fatalln(v ...interface{}) { Println(v...) os.Exit(1) } + +func Fatalf(format string, v ...interface{}) { + Printf(format, v...) + os.Exit(1) +} diff --git a/go/ssa/interp/testdata/src/reflect/deepequal.go b/go/ssa/interp/testdata/src/reflect/deepequal.go new file mode 100644 index 000000000..a48e4dafa --- /dev/null +++ b/go/ssa/interp/testdata/src/reflect/deepequal.go @@ -0,0 +1,109 @@ +package reflect + +// Not an actual implementation of DeepEqual. This is a model that supports +// the bare minimum needed to get through testing interp. +// +// Does not handle cycles. +// +// Note: unclear if reflect.go can support this. +func DeepEqual(x, y interface{}) bool { + if x == nil || y == nil { + return x == y + } + v1 := ValueOf(x) + v2 := ValueOf(y) + + return deepValueEqual(v1, v2, make(map[visit]bool)) +} + +// Key for the visitedMap in deepValueEqual. +type visit struct { + a1, a2 uintptr + typ Type +} + +func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool { + if !v1.IsValid() || !v2.IsValid() { + return v1.IsValid() == v2.IsValid() + } + if v1.Type() != v2.Type() { + return false + } + + // Short circuit on reference types that can lead to cycles in comparison. + switch v1.Kind() { + case Pointer, Map, Slice, Interface: + k := visit{v1.Pointer(), v2.Pointer(), v1.Type()} // Not safe for moving GC. + if visited[k] { + // The comparison algorithm assumes that all checks in progress are true when it reencounters them. + return true + } + visited[k] = true + } + + switch v1.Kind() { + case Array: + for i := 0; i < v1.Len(); i++ { + if !deepValueEqual(v1.Index(i), v2.Index(i), visited) { + return false + } + } + return true + case Slice: + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.Len() != v2.Len() { + return false + } + if v1.Pointer() == v2.Pointer() { + return true + } + for i := 0; i < v1.Len(); i++ { + if !deepValueEqual(v1.Index(i), v2.Index(i), visited) { + return false + } + } + return true + case Interface: + if v1.IsNil() || v2.IsNil() { + return v1.IsNil() == v2.IsNil() + } + return deepValueEqual(v1.Elem(), v2.Elem(), visited) + case Ptr: + if v1.Pointer() == v2.Pointer() { + return true + } + return deepValueEqual(v1.Elem(), v2.Elem(), visited) + case Struct: + for i, n := 0, v1.NumField(); i < n; i++ { + if !deepValueEqual(v1.Field(i), v2.Field(i), visited) { + return false + } + } + return true + case Map: + if v1.IsNil() != v2.IsNil() { + return false + } + if v1.Len() != v2.Len() { + return false + } + if v1.Pointer() == v2.Pointer() { + return true + } + for _, k := range v1.MapKeys() { + val1 := v1.MapIndex(k) + val2 := v2.MapIndex(k) + if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited) { + return false + } + } + return true + case Func: + return v1.IsNil() && v2.IsNil() + default: + // Normal equality suffices + return v1.Interface() == v2.Interface() // try interface comparison as a fallback. + } +} diff --git a/go/ssa/interp/testdata/src/reflect/reflect.go b/go/ssa/interp/testdata/src/reflect/reflect.go index 8a23d272f..207e7dcfd 100644 --- a/go/ssa/interp/testdata/src/reflect/reflect.go +++ b/go/ssa/interp/testdata/src/reflect/reflect.go @@ -11,9 +11,20 @@ type Value struct { func (Value) String() string -func (Value) Elem() string +func (Value) Elem() Value func (Value) Kind() Kind func (Value) Int() int64 +func (Value) IsValid() bool +func (Value) IsNil() bool +func (Value) Len() int +func (Value) Pointer() uintptr +func (Value) Index(i int) Value +func (Value) Type() Type +func (Value) Field(int) Value +func (Value) MapIndex(Value) Value +func (Value) MapKeys() []Value +func (Value) NumField() int +func (Value) Interface() interface{} func SliceOf(Type) Type diff --git a/go/ssa/interp/testdata/typeassert.go b/go/ssa/interp/testdata/typeassert.go new file mode 100644 index 000000000..792a7558f --- /dev/null +++ b/go/ssa/interp/testdata/typeassert.go @@ -0,0 +1,32 @@ +// Tests of type asserts. +// Requires type parameters. +package typeassert + +type fooer interface{ foo() string } + +type X int + +func (_ X) foo() string { return "x" } + +func f[T fooer](x T) func() string { + return x.foo +} + +func main() { + if f[X](0)() != "x" { + panic("f[X]() != 'x'") + } + + p := false + func() { + defer func() { + if recover() != nil { + p = true + } + }() + f[fooer](nil) // panics on x.foo when T is an interface and nil. + }() + if !p { + panic("f[fooer] did not panic") + } +} diff --git a/go/ssa/interp/testdata/width32.go b/go/ssa/interp/testdata/width32.go new file mode 100644 index 000000000..a032ba44c --- /dev/null +++ b/go/ssa/interp/testdata/width32.go @@ -0,0 +1,42 @@ +// Copyright 2022 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 interpretation on 32 bit widths. + +package main + +func main() { + mapSize() +} + +func mapSize() { + // Tests for the size argument of make on a map type. + const tooBigFor32 = 1<<33 - 1 + wantPanic( + func() { + _ = make(map[int]int, int64(tooBigFor32)) + }, + "runtime error: ssa.MakeMap.Reserve value 8589934591 does not fit in int", + ) + + // TODO: Enable the following if sizeof(int) can be different for host and target. + // _ = make(map[int]int, tooBigFor32) + // + // Second arg to make in `make(map[int]int, tooBigFor32)` is an untyped int and + // is converted into an int explicitly in ssa. + // This has a different value on 32 and 64 bit systems. +} + +func wantPanic(fn func(), s string) { + defer func() { + err := recover() + if err == nil { + panic("expected panic") + } + if got := err.(error).Error(); got != s { + panic("expected panic " + s + " got " + got) + } + }() + fn() +} diff --git a/go/ssa/interp/testdata/zeros.go b/go/ssa/interp/testdata/zeros.go new file mode 100644 index 000000000..509c78a36 --- /dev/null +++ b/go/ssa/interp/testdata/zeros.go @@ -0,0 +1,45 @@ +// Copyright 2022 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 interpretation on zero values with type params. +package zeros + +func assert(cond bool, msg string) { + if !cond { + panic(msg) + } +} + +func tp0[T int | string | float64]() T { return T(0) } + +func tpFalse[T ~bool]() T { return T(false) } + +func tpEmptyString[T string | []byte]() T { return T("") } + +func tpNil[T *int | []byte]() T { return T(nil) } + +func main() { + // zero values + var zi int + var zf float64 + var zs string + + assert(zi == int(0), "zero value of int is int(0)") + assert(zf == float64(0), "zero value of float64 is float64(0)") + assert(zs != string(0), "zero value of string is not string(0)") + + assert(zi == tp0[int](), "zero value of int is int(0)") + assert(zf == tp0[float64](), "zero value of float64 is float64(0)") + assert(zs != tp0[string](), "zero value of string is not string(0)") + + assert(zf == -0.0, "constant -0.0 is converted to 0.0") + + assert(!tpFalse[bool](), "zero value of bool is false") + + assert(tpEmptyString[string]() == zs, `zero value of string is string("")`) + assert(len(tpEmptyString[[]byte]()) == 0, `[]byte("") is empty`) + + assert(tpNil[*int]() == nil, "nil is nil") + assert(tpNil[[]byte]() == nil, "nil is nil") +} |