[go] reflect: allow conversion from slice to array ptr

226 views
Skip to first unread message

Josh Bleecher Snyder (Gerrit)

unread,
Mar 14, 2021, 5:38:56 PM3/14/21
to goph...@pubsubhelper.golang.org, Josh Bleecher Snyder, golang-co...@googlegroups.com

Josh Bleecher Snyder has uploaded this change for review.

View 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(-)

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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 1
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-MessageType: newchange

Josh Bleecher Snyder (Gerrit)

unread,
Mar 14, 2021, 5:47:14 PM3/14/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Russ Cox.

Josh Bleecher Snyder uploaded patch set #2 to this change.

View 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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 2
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Go Bot <go...@golang.org>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-MessageType: newpatchset

Josh Bleecher Snyder (Gerrit)

unread,
Mar 14, 2021, 7:15:57 PM3/14/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.

Josh Bleecher Snyder uploaded patch set #5 to this change.

Gerrit-PatchSet: 5
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Josh Bleecher Snyder <josh...@gmail.com>

Matthew Dempsky (Gerrit)

unread,
Mar 15, 2021, 12:19:58 AM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, Go Bot, Ian Lance Taylor, Russ Cox, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.

View Change

3 comments:

  • File src/reflect/type.go:

  • 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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 6
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-Comment-Date: Mon, 15 Mar 2021 04:19:53 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment

Josh Bleecher Snyder (Gerrit)

unread,
Mar 15, 2021, 2:26:35 PM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, Matthew Dempsky, Go Bot, Ian Lance Taylor, Russ Cox, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Matthew Dempsky, Russ Cox.

View Change

3 comments:

  • File src/reflect/type.go:

    • 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.

    • 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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 6
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-Comment-Date: Mon, 15 Mar 2021 18:26:30 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Matthew Dempsky <mdem...@google.com>
Gerrit-MessageType: comment

Matthew Dempsky (Gerrit)

unread,
Mar 15, 2021, 3:17:23 PM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, Go Bot, Ian Lance Taylor, Russ Cox, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.

View Change

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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 6
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-Comment-Date: Mon, 15 Mar 2021 19:17:18 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Josh Bleecher Snyder <josh...@gmail.com>

Josh Bleecher Snyder (Gerrit)

unread,
Mar 15, 2021, 3:29:45 PM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.

Josh Bleecher Snyder uploaded patch set #7 to this change.

View 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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 7
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-MessageType: newpatchset

Ian Lance Taylor (Gerrit)

unread,
Mar 15, 2021, 3:40:29 PM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, Matthew Dempsky, Go Bot, Russ Cox, golang-co...@googlegroups.com

Attention is currently required from: Josh Bleecher Snyder, Russ Cox.

View Change

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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 7
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-Comment-Date: Mon, 15 Mar 2021 19:40:25 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment

Josh Bleecher Snyder (Gerrit)

unread,
Mar 15, 2021, 3:46:05 PM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, Go Bot, Matthew Dempsky, Ian Lance Taylor, Russ Cox, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Russ Cox.

View Change

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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 7
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-Comment-Date: Mon, 15 Mar 2021 19:46:00 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Ian Lance Taylor <ia...@golang.org>
Gerrit-MessageType: comment

Josh Bleecher Snyder (Gerrit)

unread,
Mar 15, 2021, 3:46:24 PM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Russ Cox.

Josh Bleecher Snyder uploaded patch set #8 to this change.

View 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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 8
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-MessageType: newpatchset

Matthew Dempsky (Gerrit)

unread,
Mar 15, 2021, 4:49:12 PM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, Go Bot, Ian Lance Taylor, Russ Cox, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.

View Change

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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 8
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-Comment-Date: Mon, 15 Mar 2021 20:49:07 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Gerrit-MessageType: comment

Josh Bleecher Snyder (Gerrit)

unread,
Mar 15, 2021, 9:27:44 PM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.

Josh Bleecher Snyder uploaded patch set #9 to this change.

View 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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 9
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-MessageType: newpatchset

Josh Bleecher Snyder (Gerrit)

