go/types: more fixes around parameterized types

Change-Id: I81e6e70a2833e7dbb17f5155386ed3c7a75dc2ac
This commit is contained in:
Robert Griesemer 2019-07-18 16:54:05 -07:00
parent d5d25288d4
commit 0ffe62a473
6 changed files with 52 additions and 27 deletions

View File

@ -112,7 +112,7 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
// if type inference failed, a parametrized result must be invalidated
// (operands cannot have a parametrized type)
if x.mode == value && len(sig.tparams) > 0 && isParametrized(x.typ) {
if x.mode == value && len(sig.tparams) > 0 && isParameterized(x.typ) {
x.mode = invalid
}

View File

@ -16,7 +16,7 @@ import (
// debugging/development support
const (
debug = false // leave on during development
debug = true // leave on during development
trace = false // turn on for detailed type resolution traces
)

View File

@ -21,7 +21,7 @@ func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, a
var indices []int
for i := 0; i < params.Len(); i++ {
par := params.At(i).typ
if isParametrized(par) {
if isParameterized(par) {
indices = append(indices, i)
}
}
@ -71,50 +71,53 @@ func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, a
return targs
}
// isParametrized reports whether typ contains any type parameters.
// isParameterized reports whether typ contains any type parameters.
// TODO(gri) do we need to handle cycles here?
func isParametrized(typ Type) bool {
func isParameterized(typ Type) bool {
switch t := typ.(type) {
case nil, *Basic, *Named: // TODO(gri) should nil be handled here?
break
case *Array:
return isParametrized(t.elem)
return isParameterized(t.elem)
case *Slice:
return isParametrized(t.elem)
return isParameterized(t.elem)
case *Struct:
for _, fld := range t.fields {
if isParametrized(fld.typ) {
if isParameterized(fld.typ) {
return true
}
}
case *Pointer:
return isParametrized(t.base)
return isParameterized(t.base)
case *Tuple:
n := t.Len()
for i := 0; i < n; i++ {
if isParametrized(t.At(i).typ) {
if isParameterized(t.At(i).typ) {
return true
}
}
case *Signature:
assert(t.tparams == nil) // TODO(gri) is this correct?
assert(t.recv == nil || !isParametrized(t.recv.typ)) // interface method receiver may not be nil
return isParametrized(t.params) || isParametrized(t.results)
assert(t.tparams == nil) // TODO(gri) is this correct?
assert(t.recv == nil || !isParameterized(t.recv.typ)) // interface method receiver may not be nil
return isParameterized(t.params) || isParameterized(t.results)
case *Interface:
panic("unimplemented")
case *Map:
return isParametrized(t.key) || isParametrized(t.elem)
return isParameterized(t.key) || isParameterized(t.elem)
case *Chan:
return isParametrized(t.elem)
return isParameterized(t.elem)
case *Parameterized:
return isParameterizedList(t.targs)
case *TypeParam:
return true
@ -125,3 +128,13 @@ func isParametrized(typ Type) bool {
return false
}
// isParameterizedList reports whether any type in list is parameterized.
func isParameterizedList(list []Type) bool {
for _, t := range list {
if isParameterized(t) {
return true
}
}
return false
}

View File

@ -11,10 +11,9 @@ func f(type P) (x P) List(P) {
}
var (
//_ []int = f(0)
//_ []float32 = f(float32)(10)
// TODO(gri) fix next line
// [](List(int)) = f(List(int){})
// _ List(List(int)) = [](List(int)){}
// _ = [](List(int)){}
_ []int = f(0)
_ []float32 = f(float32)(10)
_ [](List(int)) = f(List(int){})
_ List(List(int)) = [](List(int)){}
_ = [](List(int)){}
)

View File

@ -33,4 +33,17 @@ func _() {
var x1 T3(int)
var x2 T2(int, int)
x1.f1.f2 = x2
}
}
func f(type P) (x P) List(P) {
return List(P){x}
}
var (
_ []int = f(0)
_ []float32 = f(float32)(10)
_ List(complex128) = f(1i)
_ [](List(int)) = f(List(int){})
_ List(List(int)) = [](List(int)){}
_ = [](List(int)){}
)

View File

@ -276,6 +276,11 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
typ := new(Parameterized)
def.setUnderlying(typ)
if check.parameterizedType(typ, e) {
if isParameterizedList(typ.targs) {
return typ
}
typ := check.inst(typ.tname, typ.targs)
def.setUnderlying(typ) // TODO(gri) do we need this?
return typ
}
// TODO(gri) If we have a cycle and we reach here, "leafs" of
@ -480,11 +485,6 @@ func (check *Checker) parameterizedType(typ *Parameterized, e *ast.CallExpr) boo
return false
}
// TODO(gri) If none of the arguments is parameterized than we can instantiate the type.
// When instantiating, it will become a different type, at which point the outer type is
// not correct anymore (it's not a Parameterized). If it's defined type, it may also have
// methods and we don't deal with those either... :-(
// complete parameterized type
typ.tname = tname
typ.targs = args