aboutsummaryrefslogtreecommitdiff
path: root/go/ssa/interp/ops.go
diff options
context:
space:
mode:
Diffstat (limited to 'go/ssa/interp/ops.go')
-rw-r--r--go/ssa/interp/ops.go87
1 files changed, 46 insertions, 41 deletions
diff --git a/go/ssa/interp/ops.go b/go/ssa/interp/ops.go
index 3bc6a4e32..39830bc8f 100644
--- a/go/ssa/interp/ops.go
+++ b/go/ssa/interp/ops.go
@@ -34,9 +34,10 @@ 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 c.Value == nil {
+ return zero(c.Type()) // typed zero
}
+ // c is not a type parameter so it's underlying type is basic.
if t, ok := c.Type().Underlying().(*types.Basic); ok {
// TODO(adonovan): eliminate untyped constants from SSA form.
@@ -87,34 +88,46 @@ func constValue(c *ssa.Const) value {
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 {
+// fitsInt returns true if x fits in type int according to sizes.
+func fitsInt(x int64, sizes types.Sizes) bool {
+ intSize := sizes.Sizeof(types.Typ[types.Int])
+ if intSize < sizes.Sizeof(types.Typ[types.Int64]) {
+ maxInt := int64(1)<<(intSize-1) - 1
+ minInt := -int64(1) << (intSize - 1)
+ return minInt <= x && x <= maxInt
+ }
+ return true
+}
+
+// asInt64 converts x, which must be an integer, to an int64.
+//
+// Callers that need a value directly usable as an int should combine this with fitsInt().
+func asInt64(x value) int64 {
switch x := x.(type) {
case int:
- return x
+ return int64(x)
case int8:
- return int(x)
+ return int64(x)
case int16:
- return int(x)
+ return int64(x)
case int32:
- return int(x)
+ return int64(x)
case int64:
- return int(x)
+ return x
case uint:
- return int(x)
+ return int64(x)
case uint8:
- return int(x)
+ return int64(x)
case uint16:
- return int(x)
+ return int64(x)
case uint32:
- return int(x)
+ return int64(x)
case uint64:
- return int(x)
+ return int64(x)
case uintptr:
- return int(x)
+ return int64(x)
}
- panic(fmt.Sprintf("cannot convert %T to int", x))
+ panic(fmt.Sprintf("cannot convert %T to int64", x))
}
// asUint64 converts x, which must be an unsigned integer, to a uint64
@@ -268,19 +281,19 @@ func slice(x, lo, hi, max value) value {
Cap = cap(a)
}
- l := 0
+ l := int64(0)
if lo != nil {
- l = asInt(lo)
+ l = asInt64(lo)
}
- h := Len
+ h := int64(Len)
if hi != nil {
- h = asInt(hi)
+ h = asInt64(hi)
}
- m := Cap
+ m := int64(Cap)
if max != nil {
- m = asInt(max)
+ m = asInt64(max)
}
switch x := x.(type) {
@@ -295,7 +308,7 @@ func slice(x, lo, hi, max value) value {
panic(fmt.Sprintf("slice: unexpected X type: %T", x))
}
-// lookup returns x[idx] where x is a map or string.
+// lookup returns x[idx] where x is a map.
func lookup(instr *ssa.Lookup, x, idx value) value {
switch x := x.(type) { // map or string
case map[value]value, *hashmap:
@@ -315,8 +328,6 @@ func lookup(instr *ssa.Lookup, x, idx value) value {
v = tuple{v, ok}
}
return v
- case string:
- return x[asInt(idx)]
}
panic(fmt.Sprintf("unexpected x type in Lookup: %T", x))
}
@@ -324,7 +335,6 @@ func lookup(instr *ssa.Lookup, x, idx value) value {
// 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:
@@ -798,7 +808,6 @@ func binop(op token.Token, t types.Type, x, y value) value {
// 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:
@@ -907,7 +916,6 @@ func unop(instr *ssa.UnOp, x value) value {
// 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 := ""
@@ -924,6 +932,8 @@ func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value {
} else {
err = fmt.Sprintf("interface conversion: interface is %s, not %s", itf.t, instr.AssertedType)
}
+ // Note: if instr.Underlying==true ever becomes reachable from interp check that
+ // types.Identical(itf.t.Underlying(), instr.AssertedType)
if err != "" {
if !instr.CommaOk {
@@ -944,7 +954,6 @@ func typeAssert(i *interpreter, instr *ssa.TypeAssert, itf iface) value {
// 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
@@ -1117,10 +1126,11 @@ func rangeIter(x value, t types.Type) iter {
// widen widens a basic typed value x to the widest type of its
// category, one of:
-// bool, int64, uint64, float64, complex128, string.
+//
+// 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:
@@ -1154,7 +1164,6 @@ func widen(x value) value {
// 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()
@@ -1388,18 +1397,15 @@ func conv(t_dst, t_src types.Type, x value) value {
// sliceToArrayPointer converts the value x of type slice to type t_dst
// a pointer to array and returns the result.
func sliceToArrayPointer(t_dst, t_src types.Type, x value) value {
- utSrc := t_src.Underlying()
- utDst := t_dst.Underlying()
-
- if _, ok := utSrc.(*types.Slice); ok {
- if utSrc, ok := utDst.(*types.Pointer); ok {
- if arr, ok := utSrc.Elem().(*types.Array); ok {
+ if _, ok := t_src.Underlying().(*types.Slice); ok {
+ if ptr, ok := t_dst.Underlying().(*types.Pointer); ok {
+ if arr, ok := ptr.Elem().Underlying().(*types.Array); ok {
x := x.([]value)
if arr.Len() > int64(len(x)) {
panic("array length is greater than slice length")
}
if x == nil {
- return zero(utSrc)
+ return zero(t_dst)
}
v := value(array(x[:arr.Len()]))
return &v
@@ -1413,7 +1419,6 @@ func sliceToArrayPointer(t_dst, t_src types.Type, x value) value {
// 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",