]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile/internal/types2: replace Sum type with Union type
authorRobert Griesemer <gri@golang.org>
Wed, 26 May 2021 00:49:32 +0000 (17:49 -0700)
committerRobert Griesemer <gri@golang.org>
Wed, 2 Jun 2021 20:31:01 +0000 (20:31 +0000)
- We still mostly ignore the tilde information.

- More consistent naming: A Union term is the pair (type, tilde).
  Rename Union.terms to Union.types; the Union.types and Union.tilde
  slices make up the Union terms.

- Replace Sum.is with Union.underIs: underIs iterates through all
  union terms and calls its argument function with the underlying
  type of the term (and thus can ignore the tilde information).
  This also eliminates the need to call under in the argument
  function.

- Added Union.is for situations where we need to consider the tilde
  information for each Union term.

Change-Id: I70fcf1813e072651dc0f61d52d5555642ee762fd
Reviewed-on: https://go-review.googlesource.com/c/go/+/323274
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
16 files changed:
src/cmd/compile/internal/types2/builtins.go
src/cmd/compile/internal/types2/expr.go
src/cmd/compile/internal/types2/index.go
src/cmd/compile/internal/types2/infer.go
src/cmd/compile/internal/types2/interface.go
src/cmd/compile/internal/types2/operand.go
src/cmd/compile/internal/types2/predicates.go
src/cmd/compile/internal/types2/sanitize.go
src/cmd/compile/internal/types2/sizeof_test.go
src/cmd/compile/internal/types2/sizes.go
src/cmd/compile/internal/types2/stmt.go
src/cmd/compile/internal/types2/subst.go
src/cmd/compile/internal/types2/type.go
src/cmd/compile/internal/types2/typestring.go
src/cmd/compile/internal/types2/unify.go
src/cmd/compile/internal/types2/union.go

index 94fb506d8012100ba94adc437701928e4fe0d8f3..1779e32c5c8934931dfefcc6561aed8d50f0b0a2 100644 (file)
@@ -178,9 +178,9 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                                mode = value
                        }
 
