if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
y := args[1]
hasString := false
- typeset(y.typ, func(_, u Type) bool {
+ for _, u := range typeset(y.typ) {
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
- return true
- }
- if isString(u) {
+ // typeset ⊇ {[]byte}
+ } else if isString(u) {
+ // typeset ⊇ {string}
hasString = true
- return true
+ } else {
+ y = nil
+ break
}
- y = nil
- return false
- })
+ }
if y != nil && hasString {
// setting the signature also signals that we're done
sig = makeSig(x.typ, x.typ, y.typ)
var special bool
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
special = true
- typeset(y.typ, func(_, u Type) bool {
+ for _, u := range typeset(y.typ) {
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
- return true
- }
- if isString(u) {
- return true
+ // typeset ⊇ {[]byte}
+ } else if isString(u) {
+ // typeset ⊇ {string}
+ } else {
+ special = false
+ break
}
- special = false
- return false
- })
+ }
}
// general case
// or a type error if x is not a slice (or a type set of slices).
func sliceElem(x *operand) (Type, *typeError) {
var E Type
- var err *typeError
- typeset(x.typ, func(_, u Type) bool {
+ for _, u := range typeset(x.typ) {
s, _ := u.(*Slice)
if s == nil {
if x.isNil() {
// Printing x in this case would just print "nil".
// Special case this so we can emphasize "untyped".
- err = typeErrorf("argument must be a slice; have untyped nil")
+ return nil, typeErrorf("argument must be a slice; have untyped nil")
} else {
- err = typeErrorf("argument must be a slice; have %s", x)
+ return nil, typeErrorf("argument must be a slice; have %s", x)
}
- return false
}
if E == nil {
E = s.elem
} else if !Identical(E, s.elem) {
- err = typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
- return false
+ return nil, typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
}
- return true
- })
- if err != nil {
- return nil, err
}
return E, nil
}
// determine common underlying type cu
var ct, cu Type // type and respective common underlying type
var hasString bool
- typeset(x.typ, func(t, u Type) bool {
+ for t, u := range typeset(x.typ) {
if u == nil {
check.errorf(x, NonSliceableOperand, "cannot slice %s: no specific type in %s", x, x.typ)
cu = nil
- return false
+ break
}
// Treat strings like byte slices but remember that we saw a string.
// If this is the first type we're seeing, we're done.
if cu == nil {
ct, cu = t, u
- return true
+ continue
}
// Otherwise, the current type must have the same underlying type as all previous types.
if !Identical(cu, u) {
check.errorf(x, NonSliceableOperand, "cannot slice %s: %s and %s have different underlying types", x, ct, t)
cu = nil
- return false
+ break
}
-
- return true
- })
+ }
if hasString {
// If we saw a string, proceed with string type,
// but don't go from untyped string to string.
}
last := params.At(n - 1).typ
var S *Slice
- typeset(last, func(t, _ Type) bool {
+ for t := range typeset(last) {
var s *Slice
if isString(t) {
s = NewSlice(universeByte)
S = s
} else if !Identical(S, s) {
S = nil
- return false
+ break
}
- return true
- })
+ }
if S == nil {
panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
}
return t.iface().typeSet().is(f)
}
-// typeset is an iterator over the (type/underlying type) pairs of the
+// typeset reports whether f(t, y) is true for all (type/underlying type) pairs of the
// specific type terms of t's constraint.
-// If there are no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (t *TypeParam) typeset(yield func(t, u Type) bool) {
- t.iface().typeSet().typeset(yield)
+// If there are no specific terms, typeset returns f(nil, nil).
+// In any case, typeset is guaranteed to call f at least once.
+func (t *TypeParam) typeset(f func(t, u Type) bool) bool {
+ return t.iface().typeSet().all(f)
}
// subsetOf reports whether s1 ⊆ s2.
func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
-// typeset is an iterator over the (type/underlying type) pairs in s.
-// If s has no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
+// all reports whether f(t, u) is true for each (type/underlying type) pairs in s.
+// If s has no specific terms, all calls f(nil, nil).
+// In any case, all is guaranteed to call f at least once.
+func (s *_TypeSet) all(f func(t, u Type) bool) bool {
if !s.hasTerms() {
- yield(nil, nil)
- return
+ return f(nil, nil)
}
for _, t := range s.terms {
if debug {
assert(Identical(u, under(u)))
}
- if !yield(t.typ, u) {
- break
+ if !f(t.typ, u) {
+ return false
}
}
+ return true
}
// is calls f with the specific type terms of s and reports whether
package types2
+import "iter"
+
// under returns the true expanded underlying type.
// If it doesn't exist, the result is Typ[Invalid].
// under must only be called when a type is known
// If typ is a type parameter, underIs returns the result of typ.underIs(f).
// Otherwise, underIs returns the result of f(under(typ)).
func underIs(typ Type, f func(Type) bool) bool {
- var ok bool
- typeset(typ, func(_, u Type) bool {
- ok = f(u)
- return ok
+ return all(typ, func(_, u Type) bool {
+ return f(u)
})
- return ok
+}
+
+// all reports whether f(t, u) is true for all (type/underlying type)
+// pairs in the typeset of t. See [typeset] for details of sequence.
+func all(t Type, f func(t, u Type) bool) bool {
+ if p, _ := Unalias(t).(*TypeParam); p != nil {
+ return p.typeset(f)
+ }
+ return f(t, under(t))
}
// typeset is an iterator over the (type/underlying type) pairs of the
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
// If t is not a type parameter, the implied type set consists of just t.
// In any case, typeset is guaranteed to call yield at least once.
-func typeset(t Type, yield func(t, u Type) bool) {
- if p, _ := Unalias(t).(*TypeParam); p != nil {
- p.typeset(yield)
- return
+func typeset(t Type) iter.Seq2[Type, Type] {
+ return func(yield func(t, u Type) bool) {
+ _ = all(t, yield)
}
- yield(t, under(t))
}
// A typeError describes a type error.
// with the single type t in its type set.
func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
var ct, cu Type // type and respective common underlying type
- var err *typeError
-
- bad := func(format string, args ...any) bool {
- err = typeErrorf(format, args...)
- return false
- }
-
- typeset(t, func(t, u Type) bool {
+ for t, u := range typeset(t) {
if cond != nil {
- if err = cond(t, u); err != nil {
- return false
+ if err := cond(t, u); err != nil {
+ return nil, err
}
}
if u == nil {
- return bad("no specific type")
+ return nil, typeErrorf("no specific type")
}
// If this is the first type we're seeing, we're done.
if cu == nil {
ct, cu = t, u
- return true
+ continue
}
// If we've seen a channel before, and we have a channel now, they must be compatible.
if chu, _ := cu.(*Chan); chu != nil {
if ch, _ := u.(*Chan); ch != nil {
if !Identical(chu.elem, ch.elem) {
- return bad("channels %s and %s have different element types", ct, t)
+ return nil, typeErrorf("channels %s and %s have different element types", ct, t)
}
// If we have different channel directions, keep the restricted one
// and complain if they conflict.
case chu.dir == SendRecv:
ct, cu = t, u // switch to restricted channel
case ch.dir != SendRecv:
- return bad("channels %s and %s have conflicting directions", ct, t)
+ return nil, typeErrorf("channels %s and %s have conflicting directions", ct, t)
}
- return true
+ continue
}
}
// Otherwise, the current type must have the same underlying type as all previous types.
if !Identical(cu, u) {
- return bad("%s and %s have different underlying types", ct, t)
+ return nil, typeErrorf("%s and %s have different underlying types", ct, t)
}
-
- return true
- })
-
- if err != nil {
- return nil, err
}
return cu, nil
}
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
y := args[1]
hasString := false
- typeset(y.typ, func(_, u Type) bool {
+ for _, u := range typeset(y.typ) {
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
- return true
- }
- if isString(u) {
+ // typeset ⊇ {[]byte}
+ } else if isString(u) {
+ // typeset ⊇ {string}
hasString = true
- return true
+ } else {
+ y = nil
+ break
}
- y = nil
- return false
- })
+ }
if y != nil && hasString {
// setting the signature also signals that we're done
sig = makeSig(x.typ, x.typ, y.typ)
var special bool
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
special = true
- typeset(y.typ, func(_, u Type) bool {
+ for _, u := range typeset(y.typ) {
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
- return true
- }
- if isString(u) {
- return true
+ // typeset ⊇ {[]byte}
+ } else if isString(u) {
+ // typeset ⊇ {string}
+ } else {
+ special = false
+ break
}
- special = false
- return false
- })
+ }
}
// general case
// or a type error if x is not a slice (or a type set of slices).
func sliceElem(x *operand) (Type, *typeError) {
var E Type
- var err *typeError
- typeset(x.typ, func(_, u Type) bool {
+ for _, u := range typeset(x.typ) {
s, _ := u.(*Slice)
if s == nil {
if x.isNil() {
// Printing x in this case would just print "nil".
// Special case this so we can emphasize "untyped".
- err = typeErrorf("argument must be a slice; have untyped nil")
+ return nil, typeErrorf("argument must be a slice; have untyped nil")
} else {
- err = typeErrorf("argument must be a slice; have %s", x)
+ return nil, typeErrorf("argument must be a slice; have %s", x)
}
- return false
}
if E == nil {
E = s.elem
} else if !Identical(E, s.elem) {
- err = typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
- return false
+ return nil, typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
}
- return true
- })
- if err != nil {
- return nil, err
}
return E, nil
}
// determine common underlying type cu
var ct, cu Type // type and respective common underlying type
var hasString bool
- typeset(x.typ, func(t, u Type) bool {
+ // TODO(adonovan): use go1.23 "range typeset()".
+ typeset(x.typ)(func(t, u Type) bool {
if u == nil {
check.errorf(x, NonSliceableOperand, "cannot slice %s: no specific type in %s", x, x.typ)
cu = nil
}
last := params.At(n - 1).typ
var S *Slice
- typeset(last, func(t, _ Type) bool {
+ for t := range typeset(last) {
var s *Slice
if isString(t) {
s = NewSlice(universeByte)
S = s
} else if !Identical(S, s) {
S = nil
- return false
+ break
}
- return true
- })
+ }
if S == nil {
panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
}
return t.iface().typeSet().is(f)
}
-// typeset is an iterator over the (type/underlying type) pairs of the
+// typeset reports whether f(t, y) is true for all (type/underlying type) pairs of the
// specific type terms of t's constraint.
-// If there are no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (t *TypeParam) typeset(yield func(t, u Type) bool) {
- t.iface().typeSet().typeset(yield)
+// If there are no specific terms, typeset returns f(nil, nil).
+// In any case, typeset is guaranteed to call f at least once.
+func (t *TypeParam) typeset(f func(t, u Type) bool) bool {
+ return t.iface().typeSet().all(f)
}
// subsetOf reports whether s1 ⊆ s2.
func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
-// typeset is an iterator over the (type/underlying type) pairs in s.
-// If s has no specific terms, typeset calls yield with (nil, nil).
-// In any case, typeset is guaranteed to call yield at least once.
-func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
+// all reports whether f(t, u) is true for each (type/underlying type) pairs in s.
+// If s has no specific terms, all calls f(nil, nil).
+// In any case, all is guaranteed to call f at least once.
+func (s *_TypeSet) all(f func(t, u Type) bool) bool {
if !s.hasTerms() {
- yield(nil, nil)
- return
+ return f(nil, nil)
}
for _, t := range s.terms {
if debug {
assert(Identical(u, under(u)))
}
- if !yield(t.typ, u) {
- break
+ if !f(t.typ, u) {
+ return false
}
}
+ return true
}
// is calls f with the specific type terms of s and reports whether
package types
+import "iter"
+
// under returns the true expanded underlying type.
// If it doesn't exist, the result is Typ[Invalid].
// under must only be called when a type is known
// If typ is a type parameter, underIs returns the result of typ.underIs(f).
// Otherwise, underIs returns the result of f(under(typ)).
func underIs(typ Type, f func(Type) bool) bool {
- var ok bool
- typeset(typ, func(_, u Type) bool {
- ok = f(u)
- return ok
+ return all(typ, func(_, u Type) bool {
+ return f(u)
})
- return ok
+}
+
+// all reports whether f(t, u) is true for all (type/underlying type)
+// pairs in the typeset of t. See [typeset] for details of sequence.
+func all(t Type, f func(t, u Type) bool) bool {
+ if p, _ := Unalias(t).(*TypeParam); p != nil {
+ return p.typeset(f)
+ }
+ return f(t, under(t))
}
// typeset is an iterator over the (type/underlying type) pairs of the
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
// If t is not a type parameter, the implied type set consists of just t.
// In any case, typeset is guaranteed to call yield at least once.
-func typeset(t Type, yield func(t, u Type) bool) {
- if p, _ := Unalias(t).(*TypeParam); p != nil {
- p.typeset(yield)
- return
+func typeset(t Type) iter.Seq2[Type, Type] {
+ return func(yield func(t, u Type) bool) {
+ _ = all(t, yield)
}
- yield(t, under(t))
}
// A typeError describes a type error.
// with the single type t in its type set.
func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
var ct, cu Type // type and respective common underlying type
- var err *typeError
-
- bad := func(format string, args ...any) bool {
- err = typeErrorf(format, args...)
- return false
- }
-
- typeset(t, func(t, u Type) bool {
+ for t, u := range typeset(t) {
if cond != nil {
- if err = cond(t, u); err != nil {
- return false
+ if err := cond(t, u); err != nil {
+ return nil, err
}
}
if u == nil {
- return bad("no specific type")
+ return nil, typeErrorf("no specific type")
}
// If this is the first type we're seeing, we're done.
if cu == nil {
ct, cu = t, u
- return true
+ continue
}
// If we've seen a channel before, and we have a channel now, they must be compatible.
if chu, _ := cu.(*Chan); chu != nil {
if ch, _ := u.(*Chan); ch != nil {
if !Identical(chu.elem, ch.elem) {
- return bad("channels %s and %s have different element types", ct, t)
+ return nil, typeErrorf("channels %s and %s have different element types", ct, t)
}
// If we have different channel directions, keep the restricted one
// and complain if they conflict.
case chu.dir == SendRecv:
ct, cu = t, u // switch to restricted channel
case ch.dir != SendRecv:
- return bad("channels %s and %s have conflicting directions", ct, t)
+ return nil, typeErrorf("channels %s and %s have conflicting directions", ct, t)
}
- return true
+ continue
}
}
// Otherwise, the current type must have the same underlying type as all previous types.
if !Identical(cu, u) {
- return bad("%s and %s have different underlying types", ct, t)
+ return nil, typeErrorf("%s and %s have different underlying types", ct, t)
}
-
- return true
- })
-
- if err != nil {
- return nil, err
}
return cu, nil
}