unread,
Mar 15, 2021, 9:28:17 PM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, Go Bot, Matthew Dempsky, Ian Lance Taylor, Russ Cox, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Matthew Dempsky, Russ Cox.

View Change

1 comment:

  • File src/reflect/value.go:

    • 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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 9
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-Comment-Date: Tue, 16 Mar 2021 01:28:12 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No

Josh Bleecher Snyder (Gerrit)

unread,
Mar 15, 2021, 9:47:40 PM3/15/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Matthew Dempsky, Russ Cox.

Josh Bleecher Snyder uploaded patch set #10 to this change.

View 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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 10
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Matthew Dempsky <mdem...@google.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-MessageType: newpatchset

Matthew Dempsky (Gerrit)

unread,
Apr 8, 2021, 4:31:52 PM4/8/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, Go Bot, Ian Lance Taylor, Russ Cox, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Josh Bleecher Snyder, Russ Cox.

Patch set 11:Code-Review +2

View Change

1 comment:

To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 11
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-Comment-Date: Thu, 08 Apr 2021 20:31:47 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment

Josh Bleecher Snyder (Gerrit)

unread,
Apr 10, 2021, 9:08:19 PM4/10/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, Matthew Dempsky, Go Bot, Ian Lance Taylor, Russ Cox, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Russ Cox.

View Change

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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 12
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-Comment-Date: Sun, 11 Apr 2021 01:08:15 +0000

Josh Bleecher Snyder (Gerrit)

unread,
Apr 10, 2021, 9:12:30 PM4/10/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Attention is currently required from: Ian Lance Taylor, Russ Cox.

Josh Bleecher Snyder uploaded patch set #13 to this change.

View 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.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 13
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-Attention: Ian Lance Taylor <ia...@golang.org>
Gerrit-Attention: Russ Cox <r...@golang.org>
Gerrit-MessageType: newpatchset

Josh Bleecher Snyder (Gerrit)

unread,
Apr 20, 2021, 8:54:44 PM4/20/21
to Josh Bleecher Snyder, goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, Go Bot, Matthew Dempsky, Ian Lance Taylor, Russ Cox, golang-co...@googlegroups.com

Josh Bleecher Snyder submitted this change.

View Change

Approvals: Matthew Dempsky: Looks good to me, approved Josh Bleecher Snyder: Trusted; Run TryBots Go Bot: TryBots succeeded
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

