package interp

import (
	
	
	
	
	
)

type opPredicates map[action]func(reflect.Type) bool

// typecheck handles all type checking following "go/types" logic.
//
// Due to variant type systems (itype vs reflect.Type) a single
// type system should used, namely reflect.Type with exception
// of the untyped flag on itype.
type typecheck struct {
	scope *scope
}

// op type checks an expression against a set of expression predicates.
func ( typecheck) ( opPredicates,  action, ,  *node,  reflect.Type) error {
	if  := [];  != nil {
		if !() {
			return .cfgErrorf("invalid operation: operator %v not defined on %s", .action, .typ.id())
		}
	} else {
		return .cfgErrorf("invalid operation: unknown operator %v", .action)
	}
	return nil
}

// assignment checks if n can be assigned to typ.
//
// Use typ == nil to indicate assignment to an untyped blank identifier.
func ( typecheck) ( *node,  *itype,  string) error {
	if .typ == nil {
		return .cfgErrorf("invalid type in %s", )
	}
	if .typ.untyped {
		if  == nil || isInterface() {
			if  == nil && .typ.cat == nilT {
				return .cfgErrorf("use of untyped nil in %s", )
			}
			 = .typ.defaultType(.rval, .scope)
		}
		if  := .convertUntyped(, );  != nil {
			return 
		}
	}

	if  == nil {
		return nil
	}

	if !.typ.assignableTo() && .str != "*unsafe2.dummy" {
		if  == "" {
			return .cfgErrorf("cannot use type %s as type %s", .typ.id(), .id())
		}
		return .cfgErrorf("cannot use type %s as type %s in %s", .typ.id(), .id(), )
	}
	return nil
}

// assignExpr type checks an assign expression.
//
// This is done per pair of assignments.
func ( typecheck) (, ,  *node) error {
	if .action == aAssign {
		 := .anc.kind == constDecl
		if ! {
			// var operations must be typed
			.typ = .typ.defaultType(.rval, .scope)
		}

		return .assignment(, .typ, "assignment")
	}

	// assignment operations.
	if .nleft > 1 || .nright > 1 {
		return .cfgErrorf("assignment operation %s requires single-valued expressions", .action)
	}

	return .binaryExpr()
}

// addressExpr type checks a unary address expression.
func ( typecheck) ( *node) error {
	 := .child[0]
	 := false
	for ! {
		switch .kind {
		case parenExpr:
			 = .child[0]
			continue
		case selectorExpr:
			 = .child[1]
			continue
		case starExpr:
			 = .child[0]
			continue
		case indexExpr, sliceExpr:
			 := .child[0]
			if isArray(.typ) || isMap(.typ) {
				 = 
				 = true
				continue
			}
		case compositeLitExpr, identExpr:
			 = true
			continue
		}
		return .cfgErrorf("invalid operation: cannot take address of %s [kind: %s]", .typ.id(), kinds[.kind])
	}
	return nil
}

// starExpr type checks a star expression on a variable.
func ( typecheck) ( *node) error {
	if .typ.TypeOf().Kind() != reflect.Ptr {
		return .cfgErrorf("invalid operation: cannot indirect %q", .name())
	}
	return nil
}

var unaryOpPredicates = opPredicates{
	aInc:    isNumber,
	aDec:    isNumber,
	aPos:    isNumber,
	aNeg:    isNumber,
	aBitNot: isInt,
	aNot:    isBoolean,
}

// unaryExpr type checks a unary expression.
func ( typecheck) ( *node) error {
	 := .child[0]
	if isBlank() {
		return .cfgErrorf("cannot use _ as value")
	}
	 := .typ.TypeOf()

	if .action == aRecv {
		if !isChan(.typ) {
			return .cfgErrorf("invalid operation: cannot receive from non-channel %s", .typ.id())
		}
		if isSendChan(.typ) {
			return .cfgErrorf("invalid operation: cannot receive from send-only channel %s", .typ.id())
		}
		return nil
	}

	return .op(unaryOpPredicates, .action, , , )
}

// shift type checks a shift binary expression.
func ( typecheck) ( *node) error {
	,  := .child[0], .child[1]
	,  := .typ.TypeOf(), .typ.TypeOf()

	var  constant.Value
	if .typ.untyped && .rval.IsValid() {
		 = constant.ToInt(.rval.Interface().(constant.Value))
		.rval = reflect.ValueOf()
	}

	if !(.typ.untyped &&  != nil && .Kind() == constant.Int || isInt()) {
		return .cfgErrorf("invalid operation: shift of type %v", .typ.id())
	}

	switch {
	case .typ.untyped:
		if  := .convertUntyped(, .scope.getType("uint"));  != nil {
			return .cfgErrorf("invalid operation: shift count type %v, must be integer", .typ.id())
		}
	case isInt():
		// nothing to do
	default:
		return .cfgErrorf("invalid operation: shift count type %v, must be integer", .typ.id())
	}
	return nil
}

// comparison type checks a comparison binary expression.
func ( typecheck) ( *node) error {
	,  := .child[0].typ, .child[1].typ

	if !.assignableTo() && !.assignableTo() {
		return .cfgErrorf("invalid operation: mismatched types %s and %s", .id(), .id())
	}

	 := false

	if !isInterface() && !isInterface() && !.isNil() && !.isNil() && .untyped == .untyped && .id() != .id() && !typeDefined(, ) {
		// Non interface types must be really equals.
		return .cfgErrorf("invalid operation: mismatched types %s and %s", .id(), .id())
	}

	switch .action {
	case aEqual, aNotEqual:
		 = .comparable() && .comparable() || .isNil() && .hasNil() || .isNil() && .hasNil()
	case aLower, aLowerEqual, aGreater, aGreaterEqual:
		 = .ordered() && .ordered()
	}
	if ! {
		 := 
		if .isNil() {
			 = 
		}
		return .cfgErrorf("invalid operation: operator %v not defined on %s", .action, .id())
	}
	return nil
}

var binaryOpPredicates = opPredicates{
	aAdd: func( reflect.Type) bool { return isNumber() || isString() },
	aSub: isNumber,
	aMul: isNumber,
	aQuo: isNumber,
	aRem: isInt,

	aAnd:    isInt,
	aOr:     isInt,
	aXor:    isInt,
	aAndNot: isInt,

	aLand: isBoolean,
	aLor:  isBoolean,
}

// binaryExpr type checks a binary expression.
func ( typecheck) ( *node) error {
	,  := .child[0], .child[1]

	if isBlank() || isBlank() {
		return .cfgErrorf("cannot use _ as value")
	}

	 := .action
	if isAssignAction() {
		--
	}

	if isShiftAction() {
		return .shift()
	}

	switch .action {
	case aAdd:
		if .typ == nil {
			break
		}
		// Catch mixing string and number for "+" operator use.
		, ,  := isNumber(.typ.TypeOf()), isNumber(.typ.TypeOf()), isNumber(.typ.TypeOf())
		if  !=  ||  !=  {
			return .cfgErrorf("cannot use type %s as type %s in assignment", .typ.id(), .typ.id())
		}
	case aRem:
		if zeroConst() {
			return .cfgErrorf("invalid operation: division by zero")
		}
	case aQuo:
		if zeroConst() {
			return .cfgErrorf("invalid operation: division by zero")
		}
		if .rval.IsValid() && .rval.IsValid() {
			// Avoid constant conversions below to ensure correct constant integer quotient.
			return nil
		}
	}

	_ = .convertUntyped(, .typ)
	_ = .convertUntyped(, .typ)

	if isComparisonAction() {
		return .comparison()
	}

	if !.typ.equals(.typ) {
		return .cfgErrorf("invalid operation: mismatched types %s and %s", .typ.id(), .typ.id())
	}

	 := .typ.TypeOf()

	return .op(binaryOpPredicates, , , , )
}

func zeroConst( *node) bool {
	return .typ.untyped && constant.Sign(.rval.Interface().(constant.Value)) == 0
}

func ( typecheck) ( *node,  int) error {
	if  := .convertUntyped(, .scope.getType("int"));  != nil {
		return 
	}

	if !isInt(.typ.TypeOf()) {
		return .cfgErrorf("index %s must be integer", .typ.id())
	}

	if !.rval.IsValid() ||  < 1 {
		return nil
	}

	if int(vInt(.rval)) >=  {
		return .cfgErrorf("index %s is out of bounds", .typ.id())
	}

	return nil
}

// arrayLitExpr type checks an array composite literal expression.
func ( typecheck) ( []*node,  *itype) error {
	 := .cat
	 := .length
	 = .val
	 := make(map[int]bool, len())
	 := 0
	for ,  := range  {
		 := 
		switch {
		case .kind == keyValueExpr:
			if  := .index(.child[0], );  != nil {
				return .cfgErrorf("index %s must be integer constant", .child[0].typ.id())
			}
			 = .child[1]
			 = int(vInt(.child[0].rval))
		case  == arrayT &&  >= :
			return .cfgErrorf("index %d is out of bounds (>= %d)", , )
		}

		if [] {
			return .cfgErrorf("duplicate index %d in array or slice literal", )
		}
		[] = true
		++

		if  := .assignment(, , "array or slice literal");  != nil {
			return 
		}
	}
	return nil
}

// mapLitExpr type checks an map composite literal expression.
func ( typecheck) ( []*node, ,  *itype) error {
	 := make(map[interface{}]bool, len())
	for ,  := range  {
		if .kind != keyValueExpr {
			return .cfgErrorf("missing key in map literal")
		}

		,  := .child[0], .child[1]
		if  := .assignment(, , "map literal");  != nil {
			return 
		}

		if .rval.IsValid() {
			 := .rval.Interface()
			if [] {
				return .cfgErrorf("duplicate key %s in map literal", )
			}
			[] = true
		}

		if  := .assignment(, , "map literal");  != nil {
			return 
		}
	}
	return nil
}

// structLitExpr type checks a struct composite literal expression.
func ( typecheck) ( []*node,  *itype) error {
	if len() == 0 {
		return nil
	}

	if [0].kind == keyValueExpr {
		// All children must be keyValueExpr
		 := make([]bool, len(.field))
		for ,  := range  {
			if .kind != keyValueExpr {
				return .cfgErrorf("mixture of field:value and value elements in struct literal")
			}

			,  := .child[0], .child[1]
			 := .ident
			if  == "" {
				return .cfgErrorf("invalid field name %s in struct literal", .typ.id())
			}
			 := .fieldIndex()
			if  < 0 {
				return .cfgErrorf("unknown field %s in struct literal", )
			}
			 := .field[]

			if  := .assignment(, .typ, "struct literal");  != nil {
				return 
			}

			if [] {
				return .cfgErrorf("duplicate field name %s in struct literal", )
			}
			[] = true
		}
		return nil
	}

	// No children can be keyValueExpr
	for ,  := range  {
		if .kind == keyValueExpr {
			return .cfgErrorf("mixture of field:value and value elements in struct literal")
		}

		if  >= len(.field) {
			return .cfgErrorf("too many values in struct literal")
		}
		 := .field[]
		// TODO(nick): check if this field is not exported and in a different package.

		if  := .assignment(, .typ, "struct literal");  != nil {
			return 
		}
	}
	if len() < len(.field) {
		return [len()-1].cfgErrorf("too few values in struct literal")
	}
	return nil
}

// structBinLitExpr type checks a struct composite literal expression on a binary type.
func ( typecheck) ( []*node,  reflect.Type) error {
	if len() == 0 {
		return nil
	}

	if [0].kind == keyValueExpr {
		// All children must be keyValueExpr
		 := make(map[string]bool, .NumField())
		for ,  := range  {
			if .kind != keyValueExpr {
				return .cfgErrorf("mixture of field:value and value elements in struct literal")
			}

			,  := .child[0], .child[1]
			 := .ident
			if  == "" {
				return .cfgErrorf("invalid field name %s in struct literal", .typ.id())
			}
			,  := .FieldByName()
			if ! {
				return .cfgErrorf("unknown field %s in struct literal", )
			}

			if  := .assignment(, valueTOf(.Type), "struct literal");  != nil {
				return 
			}

			if [.Name] {
				return .cfgErrorf("duplicate field name %s in struct literal", )
			}
			[.Name] = true
		}
		return nil
	}

	// No children can be keyValueExpr
	for ,  := range  {
		if .kind == keyValueExpr {
			return .cfgErrorf("mixture of field:value and value elements in struct literal")
		}

		if  >= .NumField() {
			return .cfgErrorf("too many values in struct literal")
		}
		 := .Field()
		if !canExport(.Name) {
			return .cfgErrorf("implicit assignment to unexported field %s in %s literal", .Name, )
		}

		if  := .assignment(, valueTOf(.Type), "struct literal");  != nil {
			return 
		}
	}
	if len() < .NumField() {
		return [len()-1].cfgErrorf("too few values in struct literal")
	}
	return nil
}

// sliceExpr type checks a slice expression.
func ( typecheck) ( *node) error {
	for ,  := range .child {
		if isBlank() {
			return .cfgErrorf("cannot use _ as value")
		}
	}

	,  := .child[0], .child[1:]

	 := .typ.TypeOf()
	var , ,  *node
	if len() >= 1 {
		if .action == aSlice {
			 = [0]
		} else {
			 = [0]
		}
	}
	if len() >= 2 {
		if .action == aSlice {
			 = [1]
		} else {
			 = [1]
		}
	}
	if len() == 3 && .action == aSlice {
		 = [2]
	}

	 := -1
	 := false
	switch .Kind() {
	case reflect.String:
		 = true
		if .rval.IsValid() {
			 = len(vString(.rval))
		}
		if  != nil {
			return .cfgErrorf("invalid operation: 3-index slice of string")
		}
	case reflect.Array:
		 = true
		 = .Len()
		// TODO(marc): check addressable status of array object (i.e. composite arrays are not).
	case reflect.Slice:
		 = true
	case reflect.Ptr:
		if .Elem().Kind() == reflect.Array {
			 = true
			 = .Elem().Len()
		}
	}
	if ! {
		return .cfgErrorf("cannot slice type %s", .typ.id())
	}

	var  [3]int64
	for ,  := range []*node{, , } {
		 := int64(-1)
		switch {
		case  != nil:
			 := -1
			if  >= 0 {
				 =  + 1
			}
			if  := .index(, );  != nil {
				return 
			}
			if .rval.IsValid() {
				 = vInt(.rval)
			}
		case  == 0:
			 = 0
		case  >= 0:
			 = int64()
		}
		[] = 
	}

	for ,  := range [:len()-1] {
		if  <= 0 {
			continue
		}
		for ,  := range [+1:] {
			if  < 0 ||  <=  {
				continue
			}
			return .cfgErrorf("invalid index values, must be low <= high <= max")
		}
	}
	return nil
}

// typeAssertionExpr type checks a type assert expression.
func ( typecheck) ( *node,  *itype) error {
	// TODO(nick): This type check is not complete and should be revisited once
	// https://github.com/golang/go/issues/39717 lands. It is currently impractical to
	// type check Named types as they cannot be asserted.

	if  := .typ.TypeOf(); .Kind() != reflect.Interface &&  != valueInterfaceType {
		return .cfgErrorf("invalid type assertion: non-interface type %s on left", .typ.id())
	}
	 := .typ.methods()
	if len() == 0 {
		// Empty interface must be a dynamic check.
		return nil
	}

	if isInterface() {
		// Asserting to an interface is a dynamic check as we must look to the
		// underlying struct.
		return nil
	}

	for  := range  {
		 := lookupFieldOrMethod(.typ, )
		 := lookupFieldOrMethod(, )
		if  == nil {
			// This should not be possible.
			continue
		}
		if  == nil {
			// Lookup for non-exported methods is impossible
			// for bin types, ignore them as they can't be used
			// directly by the interpreted programs.
			if !token.IsExported() && isBin() {
				continue
			}
			return .cfgErrorf("impossible type assertion: %s does not implement %s (missing %v method)", .id(), .typ.id(), )
		}
		if .recv != nil && .recv.TypeOf().Kind() == reflect.Ptr && .TypeOf().Kind() != reflect.Ptr {
			return .cfgErrorf("impossible type assertion: %s does not implement %s as %q method has a pointer receiver", .id(), .typ.id(), )
		}

		if .cat != funcT || .cat != funcT {
			// It only makes sense to compare in/out parameter types if both types are functions.
			continue
		}

		 := .cfgErrorf("impossible type assertion: %s does not implement %s", .id(), .typ.id())
		if .numIn() != .numIn() || .numOut() != .numOut() {
			return 
		}
		for  := 0;  < .numIn(); ++ {
			if !.in().equals(.in()) {
				return 
			}
		}
		for  := 0;  < .numOut(); ++ {
			if !.out().equals(.out()) {
				return 
			}
		}
	}
	return nil
}

