535 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			535 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Go
		
	
	
	
| // 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"
 | |
| )
 | |
| 
 | |
| 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.Sprintf("%s, %s!", "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 r != "runtime error: comparing uncomparable type []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 *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")
 | |
| 	}
 | |
| }
 |