aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/compile/internal/types2/builtins.go16
-rw-r--r--src/cmd/compile/internal/types2/expr.go4
-rw-r--r--src/cmd/compile/internal/types2/index.go18
-rw-r--r--src/cmd/compile/internal/types2/infer.go5
-rw-r--r--src/cmd/compile/internal/types2/interface.go51
-rw-r--r--src/cmd/compile/internal/types2/operand.go19
-rw-r--r--src/cmd/compile/internal/types2/predicates.go35
-rw-r--r--src/cmd/compile/internal/types2/sanitize.go5
-rw-r--r--src/cmd/compile/internal/types2/sizeof_test.go1
-rw-r--r--src/cmd/compile/internal/types2/sizes.go4
-rw-r--r--src/cmd/compile/internal/types2/stmt.go6
-rw-r--r--src/cmd/compile/internal/types2/subst.go18
-rw-r--r--src/cmd/compile/internal/types2/type.go72
-rw-r--r--src/cmd/compile/internal/types2/typestring.go5
-rw-r--r--src/cmd/compile/internal/types2/unify.go7
-rw-r--r--src/cmd/compile/internal/types2/union.go111
16 files changed, 187 insertions, 190 deletions
diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go
index 94fb506d80..1779e32c5c 100644
--- a/src/cmd/compile/internal/types2/builtins.go
+++ b/src/cmd/compile/internal/types2/builtins.go
@@ -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
diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go
index 23b79656bb..b223387f18 100644
--- a/src/cmd/compile/internal/types2/expr.go
+++ b/src/cmd/compile/internal/types2/expr.go
@@ -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
})
diff --git a/src/cmd/compile/internal/types2/index.go b/src/cmd/compile/internal/types2/index.go
index 33e79aac3e..47e0853a3b 100644
--- a/src/cmd/compile/internal/types2/index.go
+++ b/src/cmd/compile/internal/types2/index.go
@@ -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
diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go
index d8865784a5..73ea8330d4 100644
--- a/src/cmd/compile/internal/types2/infer.go
+++ b/src/cmd/compile/internal/types2/infer.go
@@ -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
diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go
index d590066ad6..db34d0705f 100644
--- a/src/cmd/compile/internal/types2/interface.go
+++ b/src/cmd/compile/internal/types2/interface.go
@@ -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))
}
diff --git a/src/cmd/compile/internal/types2/operand.go b/src/cmd/compile/internal/types2/operand.go
index 455d8b5dd1..fdc6ec52aa 100644
--- a/src/cmd/compile/internal/types2/operand.go
+++ b/src/cmd/compile/internal/types2/operand.go
@@ -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
diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go
index ab0a457276..bcb3e221d0 100644
--- a/src/cmd/compile/internal/types2/predicates.go
+++ b/src/cmd/compile/internal/types2/predicates.go
@@ -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
diff --git a/src/cmd/compile/internal/types2/sanitize.go b/src/cmd/compile/internal/types2/sanitize.go
index c30febfda8..ce26bab186 100644
--- a/src/cmd/compile/internal/types2/sanitize.go
+++ b/src/cmd/compile/internal/types2/sanitize.go
@@ -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)
diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go
index 552f3488cd..d3c391161e 100644
--- a/src/cmd/compile/internal/types2/sizeof_test.go
+++ b/src/cmd/compile/internal/types2/sizeof_test.go
@@ -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},
diff --git a/src/cmd/compile/internal/types2/sizes.go b/src/cmd/compile/internal/types2/sizes.go
index c6b807cd06..cb789598e5 100644
--- a/src/cmd/compile/internal/types2/sizes.go
+++ b/src/cmd/compile/internal/types2/sizes.go
@@ -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
}
diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go
index c3e646c80c..e9ffd4f5ca 100644
--- a/src/cmd/compile/internal/types2/stmt.go
+++ b/src/cmd/compile/internal/types2/stmt.go
@@ -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
diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go
index a2b81ba0cc..bfec61a065 100644
--- a/src/cmd/compile/internal/types2/subst.go
+++ b/src/cmd/compile/internal/types2/subst.go
@@ -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:
diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go
index 3b2a5960e8..aab75811b8 100644
--- a/src/cmd/compile/internal/types2/type.go
+++ b/src/cmd/compile/internal/types2/type.go
@@ -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
}
diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go
index 55858b7b42..466beb2398 100644
--- a/src/cmd/compile/internal/types2/typestring.go
+++ b/src/cmd/compile/internal/types2/typestring.go
@@ -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("|")
}
diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go
index f1630b75d0..e5983dd40c 100644
--- a/src/cmd/compile/internal/types2/unify.go
+++ b/src/cmd/compile/internal/types2/unify.go
@@ -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
diff --git a/src/cmd/compile/internal/types2/union.go b/src/cmd/compile/internal/types2/union.go
index 70dc3bc360..a5ef721ee6 100644
--- a/src/cmd/compile/internal/types2/union.go
+++ b/src/cmd/compile/internal/types2/union.go
@@ -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
+}