func (a *compiler) checkAssign(pos token.Pos, rs []*expr, errOp, errPosName string) (*assignCompiler, bool) {
	c := &assignCompiler{
		compiler:   a,
		pos:        pos,
		rs:         rs,
		errOp:      errOp,
		errPosName: errPosName,
	}
	if len(rs) == 1 && rs[0] != nil {
		if rmt, isUnpack := rs[0].t.(*MultiType); isUnpack {
			c.rmt = rmt
			c.isUnpack = true
			return c, true
		}
	}
	rts := make([]Type, len(rs))
	ok := true
	for i, r := range rs {
		if r == nil {
			ok = false
			continue
		}
		if _, isMT := r.t.(*MultiType); isMT {
			r.diag("multi-valued expression not allowed in %s", errOp)
			ok = false
			continue
		}
		rts[i] = r.t
	}
	c.rmt = NewMultiType(rts)
	return c, ok
}
Type check the RHS of an assignment, returning a new assignCompiler
and indicating if the type check succeeded.  This always returns an
assignCompiler with rmt set, but if type checking fails, slots in
the MultiType may be nil.  If rs contains nil's, type checking will
fail and these expressions given a nil type.
package eval
import (
	"errors"
	"fmt"
	"go/ast"
	"go/token"
	"log"
	"math/big"
	"strconv"
	"strings"
)
var (
	idealZero = big.NewInt(0)
	idealOne  = big.NewInt(1)
)
type expr struct {
	*exprInfo
	t Type
	eval interface{}
	evalMapValue func(t *Thread) (Map, interface{})
	evalAddr func(t *Thread) Value
	exec func(t *Thread)
	valType Type
	desc string
}
type exprInfo struct {
	*compiler
	pos token.Pos
}
func (a *exprInfo) newExpr(t Type, desc string) *expr {
	return &expr{exprInfo: a, t: t, desc: desc}
}
func (a *exprInfo) diag(format string, args ...interface{}) {
	a.diagAt(a.pos, format, args...)
}
func (a *exprInfo) diagOpType(op token.Token, vt Type) {
	a.diag("illegal operand type for '%v' operator\n\t%v", op, vt)
}
func (a *exprInfo) diagOpTypes(op token.Token, lt Type, rt Type) {
	a.diag("illegal operand types for '%v' operator\n\t%v\n\t%v", op, lt, rt)
}
func (a *expr) convertTo(t Type) *expr {
	if !a.t.isIdeal() {
		log.Panicf("attempted to convert from %v, expected ideal", a.t)
	}
	var rat *big.Rat
	switch a.t {
	case IdealFloatType:
		rat = a.asIdealFloat()()
		if t.isInteger() && !rat.IsInt() {
			a.diag("constant %v truncated to integer", rat.FloatString(6))
			return nil
		}
	case IdealIntType:
		i := a.asIdealInt()()
		rat = new(big.Rat).SetInt(i)
	default:
		log.Panicf("unexpected ideal type %v", a.t)
	}
	if t, ok := t.lit().(BoundedType); ok {
		if rat.Cmp(t.minVal()) < 0 {
			a.diag("constant %v underflows %v", rat.FloatString(6), t)
			return nil
		}
		if rat.Cmp(t.maxVal()) > 0 {
			a.diag("constant %v overflows %v", rat.FloatString(6), t)
			return nil
		}
	}
	res := a.newExpr(t, a.desc)
	switch t := t.lit().(type) {
	case *uintType:
		n, d := rat.Num(), rat.Denom()
		f := new(big.Int).Quo(n, d)
		f = f.Abs(f)
		v := uint64(f.Int64())
		res.eval = func(*Thread) uint64 { return v }
	case *intType:
		n, d := rat.Num(), rat.Denom()
		f := new(big.Int).Quo(n, d)
		v := f.Int64()
		res.eval = func(*Thread) int64 { return v }
	case *idealIntType:
		n, d := rat.Num(), rat.Denom()
		f := new(big.Int).Quo(n, d)
		res.eval = func() *big.Int { return f }
	case *floatType:
		n, d := rat.Num(), rat.Denom()
		v := float64(n.Int64()) / float64(d.Int64())
		res.eval = func(*Thread) float64 { return v }
	case *idealFloatType:
		res.eval = func() *big.Rat { return rat }
	default:
		log.Panicf("cannot convert to type %T", t)
	}
	return res
}
func (a *expr) convertToInt(max int64, negErr string, errOp string) *expr {
	switch a.t.lit().(type) {
	case *idealIntType:
		val := a.asIdealInt()()
		if negErr != "" && val.Sign() < 0 {
			a.diag("negative %s: %s", negErr, val)
			return nil
		}
		bound := max
		if negErr == "slice" {
			bound++
		}
		if max != -1 && val.Cmp(big.NewInt(bound)) >= 0 {
			a.diag("index %s exceeds length %d", val, max)
			return nil
		}
		return a.convertTo(IntType)
	case *uintType:
		na := a.newExpr(IntType, a.desc)
		af := a.asUint()
		na.eval = func(t *Thread) int64 { return int64(af(t)) }
		return na
	case *intType:
		return a
	}
	a.diag("illegal operand type for %s\n\t%v", errOp, a.t)
	return nil
}
func (a *expr) derefArray() *expr {
	if pt, ok := a.t.lit().(*PtrType); ok {
		if _, ok := pt.Elem.lit().(*ArrayType); ok {
			deref := a.compileStarExpr(a)
			if deref == nil {
				log.Panicf("failed to dereference *array")
			}
			return deref
		}
	}
	return a
}
func (a *expr) asValue() func(t *Thread) Value {
	var fct func(t *Thread) Value
	switch ty := a.t.lit().(type) {
	case *boolType:
		fct = func(t *Thread) Value {
			b := boolV(a.asBool()(t))
			var val Value = &b
			return val
		}
	case *uintType:
		v := a.asUint()
		switch ty.Bits {
		case 0:
			switch ty.Ptr {
			case true:
				fct = func(t *Thread) Value {
					vv := uintptrV(v(t))
					var val Value = &vv
					return val
				}
			case false:
				fct = func(t *Thread) Value {
					vv := uintV(v(t))
					var val Value = &vv
					return val
				}
			}
		case 8:
			fct = func(t *Thread) Value {
				vv := uint8V(v(t))
				return &vv
			}
		case 16:
			fct = func(t *Thread) Value {
				vv := uint16V(v(t))
				return &vv
			}
		case 32:
			fct = func(t *Thread) Value {
				vv := uint32V(v(t))
				return &vv
			}
		case 64:
			fct = func(t *Thread) Value {
				vv := uint64V(v(t))
				return &vv
			}
		}
	case *intType:
		v := a.asInt()
		switch ty.Bits {
		case 0:
			fct = func(t *Thread) Value {
				vv := intV(v(t))
				return &vv
			}
		case 8:
			fct = func(t *Thread) Value {
				vv := int8V(v(t))
				return &vv
			}
		case 16:
			fct = func(t *Thread) Value {
				vv := int16V(v(t))
				return &vv
			}
		case 32:
			fct = func(t *Thread) Value {
				vv := int32V(v(t))
				return &vv
			}
		case 64:
			fct = func(t *Thread) Value {
				vv := int64V(v(t))
				return &vv
			}
		}
	case *idealIntType:
		fct = func(t *Thread) Value {
			v := a.asIdealInt()()
			vv := idealIntV{v}
			return &vv
		}
	case *floatType:
		v := a.asFloat()
		switch ty.Bits {
		case 32:
			fct = func(t *Thread) Value {
				vv := float32V(v(t))
				return &vv
			}
		case 64:
			fct = func(t *Thread) Value {
				vv := float64V(v(t))
				return &vv
			}
		}
	case *idealFloatType:
		fct = func(t *Thread) Value {
			v := a.asIdealFloat()()
			vv := idealFloatV{v}
			return &vv
		}
	case *stringType:
		fct = func(t *Thread) Value {
			v := a.asString()
			vv := stringV(v(t))
			return &vv
		}
	case *ArrayType:
		fct = func(t *Thread) Value {
			v := a.asArray()
			vv := v(t).Get(t)
			return vv
		}
	case *StructType:
		fct = func(t *Thread) Value {
			v := a.asStruct()
			vv := v(t).Get(t)
			return vv
		}
	case *PtrType:
		fct = func(t *Thread) Value {
			return a.asPtr()(t)
		}
	case *packageType:
		fct = func(t *Thread) Value {
			return a.asPackage()(t)
		}
	default:
		a.diag("unhandled type: %v", ty.String())
	}
	return fct
}
func (a *expr) asPackage() func(*Thread) PackageValue {
	return a.eval.(func(*Thread) PackageValue)
}
type assignCompiler struct {
	*compiler
	pos token.Pos
	rs []*expr
	rmt *MultiType
	isUnpack bool
	allowMap bool
	isMapUnpack bool
	errOp string
	errPosName string
}