Esempio n. 1
0
// 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 idst, ok := underlyingType(instr.AssertedType).(*types.Interface); ok {
		v = itf
		err = checkInterface(i, idst, itf)

	} else if types.IsIdentical(itf.t, instr.AssertedType) {
		v = copyVal(itf.v) // extract value

	} else {
		err = fmt.Sprintf("type assert failed: expected %s, got %s", instr.AssertedType, itf.t)
	}

	if err != "" {
		if !instr.CommaOk {
			panic(err)
		}
		return tuple{zero(instr.AssertedType), false}
	}
	if instr.CommaOk {
		return tuple{v, true}
	}
	return v
}
Esempio n. 2
0
// isErrorMethodCall reports whether the call is of a method with signature
//	func Error() string
// where "string" is the universe's string type. We know the method is called "Error"
// and f.pkg is set.
func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
	// Is it a selector expression? Otherwise it's a function call, not a method call.
	sel, ok := call.Fun.(*ast.SelectorExpr)
	if !ok {
		return false
	}
	// The package is type-checked, so if there are no arguments, we're done.
	if len(call.Args) > 0 {
		return false
	}
	// Check the type of the method declaration
	typ := f.pkg.types[sel]
	if typ == nil {
		return false
	}
	// The type must be a signature, but be sure for safety.
	sig, ok := typ.(*types.Signature)
	if !ok {
		return false
	}
	// There must be a receiver for it to be a method call. Otherwise it is
	// a function, not something that satisfies the error interface.
	if sig.Recv == nil {
		return false
	}
	// There must be no arguments. Already verified by type checking, but be thorough.
	if len(sig.Params) > 0 {
		return false
	}
	// Finally the real questions.
	// There must be one result.
	if len(sig.Results) != 1 {
		return false
	}
	// It must have return type "string" from the universe.
	result := sig.Results[0].Type
	if types.IsIdentical(result, types.Typ[types.String]) {
		return true
	}
	return false
}
Esempio n. 3
0
// emitCompare emits to f code compute the boolean result of
// comparison comparison 'x op y'.
//
func emitCompare(f *Function, op token.Token, x, y Value) Value {
	// TODO(adonovan): fix: this is incomplete.
	xt := underlyingType(x.Type())
	yt := underlyingType(y.Type())

	// Special case to optimise a tagless SwitchStmt so that
	// these are equivalent
	//   switch { case e: ...}
	//   switch true { case e: ... }
	//   if e==true { ... }
	// even in the case when e's type is an interface.
	// TODO(adonovan): generalise to x==true, false!=y, etc.
	if x == vTrue && op == token.EQL {
		if yt, ok := yt.(*types.Basic); ok && yt.Info&types.IsBoolean != 0 {
			return y
		}
	}

	if types.IsIdentical(xt, yt) {
		// no conversion necessary
	} else if _, ok := xt.(*types.Interface); ok {
		y = emitConv(f, y, x.Type())
	} else if _, ok := yt.(*types.Interface); ok {
		x = emitConv(f, x, y.Type())
	} else if _, ok := x.(*Literal); ok {
		x = emitConv(f, x, y.Type())
	} else if _, ok := y.(*Literal); ok {
		y = emitConv(f, y, x.Type())
	} else {
		// other cases, e.g. channels.  No-op.
	}

	v := &BinOp{
		Op: op,
		X:  x,
		Y:  y,
	}
	v.setType(tBool)
	return f.emit(v)
}
Esempio n. 4
0
// conv converts the value x of type t_src to type t_dst and returns
// the result.  Possible cases are described with the ssa.Conv
// operator.  Panics if the dynamic conversion fails.
//
func conv(t_dst, t_src types.Type, x value) value {
	ut_src := underlyingType(t_src)
	ut_dst := underlyingType(t_dst)

	// Same underlying types?
	// TODO(adonovan): consider a dedicated ssa.ChangeType instruction.
	if types.IsIdentical(ut_dst, ut_src) {
		return x
	}

	// Destination type is not an "untyped" type.
	if b, ok := ut_dst.(*types.Basic); ok && b.Info&types.IsUntyped != 0 {
		panic("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: Conv should be ChangeInterface")
		} else {
			panic("oops: Conv 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 (bool, int64, uint64, float64,
	// complex128, or string), then we convert it to the desired
	// type.

	switch ut_src := ut_src.(type) {
	case *types.Signature:
		// TODO(adonovan): fix: this is a hacky workaround for the
		// unsound conversion of Signature types from
		// func(T)() to func()(T), i.e. arg0 <-> receiver
		// conversion.  Talk to gri about correct approach.
		fmt.Fprintln(os.Stderr, "Warning: unsound Signature conversion")
		return x

	case *types.Pointer:
		// *value to unsafe.Pointer?
		if ut_dst, ok := ut_dst.(*types.Basic); ok {
			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.Elt.(*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)

		// bool?
		if _, ok := x.(bool); ok {
			return 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 string(asInt(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.Elt.(*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.
			//
			// It 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.
			return (*value)(x.(unsafe.Pointer))
		}

		// 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))
}
Esempio n. 5
0
// emitConv emits to f code to convert Value val to exactly type typ,
// and returns the converted value.  Implicit conversions are implied
// by language assignability rules in the following operations:
//
// - from rvalue type to lvalue type in assignments.
// - from actual- to formal-parameter types in function calls.
// - from return value type to result type in return statements.
// - population of struct fields, array and slice elements, and map
//   keys and values within compoisite literals
// - from index value to index type in indexing expressions.
// - for both arguments of comparisons.
// - from value type to channel type in send expressions.
//
func emitConv(f *Function, val Value, typ types.Type) Value {
	// fmt.Printf("emitConv %s -> %s, %T", val.Type(), typ, val) // debugging

	// Identical types?  Conversion is a no-op.
	if types.IsIdentical(val.Type(), typ) {
		return val
	}

	ut_dst := underlyingType(typ)
	ut_src := underlyingType(val.Type())

	// Identical underlying types?  Conversion is a name change.
	if types.IsIdentical(ut_dst, ut_src) {
		// TODO(adonovan): make this use a distinct
		// instruction, ChangeType.  This instruction must
		// also cover the cases of channel type restrictions and
		// conversions between pointers to identical base
		// types.
		c := &Conv{X: val}
		c.setType(typ)
		return f.emit(c)
	}

	// Conversion to, or construction of a value of, an interface type?
	if _, ok := ut_dst.(*types.Interface); ok {

		// Assignment from one interface type to a different one?
		if _, ok := ut_src.(*types.Interface); ok {
			c := &ChangeInterface{X: val}
			c.setType(typ)
			return f.emit(c)
		}

		// Untyped nil literal?  Return interface-typed nil literal.
		if ut_src == tUntypedNil {
			return nilLiteral(typ)
		}

		// Convert (non-nil) "untyped" literals to their default type.
		// TODO(gri): expose types.isUntyped().
		if t, ok := ut_src.(*types.Basic); ok && t.Info&types.IsUntyped != 0 {
			val = emitConv(f, val, DefaultType(ut_src))
		}

		mi := &MakeInterface{
			X:       val,
			Methods: f.Prog.MethodSet(val.Type()),
		}
		mi.setType(typ)
		return f.emit(mi)
	}

	// Conversion of a literal to a non-interface type results in
	// a new literal of the destination type and (initially) the
	// same abstract value.  We don't compute the representation
	// change yet; this defers the point at which the number of
	// possible representations explodes.
	if l, ok := val.(*Literal); ok {
		return newLiteral(l.Value, typ)
	}

	// A representation-changing conversion.
	c := &Conv{X: val}
	c.setType(typ)
	return f.emit(c)
}
Esempio n. 6
0
func (x rtype) eq(y interface{}) bool {
	return types.IsIdentical(x.t, y.(rtype).t)
}
Esempio n. 7
0
func (x iface) eq(_y interface{}) bool {
	y := _y.(iface)
	return types.IsIdentical(x.t, y.t) && (x.t == nil || equals(x.v, y.v))
}