]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: introduce typeset iterators
authorRobert Griesemer <gri@golang.org>
Thu, 19 Sep 2024 21:55:36 +0000 (14:55 -0700)
committerGopher Robot <gobot@golang.org>
Tue, 24 Sep 2024 20:35:14 +0000 (20:35 +0000)
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 <taking@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Commit-Queue: Robert Griesemer <gri@google.com>

src/cmd/compile/internal/types2/expr.go
src/cmd/compile/internal/types2/typeparam.go
src/cmd/compile/internal/types2/typeset.go
src/cmd/compile/internal/types2/under.go
src/go/types/expr.go
src/go/types/typeparam.go
src/go/types/typeset.go
src/go/types/under.go

index 96f05ddb114a6f3db988eebd7cf1e5724dd918f1..df2f2e4608b751053996ab7ae7b3d6da8e90e8a6 100644 (file)
@@ -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 {
index c812df16ea003f54ea03d064fb96e5a1b1050e81..cedae76c2a1ff4c57aba97f6f48203d6f93b614b 100644 (file)
@@ -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)
+}
index 83498ad21d08c2f272a9a46d85a467ba7937e5dc..4f53d0d31cf1541321ed83d1730bc621deeb1c76 100644 (file)
@@ -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
index 2d90c35d3bacb4a73dd0ead692c147a9c311f532..b1b6f89b484aed40d42d63761d92551a4b4c66f3 100644 (file)
@@ -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
index d918059f77d3a9ded99820bbf53ef2a8772e0896..5995e9d87ece329541b9c6883d1e3f4a24ce3451 100644 (file)
@@ -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)
index 42284307e2debc23b61f245e06f236e378c088f8..4cff6b7b31c7725616a81e5bf2ff88aa3ecada16 100644 (file)
@@ -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)
+}
index 1ffe6ee14c2db471898f1f58294c978c98a779e4..5d28226596c1542a14373c9005d6a5e1c515adf5 100644 (file)
@@ -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
index ed5aab238e58573a38fdda480908a41c9d02ad2d..16afcb28a9fd4b87eb584164c9eae7e9102dc8d8 100644 (file)
@@ -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