]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: allow conversion from slice to array
authorCuong Manh Le <cuong.manhle.vn@gmail.com>
Tue, 13 Sep 2022 05:19:37 +0000 (12:19 +0700)
committerCuong Manh Le <cuong.manhle.vn@gmail.com>
Fri, 16 Sep 2022 00:17:05 +0000 (00:17 +0000)
Updates #46505

Change-Id: Ib8f52d6ae199338f278731267c966da85dd0acdd
Reviewed-on: https://go-review.googlesource.com/c/go/+/430475
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>

src/reflect/all_test.go
src/reflect/value.go

index 65ecc413775d0115c0cdd47249cf49acfa10c413..e97f69904df01866ff52303c0e50ec60a2bbef63 100644 (file)
@@ -4323,6 +4323,25 @@ var convertTests = []struct {
        {V(MyString("runes♝")), V(MyRunes("runes♝"))},
        {V(MyRunes("runes♕")), V(MyString("runes♕"))},
 
+       // slice to array
+       {V([]byte(nil)), V([0]byte{})},
+       {V([]byte{}), V([0]byte{})},
+       {V([]byte{1}), V([1]byte{1})},
+       {V([]byte{1, 2}), V([2]byte{1, 2})},
+       {V([]byte{1, 2, 3}), V([3]byte{1, 2, 3})},
+       {V(MyBytes([]byte(nil))), V([0]byte{})},
+       {V(MyBytes{}), V([0]byte{})},
+       {V(MyBytes{1}), V([1]byte{1})},
+       {V(MyBytes{1, 2}), V([2]byte{1, 2})},
+       {V(MyBytes{1, 2, 3}), V([3]byte{1, 2, 3})},
+       {V([]byte(nil)), V(MyBytesArray0{})},
+       {V([]byte{}), V(MyBytesArray0([0]byte{}))},
+       {V([]byte{1, 2, 3, 4}), V(MyBytesArray([4]byte{1, 2, 3, 4}))},
+       {V(MyBytes{}), V(MyBytesArray0([0]byte{}))},
+       {V(MyBytes{5, 6, 7, 8}), V(MyBytesArray([4]byte{5, 6, 7, 8}))},
+       {V([]MyByte{}), V([0]MyByte{})},
+       {V([]MyByte{1, 2}), V([2]MyByte{1, 2})},
+
        // slice to array pointer
        {V([]byte(nil)), V((*[0]byte)(nil))},
        {V([]byte{}), V(new([0]byte))},
@@ -4399,6 +4418,8 @@ var convertTests = []struct {
        // cannot convert mismatched array sizes
        {V([2]byte{}), V([2]byte{})},
        {V([3]byte{}), V([3]byte{})},
+       {V(MyBytesArray0{}), V([0]byte{})},
+       {V([0]byte{}), V(MyBytesArray0{})},
 
        // cannot convert other instances
        {V((**byte)(nil)), V((**byte)(nil))},
@@ -4574,6 +4595,34 @@ func TestConvertPanic(t *testing.T) {
        shouldPanic("reflect: cannot convert slice with length 4 to pointer to array with length 8", func() {
                _ = v.Convert(pt)
        })
+
+       if v.CanConvert(pt.Elem()) {
+               t.Errorf("slice with length 4 should not be convertible to [8]byte")
+       }
+       shouldPanic("reflect: cannot convert slice with length 4 to array with length 8", func() {
+               _ = v.Convert(pt.Elem())
+       })
+}
+
+func TestConvertSlice2Array(t *testing.T) {
+       s := make([]int, 4)
+       p := [4]int{}
+       pt := TypeOf(p)
+       ov := ValueOf(s)
+       v := ov.Convert(pt)
+       // Converting a slice to non-empty array needs to return
+       // a non-addressable copy of the original memory.
+       if v.CanAddr() {
+               t.Fatalf("convert slice to non-empty array returns a addressable copy array")
+       }
+       for i := range s {
+               ov.Index(i).Set(ValueOf(i + 1))
+       }
+       for i := range s {
+               if v.Index(i).Int() != 0 {
+                       t.Fatalf("slice (%v) mutation visible in converted result (%v)", ov, v)
+               }
+       }
 }
 
 var gFloat32 float32
index 4e5d3977ec8e4167436a6d24e094a376f2f80ff5..4456fdc5a57450f479019d14cf425e161a9a4fab 100644 (file)
@@ -3245,10 +3245,14 @@ func (v Value) CanConvert(t Type) bool {
        if !vt.ConvertibleTo(t) {
                return false
        }
-       // Currently the only conversion that is OK in terms of type
-       // but that can panic depending on the value is converting
-       // from slice to pointer-to-array.
-       if vt.Kind() == Slice && t.Kind() == Pointer && t.Elem().Kind() == Array {
+       // Converting from slice to array or to pointer-to-array can panic
+       // depending on the value.
+       switch {
+       case vt.Kind() == Slice && t.Kind() == Array:
+               if t.Len() > v.Len() {
+                       return false
+               }
+       case vt.Kind() == Slice && t.Kind() == Pointer && t.Elem().Kind() == Array:
                n := t.Elem().Len()
                if n > v.Len() {
                        return false
@@ -3401,6 +3405,11 @@ func convertOp(dst, src *rtype) func(Value, Type) Value {
                if dst.Kind() == Pointer && dst.Elem().Kind() == Array && src.Elem() == dst.Elem().Elem() {
                        return cvtSliceArrayPtr
                }
+               // "x is a slice, T is a array type,
+               // and the slice and array types have identical element types."
+               if dst.Kind() == Array && src.Elem() == dst.Elem() {
+                       return cvtSliceArray
+               }
 
        case Chan:
                if dst.Kind() == Chan && specialChannelAssignability(dst, src) {
@@ -3604,6 +3613,22 @@ func cvtSliceArrayPtr(v Value, t Type) Value {
        return Value{t.common(), h.Data, v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Pointer)}
 }
 
+// convertOp: []T -> [N]T
+func cvtSliceArray(v Value, t Type) Value {
+       n := t.Len()
+       if n > v.Len() {
+               panic("reflect: cannot convert slice with length " + itoa.Itoa(v.Len()) + " to array with length " + itoa.Itoa(n))
+       }
+       h := (*unsafeheader.Slice)(v.ptr)
+       typ := t.common()
+       ptr := h.Data
+       c := unsafe_New(typ)
+       typedmemmove(typ, c, ptr)
+       ptr = c
+
+       return Value{typ, ptr, v.flag&^(flagAddr|flagKindMask) | flag(Array)}
+}
+
 // convertOp: direct copy
 func cvtDirect(v Value, typ Type) Value {
        f := v.flag