Skip to content

Commit

Permalink
reflect: add iterative related methods
Browse files Browse the repository at this point in the history
WIP

DO NOT SUBMIT

FIxes golang#66056

Change-Id: I23f1ae11fea7b412a294b47775b8756fd961a53b
  • Loading branch information
qiulaidongfeng committed May 7, 2024
1 parent ad10fbd commit 00f28a2
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 0 deletions.
4 changes: 4 additions & 0 deletions api/next/66056.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pkg reflect, method (Value) Seq() iter.Seq[Value] #66056
pkg reflect, method (Value) Seq2() iter.Seq2[Value, Value] #66056
pkg reflect, type Type interface, CanSeq() bool #66056
pkg reflect, type Type interface, CanSeq2() bool #66056
46 changes: 46 additions & 0 deletions src/reflect/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ type Type interface {
// It panics if t's Kind is not Uint, Uintptr, Uint8, Uint16, Uint32, or Uint64.
OverflowUint(x uint64) bool

// CanSeq report whether the type is convertible to iter.Seq
CanSeq() bool

// CanSeq2 report whether the type is convertible to iter.Seq2
CanSeq2() bool

common() *abi.Type
uncommon() *uncommonType
}
Expand Down Expand Up @@ -866,6 +872,46 @@ func (t *rtype) OverflowUint(x uint64) bool {
panic("reflect: OverflowUint of non-uint type " + t.String())
}

func (t *rtype) CanSeq() bool {
return canSeq(&t.t)
}

func canSeq(t *abi.Type) bool {
if t.Kind() != abi.Func {
return false
}
f := t.FuncType()
if f.InCount != 1 || f.OutCount != 0 {
return false
}
y := f.In(0)
if y.Kind() != abi.Func {
return false
}
yield := y.FuncType()
return yield.InCount == 1 && yield.OutCount == 1 && yield.Out(0).Kind() == abi.Bool
}

func (t *rtype) CanSeq2() bool {
return canSeq2(&t.t)
}

func canSeq2(t *abi.Type) bool {
if t.Kind() != abi.Func {
return false
}
f := t.FuncType()
if f.InCount != 1 || f.OutCount != 0 {
return false
}
y := f.In(0)
if y.Kind() != abi.Func {
return false
}
yield := y.FuncType()
return yield.InCount == 2 && yield.OutCount == 1 && yield.Out(0).Kind() == abi.Bool
}

// add returns p+x.
//
// The whySafe string is ignored, so that the function still inlines
Expand Down
36 changes: 36 additions & 0 deletions src/reflect/type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,39 @@ func BenchmarkTypeForError(b *testing.B) {
sinkType = reflect.TypeFor[error]()
}
}

func Test_Type_CanSeq(t *testing.T) {
tests := []struct {
name string
tr reflect.Type
want bool
}{
{"func(func(int) bool)", reflect.TypeOf(func(func(int) bool) {}), true},
{"func(func(int))", reflect.TypeOf(func(func(int)) {}), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.tr.CanSeq(); got != tt.want {
t.Errorf("rtype.CanSeq() = %v, want %v", got, tt.want)
}
})
}
}

func Test_Type_CanSeq2(t *testing.T) {
tests := []struct {
name string
tr reflect.Type
want bool
}{
{"func(func(int, int) bool)", reflect.TypeOf(func(func(int, int) bool) {}), true},
{"func(func(int, int))", reflect.TypeOf(func(func(int, int)) {}), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.tr.CanSeq2(); got != tt.want {
t.Errorf("rtype.CanSeq() = %v, want %v", got, tt.want)
}
})
}
}
130 changes: 130 additions & 0 deletions src/reflect/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"internal/goarch"
"internal/itoa"
"internal/unsafeheader"
"iter"
"math"
"runtime"
"unsafe"
Expand Down Expand Up @@ -3504,6 +3505,135 @@ func (v Value) Equal(u Value) bool {
panic("reflect.Value.Equal: values of type " + v.Type().String() + " are not comparable")
}

// Seq returns an iter.Seq[reflect.Value] that loops over the elements of v.
// If v's kind is Func, it must be a function that has no results and
// that takes a single argument of type func(T) bool for some type T.
// If v's kind is Pointer, the pointer element type must have kind Array.
// Otherwise v's kind must be Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr,
// Array, Chan, Map, Slice, or String.
func (v Value) Seq() iter.Seq[Value] {
if canSeq(v.typ()) {
return func(yield func(Value) bool) {
rf := MakeFunc(v.Type().In(0), func(in []Value) []Value {
return []Value{ValueOf(yield(in[0]))}
})
v.Call([]Value{rf})
}
}
switch v.Kind() {
case Int, Int8, Int16, Int32, Int64:
return func(yield func(Value) bool) {
for i := range v.Int() {
if !yield(ValueOf(i)) {
return
}
}
}
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
return func(yield func(Value) bool) {
for i := range v.Uint() {
if !yield(ValueOf(i)) {
return
}
}
}
case Pointer:
if !(v.Elem().kind() == Array) {
return nil
}
return func(yield func(Value) bool) {
len := v.Len()
v = v.Elem()
for i := range len {
if !yield(ValueOf(v.Index(i))) {
return
}
}
}
case Slice, String:
return func(yield func(Value) bool) {
for i := range v.Len() {
if !yield(v.Index(i)) {
return
}
}
}
case Map:
return func(yield func(Value) bool) {
i := v.MapRange()
for i.Next() {
if !yield(i.Value()) {
return
}
}
}
case Chan:
return func(yield func(Value) bool) {
for v, ok := v.Recv(); ok; {
if !yield(v) {
return
}
}
}
}
return nil
}

// Seq2 is like Seq but for two values.
func (v Value) Seq2() iter.Seq2[Value, Value] {
if canSeq2(v.typ()) {
return func(yield func(Value, Value) bool) {
rf := MakeFunc(v.Type().In(0), func(in []Value) []Value {
return []Value{ValueOf(yield(in[0], in[1]))}
})
v.Call([]Value{rf})
}
}
switch v.Kind() {
case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
panic("reflect: " + v.Type().String() + " not make iter.Seq2[Value, Value] ")
case Pointer:
if !(v.Elem().kind() == Array) {
return nil
}
return func(yield func(Value, Value) bool) {
len := v.Len()
v = v.Elem()
for i := range len {
if !yield(ValueOf(i), v.Index(i)) {
return
}
}
}
case Slice, String:
return func(yield func(Value, Value) bool) {
for i := range v.Len() {
if !yield(ValueOf(i), v.Index(i)) {
return
}
}
}
case Map:
return func(yield func(Value, Value) bool) {
i := v.MapRange()
for i.Next() {
if !yield(i.Key(), i.Value()) {
return
}
}
}
case Chan:
return func(yield func(Value, Value) bool) {
for v, ok := v.Recv(); ; {
if !yield(v, ValueOf(ok)) {
return
}
}
}
}
return nil
}

// convertOp returns the function to convert a value of type src
// to a value of type dst. If the conversion is illegal, convertOp returns nil.
func convertOp(dst, src *abi.Type) func(Value, Type) Value {
Expand Down

0 comments on commit 00f28a2

Please sign in to comment.