func checkConvertible(from, to types.Type) error {
	if types.ConvertibleTo(from, to) {
		return nil
	}
	sfrom := underlyingSlice(from)
	sto := underlyingSlice(to)
	if sfrom != nil && sto != nil {
		if !types.ConvertibleTo(sfrom.Elem(), sto.Elem()) {
			return fmt.Errorf("slice element type %s is not convertible to %s", sfrom.Elem(), sto.Elem())
		}
		return nil
	}
	mfrom := underlyingMap(from)
	mto := underlyingMap(to)
	if mfrom != nil && mto != nil {
		if !types.ConvertibleTo(mfrom.Key(), mto.Key()) {
			return fmt.Errorf("map key type %s is not convertible to %s", mfrom.Key(), mto.Key())
		}
		if !types.ConvertibleTo(mfrom.Elem(), mto.Elem()) {
			return fmt.Errorf("map element type %s is not convertible to %s", mfrom.Elem(), mto.Elem())
		}
		return nil
	}
	return fmt.Errorf("type %s is not convertible to %s", from, to)
}
checkConvertible determines whether values of type from can be converted to type to. It
returns nil if convertible and a descriptive error otherwise.
See package documentation for this definition of 'convertible'.
package main
import (
	"errors"
	"fmt"
	"go/types"
	"io"
	"sort"
	"strconv"
)
func walkNamedTypes(typ types.Type, callback func(*types.Named)) {
	switch typ := typ.(type) {
	case *types.Basic:
	case *types.Chan:
		walkNamedTypes(typ.Elem(), callback)
	case *types.Map:
		walkNamedTypes(typ.Key(), callback)
		walkNamedTypes(typ.Elem(), callback)
	case *types.Named:
		callback(typ)
	case *types.Pointer:
		walkNamedTypes(typ.Elem(), callback)
	case *types.Slice:
		walkNamedTypes(typ.Elem(), callback)
	case *types.Struct:
		for i := 0; i < typ.NumFields(); i++ {
			walkNamedTypes(typ.Field(i).Type(), callback)
		}
	case *types.Interface:
		if typ.NumMethods() > 0 {
			panic("BUG: can't walk non-empty interface")
		}
	default:
		panic(fmt.Errorf("BUG: can't walk %T", typ))
	}
}
func lookupStructType(scope *types.Scope, name string) (*types.Named, error) {
	typ, err := lookupType(scope, name)
	if err != nil {
		return nil, err
	}
	_, ok := typ.Underlying().(*types.Struct)
	if !ok {
		return nil, errors.New("not a struct type")
	}
	return typ, nil
}
func lookupType(scope *types.Scope, name string) (*types.Named, error) {
	obj := scope.Lookup(name)
	if obj == nil {
		return nil, errors.New("no such identifier")
	}
	typ, ok := obj.(*types.TypeName)
	if !ok {
		return nil, errors.New("not a type")
	}
	return typ.Type().(*types.Named), nil
}
func isPointer(typ types.Type) bool {
	_, ok := typ.(*types.Pointer)
	return ok
}
func underlyingSlice(typ types.Type) *types.Slice {
	for {
		switch typ.(type) {
		case *types.Named:
			typ = typ.Underlying()
		case *types.Slice:
			return typ.(*types.Slice)
		default:
			return nil
		}
	}
}
func underlyingMap(typ types.Type) *types.Map {
	for {
		switch typ.(type) {
		case *types.Named:
			typ = typ.Underlying()
		case *types.Map:
			return typ.(*types.Map)
		default:
			return nil
		}
	}
}
func ensureNilCheckable(typ types.Type) types.Type {
	orig := typ
	named := false
	for {
		switch typ.(type) {
		case *types.Named:
			typ = typ.Underlying()
			named = true
		case *types.Slice, *types.Map:
			if named {
				return types.NewPointer(orig)
			}
			return orig
		case *types.Pointer, *types.Interface:
			return orig
		default:
			return types.NewPointer(orig)
		}
	}
}