From: Robert Griesemer Date: Thu, 19 Sep 2024 21:55:36 +0000 (-0700) Subject: go/types, types2: introduce typeset iterators X-Git-Tag: go1.24rc1~848 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=1e5a72e980d481dbe1c605aab870275d6ab4e81d;p=gostls13.git go/types, types2: introduce typeset iterators Preparation for removing the existing non-standard iterators (is, underIs). Note that we cannot use typeset iterators in range-over-func because the bootstrap compiler doesn't have access to it yet. While at it, move underIs from expr.go to under.go and adjust some doc strings in typset.go to match prevailing style in that file. Change-Id: Iecd014eeb5b3fca56a807381c148c5f7a29bfb78 Reviewed-on: https://go-review.googlesource.com/c/go/+/614239 Reviewed-by: Tim King Auto-Submit: Robert Griesemer LUCI-TryBot-Result: Go LUCI Reviewed-by: Robert Griesemer Commit-Queue: Robert Griesemer --- diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index 96f05ddb11..df2f2e4608 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -127,16 +127,6 @@ var op2str2 = [...]string{ syntax.Shl: "shift", } -// 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 { - typ = Unalias(typ) - if tpar, _ := typ.(*TypeParam); tpar != nil { - return tpar.underIs(f) - } - return f(under(typ)) -} - func (check *Checker) unary(x *operand, e *syntax.Operation) { check.expr(nil, x, e.X) if x.mode == invalid { diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go index c812df16ea..cedae76c2a 100644 --- a/src/cmd/compile/internal/types2/typeparam.go +++ b/src/cmd/compile/internal/types2/typeparam.go @@ -161,3 +161,11 @@ func (t *TypeParam) is(f func(*term) bool) bool { func (t *TypeParam) underIs(f func(Type) bool) bool { return t.iface().typeSet().underIs(f) } + +// typeset is an iterator over the (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) +} diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go index 83498ad21d..4f53d0d31c 100644 --- a/src/cmd/compile/internal/types2/typeset.go +++ b/src/cmd/compile/internal/types2/typeset.go @@ -29,10 +29,10 @@ type _TypeSet struct { comparable bool // invariant: !comparable || terms.isAll() } -// IsEmpty reports whether type set s is the empty set. +// IsEmpty reports whether s is the empty set. func (s *_TypeSet) IsEmpty() bool { return s.terms.isEmpty() } -// IsAll reports whether type set s is the set of all types (corresponding to the empty interface). +// IsAll reports whether s is the set of all types (corresponding to the empty interface). func (s *_TypeSet) IsAll() bool { return s.IsMethodSet() && len(s.methods) == 0 } // IsMethodSet reports whether the interface t is fully described by its method set. @@ -51,7 +51,7 @@ func (s *_TypeSet) IsComparable(seen map[Type]bool) bool { // NumMethods returns the number of methods available. func (s *_TypeSet) NumMethods() int { return len(s.methods) } -// Method returns the i'th method of type set s for 0 <= i < s.NumMethods(). +// Method returns the i'th method of s for 0 <= i < s.NumMethods(). // The methods are ordered by their unique ID. func (s *_TypeSet) Method(i int) *Func { return s.methods[i] } @@ -98,13 +98,36 @@ func (s *_TypeSet) String() string { // ---------------------------------------------------------------------------- // Implementation -// hasTerms reports whether the type set has specific type terms. +// hasTerms reports whether s has specific type terms. func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll() } // subsetOf reports whether s1 ⊆ s2. func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) } -// TODO(gri) TypeSet.is and TypeSet.underIs should probably also go into termlist.go +// 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) { + if !s.hasTerms() { + yield(nil, nil) + return + } + + for _, t := range s.terms { + assert(t.typ != nil) + // Unalias(x) == under(x) for ~x terms + u := Unalias(t.typ) + if !t.tilde { + u = under(u) + } + if debug { + assert(Identical(u, under(u))) + } + if !yield(t.typ, u) { + break + } + } +} // is calls f with the specific type terms of s and reports whether // all calls to f returned true. If there are no specific terms, is diff --git a/src/cmd/compile/internal/types2/under.go b/src/cmd/compile/internal/types2/under.go index 2d90c35d3b..b1b6f89b48 100644 --- a/src/cmd/compile/internal/types2/under.go +++ b/src/cmd/compile/internal/types2/under.go @@ -15,6 +15,30 @@ func under(t Type) Type { return t.Underlying() } +// 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 { + typ = Unalias(typ) + if tpar, _ := typ.(*TypeParam); tpar != nil { + return tpar.underIs(f) + } + return f(under(typ)) +} + +// typeset is an iterator over the (type/underlying type) pairs of the +// specific type terms of the type set implied by t. +// If t is a type parameter, the implied type set is the type set of t's constraint. +// 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 + } + yield(t, under(t)) +} + // If t is not a type parameter, coreType returns the underlying type. // If t is a type parameter, coreType returns the single underlying // type of all types in its type set if it exists, or nil otherwise. If the diff --git a/src/go/types/expr.go b/src/go/types/expr.go index d918059f77..5995e9d87e 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -126,16 +126,6 @@ var op2str2 = [...]string{ token.SHL: "shift", } -// 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 { - typ = Unalias(typ) - if tpar, _ := typ.(*TypeParam); tpar != nil { - return tpar.underIs(f) - } - return f(under(typ)) -} - // The unary expression e may be nil. It's passed in for better error messages only. func (check *Checker) unary(x *operand, e *ast.UnaryExpr) { check.expr(nil, x, e.X) diff --git a/src/go/types/typeparam.go b/src/go/types/typeparam.go index 42284307e2..4cff6b7b31 100644 --- a/src/go/types/typeparam.go +++ b/src/go/types/typeparam.go @@ -164,3 +164,11 @@ func (t *TypeParam) is(f func(*term) bool) bool { func (t *TypeParam) underIs(f func(Type) bool) bool { return t.iface().typeSet().underIs(f) } + +// typeset is an iterator over the (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) +} diff --git a/src/go/types/typeset.go b/src/go/types/typeset.go index 1ffe6ee14c..5d28226596 100644 --- a/src/go/types/typeset.go +++ b/src/go/types/typeset.go @@ -32,10 +32,10 @@ type _TypeSet struct { comparable bool // invariant: !comparable || terms.isAll() } -// IsEmpty reports whether type set s is the empty set. +// IsEmpty reports whether s is the empty set. func (s *_TypeSet) IsEmpty() bool { return s.terms.isEmpty() } -// IsAll reports whether type set s is the set of all types (corresponding to the empty interface). +// IsAll reports whether s is the set of all types (corresponding to the empty interface). func (s *_TypeSet) IsAll() bool { return s.IsMethodSet() && len(s.methods) == 0 } // IsMethodSet reports whether the interface t is fully described by its method set. @@ -54,7 +54,7 @@ func (s *_TypeSet) IsComparable(seen map[Type]bool) bool { // NumMethods returns the number of methods available. func (s *_TypeSet) NumMethods() int { return len(s.methods) } -// Method returns the i'th method of type set s for 0 <= i < s.NumMethods(). +// Method returns the i'th method of s for 0 <= i < s.NumMethods(). // The methods are ordered by their unique ID. func (s *_TypeSet) Method(i int) *Func { return s.methods[i] } @@ -101,13 +101,36 @@ func (s *_TypeSet) String() string { // ---------------------------------------------------------------------------- // Implementation -// hasTerms reports whether the type set has specific type terms. +// hasTerms reports whether s has specific type terms. func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll() } // subsetOf reports whether s1 ⊆ s2. func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) } -// TODO(gri) TypeSet.is and TypeSet.underIs should probably also go into termlist.go +// 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) { + if !s.hasTerms() { + yield(nil, nil) + return + } + + for _, t := range s.terms { + assert(t.typ != nil) + // Unalias(x) == under(x) for ~x terms + u := Unalias(t.typ) + if !t.tilde { + u = under(u) + } + if debug { + assert(Identical(u, under(u))) + } + if !yield(t.typ, u) { + break + } + } +} // is calls f with the specific type terms of s and reports whether // all calls to f returned true. If there are no specific terms, is diff --git a/src/go/types/under.go b/src/go/types/under.go index ed5aab238e..16afcb28a9 100644 --- a/src/go/types/under.go +++ b/src/go/types/under.go @@ -18,6 +18,30 @@ func under(t Type) Type { return t.Underlying() } +// 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 { + typ = Unalias(typ) + if tpar, _ := typ.(*TypeParam); tpar != nil { + return tpar.underIs(f) + } + return f(under(typ)) +} + +// typeset is an iterator over the (type/underlying type) pairs of the +// specific type terms of the type set implied by t. +// If t is a type parameter, the implied type set is the type set of t's constraint. +// 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 + } + yield(t, under(t)) +} + // If t is not a type parameter, coreType returns the underlying type. // If t is a type parameter, coreType returns the single underlying // type of all types in its type set if it exists, or nil otherwise. If the