Josh Bleecher Snyder has uploaded this change for review.
reflect: allow conversion from slice to array ptr
Note that this removes an invariant:
v.Type().ConvertibleTo(t) might return true,
yet v.Convert(t) might panic nevertheless.
This is a fairly unavoidable consequence of the decision
to add the first-ever conversion that can panic.
ConvertibleTo describes a relationship between types,
but whether the conversion panics now depends on the value,
not just the type.
If this turns out to be a problem, we can add v.ConvertibleTo(t),
or something similar, to allow callers to avoid the panic.
This is the last of the changes needed to complete the implementation.
Fixes #395
Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
---
M src/reflect/all_test.go
M src/reflect/type.go
M src/reflect/value.go
3 files changed, 60 insertions(+), 1 deletion(-)
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 35cc469..41867a5 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -3794,6 +3794,10 @@
}
type MyString string
type MyBytes []byte
+type MyBytesArrayPtr0 *[0]byte
+type MyBytesArrayPtr *[4]byte
+type MyBytesArray0 [0]byte
+type MyBytesArray [4]byte
type MyRunes []int32
type MyFunc func()
type MyByte byte
@@ -4100,6 +4104,30 @@
{V(MyString("runes♝")), V(MyRunes("runes♝"))},
{V(MyRunes("runes♕")), V(MyString("runes♕"))},
+ // slice to array pointer
+ {V([]byte(nil)), V((*[0]byte)(nil))},
+ {V([]byte{}), V(new([0]byte))},
+ {V([]byte{7}), V(&[1]byte{7})},
+ {V(MyBytes([]byte(nil))), V((*[0]byte)(nil))},
+ {V(MyBytes([]byte{})), V(new([0]byte))},
+ {V(MyBytes([]byte{9})), V(&[1]byte{9})},
+ {V([]byte(nil)), V(MyBytesArrayPtr0(nil))},
+ {V([]byte{}), V(MyBytesArrayPtr0(new([0]byte)))},
+ {V([]byte{1, 2, 3, 4}), V(MyBytesArrayPtr(&[4]byte{1, 2, 3, 4}))},
+ {V(MyBytes([]byte{})), V(MyBytesArrayPtr0(new([0]byte)))},
+ {V(MyBytes([]byte{5, 6, 7, 8})), V(MyBytesArrayPtr(&[4]byte{5, 6, 7, 8}))},
+
+ {V([]byte(nil)), V((*MyBytesArray0)(nil))},
+ {V([]byte{}), V((*MyBytesArray0)(new([0]byte)))},
+ {V([]byte{1, 2, 3, 4}), V(&MyBytesArray{1, 2, 3, 4})},
+ {V(MyBytes([]byte(nil))), V((*MyBytesArray0)(nil))},
+ {V(MyBytes([]byte{})), V((*MyBytesArray0)(new([0]byte)))},
+ {V(MyBytes([]byte{5, 6, 7, 8})), V(&MyBytesArray{5, 6, 7, 8})},
+ {V(new([0]byte)), V(new(MyBytesArray0))},
+ {V(new(MyBytesArray0)), V(new([0]byte))},
+ {V(MyBytesArrayPtr0(nil)), V((*[0]byte)(nil))},
+ {V((*[0]byte)(nil)), V(MyBytesArrayPtr0(nil))},
+
// named types and equal underlying types
{V(new(int)), V(new(integer))},
{V(new(integer)), V(new(int))},
@@ -4301,6 +4329,19 @@
}
}
+func TestConvertPanic(t *testing.T) {
+ s := make([]byte, 4)
+ p := new([8]byte)
+ v := ValueOf(s)
+ pt := TypeOf(p)
+ if !v.Type().ConvertibleTo(pt) {
+ t.Errorf("[]byte should be convertible to *[8]byte")
+ }
+ shouldPanic("reflect: cannot convert slice with capacity 4 to array pointer with length 8", func() {
+ _ = v.Convert(pt)
+ })
+}
+
var gFloat32 float32
func TestConvertNaNs(t *testing.T) {
diff --git a/src/reflect/type.go b/src/reflect/type.go
index dc235ea..6a9906b 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -106,6 +106,7 @@
AssignableTo(u Type) bool
// ConvertibleTo reports whether a value of the type is convertible to type u.
+ // Even if ConvertibleTo returns true, the conversion may still panic.
ConvertibleTo(u Type) bool
// Comparable reports whether values of this type are comparable.
diff --git a/src/reflect/value.go b/src/reflect/value.go
index eae1b9b..a598dae 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -6,6 +6,7 @@
import (
"internal/abi"
+ "internal/itoa"
"internal/unsafeheader"
"math"
"runtime"
@@ -2576,7 +2577,7 @@
// Convert returns the value v converted to type t.
// If the usual Go conversion rules do not allow conversion
-// of the value v to type t, Convert panics.
+// of the value v to type t, or if converting v to type t panics, Convert panics.
func (v Value) Convert(t Type) Value {
if v.flag&flagMethod != 0 {
v = makeMethodValue("Convert", v)
@@ -2647,6 +2648,9 @@
return cvtRunesString
}
}
+ if dst.Kind() == Ptr && dst.Elem().Kind() == Array && src.Elem() == dst.Elem().Elem() {
+ return cvtSliceArrayPtr
+ }
case Chan:
if dst.Kind() == Chan && specialChannelAssignability(dst, src) {
@@ -2840,6 +2844,19 @@
return makeRunes(v.flag.ro(), []rune(v.String()), t)
}
+// convertOp: []T -> *[N]T
+func cvtSliceArrayPtr(v Value, t Type) Value {
+ n := t.Elem().Len()
+ h := (*unsafeheader.Slice)(v.ptr)
+ if n > h.Cap {
+ panic("reflect: cannot convert slice with capacity " + itoa.Itoa(h.Cap) + " to array pointer with length " + itoa.Itoa(n))
+ }
+ ret := New(t).Elem()
+ ret.ptr = h.Data
+ ret.flag = v.flag &^ flagIndir
+ return ret
+}
+
// convertOp: direct copy
func cvtDirect(v Value, typ Type) Value {
f := v.flag
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Russ Cox.
Josh Bleecher Snyder uploaded patch set #2 to this change.
reflect: allow conversion from slice to array ptr
Note that this removes an invariant:
v.Type().ConvertibleTo(t) might return true,
yet v.Convert(t) might panic nevertheless.
This is a fairly unavoidable consequence of the decision
to add the first-ever conversion that can panic.
ConvertibleTo describes a relationship between types,
but whether the conversion panics now depends on the value,
not just the type.
If this turns out to be a problem, we can add v.ConvertibleTo(t),
or something similar, to allow callers to avoid the panic.
This is the last of the changes needed to complete the implementation.
Fixes #395
Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
---
M src/reflect/all_test.go
M src/reflect/type.go
M src/reflect/value.go
3 files changed, 60 insertions(+), 1 deletion(-)
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.
Josh Bleecher Snyder uploaded patch set #5 to this change.
Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.
3 comments:
File src/reflect/type.go:
Patch Set #6, Line 113: Comparable() bool
Add the same "Even if" warning here too?
File src/reflect/value.go:
Patch Set #6, Line 2852: panic("reflect: cannot convert slice with capacity " + itoa.Itoa(h.Cap) + " to array pointer with length " + itoa.Itoa(n))
Should this panic produce the same runtime.Error that an out-of-bounds conversion would normally produce, instead of a string?
Patch Set #6, Line 2854: ret := New(t).Elem()
This will allocate a new pointer that goes dead right away. Probably better to directly construct the Value.
(I think "NewAt(t.Elem(), h.Data)" is almost what you want, but that wouldn't handle if t is a defined type.)
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Matthew Dempsky, Russ Cox.
3 comments:
File src/reflect/type.go:
Patch Set #6, Line 113: Comparable() bool
Add the same "Even if" warning here too?
Done
File src/reflect/value.go:
Patch Set #6, Line 2852: panic("reflect: cannot convert slice with capacity " + itoa.Itoa(h.Cap) + " to array pointer with length " + itoa.Itoa(n))
Should this panic produce the same runtime. […]
I don't know. I don't see that happen anywhere else in package reflect.
Patch Set #6, Line 2854: ret := New(t).Elem()
This will allocate a new pointer that goes dead right away. […]
Done
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.
1 comment:
File src/reflect/value.go:
Patch Set #6, Line 2852: panic("reflect: cannot convert slice with capacity " + itoa.Itoa(h.Cap) + " to array pointer with length " + itoa.Itoa(n))
I don't know. I don't see that happen anywhere else in package reflect.
Fair point. Value.Index panics with "reflect: array index out of range" instead of raising runtime.Error, so a string panic here seems consistent.
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.
Josh Bleecher Snyder uploaded patch set #7 to this change.
reflect: allow conversion from slice to array ptr
Note that this removes an invariant:
v.Type().ConvertibleTo(t) might return true,
yet v.Convert(t) might panic nevertheless.
This is a fairly unavoidable consequence of the decision
to add the first-ever conversion that can panic.
ConvertibleTo describes a relationship between types,
but whether the conversion panics now depends on the value,
not just the type.
If this turns out to be a problem, we can add v.ConvertibleTo(t),
or something similar, to allow callers to avoid the panic.
This is the last of the changes needed to complete the implementation.
Fixes #395
Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
---
M src/reflect/all_test.go
M src/reflect/type.go
M src/reflect/value.go
3 files changed, 60 insertions(+), 1 deletion(-)
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Josh Bleecher Snyder, Russ Cox.
1 comment:
File src/reflect/value.go:
Patch Set #7, Line 2856: return Value{t.common(), h.Data, v.flag &^ flagIndir}
Seems like you have to clear flagAddr here as well as flagIndir.
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Russ Cox.
1 comment:
File src/reflect/value.go:
Patch Set #7, Line 2856: return Value{t.common(), h.Data, v.flag &^ flagIndir}
Seems like you have to clear flagAddr here as well as flagIndir.
Done. Thanks, Ian.
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Russ Cox.
Josh Bleecher Snyder uploaded patch set #8 to this change.
reflect: allow conversion from slice to array ptr
Note that this removes an invariant:
v.Type().ConvertibleTo(t) might return true,
yet v.Convert(t) might panic nevertheless.
This is a fairly unavoidable consequence of the decision
to add the first-ever conversion that can panic.
ConvertibleTo describes a relationship between types,
but whether the conversion panics now depends on the value,
not just the type.
If this turns out to be a problem, we can add v.ConvertibleTo(t),
or something similar, to allow callers to avoid the panic.
This is the last of the changes needed to complete the implementation.
Fixes #395
Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
---
M src/reflect/all_test.go
M src/reflect/type.go
M src/reflect/value.go
3 files changed, 60 insertions(+), 1 deletion(-)
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.
1 comment:
File src/reflect/value.go:
Patch Set #8, Line 2856: v.flag
I think the embedded Kind needs to be Ptr too. Perhaps TestConvert should also check that Kind is correct, in addition to Type.
I'd also probably write "x &^ (y | z)" instead of "x &^ y &^ z", but either way is fine.
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.
Josh Bleecher Snyder uploaded patch set #9 to this change.
reflect: allow conversion from slice to array ptr
Note that this removes an invariant:
v.Type().ConvertibleTo(t) might return true,
yet v.Convert(t) might panic nevertheless.
This is a fairly unavoidable consequence of the decision
to add the first-ever conversion that can panic.
ConvertibleTo describes a relationship between types,
but whether the conversion panics now depends on the value,
not just the type.
If this turns out to be a problem, we can add v.ConvertibleTo(t),
or something similar, to allow callers to avoid the panic.
This is the last of the changes needed to complete the implementation.
Fixes #395
Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
---
M src/reflect/all_test.go
M src/reflect/type.go
M src/reflect/value.go
3 files changed, 63 insertions(+), 1 deletion(-)
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Matthew Dempsky, Russ Cox.
1 comment:
File src/reflect/value.go:
Patch Set #8, Line 2856: v.flag
I think the embedded Kind needs to be Ptr too. […]
Nice catch. Added a test that catches the bug, and fixed.
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Matthew Dempsky, Russ Cox.
Josh Bleecher Snyder uploaded patch set #10 to this change.
reflect: allow conversion from slice to array ptr
Note that this removes an invariant:
v.Type().ConvertibleTo(t) might return true,
yet v.Convert(t) might panic nevertheless.
This is a fairly unavoidable consequence of the decision
to add the first-ever conversion that can panic.
ConvertibleTo describes a relationship between types,
but whether the conversion panics now depends on the value,
not just the type.
If this turns out to be a problem, we can add v.ConvertibleTo(t),
or something similar, to allow callers to avoid the panic.
This is the last of the changes needed to complete the implementation.
Fixes #395
Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
---
M src/reflect/all_test.go
M src/reflect/type.go
M src/reflect/value.go
3 files changed, 63 insertions(+), 1 deletion(-)
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.
Patch set 11:Code-Review +2
1 comment:
File src/reflect/value.go:
Patch Set #11, Line 2853: h.Cap
h.Len?
Error message needs revising too.
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Russ Cox.
1 comment:
File src/reflect/value.go:
h.Len? […]
Done, thanks.
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Ian Lance Taylor, Russ Cox.
Josh Bleecher Snyder uploaded patch set #13 to this change.
reflect: allow conversion from slice to array ptr
Note that this removes an invariant:
v.Type().ConvertibleTo(t) might return true,
yet v.Convert(t) might panic nevertheless.
This is a fairly unavoidable consequence of the decision
to add the first-ever conversion that can panic.
ConvertibleTo describes a relationship between types,
but whether the conversion panics now depends on the value,
not just the type.
If this turns out to be a problem, we can add v.ConvertibleTo(t),
or something similar, to allow callers to avoid the panic.
This is the last of the changes needed to complete the implementation.
Fixes #395
Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
---
M src/reflect/all_test.go
M src/reflect/type.go
M src/reflect/value.go
3 files changed, 63 insertions(+), 1 deletion(-)
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.
Josh Bleecher Snyder submitted this change.
reflect: allow conversion from slice to array ptr
Note that this removes an invariant:
v.Type().ConvertibleTo(t) might return true,
yet v.Convert(t) might panic nevertheless.
This is a fairly unavoidable consequence of the decision
to add the first-ever conversion that can panic.
ConvertibleTo describes a relationship between types,
but whether the conversion panics now depends on the value,
not just the type.
If this turns out to be a problem, we can add v.ConvertibleTo(t),
or something similar, to allow callers to avoid the panic.
This is the last of the changes needed to complete the implementation.
Fixes #395
Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Reviewed-on: https://go-review.googlesource.com/c/go/+/301652
Trust: Josh Bleecher Snyder <josh...@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josh...@gmail.com>
TryBot-Result: Go Bot <go...@golang.org>
Reviewed-by: Matthew Dempsky <mdem...@google.com>
---
M src/reflect/all_test.go
M src/reflect/type.go
M src/reflect/value.go
3 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 3269f5f..065ff046 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -3822,6 +3822,10 @@
}
type MyString string
type MyBytes []byte
+type MyBytesArrayPtr0 *[0]byte
+type MyBytesArrayPtr *[4]byte
+type MyBytesArray0 [0]byte
+type MyBytesArray [4]byte
type MyRunes []int32
type MyFunc func()
type MyByte byte
@@ -4128,6 +4132,30 @@@@ -4288,6 +4316,9 @@
if vout2.Type() != tt.out.Type() || !DeepEqual(out2, tt.out.Interface()) {
t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t2, out2, tt.out.Interface())
}
+ if got, want := vout2.Kind(), vout2.Type().Kind(); got != want {
+ t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) has internal kind %v want %v", tt.in.Interface(), t1, got, want)
+ }
// vout3 represents a new value of the out type, set to vout2. This makes
// sure the converted value vout2 is really usable as a regular value.
@@ -4332,6 +4363,19 @@
}
}
+func TestConvertPanic(t *testing.T) {
+ s := make([]byte, 4)
+ p := new([8]byte)
+ v := ValueOf(s)
+ pt := TypeOf(p)
+ if !v.Type().ConvertibleTo(pt) {
+ t.Errorf("[]byte should be convertible to *[8]byte")
+ }
+ shouldPanic("reflect: cannot convert slice with length 4 to array pointer with length 8", func() {
+ _ = v.Convert(pt)
+ })
+}
+
var gFloat32 float32
func TestConvertNaNs(t *testing.T) {
diff --git a/src/reflect/type.go b/src/reflect/type.go
index d50559e..9727bfe 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -106,9 +106,11 @@
AssignableTo(u Type) bool
// ConvertibleTo reports whether a value of the type is convertible to type u.
+ // Even if ConvertibleTo returns true, the conversion may still panic.
ConvertibleTo(u Type) bool
// Comparable reports whether values of this type are comparable.
+ // Even if Comparable returns true, the comparison may still panic.
Comparable() bool
// Methods applicable only to some types, depending on Kind.
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 6f1a3c0..418dff7 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -6,6 +6,7 @@
import (
"internal/abi"
+ "internal/itoa"
"internal/unsafeheader"
"math"
"runtime"
@@ -2770,7 +2771,7 @@
// Convert returns the value v converted to type t.
// If the usual Go conversion rules do not allow conversion
-// of the value v to type t, Convert panics.
+// of the value v to type t, or if converting v to type t panics, Convert panics.
func (v Value) Convert(t Type) Value {
if v.flag&flagMethod != 0 {
v = makeMethodValue("Convert", v)
@@ -2841,6 +2842,11 @@
return cvtRunesString
}
}
+ // "x is a slice, T is a pointer-to-array type,
+ // and the slice and array types have identical element types."
+ if dst.Kind() == Ptr && dst.Elem().Kind() == Array && src.Elem() == dst.Elem().Elem() {
+ return cvtSliceArrayPtr
+ }
case Chan:
if dst.Kind() == Chan && specialChannelAssignability(dst, src) {
@@ -3034,6 +3040,16 @@
return makeRunes(v.flag.ro(), []rune(v.String()), t)
}
+// convertOp: []T -> *[N]T
+func cvtSliceArrayPtr(v Value, t Type) Value {
+ n := t.Elem().Len()
+ h := (*unsafeheader.Slice)(v.ptr)
+ if n > h.Len {
+ panic("reflect: cannot convert slice with length " + itoa.Itoa(h.Len) + " to array pointer with length " + itoa.Itoa(n))
+ }
+ return Value{t.common(), h.Data, v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Ptr)}
+}
+
// convertOp: direct copy
func cvtDirect(v Value, typ Type) Value {
f := v.flag
To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.