11 is the latest approved patch-set. The change was submitted with unreviewed changes in the following files: The name of the file: src/reflect/all_test.go Insertions: 1, Deletions: 1. ``` @@ +17:19 @@ + "reflect/internal/example1" + "reflect/internal/example2" @@ +1946:1962 @@ + type myint int64 + + func (i *myint) inc() { + *i = *i + 1 + } + + func BenchmarkCallMethod(b *testing.B) { + b.ReportAllocs() + z := new(myint) + + v := ValueOf(z.inc) + for i := 0; i < b.N; i++ { + v.Call(nil) + } + } + @@ +3812:3822 @@ + type MyStruct1 struct { + x struct { + int `some:"bar"` + } + } + type MyStruct2 struct { + x struct { + int `some:"foo"` + } + } @@ +4200:4203 @@ + {V(MyStruct1{}), V(MyStruct2{})}, + {V(MyStruct2{}), V(MyStruct1{})}, + @@ -4342:4343, +4373:4374 @@ - shouldPanic("reflect: cannot convert slice with capacity 4 to array pointer with length 8", func() { + shouldPanic("reflect: cannot convert slice with length 4 to array pointer with length 8", func() { @@ -6426:6439, +6457:6460 @@ - type funcLayoutTest struct { - rcvr, t Type - size, argsize, retOffset uintptr - stack []byte // pointer bitmap: 1 is pointer, 0 is scalar - gc []byte - } - - var funcLayoutTests []funcLayoutTest - - func init() { - var argAlign uintptr = PtrSize - roundup := func(x uintptr, a uintptr) uintptr { - return (x + a - 1) / a * a + func TestFuncLayout(t *testing.T) { + align := func(x uintptr) uintptr { + return (x + PtrSize - 1) &^ (PtrSize - 1) @@ -6440:6452 @@ - - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func(a, b string) string { return "" }).Type(), - 6 * PtrSize, - 4 * PtrSize, - 4 * PtrSize, - []byte{1, 0, 1, 0, 1}, - []byte{1, 0, 1, 0, 1}, - }) - @@ -6458:6479 @@ - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(), - roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign), - roundup(3*4, PtrSize) + PtrSize + 2, - roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign), - r, - r, - }) - - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(), - 4 * PtrSize, - 4 * PtrSize, - 4 * PtrSize, - []byte{1, 0, 1, 1}, - []byte{1, 0, 1, 1}, - }) @@ -6484:6494 @@ - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func(a S) {}).Type(), - 4 * PtrSize, - 4 * PtrSize, - 4 * PtrSize, - []byte{0, 0, 1, 1}, - []byte{0, 0, 1, 1}, - }) @@ -6495:6537, +6473:6546 @@ - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - ValueOf((*byte)(nil)).Type(), - ValueOf(func(a uintptr, b *int) {}).Type(), - roundup(3*PtrSize, argAlign), - 3 * PtrSize, - roundup(3*PtrSize, argAlign), - []byte{1, 0, 1}, - []byte{1, 0, 1}, - }) - - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func(a uintptr) {}).Type(), - roundup(PtrSize, argAlign), - PtrSize, - roundup(PtrSize, argAlign), - []byte{}, - []byte{}, - }) - - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - nil, - ValueOf(func() uintptr { return 0 }).Type(), - PtrSize, - 0, - 0, - []byte{}, - []byte{}, - }) - - funcLayoutTests = append(funcLayoutTests, - funcLayoutTest{ - ValueOf(uintptr(0)).Type(), - ValueOf(func(a uintptr) {}).Type(), - 2 * PtrSize, - 2 * PtrSize, - 2 * PtrSize, - []byte{1}, - []byte{1}, + type test struct { + rcvr, typ Type + size, argsize, retOffset uintptr + stack, gc, inRegs, outRegs []byte // pointer bitmap: 1 is pointer, 0 is scalar + intRegs, floatRegs int + floatRegSize uintptr + } + tests := []test{ + { + typ: ValueOf(func(a, b string) string { return "" }).Type(), + size: 6 * PtrSize, + argsize: 4 * PtrSize, + retOffset: 4 * PtrSize, + stack: []byte{1, 0, 1, 0, 1}, + gc: []byte{1, 0, 1, 0, 1}, + }, + { + typ: ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(), + size: align(align(3*4) + PtrSize + 2), + argsize: align(3*4) + PtrSize + 2, + retOffset: align(align(3*4) + PtrSize + 2), + stack: r, + gc: r, + }, + { + typ: ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(), + size: 4 * PtrSize, + argsize: 4 * PtrSize, + retOffset: 4 * PtrSize, + stack: []byte{1, 0, 1, 1}, + gc: []byte{1, 0, 1, 1}, + }, + { + typ: ValueOf(func(a S) {}).Type(), + size: 4 * PtrSize, + argsize: 4 * PtrSize, + retOffset: 4 * PtrSize, + stack: []byte{0, 0, 1, 1}, + gc: []byte{0, 0, 1, 1}, + }, + { + rcvr: ValueOf((*byte)(nil)).Type(), + typ: ValueOf(func(a uintptr, b *int) {}).Type(), + size: 3 * PtrSize, + argsize: 3 * PtrSize, + retOffset: 3 * PtrSize, + stack: []byte{1, 0, 1}, + gc: []byte{1, 0, 1}, + }, + { + typ: ValueOf(func(a uintptr) {}).Type(), + size: PtrSize, + argsize: PtrSize, + retOffset: PtrSize, + stack: []byte{}, + gc: []byte{}, + }, + { + typ: ValueOf(func() uintptr { return 0 }).Type(), + size: PtrSize, + argsize: 0, + retOffset: 0, + stack: []byte{}, + gc: []byte{}, + }, + { + rcvr: ValueOf(uintptr(0)).Type(), + typ: ValueOf(func(a uintptr) {}).Type(), + size: 2 * PtrSize, + argsize: 2 * PtrSize, + retOffset: 2 * PtrSize, + stack: []byte{1}, + gc: []byte{1}, @@ -6540:6542, +6549:6559 @@ - }) - } + }, + // TODO(mknyszek): Add tests for non-zero register count. + } + for _, lt := range tests { + name := lt.typ.String() + if lt.rcvr != nil { + name = lt.rcvr.String() + "." + name + } + t.Run(name, func(t *testing.T) { + defer SetArgRegs(SetArgRegs(lt.intRegs, lt.floatRegs, lt.floatRegSize)) @@ -6543:6564, +6560:6586 @@ - func TestFuncLayout(t *testing.T) { - for _, lt := range funcLayoutTests { - typ, argsize, retOffset, stack, gc, ptrs := FuncLayout(lt.t, lt.rcvr) - if typ.Size() != lt.size { - t.Errorf("funcLayout(%v, %v).size=%d, want %d", lt.t, lt.rcvr, typ.Size(), lt.size) - } - if argsize != lt.argsize { - t.Errorf("funcLayout(%v, %v).argsize=%d, want %d", lt.t, lt.rcvr, argsize, lt.argsize) - } - if retOffset != lt.retOffset { - t.Errorf("funcLayout(%v, %v).retOffset=%d, want %d", lt.t, lt.rcvr, retOffset, lt.retOffset) - } - if !bytes.Equal(stack, lt.stack) { - t.Errorf("funcLayout(%v, %v).stack=%v, want %v", lt.t, lt.rcvr, stack, lt.stack) - } - if !bytes.Equal(gc, lt.gc) { - t.Errorf("funcLayout(%v, %v).gc=%v, want %v", lt.t, lt.rcvr, gc, lt.gc) - } - if ptrs && len(stack) == 0 || !ptrs && len(stack) > 0 { - t.Errorf("funcLayout(%v, %v) pointers flag=%v, want %v", lt.t, lt.rcvr, ptrs, !ptrs) - } + typ, argsize, retOffset, stack, gc, inRegs, outRegs, ptrs := FuncLayout(lt.typ, lt.rcvr) + if typ.Size() != lt.size { + t.Errorf("funcLayout(%v, %v).size=%d, want %d", lt.typ, lt.rcvr, typ.Size(), lt.size) + } + if argsize != lt.argsize { + t.Errorf("funcLayout(%v, %v).argsize=%d, want %d", lt.typ, lt.rcvr, argsize, lt.argsize) + } + if retOffset != lt.retOffset { + t.Errorf("funcLayout(%v, %v).retOffset=%d, want %d", lt.typ, lt.rcvr, retOffset, lt.retOffset) + } + if !bytes.Equal(stack, lt.stack) { + t.Errorf("funcLayout(%v, %v).stack=%v, want %v", lt.typ, lt.rcvr, stack, lt.stack) + } + if !bytes.Equal(gc, lt.gc) { + t.Errorf("funcLayout(%v, %v).gc=%v, want %v", lt.typ, lt.rcvr, gc, lt.gc) + } + if !bytes.Equal(inRegs, lt.inRegs) { + t.Errorf("funcLayout(%v, %v).inRegs=%v, want %v", lt.typ, lt.rcvr, inRegs, lt.inRegs) + } + if !bytes.Equal(outRegs, lt.outRegs) { + t.Errorf("funcLayout(%v, %v).outRegs=%v, want %v", lt.typ, lt.rcvr, outRegs, lt.outRegs) + } + if ptrs && len(stack) == 0 || !ptrs && len(stack) > 0 { + t.Errorf("funcLayout(%v, %v) pointers flag=%v, want %v", lt.typ, lt.rcvr, ptrs, !ptrs) + } + }) @@ +7292:7302 @@ + + func TestConvertibleTo(t *testing.T) { + t1 := ValueOf(example1.MyStruct{}).Type() + t2 := ValueOf(example2.MyStruct{}).Type() + + // Shouldn't raise stack overflow + if t1.ConvertibleTo(t2) { + t.Fatalf("(%s).ConvertibleTo(%s) = true, want false", t1, t2) + } + } ``` The name of the file: src/reflect/value.go Insertions: 2, Deletions: 2. ``` @@ +381:382 @@ + isVariadic := t.IsVariadic() @@ -382:383, +383:384 @@ - if !t.IsVariadic() { + if !isVariadic { @@ -392:393, +393:394 @@ - if t.IsVariadic() { + if isVariadic { @@ -398:399, +399:400 @@ - if !t.IsVariadic() && len(in) > n { + if !isVariadic && len(in) > n { @@ -412:413, +413:414 @@ - if !isSlice && t.IsVariadic() { + if !isSlice && isVariadic { @@ -594:595, +595:596 @@ - ret[i] = Value{tv.common(), regArgs.Ptrs[steps[0].ireg], flag(t.Kind())} + ret[i] = Value{tv.common(), regArgs.Ptrs[steps[0].ireg], flag(tv.Kind())} @@ -649:650, +650:662 @@ - func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool) { + // + // regs contains the argument values passed in registers and will contain + // the values returned from ctxt.fn in registers. + func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer, retValid *bool, regs *abi.RegArgs) { + if callGC { + // Call GC upon entry during testing. + // Getting our stack scanned here is the biggest hazard, because + // our caller (makeFuncStub) could have failed to place the last + // pointer to a value in regs' pointer space, in which case it + // won't be visible to the GC. + runtime.GC() + } @@ -653:654, +665:668 @@ - // Copy argument frame into Values. + _, _, abi := funcLayout(ftyp, nil) + + // Copy arguments into Values. @@ -655:656 @@ - off := uintptr(0) @@ -657:659, +670:675 @@ - for _, typ := range ftyp.in() { - off += -off & uintptr(typ.align-1) + for i, typ := range ftyp.in() { + if typ.Size() == 0 { + in = append(in, Zero(typ)) + continue + } @@ -660:668, +676:690 @@ - if ifaceIndir(typ) { - // value cannot be inlined in interface data. - // Must make a copy, because f might keep a reference to it, - // and we cannot let f keep a reference to the stack frame - // after this function returns, not even a read-only reference. - v.ptr = unsafe_New(typ) - if typ.size > 0 { - typedmemmove(typ, v.ptr, add(ptr, off, "typ.size > 0")) + steps := abi.call.stepsForValue(i) + if st := steps[0]; st.kind == abiStepStack { + if ifaceIndir(typ) { + // value cannot be inlined in interface data. + // Must make a copy, because f might keep a reference to it, + // and we cannot let f keep a reference to the stack frame + // after this function returns, not even a read-only reference. + v.ptr = unsafe_New(typ) + if typ.size > 0 { + typedmemmove(typ, v.ptr, add(ptr, st.stkOff, "typ.size > 0")) + } + v.flag |= flagIndir + } else { + v.ptr = *(*unsafe.Pointer)(add(ptr, st.stkOff, "1-ptr")) @@ -669:670 @@ - v.flag |= flagIndir @@ -671:672, +692:723 @@ - v.ptr = *(*unsafe.Pointer)(add(ptr, off, "1-ptr")) + if ifaceIndir(typ) { + // All that's left is values passed in registers that we need to + // create space for the values. + v.flag |= flagIndir + v.ptr = unsafe_New(typ) + for _, st := range steps { + switch st.kind { + case abiStepIntReg: + offset := add(v.ptr, st.offset, "precomputed value offset") + memmove(offset, unsafe.Pointer(&regs.Ints[st.ireg]), st.size) + case abiStepPointer: + s := add(v.ptr, st.offset, "precomputed value offset") + *((*unsafe.Pointer)(s)) = regs.Ptrs[st.ireg] + case abiStepFloatReg: + offset := add(v.ptr, st.offset, "precomputed value offset") + memmove(offset, unsafe.Pointer(&regs.Floats[st.freg]), st.size) + case abiStepStack: + panic("register-based return value has stack component") + default: + panic("unknown ABI part kind") + } + } + } else { + // Pointer-valued data gets put directly + // into v.ptr. + if steps[0].kind != abiStepPointer { + print("kind=", steps[0].kind, ", type=", typ.String(), "\n") + panic("mismatch between ABI description and types") + } + v.ptr = regs.Ptrs[steps[0].ireg] + } @@ -674:675 @@ - off += typ.size @@ -684:685, +734:735 @@ - // Copy results back into argument frame. + // Copy results back into argument frame and register space. @@ -686:687 @@ - off += -off & (ptrSize - 1) @@ -697:698 @@ - off += -off & uintptr(typ.align-1) @@ -701:702 @@ - addr := add(ptr, off, "typ.size > 0") @@ -706:712, +753:806 @@ - if typ.Kind() == Interface { - // We must clear the destination before calling assignTo, - // in case assignTo writes (with memory barriers) to the - // target location used as scratch space. See issue 39541. - *(*uintptr)(addr) = 0 - *(*uintptr)(add(addr, ptrSize, "typ.size == 2*ptrSize")) = 0 + // + // + // TODO(mknyszek): In the switch to the register ABI we lost + // the scratch space here for the register cases (and + // temporarily for all the cases). + // + // If/when this happens, take note of the following: + // + // We must clear the destination before calling assignTo, + // in case assignTo writes (with memory barriers) to the + // target location used as scratch space. See issue 39541. + v = v.assignTo("reflect.MakeFunc", typ, nil) + stepsLoop: + for _, st := range abi.ret.stepsForValue(i) { + switch st.kind { + case abiStepStack: + // Copy values to the "stack." + addr := add(ptr, st.stkOff, "precomputed stack arg offset") + // Do not use write barriers. The stack space used + // for this call is not adequately zeroed, and we + // are careful to keep the arguments alive until we + // return to makeFuncStub's caller. + if v.flag&flagIndir != 0 { + memmove(addr, v.ptr, st.size) + } else { + // This case must be a pointer type. + *(*uintptr)(addr) = uintptr(v.ptr) + } + // There's only one step for a stack-allocated value. + break stepsLoop + case abiStepIntReg, abiStepPointer: + // Copy values to "integer registers." + if v.flag&flagIndir != 0 { + offset := add(v.ptr, st.offset, "precomputed value offset") + memmove(unsafe.Pointer(&regs.Ints[st.ireg]), offset, st.size) + } else { + // Only populate the Ints space on the return path. + // This is safe because out is kept alive until the + // end of this function, and the return path through + // makeFuncStub has no preemption, so these pointers + // are always visible to the GC. + regs.Ints[st.ireg] = uintptr(v.ptr) + } + case abiStepFloatReg: + // Copy values to "float registers." + if v.flag&flagIndir == 0 { + panic("attempted to copy pointer to FP register") + } + offset := add(v.ptr, st.offset, "precomputed value offset") + memmove(unsafe.Pointer(&regs.Floats[st.freg]), offset, st.size) + default: + panic("unknown ABI part kind") + } @@ -713:722 @@ - v = v.assignTo("reflect.MakeFunc", typ, addr) - - // We are writing to stack. No write barrier. - if v.flag&flagIndir != 0 { - memmove(addr, v.ptr, typ.size) - } else { - *(*uintptr)(addr) = uintptr(v.ptr) - } - off += typ.size @@ -822:823, +907:911 @@ - func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool) { + // + // regs contains the argument values passed in registers and will contain + // the values returned from ctxt.fn in registers. + func callMethod(ctxt *methodValue, frame unsafe.Pointer, retValid *bool, regs *abi.RegArgs) { @@ -824:827, +912:925 @@ - rcvrtype, t, fn := methodReceiver("call", rcvr, ctxt.method) - frametype, framePool, abid := funcLayout(t, rcvrtype) - argSize, retOffset := abid.stackCallArgsSize, abid.retOffset + rcvrType, valueFuncType, methodFn := methodReceiver("call", rcvr, ctxt.method) + + // There are two ABIs at play here. + // + // methodValueCall was invoked with the ABI assuming there was no + // receiver ("value ABI") and that's what frame and regs are holding. + // + // Meanwhile, we need to actually call the method with a receiver, which + // has its own ABI ("method ABI"). Everything that follows is a translation + // between the two. + _, _, valueABI := funcLayout(valueFuncType, nil) + valueFrame, valueRegs := frame, regs + methodFrameType, methodFramePool, methodABI := funcLayout(valueFuncType, rcvrType) @@ -830:831, +928:930 @@ - scratch := framePool.Get().(unsafe.Pointer) + methodFrame := methodFramePool.Get().(unsafe.Pointer) + var methodRegs abi.RegArgs @@ -832:842, +931:939 @@ - // Copy in receiver and rest of args. - storeRcvr(rcvr, scratch) - // Align the first arg. The alignment can't be larger than ptrSize. - argOffset := uintptr(ptrSize) - if len(t.in()) > 0 { - argOffset = align(argOffset, uintptr(t.in()[0].align)) - } - // Avoid constructing out-of-bounds pointers if there are no args. - if argSize-argOffset > 0 { - typedmemmovepartial(frametype, add(scratch, argOffset, "argSize > argOffset"), frame, argOffset, argSize-argOffset) + // Deal with the receiver. It's guaranteed to only be one word in size. + if st := methodABI.call.steps[0]; st.kind == abiStepStack { + // Only copy the reciever to the stack if the ABI says so. + // Otherwise, it'll be in a register already. + storeRcvr(rcvr, methodFrame) + } else { + // Put the receiver in a register. + storeRcvr(rcvr, unsafe.Pointer(&methodRegs.Ints)) @@ -844:845, +941:1021 @@ - frameSize := frametype.size + // Translate the rest of the arguments. + for i, t := range valueFuncType.in() { + valueSteps := valueABI.call.stepsForValue(i) + methodSteps := methodABI.call.stepsForValue(i + 1) + + // Zero-sized types are trivial: nothing to do. + if len(valueSteps) == 0 { + if len(methodSteps) != 0 { + panic("method ABI and value ABI do not align") + } + continue + } + + // There are three cases to handle in translating each + // argument: + // 1. Stack -> stack translation. + // 2. Registers -> stack translation. + // 3. Registers -> registers translation. + // The fourth cases can't happen, because a method value + // call uses strictly fewer registers than a method call. + + // If the value ABI passes the value on the stack, + // then the method ABI does too, because it has strictly + // fewer arguments. Simply copy between the two. + if vStep := valueSteps[0]; vStep.kind == abiStepStack { + mStep := methodSteps[0] + if mStep.kind != abiStepStack || vStep.size != mStep.size { + panic("method ABI and value ABI do not align") + } + typedmemmove(t, + add(methodFrame, mStep.stkOff, "precomputed stack offset"), + add(valueFrame, vStep.stkOff, "precomputed stack offset")) + continue + } + // Handle register -> stack translation. + if mStep := methodSteps[0]; mStep.kind == abiStepStack { + for _, vStep := range valueSteps { + to := add(methodFrame, mStep.stkOff+vStep.offset, "precomputed stack offset") + switch vStep.kind { + case abiStepPointer: + // Do the pointer copy directly so we get a write barrier. + *(*unsafe.Pointer)(to) = valueRegs.Ptrs[vStep.ireg] + case abiStepIntReg: + memmove(to, unsafe.Pointer(&valueRegs.Ints[vStep.ireg]), vStep.size) + case abiStepFloatReg: + memmove(to, unsafe.Pointer(&valueRegs.Floats[vStep.freg]), vStep.size) + default: + panic("unexpected value step") + } + } + continue + } + // Handle register -> register translation. + if len(valueSteps) != len(methodSteps) { + // Because it's the same type for the value, and it's assigned + // to registers both times, it should always take up the same + // number of registers for each ABI. + panic("method ABI and value ABI don't align") + } + for i, vStep := range valueSteps { + mStep := methodSteps[i] + if mStep.kind != vStep.kind { + panic("method ABI and value ABI don't align") + } + switch vStep.kind { + case abiStepPointer: + // Copy this too, so we get a write barrier. + methodRegs.Ptrs[mStep.ireg] = valueRegs.Ptrs[vStep.ireg] + fallthrough + case abiStepIntReg: + methodRegs.Ints[mStep.ireg] = valueRegs.Ints[vStep.ireg] + case abiStepFloatReg: + methodRegs.Floats[mStep.freg] = valueRegs.Floats[vStep.freg] + default: + panic("unexpected value step") + } + } + } + + methodFrameSize := methodFrameType.size @@ -847:849, +1023:1028 @@ - frameSize = align(frameSize, ptrSize) - frameSize += abid.spill + methodFrameSize = align(methodFrameSize, ptrSize) + methodFrameSize += methodABI.spill + + // Mark pointers in registers for the return path. + methodRegs.ReturnIsPtr = methodABI.outRegPtrs @@ -853:857, +1032:1033 @@ - // - // TODO(mknyszek): Have this actually support the register-based ABI. - var regs abi.RegArgs - call(frametype, fn, scratch, uint32(frametype.size), uint32(retOffset), uint32(frameSize), &regs) + call(methodFrameType, methodFn, methodFrame, uint32(methodFrameType.size), uint32(methodABI.retOffset), uint32(methodFrameSize), &methodRegs) @@ -859:860, +1035:1041 @@ - // Ignore any changes to args and just copy return values. + // + // This is somewhat simpler because both ABIs have an identical + // return value ABI (the types are identical). As a result, register + // results can simply be copied over. Stack-allocated values are laid + // out the same, but are at different offsets from the start of the frame + // Ignore any changes to args. @@ -861:863, +1042:1049 @@ - if frametype.size-retOffset > 0 { - callerRetOffset := retOffset - argOffset + // because the arguments may be laid out differently. + if valueRegs != nil { + *valueRegs = methodRegs + } + if retSize := methodFrameType.size - methodABI.retOffset; retSize > 0 { + valueRet := add(valueFrame, valueABI.retOffset, "valueFrame's size > retOffset") + methodRet := add(methodFrame, methodABI.retOffset, "methodFrame's size > retOffset") @@ -864:867, +1050:1051 @@ - memmove(add(frame, callerRetOffset, "frametype.size > retOffset"), - add(scratch, retOffset, "frametype.size > retOffset"), - frametype.size-retOffset) + memmove(valueRet, methodRet, retSize) @@ -876:878, +1060:1062 @@ - typedmemclr(frametype, scratch) - framePool.Put(scratch) + typedmemclr(methodFrameType, methodFrame) + methodFramePool.Put(methodFrame) @@ +1065:1070 @@ + + // Keep valueRegs alive because it may hold live pointer results. + // The caller (methodValueCall) has it as a stack object, which is only + // scanned when there is a reference to it. + runtime.KeepAlive(valueRegs) @@ +2713:2718 @@ + pt := t.ptrTo() + if ifaceIndir(pt) { + // This is a pointer to a go:notinheap type. + panic("reflect: New of type that may not be allocated in heap (possibly undefined cgo C type)") + } @@ -2526:2527, +2720:2721 @@ - return Value{t.ptrTo(), ptr, fl} + return Value{pt, ptr, fl} @@ -2852:2854, +3046:3048 @@ - if n > h.Cap { - panic("reflect: cannot convert slice with capacity " + itoa.Itoa(h.Cap) + " to array pointer with length " + itoa.Itoa(n)) + if n > h.Len { + panic("reflect: cannot convert slice with length " + itoa.Itoa(h.Len) + " to array pointer with length " + itoa.Itoa(n)) ```

To view, visit change 301652. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I79b7e4dd87a67a47723e00a65d0b1ac6090371b7
Gerrit-Change-Number: 301652
Gerrit-PatchSet: 15
Gerrit-Owner: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Go Bot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Josh Bleecher Snyder <josh...@gmail.com>
Gerrit-Reviewer: Matthew Dempsky <mdem...@google.com>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-MessageType: merged
Reply all
Reply to author
Forward
0 new messages