// conversion type checks the conversion of n to typ.
func ( typecheck) ( *node,  *itype) error {
	var  constant.Value
	if .rval.IsValid() {
		if ,  := .rval.Interface().(constant.Value);  {
			 = 
		}
	}

	var  bool
	switch {
	case  != nil && isConstType():
		switch  := .TypeOf(); {
		case representableConst(, ):
			 = true
		case isInt(.typ.TypeOf()) && isString():
			 := int64(-1)
			if ,  := constant.Int64Val();  {
				 = 
			}
			.rval = reflect.ValueOf(constant.MakeString(string(rune())))
			 = true
		}

	case .typ.convertibleTo():
		 = true
	}
	if ! {
		return .cfgErrorf("cannot convert expression of type %s to type %s", .typ.id(), .id())
	}
	if !.typ.untyped ||  == nil {
		return nil
	}
	if isInterface() || !isConstType() {
		 = .typ.defaultType(.rval, .scope)
	}
	return .convertUntyped(, )
}

type param struct {
	nod *node
	typ *itype
}

func ( param) () *itype {
	if .typ != nil {
		return .typ
	}
	return .nod.typ
}

// unpackParams unpacks child parameters into a slice of param.
// If there is only 1 child and it is a callExpr with an n-value return,
// the return types are returned, otherwise the original child nodes are
// returned with nil typ.
func ( typecheck) ( []*node) ( []param) {
	if len() == 1 && isCall([0]) && [0].child[0].typ.numOut() > 1 {
		 := [0]
		 := [0].child[0].typ
		for  := 0;  < .numOut(); ++ {
			 = append(, param{nod: , typ: .out()})
		}
		return 
	}

	for ,  := range  {
		 = append(, param{nod: })
	}
	return 
}

var builtinFuncs = map[string]struct {
	args     int
	variadic bool
}{
	bltnAlignof:  {args: 1, variadic: false},
	bltnAppend:   {args: 1, variadic: true},
	bltnCap:      {args: 1, variadic: false},
	bltnClose:    {args: 1, variadic: false},
	bltnComplex:  {args: 2, variadic: false},
	bltnImag:     {args: 1, variadic: false},
	bltnCopy:     {args: 2, variadic: false},
	bltnDelete:   {args: 2, variadic: false},
	bltnLen:      {args: 1, variadic: false},
	bltnMake:     {args: 1, variadic: true},
	bltnNew:      {args: 1, variadic: false},
	bltnOffsetof: {args: 1, variadic: false},
	bltnPanic:    {args: 1, variadic: false},
	bltnPrint:    {args: 0, variadic: true},
	bltnPrintln:  {args: 0, variadic: true},
	bltnReal:     {args: 1, variadic: false},
	bltnRecover:  {args: 0, variadic: false},
	bltnSizeof:   {args: 1, variadic: false},
}

