[dev.typeparams] cmd/compile/internal/types2: replace Sum type with Union type

- 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>
This commit is contained in:
Robert Griesemer 2021-05-25 17:49:32 -07:00
parent 7b876def6c
commit 589e32dbdf
16 changed files with 187 additions and 190 deletions

View file

@ -178,9 +178,9 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
mode = value mode = value
} }
case *Sum: case *Union:
if t.is(func(t Type) bool { if t.underIs(func(t Type) bool {
switch t := under(t).(type) { switch t := t.(type) {
case *Basic: case *Basic:
if isString(t) && id == _Len { if isString(t) && id == _Len {
return true return true
@ -460,8 +460,8 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
m = 2 m = 2
case *Map, *Chan: case *Map, *Chan:
m = 1 m = 1
case *Sum: case *Union:
return t.is(valid) return t.underIs(valid)
default: default:
return false return false
} }
@ -749,10 +749,14 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
if tp := asTypeParam(x); tp != nil { if tp := asTypeParam(x); tp != nil {
// Test if t satisfies the requirements for the argument // Test if t satisfies the requirements for the argument
// type and collect possible result types at the same time. // 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 rtypes []Type
var tilde []bool
if !tp.Bound().is(func(x Type) bool { if !tp.Bound().is(func(x Type) bool {
if r := f(x); r != nil { if r := f(x); r != nil {
rtypes = append(rtypes, r) rtypes = append(rtypes, r)
tilde = append(tilde, true) // for now - see TODO above
return true return true
} }
return false return false
@ -768,7 +772,7 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
// construct a suitable new type parameter // construct a suitable new type parameter
tpar := NewTypeName(nopos, nil /* = Universe pkg */, "<type parameter>", nil) tpar := NewTypeName(nopos, nil /* = Universe pkg */, "<type parameter>", nil)
ptyp := check.NewTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect 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} ptyp.bound = &Interface{allMethods: markComplete, allTypes: tsum}
return ptyp return ptyp

View file

@ -723,8 +723,8 @@ func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, const
default: default:
return nil, nil, _InvalidUntypedConversion return nil, nil, _InvalidUntypedConversion
} }
case *Sum: case *Union:
ok := t.is(func(t Type) bool { ok := t.underIs(func(t Type) bool {
target, _, _ := check.implicitTypeAndValue(x, t) target, _, _ := check.implicitTypeAndValue(x, t)
return target != nil return target != nil
}) })

View file

@ -91,15 +91,15 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
x.expr = e x.expr = e
return return
case *Sum: case *Union:
// A sum type can be indexed if all of the sum's types // A union type can be indexed if all of the union's terms
// support indexing and have the same index and element // 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 var tkey, telem Type // key is for map types only
nmaps := 0 // number of map types in sum type nmaps := 0 // number of map types in union type
if typ.is(func(t Type) bool { if typ.underIs(func(t Type) bool {
var e Type var e Type
switch t := under(t).(type) { switch t := t.(type) {
case *Basic: case *Basic:
if isString(t) { if isString(t) {
e = universeByte e = universeByte
@ -113,7 +113,7 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
case *Slice: case *Slice:
e = t.elem e = t.elem
case *Map: 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. // they must have identical key types.
// TODO(gri) We may be able to relax this rule // TODO(gri) We may be able to relax this rule
// but it becomes complicated very quickly. // 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 // ok to continue even if indexing failed - map element type is known
// If there are only maps, we are done. // If there are only maps, we are done.
if nmaps == len(typ.types) { if nmaps == typ.NumTerms() {
x.mode = mapindex x.mode = mapindex
x.typ = telem x.typ = telem
x.expr = e x.expr = e
@ -246,7 +246,7 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) {
valid = true valid = true
// x.typ doesn't change // x.typ doesn't change
case *Sum, *TypeParam: case *Union, *TypeParam:
check.error(x, "generic slice expressions not yet implemented") check.error(x, "generic slice expressions not yet implemented")
x.mode = invalid x.mode = invalid
return return

View file

@ -307,7 +307,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
} }
} }
case *Sum: case *Union:
return w.isParameterizedList(t.types) return w.isParameterizedList(t.types)
case *Signature: 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. // Thus, we only need to look at the input and result parameters.
return w.isParameterized(t.params) || w.isParameterized(t.results) return w.isParameterized(t.params) || w.isParameterized(t.results)
case *Union:
unimplemented()
case *Interface: case *Interface:
if t.allMethods != nil { if t.allMethods != nil {
// interface is complete - quick test // interface is complete - quick test

View file

@ -242,23 +242,26 @@ func completeInterface(check *Checker, pos syntax.Pos, ityp *Interface) {
} }
types = t.allTypes types = t.allTypes
case *Union: case *Union:
types = NewSum(t.terms) // TODO(gri) combine with default case once we have
// TODO(gri) don't ignore tilde information // 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: case *TypeParam:
if check != nil && !check.allowVersion(check.pkg, 1, 18) { if check != nil && !check.allowVersion(check.pkg, 1, 18) {
check.errorf(pos, "%s is a type parameter, not an interface", typ) check.errorf(pos, "%s is a type parameter, not an interface", typ)
continue continue
} }
types = t types = typ
default: default:
if t == Typ[Invalid] { if typ == Typ[Invalid] {
continue continue
} }
if check != nil && !check.allowVersion(check.pkg, 1, 18) { if check != nil && !check.allowVersion(check.pkg, 1, 18) {
check.errorf(pos, "%s is not an interface", typ) check.errorf(pos, "%s is not an interface", typ)
continue continue
} }
types = t types = typ
} }
allTypes = intersect(allTypes, types) allTypes = intersect(allTypes, types)
} }
@ -279,44 +282,6 @@ func completeInterface(check *Checker, pos syntax.Pos, ityp *Interface) {
ityp.allTypes = allTypes 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) { func sortTypes(list []Type) {
sort.Stable(byUniqueTypeName(list)) sort.Stable(byUniqueTypeName(list))
} }

View file

@ -248,6 +248,12 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
V := x.typ 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 // x's type is identical to T
if check.identical(V, T) { if check.identical(V, T) {
return true, 0 return true, 0
@ -256,11 +262,20 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
Vu := optype(V) Vu := optype(V)
Tu := optype(T) 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. // x is an untyped value representable by a value of type T.
if isUntyped(Vu) { if isUntyped(Vu) {
if t, ok := Tu.(*Sum); ok { if t, ok := Tu.(*Union); ok {
return t.is(func(t Type) bool { return t.is(func(t Type, tilde bool) bool {
// TODO(gri) this could probably be more efficient // 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) ok, _ := x.assignableTo(check, t, reason)
return ok return ok
}), _IncompatibleAssign }), _IncompatibleAssign

View file

@ -28,8 +28,8 @@ func is(typ Type, what BasicInfo) bool {
switch t := optype(typ).(type) { switch t := optype(typ).(type) {
case *Basic: case *Basic:
return t.info&what != 0 return t.info&what != 0
case *Sum: case *Union:
return t.is(func(typ Type) bool { return is(typ, what) }) return t.underIs(func(t Type) bool { return is(t, what) })
} }
return false return false
} }
@ -124,11 +124,10 @@ func comparable(T Type, seen map[Type]bool) bool {
return true return true
case *Array: case *Array:
return comparable(t.elem, seen) return comparable(t.elem, seen)
case *Sum: case *Union:
pred := func(t Type) bool { return t.underIs(func(t Type) bool {
return comparable(t, seen) return comparable(t, seen)
} })
return t.is(pred)
case *TypeParam: case *TypeParam:
return t.Bound().IsComparable() return t.Bound().IsComparable()
} }
@ -142,8 +141,8 @@ func hasNil(typ Type) bool {
return t.kind == UnsafePointer return t.kind == UnsafePointer
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan: case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
return true return true
case *Sum: case *Union:
return t.is(hasNil) return t.underIs(hasNil)
} }
return false 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) check.identical0(x.results, y.results, cmpTags, p)
} }
case *Sum: case *Union:
// Two sum types are identical if they contain the same types. // Two union types are identical if they contain the same terms.
// (Sum types always consist of at least two types. Also, the // The set (list) of types in a union type consists of unique
// the set (list) of types in a sum type consists of unique // types - each type appears exactly once. Thus, two union types
// types - each type appears exactly once. Thus, two sum types
// must contain the same number of types to have chance of // must contain the same number of types to have chance of
// being equal. // 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. // Every type in x.types must be in y.types.
// Quadratic algorithm, but probably good enough for now. // Quadratic algorithm, but probably good enough for now.
// TODO(gri) we need a fast quick type ID/hash for all types. // TODO(gri) we need a fast quick type ID/hash for all types.
L: L:
for _, x := range x.types { for i, xt := range x.types {
for _, y := range y.types { for j, yt := range y.types {
if Identical(x, y) { if Identical(xt, yt) && x.tilde[i] == y.tilde[j] {
continue L // x is in y.types 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 return true
} }
case *Union:
unimplemented()
case *Interface: case *Interface:
// Two interface types are identical if they have the same set of methods with // 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 // the same names and identical function types. Lower-case method names from

View file

@ -106,11 +106,8 @@ func (s sanitizer) typ(typ Type) Type {
s.tuple(t.params) s.tuple(t.params)
s.tuple(t.results) s.tuple(t.results)
case *Sum:
s.typeList(t.types)
case *Union: case *Union:
s.typeList(t.terms) s.typeList(t.types)
case *Interface: case *Interface:
s.funcList(t.methods) s.funcList(t.methods)

View file

@ -27,7 +27,6 @@ func TestSizeof(t *testing.T) {
{Pointer{}, 8, 16}, {Pointer{}, 8, 16},
{Tuple{}, 12, 24}, {Tuple{}, 12, 24},
{Signature{}, 44, 88}, {Signature{}, 44, 88},
{Sum{}, 12, 24},
{Union{}, 24, 48}, {Union{}, 24, 48},
{Interface{}, 52, 104}, {Interface{}, 52, 104},
{Map{}, 16, 32}, {Map{}, 16, 32},

View file

@ -148,10 +148,8 @@ func (s *StdSizes) Sizeof(T Type) int64 {
} }
offsets := s.Offsetsof(t.fields) offsets := s.Offsetsof(t.fields)
return offsets[n-1] + s.Sizeof(t.fields[n-1].typ) return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
case *Sum:
panic("Sizeof unimplemented for type sum")
case *Union: case *Union:
unimplemented() panic("Sizeof unimplemented for union")
case *Interface: case *Interface:
return s.WordSize * 2 return s.WordSize * 2
} }

View file

@ -912,12 +912,12 @@ func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
msg = "receive from send-only channel" msg = "receive from send-only channel"
} }
return typ.elem, Typ[Invalid], msg return typ.elem, Typ[Invalid], msg
case *Sum: case *Union:
first := true first := true
var key, val Type var key, val Type
var msg string var msg string
typ.is(func(t Type) bool { typ.underIs(func(t Type) bool {
k, v, m := rangeKeyVal(under(t), wantKey, wantVal) k, v, m := rangeKeyVal(t, wantKey, wantVal)
if k == nil || m != "" { if k == nil || m != "" {
key, val, msg = k, v, m key, val, msg = k, v, m
return false return false

View file

@ -298,21 +298,13 @@ func (subst *subster) typ(typ Type) Type {
} }
} }
case *Sum: case *Union:
types, copied := subst.typeList(t.types) types, copied := subst.typeList(t.types)
if copied { if copied {
// Don't do it manually, with a Sum literal: the new // TODO(gri) Remove duplicates that may have crept in after substitution
// types list may not be unique and NewSum may remove // (unlikely but possible). This matters for the Identical
// duplicates. // predicate on unions.
return NewSum(types) return newUnion(types, t.tilde)
}
case *Union:
terms, copied := subst.typeList(t.terms)
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)
} }
case *Interface: case *Interface:

View file

@ -261,53 +261,6 @@ func (s *Signature) Results() *Tuple { return s.results }
// Variadic reports whether the signature s is variadic. // Variadic reports whether the signature s is variadic.
func (s *Signature) Variadic() bool { return s.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. // An Interface represents an interface type.
type Interface struct { type Interface struct {
methods []*Func // ordered list of explicitly declared methods methods []*Func // ordered list of explicitly declared methods
@ -325,8 +278,8 @@ func unpack(typ Type) []Type {
if typ == nil { if typ == nil {
return nil return nil
} }
if sum := asSum(typ); sum != nil { if u := asUnion(typ); u != nil {
return sum.types return u.types
} }
return []Type{typ} return []Type{typ}
} }
@ -716,9 +669,16 @@ func optype(typ Type) Type {
// for a type parameter list of the form: // for a type parameter list of the form:
// (type T interface { type T }). // (type T interface { type T }).
// See also issue #39680. // See also issue #39680.
if u := t.Bound().allTypes; u != nil && u != typ { if a := t.Bound().allTypes; a != nil {
// u != typ and u is a type parameter => under(u) != typ, so this is ok // If we have a union with a single entry, ignore
return under(u) // 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 return theTop
} }
@ -800,7 +760,6 @@ func (t *Struct) Underlying() Type { return t }
func (t *Pointer) Underlying() Type { return t } func (t *Pointer) Underlying() Type { return t }
func (t *Tuple) Underlying() Type { return t } func (t *Tuple) Underlying() Type { return t }
func (t *Signature) 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 *Interface) Underlying() Type { return t }
func (t *Map) Underlying() Type { return t } func (t *Map) Underlying() Type { return t }
func (t *Chan) 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 *Pointer) String() string { return TypeString(t, nil) }
func (t *Tuple) 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 *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 *Interface) String() string { return TypeString(t, nil) }
func (t *Map) String() string { return TypeString(t, nil) } func (t *Map) String() string { return TypeString(t, nil) }
func (t *Chan) 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 // under must only be called when a type is known
// to be fully set up. // to be fully set up.
func under(t Type) Type { 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 { if n := asNamed(t); n != nil {
return n.under() return n.under()
} }
@ -880,8 +838,8 @@ func asSignature(t Type) *Signature {
return op return op
} }
func asSum(t Type) *Sum { func asUnion(t Type) *Union {
op, _ := optype(t).(*Sum) op, _ := optype(t).(*Union)
return op return op
} }

View file

@ -157,11 +157,8 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
buf.WriteString("func") buf.WriteString("func")
writeSignature(buf, t, qf, visited) writeSignature(buf, t, qf, visited)
case *Sum:
writeTypeList(buf, t.types, qf, visited)
case *Union: case *Union:
for i, e := range t.terms { for i, e := range t.types {
if i > 0 { if i > 0 {
buf.WriteString("|") buf.WriteString("|")
} }

View file

@ -352,12 +352,9 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
u.nify(x.results, y.results, p) 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: 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: case *Interface:
// Two interface types are identical if they have the same set of methods with // Two interface types are identical if they have the same set of methods with

View file

@ -10,16 +10,16 @@ import "cmd/compile/internal/syntax"
// API // API
// A Union represents a union of terms. // 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 { 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 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) NumTerms() int { return len(u.types) }
func (u *Union) Term(i int) (Type, bool) { return u.terms[i], u.tilde[i] } 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) Underlying() Type { return u }
func (u *Union) String() string { return TypeString(u, nil) } func (u *Union) String() string { return TypeString(u, nil) }
@ -27,26 +27,52 @@ func (u *Union) String() string { return TypeString(u, nil) }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Implementation // Implementation
func newUnion(terms []Type, tilde []bool) Type { func newUnion(types []Type, tilde []bool) Type {
assert(len(terms) == len(tilde)) assert(len(types) == len(tilde))
if terms == nil { if types == nil {
return nil return nil
} }
t := new(Union) t := new(Union)
t.terms = terms t.types = types
t.tilde = tilde t.tilde = tilde
return t 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 { func parseUnion(check *Checker, tlist []syntax.Expr) Type {
var terms []Type var types []Type
var tilde []bool var tilde []bool
for _, x := range tlist { for _, x := range tlist {
t, d := parseTilde(check, x) t, d := parseTilde(check, x)
if len(tlist) == 1 && !d { if len(tlist) == 1 && !d {
return t // single type return t // single type
} }
terms = append(terms, t) types = append(types, t)
tilde = append(tilde, d) tilde = append(tilde, d)
} }
@ -55,7 +81,7 @@ func parseUnion(check *Checker, tlist []syntax.Expr) Type {
// for correctness of the code. // for correctness of the code.
// Note: This is a quadratic algorithm, but unions tend to be short. // Note: This is a quadratic algorithm, but unions tend to be short.
check.later(func() { check.later(func() {
for i, t := range terms { for i, t := range types {
t := expand(t) t := expand(t)
if t == Typ[Invalid] { if t == Typ[Invalid] {
continue 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. // 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 // TODO(gri) this currently doesn't print the ~ if present
check.softErrorf(pos, "duplicate term %s in union element", t) 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) { 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 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
}