Skip to content

Commit 48c88f1

Browse files
reflect: add Value.CanConvert
For #395 For #46746 Change-Id: I4bfc094cf1cecd27ce48e31f92384cf470f371a6 Reviewed-on: https://go-review.googlesource.com/c/go/+/334669 Trust: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Joe Tsai <[email protected]>
1 parent 9e26569 commit 48c88f1

File tree

4 files changed

+42
-0
lines changed

4 files changed

+42
-0
lines changed

api/go1.17.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pkg net/url, method (Values) Has(string) bool
8080
pkg reflect, func VisibleFields(Type) []StructField
8181
pkg reflect, method (Method) IsExported() bool
8282
pkg reflect, method (StructField) IsExported() bool
83+
pkg reflect, method (Value) CanConvert(Type) bool
8384
pkg runtime/cgo (darwin-amd64-cgo), func NewHandle(interface{}) Handle
8485
pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Delete()
8586
pkg runtime/cgo (darwin-amd64-cgo), method (Handle) Value() interface{}

doc/go1.17.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,18 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
989989

990990
<dl id="reflect"><dt><a href="/pkg/reflect/">reflect</a></dt>
991991
<dd>
992+
<p><!-- CL 334669 -->
993+
The new
994+
<a href="/pkg/reflect/#Value.CanConvert"><code>Value.CanConvert</code></a>
995+
method reports whether a value can be converted to a type.
996+
This may be used to avoid a panic when converting a slice to an
997+
array pointer type if the slice is too short.
998+
Previously it was sufficient to use
999+
<a href="/pkg/reflect/#Type.ConvertibleTo"><code>Type.ConvertibleTo</code></a>
1000+
for this, but the newly permitted conversion from slice to array
1001+
pointer type can panic even if the types are convertible.
1002+
</p>
1003+
9921004
<p><!-- CL 266197 -->
9931005
The new
9941006
<a href="/pkg/reflect/#StructField.IsExported"><code>StructField.IsExported</code></a>

src/reflect/all_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4304,13 +4304,19 @@ func TestConvert(t *testing.T) {
43044304

43054305
// vout1 represents the in value converted to the in type.
43064306
v1 := tt.in
4307+
if !v1.CanConvert(t1) {
4308+
t.Errorf("ValueOf(%T(%[1]v)).CanConvert(%s) = false, want true", tt.in.Interface(), t1)
4309+
}
43074310
vout1 := v1.Convert(t1)
43084311
out1 := vout1.Interface()
43094312
if vout1.Type() != tt.in.Type() || !DeepEqual(out1, tt.in.Interface()) {
43104313
t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t1, out1, tt.in.Interface())
43114314
}
43124315

43134316
// vout2 represents the in value converted to the out type.
4317+
if !v1.CanConvert(t2) {
4318+
t.Errorf("ValueOf(%T(%[1]v)).CanConvert(%s) = false, want true", tt.in.Interface(), t2)
4319+
}
43144320
vout2 := v1.Convert(t2)
43154321
out2 := vout2.Interface()
43164322
if vout2.Type() != tt.out.Type() || !DeepEqual(out2, tt.out.Interface()) {
@@ -4371,6 +4377,9 @@ func TestConvertPanic(t *testing.T) {
43714377
if !v.Type().ConvertibleTo(pt) {
43724378
t.Errorf("[]byte should be convertible to *[8]byte")
43734379
}
4380+
if v.CanConvert(pt) {
4381+
t.Errorf("slice with length 4 should not be convertible to *[8]byte")
4382+
}
43744383
shouldPanic("reflect: cannot convert slice with length 4 to pointer to array with length 8", func() {
43754384
_ = v.Convert(pt)
43764385
})

src/reflect/value.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2811,6 +2811,26 @@ func (v Value) Convert(t Type) Value {
28112811
return op(v, t)
28122812
}
28132813

2814+
// CanConvert reports whether the value v can be converted to type t.
2815+
// If v.CanConvert(t) returns true then v.Convert(t) will not panic.
2816+
func (v Value) CanConvert(t Type) bool {
2817+
vt := v.Type()
2818+
if !vt.ConvertibleTo(t) {
2819+
return false
2820+
}
2821+
// Currently the only conversion that is OK in terms of type
2822+
// but that can panic depending on the value is converting
2823+
// from slice to pointer-to-array.
2824+
if vt.Kind() == Slice && t.Kind() == Ptr && t.Elem().Kind() == Array {
2825+
n := t.Elem().Len()
2826+
h := (*unsafeheader.Slice)(v.ptr)
2827+
if n > h.Len {
2828+
return false
2829+
}
2830+
}
2831+
return true
2832+
}
2833+
28142834
// convertOp returns the function to convert a value of type src
28152835
// to a value of type dst. If the conversion is illegal, convertOp returns nil.
28162836
func convertOp(dst, src *rtype) func(Value, Type) Value {

0 commit comments

Comments
 (0)