func ( typecheck) ( string,  *node,  []*node,  bool) error {
	 := builtinFuncs[]
	if  &&  != bltnAppend {
		return .cfgErrorf("invalid use of ... with builtin %s", )
	}

	var  []param
	 := len()
	switch  {
	case bltnMake, bltnNew:
		// Special param handling
	default:
		 = .unpackParams()
		 = len()
	}

	if  < .args {
		return .cfgErrorf("not enough arguments in call to %s", )
	} else if !.variadic &&  > .args {
		return .cfgErrorf("too many arguments for %s", )
	}

	switch  {
	case bltnAppend:
		 := [0].Type()
		 := .TypeOf()
		if  == nil || .Kind() != reflect.Slice {
			return [0].nod.cfgErrorf("first argument to append must be slice; have %s", .id())
		}

		if  == 1 {
			return nil
		}
		// Special case append([]byte, "test"...) is allowed.
		 := [1].Type()
		if  == 2 &&  && .Elem().Kind() == reflect.Uint8 && .TypeOf().Kind() == reflect.String {
			if .untyped {
				return .convertUntyped([1].nod, .scope.getType("string"))
			}
			return nil
		}

		 := &node{
			typ: &itype{
				cat: funcT,
				arg: []*itype{
					,
					{cat: variadicT, val: valueTOf(.Elem())},
				},
				ret: []*itype{},
			},
			ident: "append",
		}
		return .arguments(, , , )
	case bltnCap, bltnLen:
		 := arrayDeref([0].Type())
		 := false
		switch .TypeOf().Kind() {
		case reflect.Array, reflect.Slice, reflect.Chan:
			 = true
		case reflect.String, reflect.Map:
			 =  == bltnLen
		}
		if ! {
			return [0].nod.cfgErrorf("invalid argument for %s", )
		}
	case bltnClose:
		 := [0]
		 := .Type()
		 := .TypeOf()
		if .Kind() != reflect.Chan {
			return .nod.cfgErrorf("invalid operation: non-chan type %s", .nod.typ.id())
		}
		if .ChanDir() == reflect.RecvDir {
			return .nod.cfgErrorf("invalid operation: cannot close receive-only channel")
		}
	case bltnComplex:
		var  error
		,  := [0], [1]
		,  := .Type(), .Type()
		switch {
		case .untyped && !.untyped:
			 = .convertUntyped(.nod, )
		case !.untyped && .untyped:
			 = .convertUntyped(.nod, )
		case .untyped && .untyped:
			 := untypedFloat(nil)
			 = .convertUntyped(.nod, )
			if  != nil {
				break
			}
			 = .convertUntyped(.nod, )
		}
		if  != nil {
			return 
		}

		// check we have the correct types after conversion.
		,  = .Type(), .Type()
		if !.equals() {
			return .cfgErrorf("invalid operation: mismatched types %s and %s", .id(), .id())
		}
		if !isFloat(.TypeOf()) {
			return .cfgErrorf("invalid operation: arguments have type %s, expected floating-point", .id())
		}
	case bltnImag, bltnReal:
		 := [0]
		 := .Type()
		if .untyped {
			if  := .convertUntyped(.nod, untypedComplex(nil));  != nil {
				return 
			}
		}
		 = .Type()
		if !isComplex(.TypeOf()) {
			return .nod.cfgErrorf("invalid argument type %s for %s", .id(), )
		}
	case bltnCopy:
		,  := [0].Type(), [1].Type()
		var ,  reflect.Type
		if  := .TypeOf(); .Kind() == reflect.Slice {
			 = .Elem()
		}

		switch  := .TypeOf(); .Kind() {
		case reflect.String:
			 = reflect.TypeOf(byte(1))
		case reflect.Slice:
			 = .Elem()
		}

		if  == nil ||  == nil {
			return .cfgErrorf("copy expects slice arguments")
		}
		if !reflect.DeepEqual(, ) {
			return .cfgErrorf("arguments to copy have different element types %s and %s", .id(), .id())
		}
	case bltnDelete:
		 := [0].Type()
		if .TypeOf().Kind() != reflect.Map {
			return [0].nod.cfgErrorf("first argument to delete must be map; have %s", .id())
		}
		 := [1].Type()
		if .key != nil && !.assignableTo(.key) {
			return [1].nod.cfgErrorf("cannot use %s as type %s in delete", .id(), .key.id())
		}
	case bltnMake:
		var  int
		switch [0].typ.TypeOf().Kind() {
		case reflect.Slice:
			 = 2
		case reflect.Map, reflect.Chan:
			 = 1
		default:
			return [0].cfgErrorf("cannot make %s; type must be slice, map, or channel", [0].typ.id())
		}
		if  <  {
			return .cfgErrorf("not enough arguments in call to make")
		} else if  > +1 {
			return .cfgErrorf("too many arguments for make")
		}

		var  []int
		for ,  := range [1:] {
			if  := .index(, -1);  != nil {
				return 
			}
			if .rval.IsValid() {
				 = append(, int(vInt(.rval)))
			}
		}
		for len() == 2 && [0] > [1] {
			return .cfgErrorf("len larger than cap in make")
		}

	case bltnPanic:
		return .assignment([0].nod, .scope.getType("interface{}"), "argument to panic")
	case bltnPrint, bltnPrintln:
		for ,  := range  {
			if .typ != nil {
				continue
			}

			if  := .assignment(.nod, nil, "argument to "+);  != nil {
				return 
			}
		}
	case bltnRecover, bltnNew, bltnAlignof, bltnOffsetof, bltnSizeof:
		// Nothing to do.
	default:
		return .cfgErrorf("unsupported builtin %s", )
	}
	return nil
}

// arrayDeref returns A if typ is *A, otherwise typ.
func arrayDeref( *itype) *itype {
	if .cat == valueT && .TypeOf().Kind() == reflect.Ptr {
		 := .TypeOf()
		if .Elem().Kind() == reflect.Array {
			return valueTOf(.Elem())
		}
		return 
	}

	if .cat == ptrT && .val.cat == arrayT {
		return .val
	}
	return 
}

