// 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")
	}
}
