aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2021-07-29 11:10:04 -0700
committerRobert Griesemer <gri@golang.org>2021-08-05 19:36:47 +0000
commitbb5608dd5d056519bd90666b815e0b2bf65e5ee8 (patch)
treece15dcdb79b6a54969dda7ec002a630c71ff40fb
parent6dadee759c812961300c8d1a44959d14299fd9f8 (diff)
downloadgo-bb5608dd5d056519bd90666b815e0b2bf65e5ee8.tar.gz
go-bb5608dd5d056519bd90666b815e0b2bf65e5ee8.zip
[dev.typeparams] cmd/compile/internal/types2: implement type sets with term lists
This CL resolves several known issues and TODOs. - Represent type sets with term lists and using term list abstractions. - Represent Unions internally as a list of (syntactical) terms. Use term operations to print terms and detect overlapping union entries. - Compute type sets corresponding to unions lazily, on demand. - Adjust code throughout. - Adjusted error check in test/typeparam/mincheck.dir/main.go to make test pass. Change-Id: Ib36fb7e1d343c2b6aec51d304f0f7d1ad415f999 Reviewed-on: https://go-review.googlesource.com/c/go/+/338310 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
-rw-r--r--src/cmd/compile/internal/types2/builtins.go16
-rw-r--r--src/cmd/compile/internal/types2/infer.go37
-rw-r--r--src/cmd/compile/internal/types2/instantiate.go24
-rw-r--r--src/cmd/compile/internal/types2/interface.go33
-rw-r--r--src/cmd/compile/internal/types2/operand.go2
-rw-r--r--src/cmd/compile/internal/types2/predicates.go12
-rw-r--r--src/cmd/compile/internal/types2/sizeof_test.go4
-rw-r--r--src/cmd/compile/internal/types2/stmt.go2
-rw-r--r--src/cmd/compile/internal/types2/subst.go12
-rw-r--r--src/cmd/compile/internal/types2/termlist.go2
-rw-r--r--src/cmd/compile/internal/types2/testdata/check/typeinst2.go28
-rw-r--r--src/cmd/compile/internal/types2/testdata/check/typeparams.go265
-rw-r--r--src/cmd/compile/internal/types2/testdata/examples/constraints.go241
-rw-r--r--src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go210
-rw-r--r--src/cmd/compile/internal/types2/type.go21
-rw-r--r--src/cmd/compile/internal/types2/typeparam.go27
-rw-r--r--src/cmd/compile/internal/types2/typeset.go169
-rw-r--r--src/cmd/compile/internal/types2/typestring.go25
-rw-r--r--src/cmd/compile/internal/types2/typexpr.go16
-rw-r--r--src/cmd/compile/internal/types2/unify.go5
-rw-r--r--src/cmd/compile/internal/types2/union.go206
-rw-r--r--src/cmd/compile/internal/types2/universe.go4
-rw-r--r--test/typeparam/mincheck.dir/main.go4
23 files changed, 331 insertions, 414 deletions
diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go
index 7b2c92bfa8..c022e79c97 100644
--- a/src/cmd/compile/internal/types2/builtins.go
+++ b/src/cmd/compile/internal/types2/builtins.go
@@ -144,7 +144,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
mode := invalid
var typ Type
var val constant.Value
- switch typ = implicitArrayDeref(optype(x.typ)); t := typ.(type) {
+ switch typ = implicitArrayDeref(under(x.typ)); t := typ.(type) {
case *Basic:
if isString(t) && id == _Len {
if x.mode == constant_ {
@@ -178,9 +178,9 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
mode = value
}
- case *Union:
+ case *TypeParam:
if t.underIs(func(t Type) bool {
- switch t := t.(type) {
+ switch t := implicitArrayDeref(t).(type) {
case *Basic:
if isString(t) && id == _Len {
return true
@@ -817,10 +817,10 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
// type and collect possible result types at the same time.
var rtypes []Type
var tildes []bool
- if !tp.iface().is(func(typ Type, tilde bool) bool {
- if r := f(typ); r != nil {
+ if !tp.iface().typeSet().is(func(t *term) bool {
+ if r := f(t.typ); r != nil {
rtypes = append(rtypes, r)
- tildes = append(tildes, tilde)
+ tildes = append(tildes, t.tilde)
return true
}
return false
@@ -837,10 +837,8 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
// type param is placed in the current package so export/import
// works as expected.
tpar := NewTypeName(nopos, check.pkg, "<type parameter>", nil)
- ptyp := check.NewTypeParam(tpar, &emptyInterface) // assigns type to tpar as a side-effect
+ ptyp := check.NewTypeParam(tpar, NewInterfaceType(nil, []Type{newUnion(rtypes, tildes)})) // assigns type to tpar as a side-effect
ptyp.index = tp.index
- tsum := newUnion(rtypes, tildes)
- ptyp.bound = &Interface{complete: true, tset: &TypeSet{types: tsum}}
return ptyp
}
diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go
index 00548b402e..a3772aa713 100644
--- a/src/cmd/compile/internal/types2/infer.go
+++ b/src/cmd/compile/internal/types2/infer.go
@@ -280,7 +280,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
}()
switch t := typ.(type) {
- case nil, *Basic: // TODO(gri) should nil be handled here?
+ case nil, *top, *Basic: // TODO(gri) should nil be handled here?
break
case *Array:
@@ -307,9 +307,6 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
}
}
- case *Union:
- return w.isParameterizedTermList(t.terms)
-
case *Signature:
// t.tparams may not be nil if we are looking at a signature
// of a generic function type (or an interface method) that is
@@ -327,7 +324,9 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
return true
}
}
- return w.isParameterized(tset.types)
+ return tset.is(func(t *term) bool {
+ return w.isParameterized(t.typ)
+ })
case *Map:
return w.isParameterized(t.key) || w.isParameterized(t.elem)
@@ -358,15 +357,6 @@ func (w *tpWalker) isParameterizedTypeList(list []Type) bool {
return false
}
-func (w *tpWalker) isParameterizedTermList(list []*term) bool {
- for _, t := range list {
- if w.isParameterized(t.typ) {
- return true
- }
- }
- return false
-}
-
// inferB returns the list of actual type arguments inferred from the type parameters'
// bounds and an initial set of type arguments. If type inference is impossible because
// unification fails, an error is reported if report is set to true, the resulting types
@@ -394,7 +384,7 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty
// Unify type parameters with their structural constraints, if any.
for _, tpar := range tparams {
typ := tpar.typ.(*TypeParam)
- sbound := check.structuralType(typ.bound)
+ sbound := typ.structuralType()
if sbound != nil {
if !u.unify(typ, sbound) {
if report {
@@ -467,20 +457,3 @@ func (check *Checker) inferB(tparams []*TypeName, targs []Type, report bool) (ty
return
}
-
-// structuralType returns the structural type of a constraint, if any.
-func (check *Checker) structuralType(constraint Type) Type {
- if iface, _ := under(constraint).(*Interface); iface != nil {
- types := iface.typeSet().types
- if u, _ := types.(*Union); u != nil {
- if u.NumTerms() == 1 {
- // TODO(gri) do we need to respect tilde?
- t, _ := u.Term(0)
- return t
- }
- return nil
- }
- return types
- }
- return nil
-}
diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go
index 357f041c46..b7ea193a06 100644
--- a/src/cmd/compile/internal/types2/instantiate.go
+++ b/src/cmd/compile/internal/types2/instantiate.go
@@ -212,7 +212,7 @@ func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap
}
// targ's underlying type must also be one of the interface types listed, if any
- if iface.typeSet().types == nil {
+ if !iface.typeSet().hasTerms() {
return true // nothing to do
}
@@ -220,24 +220,22 @@ func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap
// list of iface types (i.e., the targ type list must be a non-empty subset of the iface types).
if targ := asTypeParam(targ); targ != nil {
targBound := targ.iface()
- if targBound.typeSet().types == nil {
+ if !targBound.typeSet().hasTerms() {
check.softErrorf(pos, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
return false
}
- return iface.is(func(typ Type, tilde bool) bool {
- // TODO(gri) incorporate tilde information!
- if !iface.isSatisfiedBy(typ) {
- // TODO(gri) match this error message with the one below (or vice versa)
- check.softErrorf(pos, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, typ, iface.typeSet().types)
- return false
- }
- return true
- })
+ if !targBound.typeSet().subsetOf(iface.typeSet()) {
+ // TODO(gri) need better error message
+ check.softErrorf(pos, "%s does not satisfy %s", targ, tpar.bound)
+ return false
+ }
+ return true
}
// 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, targ, iface.typeSet().types)
+ if !iface.typeSet().includes(targ) {
+ // TODO(gri) better error message
+ check.softErrorf(pos, "%s does not satisfy %s", targ, tpar.bound)
return false
}
diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go
index fc1f5ffe00..aa7d0b05a0 100644
--- a/src/cmd/compile/internal/types2/interface.go
+++ b/src/cmd/compile/internal/types2/interface.go
@@ -21,20 +21,7 @@ type Interface struct {
}
// typeSet returns the type set for interface t.
-func (t *Interface) typeSet() *TypeSet { return computeTypeSet(nil, nopos, t) }
-
-// is reports whether interface t represents types that all satisfy f.
-func (t *Interface) is(f func(Type, bool) bool) bool {
- switch t := t.typeSet().types.(type) {
- case nil, *top:
- // TODO(gri) should settle on top or nil to represent this case
- return false // we must have at least one type! (was bug)
- case *Union:
- return t.is(func(t *term) bool { return f(t.typ, t.tilde) })
- default:
- return f(t, false)
- }
-}
+func (t *Interface) typeSet() *TypeSet { return computeInterfaceTypeSet(nil, nopos, t) }
// emptyInterface represents the empty interface
var emptyInterface = Interface{complete: true, tset: &topTypeSet}
@@ -113,22 +100,6 @@ func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable() }
// IsConstraint reports whether interface t is not just a method set.
func (t *Interface) IsConstraint() bool { return !t.typeSet().IsMethodSet() }
-// isSatisfiedBy reports whether interface t's type list is satisfied by the type typ.
-// If the type list is empty (absent), typ trivially satisfies the interface.
-// TODO(gri) This is not a great name. Eventually, we should have a more comprehensive
-// "implements" predicate.
-func (t *Interface) isSatisfiedBy(typ Type) bool {
- switch t := t.typeSet().types.(type) {
- case nil:
- return true // no type restrictions
- case *Union:
- r, _ := t.intersect(typ, false)
- return r != nil
- default:
- return Identical(t, typ)
- }
-}
-
// Complete computes the interface's type set. It must be called by users of
// NewInterfaceType and NewInterface after the interface's embedded types are
// fully defined and before using the interface type in any way other than to
@@ -262,7 +233,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
// Compute type set with a non-nil *Checker as soon as possible
// to report any errors. Subsequent uses of type sets will use
// this computed type set and won't need to pass in a *Checker.
- check.later(func() { computeTypeSet(check, iface.Pos(), ityp) })
+ check.later(func() { computeInterfaceTypeSet(check, iface.Pos(), ityp) })
}
func flattenUnion(list []syntax.Expr, x syntax.Expr) []syntax.Expr {
diff --git a/src/cmd/compile/internal/types2/operand.go b/src/cmd/compile/internal/types2/operand.go
index 34d35b2594..8336451e9c 100644
--- a/src/cmd/compile/internal/types2/operand.go
+++ b/src/cmd/compile/internal/types2/operand.go
@@ -273,7 +273,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
// x is an untyped value representable by a value of type T.
if isUntyped(Vu) {
- if t, ok := Tu.(*Union); ok {
+ if t, ok := Tu.(*TypeParam); ok {
return t.is(func(t *term) bool {
// TODO(gri) this could probably be more efficient
if t.tilde {
diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go
index bb7fedda3b..afef488b96 100644
--- a/src/cmd/compile/internal/types2/predicates.go
+++ b/src/cmd/compile/internal/types2/predicates.go
@@ -229,16 +229,6 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
identical(x.results, y.results, cmpTags, p)
}
- 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.(*Union); ok {
- return identicalTerms(x.terms, y.terms)
- }
-
case *Interface:
// Two interface types are identical if they describe the same type sets.
// With the existing implementation restriction, this simplifies to:
@@ -250,7 +240,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
if y, ok := y.(*Interface); ok {
xset := x.typeSet()
yset := y.typeSet()
- if !Identical(xset.types, yset.types) {
+ if !xset.terms.equal(yset.terms) {
return false
}
a := xset.methods
diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go
index 8255e6ded4..d2f53258f0 100644
--- a/src/cmd/compile/internal/types2/sizeof_test.go
+++ b/src/cmd/compile/internal/types2/sizeof_test.go
@@ -27,7 +27,7 @@ func TestSizeof(t *testing.T) {
{Pointer{}, 8, 16},
{Tuple{}, 12, 24},
{Signature{}, 28, 56},
- {Union{}, 12, 24},
+ {Union{}, 16, 32},
{Interface{}, 40, 80},
{Map{}, 16, 32},
{Chan{}, 12, 24},
@@ -49,7 +49,7 @@ func TestSizeof(t *testing.T) {
// Misc
{Scope{}, 60, 104},
{Package{}, 40, 80},
- {TypeSet{}, 24, 48},
+ {TypeSet{}, 28, 56},
}
for _, test := range tests {
diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go
index 9b8295c4f4..1efce511f1 100644
--- a/src/cmd/compile/internal/types2/stmt.go
+++ b/src/cmd/compile/internal/types2/stmt.go
@@ -921,7 +921,7 @@ func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
msg = "receive from send-only channel"
}
return typ.elem, Typ[Invalid], msg
- case *Union:
+ case *TypeParam:
first := true
var key, val Type
var msg string
diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go
index e497e17463..6c5f756491 100644
--- a/src/cmd/compile/internal/types2/subst.go
+++ b/src/cmd/compile/internal/types2/subst.go
@@ -145,12 +145,12 @@ func (subst *subster) typ(typ Type) Type {
}
case *Union:
- terms, copied := subst.termList(t.terms)
+ terms, copied := subst.termlist(t.terms)
if copied {
- // TODO(gri) Remove duplicates that may have crept in after substitution
- // (unlikely but possible). This matters for the Identical
- // predicate on unions.
- return &Union{terms}
+ // term list substitution may introduce duplicate terms (unlikely but possible).
+ // This is ok; lazy type set computation will determine the actual type set
+ // in normal form.
+ return &Union{terms, nil}
}
case *Interface:
@@ -387,7 +387,7 @@ func (subst *subster) typeList(in []Type) (out []Type, copied bool) {
return
}
-func (subst *subster) termList(in []*term) (out []*term, copied bool) {
+func (subst *subster) termlist(in []*term) (out []*term, copied bool) {
out = in
for i, t := range in {
if u := subst.typ(t.typ); u != t.typ {
diff --git a/src/cmd/compile/internal/types2/termlist.go b/src/cmd/compile/internal/types2/termlist.go
index b2c26f41be..07056edd97 100644
--- a/src/cmd/compile/internal/types2/termlist.go
+++ b/src/cmd/compile/internal/types2/termlist.go
@@ -13,7 +13,7 @@ import "bytes"
// normal form.
type termlist []*term
-// topTermList represents the set of all types.
+// topTermlist represents the set of all types.
// It is in normal form.
var topTermlist = termlist{new(term)}
diff --git a/src/cmd/compile/internal/types2/testdata/check/typeinst2.go2 b/src/cmd/compile/internal/types2/testdata/check/typeinst2.go2
index 14d8f0ea8c..e90e4dde44 100644
--- a/src/cmd/compile/internal/types2/testdata/check/typeinst2.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/typeinst2.go2
@@ -164,13 +164,13 @@ type _ interface {
// for them to be all in a single list, and we report the error
// as well.)
type _ interface {
- ~int|~int /* ERROR duplicate term int */
- ~int|int /* ERROR duplicate term int */
- int|int /* ERROR duplicate term int */
+ ~int|~int /* ERROR overlapping terms ~int */
+ ~int|int /* ERROR overlapping terms int */
+ int|int /* ERROR overlapping terms int */
}
type _ interface {
- ~struct{f int} | ~struct{g int} | ~struct /* ERROR duplicate term */ {f int}
+ ~struct{f int} | ~struct{g int} | ~struct /* ERROR overlapping terms */ {f int}
}
// Interface type lists can contain any type, incl. *Named types.
diff --git a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
index 54efd1485b..7392b88555 100644
--- a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
+++ b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2
@@ -149,37 +149,40 @@ func _[T interface{}](x T) {
for range x /* ERROR cannot range */ {}
}
-func _[T interface{ ~string | ~[]string }](x T) {
- for range x {}
- for i := range x { _ = i }
- for i, _ := range x { _ = i }
- for i, e := range x /* ERROR must have the same element type */ { _ = i }
- for _, e := range x /* ERROR must have the same element type */ {}
- var e rune
- _ = e
- for _, (e) = range x /* ERROR must have the same element type */ {}
-}
-
-
-func _[T interface{ ~string | ~[]rune | ~map[int]rune }](x T) {
- for _, e := range x { _ = e }
- for i, e := range x { _ = i; _ = e }
-}
-
-func _[T interface{ ~string | ~[]rune | ~map[string]rune }](x T) {
- for _, e := range x { _ = e }
- for i, e := range x /* ERROR must have the same key type */ { _ = e }
-}
-
-func _[T interface{ ~string | ~chan int }](x T) {
- for range x {}
- for i := range x { _ = i }
- for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value
-}
-
-func _[T interface{ ~string | ~chan<-int }](x T) {
- for i := range x /* ERROR send-only channel */ { _ = i }
-}
+// Disabled for now until we have clarified semantics of range.
+// TODO(gri) fix this
+//
+// func _[T interface{ ~string | ~[]string }](x T) {
+// for range x {}
+// for i := range x { _ = i }
+// for i, _ := range x { _ = i }
+// for i, e := range x /* ERROR must have the same element type */ { _ = i }
+// for _, e := range x /* ERROR must have the same element type */ {}
+// var e rune
+// _ = e
+// for _, (e) = range x /* ERROR must have the same element type */ {}
+// }
+//
+//
+// func _[T interface{ ~string | ~[]rune | ~map[int]rune }](x T) {
+// for _, e := range x { _ = e }
+// for i, e := range x { _ = i; _ = e }
+// }
+//
+// func _[T interface{ ~string | ~[]rune | ~map[string]rune }](x T) {
+// for _, e := range x { _ = e }
+// for i, e := range x /* ERROR must have the same key type */ { _ = e }
+// }
+//
+// func _[T interface{ ~string | ~chan int }](x T) {
+// for range x {}
+// for i := range x { _ = i }
+// for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value
+// }
+//
+// func _[T interface{ ~string | ~chan<-int }](x T) {
+// for i := range x /* ERROR send-only channel */ { _ = i }
+// }
// type inference checks
diff --git a/src/cmd/compile/internal/types2/testdata/examples/constraints.go2 b/src/cmd/compile/internal/types2/testdata/examples/constraints.go2
index 28aa19bb12..f40d18c63e 100644
--- a/src/cmd/compile/internal/types2/testdata/examples/constraints.go2
+++ b/src/cmd/compile/internal/types2/testdata/examples/constraints.go2
@@ -18,18 +18,25 @@ type (
}
)
+type MyInt int
+
type (
// Arbitrary types may be embedded like interfaces.
_ interface{int}
_ interface{~int}
// Types may be combined into a union.
- _ interface{int|~string}
+ union interface{int|~string}
- // Union terms must be unique independent of whether they are ~ or not.
- _ interface{int|int /* ERROR duplicate term int */ }
- _ interface{int|~ /* ERROR duplicate term int */ int }
- _ interface{~int|~ /* ERROR duplicate term int */ int }
+ // Union terms must describe disjoint (non-overlapping) type sets.
+ _ interface{int|int /* ERROR overlapping terms int */ }
+ _ interface{int|~ /* ERROR overlapping terms ~int */ int }
+ _ interface{~int|~ /* ERROR overlapping terms ~int */ int }
+ _ interface{~int|MyInt /* ERROR overlapping terms p.MyInt and ~int */ }
+ _ interface{int|interface{}}
+ _ interface{int|~string|union}
+ _ interface{int|~string|interface{int}}
+ _ interface{union|union /* ERROR overlapping terms p.union and p.union */ }
// For now we do not permit interfaces with methods in unions.
_ interface{~ /* ERROR invalid use of ~ */ interface{}}
@@ -45,6 +52,15 @@ type (
_ interface{~ /* ERROR invalid use of ~ */ bar }
)
+// Stand-alone type parameters are not permitted as elements or terms in unions.
+type (
+ _[T interface{ *T } ] struct{} // ok
+ _[T interface{ int | *T } ] struct{} // ok
+ _[T interface{ T /* ERROR cannot embed a type parameter */ } ] struct{}
+ _[T interface{ ~T /* ERROR cannot embed a type parameter */ } ] struct{}
+ _[T interface{ int|T /* ERROR cannot embed a type parameter */ }] struct{}
+)
+
// Multiple embedded union elements are intersected. The order in which they
// appear in the interface doesn't matter since intersection is a symmetric
// operation.
@@ -58,3 +74,18 @@ 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 */ ) }
+
+// Union elements may be interfaces as long as they don't define
+// any methods or embed comparable.
+
+type (
+ Integer interface{ ~int|~int8|~int16|~int32|~int64 }
+ Unsigned interface{ ~uint|~uint8|~uint16|~uint32|~uint64 }
+ Floats interface{ ~float32|~float64 }
+ Complex interface{ ~complex64|~complex128 }
+ Number interface{ Integer|Unsigned|Floats|Complex }
+ Ordered interface{ Integer|Unsigned|Floats|~string }
+
+ _ interface{ Number | error /* ERROR cannot use error in union */ }
+ _ interface{ Ordered | comparable /* ERROR cannot use comparable in union */ }
+)
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2
index ab535049dd..60650432a4 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue41124.go2
@@ -47,7 +47,7 @@ type _ struct{
}
type _ struct{
- I3 // ERROR interface contains type constraints
+ I3 // ERROR interface is .* comparable
}
// General composite types.
@@ -59,19 +59,19 @@ type (
_ []I1 // ERROR interface is .* comparable
_ []I2 // ERROR interface contains type constraints
- _ *I3 // ERROR interface contains type constraints
+ _ *I3 // ERROR interface is .* comparable
_ map[I1 /* ERROR interface is .* comparable */ ]I2 // ERROR interface contains type constraints
- _ chan I3 // ERROR interface contains type constraints
+ _ chan I3 // ERROR interface is .* comparable
_ func(I1 /* ERROR interface is .* comparable */ )
_ func() I2 // ERROR interface contains type constraints
)
// Other cases.
-var _ = [...]I3 /* ERROR interface contains type constraints */ {}
+var _ = [...]I3 /* ERROR interface is .* comparable */ {}
func _(x interface{}) {
- _ = x.(I3 /* ERROR interface contains type constraints */ )
+ _ = x.(I3 /* ERROR interface is .* comparable */ )
}
type T1[_ any] struct{}
diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go
index a943926189..7ae2db3412 100644
--- a/src/cmd/compile/internal/types2/type.go
+++ b/src/cmd/compile/internal/types2/type.go
@@ -44,28 +44,21 @@ func under(t Type) Type {
// 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.
+// Type parameters, the operational type is the structural
+// type, if any; otherwise it's the top type.
+// The result is never the incoming type parameter.
func optype(typ Type) Type {
if t := asTypeParam(typ); t != nil {
+ // TODO(gri) review accuracy of this comment
// If the optype is typ, return the top type as we have
// no information. It also prevents infinite recursion
// via the asTypeParam converter function. This can happen
// for a type parameter list of the form:
// (type T interface { type T }).
// See also issue #39680.
- if a := t.iface().typeSet().types; 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.Term(0)
- }
- if a != typ {
- // a != typ and a is a type parameter => under(a) != typ, so this is ok
- return under(a)
- }
+ if u := t.structuralType(); u != nil {
+ assert(u != typ) // "naked" type parameters cannot be embedded
+ return u
}
return theTop
}
diff --git a/src/cmd/compile/internal/types2/typeparam.go b/src/cmd/compile/internal/types2/typeparam.go
index 2614f467b1..27e6e35588 100644
--- a/src/cmd/compile/internal/types2/typeparam.go
+++ b/src/cmd/compile/internal/types2/typeparam.go
@@ -67,7 +67,7 @@ func (t *TypeParam) Constraint() Type {
if n, _ := t.bound.(*Named); n != nil {
pos = n.obj.pos
}
- computeTypeSet(t.check, pos, iface)
+ computeInterfaceTypeSet(t.check, pos, iface)
}
return t.bound
}
@@ -80,14 +80,6 @@ func (t *TypeParam) SetConstraint(bound Type) {
t.bound = bound
}
-// iface returns the constraint interface of t.
-func (t *TypeParam) iface() *Interface {
- if iface, _ := under(t.Constraint()).(*Interface); iface != nil {
- return iface
- }
- return &emptyInterface
-}
-
// Bound returns the constraint interface of t.
// Deprecated. Only here for the compiler.
// TODO(gri) remove in favor of uses of Constraint.
@@ -136,6 +128,23 @@ func bindTParams(list []*TypeName) *TypeParams {
// ----------------------------------------------------------------------------
// Implementation
+// iface returns the constraint interface of t.
+func (t *TypeParam) iface() *Interface {
+ if iface, _ := under(t.Constraint()).(*Interface); iface != nil {
+ return iface
+ }
+ return &emptyInterface
+}
+
+// structuralType returns the structural type of the type parameter's constraint; or nil.
+func (t *TypeParam) structuralType() Type {
+ return t.iface().typeSet().structuralType()
+}
+
+func (t *TypeParam) is(f func(*term) bool) bool {
+ return t.iface().typeSet().is(f)
+}
+
func (t *TypeParam) underIs(f func(Type) bool) bool {
return t.iface().typeSet().underIs(f)
}
diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go
index 5a334b2f53..6e19115ff5 100644
--- a/src/cmd/compile/internal/types2/typeset.go
+++ b/src/cmd/compile/internal/types2/typeset.go
@@ -18,31 +18,32 @@ import (
type TypeSet struct {
comparable bool // if set, the interface is or embeds comparable
// TODO(gri) consider using a set for the methods for faster lookup
- methods []*Func // all methods of the interface; sorted by unique ID
- types Type // typically a *Union; nil means no type restrictions
+ methods []*Func // all methods of the interface; sorted by unique ID
+ terms termlist // type terms of the type set
}
-// IsTop reports whether type set s is the top type set (corresponding to the empty interface).
-func (s *TypeSet) IsTop() bool { return !s.comparable && len(s.methods) == 0 && s.types == nil }
+// IsEmpty reports whether type set s is the empty set.
+func (s *TypeSet) IsEmpty() bool { return s.terms.isEmpty() }
+
+// IsTop reports whether type set s is the set of all types (corresponding to the empty interface).
+func (s *TypeSet) IsTop() bool { return !s.comparable && len(s.methods) == 0 && s.terms.isTop() }
+
+// TODO(gri) IsMethodSet is not a great name for this predicate. Find a better one.
// IsMethodSet reports whether the type set s is described by a single set of methods.
-func (s *TypeSet) IsMethodSet() bool { return !s.comparable && s.types == nil }
+func (s *TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isTop() }
// IsComparable reports whether each type in the set is comparable.
func (s *TypeSet) IsComparable() bool {
- if s.types == nil {
+ if s.terms.isTop() {
return s.comparable
}
- tcomparable := s.underIs(func(u Type) bool {
- return Comparable(u)
+ return s.is(func(t *term) bool {
+ return Comparable(t.typ)
})
- if !s.comparable {
- return tcomparable
- }
- return s.comparable && tcomparable
}
-// TODO(gri) IsTypeSet is not a great name. Find a better one.
+// TODO(gri) IsTypeSet is not a great name for this predicate. Find a better one.
// IsTypeSet reports whether the type set s is represented by a finite set of underlying types.
func (s *TypeSet) IsTypeSet() bool {
@@ -63,15 +64,21 @@ func (s *TypeSet) LookupMethod(pkg *Package, name string) (int, *Func) {
}
func (s *TypeSet) String() string {
- if s.IsTop() {
+ switch {
+ case s.IsEmpty():
+ return "∅"
+ case s.IsTop():
return "⊤"
}
+ hasMethods := len(s.methods) > 0
+ hasTerms := s.hasTerms()
+
var buf bytes.Buffer
buf.WriteByte('{')
if s.comparable {
buf.WriteString(" comparable")
- if len(s.methods) > 0 || s.types != nil {
+ if hasMethods || hasTerms {
buf.WriteByte(';')
}
}
@@ -82,41 +89,77 @@ func (s *TypeSet) String() string {
buf.WriteByte(' ')
buf.WriteString(m.String())
}
- if len(s.methods) > 0 && s.types != nil {
+ if hasMethods && hasTerms {
buf.WriteByte(';')
}
- if s.types != nil {
- buf.WriteByte(' ')
- writeType(&buf, s.types, nil, nil)
+ if hasTerms {
+ buf.WriteString(s.terms.String())
}
+ buf.WriteString(" }") // there was at least one method or term
- buf.WriteString(" }") // there was a least one method or type
return buf.String()
}
// ----------------------------------------------------------------------------
// Implementation
-// underIs reports whether f returned true for the underlying types of the
-// enumerable types in the type set s. If the type set comprises all types
-// f is called once with the top type; if the type set is empty, the result
-// is false.
+func (s *TypeSet) hasTerms() bool { return !s.terms.isTop() }
+func (s *TypeSet) structuralType() Type { return s.terms.structuralType() }
+func (s *TypeSet) includes(t Type) bool { return s.terms.includes(t) }
+func (s1 *TypeSet) subsetOf(s2 *TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
+
+// TODO(gri) TypeSet.is and TypeSet.underIs should probably also go into termlist.go
+
+var topTerm = term{false, theTop}
+
+func (s *TypeSet) is(f func(*term) bool) bool {
+ if len(s.terms) == 0 {
+ return false
+ }
+ for _, t := range s.terms {
+ // Terms represent the top term with a nil type.
+ // The rest of the type checker uses the top type
+ // instead. Convert.
+ // TODO(gri) investigate if we can do without this
+ if t.typ == nil {
+ t = &topTerm
+ }
+ if !f(t) {
+ return false
+ }
+ }
+ return true
+}
+
func (s *TypeSet) underIs(f func(Type) bool) bool {
- switch t := s.types.(type) {
- case nil:
- return f(theTop)
- default:
- return f(t)
- case *Union:
- return t.underIs(f)
+ if len(s.terms) == 0 {
+ return false
+ }
+ for _, t := range s.terms {
+ // see corresponding comment in TypeSet.is
+ u := t.typ
+ if u == nil {
+ u = theTop
+ }
+ // t == under(t) for ~t terms
+ if !t.tilde {
+ u = under(u)
+ }
+ if debug {
+ assert(Identical(u, under(u)))
+ }
+ if !f(u) {
+ return false
+ }
}
+ return true
}
// topTypeSet may be used as type set for the empty interface.
-var topTypeSet TypeSet
+var topTypeSet = TypeSet{terms: topTermlist}
-// computeTypeSet may be called with check == nil.
-func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
+// computeInterfaceTypeSet may be called with check == nil.
+func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
if ityp.tset != nil {
return ityp.tset
}
@@ -152,7 +195,7 @@ func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
// have valid interfaces. Mark the interface as complete to avoid
// infinite recursion if the validType check occurs later for some
// reason.
- ityp.tset = new(TypeSet) // TODO(gri) is this sufficient?
+ ityp.tset = &TypeSet{terms: topTermlist} // TODO(gri) is this sufficient?
// Methods of embedded interfaces are collected unchanged; i.e., the identity
// of a method I.m's Func Object of an interface I is the same as that of
@@ -213,7 +256,7 @@ func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
}
// collect embedded elements
- var allTypes Type
+ var allTerms = topTermlist
for i, typ := range ityp.embeddeds {
// The embedding position is nil for imported interfaces
// and also for interface copies after substitution (but
@@ -222,25 +265,22 @@ func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
if ityp.embedPos != nil {
pos = (*ityp.embedPos)[i]
}
- var types Type
+ var terms termlist
switch t := under(typ).(type) {
case *Interface:
- tset := computeTypeSet(check, pos, t)
+ tset := computeInterfaceTypeSet(check, pos, t)
if tset.comparable {
ityp.tset.comparable = true
}
for _, m := range tset.methods {
addMethod(pos, m, false) // use embedding position pos rather than m.pos
}
- types = tset.types
+ terms = tset.terms
case *Union:
- // 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
+ tset := computeUnionTypeSet(check, pos, t)
+ terms = tset.terms
case *TypeParam:
- // Embedding stand-alone type parameters is not permitted for now.
+ // Embedding stand-alone type parameters is not permitted.
// This case is handled during union parsing.
unreachable()
default:
@@ -251,9 +291,11 @@ func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
check.errorf(pos, "%s is not an interface", typ)
continue
}
- types = typ
+ terms = termlist{{false, typ}}
}
- allTypes = intersect(allTypes, types)
+ // The type set of an interface is the intersection
+ // of the type sets of all its elements.
+ allTerms = allTerms.intersect(terms)
}
ityp.embedPos = nil // not needed anymore (errors have been reported)
@@ -270,7 +312,7 @@ func computeTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *TypeSet {
sortMethods(methods)
ityp.tset.methods = methods
}
- ityp.tset.types = allTypes
+ ityp.tset.terms = allTerms
return ityp.tset
}
@@ -294,3 +336,34 @@ type byUniqueMethodName []*Func
func (a byUniqueMethodName) Len() int { return len(a) }
func (a byUniqueMethodName) Less(i, j int) bool { return a[i].less(&a[j].object) }
func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// computeUnionTypeSet may be called with check == nil.
+func computeUnionTypeSet(check *Checker, pos syntax.Pos, utyp *Union) *TypeSet {
+ if utyp.tset != nil {
+ return utyp.tset
+ }
+
+ // avoid infinite recursion (see also computeInterfaceTypeSet)
+ utyp.tset = new(TypeSet)
+
+ var allTerms termlist
+ for _, t := range utyp.terms {
+ var terms termlist
+ switch u := under(t.typ).(type) {
+ case *Interface:
+ terms = computeInterfaceTypeSet(check, pos, u).terms
+ case *TypeParam:
+ // A stand-alone type parameters is not permitted as union term.
+ // This case is handled during union parsing.
+ unreachable()
+ default:
+ terms = termlist{t}
+ }
+ // The type set of a union expression is the union
+ // of the type sets of each term.
+ allTerms = allTerms.union(terms)
+ }
+ utyp.tset.terms = allTerms
+
+ return utyp.tset
+}
diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go
index b7e32c9860..558da50528 100644
--- a/src/cmd/compile/internal/types2/typestring.go
+++ b/src/cmd/compile/internal/types2/typestring.go
@@ -158,9 +158,10 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
writeSignature(buf, t, qf, visited)
case *Union:
- if t.IsEmpty() {
- buf.WriteString("⊥")
- break
+ // Unions only appear as (syntactic) embedded elements
+ // in interfaces and syntactically cannot be empty.
+ if t.NumTerms() == 0 {
+ panic("internal error: empty union")
}
for i, t := range t.terms {
if i > 0 {
@@ -198,13 +199,21 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
writeSignature(buf, m.typ.(*Signature), qf, visited)
empty = false
}
- if !empty && tset.types != nil {
+ if !empty && tset.hasTerms() {
buf.WriteString("; ")
}
- if tset.types != nil {
- buf.WriteString("type ")
- writeType(buf, tset.types, qf, visited)
- }
+ first := true
+ tset.is(func(t *term) bool {
+ if !first {
+ buf.WriteByte('|')
+ }
+ first = false
+ if t.tilde {
+ buf.WriteByte('~')
+ }
+ writeType(buf, t.typ, qf, visited)
+ return true
+ })
} else {
// print explicit interface methods and embedded types
for i, m := range t.methods {
diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go
index c55d5c093a..fa4a1638b6 100644
--- a/src/cmd/compile/internal/types2/typexpr.go
+++ b/src/cmd/compile/internal/types2/typexpr.go
@@ -147,18 +147,18 @@ func (check *Checker) varType(e syntax.Expr) Type {
// ordinaryType reports an error if typ is an interface type containing
// type lists or is (or embeds) the predeclared type comparable.
func (check *Checker) ordinaryType(pos syntax.Pos, typ Type) {
- // We don't want to call under() (via Interface) or complete interfaces while we
+ // We don't want to call under() (via asInterface) or complete interfaces while we
// are in the middle of type-checking parameter declarations that might belong to
// interface methods. Delay this check to the end of type-checking.
check.later(func() {
if t := asInterface(typ); t != nil {
- tset := computeTypeSet(check, pos, t) // TODO(gri) is this the correct position?
- if tset.types != nil {
- check.softErrorf(pos, "interface contains type constraints (%s)", tset.types)
- return
- }
- if tset.IsComparable() {
- check.softErrorf(pos, "interface is (or embeds) comparable")
+ tset := computeInterfaceTypeSet(check, pos, t) // TODO(gri) is this the correct position?
+ if !tset.IsMethodSet() {
+ if tset.comparable {
+ check.softErrorf(pos, "interface is (or embeds) comparable")
+ } else {
+ check.softErrorf(pos, "interface contains type constraints")
+ }
}
}
})
diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go
index aa9a23d243..75b9a12197 100644
--- a/src/cmd/compile/internal/types2/unify.go
+++ b/src/cmd/compile/internal/types2/unify.go
@@ -361,9 +361,6 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
u.nify(x.results, y.results, p)
}
- case *Union:
- panic("unimplemented: unification with type sets described by types")
-
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
@@ -371,7 +368,7 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
if y, ok := y.(*Interface); ok {
xset := x.typeSet()
yset := y.typeSet()
- if !Identical(xset.types, yset.types) {
+ if !xset.terms.equal(yset.terms) {
return false
}
a := xset.methods
diff --git a/src/cmd/compile/internal/types2/union.go b/src/cmd/compile/internal/types2/union.go
index 1215ef9057..fcd83ce688 100644
--- a/src/cmd/compile/internal/types2/union.go
+++ b/src/cmd/compile/internal/types2/union.go
@@ -9,17 +9,17 @@ import "cmd/compile/internal/syntax"
// ----------------------------------------------------------------------------
// API
-// A Union represents a union of terms.
+// A Union represents a union of terms embedded in an interface.
type Union struct {
- terms []*term
+ terms []*term // list of syntactical terms (not a canonicalized termlist)
+ tset *TypeSet // type set described by this union, computed lazily
}
// 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.
+// The lengths of both arguments must match. It is an error to create an empty
+// union; they are syntactically not possible.
func NewUnion(types []Type, tilde []bool) *Union { return newUnion(types, tilde) }
-func (u *Union) IsEmpty() bool { return len(u.terms) == 0 }
func (u *Union) NumTerms() int { return len(u.terms) }
func (u *Union) Term(i int) (Type, bool) { t := u.terms[i]; return t.typ, t.tilde }
@@ -29,12 +29,10 @@ func (u *Union) String() string { return TypeString(u, nil) }
// ----------------------------------------------------------------------------
// Implementation
-var emptyUnion = new(Union)
-
func newUnion(types []Type, tilde []bool) *Union {
assert(len(types) == len(tilde))
if len(types) == 0 {
- return emptyUnion
+ panic("empty union")
}
t := new(Union)
t.terms = make([]*term, len(types))
@@ -44,52 +42,23 @@ func newUnion(types []Type, tilde []bool) *Union {
return t
}
-// is reports whether f returns true for all terms of u.
-func (u *Union) is(f func(*term) bool) bool {
- if u.IsEmpty() {
- return false
- }
- for _, t := range u.terms {
- if !f(t) {
- return false
- }
- }
- return true
-}
-
-// underIs 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.IsEmpty() {
- return false
- }
- for _, t := range u.terms {
- if !f(under(t.typ)) {
- return false
- }
- }
- return true
-}
-
func parseUnion(check *Checker, tlist []syntax.Expr) Type {
- var types []Type
- var tilde []bool
+ var terms []*term
for _, x := range tlist {
- t, d := parseTilde(check, x)
- if len(tlist) == 1 && !d {
- return t // single type
+ tilde, typ := parseTilde(check, x)
+ if len(tlist) == 1 && !tilde {
+ return typ // single type
}
- types = append(types, t)
- tilde = append(tilde, d)
+ terms = append(terms, &term{tilde, typ})
}
- // Ensure that each type is only present once in the type list.
- // It's ok to do this check later because it's not a requirement
- // for correctness of the code.
+ // Check validity of terms.
+ // Do this check later because it requires types to be set up.
// Note: This is a quadratic algorithm, but unions tend to be short.
check.later(func() {
- for i, t := range types {
- t := expand(t)
- if t == Typ[Invalid] {
+ for i, t := range terms {
+ typ := expand(t.typ)
+ if typ == Typ[Invalid] {
continue
}
@@ -105,16 +74,16 @@ func parseUnion(check *Checker, tlist []syntax.Expr) Type {
}
}
- u := under(t)
+ u := under(typ)
f, _ := u.(*Interface)
- if tilde[i] {
+ if t.tilde {
if f != nil {
- check.errorf(x, "invalid use of ~ (%s is an interface)", t)
+ check.errorf(x, "invalid use of ~ (%s is an interface)", typ)
continue // don't report another error for t
}
- if !Identical(u, t) {
- check.errorf(x, "invalid use of ~ (underlying type of %s is %s)", t, u)
+ if !Identical(u, typ) {
+ check.errorf(x, "invalid use of ~ (underlying type of %s is %s)", typ, u)
continue // don't report another error for t
}
}
@@ -127,19 +96,18 @@ func parseUnion(check *Checker, tlist []syntax.Expr) Type {
continue // don't report another error for t
}
- // Complain about duplicate entries a|a, but also a|~a, and ~a|~a.
- // TODO(gri) We should also exclude myint|~int since myint is included in ~int.
- 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)
+ // Report overlapping (non-disjoint) terms such as
+ // a|a, a|~a, ~a|~a, and ~a|A (where under(A) == a).
+ if j := overlappingTerm(terms[:i], t); j >= 0 {
+ check.softErrorf(pos, "overlapping terms %s and %s", t, terms[j])
}
}
})
- return newUnion(types, tilde)
+ return &Union{terms, nil}
}
-func parseTilde(check *Checker, x syntax.Expr) (typ Type, tilde bool) {
+func parseTilde(check *Checker, x syntax.Expr) (tilde bool, typ Type) {
if op, _ := x.(*syntax.Operation); op != nil && op.Op == syntax.Tilde {
x = op.X
tilde = true
@@ -153,116 +121,20 @@ func parseTilde(check *Checker, x syntax.Expr) (typ Type, tilde bool) {
return
}
-// 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) {
- // If one of the types is nil (no restrictions)
- // the result is the other type.
- switch {
- case x == nil:
- return y
- 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 {
- case xu != nil && yu != nil:
- return &Union{intersectTerms(xu.terms, yu.terms)}
-
- case xu != nil:
- if r, _ := xu.intersect(y, false); r != nil {
- return y
- }
-
- case yu != nil:
- if r, _ := yu.intersect(x, false); r != nil {
- return x
- }
-
- default: // xu == nil && yu == nil
- if Identical(x, y) {
- return x
- }
- }
-
- 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).
-// TODO(gri) this needs to cleaned up/removed once we switch to lazy
-// union type set computation.
-func (u *Union) intersect(y Type, yt bool) (Type, bool) {
- under_y := under(y)
- for _, x := range u.terms {
- xt := x.tilde
- // determine which types xx, yy to compare
- xx := x.typ
- if yt {
- xx = under(xx)
- }
- 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
-}
-
-func identicalTerms(list1, list2 []*term) bool {
- if len(list1) != len(list2) {
- return false
- }
- // Every term in list1 must be in list2.
- // 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 list1 {
- for _, y := range list2 {
- if x.equal(y) {
- continue L // x is in list2
+// overlappingTerm reports the index of the term x in terms which is
+// overlapping (not disjoint) from y. The result is < 0 if there is no
+// such term.
+func overlappingTerm(terms []*term, y *term) int {
+ for i, x := range terms {
+ // disjoint requires non-nil, non-top arguments
+ if debug {
+ if x == nil || x.typ == nil || y == nil || y.typ == nil {
+ panic("internal error: empty or top union term")
}
}
- return false
- }
- return true
-}
-
-func intersectTerms(list1, list2 []*term) (list []*term) {
- // Quadratic algorithm, but good enough for now.
- // TODO(gri) fix asymptotic performance
- for _, x := range list1 {
- for _, y := range list2 {
- if r := x.intersect(y); r != nil {
- list = append(list, r)
- }
+ if !x.disjoint(y) {
+ return i
}
}
- return
+ return -1
}
diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go
index a3dd4bd0d6..7b6c297d05 100644
--- a/src/cmd/compile/internal/types2/universe.go
+++ b/src/cmd/compile/internal/types2/universe.go
@@ -89,7 +89,7 @@ func defPredeclaredTypes() {
sig := NewSignature(nil, nil, NewTuple(res), false)
err := NewFunc(nopos, nil, "Error", sig)
ityp := &Interface{obj, []*Func{err}, nil, nil, true, nil}
- computeTypeSet(nil, nopos, ityp) // prevent races due to lazy computation of tset
+ computeInterfaceTypeSet(nil, nopos, ityp) // prevent races due to lazy computation of tset
typ := NewNamed(obj, ityp, nil)
sig.recv = NewVar(nopos, nil, "", typ)
def(obj)
@@ -99,7 +99,7 @@ func defPredeclaredTypes() {
{
obj := NewTypeName(nopos, nil, "comparable", nil)
obj.setColor(black)
- ityp := &Interface{obj, nil, nil, nil, true, &TypeSet{true, nil, nil}}
+ ityp := &Interface{obj, nil, nil, nil, true, &TypeSet{true, nil, topTermlist}}
NewNamed(obj, ityp, nil)
def(obj)
}
diff --git a/test/typeparam/mincheck.dir/main.go b/test/typeparam/mincheck.dir/main.go
index 72d8effcc5..9cf2c6bafd 100644
--- a/test/typeparam/mincheck.dir/main.go
+++ b/test/typeparam/mincheck.dir/main.go
@@ -28,11 +28,11 @@ func main() {
}
const want2 = "ay"
- if got := a.Min[string]("bb", "ay"); got != want2 { // ERROR "string does not satisfy interface{int|int64|float64}"
+ if got := a.Min[string]("bb", "ay"); got != want2 { // ERROR "string does not satisfy"
panic(fmt.Sprintf("got %d, want %d", got, want2))
}
- if got := a.Min("bb", "ay"); got != want2 { // ERROR "string does not satisfy interface{int|int64|float64}"
+ if got := a.Min("bb", "ay"); got != want2 { // ERROR "string does not satisfy"
panic(fmt.Sprintf("got %d, want %d", got, want2))
}
}