// arguments type checks the call expression arguments.
func ( typecheck) ( *node,  []*node,  *node,  bool) error {
	 := .unpackParams()
	 := len()
	if  {
		if !.typ.isVariadic() {
			return .cfgErrorf("invalid use of ..., corresponding parameter is non-variadic")
		}
		if len() >  {
			return [0].cfgErrorf("cannot use ... with %d-valued %s", [0].child[0].typ.numOut(), [0].child[0].typ.id())
		}
	}

	var  int
	for ,  := range  {
		 :=  == -1 && 
		if  := .argument(, .typ, , , );  != nil {
			return 
		}
		++
	}

	if .typ.isVariadic() {
		++
	}
	if  < .typ.numIn() {
		return .cfgErrorf("not enough arguments in call to %s", .name())
	}
	return nil
}

func ( typecheck) ( param,  *itype, ,  int,  bool) error {
	 := getArg(, )
	if  == nil {
		return .nod.cfgErrorf("too many arguments")
	}

	if .typ == nil && isCall(.nod) && .nod.child[0].typ.numOut() != 1 {
		if  == 1 {
			return .nod.cfgErrorf("cannot use %s as type %s", .nod.child[0].typ.id(), getArgsID())
		}
		return .nod.cfgErrorf("cannot use %s as type %s", .nod.child[0].typ.id(), .id())
	}

	if  {
		if  != .numIn()-1 {
			return .nod.cfgErrorf("can only use ... with matching parameter")
		}
		 := .Type().TypeOf()
		if .Kind() != reflect.Slice || !(valueTOf(.Elem())).assignableTo() {
			return .nod.cfgErrorf("cannot use %s as type %s", .nod.typ.id(), (sliceOf()).id())
		}
		return nil
	}

	if .typ != nil {
		if !.typ.assignableTo() {
			return .nod.cfgErrorf("cannot use %s as type %s", .nod.child[0].typ.id(), getArgsID())
		}
		return nil
	}
	return .assignment(.nod, , "")
}

func getArg( *itype,  int) *itype {
	 := .numIn()
	switch {
	case .isVariadic() &&  >= -1:
		 := .in( - 1).val
		return 
	case  < :
		return .in()
	case .cat == valueT &&  < .rtype.NumIn():
		return valueTOf(.rtype.In())
	default:
		return nil
	}
}

func getArgsID( *itype) string {
	 := "("
	for ,  := range .arg {
		if  > 0 {
			 += ","
		}
		 += .id()
	}
	 += ")"
	return 
}

var errCantConvert = errors.New("cannot convert")

func ( typecheck) ( *node,  *itype) error {
	if .typ == nil || !.typ.untyped ||  == nil {
		return nil
	}

	 := .cfgErrorf("cannot convert %s to %s", .typ.id(), .id())

	,  := .typ.TypeOf(), .TypeOf()
	if .untyped {
		// Both n and target are untyped.
		,  := .Kind(), .Kind()
		if isNumber() && isNumber() {
			if  <  {
				.typ = 
			}
		} else if  !=  {
			return 
		}
		return nil
	}

	var (
		 *itype
		 reflect.Type
		  error
	)
	switch {
	case .isNil() && .typ.isNil():
		.typ = 
		return nil
	case isNumber() || isString() || isBoolean():
		 = 
		 = 
	case isInterface():
		if .typ.isNil() {
			return nil
		}
		if len(.typ.methods()) > 0 { // untyped cannot be set to iface
			return 
		}
		 = .typ.defaultType(.rval, .scope)
		 = 
	case isArray() || isMap() || isChan() || isFunc() || isPtr():
		// TODO(nick): above we are acting on itype, but really it is an rtype check. This is not clear which type
		// 		 	   plain we are in. Fix this later.
		if !.typ.isNil() {
			return 
		}
		return nil
	case .typ.isNil() && .id() == "unsafe.Pointer":
		.typ = 
		return nil
	default:
		return 
	}

	if  := .representable(, );  != nil {
		return 
	}
	.rval,  = .convertConst(.rval, )
	if  != nil {
		if errors.Is(, errCantConvert) {
			return 
		}
		return .cfgErrorf(.Error())
	}
	.typ = 
	return nil
}

func ( typecheck) ( *node,  reflect.Type) error {
	if !.rval.IsValid() {
		// TODO(nick): This should be an error as the const is in the frame which is undesirable.
		return nil
	}
	,  := .rval.Interface().(constant.Value)
	if ! {
		// TODO(nick): This should be an error as untyped strings and bools should be constant.Values.
		return nil
	}

	if !representableConst(, ) {
		 := .typ.TypeOf()
		if isNumber() && isNumber() {
			// numeric conversion : error msg
			//
			// integer -> integer : overflows
			// integer -> float   : overflows (actually not possible)
			// float   -> integer : truncated
			// float   -> float   : overflows
			//
			if !isInt() && isInt() {
				return .cfgErrorf("%s truncated to %s", .ExactString(), .Kind().String())
			}
			return .cfgErrorf("%s overflows %s", .ExactString(), .Kind().String())
		}
		return .cfgErrorf("cannot convert %s to %s", .ExactString(), .Kind().String())
	}
	return nil
}

func ( typecheck) ( reflect.Value,  reflect.Type) (reflect.Value, error) {
	if !.IsValid() {
		// TODO(nick): This should be an error as the const is in the frame which is undesirable.
		return , nil
	}
	,  := .Interface().(constant.Value)
	if ! {
		// TODO(nick): This should be an error as untyped strings and bools should be constant.Values.
		return , nil
	}

	 := .Kind()
	switch  {
	case reflect.Bool:
		 = reflect.ValueOf(constant.BoolVal())
	case reflect.String:
		 = reflect.ValueOf(constant.StringVal())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		,  := constant.Int64Val(constant.ToInt())
		 = reflect.ValueOf().Convert()
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		,  := constant.Uint64Val(constant.ToInt())
		 = reflect.ValueOf().Convert()
	case reflect.Float32:
		,  := constant.Float32Val(constant.ToFloat())
		 = reflect.ValueOf()
	case reflect.Float64:
		,  := constant.Float64Val(constant.ToFloat())
		 = reflect.ValueOf()
	case reflect.Complex64:
		,  := constant.Float32Val(constant.Real())
		,  := constant.Float32Val(constant.Imag())
		 = reflect.ValueOf(complex(, )).Convert()
	case reflect.Complex128:
		,  := constant.Float64Val(constant.Real())
		,  := constant.Float64Val(constant.Imag())
		 = reflect.ValueOf(complex(, )).Convert()
	default:
		return , errCantConvert
	}
	return , nil
}

var bitlen = [...]int{
	reflect.Int:     64,
	reflect.Int8:    8,
	reflect.Int16:   16,
	reflect.Int32:   32,
	reflect.Int64:   64,
	reflect.Uint:    64,
	reflect.Uint8:   8,
	reflect.Uint16:  16,
	reflect.Uint32:  32,
	reflect.Uint64:  64,
	reflect.Uintptr: 64,
}

func representableConst( constant.Value,  reflect.Type) bool {
	switch {
	case isInt():
		 := constant.ToInt()
		if .Kind() != constant.Int {
			return false
		}
		switch .Kind() {
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if ,  := constant.Int64Val(); ! {
				return false
			}
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			if ,  := constant.Uint64Val(); ! {
				return false
			}
		default:
			return false
		}
		return constant.BitLen() <= bitlen[.Kind()]
	case isFloat():
		 := constant.ToFloat()
		if .Kind() != constant.Float {
			return false
		}
		switch .Kind() {
		case reflect.Float32:
			,  := constant.Float32Val()
			return !math.IsInf(float64(), 0)
		case reflect.Float64:
			,  := constant.Float64Val()
			return !math.IsInf(, 0)
		default:
			return false
		}
	case isComplex():
		 := constant.ToComplex()
		if .Kind() != constant.Complex {
			return false
		}
		switch .Kind() {
		case reflect.Complex64:
			,  := constant.Float32Val(constant.Real())
			,  := constant.Float32Val(constant.Imag())
			return !math.IsInf(float64(), 0) && !math.IsInf(float64(), 0)
		case reflect.Complex128:
			,  := constant.Float64Val(constant.Real())
			,  := constant.Float64Val(constant.Imag())
			return !math.IsInf(, 0) && !math.IsInf(, 0)
		default:
			return false
		}
	case isString():
		return .Kind() == constant.String
	case isBoolean():
		return .Kind() == constant.Bool
	default:
		return false
	}
}

func isShiftAction( action) bool {
	switch  {
	case aShl, aShr, aShlAssign, aShrAssign:
		return true
	}
	return false
}

func isComparisonAction( action) bool {
	switch  {
	case aEqual, aNotEqual, aGreater, aGreaterEqual, aLower, aLowerEqual:
		return true
	}
	return false
}