1
0
mirror of https://github.com/golang/go synced 2024-11-26 08:17:59 -07:00

[dev.typeparams] go/types: accept embedded interface elements

This is a port of CL 321689 to go/types. It differs from that CL in the
uses of the position, AST and error APIs, and in not factoring out an
unimplemented() helper (this helper didn't already exist in go/types, so
it seemed cleaner to defer adding it).

Change-Id: I577a57297caf35eb7a23f63f3f52037a7bb528ea
Reviewed-on: https://go-review.googlesource.com/c/go/+/326069
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Rob Findley 2021-06-08 10:21:51 -04:00 committed by Robert Findley
parent 54f854fb41
commit e7451f6616
23 changed files with 316 additions and 161 deletions

View File

@ -783,7 +783,7 @@ func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
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 := _NewSum(rtypes)
ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum} ptyp.bound = &Interface{allMethods: markComplete, allTypes: tsum}
return ptyp return ptyp
} }

View File

@ -281,16 +281,7 @@ const (
_IncomparableMapKey _IncomparableMapKey
// _InvalidIfaceEmbed occurs when a non-interface type is embedded in an // _InvalidIfaceEmbed occurs when a non-interface type is embedded in an
// interface. // interface (for go 1.17 or earlier).
//
// Example:
// type T struct {}
//
// func (T) m()
//
// type I interface {
// T
// }
_InvalidIfaceEmbed _InvalidIfaceEmbed
// _InvalidPtrEmbed occurs when an embedded field is of the pointer form *T, // _InvalidPtrEmbed occurs when an embedded field is of the pointer form *T,

View File

@ -315,6 +315,9 @@ 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
@ -332,7 +335,7 @@ func (w *tpWalker) isParameterized(typ Type) (res bool) {
return true return true
} }
} }
return w.isParameterizedList(unpackType(t.types)) return w.isParameterizedList(t.embeddeds)
}, nil) }, nil)
case *Map: case *Map:

View File

