// 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 }
// emitTypeAssert emits to f a type assertion value := x.(t) and // returns the value. x.Type() must be an interface. // func emitTypeAssert(f *Function, x Value, t types.Type) Value { // Simplify infallible assertions. txi := underlyingType(x.Type()).(*types.Interface) if ti, ok := underlyingType(t).(*types.Interface); ok { if types.IsIdentical(ti, txi) { return x } if isSuperinterface(ti, txi) { c := &ChangeInterface{X: x} c.setType(t) return f.emit(c) } } a := &TypeAssert{X: x, AssertedType: t} a.setType(t) return f.emit(a) }
// isSuperinterface returns true if x is a superinterface of y, // i.e. x's methods are a subset of y's. // func isSuperinterface(x, y *types.Interface) bool { if len(y.Methods) < len(x.Methods) { return false } // TODO(adonovan): opt: this is quadratic. outer: for _, xm := range x.Methods { for _, ym := range y.Methods { if IdFromQualifiedName(xm.QualifiedName) == IdFromQualifiedName(ym.QualifiedName) { if !types.IsIdentical(xm.Type, ym.Type) { return false // common name but conflicting types } continue outer } } return false // y doesn't have this method } return true }
// 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". 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 }
// 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 { 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): opt: 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) }
// 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 another? if _, ok := ut_src.(*types.Interface); ok { return emitTypeAssert(f, val, typ) } // 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) }
// 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. // TODO(adonovan): fix: what about channels of different direction? 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: 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.Pointer: return x } 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)) }
func (x rtype) eq(y interface{}) bool { return types.IsIdentical(x.t, y.(rtype).t) }
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)) }