-               case *Sum:
-                       if t.is(func(t Type) bool {
-                               switch t := under(t).(type) {
+               case *Union:
+                       if t.underIs(func(t Type) bool {
+                               switch t := t.(type) {
                                case *Basic:
                                        if isString(t) && id == _Len {
                                                return true
@@ -460,8 +460,8 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                                m = 2
                        case *Map, *Chan:
                                m = 1
-                       case *Sum:
-                               return t.is(valid)
+                       case *Union:
+                               return t.underIs(valid)
                        default:
                                return false
                        }
@@ -749,10 +749,14 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
        if tp := asTypeParam(x); tp != nil {
                // Test if t satisfies the requirements for the argument
                // type and collect possible result types at the same time.
+               // TODO(gri) This needs to consider the ~ information if we
+               //           have a union type.
                var rtypes []Type
+               var tilde []bool
                if !tp.Bound().is(func(x Type) bool {
                        if r := f(x); r != nil {
                                rtypes = append(rtypes, r)
+                               tilde = append(tilde, true) // for now - see TODO above
                                return true
                        }
                        return false
@@ -768,7 +772,7 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
                // construct a suitable new type parameter
                tpar := NewTypeName(nopos, nil /* = Universe pkg */, "<type parameter>", nil)
                ptyp := check.NewTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
-               tsum := NewSum(rtypes)
+               tsum := newUnion(rtypes, tilde)
                ptyp.bound = &Interface{allMethods: markComplete, allTypes: tsum}
 
                return ptyp
index 23b79656bb5fb0dabb7c769a27bcb86e10e68b7a..b223387f18e2c97ed3ee6a7bbb109dee74383537 100644 (file)
@@ -723,8 +723,8 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
                default:
                        return nil, nil, _InvalidUntypedConversion
                }
-       case *Sum:
-               ok := t.is(func(t Type) bool {
+       case *Union:
+               ok := t.underIs(func(t Type) bool {
                        target, _, _ := check.implicitTypeAndValue(x, t)
                        return target != nil
                })
index 33e79aac3eb5b6d2da878cc94755cd5bb5bbc175..47e0853a3b0ee77bd8ea4fddbab7876e3172a24b 100644 (file)
@@ -91,15 +91,15 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
                x.expr = e
                return
 
-       case *Sum:
-               // A sum type can be indexed if all of the sum's types
+       case *Union:
+               // A union type can be indexed if all of the union's terms
                // support indexing and have the same index and element
-               // type. Special rules apply for maps in the sum type.
+               // type. Special rules apply for maps in the union type.
                var tkey, telem Type // key is for map types only
-               nmaps := 0           // number of map types in sum type
-               if typ.is(func(t Type) bool {
+               nmaps := 0           // number of map types in union type
+               if typ.underIs(func(t Type) bool {
                        var e Type
-                       switch t := under(t).(type) {
+                       switch t := t.(type) {
                        case *Basic:
                                if isString(t) {
                                        e = universeByte
@@ -113,7 +113,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
                        case *Slice:
                                e = t.elem
                        case *Map:
-                               // If there are multiple maps in the sum type,
+                               // If there are multiple maps in the union type,
                                // they must have identical key types.
                                // TODO(gri) We may be able to relax this rule
                                // but it becomes complicated very quickly.
@@ -148,7 +148,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
                                // ok to continue even if indexing failed - map element type is known
 
                                // If there are only maps, we are done.
-                               if nmaps == len(typ.types) {
+                               if nmaps == typ.NumTerms() {
                                        x.mode = mapindex
                                        x.typ = telem
                                        x.expr = e
@@ -246,7 +246,7 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
                valid = true
                // x.typ doesn't change
 
-       case *Sum, *TypeParam:
+       case *Union, *TypeParam:
                check.error(x, "generic slice expressions not yet implemented")
                x.mode = invalid
                return
index d8865784a5c3fab757580f92c1cf672ad59558bb..73ea8330d4ad8bdfe383a950d5d5455a61a11aef 100644 (file)
@@ -307,7 +307,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
                        }
                }
 
-       case *Sum:
+       case *Union:
                return w.isParameterizedList(t.types)
 
        case *Signature:
@@ -320,9 +320,6 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
                // Thus, we only need to look at the input and result parameters.
                return w.isParameterized(t.params) || w.isParameterized(t.results)
 
-       case *Union:
-               unimplemented()
-
        case *Interface:
                if t.allMethods != nil {
                        // interface is complete - quick test
index d590066ad606692a53f93386bbecd773d44494cd..db34d0705fd18306766af9d98f0b0762278f078b 100644 (file)
@@ -242,23 +242,26 @@ func completeInterface(check *Checker, pos syntax.Pos, ityp *Interface) {
                        }
                        types = t.allTypes
                case *Union:
-                       types = NewSum(t.terms)
-                       // TODO(gri) don't ignore tilde information
+                       // 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
                case *TypeParam:
                        if check != nil && !check.allowVersion(check.pkg, 1, 18) {
                                check.errorf(pos, "%s is a type parameter, not an interface", typ)
                                continue
                        }
-                       types = t
+                       types = typ
                default:
-                       if t == Typ[Invalid] {
+                       if typ == Typ[Invalid] {
                                continue
                        }
                        if check != nil && !check.allowVersion(check.pkg, 1, 18) {
                                check.errorf(pos, "%s is not an interface", typ)
                                continue
                        }
-                       types = t
+                       types = typ
                }
                allTypes = intersect(allTypes, types)
        }
@@ -279,44 +282,6 @@ func completeInterface(check *Checker, pos syntax.Pos, ityp *Interface) {
        ityp.allTypes = allTypes
 }
 
-// intersect computes the intersection of the types x and y.
-// Note: An incomming nil type stands for the top type. A top
-// type result is returned as nil.
-func intersect(x, y Type) (r Type) {
-       defer func() {
-               if r == theTop {
-                       r = nil
-               }
-       }()
-
-       switch {
-       case x == theBottom || y == theBottom:
-               return theBottom
-       case x == nil || x == theTop:
-               return y
-       case y == nil || x == theTop:
-               return x
-       }
-
-       xtypes := unpack(x)
-       ytypes := unpack(y)
-       // Compute the list rtypes which includes only
-       // types that are in both xtypes and ytypes.
-       // Quadratic algorithm, but good enough for now.
-       // TODO(gri) fix this
-       var rtypes []Type
-       for _, x := range xtypes {
-               if includes(ytypes, x) {
-                       rtypes = append(rtypes, x)
-               }
-       }
-
-       if rtypes == nil {
-               return theBottom
-       }
-       return NewSum(rtypes)
-}
-
 func sortTypes(list []Type) {
        sort.Stable(byUniqueTypeName(list))
 }
index 455d8b5dd1df22c40fb3b0bf12d8f8b914ed1ea9..fdc6ec52aaec1e52cf4558ed21c5e0ad1673f074 100644 (file)
@@ -248,6 +248,12 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
 
        V := x.typ
 
+       const debugAssignableTo = false
+       if debugAssignableTo && check != nil {
+               check.dump("V = %s", V)
+               check.dump("T = %s", T)
+       }
+
        // x's type is identical to T
        if check.identical(V, T) {
                return true, 0
@@ -256,11 +262,20 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
        Vu := optype(V)
        Tu := optype(T)
 
+       if debugAssignableTo && check != nil {
+               check.dump("Vu = %s", Vu)
+               check.dump("Tu = %s", Tu)
+       }
+
        // x is an untyped value representable by a value of type T.
        if isUntyped(Vu) {
-               if t, ok := Tu.(*Sum); ok {
-                       return t.is(func(t Type) bool {
+               if t, ok := Tu.(*Union); ok {
+                       return t.is(func(t Type, tilde bool) bool {
                                // TODO(gri) this could probably be more efficient
+                               if tilde {
+                                       // TODO(gri) We need to check assignability
+                                       //           for the underlying type of x.
+                               }
                                ok, _ := x.assignableTo(check, t, reason)
                                return ok
                        }), _IncompatibleAssign
index ab0a4572767a6485ad65a9d32beb192272fac3d2..bcb3e221d00b2741aa2a4695f3e1a9dc2ff7da2c 100644 (file)
@@ -28,8 +28,8 @@ func is(typ Type, what BasicInfo) bool {
        switch t := optype(typ).(type) {
        case *Basic:
                return t.info&what != 0
-       case *Sum:
-               return t.is(func(typ Type) bool { return is(typ, what) })
+       case *Union:
+               return t.underIs(func(t Type) bool { return is(t, what) })
        }
        return false
 }
@@ -124,11 +124,10 @@ func comparable(T Type, seen map[Type]bool) bool {
                return true
        case *Array:
                return comparable(t.elem, seen)
-       case *Sum:
-               pred := func(t Type) bool {
+       case *Union:
+               return t.underIs(func(t Type) bool {
                        return comparable(t, seen)
-               }
-               return t.is(pred)
+               })
        case *TypeParam:
                return t.Bound().IsComparable()
        }
@@ -142,8 +141,8 @@ func hasNil(typ Type) bool {
                return t.kind == UnsafePointer
        case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
                return true
-       case *Sum:
-               return t.is(hasNil)
+       case *Union:
+               return t.underIs(hasNil)
        }
        return false
 }
@@ -261,21 +260,20 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
                                check.identical0(x.results, y.results, cmpTags, p)
                }
 
-       case *Sum:
-               // Two sum types are identical if they contain the same types.
-               // (Sum types always consist of at least two types. Also, the
-               // the set (list) of types in a sum type consists of unique
-               // types - each type appears exactly once. Thus, two sum types
+       case *Union:
+               // Two union types are identical if they contain the same terms.
+               // The set (list) of types in a union type consists of unique
+               // types - each type appears exactly once. Thus, two union types
                // must contain the same number of types to have chance of
                // being equal.
-               if y, ok := y.(*Sum); ok && len(x.types) == len(y.types) {
+               if y, ok := y.(*Union); ok && x.NumTerms() == y.NumTerms() {
                        // Every type in x.types must be in y.types.
                        // Quadratic algorithm, but probably good enough for now.
                        // TODO(gri) we need a fast quick type ID/hash for all types.
                L:
-                       for _, x := range x.types {
-                               for _, y := range y.types {
-                                       if Identical(x, y) {
+                       for i, xt := range x.types {
+                               for j, yt := range y.types {
+                                       if Identical(xt, yt) && x.tilde[i] == y.tilde[j] {
                                                continue L // x is in y.types
                                        }
                                }
@@ -284,9 +282,6 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
                        return true
                }
 
-       case *Union:
-               unimplemented()
-
        case *Interface:
                // Two interface types are identical if they have the same set of methods with
                // the same names and identical function types. Lower-case method names from
index c30febfda8958c705554b548683dd22850325bd9..ce26bab186ee148ec462fed5f604b4dbfbc3f9dd 100644 (file)
@@ -106,11 +106,8 @@ func (s sanitizer) typ(typ Type) Type {
                s.tuple(t.params)
                s.tuple(t.results)
 
-       case *Sum:
-               s.typeList(t.types)
-
        case *Union:
-               s.typeList(t.terms)
+               s.typeList(t.types)
 
        case *Interface:
                s.funcList(t.methods)
index 552f3488cd86ef4b03a719e4341f4f4f488d6235..d3c391161ed7e080625cc541c48a95a3a796a696 100644 (file)
@@ -27,7 +27,6 @@ func TestSizeof(t *testing.T) {
                {Pointer{}, 8, 16},
                {Tuple{}, 12, 24},
                {Signature{}, 44, 88},
-               {Sum{}, 12, 24},
                {Union{}, 24, 48},
                {Interface{}, 52, 104},
                {Map{}, 16, 32},
index c6b807cd06599d959af1f27fb58eda02f0c0017a..cb789598e5785d27a4bb2380f4f8bbb07811b735 100644 (file)
@@ -148,10 +148,8 @@ func (s *StdSizes) Sizeof(T Type) int64 {
                }
                offsets := s.Offsetsof(t.fields)
                return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
-       case *Sum:
-               panic("Sizeof unimplemented for type sum")
        case *Union:
-               unimplemented()
+               panic("Sizeof unimplemented for union")
        case *Interface:
                return s.WordSize * 2
        }
index c3e646c80c142d91434991bc5f37d57ab20d4225..e9ffd4f5ca9c681691883d21b2ed9ff56fcaf46a 100644 (file)
@@ -912,12 +912,12 @@ func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
                        msg = "receive from send-only channel"
                }
                return typ.elem, Typ[Invalid], msg
-       case *Sum:
+       case *Union:
                first := true
                var key, val Type
                var msg string
-               typ.is(func(t Type) bool {
-                       k, v, m := rangeKeyVal(under(t), wantKey, wantVal)
+               typ.underIs(func(t Type) bool {
+                       k, v, m := rangeKeyVal(t, wantKey, wantVal)
                        if k == nil || m != "" {
                                key, val, msg = k, v, m
                                return false
index a2b81ba0cc00bc282cc958585b3039b005d7ce80..bfec61a06563781fac3c31a35248bd275a8e7bcc 100644 (file)
@@ -298,21 +298,13 @@ func (subst *subster) typ(typ Type) Type {
                        }
                }
 
-       case *Sum:
-               types, copied := subst.typeList(t.types)
-               if copied {
-                       // Don't do it manually, with a Sum literal: the new
-                       // types list may not be unique and NewSum may remove
-                       // duplicates.
-                       return NewSum(types)
-               }
-
        case *Union:
-               terms, copied := subst.typeList(t.terms)
+               types, copied := subst.typeList(t.types)
                if copied {
-                       // TODO(gri) Do we need to remove duplicates that may have
-                       //           crept in after substitution? It may not matter.
-                       return newUnion(terms, t.tilde)
+                       // TODO(gri) Remove duplicates that may have crept in after substitution
+                       //           (unlikely but possible). This matters for the Identical
+                       //           predicate on unions.
+                       return newUnion(types, t.tilde)
                }
 
        case *Interface:
index 3b2a5960e812803f2a1c74867d826fa081dd81bc..aab75811b8a6f4fbb4f941c3d1dcf81c90e61f25 100644 (file)
@@ -261,53 +261,6 @@ func (s *Signature) Results() *Tuple { return s.results }
 // Variadic reports whether the signature s is variadic.
 func (s *Signature) Variadic() bool { return s.variadic }
 
-// A Sum represents a set of possible types.
-// Sums are currently used to represent type lists of interfaces
-// and thus the underlying types of type parameters; they are not
-// first class types of Go.
-type Sum struct {
-       types []Type // types are unique
-}
-
-// NewSum returns a new Sum type consisting of the provided
-// types if there are more than one. If there is exactly one
-// type, it returns that type. If the list of types is empty
-// the result is nil.
-func NewSum(types []Type) Type {
-       if len(types) == 0 {
-               return nil
-       }
-
-       // What should happen if types contains a sum type?
-       // Do we flatten the types list? For now we check
-       // and panic. This should not be possible for the
-       // current use case of type lists.
-       // TODO(gri) Come up with the rules for sum types.
-       for _, t := range types {
-               if _, ok := t.(*Sum); ok {
-                       panic("sum type contains sum type - unimplemented")
-               }
-       }
-
-       if len(types) == 1 {
-               return types[0]
-       }
-       return &Sum{types: types}
-}
-
-// is reports whether all types in t satisfy pred.
-func (s *Sum) is(pred func(Type) bool) bool {
-       if s == nil {
-               return false
-       }
-       for _, t := range s.types {
-               if !pred(t) {
-                       return false
-               }
-       }
-       return true
-}
-
 // An Interface represents an interface type.
 type Interface struct {
        methods   []*Func // ordered list of explicitly declared methods
@@ -325,8 +278,8 @@ func unpack(typ Type) []Type {
        if typ == nil {
                return nil
        }
-       if sum := asSum(typ); sum != nil {
-               return sum.types
+       if u := asUnion(typ); u != nil {
+               return u.types
        }
        return []Type{typ}
 }
@@ -716,9 +669,16 @@ func optype(typ Type) Type {
                // for a type parameter list of the form:
                // (type T interface { type T }).
                // See also issue #39680.
-               if u := t.Bound().allTypes; u != nil && u != typ {
-                       // u != typ and u is a type parameter => under(u) != typ, so this is ok
-                       return under(u)
+               if a := t.Bound().allTypes; a != nil {
+                       // If we have a union with a single entry, ignore
+                       // any tilde because under(~t) == under(t).
+                       if u, _ := a.(*Union); u != nil && u.NumTerms() == 1 {
+                               a = u.types[0]
+                       }
+                       if a != typ {
+                               // a != typ and a is a type parameter => under(a) != typ, so this is ok
+                               return under(a)
+                       }
                }
                return theTop
        }
@@ -800,7 +760,6 @@ func (t *Struct) Underlying() Type    { return t }
 func (t *Pointer) Underlying() Type   { return t }
 func (t *Tuple) Underlying() Type     { return t }
 func (t *Signature) Underlying() Type { return t }
-func (t *Sum) Underlying() Type       { return t }
 func (t *Interface) Underlying() Type { return t }
 func (t *Map) Underlying() Type       { return t }
 func (t *Chan) Underlying() Type      { return t }
@@ -818,7 +777,6 @@ func (t *Struct) String() string    { return TypeString(t, nil) }
 func (t *Pointer) String() string   { return TypeString(t, nil) }
 func (t *Tuple) String() string     { return TypeString(t, nil) }
 func (t *Signature) String() string { return TypeString(t, nil) }
-func (t *Sum) String() string       { return TypeString(t, nil) }
 func (t *Interface) String() string { return TypeString(t, nil) }
 func (t *Map) String() string       { return TypeString(t, nil) }
 func (t *Chan) String() string      { return TypeString(t, nil) }
@@ -833,7 +791,7 @@ func (t *top) String() string       { return TypeString(t, nil) }
 // under must only be called when a type is known
 // to be fully set up.
 func under(t Type) Type {
-       // TODO(gri) is this correct for *Sum?
+       // TODO(gri) is this correct for *Union?
        if n := asNamed(t); n != nil {
                return n.under()
        }
@@ -880,8 +838,8 @@ func asSignature(t Type) *Signature {
        return op
 }
 
-func asSum(t Type) *Sum {
-       op, _ := optype(t).(*Sum)
+func asUnion(t Type) *Union {
+       op, _ := optype(t).(*Union)
        return op
 }
 
index 55858b7b42a26d1cb05c560ad6be17fbe6e35c5f..466beb239888315ef6b0397d507f1128e45f29b6 100644 (file)
@@ -157,11 +157,8 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
                buf.WriteString("func")
                writeSignature(buf, t, qf, visited)
 
-       case *Sum:
-               writeTypeList(buf, t.types, qf, visited)
-
        case *Union:
-               for i, e := range t.terms {
+               for i, e := range t.types {
                        if i > 0 {
                                buf.WriteString("|")
                        }
index f1630b75d0ed437ae21bc8e85471ec1a4cfe871f..e5983dd40c6c56897d03d6837f279619e01d3f58 100644 (file)
@@ -352,12 +352,9 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
                                u.nify(x.results, y.results, p)
                }
 
-       case *Sum:
-               // This should not happen with the current internal use of sum types.
-               panic("type inference across sum types not implemented")
-
        case *Union:
-               unimplemented()
+               // This should not happen with the current internal use of union types.
+               panic("type inference across union types not implemented")
 
        case *Interface:
                // Two interface types are identical if they have the same set of methods with
index 70dc3bc360b887ae62973144a787349ba2e32f1d..a5ef721ee6bcde7671f25de17a60926f993701a7 100644 (file)
@@ -10,16 +10,16 @@ import "cmd/compile/internal/syntax"
 // API
 
 // A Union represents a union of terms.
-// A term is a type, possibly with a ~ (tilde) indication.
+// A term is a type with a ~ (tilde) flag.
 type Union struct {
-       terms []Type // terms are unique
+       types []Type // types are unique
        tilde []bool // if tilde[i] is set, terms[i] is of the form ~T
 }
 
-func NewUnion(terms []Type, tilde []bool) Type { return newUnion(terms, tilde) }
+func NewUnion(types []Type, tilde []bool) Type { return newUnion(types, tilde) }
 
-func (u *Union) NumTerms() int           { return len(u.terms) }
-func (u *Union) Term(i int) (Type, bool) { return u.terms[i], u.tilde[i] }
+func (u *Union) NumTerms() int           { return len(u.types) }
+func (u *Union) Term(i int) (Type, bool) { return u.types[i], u.tilde[i] }
 
 func (u *Union) Underlying() Type { return u }
 func (u *Union) String() string   { return TypeString(u, nil) }
@@ -27,26 +27,52 @@ func (u *Union) String() string   { return TypeString(u, nil) }
 // ----------------------------------------------------------------------------
 // Implementation
 
-func newUnion(terms []Type, tilde []bool) Type {
-       assert(len(terms) == len(tilde))
-       if terms == nil {
+func newUnion(types []Type, tilde []bool) Type {
+       assert(len(types) == len(tilde))
+       if types == nil {
                return nil
        }
        t := new(Union)
-       t.terms = terms
+       t.types = types
        t.tilde = tilde
        return t
 }
 
+// is reports whether f returned true for all terms (type, tilde) of u.
+func (u *Union) is(f func(Type, bool) bool) bool {
+       if u == nil {
+               return false
+       }
+       for i, t := range u.types {
+               if !f(t, u.tilde[i]) {
+                       return false
+               }
+       }
+       return true
+}
+
+// is reports whether f returned true for the underlying types of all terms of u.
+func (u *Union) underIs(f func(Type) bool) bool {
+       if u == nil {
+               return false
+       }
+       for _, t := range u.types {
+               if !f(under(t)) {
+                       return false
+               }
+       }
+       return true
+}
+
 func parseUnion(check *Checker, tlist []syntax.Expr) Type {
-       var terms []Type
+       var types []Type
        var tilde []bool
        for _, x := range tlist {
                t, d := parseTilde(check, x)
                if len(tlist) == 1 && !d {
                        return t // single type
                }
-               terms = append(terms, t)
+               types = append(types, t)
                tilde = append(tilde, d)
        }
 
@@ -55,7 +81,7 @@ func parseUnion(check *Checker, tlist []syntax.Expr) Type {
        // for correctness of the code.
        // Note: This is a quadratic algorithm, but unions tend to be short.
        check.later(func() {
-               for i, t := range terms {
+               for i, t := range types {
                        t := expand(t)
                        if t == Typ[Invalid] {
                                continue
@@ -85,14 +111,14 @@ func parseUnion(check *Checker, tlist []syntax.Expr) Type {
                        }
 
                        // Complain about duplicate entries a|a, but also a|~a, and ~a|~a.
-                       if includes(terms[:i], t) {
+                       if includes(types[:i], t) {
                                // TODO(gri) this currently doesn't print the ~ if present
                                check.softErrorf(pos, "duplicate term %s in union element", t)
                        }
                }
        })
 
-       return newUnion(terms, tilde)
+       return newUnion(types, tilde)
 }
 
 func parseTilde(check *Checker, x syntax.Expr) (Type, bool) {
@@ -103,3 +129,60 @@ func parseTilde(check *Checker, x syntax.Expr) (Type, bool) {
        }
        return check.anyType(x), tilde
 }
+
+// intersect computes the intersection of the types x and y.
+// Note: An incomming nil type stands for the top type. A top
+// type result is returned as nil.
+func intersect(x, y Type) (r Type) {
+       defer func() {
+               if r == theTop {
+                       r = nil
+               }
+       }()
+
+       switch {
+       case x == theBottom || y == theBottom:
+               return theBottom
+       case x == nil || x == theTop:
+               return y
+       case y == nil || x == theTop:
+               return x
+       }
+
+       // Compute the terms which are in both x and y.
+       xu, _ := x.(*Union)
+       yu, _ := y.(*Union)
+       switch {
+       case xu != nil && yu != nil:
+               // Quadratic algorithm, but good enough for now.
+               // TODO(gri) fix asymptotic performance
+               var types []Type
+               var tilde []bool
+               for _, y := range yu.types {
+                       if includes(xu.types, y) {
+                               types = append(types, y)
+                               tilde = append(tilde, true) // TODO(gri) fix this
+                       }
+               }
+               if types != nil {
+                       return newUnion(types, tilde)
+               }
+
+       case xu != nil:
+               if includes(xu.types, y) {
+                       return y
+               }
+
+       case yu != nil:
+               if includes(yu.types, x) {
+                       return x
+               }
+
+       default: // xu == nil && yu == nil
+               if Identical(x, y) {
+                       return x
+               }
+       }
+
+       return theBottom
+}