go/types: implemented comparison of parameterized types; minor bug fixes

Also: Added -h flag to test framework; setting -h causes a panic when
an error is reported (for debugging).

Change-Id: Ib45d4ef38769f2ecdd3ce53fa3aed9fd99ef6bc8
This commit is contained in:
Robert Griesemer 2019-07-17 15:28:08 -07:00
parent 06ce58509d
commit de712f2df8
9 changed files with 39 additions and 22 deletions

View File

@ -18,7 +18,6 @@ import (
const (
debug = false // leave on during development
trace = false // turn on for detailed type resolution traces
halt = false // panic on error
)
// If Strict is set, the type-checker enforces additional

View File

@ -100,7 +100,7 @@ var tests = [][]string{
{"testdata/issue6977.src"},
// Go 2 tests (type parameters and contracts)
// {"testdata/tmp.go2"}, // used for ad-hoc tests - file contents transient, excluded from tests
{"testdata/tmp.go2"}, // used for ad-hoc tests - file contents transient
{"testdata/typeparams.go2"},
{"testdata/typeinst.go2"},
{"testdata/typeinst2.go2"},

View File

@ -90,10 +90,6 @@ func (check *Checker) err(pos token.Pos, msg string, soft bool) {
if trace {
check.trace(pos, "ERROR: %s", msg)
}
if halt {
panic(err)
}
f := check.conf.Error
if f == nil {

View File

@ -308,7 +308,22 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair, tparams
}
case *Parameterized:
panic("internal error: cannot compare uninstantiated parametrized types for identity")
// Two parameterized types are identical if the type name and the parameters are identical.
// TODO(gri) This ignores alias types for now. E.g., type A(type P) = P; A(int) == int does
// not work.
if y, ok := y.(*Parameterized); ok {
if x.tname != y.tname {
return false
}
assert(len(x.targs) == len(y.targs)) // since x, y have identical tname
for i, x := range x.targs {
y := y.targs[i]
if !identical(x, y, cmpTags, p, tparams) {
return false
}
}
return true
}
case *TypeParam:
if y, ok := y.(*TypeParam); ok {

View File

@ -4,8 +4,14 @@
package p
type List(type P) []P
type List(type E) []E
type T(type P) List(P)
func f(type P) (x P) List(P) {
return List(P){x}
}
var _ T(int) //= T(int)(List(int){1, 2, 3})
var (
_ []int = f(0)
_ []float32 = f(float32)(10)
//_ []List(int) = f(List(int){})
)

View File

@ -59,7 +59,3 @@ var _ List(List(List(int)))
type T3(type P) List(P)
var _ T3(int) = T3(int)(List(int){1, 2, 3})
// TODO
// type map maps to unique name found in scopes?

View File

@ -32,5 +32,5 @@ type T3(type P) T1(T2(P, P))
func _() {
var x1 T3(int)
var x2 T2(int, int)
x1.f1.f2.f2 = x2
x1.f1.f2 = x2
}

View File

@ -556,7 +556,7 @@ func (t *Interface) Underlying() Type { return t }
func (m *Map) Underlying() Type { return m }
func (c *Chan) Underlying() Type { return c }
func (t *Named) Underlying() Type { return t.underlying }
func (p *Parameterized) Underlying() Type { return p } // TODO(gri) is this correct?
func (p *Parameterized) Underlying() Type { return p.tname.typ.Underlying() }
func (c *Contract) Underlying() Type { return c }
func (c *TypeParam) Underlying() Type { return c }

View File

@ -275,7 +275,11 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
case *ast.CallExpr:
typ := new(Parameterized)
def.setUnderlying(typ)
check.parameterizedType(typ, e)
if check.parameterizedType(typ, e) {
return typ
}
// TODO(gri) If we have a cycle and we reach here, "leafs" of
// the cycle may refer to a not fully set up Parameterized typ.
case *ast.ParenExpr:
return check.definedType(e.X, def)
@ -447,18 +451,18 @@ func (check *Checker) typeList(list []ast.Expr) []Type {
return nil
}
func (check *Checker) parameterizedType(typ *Parameterized, e *ast.CallExpr) {
func (check *Checker) parameterizedType(typ *Parameterized, e *ast.CallExpr) bool {
// TODO(gri) This code cannot handle type aliases at the moment.
// Probably need to do the name lookup here.
t := check.typ(e.Fun)
if t == Typ[Invalid] {
return // error already reported
return false // error already reported
}
named, _ := t.(*Named)
if named == nil || named.obj == nil || !named.obj.IsParameterized() {
check.errorf(e.Pos(), "%s is not a parametrized type", t)
return
return false
}
// the number of supplied types must match the number of type parameters
@ -467,13 +471,13 @@ func (check *Checker) parameterizedType(typ *Parameterized, e *ast.CallExpr) {
if len(e.Args) != len(tname.tparams) {
// TODO(gri) provide better error message
check.errorf(e.Pos(), "got %d arguments but %d type parameters", len(e.Args), len(tname.tparams))
return
return false
}
// evaluate arguments
args := check.typeList(e.Args)
if args == nil {
return
return false
}
// TODO(gri) If none of the arguments is parameterized than we can instantiate the type.
@ -484,6 +488,7 @@ func (check *Checker) parameterizedType(typ *Parameterized, e *ast.CallExpr) {
// complete parameterized type
typ.tname = tname
typ.targs = args
return true
}
func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {