diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 65ecc41377..e97f69904d 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -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 diff --git a/src/reflect/value.go b/src/reflect/value.go index 4e5d3977ec..4456fdc5a5 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -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