From ac330f4a70a802a430d617a19fb51ab644fda5b4 Mon Sep 17 00:00:00 2001 From: Konstantin Itskov Date: Tue, 17 Sep 2019 18:36:43 -0400 Subject: [PATCH] runtime: implement interface equality Code copied from Konstantin Itskov and modified by Ayke van Laethem. For details: https://github.com/tinygo-org/tinygo/pull/569 --- src/runtime/interface.go | 57 ++++++++++++++++++++++++++++++++++------ testdata/interface.go | 56 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 8 deletions(-) diff --git a/src/runtime/interface.go b/src/runtime/interface.go index d0fce58d..028ea31a 100644 --- a/src/runtime/interface.go +++ b/src/runtime/interface.go @@ -5,7 +5,10 @@ package runtime // Interfaces are represented as a pair of {typecode, value}, where value can be // anything (including non-pointers). -import "unsafe" +import ( + "reflect" + "unsafe" +) type _interface struct { typecode uintptr @@ -23,17 +26,55 @@ func decomposeInterface(i _interface) (uintptr, unsafe.Pointer) { } // Return true iff both interfaces are equal. -func interfaceEqual(x, y _interface) bool { - if x.typecode != y.typecode { - // Different dynamic type so always unequal. +func interfaceEqual(x, y interface{}) bool { + return reflectValueEqual(reflect.ValueOf(x), reflect.ValueOf(y)) +} + +func reflectValueEqual(x, y reflect.Value) bool { + if x.Type() == 0 || y.Type() == 0 { + // One of them is nil. + return x.Type() == y.Type() + } + + if x.Type() != y.Type() { + // The type is not the same, which means the interfaces are definitely + // not the same. return false } - if x.typecode == 0 { - // Both interfaces are nil, so they are equal. + + switch x.Type().Kind() { + case reflect.Bool: + return x.Bool() == y.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return x.Int() == y.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return x.Uint() == y.Uint() + case reflect.Float32, reflect.Float64: + return x.Float() == y.Float() + case reflect.Complex64, reflect.Complex128: + return x.Complex() == y.Complex() + case reflect.String: + return x.String() == y.String() + case reflect.Chan, reflect.Ptr, reflect.UnsafePointer: + return x.Pointer() == y.Pointer() + case reflect.Array: + for i := 0; i < x.Len(); i++ { + if !reflectValueEqual(x.Index(i), y.Index(i)) { + return false + } + } return true + case reflect.Struct: + for i := 0; i < x.NumField(); i++ { + if !reflectValueEqual(x.Field(i), y.Field(i)) { + return false + } + } + return true + default: + runtimePanic("comparing un-comparable type") + return false // unreachable } - // TODO: depends on reflection. - panic("unimplemented: interface equality") } // interfaceTypeAssert is called when a type assert without comma-ok still diff --git a/testdata/interface.go b/testdata/interface.go index 88b0944b..23f99770 100644 --- a/testdata/interface.go +++ b/testdata/interface.go @@ -30,6 +30,62 @@ func main() { // Try putting a linked list in an interface: // https://github.com/tinygo-org/tinygo/issues/309 itf = linkedList{} + + var n int + var f float32 + var interfaceEqualTests = []struct { + equal bool + lhs interface{} + rhs interface{} + }{ + {true, true, true}, + {true, int(1), int(1)}, + {true, int8(1), int8(1)}, + {true, int16(1), int16(1)}, + {true, int32(1), int32(1)}, + {true, int64(1), int64(1)}, + {true, uint(1), uint(1)}, + {false, uint(1), uint(2)}, + {true, uint8(1), uint8(1)}, + {true, uint16(1), uint16(1)}, + {true, uint32(1), uint32(1)}, + {true, uint64(1), uint64(1)}, + {true, float32(1.1), float32(1.1)}, + {true, float64(1.1), float64(1.1)}, + {true, complex(100, 8), complex(100, 8)}, + {false, complex(100, 8), complex(101, 8)}, + {false, complex(100, 8), complex(100, 9)}, + {true, complex64(8), complex64(8)}, + {true, complex128(8), complex128(8)}, + {true, "string", "string"}, + {false, "string", "stringx"}, + {true, [2]int16{-5, 201}, [2]int16{-5, 201}}, + {false, [2]int16{-5, 201}, [2]int16{-5, 202}}, + {false, [2]int16{-5, 201}, [2]int16{5, 201}}, + {true, &n, &n}, + {false, &n, new(int)}, + {false, new(int), new(int)}, + {false, &n, &f}, + {true, struct { + a int + b int + }{3, 5}, struct { + a int + b int + }{3, 5}}, + {false, struct { + a int + b int + }{3, 5}, struct { + a int + b int + }{3, 6}}, + } + for i, tc := range interfaceEqualTests { + if (tc.lhs == tc.rhs) != tc.equal { + println("test", i, "of interfaceEqualTests failed") + } + } } func printItf(val interface{}) {