Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / go / ssa / interp / ops.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/go/ssa/interp/ops.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/go/ssa/interp/ops.go
new file mode 100644 (file)
index 0000000..90d9452
--- /dev/null
@@ -0,0 +1,1370 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package interp
+
+import (
+       "bytes"
+       "fmt"
+       "go/constant"
+       "go/token"
+       "go/types"
+       "os"
+       "reflect"
+       "strings"
+       "sync"
+       "unsafe"
+
+       "golang.org/x/tools/go/ssa"
+)
+
+// If the target program panics, the interpreter panics with this type.
+type targetPanic struct {
+       v value
+}
+
+func (p targetPanic) String() string {
+       return toString(p.v)
+}
+
+// If the target program calls exit, the interpreter panics with this type.
+type exitPanic int
+
+// constValue returns the value of the constant with the
+// dynamic type tag appropriate for c.Type().
+func constValue(c *ssa.Const) value {
+       if c.IsNil() {
+               return zero(c.Type()) // typed nil
+       }
+
+       if t, ok := c.Type().Underlying().(*types.Basic); ok {
+               // TODO(adonovan): eliminate untyped constants from SSA form.
+               switch t.Kind() {
+               case types.Bool, types.UntypedBool:
+                       return constant.BoolVal(c.Value)
+               case types.Int, types.UntypedInt:
+                       // Assume sizeof(int) is same on host and target.
+                       return int(c.Int64())
+               case types.Int8:
+                       return int8(c.Int64())
+               case types.Int16:
+                       return int16(c.Int64())
+               case types.Int32, types.UntypedRune:
+                       return int32(c.Int64())
+               case types.Int64:
+                       return c.Int64()
+               case types.Uint:
+                       // Assume sizeof(uint) is same on host and target.
+                       return uint(c.Uint64())
+               case types.Uint8:
+                       return uint8(c.Uint64())
+               case types.Uint16:
+                       return uint16(c.Uint64())
+               case types.Uint32:
+                       return uint32(c.Uint64())
+               case types.Uint64:
+                       return c.Uint64()
+               case types.Uintptr:
+                       // Assume sizeof(uintptr) is same on host and target.
+                       return uintptr(c.Uint64())
+               case types.Float32:
+                       return float32(c.Float64())
+               case types.Float64, types.UntypedFloat:
+                       return c.Float64()
+               case types.Complex64:
+                       return complex64(c.Complex128())
+               case types.Complex128, types.UntypedComplex:
+                       return c.Complex128()
+               case types.String, types.UntypedString:
+                       if c.Value.Kind() == constant.String {
+                               return constant.StringVal(c.Value)
+                       }
+                       return string(rune(c.Int64()))
+               }
+       }
+
+       panic(fmt.Sprintf("constValue: %s", c))
+}
+
+// asInt converts x, which must be an integer, to an int suitable for
+// use as a slice or array index or operand to make().
+func asInt(x value) int {
+       switch x := x.(type) {
+       case int:
+               return x
+       case int8:
+               return int(x)
+       case int16:
+               return int(x)
+       case int32:
+               return int(x)
+       case int64:
+               return int(x)
+       case uint:
+               return int(x)
+       case uint8:
+               return int(x)
+       case uint16:
+               return int(x)
+       case uint32:
+               return int(x)
+       case uint64:
+               return int(x)
+       case uintptr:
+               return int(x)
+       }
+       panic(fmt.Sprintf("cannot convert %T to int", x))
+}
+
+// asUint64 converts x, which must be an unsigned integer, to a uint64
+// suitable for use as a bitwise shift count.
+func asUint64(x value) uint64 {
+       switch x := x.(type) {
+       case uint:
+               return uint64(x)
+       case uint8:
+               return uint64(x)
+       case uint16:
+               return uint64(x)
+       case uint32:
+               return uint64(x)
+       case uint64:
+               return x
+       case uintptr:
+               return uint64(x)
+       }
+       panic(fmt.Sprintf("cannot convert %T to uint64", x))
+}
+
+// zero returns a new "zero" value of the specified type.
+func zero(t types.Type) value {
+       switch t := t.(type) {
+       case *types.Basic:
+               if t.Kind() == types.UntypedNil {
+                       panic("untyped nil has no zero value")
+               }
+               if t.Info()&types.IsUntyped != 0 {
+                       // TODO(adonovan): make it an invariant that
+                       // this is unreachable.  Currently some
+                       // constants have 'untyped' types when they
+                       // should be defaulted by the typechecker.
+                       t = types.Default(t).(*types.Basic)
+               }
+               switch t.Kind() {
+               case types.Bool:
+                       return false
+               case types.Int:
+                       return int(0)
+               case types.Int8:
+                       return int8(0)
+               case types.Int16:
+                       return int16(0)
+               case types.Int32:
+                       return int32(0)
+               case types.Int64:
+                       return int64(0)
+               case types.Uint:
+                       return uint(0)
+               case types.Uint8:
+                       return uint8(0)
+               case types.Uint16:
+                       return uint16(0)
+               case types.Uint32:
+                       return uint32(0)
+               case types.Uint64:
+                       return uint64(0)
+               case types.Uintptr:
+                       return uintptr(0)
+               case types.Float32:
+                       return float32(0)
+               case types.Float64:
+                       return float64(0)
+               case types.Complex64:
+                       return complex64(0)
+               case types.Complex128:
+                       return complex128(0)
+               case types.String:
+                       return ""
+               case types.UnsafePointer:
+                       return unsafe.Pointer(nil)
+               default:
+                       panic(fmt.Sprint("zero for unexpected type:", t))
+               }
+       case *types.Pointer:
+               return (*value)(nil)
+       case *types.Array:
+               a := make(array, t.Len())
+               for i := range a {
+                       a[i] = zero(t.Elem())
+               }
+               return a
+       case *types.Named:
+               return zero(t.Underlying())
+       case *types.Interface:
+               return iface{} // nil type, methodset and value
+       case *types.Slice:
+               return []value(nil)
+       case *types.Struct:
+               s := make(structure, t.NumFields())
+               for i := range s {
+                       s[i] = zero(t.Field(i).Type())
+               }
+               return s
+       case *types.Tuple:
+               if t.Len() == 1 {
+                       return zero(t.At(0).Type())
+               }
+               s := make(tuple, t.Len())
+               for i := range s {
+                       s[i] = zero(t.At(i).Type())
+               }
+               return s
+       case *types.Chan:
+               return chan value(nil)
+       case *types.Map:
+               if usesBuiltinMap(t.Key()) {
+                       return map[value]value(nil)
+               }
+               return (*hashmap)(nil)
+       case *types.Signature:
+               return (*ssa.Function)(nil)
+       }
+       panic(fmt.Sprint("zero: unexpected ", t))
+}
+
+// slice returns x[lo:hi:max].  Any of lo, hi and max may be nil.
+func slice(x, lo, hi, max value) value {
+       var Len, Cap int
+       switch x := x.(type) {
+       case string:
+               Len = len(x)
+       case []value:
+               Len = len(x)
+               Cap = cap(x)
+       case *value: // *array
+               a := (*x).(array)
+               Len = len(a)
+               Cap = cap(a)
+       }
+
+       l := 0
+       if lo != nil {
+               l = asInt(lo)
+       }
+
+       h := Len
+       if hi != nil {
+               h = asInt(hi)
+       }
+
+       m := Cap
+       if max != nil {
+               m = asInt(max)
+       }
+
+       switch x := x.(type) {
+       case string:
+               return x[l:h]
+       case []value:
+               return x[l:h:m]
+       case *value: // *array
+               a := (*x).(array)
+               return []value(a)[l:h:m]
+       }
+       panic(fmt.Sprintf("slice: unexpected X type: %T", x))
+}
+
+// lookup returns x[idx] where x is a map or string.
+func lookup(instr *ssa.Lookup, x, idx value) value {
+       switch x := x.(type) { // map or string
+       case map[value]value, *hashmap:
+               var v value
+               var ok bool
+               switch x := x.(type) {
+               case map[value]value:
+                       v, ok = x[idx]
+               case *hashmap:
+                       v = x.lookup(idx.(hashable))
+                       ok = v != nil
+               }
+               if !ok {
+                       v = zero(instr.X.Type().Underlying().(*types.Map).Elem())
+               }
+               if instr.CommaOk {
+                       v = tuple{v, ok}
+               }
+               return v
+       case string:
+               return x[asInt(idx)]
+       }
+       panic(fmt.Sprintf("unexpected x type in Lookup: %T", x))
+}
+
+// binop implements all arithmetic and logical binary operators for
+// numeric datatypes and strings.  Both operands must have identical
+// dynamic type.
+//
+func binop(op token.Token, t types.Type, x, y value) value {
+       switch op {
+       case token.ADD:
+               switch x.(type) {
+               case int:
+                       return x.(int) + y.(int)
+               case int8:
+                       return x.(int8) + y.(int8)
+               case int16:
+                       return x.(int16) + y.(int16)
+               case int32:
+                       return x.(int32) + y.(int32)
+               case int64:
+                       return x.(int64) + y.(int64)
+               case uint:
+                       return x.(uint) + y.(uint)
+               case uint8:
+                       return x.(uint8) + y.(uint8)
+               case uint16:
+                       return x.(uint16) + y.(uint16)
+               case uint32:
+                       return x.(uint32) + y.(uint32)
+               case uint64:
+                       return x.(uint64) + y.(uint64)
+               case uintptr:
+                       return x.(uintptr) + y.(uintptr)
+               case float32:
+                       return x.(float32) + y.(float32)
+               case float64:
+                       return x.(float64) + y.(float64)
+               case complex64:
+                       return x.(complex64) + y.(complex64)
+               case complex128:
+                       return x.(complex128) + y.(complex128)
+               case string:
+                       return x.(string) + y.(string)
+               }
+
+       case token.SUB:
+               switch x.(type) {
+               case int:
+                       return x.(int) - y.(int)
+               case int8:
+                       return x.(int8) - y.(int8)
+               case int16:
+                       return x.(int16) - y.(int16)
+               case int32:
+                       return x.(int32) - y.(int32)
+               case int64:
+                       return x.(int64) - y.(int64)
+               case uint:
+                       return x.(uint) - y.(uint)
+               case uint8:
+                       return x.(uint8) - y.(uint8)
+               case uint16:
+                       return x.(uint16) - y.(uint16)
+               case uint32:
+                       return x.(uint32) - y.(uint32)
+               case uint64:
+                       return x.(uint64) - y.(uint64)
+               case uintptr:
+                       return x.(uintptr) - y.(uintptr)
+               case float32:
+                       return x.(float32) - y.(float32)
+               case float64:
+                       return x.(float64) - y.(float64)
+               case complex64:
+                       return x.(complex64) - y.(complex64)
+               case complex128:
+                       return x.(complex128) - y.(complex128)
+               }
+
+       case token.MUL:
+               switch x.(type) {
+               case int:
+                       return x.(int) * y.(int)
+               case int8:
+                       return x.(int8) * y.(int8)
+               case int16:
+                       return x.(int16) * y.(int16)
+               case int32:
+                       return x.(int32) * y.(int32)
+               case int64:
+                       return x.(int64) * y.(int64)
+               case uint:
+                       return x.(uint) * y.(uint)
+               case uint8:
+                       return x.(uint8) * y.(uint8)
+               case uint16:
+                       return x.(uint16) * y.(uint16)
+               case uint32:
+                       return x.(uint32) * y.(uint32)
+               case uint64:
+                       return x.(uint64) * y.(uint64)
+               case uintptr:
+                       return x.(uintptr) * y.(uintptr)
+               case float32:
+                       return x.(float32) * y.(float32)
+               case float64:
+                       return x.(float64) * y.(float64)
+               case complex64:
+                       return x.(complex64) * y.(complex64)
+               case complex128:
+                       return x.(complex128) * y.(complex128)
+               }
+
+       case token.QUO:
+               switch x.(type) {
+               case int:
+                       return x.(int) / y.(int)
+               case int8:
+                       return x.(int8) / y.(int8)
+               case int16:
+                       return x.(int16) / y.(int16)
+               case int32:
+                       return x.(int32) / y.(int32)
+               case int64:
+                       return x.(int64) / y.(int64)
+               case uint:
+                       return x.(uint) / y.(uint)
+               case uint8:
+                       return x.(uint8) / y.(uint8)
+               case uint16:
+                       return x.(uint16) / y.(uint16)
+               case uint32:
+                       return x.(uint32) / y.(uint32)
+               case uint64:
+                       return x.(uint64) / y.(uint64)
+               case uintptr:
+                       return x.(uintptr) / y.(uintptr)
+               case float32:
+                       return x.(float32) / y.(float32)
+               case float64:
+                       return x.(float64) / y.(float64)
+               case complex64:
+                       return x.(complex64) / y.(complex64)
+               case complex128:
+                       return x.(complex128) / y.(complex128)
+               }
+
+       case token.REM:
+               switch x.(type) {
+               case int:
+                       return x.(int) % y.(int)
+               case int8:
+                       return x.(int8) % y.(int8)
+               case int16:
+                       return x.(int16) % y.(int16)
+               case int32:
+                       return x.(int32) % y.(int32)
+               case int64:
+                       return x.(int64) % y.(int64)
+               case uint:
+                       return x.(uint) % y.(uint)
+               case uint8:
+                       return x.(uint8) % y.(uint8)
+               case uint16:
+                       return x.(uint16) % y.(uint16)
+               case uint32:
+                       return x.(uint32) % y.(uint32)
+               case uint64:
+                       return x.(uint64) % y.(uint64)
+               case uintptr:
+                       return x.(uintptr) % y.(uintptr)
+               }
+
+       case token.AND:
+               switch x.(type) {
+               case int:
+                       return x.(int) & y.(int)
+               case int8:
+                       return x.(int8) & y.(int8)
+               case int16:
+                       return x.(int16) & y.(int16)
+               case int32:
+                       return x.(int32) & y.(int32)
+               case int64:
+                       return x.(int64) & y.(int64)
+               case uint:
+                       return x.(uint) & y.(uint)
+               case uint8:
+                       return x.(uint8) & y.(uint8)
+               case uint16:
+                       return x.(uint16) & y.(uint16)
+               case uint32:
+                       return x.(uint32) & y.(uint32)
+               case uint64:
+                       return x.(uint64) & y.(uint64)
+               case uintptr:
+                       return x.(uintptr) & y.(uintptr)
+               }
+
+       case token.OR:
+               switch x.(type) {
+               case int:
+                       return x.(int) | y.(int)
+               case int8:
+                       return x.(int8) | y.(int8)
+               case int16:
+                       return x.(int16) | y.(int16)
+               case int32:
+                       return x.(int32) | y.(int32)
+               case int64:
+                       return x.(int64) | y.(int64)
+               case uint:
+                       return x.(uint) | y.(uint)
+               case uint8:
+                       return x.(uint8) | y.(uint8)
+               case uint16:
+                       return x.(uint16) | y.(uint16)
+               case uint32:
+                       return x.(uint32) | y.(uint32)
+               case uint64:
+                       return x.(uint64) | y.(uint64)
+               case uintptr:
+                       return x.(uintptr) | y.(uintptr)
+               }
+
+       case token.XOR:
+               switch x.(type) {
+               case int:
+                       return x.(int) ^ y.(int)
+               case int8:
+                       return x.(int8) ^ y.(int8)
+               case int16:
+                       return x.(int16) ^ y.(int16)
+               case int32:
+                       return x.(int32) ^ y.(int32)
+               case int64:
+                       return x.(int64) ^ y.(int64)
+               case uint:
+                       return x.(uint) ^ y.(uint)
+               case uint8:
+                       return x.(uint8) ^ y.(uint8)
+               case uint16:
+                       return x.(uint16) ^ y.(uint16)
+               case uint32:
+                       return x.(uint32) ^ y.(uint32)
+               case uint64:
+                       return x.(uint64) ^ y.(uint64)
+               case uintptr:
+                       return x.(uintptr) ^ y.(uintptr)
+               }
+
+       case token.AND_NOT:
+               switch x.(type) {
+               case int:
+                       return x.(int) &^ y.(int)
+               case int8:
+                       return x.(int8) &^ y.(int8)
+               case int16:
+                       return x.(int16) &^ y.(int16)
+               case int32:
+                       return x.(int32) &^ y.(int32)
+               case int64:
+                       return x.(int64) &^ y.(int64)
+               case uint:
+                       return x.(uint) &^ y.(uint)
+               case uint8:
+                       return x.(uint8) &^ y.(uint8)
+               case uint16:
+                       return x.(uint16) &^ y.(uint16)
+               case uint32:
+                       return x.(uint32) &^ y.(uint32)
+               case uint64:
+                       return x.(uint64) &^ y.(uint64)
+               case uintptr:
+                       return x.(uintptr) &^ y.(uintptr)
+               }
+
+       case token.SHL:
+               y := asUint64(y)
+               switch x.(type) {
+               case int:
+                       return x.(int) << y
+               case int8:
+                       return x.(int8) << y
+               case int16:
+                       return x.(int16) << y
+               case int32:
+                       return x.(int32) << y
+               case int64:
+                       return x.(int64) << y
+               case uint:
+                       return x.(uint) << y
+               case uint8:
+                       return x.(uint8) << y
+               case uint16:
+                       return x.(uint16) << y
+               case uint32:
+                       return x.(uint32) << y
+               case uint64:
+                       return x.(uint64) << y
+               case uintptr:
+                       return x.(uintptr) << y
+               }
+
+       case token.SHR:
+               y := asUint64(y)
+               switch x.(type) {
+               case int:
+                       return x.(int) >> y
+               case int8:
+                       return x.(int8) >> y
+               case int16:
+                       return x.(int16) >> y
+               case int32:
+                       return x.(int32) >> y
+               case int64:
+                       return x.(int64) >> y
+               case uint:
+                       return x.(uint) >> y
+               case uint8:
+                       return x.(uint8) >> y
+               case uint16:
+                       return x.(uint16) >> y
+               case uint32:
+                       return x.(uint32) >> y
+               case uint64:
+                       return x.(uint64) >> y
+               case uintptr:
+                       return x.(uintptr) >> y
+               }
+
+       case token.LSS:
+               switch x.(type) {
+               case int:
+                       return x.(int) < y.(int)
+               case int8:
+                       return x.(int8) < y.(int8)
+               case int16:
+                       return x.(int16) < y.(int16)
+               case int32:
+                       return x.(int32) < y.(int32)
+               case int64:
+                       return x.(int64) < y.(int64)
+               case uint:
+                       return x.(uint) < y.(uint)
+               case uint8:
+                       return x.(uint8) < y.(uint8)
+               case uint16:
+                       return x.(uint16) < y.(uint16)
+               case uint32:
+                       return x.(uint32) < y.(uint32)
+               case uint64:
+                       return x.(uint64) < y.(uint64)
+               case uintptr:
+                       return x.(uintptr) < y.(uintptr)
+               case float32:
+                       return x.(float32) < y.(float32)
+               case float64:
+                       return x.(float64) < y.(float64)
+               case string:
+                       return x.(string) < y.(string)
+               }
+
+       case token.LEQ:
+               switch x.(type) {
+               case int:
+                       return x.(int) <= y.(int)
+               case int8:
+                       return x.(int8) <= y.(int8)
+               case int16:
+                       return x.(int16) <= y.(int16)
+               case int32:
+                       return x.(int32) <= y.(int32)
+               case int64:
+                       return x.(int64) <= y.(int64)
+               case uint:
+                       return x.(uint) <= y.(uint)
+               case uint8:
+                       return x.(uint8) <= y.(uint8)
+               case uint16:
+                       return x.(uint16) <= y.(uint16)
+               case uint32:
+                       return x.(uint32) <= y.(uint32)
+               case uint64:
+                       return x.(uint64) <= y.(uint64)
+               case uintptr:
+                       return x.(uintptr) <= y.(uintptr)
+               case float32:
+                       return x.(float32) <= y.(float32)
+               case float64:
+                       return x.(float64) <= y.(float64)
+               case string:
+                       return x.(string) <= y.(string)
+               }
+
+       case token.EQL:
+               return eqnil(t, x, y)
+
+       case token.NEQ:
+               return !eqnil(t, x, y)
+
+       case token.GTR:
+               switch x.(type) {
+               case int:
+                       return x.(int) > y.(int)
+               case int8:
+                       return x.(int8) > y.(int8)
+               case int16:
+                       return x.(int16) > y.(int16)
+               case int32:
+                       return x.(int32) > y.(int32)
+               case int64:
+                       return x.(int64) > y.(int64)
+               case uint:
+                       return x.(uint) > y.(uint)
+               case uint8:
+                       return x.(uint8) > y.(uint8)
+               case uint16:
+                       return x.(uint16) > y.(uint16)
+               case uint32:
+                       return x.(uint32) > y.(uint32)
+               case uint64:
+                       return x.(uint64) > y.(uint64)
+               case uintptr:
+                       return x.(uintptr) > y.(uintptr)
+               case float32:
+                       return x.(float32) > y.(float32)
+               case float64:
+                       return x.(float64) > y.(float64)
+               case string:
+                       return x.(string) > y.(string)
+               }
+
+       case token.GEQ:
+               switch x.(type) {
+               case int:
+                       return x.(int) >= y.(int)
+               case int8:
+                       return x.(int8) >= y.(int8)
+               case int16:
+                       return x.(int16) >= y.(int16)
+               case int32:
+                       return x.(int32) >= y.(int32)
+               case int64:
+                       return x.(int64) >= y.(int64)
+               case uint:
+                       return x.(uint) >= y.(uint)
+               case uint8:
+                       return x.(uint8) >= y.(uint8)
+               case uint16:
+                       return x.(uint16) >= y.(uint16)
+               case uint32:
+                       return x.(uint32) >= y.(uint32)
+               case uint64:
+                       return x.(uint64) >= y.(uint64)
+               case uintptr:
+                       return x.(uintptr) >= y.(uintptr)
+               case float32:
+                       return x.(float32) >= y.(float32)
+               case float64:
+                       return x.(float64) >= y.(float64)
+               case string:
+                       return x.(string) >= y.(string)
+               }
+       }
+       panic(fmt.Sprintf("invalid binary op: %T %s %T", x, op, y))
+}
+
+// eqnil returns the comparison x == y using the equivalence relation
+// appropriate for type t.
+// If t is a reference type, at most one of x or y may be a nil value
+// of that type.
+//
+func eqnil(t types.Type, x, y value) bool {
+       switch t.Underlying().(type) {
+       case *types.Map, *types.Signature, *types.Slice:
+               // Since these types don't support comparison,
+               // one of the operands must be a literal nil.
+               switch x := x.(type) {
+               case *hashmap:
+                       return (x != nil) == (y.(*hashmap) != nil)
+               case map[value]value:
+                       return (x != nil) == (y.(map[value]value) != nil)
+               case *ssa.Function:
+                       switch y := y.(type) {
+                       case *ssa.Function:
+                               return (x != nil) == (y != nil)
+                       case *closure:
+                               return true
+                       }
+               case *closure:
+                       return (x != nil) == (y.(*ssa.Function) != nil)
+               case []value:
+                       return (x != nil) == (y.([]value) != nil)
+               }
+               panic(fmt.Sprintf("eqnil(%s): illegal dynamic type: %T", t, x))
+       }
+
+       return equals(t, x, y)
+}
+
+func unop(instr *ssa.UnOp, x value) value {
+       switch instr.Op {
+       case token.ARROW: // receive
+               v, ok := <-x.(chan value)
+               if !ok {
+                       v = zero(instr.X.Type().Underlying().(*types.Chan).Elem())
+               }
+               if instr.CommaOk {
+                       v = tuple{v, ok}
+               }
+               return v
+       case token.SUB:
+               switch x := x.(type) {
+               case int:
+                       return -x
+               case int8:
+                       return -x
+               case int16:
+                       return -x
+               case int32:
+                       return -x
+               case int64:
+                       return -x
+               case uint:
+                       return -x
+               case uint8:
+                       return -x
+               case uint16:
+                       return -x
+               case uint32:
+                       return -x
+               case uint64:
+                       return -x
+               case uintptr:
+                       return -x
+               case float32:
+                       return -x
+               case float64:
+                       return -x
+               case complex64:
+                       return -x
+               case complex128:
+                       return -x
+               }
+       case token.MUL:
+               return load(deref(instr.X.Type()), x.(*value))
+       case token.NOT:
+               return !x.(bool)
+       case token.XOR:
+               switch x := x.(type) {
+               case int:
+                       return ^x
+               case int8:
+                       return ^x
+               case int16:
+                       return ^x
+               case int32:
+                       return ^x
+               case int64:
+                       return ^x
+               case uint:
+                       return ^x
+               case uint8:
+                       return ^x
+               case uint16:
+                       return ^x
+               case uint32:
+                       return ^x
+               case uint64:
+                       return ^x
+               case uintptr:
+                       return ^x
+               }
+       }
+       panic(fmt.Sprintf("invalid unary op %s %T", instr.Op, x))
+}
+
+// typeAssert checks whether dynamic type of itf is instr.AssertedType.
+// It returns the extracted value on success, and panics on failure,
+// unless instr.CommaOk, in which case it always returns a "value,ok" tuple.
+//
+func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value {
+       var v value
+       err := ""
+       if itf.t == nil {
+               err = fmt.Sprintf("interface conversion: interface is nil, not %s", instr.AssertedType)
+
+       } else if idst, ok := instr.AssertedType.Underlying().(*types.Interface); ok {
+               v = itf
+               err = checkInterface(i, idst, itf)
+
+       } else if types.Identical(itf.t, instr.AssertedType) {
+               v = itf.v // extract value
+
+       } else {
+               err = fmt.Sprintf("interface conversion: interface is %s, not %s", itf.t, instr.AssertedType)
+       }
+
+       if err != "" {
+               if !instr.CommaOk {
+                       panic(err)
+               }
+               return tuple{zero(instr.AssertedType), false}
+       }
+       if instr.CommaOk {
+               return tuple{v, true}
+       }
+       return v
+}
+
+// If CapturedOutput is non-nil, all writes by the interpreted program
+// to file descriptors 1 and 2 will also be written to CapturedOutput.
+//
+// (The $GOROOT/test system requires that the test be considered a
+// failure if "BUG" appears in the combined stdout/stderr output, even
+// if it exits zero.  This is a global variable shared by all
+// interpreters in the same process.)
+//
+var CapturedOutput *bytes.Buffer
+var capturedOutputMu sync.Mutex
+
+// write writes bytes b to the target program's standard output.
+// The print/println built-ins and the write() system call funnel
+// through here so they can be captured by the test driver.
+func print(b []byte) (int, error) {
+       if CapturedOutput != nil {
+               capturedOutputMu.Lock()
+               CapturedOutput.Write(b) // ignore errors
+               capturedOutputMu.Unlock()
+       }
+       return os.Stdout.Write(b)
+}
+
+// callBuiltin interprets a call to builtin fn with arguments args,
+// returning its result.
+func callBuiltin(caller *frame, callpos token.Pos, fn *ssa.Builtin, args []value) value {
+       switch fn.Name() {
+       case "append":
+               if len(args) == 1 {
+                       return args[0]
+               }
+               if s, ok := args[1].(string); ok {
+                       // append([]byte, ...string) []byte
+                       arg0 := args[0].([]value)
+                       for i := 0; i < len(s); i++ {
+                               arg0 = append(arg0, s[i])
+                       }
+                       return arg0
+               }
+               // append([]T, ...[]T) []T
+               return append(args[0].([]value), args[1].([]value)...)
+
+       case "copy": // copy([]T, []T) int or copy([]byte, string) int
+               src := args[1]
+               if _, ok := src.(string); ok {
+                       params := fn.Type().(*types.Signature).Params()
+                       src = conv(params.At(0).Type(), params.At(1).Type(), src)
+               }
+               return copy(args[0].([]value), src.([]value))
+
+       case "close": // close(chan T)
+               close(args[0].(chan value))
+               return nil
+
+       case "delete": // delete(map[K]value, K)
+               switch m := args[0].(type) {
+               case map[value]value:
+                       delete(m, args[1])
+               case *hashmap:
+                       m.delete(args[1].(hashable))
+               default:
+                       panic(fmt.Sprintf("illegal map type: %T", m))
+               }
+               return nil
+
+       case "print", "println": // print(any, ...)
+               ln := fn.Name() == "println"
+               var buf bytes.Buffer
+               for i, arg := range args {
+                       if i > 0 && ln {
+                               buf.WriteRune(' ')
+                       }
+                       buf.WriteString(toString(arg))
+               }
+               if ln {
+                       buf.WriteRune('\n')
+               }
+               print(buf.Bytes())
+               return nil
+
+       case "len":
+               switch x := args[0].(type) {
+               case string:
+                       return len(x)
+               case array:
+                       return len(x)
+               case *value:
+                       return len((*x).(array))
+               case []value:
+                       return len(x)
+               case map[value]value:
+                       return len(x)
+               case *hashmap:
+                       return x.len()
+               case chan value:
+                       return len(x)
+               default:
+                       panic(fmt.Sprintf("len: illegal operand: %T", x))
+               }
+
+       case "cap":
+               switch x := args[0].(type) {
+               case array:
+                       return cap(x)
+               case *value:
+                       return cap((*x).(array))
+               case []value:
+                       return cap(x)
+               case chan value:
+                       return cap(x)
+               default:
+                       panic(fmt.Sprintf("cap: illegal operand: %T", x))
+               }
+
+       case "real":
+               switch c := args[0].(type) {
+               case complex64:
+                       return real(c)
+               case complex128:
+                       return real(c)
+               default:
+                       panic(fmt.Sprintf("real: illegal operand: %T", c))
+               }
+
+       case "imag":
+               switch c := args[0].(type) {
+               case complex64:
+                       return imag(c)
+               case complex128:
+                       return imag(c)
+               default:
+                       panic(fmt.Sprintf("imag: illegal operand: %T", c))
+               }
+
+       case "complex":
+               switch f := args[0].(type) {
+               case float32:
+                       return complex(f, args[1].(float32))
+               case float64:
+                       return complex(f, args[1].(float64))
+               default:
+                       panic(fmt.Sprintf("complex: illegal operand: %T", f))
+               }
+
+       case "panic":
+               // ssa.Panic handles most cases; this is only for "go
+               // panic" or "defer panic".
+               panic(targetPanic{args[0]})
+
+       case "recover":
+               return doRecover(caller)
+
+       case "ssa:wrapnilchk":
+               recv := args[0]
+               if recv.(*value) == nil {
+                       recvType := args[1]
+                       methodName := args[2]
+                       panic(fmt.Sprintf("value method (%s).%s called using nil *%s pointer",
+                               recvType, methodName, recvType))
+               }
+               return recv
+       }
+
+       panic("unknown built-in: " + fn.Name())
+}
+
+func rangeIter(x value, t types.Type) iter {
+       switch x := x.(type) {
+       case map[value]value:
+               return &mapIter{iter: reflect.ValueOf(x).MapRange()}
+       case *hashmap:
+               return &hashmapIter{iter: reflect.ValueOf(x.entries()).MapRange()}
+       case string:
+               return &stringIter{Reader: strings.NewReader(x)}
+       }
+       panic(fmt.Sprintf("cannot range over %T", x))
+}
+
+// widen widens a basic typed value x to the widest type of its
+// category, one of:
+//   bool, int64, uint64, float64, complex128, string.
+// This is inefficient but reduces the size of the cross-product of
+// cases we have to consider.
+//
+func widen(x value) value {
+       switch y := x.(type) {
+       case bool, int64, uint64, float64, complex128, string, unsafe.Pointer:
+               return x
+       case int:
+               return int64(y)
+       case int8:
+               return int64(y)
+       case int16:
+               return int64(y)
+       case int32:
+               return int64(y)
+       case uint:
+               return uint64(y)
+       case uint8:
+               return uint64(y)
+       case uint16:
+               return uint64(y)
+       case uint32:
+               return uint64(y)
+       case uintptr:
+               return uint64(y)
+       case float32:
+               return float64(y)
+       case complex64:
+               return complex128(y)
+       }
+       panic(fmt.Sprintf("cannot widen %T", x))
+}
+
+// conv converts the value x of type t_src to type t_dst and returns
+// the result.
+// Possible cases are described with the ssa.Convert operator.
+//
+func conv(t_dst, t_src types.Type, x value) value {
+       ut_src := t_src.Underlying()
+       ut_dst := t_dst.Underlying()
+
+       // Destination type is not an "untyped" type.
+       if b, ok := ut_dst.(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
+               panic("oops: conversion to 'untyped' type: " + b.String())
+       }
+
+       // Nor is it an interface type.
+       if _, ok := ut_dst.(*types.Interface); ok {
+               if _, ok := ut_src.(*types.Interface); ok {
+                       panic("oops: Convert should be ChangeInterface")
+               } else {
+                       panic("oops: Convert should be MakeInterface")
+               }
+       }
+
+       // Remaining conversions:
+       //    + untyped string/number/bool constant to a specific
+       //      representation.
+       //    + conversions between non-complex numeric types.
+       //    + conversions between complex numeric types.
+       //    + integer/[]byte/[]rune -> string.
+       //    + string -> []byte/[]rune.
+       //
+       // All are treated the same: first we extract the value to the
+       // widest representation (int64, uint64, float64, complex128,
+       // or string), then we convert it to the desired type.
+
+       switch ut_src := ut_src.(type) {
+       case *types.Pointer:
+               switch ut_dst := ut_dst.(type) {
+               case *types.Basic:
+                       // *value to unsafe.Pointer?
+                       if ut_dst.Kind() == types.UnsafePointer {
+                               return unsafe.Pointer(x.(*value))
+                       }
+               }
+
+       case *types.Slice:
+               // []byte or []rune -> string
+               // TODO(adonovan): fix: type B byte; conv([]B -> string).
+               switch ut_src.Elem().(*types.Basic).Kind() {
+               case types.Byte:
+                       x := x.([]value)
+                       b := make([]byte, 0, len(x))
+                       for i := range x {
+                               b = append(b, x[i].(byte))
+                       }
+                       return string(b)
+
+               case types.Rune:
+                       x := x.([]value)
+                       r := make([]rune, 0, len(x))
+                       for i := range x {
+                               r = append(r, x[i].(rune))
+                       }
+                       return string(r)
+               }
+
+       case *types.Basic:
+               x = widen(x)
+
+               // integer -> string?
+               // TODO(adonovan): fix: test integer -> named alias of string.
+               if ut_src.Info()&types.IsInteger != 0 {
+                       if ut_dst, ok := ut_dst.(*types.Basic); ok && ut_dst.Kind() == types.String {
+                               return fmt.Sprintf("%c", x)
+                       }
+               }
+
+               // string -> []rune, []byte or string?
+               if s, ok := x.(string); ok {
+                       switch ut_dst := ut_dst.(type) {
+                       case *types.Slice:
+                               var res []value
+                               // TODO(adonovan): fix: test named alias of rune, byte.
+                               switch ut_dst.Elem().(*types.Basic).Kind() {
+                               case types.Rune:
+                                       for _, r := range []rune(s) {
+                                               res = append(res, r)
+                                       }
+                                       return res
+                               case types.Byte:
+                                       for _, b := range []byte(s) {
+                                               res = append(res, b)
+                                       }
+                                       return res
+                               }
+                       case *types.Basic:
+                               if ut_dst.Kind() == types.String {
+                                       return x.(string)
+                               }
+                       }
+                       break // fail: no other conversions for string
+               }
+
+               // unsafe.Pointer -> *value
+               if ut_src.Kind() == types.UnsafePointer {
+                       // TODO(adonovan): this is wrong and cannot
+                       // really be fixed with the current design.
+                       //
+                       // return (*value)(x.(unsafe.Pointer))
+                       // creates a new pointer of a different
+                       // type but the underlying interface value
+                       // knows its "true" type and so cannot be
+                       // meaningfully used through the new pointer.
+                       //
+                       // To make this work, the interpreter needs to
+                       // simulate the memory layout of a real
+                       // compiled implementation.
+                       //
+                       // To at least preserve type-safety, we'll
+                       // just return the zero value of the
+                       // destination type.
+                       return zero(t_dst)
+               }
+
+               // Conversions between complex numeric types?
+               if ut_src.Info()&types.IsComplex != 0 {
+                       switch ut_dst.(*types.Basic).Kind() {
+                       case types.Complex64:
+                               return complex64(x.(complex128))
+                       case types.Complex128:
+                               return x.(complex128)
+                       }
+                       break // fail: no other conversions for complex
+               }
+
+               // Conversions between non-complex numeric types?
+               if ut_src.Info()&types.IsNumeric != 0 {
+                       kind := ut_dst.(*types.Basic).Kind()
+                       switch x := x.(type) {
+                       case int64: // signed integer -> numeric?
+                               switch kind {
+                               case types.Int:
+                                       return int(x)
+                               case types.Int8:
+                                       return int8(x)
+                               case types.Int16:
+                                       return int16(x)
+                               case types.Int32:
+                                       return int32(x)
+                               case types.Int64:
+                                       return int64(x)
+                               case types.Uint:
+                                       return uint(x)
+                               case types.Uint8:
+                                       return uint8(x)
+                               case types.Uint16:
+                                       return uint16(x)
+                               case types.Uint32:
+                                       return uint32(x)
+                               case types.Uint64:
+                                       return uint64(x)
+                               case types.Uintptr:
+                                       return uintptr(x)
+                               case types.Float32:
+                                       return float32(x)
+                               case types.Float64:
+                                       return float64(x)
+                               }
+
+                       case uint64: // unsigned integer -> numeric?
+                               switch kind {
+                               case types.Int:
+                                       return int(x)
+                               case types.Int8:
+                                       return int8(x)
+                               case types.Int16:
+                                       return int16(x)
+                               case types.Int32:
+                                       return int32(x)
+                               case types.Int64:
+                                       return int64(x)
+                               case types.Uint:
+                                       return uint(x)
+                               case types.Uint8:
+                                       return uint8(x)
+                               case types.Uint16:
+                                       return uint16(x)
+                               case types.Uint32:
+                                       return uint32(x)
+                               case types.Uint64:
+                                       return uint64(x)
+                               case types.Uintptr:
+                                       return uintptr(x)
+                               case types.Float32:
+                                       return float32(x)
+                               case types.Float64:
+                                       return float64(x)
+                               }
+
+                       case float64: // floating point -> numeric?
+                               switch kind {
+                               case types.Int:
+                                       return int(x)
+                               case types.Int8:
+                                       return int8(x)
+                               case types.Int16:
+                                       return int16(x)
+                               case types.Int32:
+                                       return int32(x)
+                               case types.Int64:
+                                       return int64(x)
+                               case types.Uint:
+                                       return uint(x)
+                               case types.Uint8:
+                                       return uint8(x)
+                               case types.Uint16:
+                                       return uint16(x)
+                               case types.Uint32:
+                                       return uint32(x)
+                               case types.Uint64:
+                                       return uint64(x)
+                               case types.Uintptr:
+                                       return uintptr(x)
+                               case types.Float32:
+                                       return float32(x)
+                               case types.Float64:
+                                       return float64(x)
+                               }
+                       }
+               }
+       }
+
+       panic(fmt.Sprintf("unsupported conversion: %s  -> %s, dynamic type %T", t_src, t_dst, x))
+}
+
+// checkInterface checks that the method set of x implements the
+// interface itype.
+// On success it returns "", on failure, an error message.
+//
+func checkInterface(i *interpreter, itype *types.Interface, x iface) string {
+       if meth, _ := types.MissingMethod(x.t, itype, true); meth != nil {
+               return fmt.Sprintf("interface conversion: %v is not %v: missing method %s",
+                       x.t, itype, meth.Name())
+       }
+       return "" // ok
+}