@ -13,10 +13,17 @@ import (
) )
func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named) { func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named) {
var tlist *ast.Ident // "type" name of first entry in a type list declaration var tlist []ast.Expr
var types []ast.Expr var tname *ast.Ident // "type" name of first entry in a type list declaration
for _, f := range iface.Methods.List { for _, f := range iface.Methods.List {
if len(f.Names) > 0 { if len(f.Names) == 0 {
// We have an embedded type; possibly a union of types.
ityp.embeddeds = append(ityp.embeddeds, parseUnion(check, flattenUnion(nil, f.Type)))
check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos())
continue
}
// We have a method with name f.Names[0], or a type // We have a method with name f.Names[0], or a type
// of a type list (name.Name == "type"). // of a type list (name.Name == "type").
// (The parser ensures that there's only one method // (The parser ensures that there's only one method
@ -28,14 +35,18 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
} }
if name.Name == "type" { if name.Name == "type" {
// Always collect all type list entries, even from // For now, collect all type list entries as if it
// different type lists, under the assumption that // were a single union, where each union element is
// the author intended to include all types. // of the form ~T.
types = append(types, f.Type) // TODO(rfindley) remove once we disallow type lists
if tlist != nil && tlist != name { op := new(ast.UnaryExpr)
op.Op = token.TILDE
op.X = f.Type
tlist = append(tlist, op)
if tname != nil && tname != name {
check.errorf(name, _Todo, "cannot have multiple type lists in an interface") check.errorf(name, _Todo, "cannot have multiple type lists in an interface")
} }
tlist = name tname = name
continue continue
} }
@ -69,18 +80,17 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
m := NewFunc(name.Pos(), check.pkg, name.Name, sig) m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
check.recordDef(name, m) check.recordDef(name, m)
ityp.methods = append(ityp.methods, m) ityp.methods = append(ityp.methods, m)
} else {
// We have an embedded type. completeInterface will
// eventually verify that we have an interface.
ityp.embeddeds = append(ityp.embeddeds, check.typ(f.Type))
check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos())
}
} }
// type constraints // type constraints
ityp.types = _NewSum(check.collectTypeConstraints(iface.Pos(), types)) if tlist != nil {
ityp.embeddeds = append(ityp.embeddeds, parseUnion(check, tlist))
// Types T in a type list are added as ~T expressions but we don't
// have the position of the '~'. Use the first type position instead.
check.posMap[ityp] = append(check.posMap[ityp], tlist[0].(*ast.UnaryExpr).X.Pos())
}
if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 { if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 {
// empty interface // empty interface
ityp.allMethods = markComplete ityp.allMethods = markComplete
return return
@ -93,32 +103,12 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
check.later(func() { check.completeInterface(iface.Pos(), ityp) }) check.later(func() { check.completeInterface(iface.Pos(), ityp) })
} }
func (check *Checker) collectTypeConstraints(pos token.Pos, types []ast.Expr) []Type { func flattenUnion(list []ast.Expr, x ast.Expr) []ast.Expr {
list := make([]Type, 0, len(types)) // assume all types are correct if o, _ := x.(*ast.BinaryExpr); o != nil && o.Op == token.OR {
for _, texpr := range types { list = flattenUnion(list, o.X)
if texpr == nil { x = o.Y
check.invalidAST(atPos(pos), "missing type constraint")
continue
} }
list = append(list, check.varType(texpr)) return append(list, x)
}
// Ensure that each type is only present once in the type list. Types may be
// interfaces, which may not be complete yet. It's ok to do this check at the
// end because it's not a requirement for correctness of the code.
// Note: This is a quadratic algorithm, but type lists tend to be short.
check.later(func() {
for i, t := range list {
if t := asInterface(t); t != nil {
check.completeInterface(types[i].Pos(), t)
}
if includes(list[:i], t) {
check.softErrorf(types[i], _Todo, "duplicate type %s in type list", t)
}
}
})
return list
} }
// includes reports whether typ is in list. // includes reports whether typ is in list.
@ -146,6 +136,7 @@ func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
completeInterface(check, pos, ityp) completeInterface(check, pos, ityp)
} }
// completeInterface may be called with check == nil.
func completeInterface(check *Checker, pos token.Pos, ityp *Interface) { func completeInterface(check *Checker, pos token.Pos, ityp *Interface) {
assert(ityp.allMethods == nil) assert(ityp.allMethods == nil)
@ -198,6 +189,7 @@ func completeInterface(check *Checker, pos token.Pos, ityp *Interface) {
if check == nil { if check == nil {
panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name)) panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name))
} }
// check != nil
check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name) check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
default: default:
@ -211,6 +203,7 @@ func completeInterface(check *Checker, pos token.Pos, ityp *Interface) {
todo = append(todo, m, other.(*Func)) todo = append(todo, m, other.(*Func))
break break
} }
// check != nil
check.later(func() { check.later(func() {
if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) { if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) {
check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name) check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
@ -224,9 +217,8 @@ func completeInterface(check *Checker, pos token.Pos, ityp *Interface) {
addMethod(m.pos, m, true) addMethod(m.pos, m, true)
} }
// collect types // collect embedded elements
allTypes := ityp.types var allTypes Type
var posList []token.Pos var posList []token.Pos
if check != nil { if check != nil {
posList = check.posMap[ityp] posList = check.posMap[ityp]
@ -236,32 +228,36 @@ func completeInterface(check *Checker, pos token.Pos, ityp *Interface) {
if posList != nil { if posList != nil {
pos = posList[i] pos = posList[i]
} }
utyp := under(typ) var types Type
etyp := asInterface(utyp) switch t := under(typ).(type) {
if etyp == nil { case *Interface:
if utyp != Typ[Invalid] { if t.allMethods == nil {
var format string completeInterface(check, pos, t)
if _, ok := utyp.(*_TypeParam); ok {
format = "%s is a type parameter, not an interface"
} else {
format = "%s is not an interface"
}
if check != nil {
// TODO: correct error code.
check.errorf(atPos(pos), _InvalidIfaceEmbed, format, typ)
} else {
panic(fmt.Sprintf(format, typ))
} }
for _, m := range t.allMethods {
addMethod(pos, m, false) // use embedding position pos rather than m.pos
} }
types = t.allTypes
case *Union:
types = NewSum(t.terms)
case *TypeParam:
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
check.errorf(atPos(pos), _InvalidIfaceEmbed, "%s is a type parameter, not an interface", typ)
continue continue
} }
if etyp.allMethods == nil { types = t
completeInterface(check, pos, etyp) default:
if t == Typ[Invalid] {
continue
} }
for _, m := range etyp.allMethods { if check != nil && !check.allowVersion(check.pkg, 1, 18) {
addMethod(pos, m, false) // use embedding position pos rather than m.pos check.errorf(atPos(pos), _InvalidIfaceEmbed, "%s is not an interface", typ)
continue
} }
allTypes = intersect(allTypes, etyp.allTypes) types = t
}
allTypes = intersect(allTypes, types)
} }
// process todo's (this only happens if check == nil) // process todo's (this only happens if check == nil)
@ -281,7 +277,7 @@ func completeInterface(check *Checker, pos token.Pos, ityp *Interface) {
} }
// intersect computes the intersection of the types x and y. // intersect computes the intersection of the types x and y.
// Note: A incomming nil type stands for the top type. A top // Note: An incomming nil type stands for the top type. A top
// type result is returned as nil. // type result is returned as nil.
func intersect(x, y Type) (r Type) { func intersect(x, y Type) (r Type) {
defer func() { defer func() {

View File

@ -288,6 +288,9 @@ 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

View File

@ -110,11 +110,11 @@ func (s sanitizer) typ(typ Type) Type {
case *_Sum: case *_Sum:
s.typeList(t.types) s.typeList(t.types)
case *Union:
s.typeList(t.terms)
case *Interface: case *Interface:
s.funcList(t.methods) s.funcList(t.methods)
if types := s.typ(t.types); types != t.types {
t.types = types
}
s.typeList(t.embeddeds) s.typeList(t.embeddeds)
s.funcList(t.allMethods) s.funcList(t.allMethods)
if allTypes := s.typ(t.allTypes); allTypes != t.allTypes { if allTypes := s.typ(t.allTypes); allTypes != t.allTypes {

View File

@ -27,7 +27,8 @@ func TestSizeof(t *testing.T) {
{Tuple{}, 12, 24}, {Tuple{}, 12, 24},
{Signature{}, 44, 88}, {Signature{}, 44, 88},
{_Sum{}, 12, 24}, {_Sum{}, 12, 24},
{Interface{}, 60, 120}, {Union{}, 24, 48},
{Interface{}, 52, 104},
{Map{}, 16, 32}, {Map{}, 16, 32},
{Chan{}, 12, 24}, {Chan{}, 12, 24},
{Named{}, 68, 136}, {Named{}, 68, 136},

View File

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

View File

@ -311,15 +311,19 @@ func (subst *subster) typ(typ Type) Type {
return _NewSum(types) return _NewSum(types)
} }
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:
methods, mcopied := subst.funcList(t.methods) methods, mcopied := subst.funcList(t.methods)
types := t.types
if t.types != nil {
types = subst.typ(t.types)
}
embeddeds, ecopied := subst.typeList(t.embeddeds) embeddeds, ecopied := subst.typeList(t.embeddeds)
if mcopied || types != t.types || ecopied { if mcopied || ecopied {
iface := &Interface{methods: methods, types: types, embeddeds: embeddeds} iface := &Interface{methods: methods, embeddeds: embeddeds}
if subst.check == nil { if subst.check == nil {
panic("internal error: cannot instantiate interfaces yet") panic("internal error: cannot instantiate interfaces yet")
} }

View File

@ -4,7 +4,7 @@
// type declarations // type declarations
package decls0 package go1_17 // don't permit non-interface elements in interfaces
import "unsafe" import "unsafe"

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package issues package go1_17 // don't permit non-interface elements in interfaces
import ( import (
"fmt" "fmt"

View File

@ -164,12 +164,12 @@ type _ interface {
// for them to be all in a single list, and we report the error // for them to be all in a single list, and we report the error
// as well.) // as well.)
type _ interface { type _ interface {
type int, int /* ERROR duplicate type int */ type int, int /* ERROR duplicate term int */
type /* ERROR multiple type lists */ int /* ERROR duplicate type int */ type /* ERROR multiple type lists */ int /* ERROR duplicate term int */
} }
type _ interface { type _ interface {
type struct{f int}, struct{g int}, struct /* ERROR duplicate type */ {f int} type struct{f int}, struct{g int}, struct /* ERROR duplicate term */ {f int}
} }
// Interface type lists can contain any type, incl. *Named types. // Interface type lists can contain any type, incl. *Named types.

View File

@ -0,0 +1,25 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file shows some examples of generic constraint interfaces.
package p
type (
// Arbitrary types may be embedded like interfaces.
_ interface{int}
_ interface{~int}
// Types may be combined into a 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 }
// For now we do not permit interfaces with ~ or in unions.
_ interface{~ /* ERROR cannot use interface */ interface{}}
_ interface{int|interface /* ERROR cannot use interface */ {}}
)

View File

@ -36,7 +36,7 @@ func bar8[A foo8[A]](a A) {}
func main8() {} func main8() {}
// crash 9 // crash 9
type foo9[A any] interface { type foo9 /* ERROR interface contains type constraints */ [A] } type foo9[A any] interface { type foo9 /* ERROR cannot use interface */ [A] }
func _() { var _ = new(foo9 /* ERROR interface contains type constraints */ [int]) } func _() { var _ = new(foo9 /* ERROR interface contains type constraints */ [int]) }
// crash 12 // crash 12

View File

@ -4,11 +4,20 @@
package p package p
type Number interface { type Number1 interface {
int /* ERROR int is not an interface */ // embedding non-interface types is permitted
float64 /* ERROR float64 is not an interface */ int
float64
} }
func Add[T Number](a, b T) T { func Add[T Number1](a, b T) T {
return a /* ERROR not defined */ + b return a /* ERROR not defined */ + b
} }
type Number2 interface {
int|float64
}
func Add2[T Number2](a, b T) T {
return a + b
}

View File

@ -7,5 +7,7 @@ package p
// Do not report a duplicate type error for this type list. // Do not report a duplicate type error for this type list.
// (Check types after interfaces have been completed.) // (Check types after interfaces have been completed.)
type _ interface { type _ interface {
type interface{ Error() string }, interface{ String() string } // TODO(rfindley) Once we have full type sets we can enable this again.
// Fow now we don't permit interfaces in type lists.
// type interface{ Error() string }, interface{ String() string }
} }

View File

@ -6,4 +6,4 @@ package p
// A constraint must be an interface; it cannot // A constraint must be an interface; it cannot
// be a type parameter, for instance. // be a type parameter, for instance.
func _[A interface{ type interface{} }, B A /* ERROR not an interface */ ]() func _[A interface{ type int }, B A /* ERROR not an interface */ ]()

View File

@ -2,7 +2,13 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package p // TODO(rfindley) Eventually, once we disallow type lists, we need to
// adjust this code: for 1.17 we don't accept type parameters,
// and for 1.18 this code is valid.
// Leaving for now so we can see that existing errors
// are being reported.
package go1_17 // don't permit non-interface elements in interfaces
type T[P any] interface{ type T[P any] interface{
P // ERROR P is a type parameter, not an interface P // ERROR P is a type parameter, not an interface

View File

@ -305,7 +305,6 @@ func (s *_Sum) is(pred func(Type) bool) bool {
// 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
types Type // (possibly a Sum) type declared with a type list (TODO(gri) need better field name)
embeddeds []Type // ordered list of explicitly embedded types embeddeds []Type // ordered list of explicitly embedded types
allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset) allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)

View File

@ -159,11 +159,17 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
writeSignature(buf, t, qf, visited) writeSignature(buf, t, qf, visited)
case *_Sum: case *_Sum:
for i, t := range t.types { writeTypeList(buf, t.types, qf, visited)
case *Union:
for i, e := range t.terms {
if i > 0 { if i > 0 {
buf.WriteString(", ") buf.WriteString("|")
} }
writeType(buf, t, qf, visited) if t.tilde[i] {
buf.WriteByte('~')
}
writeType(buf, e, qf, visited)
} }
case *Interface: case *Interface:
@ -208,14 +214,6 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
writeSignature(buf, m.typ.(*Signature), qf, visited) writeSignature(buf, m.typ.(*Signature), qf, visited)
empty = false empty = false
} }
if !empty && t.types != nil {
buf.WriteString("; ")
}
if t.types != nil {
buf.WriteString("type ")
writeType(buf, t.types, qf, visited)
empty = false
}
if !empty && len(t.embeddeds) > 0 { if !empty && len(t.embeddeds) > 0 {
buf.WriteString("; ") buf.WriteString("; ")
} }
@ -301,6 +299,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
default: default:
// For externally defined implementations of Type. // For externally defined implementations of Type.
// Note: In this case cycles won't be caught.
buf.WriteString(t.String()) buf.WriteString(t.String())
} }
} }

View File

@ -95,6 +95,9 @@ var independentTestTypes = []testEntry{
dup("interface{}"), dup("interface{}"),
dup("interface{m()}"), dup("interface{m()}"),
dup(`interface{String() string; m(int) float32}`), dup(`interface{String() string; m(int) float32}`),
{"interface{type int, float32, complex128}", "interface{~int|~float32|~complex128}"},
dup("interface{int|float32|complex128}"),
dup("interface{int|~float32|~complex128}"),
// TODO(rFindley) uncomment this once this AST is accepted, and add more test // TODO(rFindley) uncomment this once this AST is accepted, and add more test
// cases. // cases.

View File

@ -356,6 +356,10 @@ func (u *unifier) nify(x, y Type, p *ifacePair) bool {
// This should not happen with the current internal use of sum types. // This should not happen with the current internal use of sum types.
panic("type inference across sum types not implemented") panic("type inference across sum types not implemented")
case *Union:
// This should not happen with the current internal use of union types.
panic("type inference across union types not implemented")
case *Interface: case *Interface:
// Two interface types are identical if they have the same set of methods with // Two interface types are identical if they have the same set of methods with
// the same names and identical function types. Lower-case method names from // the same names and identical function types. Lower-case method names from

108
src/go/types/union.go Normal file
View File

@ -0,0 +1,108 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types
import (
"go/ast"
"go/token"
)
// ----------------------------------------------------------------------------
// API
// A Union represents a union of terms.
// A term is a type, possibly with a ~ (tilde) indication.
type Union struct {
terms []Type // terms are unique
tilde []bool // if tilde[i] is set, terms[i] is of the form ~T
}
func NewUnion(terms []Type, tilde []bool) Type { return newUnion(terms, tilde) }
func (u *Union) NumTerms() int { return len(u.terms) }
func (u *Union) Term(i int) (Type, bool) { return u.terms[i], u.tilde[i] }
func (u *Union) Underlying() Type { return u }
func (u *Union) String() string { return TypeString(u, nil) }
// ----------------------------------------------------------------------------
// Implementation
func newUnion(terms []Type, tilde []bool) Type {
assert(len(terms) == len(tilde))
if terms == nil {
return nil
}
t := new(Union)
t.terms = terms
t.tilde = tilde
return t
}
func parseUnion(check *Checker, tlist []ast.Expr) Type {
var terms []Type
var tilde []bool
for _, x := range tlist {
t, d := parseTilde(check, x)
if len(tlist) == 1 && !d {
return t // single type
}
terms = append(terms, t)
tilde = append(tilde, d)
}
// Ensure that each type is only present once in the type list.
// It's ok to do this check at the end because it's not a requirement
// for correctness of the code.
// Note: This is a quadratic algorithm, but unions tend to be short.
check.later(func() {
for i, t := range terms {
t := expand(t)
if t == Typ[Invalid] {
continue
}
x := tlist[i]
pos := x.Pos()
// We may not know the position of x if it was a typechecker-
// introduced ~T type of a type list entry T. Use the position
// of T instead.
// TODO(rfindley) remove this test once we don't support type lists anymore
if !pos.IsValid() {
if op, _ := x.(*ast.UnaryExpr); op != nil {
pos = op.X.Pos()
}
}
u := under(t)
if tilde[i] {
// TODO(rfindley) enable this check once we have converted tests
// if !Identical(u, t) {
// check.errorf(x, "invalid use of ~ (underlying type of %s is %s)", t, u)
// }
}
if _, ok := u.(*Interface); ok {
check.errorf(atPos(pos), _Todo, "cannot use interface %s with ~ or inside a union (implementation restriction)", t)
}
// Complain about duplicate entries a|a, but also a|~a, and ~a|~a.
if includes(terms[:i], t) {
// TODO(rfindley) this currently doesn't print the ~ if present
check.softErrorf(atPos(pos), _Todo, "duplicate term %s in union element", t)
}
}
})
return newUnion(terms, tilde)
}
func parseTilde(check *Checker, x ast.Expr) (Type, bool) {
tilde := false
if op, _ := x.(*ast.UnaryExpr); op != nil && op.Op == token.TILDE {
x = op.X
tilde = true
}
return check.anyType(x), tilde
}