mirror of
https://github.com/golang/go
synced 2024-09-04 23:44:16 +00:00
[dev.typeparams] go/types: replace Sum type with Union type
This is a straightforward port of CL 323274 to go/types. Change-Id: Ica769d90fd482703f260f105199d2f2229498e95 Reviewed-on: https://go-review.googlesource.com/c/go/+/326677 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
e7451f6616
commit
c7a460526e
|
@ -10,12 +10,9 @@ import (
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Inferred = _Inferred
|
Inferred = _Inferred
|
||||||
Sum = _Sum
|
|
||||||
TypeParam = _TypeParam
|
TypeParam = _TypeParam
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewSum(types []Type) Type { return _NewSum(types) }
|
|
||||||
|
|
||||||
// NewTypeParam returns a new TypeParam.
|
// NewTypeParam returns a new TypeParam.
|
||||||
func NewTypeParam(obj *TypeName, index int, bound Type) *TypeParam {
|
func NewTypeParam(obj *TypeName, index int, bound Type) *TypeParam {
|
||||||
return (*Checker)(nil).newTypeParam(obj, index, bound)
|
return (*Checker)(nil).newTypeParam(obj, index, bound)
|
||||||
|
|
|
@ -179,9 +179,9 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
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
|
||||||
|
@ -469,8 +469,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -768,10 +768,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)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -782,7 +786,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(token.NoPos, nil /* = Universe pkg */, "<type parameter>", nil)
|
tpar := NewTypeName(token.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
|
||||||
|
|
|
@ -661,8 +661,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
|
||||||
})
|
})
|
||||||
|
|
|
@ -91,15 +91,15 @@ func (check *Checker) indexExpr(x *operand, e *ast.IndexExpr) (isFuncInst bool)
|
||||||
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 *ast.IndexExpr) (isFuncInst bool)
|
||||||
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 *ast.IndexExpr) (isFuncInst bool)
|
||||||
// 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 *ast.SliceExpr) {
|
||||||
valid = true
|
valid = true
|
||||||
// x.typ doesn't change
|
// x.typ doesn't change
|
||||||
|
|
||||||
case *_Sum, *_TypeParam:
|
case *Union, *_TypeParam:
|
||||||
check.errorf(x, 0, "generic slice expressions not yet implemented")
|
check.errorf(x, 0, "generic slice expressions not yet implemented")
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
return
|
return
|
||||||
|
|
|
@ -302,7 +302,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:
|
||||||
|
@ -315,9 +315,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:
|
|
||||||
panic("unimplemented")
|
|
||||||
|
|
||||||
case *Interface:
|
case *Interface:
|
||||||
if t.allMethods != nil {
|
if t.allMethods != nil {
|
||||||
// TODO(rFindley) at some point we should enforce completeness here
|
// TODO(rFindley) at some point we should enforce completeness here
|
||||||
|
|
|
@ -240,22 +240,26 @@ func completeInterface(check *Checker, pos token.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
|
||||||
|
// 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(atPos(pos), _InvalidIfaceEmbed, "%s is a type parameter, not an interface", typ)
|
check.errorf(atPos(pos), _InvalidIfaceEmbed, "%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(atPos(pos), _InvalidIfaceEmbed, "%s is not an interface", typ)
|
check.errorf(atPos(pos), _InvalidIfaceEmbed, "%s is not an interface", typ)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
types = t
|
types = typ
|
||||||
}
|
}
|
||||||
allTypes = intersect(allTypes, types)
|
allTypes = intersect(allTypes, types)
|
||||||
}
|
}
|
||||||
|
@ -276,44 +280,6 @@ func completeInterface(check *Checker, pos token.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 := unpackType(x)
|
|
||||||
ytypes := unpackType(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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,6 +233,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
|
||||||
|
@ -241,11 +247,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
|
||||||
|
|
|
@ -32,8 +32,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(typ Type) bool { return is(typ, what) })
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -128,11 +128,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()
|
||||||
}
|
}
|
||||||
|
@ -146,8 +145,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
|
||||||
}
|
}
|
||||||
|
@ -265,21 +264,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -288,9 +286,6 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
case *Union:
|
|
||||||
panic("identical0 not implemented for union types")
|
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -107,11 +107,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)
|
||||||
|
|
|
@ -26,7 +26,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},
|
||||||
|
|
|
@ -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:
|
||||||
panic("Sizeof unimplemented for type union")
|
panic("Sizeof unimplemented for union")
|
||||||
case *Interface:
|
case *Interface:
|
||||||
return s.WordSize * 2
|
return s.WordSize * 2
|
||||||
}
|
}
|
||||||
|
|
|
@ -911,12 +911,12 @@ func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
|
||||||
msg = "send-only channel"
|
msg = "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
|
||||||
|
|
|
@ -302,21 +302,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:
|
||||||
|
|
|
@ -255,53 +255,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
|
||||||
|
@ -319,8 +272,8 @@ func unpackType(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}
|
||||||
}
|
}
|
||||||
|
@ -709,9 +662,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 && a != typ {
|
||||||
// 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
|
||||||
}
|
}
|
||||||
|
@ -793,7 +753,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 }
|
||||||
|
@ -811,7 +770,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) }
|
||||||
|
@ -826,7 +784,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()
|
||||||
}
|
}
|
||||||
|
@ -876,8 +834,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,11 +158,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("|")
|
||||||
}
|
}
|
||||||
|
|
|
@ -352,10 +352,6 @@ 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:
|
||||||
// This should not happen with the current internal use of union types.
|
// This should not happen with the current internal use of union types.
|
||||||
panic("type inference across union types not implemented")
|
panic("type inference across union types not implemented")
|
||||||
|
|
|
@ -13,16 +13,16 @@ import (
|
||||||
// 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, possibly 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) }
|
||||||
|
@ -30,26 +30,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 []ast.Expr) Type {
|
func parseUnion(check *Checker, tlist []ast.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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +84,7 @@ func parseUnion(check *Checker, tlist []ast.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
|
||||||
|
@ -88,14 +114,14 @@ func parseUnion(check *Checker, tlist []ast.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(rfindley) this currently doesn't print the ~ if present
|
// TODO(rfindley) this currently doesn't print the ~ if present
|
||||||
check.softErrorf(atPos(pos), _Todo, "duplicate term %s in union element", t)
|
check.softErrorf(atPos(pos), _Todo, "duplicate term %s in union element", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return newUnion(terms, tilde)
|
return newUnion(types, tilde)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTilde(check *Checker, x ast.Expr) (Type, bool) {
|
func parseTilde(check *Checker, x ast.Expr) (Type, bool) {
|
||||||
|
@ -106,3 +132,60 @@ func parseTilde(check *Checker, x ast.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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue