blob: 00c7165cbb7cd7045ff2c44fad498199e0f2e1d3 [file] [log] [blame]
// This interpreter test is designed to run very quickly yet provide
// some coverage of a broad selection of constructs.
//
// Validate this file with 'go run' after editing.
// TODO(adonovan): break this into small files organized by theme.
package main
import (
"fmt"
"reflect"
"strings"
)
func init() {
// Call of variadic function with (implicit) empty slice.
if x := fmt.Sprint(); x != "" {
panic(x)
}
}
type empty interface{}
type I interface {
f() int
}
type T struct{ z int }
func (t T) f() int { return t.z }
func use(interface{}) {}
var counter = 2
// Test initialization, including init blocks containing 'return'.
// Assertion is in main.
func init() {
counter *= 3
return
counter *= 3
}
func init() {
counter *= 5
return
counter *= 5
}
// Recursion.
func fib(x int) int {
if x < 2 {
return x
}
return fib(x-1) + fib(x-2)
}
func fibgen(ch chan int) {
for x := 0; x < 10; x++ {
ch <- fib(x)
}
close(ch)
}
// Goroutines and channels.
func init() {
ch := make(chan int)
go fibgen(ch)
var fibs []int
for v := range ch {
fibs = append(fibs, v)
if len(fibs) == 10 {
break
}
}
if x := fmt.Sprint(fibs); x != "[0 1 1 2 3 5 8 13 21 34]" {
panic(x)
}
}
// Test of aliasing.
func init() {
type S struct {
a, b string
}
s1 := []string{"foo", "bar"}
s2 := s1 // creates an alias
s2[0] = "wiz"
if x := fmt.Sprint(s1, s2); x != "[wiz bar] [wiz bar]" {
panic(x)
}
pa1 := &[2]string{"foo", "bar"}
pa2 := pa1 // creates an alias
pa2[0] = "wiz"
if x := fmt.Sprint(*pa1, *pa2); x != "[wiz bar] [wiz bar]" {
panic(x)
}
a1 := [2]string{"foo", "bar"}
a2 := a1 // creates a copy
a2[0] = "wiz"
if x := fmt.Sprint(a1, a2); x != "[foo bar] [wiz bar]" {
panic(x)
}
t1 := S{"foo", "bar"}
t2 := t1 // copy
t2.a = "wiz"
if x := fmt.Sprint(t1, t2); x != "{foo bar} {wiz bar}" {
panic(x)
}
}
func main() {
print() // legal
if counter != 2*3*5 {
panic(counter)
}
// Test builtins (e.g. complex) preserve named argument types.
type N complex128
var n N
n = complex(1.0, 2.0)
if n != complex(1.0, 2.0) {
panic(n)
}
if x := reflect.TypeOf(n).String(); x != "main.N" {
panic(x)
}
if real(n) != 1.0 || imag(n) != 2.0 {
panic(n)
}
// Channel + select.
ch := make(chan int, 1)
select {
case ch <- 1:
// ok
default:
panic("couldn't send")
}
if <-ch != 1 {
panic("couldn't receive")
}
// A "receive" select-case that doesn't declare its vars. (regression test)
anint := 0
ok := false
select {
case anint, ok = <-ch:
case anint = <-ch:
default:
}
_ = anint
_ = ok
// Anon structs with methods.
anon := struct{ T }{T: T{z: 1}}
if x := anon.f(); x != 1 {
panic(x)
}
var i I = anon
if x := i.f(); x != 1 {
panic(x)
}
// NB. precise output of reflect.Type.String is undefined.
if x := reflect.TypeOf(i).String(); x != "struct { main.T }" && x != "struct{main.T}" {
panic(x)
}
// fmt.
const message = "Hello, World!"
if fmt.Sprint("Hello", ", ", "World", "!") != message {
panic("oops")
}
// Type assertion.
type S struct {
f int
}
var e empty = S{f: 42}
switch v := e.(type) {
case S:
if v.f != 42 {
panic(v.f)
}
default:
panic(reflect.TypeOf(v))
}
if i, ok := e.(I); ok {
panic(i)
}
// Switch.
var x int
switch x {
case 1:
panic(x)
fallthrough
case 2, 3:
panic(x)
default:
// ok
}
// empty switch
switch {
}
// empty switch
switch {
default:
}
// empty switch
switch {
default:
fallthrough
case false:
}
// string -> []rune conversion.
use([]rune("foo"))
// Calls of form x.f().
type S2 struct {
f func() int
}
S2{f: func() int { return 1 }}.f() // field is a func value
T{}.f() // method call
i.f() // interface method invocation
(interface {
f() int
}(T{})).f() // anon interface method invocation
// Map lookup.
if v, ok := map[string]string{}["foo5"]; v != "" || ok {
panic("oops")
}
// Regression test: implicit address-taken struct literal
// inside literal map element.
_ = map[int]*struct{}{0: {}}
}
type mybool bool
func (mybool) f() {}
func init() {
type mybool bool
var b mybool
var i interface{} = b || b // result preserves types of operands
_ = i.(mybool)
i = false && b // result preserves type of "typed" operand
_ = i.(mybool)
i = b || true // result preserves type of "typed" operand
_ = i.(mybool)
}
func init() {
var x, y int
var b mybool = x == y // x==y is an untyped bool
b.f()
}
// Simple closures.
func init() {
b := 3
f := func(a int) int {
return a + b
}
b++
if x := f(1); x != 5 { // 1+4 == 5
panic(x)
}
b++
if x := f(2); x != 7 { // 2+5 == 7
panic(x)
}
if b := f(1) < 16 || f(2) < 17; !b {
panic("oops")
}
}
// Shifts.
func init() {
var i int64 = 1
var u uint64 = 1 << 32
if x := i << uint32(u); x != 1 {
panic(x)
}
if x := i << uint64(u); x != 0 {
panic(x)
}
}
// Implicit conversion of delete() key operand.
func init() {
type I interface{}
m := make(map[I]bool)
m[1] = true
m[I(2)] = true
if len(m) != 2 {
panic(m)
}
delete(m, I(1))
delete(m, 2)
if len(m) != 0 {
panic(m)
}
}
// An I->I conversion always succeeds.
func init() {
var x I
if I(x) != I(nil) {
panic("I->I conversion failed")
}
}
// An I->I type-assert fails iff the value is nil.
func init() {
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)
}
//////////////////////////////////////////////////////////////////////
// Variadic bridge methods and interface thunks.
type VT int
var vcount = 0
func (VT) f(x int, y ...string) {
vcount++
if x != 1 {
panic(x)
}
if len(y) != 2 || y[0] != "foo" || y[1] != "bar" {
panic(y)
}
}
type VS struct {
VT
}
type VI interface {
f(x int, y ...string)
}
func init() {
foobar := []string{"foo", "bar"}
var s VS
s.f(1, "foo", "bar")
s.f(1, foobar...)
if vcount != 2 {
panic("s.f not called twice")
}
fn := VI.f
fn(s, 1, "foo", "bar")
fn(s, 1, foobar...)
if vcount != 4 {
panic("I.f not called twice")
}
}
// Multiple labels on same statement.
func multipleLabels() {
var trace []int
i := 0
one:
two:
for ; i < 3; i++ {
trace = append(trace, i)
switch i {
case 0:
continue two
case 1:
i++
goto one
case 2:
break two
}
}
if x := fmt.Sprint(trace); x != "[0 1 2]" {
panic(x)
}
}
func init() {
multipleLabels()
}
func init() {
// Struct equivalence ignores blank fields.
type s struct{ x, _, z int }
s1 := s{x: 1, z: 3}
s2 := s{x: 1, z: 3}
if s1 != s2 {
panic("not equal")
}
}
func init() {
// A slice var can be compared to const []T nil.
var i interface{} = []string{"foo"}
var j interface{} = []string(nil)
if i.([]string) == nil {
panic("expected i non-nil")
}
if j.([]string) != nil {
panic("expected j nil")
}
// But two slices cannot be compared, even if one is nil.
defer func() {
r := fmt.Sprint(recover())
if !(strings.Contains(r, "compar") && strings.Contains(r, "[]string")) {
panic("want panic from slice comparison, got " + r)
}
}()
_ = i == j // interface comparison recurses on types
}
func init() {
// Regression test for SSA renaming bug.
var ints []int
for range "foo" {
var x int
x++
ints = append(ints, x)
}
if fmt.Sprint(ints) != "[1 1 1]" {
panic(ints)
}
}
// Regression test for issue 6949:
// []byte("foo") is not a constant since it allocates memory.
func init() {
var r string
for i, b := range "ABC" {
x := []byte("abc")
x[i] = byte(b)
r += string(x)
}
if r != "AbcaBcabC" {
panic(r)
}
}
// Test of 3-operand x[lo:hi:max] slice.
func init() {
s := []int{0, 1, 2, 3}
lenCapLoHi := func(x []int) [4]int { return [4]int{len(x), cap(x), x[0], x[len(x)-1]} }
if got := lenCapLoHi(s[1:3]); got != [4]int{2, 3, 1, 2} {
panic(got)
}
if got := lenCapLoHi(s[1:3:3]); got != [4]int{2, 2, 1, 2} {
panic(got)
}
max := 3
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)
}
}
var one = 1 // not a constant
// Test makeslice.
func init() {
check := func(s []string, wantLen, wantCap int) {
if len(s) != wantLen {
panic(len(s))
}
if cap(s) != wantCap {
panic(cap(s))
}
}
// SSA form:
check(make([]string, 10), 10, 10) // new([10]string)[:10]
check(make([]string, one), 1, 1) // make([]string, one, one)
check(make([]string, 0, 10), 0, 10) // new([10]string)[:0]
check(make([]string, 0, one), 0, 1) // make([]string, 0, one)
check(make([]string, one, 10), 1, 10) // new([10]string)[:one]
check(make([]string, one, one), 1, 1) // make([]string, one, one)
}
// Test that a nice error is issued by indirection wrappers.
func init() {
var ptr *T
var i I = ptr
defer func() {
r := fmt.Sprint(recover())
// Exact error varies by toolchain:
if r != "runtime error: value method (main.T).f called using nil *main.T pointer" &&
r != "value method (main.T).f called using nil *main.T pointer" {
panic("want panic from call with nil receiver, got " + r)
}
}()
i.f()
panic("unreachable")
}
// Regression test for a subtle bug in which copying values would causes
// subcomponents of aggregate variables to change address, breaking
// aliases.
func init() {
type T struct{ f int }
var x T
p := &x.f
x = T{}
*p = 1
if x.f != 1 {
panic("lost store")
}
if p != &x.f {
panic("unstable address")
}
}