mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
go/types: make typeset return an iterator
typeset(t) now returns a func equivalent to iter.Seq2[Type, Type] for the sequence over (type, underlying) pairs in the typeset of t. underIs was modified to take advantage of the underlying iteration primitive, all, which computes the desired boolean conjunction directly. Change-Id: I3e17d5970fd2908c5dca0754db3e251bf1200af2 Reviewed-on: https://go-review.googlesource.com/c/go/+/688876 Auto-Submit: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
fbba930271
commit
6b32c613ca
12 changed files with 129 additions and 160 deletions
|
|
@ -98,17 +98,17 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||||
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
|
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
|
||||||
y := args[1]
|
y := args[1]
|
||||||
hasString := false
|
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) {
|
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
|
||||||
return true
|
// typeset ⊇ {[]byte}
|
||||||
}
|
} else if isString(u) {
|
||||||
if isString(u) {
|
// typeset ⊇ {string}
|
||||||
hasString = true
|
hasString = true
|
||||||
return true
|
} else {
|
||||||
|
y = nil
|
||||||
|
break
|
||||||
}
|
}
|
||||||
y = nil
|
}
|
||||||
return false
|
|
||||||
})
|
|
||||||
if y != nil && hasString {
|
if y != nil && hasString {
|
||||||
// setting the signature also signals that we're done
|
// setting the signature also signals that we're done
|
||||||
sig = makeSig(x.typ, x.typ, y.typ)
|
sig = makeSig(x.typ, x.typ, y.typ)
|
||||||
|
|
@ -368,16 +368,16 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||||
var special bool
|
var special bool
|
||||||
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
|
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
|
||||||
special = true
|
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) {
|
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
|
||||||
return true
|
// typeset ⊇ {[]byte}
|
||||||
|
} else if isString(u) {
|
||||||
|
// typeset ⊇ {string}
|
||||||
|
} else {
|
||||||
|
special = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if isString(u) {
|
}
|
||||||
return true
|
|
||||||
}
|
|
||||||
special = false
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// general case
|
// general case
|
||||||
|
|
@ -980,29 +980,22 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
||||||
// or a type error if x is not a slice (or a type set of slices).
|
// or a type error if x is not a slice (or a type set of slices).
|
||||||
func sliceElem(x *operand) (Type, *typeError) {
|
func sliceElem(x *operand) (Type, *typeError) {
|
||||||
var E Type
|
var E Type
|
||||||
var err *typeError
|
for _, u := range typeset(x.typ) {
|
||||||
typeset(x.typ, func(_, u Type) bool {
|
|
||||||
s, _ := u.(*Slice)
|
s, _ := u.(*Slice)
|
||||||
if s == nil {
|
if s == nil {
|
||||||
if x.isNil() {
|
if x.isNil() {
|
||||||
// Printing x in this case would just print "nil".
|
// Printing x in this case would just print "nil".
|
||||||
// Special case this so we can emphasize "untyped".
|
// 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 {
|
} 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 {
|
if E == nil {
|
||||||
E = s.elem
|
E = s.elem
|
||||||
} else if !Identical(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 nil, typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
return E, nil
|
return E, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -216,11 +216,11 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
|
||||||
// determine common underlying type cu
|
// determine common underlying type cu
|
||||||
var ct, cu Type // type and respective common underlying type
|
var ct, cu Type // type and respective common underlying type
|
||||||
var hasString bool
|
var hasString bool
|
||||||
typeset(x.typ, func(t, u Type) bool {
|
for t, u := range typeset(x.typ) {
|
||||||
if u == nil {
|
if u == nil {
|
||||||
check.errorf(x, NonSliceableOperand, "cannot slice %s: no specific type in %s", x, x.typ)
|
check.errorf(x, NonSliceableOperand, "cannot slice %s: no specific type in %s", x, x.typ)
|
||||||
cu = nil
|
cu = nil
|
||||||
return false
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Treat strings like byte slices but remember that we saw a string.
|
// Treat strings like byte slices but remember that we saw a string.
|
||||||
|
|
@ -232,18 +232,16 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
|
||||||
// If this is the first type we're seeing, we're done.
|
// If this is the first type we're seeing, we're done.
|
||||||
if cu == nil {
|
if cu == nil {
|
||||||
ct, cu = t, u
|
ct, cu = t, u
|
||||||
return true
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, the current type must have the same underlying type as all previous types.
|
// Otherwise, the current type must have the same underlying type as all previous types.
|
||||||
if !Identical(cu, u) {
|
if !Identical(cu, u) {
|
||||||
check.errorf(x, NonSliceableOperand, "cannot slice %s: %s and %s have different underlying types", x, ct, t)
|
check.errorf(x, NonSliceableOperand, "cannot slice %s: %s and %s have different underlying types", x, ct, t)
|
||||||
cu = nil
|
cu = nil
|
||||||
return false
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true
|
|
||||||
})
|
|
||||||
if hasString {
|
if hasString {
|
||||||
// If we saw a string, proceed with string type,
|
// If we saw a string, proceed with string type,
|
||||||
// but don't go from untyped string to string.
|
// but don't go from untyped string to string.
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
|
||||||
}
|
}
|
||||||
last := params.At(n - 1).typ
|
last := params.At(n - 1).typ
|
||||||
var S *Slice
|
var S *Slice
|
||||||
typeset(last, func(t, _ Type) bool {
|
for t := range typeset(last) {
|
||||||
var s *Slice
|
var s *Slice
|
||||||
if isString(t) {
|
if isString(t) {
|
||||||
s = NewSlice(universeByte)
|
s = NewSlice(universeByte)
|
||||||
|
|
@ -60,10 +60,9 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
|
||||||
S = s
|
S = s
|
||||||
} else if !Identical(S, s) {
|
} else if !Identical(S, s) {
|
||||||
S = nil
|
S = nil
|
||||||
return false
|
break
|
||||||
}
|
}
|
||||||
return true
|
}
|
||||||
})
|
|
||||||
if S == nil {
|
if S == nil {
|
||||||
panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
|
panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -155,10 +155,10 @@ func (t *TypeParam) is(f func(*term) bool) bool {
|
||||||
return t.iface().typeSet().is(f)
|
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.
|
// specific type terms of t's constraint.
|
||||||
// If there are no specific terms, typeset calls yield with (nil, nil).
|
// If there are no specific terms, typeset returns f(nil, nil).
|
||||||
// In any case, typeset is guaranteed to call yield at least once.
|
// In any case, typeset is guaranteed to call f at least once.
|
||||||
func (t *TypeParam) typeset(yield func(t, u Type) bool) {
|
func (t *TypeParam) typeset(f func(t, u Type) bool) bool {
|
||||||
t.iface().typeSet().typeset(yield)
|
return t.iface().typeSet().all(f)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,13 +104,12 @@ func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll
|
||||||
// subsetOf reports whether s1 ⊆ s2.
|
// subsetOf reports whether s1 ⊆ s2.
|
||||||
func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
|
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.
|
// all reports whether f(t, u) is true for each (type/underlying type) pairs in s.
|
||||||
// If s has no specific terms, typeset calls yield with (nil, nil).
|
// If s has no specific terms, all calls f(nil, nil).
|
||||||
// In any case, typeset is guaranteed to call yield at least once.
|
// In any case, all is guaranteed to call f at least once.
|
||||||
func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
|
func (s *_TypeSet) all(f func(t, u Type) bool) bool {
|
||||||
if !s.hasTerms() {
|
if !s.hasTerms() {
|
||||||
yield(nil, nil)
|
return f(nil, nil)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range s.terms {
|
for _, t := range s.terms {
|
||||||
|
|
@ -123,10 +122,11 @@ func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
|
||||||
if debug {
|
if debug {
|
||||||
assert(Identical(u, under(u)))
|
assert(Identical(u, under(u)))
|
||||||
}
|
}
|
||||||
if !yield(t.typ, u) {
|
if !f(t.typ, u) {
|
||||||
break
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// is calls f with the specific type terms of s and reports whether
|
// is calls f with the specific type terms of s and reports whether
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
package types2
|
package types2
|
||||||
|
|
||||||
|
import "iter"
|
||||||
|
|
||||||
// under returns the true expanded underlying type.
|
// under returns the true expanded underlying type.
|
||||||
// If it doesn't exist, the result is Typ[Invalid].
|
// If it doesn't exist, the result is Typ[Invalid].
|
||||||
// under must only be called when a type is known
|
// under must only be called when a type is known
|
||||||
|
|
@ -18,12 +20,18 @@ func under(t Type) Type {
|
||||||
// If typ is a type parameter, underIs returns the result of typ.underIs(f).
|
// If typ is a type parameter, underIs returns the result of typ.underIs(f).
|
||||||
// Otherwise, underIs returns the result of f(under(typ)).
|
// Otherwise, underIs returns the result of f(under(typ)).
|
||||||
func underIs(typ Type, f func(Type) bool) bool {
|
func underIs(typ Type, f func(Type) bool) bool {
|
||||||
var ok bool
|
return all(typ, func(_, u Type) bool {
|
||||||
typeset(typ, func(_, u Type) bool {
|
return f(u)
|
||||||
ok = f(u)
|
|
||||||
return ok
|
|
||||||
})
|
})
|
||||||
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
|
// typeset is an iterator over the (type/underlying type) pairs of the
|
||||||
|
|
@ -32,12 +40,10 @@ func underIs(typ Type, f func(Type) bool) bool {
|
||||||
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
|
// 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.
|
// 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.
|
// In any case, typeset is guaranteed to call yield at least once.
|
||||||
func typeset(t Type, yield func(t, u Type) bool) {
|
func typeset(t Type) iter.Seq2[Type, Type] {
|
||||||
if p, _ := Unalias(t).(*TypeParam); p != nil {
|
return func(yield func(t, u Type) bool) {
|
||||||
p.typeset(yield)
|
_ = all(t, yield)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
yield(t, under(t))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A typeError describes a type error.
|
// A typeError describes a type error.
|
||||||
|
|
@ -80,35 +86,28 @@ func (err *typeError) format(check *Checker) string {
|
||||||
// with the single type t in its type set.
|
// with the single type t in its type set.
|
||||||
func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
|
func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
|
||||||
var ct, cu Type // type and respective common underlying type
|
var ct, cu Type // type and respective common underlying type
|
||||||
var err *typeError
|
for t, u := range typeset(t) {
|
||||||
|
|
||||||
bad := func(format string, args ...any) bool {
|
|
||||||
err = typeErrorf(format, args...)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
typeset(t, func(t, u Type) bool {
|
|
||||||
if cond != nil {
|
if cond != nil {
|
||||||
if err = cond(t, u); err != nil {
|
if err := cond(t, u); err != nil {
|
||||||
return false
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u == nil {
|
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 this is the first type we're seeing, we're done.
|
||||||
if cu == nil {
|
if cu == nil {
|
||||||
ct, cu = t, u
|
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 we've seen a channel before, and we have a channel now, they must be compatible.
|
||||||
if chu, _ := cu.(*Chan); chu != nil {
|
if chu, _ := cu.(*Chan); chu != nil {
|
||||||
if ch, _ := u.(*Chan); ch != nil {
|
if ch, _ := u.(*Chan); ch != nil {
|
||||||
if !Identical(chu.elem, ch.elem) {
|
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
|
// If we have different channel directions, keep the restricted one
|
||||||
// and complain if they conflict.
|
// and complain if they conflict.
|
||||||
|
|
@ -118,22 +117,16 @@ func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
|
||||||
case chu.dir == SendRecv:
|
case chu.dir == SendRecv:
|
||||||
ct, cu = t, u // switch to restricted channel
|
ct, cu = t, u // switch to restricted channel
|
||||||
case ch.dir != SendRecv:
|
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.
|
// Otherwise, the current type must have the same underlying type as all previous types.
|
||||||
if !Identical(cu, u) {
|
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
|
return cu, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,17 +101,17 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
|
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
|
||||||
y := args[1]
|
y := args[1]
|
||||||
hasString := false
|
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) {
|
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
|
||||||
return true
|
// typeset ⊇ {[]byte}
|
||||||
}
|
} else if isString(u) {
|
||||||
if isString(u) {
|
// typeset ⊇ {string}
|
||||||
hasString = true
|
hasString = true
|
||||||
return true
|
} else {
|
||||||
|
y = nil
|
||||||
|
break
|
||||||
}
|
}
|
||||||
y = nil
|
}
|
||||||
return false
|
|
||||||
})
|
|
||||||
if y != nil && hasString {
|
if y != nil && hasString {
|
||||||
// setting the signature also signals that we're done
|
// setting the signature also signals that we're done
|
||||||
sig = makeSig(x.typ, x.typ, y.typ)
|
sig = makeSig(x.typ, x.typ, y.typ)
|
||||||
|
|
@ -371,16 +371,16 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
var special bool
|
var special bool
|
||||||
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
|
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
|
||||||
special = true
|
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) {
|
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
|
||||||
return true
|
// typeset ⊇ {[]byte}
|
||||||
|
} else if isString(u) {
|
||||||
|
// typeset ⊇ {string}
|
||||||
|
} else {
|
||||||
|
special = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if isString(u) {
|
}
|
||||||
return true
|
|
||||||
}
|
|
||||||
special = false
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// general case
|
// general case
|
||||||
|
|
@ -983,29 +983,22 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
// or a type error if x is not a slice (or a type set of slices).
|
// or a type error if x is not a slice (or a type set of slices).
|
||||||
func sliceElem(x *operand) (Type, *typeError) {
|
func sliceElem(x *operand) (Type, *typeError) {
|
||||||
var E Type
|
var E Type
|
||||||
var err *typeError
|
for _, u := range typeset(x.typ) {
|
||||||
typeset(x.typ, func(_, u Type) bool {
|
|
||||||
s, _ := u.(*Slice)
|
s, _ := u.(*Slice)
|
||||||
if s == nil {
|
if s == nil {
|
||||||
if x.isNil() {
|
if x.isNil() {
|
||||||
// Printing x in this case would just print "nil".
|
// Printing x in this case would just print "nil".
|
||||||
// Special case this so we can emphasize "untyped".
|
// 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 {
|
} 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 {
|
if E == nil {
|
||||||
E = s.elem
|
E = s.elem
|
||||||
} else if !Identical(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 nil, typeErrorf("mismatched slice element types %s and %s in %s", E, s.elem, x)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
return E, nil
|
return E, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,8 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
|
||||||
// determine common underlying type cu
|
// determine common underlying type cu
|
||||||
var ct, cu Type // type and respective common underlying type
|
var ct, cu Type // type and respective common underlying type
|
||||||
var hasString bool
|
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 {
|
if u == nil {
|
||||||
check.errorf(x, NonSliceableOperand, "cannot slice %s: no specific type in %s", x, x.typ)
|
check.errorf(x, NonSliceableOperand, "cannot slice %s: no specific type in %s", x, x.typ)
|
||||||
cu = nil
|
cu = nil
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
|
||||||
}
|
}
|
||||||
last := params.At(n - 1).typ
|
last := params.At(n - 1).typ
|
||||||
var S *Slice
|
var S *Slice
|
||||||
typeset(last, func(t, _ Type) bool {
|
for t := range typeset(last) {
|
||||||
var s *Slice
|
var s *Slice
|
||||||
if isString(t) {
|
if isString(t) {
|
||||||
s = NewSlice(universeByte)
|
s = NewSlice(universeByte)
|
||||||
|
|
@ -73,10 +73,9 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
|
||||||
S = s
|
S = s
|
||||||
} else if !Identical(S, s) {
|
} else if !Identical(S, s) {
|
||||||
S = nil
|
S = nil
|
||||||
return false
|
break
|
||||||
}
|
}
|
||||||
return true
|
}
|
||||||
})
|
|
||||||
if S == nil {
|
if S == nil {
|
||||||
panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
|
panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,10 +158,10 @@ func (t *TypeParam) is(f func(*term) bool) bool {
|
||||||
return t.iface().typeSet().is(f)
|
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.
|
// specific type terms of t's constraint.
|
||||||
// If there are no specific terms, typeset calls yield with (nil, nil).
|
// If there are no specific terms, typeset returns f(nil, nil).
|
||||||
// In any case, typeset is guaranteed to call yield at least once.
|
// In any case, typeset is guaranteed to call f at least once.
|
||||||
func (t *TypeParam) typeset(yield func(t, u Type) bool) {
|
func (t *TypeParam) typeset(f func(t, u Type) bool) bool {
|
||||||
t.iface().typeSet().typeset(yield)
|
return t.iface().typeSet().all(f)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,13 +107,12 @@ func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll
|
||||||
// subsetOf reports whether s1 ⊆ s2.
|
// subsetOf reports whether s1 ⊆ s2.
|
||||||
func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
|
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.
|
// all reports whether f(t, u) is true for each (type/underlying type) pairs in s.
|
||||||
// If s has no specific terms, typeset calls yield with (nil, nil).
|
// If s has no specific terms, all calls f(nil, nil).
|
||||||
// In any case, typeset is guaranteed to call yield at least once.
|
// In any case, all is guaranteed to call f at least once.
|
||||||
func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
|
func (s *_TypeSet) all(f func(t, u Type) bool) bool {
|
||||||
if !s.hasTerms() {
|
if !s.hasTerms() {
|
||||||
yield(nil, nil)
|
return f(nil, nil)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range s.terms {
|
for _, t := range s.terms {
|
||||||
|
|
@ -126,10 +125,11 @@ func (s *_TypeSet) typeset(yield func(t, u Type) bool) {
|
||||||
if debug {
|
if debug {
|
||||||
assert(Identical(u, under(u)))
|
assert(Identical(u, under(u)))
|
||||||
}
|
}
|
||||||
if !yield(t.typ, u) {
|
if !f(t.typ, u) {
|
||||||
break
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// is calls f with the specific type terms of s and reports whether
|
// is calls f with the specific type terms of s and reports whether
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
|
import "iter"
|
||||||
|
|
||||||
// under returns the true expanded underlying type.
|
// under returns the true expanded underlying type.
|
||||||
// If it doesn't exist, the result is Typ[Invalid].
|
// If it doesn't exist, the result is Typ[Invalid].
|
||||||
// under must only be called when a type is known
|
// under must only be called when a type is known
|
||||||
|
|
@ -21,12 +23,18 @@ func under(t Type) Type {
|
||||||
// If typ is a type parameter, underIs returns the result of typ.underIs(f).
|
// If typ is a type parameter, underIs returns the result of typ.underIs(f).
|
||||||
// Otherwise, underIs returns the result of f(under(typ)).
|
// Otherwise, underIs returns the result of f(under(typ)).
|
||||||
func underIs(typ Type, f func(Type) bool) bool {
|
func underIs(typ Type, f func(Type) bool) bool {
|
||||||
var ok bool
|
return all(typ, func(_, u Type) bool {
|
||||||
typeset(typ, func(_, u Type) bool {
|
return f(u)
|
||||||
ok = f(u)
|
|
||||||
return ok
|
|
||||||
})
|
})
|
||||||
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
|
// typeset is an iterator over the (type/underlying type) pairs of the
|
||||||
|
|
@ -35,12 +43,10 @@ func underIs(typ Type, f func(Type) bool) bool {
|
||||||
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
|
// 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.
|
// 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.
|
// In any case, typeset is guaranteed to call yield at least once.
|
||||||
func typeset(t Type, yield func(t, u Type) bool) {
|
func typeset(t Type) iter.Seq2[Type, Type] {
|
||||||
if p, _ := Unalias(t).(*TypeParam); p != nil {
|
return func(yield func(t, u Type) bool) {
|
||||||
p.typeset(yield)
|
_ = all(t, yield)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
yield(t, under(t))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A typeError describes a type error.
|
// A typeError describes a type error.
|
||||||
|
|
@ -83,35 +89,28 @@ func (err *typeError) format(check *Checker) string {
|
||||||
// with the single type t in its type set.
|
// with the single type t in its type set.
|
||||||
func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
|
func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
|
||||||
var ct, cu Type // type and respective common underlying type
|
var ct, cu Type // type and respective common underlying type
|
||||||
var err *typeError
|
for t, u := range typeset(t) {
|
||||||
|
|
||||||
bad := func(format string, args ...any) bool {
|
|
||||||
err = typeErrorf(format, args...)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
typeset(t, func(t, u Type) bool {
|
|
||||||
if cond != nil {
|
if cond != nil {
|
||||||
if err = cond(t, u); err != nil {
|
if err := cond(t, u); err != nil {
|
||||||
return false
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u == nil {
|
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 this is the first type we're seeing, we're done.
|
||||||
if cu == nil {
|
if cu == nil {
|
||||||
ct, cu = t, u
|
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 we've seen a channel before, and we have a channel now, they must be compatible.
|
||||||
if chu, _ := cu.(*Chan); chu != nil {
|
if chu, _ := cu.(*Chan); chu != nil {
|
||||||
if ch, _ := u.(*Chan); ch != nil {
|
if ch, _ := u.(*Chan); ch != nil {
|
||||||
if !Identical(chu.elem, ch.elem) {
|
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
|
// If we have different channel directions, keep the restricted one
|
||||||
// and complain if they conflict.
|
// and complain if they conflict.
|
||||||
|
|
@ -121,22 +120,16 @@ func commonUnder(t Type, cond func(t, u Type) *typeError) (Type, *typeError) {
|
||||||
case chu.dir == SendRecv:
|
case chu.dir == SendRecv:
|
||||||
ct, cu = t, u // switch to restricted channel
|
ct, cu = t, u // switch to restricted channel
|
||||||
case ch.dir != SendRecv:
|
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.
|
// Otherwise, the current type must have the same underlying type as all previous types.
|
||||||
if !Identical(cu, u) {
|
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
|
return cu, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue