return append(list, x)
}
-// includes reports whether typ is in list
-func includes(list []Type, typ Type) bool {
- for _, e := range list {
- if Identical(typ, e) {
- return true
- }
- }
- return false
-}
-
func (check *Checker) completeInterface(pos syntax.Pos, ityp *Interface) {
if ityp.allMethods != nil {
return
seen[T] = true
// If T is a type parameter not constrained by any type
- // list (i.e., it's underlying type is the top type),
+ // list (i.e., it's operational type is the top type),
// T is comparable if it has the == method. Otherwise,
- // the underlying type "wins". For instance
+ // the operational type "wins". For instance
//
// interface{ comparable; type []byte }
//
// case *instance:
// unreachable since types are expanded
- case *bottom, *top:
- // Either both types are theBottom, or both are theTop in which
- // case the initial x == y check will have caught them. Otherwise
- // they are not identical.
+ case *top:
+ // Either both types are theTop in which case the initial x == y check
+ // will have caught them. Otherwise they are not identical.
case nil:
// avoid a crash in case of nil type
s[typ] = typ
switch t := typ.(type) {
- case *Basic, *bottom, *top:
+ case *Basic, *top:
// nothing to do
case *Array:
{Named{}, 68, 136},
{TypeParam{}, 28, 48},
{instance{}, 52, 96},
- {bottom{}, 0, 0},
{top{}, 0, 0},
// Objects
// Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
if !iface.isSatisfiedBy(targ) {
- check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, under(targ), iface.allTypes)
+ check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, targ, iface.allTypes)
return false
}
// Call typOrNil if it's possible that typ is nil.
panic("nil typ")
- case *Basic, *bottom, *top:
+ case *Basic, *top:
// nothing to do
case *Array:
type sliceOf[E any] interface{ type []E }
-func append[T interface{}, S sliceOf[T], T2 interface{ type T }](s S, t ...T2) S
+func append[T interface{}, S sliceOf[T], T2 interface{ T }](s S, t ...T2) S
var f func()
var cancelSlice []context.CancelFunc
_ interface{~ /* ERROR cannot use interface */ interface{}}
_ interface{int|interface /* ERROR cannot use interface */ {}}
)
+
+// Multiple embedded union elements are intersected. The order in which they
+// appear in the interface doesn't matter since intersection is a symmetric
+// operation.
+
+type myInt1 int
+type myInt2 int
+
+func _[T interface{ myInt1|myInt2; ~int }]() T { return T(0) }
+func _[T interface{ ~int; myInt1|myInt2 }]() T { return T(0) }
+
+// Here the intersections are empty - there's no type that's in the type set of T.
+func _[T interface{ myInt1|myInt2; int }]() T { return T(0 /* ERROR cannot convert */ ) }
+func _[T interface{ int; myInt1|myInt2 }]() T { return T(0 /* ERROR cannot convert */ ) }
// Empty reports whether t is the empty interface.
func (t *Interface) Empty() bool {
t.Complete()
- // A non-nil allTypes may still have length 0 but represents the bottom type.
return len(t.allMethods) == 0 && t.allTypes == nil
}
// "implements" predicate.
func (t *Interface) isSatisfiedBy(typ Type) bool {
t.Complete()
- if t.allTypes == nil {
- return true
+ switch t := t.allTypes.(type) {
+ case nil:
+ return true // no type restrictions
+ case *Union:
+ r, _ := t.intersect(typ, false)
+ return r != nil
+ default:
+ return Identical(t, typ)
}
- types := unpack(t.allTypes)
- return includes(types, typ) || includes(types, under(typ))
}
// Complete computes the interface's method set. It must be called by users of
return iface
}
-// optype returns a type's operational type. Except for
-// type parameters, the operational type is the same
-// as the underlying type (as returned by under). For
-// Type parameters, the operational type is determined
-// by the corresponding type bound's type list. The
-// result may be the bottom or top type, but it is never
-// the incoming type parameter.
+// optype returns a type's operational type. Except for type parameters,
+// the operational type is the same as the underlying type (as returned
+// by under). For Type parameters, the operational type is determined
+// by the corresponding type constraint. The result may be the top type,
+// but it is never the incoming type parameter.
func optype(typ Type) Type {
if t := asTypeParam(typ); t != nil {
// If the optype is typ, return the top type as we have
func init() { expandf = expand }
-// bottom represents the bottom of the type lattice.
-// It is the underlying type of a type parameter that
-// cannot be satisfied by any type, usually because
-// the intersection of type constraints left nothing).
-type bottom struct{}
-
-// theBottom is the singleton bottom type.
-var theBottom = &bottom{}
-
// top represents the top of the type lattice.
// It is the underlying type of a type parameter that
// can be satisfied by any type (ignoring methods),
-// usually because the type constraint has no type
-// list.
+// because its type constraint contains no restrictions
+// besides methods.
type top struct{}
// theTop is the singleton top type.
func (t *Named) Underlying() Type { return t.underlying }
func (t *TypeParam) Underlying() Type { return t }
func (t *instance) Underlying() Type { return t }
-func (t *bottom) Underlying() Type { return t }
func (t *top) Underlying() Type { return t }
// Type-specific implementations of String.
func (t *Named) String() string { return TypeString(t, nil) }
func (t *TypeParam) String() string { return TypeString(t, nil) }
func (t *instance) String() string { return TypeString(t, nil) }
-func (t *bottom) String() string { return TypeString(t, nil) }
func (t *top) String() string { return TypeString(t, nil) }
// under returns the true expanded underlying type.
writeSignature(buf, t, qf, visited)
case *Union:
+ if t.IsEmpty() {
+ buf.WriteString("⊥")
+ break
+ }
for i, e := range t.types {
if i > 0 {
buf.WriteString("|")
writeTypeList(buf, t.targs, qf, visited)
buf.WriteByte(']')
- case *bottom:
- buf.WriteString("⊥")
-
case *top:
buf.WriteString("⊤")
tilde []bool // if tilde[i] is set, terms[i] is of the form ~T
}
-func NewUnion(types []Type, tilde []bool) Type { return newUnion(types, tilde) }
+// NewUnion returns a new Union type with the given terms (types[i], tilde[i]).
+// The lengths of both arguments must match. An empty union represents the set
+// of no types.
+func NewUnion(types []Type, tilde []bool) *Union { return newUnion(types, tilde) }
+func (u *Union) IsEmpty() bool { return len(u.types) == 0 }
func (u *Union) NumTerms() int { return len(u.types) }
func (u *Union) Term(i int) (Type, bool) { return u.types[i], u.tilde[i] }
// ----------------------------------------------------------------------------
// Implementation
-func newUnion(types []Type, tilde []bool) Type {
+var emptyUnion = new(Union)
+
+func newUnion(types []Type, tilde []bool) *Union {
assert(len(types) == len(tilde))
- if types == nil {
- return nil
+ if len(types) == 0 {
+ return emptyUnion
}
t := new(Union)
t.types = types
// 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 {
+ if u.IsEmpty() {
return false
}
for i, t := range u.types {
// 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 {
+ if u.IsEmpty() {
return false
}
for _, t := range u.types {
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.
+// intersect computes the intersection of the types x and y,
+// A nil type stands for the set of all types; an empty union
+// stands for the set of no types.
func intersect(x, y Type) (r Type) {
- defer func() {
- if r == theTop {
- r = nil
- }
- }()
-
+ // If one of the types is nil (no restrictions)
+ // the result is the other type.
switch {
- case x == theBottom || y == theBottom:
- return theBottom
- case x == nil || x == theTop:
+ case x == nil:
return y
- case y == nil || x == theTop:
+ case y == nil:
return x
}
// Compute the terms which are in both x and y.
+ // TODO(gri) This is not correct as it may not always compute
+ // the "largest" intersection. For instance, for
+ // x = myInt|~int, y = ~int
+ // we get the result myInt but we should get ~int.
xu, _ := x.(*Union)
yu, _ := y.(*Union)
switch {
// 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
+ for j, y := range yu.types {
+ yt := yu.tilde[j]
+ if r, rt := xu.intersect(y, yt); r != nil {
+ // Terms x[i] and y[j] match: Select the one that
+ // is not a ~t because that is the intersection
+ // type. If both are ~t, they are identical:
+ // T ∩ T = T
+ // T ∩ ~t = T
+ // ~t ∩ T = T
+ // ~t ∩ ~t = ~t
+ types = append(types, r)
+ tilde = append(tilde, rt)
}
}
- if types != nil {
- return newUnion(types, tilde)
- }
+ return newUnion(types, tilde)
case xu != nil:
- if includes(xu.types, y) {
+ if r, _ := xu.intersect(y, false); r != nil {
return y
}
case yu != nil:
- if includes(yu.types, x) {
+ if r, _ := yu.intersect(x, false); r != nil {
return x
}
}
}
- return theBottom
+ return emptyUnion
+}
+
+// includes reports whether typ is in list.
+func includes(list []Type, typ Type) bool {
+ for _, e := range list {
+ if Identical(typ, e) {
+ return true
+ }
+ }
+ return false
+}
+
+// intersect computes the intersection of the union u and term (y, yt)
+// and returns the intersection term, if any. Otherwise the result is
+// (nil, false).
+func (u *Union) intersect(y Type, yt bool) (Type, bool) {
+ under_y := under(y)
+ for i, x := range u.types {
+ xt := u.tilde[i]
+ // determine which types xx, yy to compare
+ xx := x
+ if yt {
+ xx = under(x)
+ }
+ yy := y
+ if xt {
+ yy = under_y
+ }
+ if Identical(xx, yy) {
+ // T ∩ T = T
+ // T ∩ ~t = T
+ // ~t ∩ T = T
+ // ~t ∩ ~t = ~t
+ return xx, xt && yt
+ }
+ }
+ return nil, false
}