mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-22 13:29:01 +00:00
reflect: fully implement Type.AssignableTo
This fully implements AssignableTo, fixing a number of bugs in the previous implementation. In particular, it now supports AssignableTo for interface types.
This commit is contained in:
+26
-30
@@ -196,34 +196,30 @@ func TestImplements(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: AssignableTo should be possible to implement now that Implements()
|
var assignableTests = []struct {
|
||||||
// mostly works.
|
x any
|
||||||
|
t any
|
||||||
|
b bool
|
||||||
|
}{
|
||||||
|
{new(chan int), new(<-chan int), true},
|
||||||
|
{new(<-chan int), new(chan int), false},
|
||||||
|
{new(*int), new(IntPtr), true},
|
||||||
|
{new(IntPtr), new(*int), true},
|
||||||
|
{new(IntPtr), new(IntPtr1), false},
|
||||||
|
{new(Ch), new(<-chan any), true},
|
||||||
|
// test runs implementsTests too
|
||||||
|
}
|
||||||
|
|
||||||
//var assignableTests = []struct {
|
type IntPtr *int
|
||||||
// x any
|
type IntPtr1 *int
|
||||||
// t any
|
type Ch <-chan any
|
||||||
// b bool
|
|
||||||
//}{
|
func TestAssignableTo(t *testing.T) {
|
||||||
// {new(chan int), new(<-chan int), true},
|
for _, tt := range append(assignableTests, implementsTests...) {
|
||||||
// {new(<-chan int), new(chan int), false},
|
xv := TypeOf(tt.x).Elem()
|
||||||
// {new(*int), new(IntPtr), true},
|
xt := TypeOf(tt.t).Elem()
|
||||||
// {new(IntPtr), new(*int), true},
|
if b := xv.AssignableTo(xt); b != tt.b {
|
||||||
// {new(IntPtr), new(IntPtr1), false},
|
t.Errorf("(%s).AssignableTo(%s) = %v, want %v", xv.String(), xt.String(), b, tt.b)
|
||||||
// {new(Ch), new(<-chan any), true},
|
}
|
||||||
// // test runs implementsTests too
|
}
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//type IntPtr *int
|
|
||||||
//type IntPtr1 *int
|
|
||||||
//type Ch <-chan any
|
|
||||||
//
|
|
||||||
//func TestAssignableTo(t *testing.T) {
|
|
||||||
// for _, tt := range append(assignableTests, implementsTests...) {
|
|
||||||
// xv := TypeOf(tt.x).Elem()
|
|
||||||
// xt := TypeOf(tt.t).Elem()
|
|
||||||
// if b := xv.AssignableTo(xt); b != tt.b {
|
|
||||||
// t.Errorf("(%s).AssignableTo(%s) = %v, want %v", xv.String(), xt.String(), b, tt.b)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
|
|||||||
+41
-6
@@ -972,17 +972,52 @@ func (t *rawType) FieldAlign() int {
|
|||||||
// AssignableTo returns whether a value of type t can be assigned to a variable
|
// AssignableTo returns whether a value of type t can be assigned to a variable
|
||||||
// of type u.
|
// of type u.
|
||||||
func (t *rawType) AssignableTo(u Type) bool {
|
func (t *rawType) AssignableTo(u Type) bool {
|
||||||
if t == u.(*rawType) {
|
// Quotes come from the language spec:
|
||||||
return true
|
// https://go.dev/ref/spec#Assignability
|
||||||
}
|
// > A value x of type V is assignable to a variable of type T ("x is
|
||||||
|
// > assignable to T") if one of the following conditions applies:
|
||||||
if u.Kind() == Interface && u.NumMethod() == 0 {
|
// [...]
|
||||||
|
// (Replace T with t, and x with u).
|
||||||
|
u_raw := u.(*rawType)
|
||||||
|
if t == u_raw {
|
||||||
|
// > V and T are identical.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Kind() == Interface {
|
if u.Kind() == Interface {
|
||||||
panic("reflect: unimplemented: AssignableTo with interface")
|
// > T is an interface type, but not a type parameter, and x implements
|
||||||
|
// > T.
|
||||||
|
u_itf := (*interfaceType)(unsafe.Pointer(u_raw.underlying()))
|
||||||
|
res := typeImplementsMethodSet(unsafe.Pointer(t), unsafe.Pointer(&u_itf.methods))
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t_named := t.isNamed()
|
||||||
|
u_named := u_raw.isNamed()
|
||||||
|
if t_named && u_named {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t.underlying() == u_raw.underlying() {
|
||||||
|
// > V and T have identical underlying types but are not type parameters
|
||||||
|
// > and at least one of V or T is not a named type.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Kind() == Chan && u_raw.Kind() == Chan {
|
||||||
|
// > V and T are channel types with identical element types, V is a
|
||||||
|
// > bidirectional channel, and at least one of V or T is not a named
|
||||||
|
// > type.
|
||||||
|
t_chan := (*elemType)(unsafe.Pointer(t.underlying()))
|
||||||
|
u_chan := (*elemType)(unsafe.Pointer(u_raw.underlying()))
|
||||||
|
if t_chan.elem != u_chan.elem {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t_chan.ChanDir() != BothDir {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -592,15 +592,6 @@ func TestTinyNumMethods(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssignableTo(t *testing.T) {
|
|
||||||
var a any
|
|
||||||
refa := ValueOf(&a).Elem()
|
|
||||||
refa.Set(ValueOf(4))
|
|
||||||
if got, want := refa.Interface().(int), 4; got != want {
|
|
||||||
t.Errorf("AssignableTo / Set failed, got %v, want %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConvert(t *testing.T) {
|
func TestConvert(t *testing.T) {
|
||||||
v := ValueOf(int64(3))
|
v := ValueOf(int64(3))
|
||||||
c := v.Convert(TypeOf(byte(0)))
|
c := v.Convert(TypeOf(byte(0)))
|
||||||
|
|||||||
Reference in New Issue
Block a user