aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2/typeset.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/types2/typeset.go')
-rw-r--r--src/cmd/compile/internal/types2/typeset.go169
1 files changed, 121 insertions, 48 deletions
diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go
index 5a334b2f53..6e19115ff5 100644
--- a/src/cmd/compile/internal/types2/typeset.go
+++ b/src/cmd/compile/internal/types2/typeset.go
@@ -18,31 +18,32 @@ import (
type TypeSet struct {
comparable bool // if set, the interface is or embeds comparable
// TODO(gri) consider using a set for the methods for faster lookup
- methods []*Func // all methods of the interface; sorted by unique ID
- types Type // typically a *Union; nil means no type restrictions
+ methods []*Func // all methods of the interface; sorted by unique ID
+ terms termlist // type terms of the type set
}
-// IsTop reports whether type set s is the top type set (corresponding to the empty interface).
-func (s *TypeSet) IsTop() bool { return !s.comparable && len(s.methods) == 0 && s.types == nil }
+// IsEmpty reports whether type set s is the empty set.
+func (s *TypeSet) IsEmpty() bool { return s.terms.isEmpty() }
+
+// IsTop reports whether type set s is the set of all types (corresponding to the empty interface).
+func (s *TypeSet) IsTop() bool { return !s.comparable && len(s.methods) == 0 && s.terms.isTop() }
+
+// TODO(gri) IsMethodSet is not a great name for this predicate. Find a better one.
// IsMethodSet reports whether the type set s is described by a single set of methods.
-func (s *TypeSet) IsMethodSet() bool { return !s.comparable && s.types == nil }
+func (s *TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isTop() }
// IsComparable reports whether each type in the set is comparable.
func (s *TypeSet) IsComparable() bool {
- if s.types == nil {
+ if s.terms.isTop() {
return s.comparable
}
- tcomparable := s.underIs(func(u Type) bool {
- return Comparable(u)
+ return s.is(func(t *term) bool {
+ return Comparable(t.typ)
})
- if !s.comparable {
- return tcomparable
- }
- return s.comparable && tcomparable
}
-// TODO(gri) IsTypeSet is not a great name. Find a better one.
+// TODO(gri) IsTypeSet is not a great name for this predicate. Find a better one.
// IsTypeSet reports whether the type set s is represented by a finite set of underlying types.
func (s *TypeSet) IsTypeSet() bool {
@@ -63,15 +64,21 @@ func (s *TypeSet) LookupMethod(pkg *Package, name string) (int, *Func) {
}
func (s *TypeSet) String() string {
- if s.IsTop() {
+ switch {
+ case s.IsEmpty():
+ return "∅"
+ case s.IsTop():
return "⊤"
}
+ hasMethods := len(s.methods) > 0
+ hasTerms := s.hasTerms()
+
var buf bytes.Buffer
buf.WriteByte('{')
if s.comparable {
buf.WriteString(" comparable")
- if len(s.methods) > 0 || s.types != nil {
+ if hasMethods || hasTerms {
buf.WriteByte(';')
}
}
@@ -82,41 +89,77 @@ func (s *TypeSet) String() string {
buf.WriteByte(' ')
buf.WriteString(m.String())
}
- if len(s.methods) > 0 && s.types != nil {
+ if hasMethods && hasTerms {
buf.WriteByte(';')
}
- if s.types != nil {
- buf.WriteByte(' ')
- writeType(&buf, s.types, nil, nil)
+ if hasTerms {
+ buf.WriteString(s.terms.String())
}
+ buf.WriteString(" }") // there was at least one method or term
- buf.WriteString(" }") // there was a least one method or type
return buf.String()
}
// ----------------------------------------------------------------------------
// Implementation
-// underIs reports whether f returned true for the underlying types of the
-// enumerable types in the type set s. If the type set comprises all types
-// f is called once with the top type; if the type set is empty, the result
-// is false.
+func (s *TypeSet) hasTerms() bool { return !s.terms.isTop() }
+func (s *TypeSet) structuralType() Type { return s.terms.structuralType() }
+func (s *TypeSet) includes(t Type) bool { return s.terms.includes(t) }
+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
+
+var topTerm = term{false, theTop}
+
+func (s *TypeSet) is(f func(*term) bool) bool {
+ if len(s.terms) == 0 {
+ return false
+ }
+ for _, t := range s.terms {
+ // Terms represent the top term with a nil type.
+ // The rest of the type checker uses the top type
+ // instead. Convert.
+ // TODO(gri) investigate if we can do without this
+ if t.typ == nil {
+ t = &topTerm
+ }
+ if !f(t) {
+ return false
+ }
+ }
+ return true
+}
+
func (s *TypeSet) underIs(f func(Type) bool) bool {
- switch t := s.types.(type) {
- case nil:
- return f(theTop)
- default:
- return f(t)
- case *Union:
- return t.underIs(f)
+ if len(s.terms) == 0 {
+ return false
+ }
+ for _, t := range s.terms {
+ // see corresponding comment in TypeSet.is
+ u := t.typ
+ if u == nil {
+ u = theTop
+ }
+ // t == under(t) for ~t terms
+ if !t.tilde {
+ u = under(u)
+ }
+ if debug {
+ assert(Identical(u, under(u)))
+ }
+ if !f(u) {
+ return false
+ }
}
+ return true
}
// topTypeSet may be used as type set for the empty interface.
-var topTypeSet TypeSet
+var topTypeSet = TypeSet{terms: topTermlist}
-// computeTypeSet may be called with check == nil.
-func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
+// computeInterfaceTypeSet may be called with check == nil.
+func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
if ityp.tset != nil {
return ityp.tset
}
@@ -152,7 +195,7 @@ func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
// have valid interfaces. Mark the interface as complete to avoid
// infinite recursion if the validType check occurs later for some
// reason.
- ityp.tset = new(TypeSet) // TODO(gri) is this sufficient?
+ ityp.tset = &TypeSet{terms: topTermlist} // TODO(gri) is this sufficient?
// Methods of embedded interfaces are collected unchanged; i.e., the identity
// of a method I.m's Func Object of an interface I is the same as that of
@@ -213,7 +256,7 @@ func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
}
// collect embedded elements
- var allTypes Type
+ var allTerms = topTermlist
for i, typ := range ityp.embeddeds {
// The embedding position is nil for imported interfaces
// and also for interface copies after substitution (but
@@ -222,25 +265,22 @@ func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
if ityp.embedPos != nil {
pos = (*ityp.embedPos)[i]
}
- var types Type
+ var terms termlist
switch t := under(typ).(type) {
case *Interface:
- tset := computeTypeSet(check, pos, t)
+ tset := computeInterfaceTypeSet(check, pos, t)
if tset.comparable {
ityp.tset.comparable = true
}
for _, m := range tset.methods {
addMethod(pos, m, false) // use embedding position pos rather than m.pos
}
- types = tset.types
+ terms = tset.terms
case *Union:
- // TODO(gri) combine with default case once we have
- // converted all tests to new notation and we
- // can report an error when we don't have an
- // interface before go1.18.
- types = typ
+ tset := computeUnionTypeSet(check, pos, t)
+ terms = tset.terms
case *TypeParam:
- // Embedding stand-alone type parameters is not permitted for now.
+ // Embedding stand-alone type parameters is not permitted.
// This case is handled during union parsing.
unreachable()
default:
@@ -251,9 +291,11 @@ func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
check.errorf(pos, "%s is not an interface", typ)
continue
}
- types = typ
+ terms = termlist{{false, typ}}
}
- allTypes = intersect(allTypes, types)
+ // The type set of an interface is the intersection
+ // of the type sets of all its elements.
+ allTerms = allTerms.intersect(terms)
}
ityp.embedPos = nil // not needed anymore (errors have been reported)
@@ -270,7 +312,7 @@ func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
sortMethods(methods)
ityp.tset.methods = methods
}
- ityp.tset.types = allTypes
+ ityp.tset.terms = allTerms
return ityp.tset
}
@@ -294,3 +336,34 @@ type byUniqueMethodName []*Func
func (a byUniqueMethodName) Len() int { return len(a) }
func (a byUniqueMethodName) Less(i, j int) bool { return a[i].less(&a[j].object) }
func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// computeUnionTypeSet may be called with check == nil.
+func computeUnionTypeSet(check *Checker, pos syntax.Pos, utyp *Union) *TypeSet {
+ if utyp.tset != nil {
+ return utyp.tset
+ }
+
+ // avoid infinite recursion (see also computeInterfaceTypeSet)
+ utyp.tset = new(TypeSet)
+
+ var allTerms termlist
+ for _, t := range utyp.terms {
+ var terms termlist
+ switch u := under(t.typ).(type) {
+ case *Interface:
+ terms = computeInterfaceTypeSet(check, pos, u).terms
+ case *TypeParam:
+ // A stand-alone type parameters is not permitted as union term.
+ // This case is handled during union parsing.
+ unreachable()
+ default:
+ terms = termlist{t}
+ }
+ // The type set of a union expression is the union
+ // of the type sets of each term.
+ allTerms = allTerms.union(terms)
+ }
+ utyp.tset.terms = allTerms
+
+ return utyp.tset
+}