go/types: more steps towards type-checking instantiated types

Change-Id: Iab2dab932faad704fc7141f7afe04eb80597ec57
This commit is contained in:
Robert Griesemer 2019-07-02 17:30:32 -07:00
parent e6f3c8a0b4
commit fac6c330ac
5 changed files with 102 additions and 29 deletions

View File

@ -66,32 +66,17 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
}
// evaluate arguments
args := check.exprOrTypeList(e.Args)
args, _ := check.exprOrTypeList(e.Args)
// instantiate function if needed
if n := len(args); len(sig.tparams) > 0 && n > 0 && args[0].mode == typexpr {
// if the first argument is a type, assume we have explicit type arguments
if len(sig.tparams) != n {
check.errorf(args[n-1].pos(), "got %d type arguments but want %d", n, len(sig.tparams))
x.mode = invalid
x.expr = e
return expression
}
// collect types
targs := make([]Type, n)
for i, a := range args {
if a.mode != typexpr {
// error was reported earlier
x.mode = invalid
x.expr = e
return expression
}
targs[i] = a.typ
}
// result is type-instantiated function value
x.mode = value
x.typ = check.instantiate(sig, sig.tparams, args)
if x.typ == nil {
x.mode = invalid
}
x.expr = e
x.typ = subst(sig, targs)
return expression
}
@ -127,7 +112,9 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
// exprOrTypeList returns a list of operands and reports an error if the
// list contains a mix of values and types (ignoring invalid operands).
func (check *Checker) exprOrTypeList(elist []ast.Expr) (xlist []*operand) {
func (check *Checker) exprOrTypeList(elist []ast.Expr) (xlist []*operand, ok bool) {
ok = true
switch len(elist) {
case 0:
// nothing to do
@ -166,12 +153,32 @@ func (check *Checker) exprOrTypeList(elist []ast.Expr) (xlist []*operand) {
}
if 0 < ntypes && ntypes < len(xlist) {
check.errorf(xlist[0].pos(), "mix of value and type expressions")
ok = false
}
}
return
}
func (check *Checker) instantiate(typ Type, tparams []*TypeName, args []*operand) Type {
n := len(args)
if n != len(tparams) {
check.errorf(args[n-1].pos(), "got %d type arguments but want %d", n, len(tparams))
return nil
}
// collect types
targs := make([]Type, n)
for i, a := range args {
if a.mode != typexpr {
// error was reported earlier
return nil
}
targs[i] = a.typ
}
// result is instantiated typ
return subst(typ, targs)
}
func (check *Checker) exprList(elist []ast.Expr, allowCommaOk bool) (xlist []*operand, commaOk bool) {
switch len(elist) {
case 0:

View File

@ -15,7 +15,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

@ -66,7 +66,7 @@ func (s *subster) typ(typ Type) (res Type) {
results := s.tuple(t.results)
if recv != t.recv || params != t.params || results != t.results {
copy := *t
copy.tparams = nil // TODO(gri) is this correct?
copy.tparams = nil // TODO(gri) is this correct? (another indication that perhaps tparams belong to the function decl)
copy.recv = recv
copy.params = params
copy.results = results

View File

@ -17,17 +17,27 @@ type T2(type P) struct {
type List(type P) []P
type A1(type P) P
type A1(type P) = P
type A2(type P) struct {
type A2(type P) = struct {
f P
g myInt // myInt should still be in scope chain
}
// Parametrized type instantiations
type _ T1 /* ERROR not a type */ (int) // TODO fix this
var x int
type _ x /* ERROR not a type */ (int)
type _ int /* ERROR not a parametrized type */ ()
type _ myInt /* ERROR not a parametrized type */ ()
// TODO(gri) better error messages
type _ T1 /* ERROR got 0 arguments but 1 type parameters */ ()
type _ T1(x /* ERROR not a type */ )
type _ T1 /* ERROR got 2 arguments but 1 type parameters */ (int, float32)
// var _ A1(int) = x
// TODO

View File

@ -258,9 +258,65 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
check.errorf(x.pos(), "%s is not a type", &x)
}
// case *ast.CallExpr:
// check.typ(e.Fun)
// panic("type instantiation not yet implemented")
case *ast.CallExpr:
// Type instantiation requires a type name, handle everything
// here so we don't need to introduce type parameters into
// operands: parametrized types can only appear in type
// instatiation expressions.
// e.Fun must be a type name
var tname *TypeName
if ident, ok := e.Fun.(*ast.Ident); ok {
obj := check.lookup(ident.Name)
if obj == nil {
if ident.Name == "_" {
check.errorf(ident.Pos(), "cannot use _ as type")
} else {
check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
}
break
}
check.recordUse(ident, obj)
tname, _ = obj.(*TypeName)
if tname == nil {
check.errorf(ident.Pos(), "%s is not a type", ident.Name)
break
}
}
if !tname.IsParametrized() {
check.errorf(e.Pos(), "%s is not a parametrized type", tname.name)
break
}
// the number of supplied types must match the number of type parameters
// TODO(gri) fold into code below - we want to eval args always
if len(e.Args) != len(tname.tparams) {
// TODO(gri) provide better error message
check.errorf(e.Fun.Pos(), "got %d arguments but %d type parameters", len(e.Args), len(tname.tparams))
break
}
// evaluate arguments
args, ok := check.exprOrTypeList(e.Args) // reports error if types and expressions are mixed
if !ok {
break
}
// arguments must be types
assert(len(args) > 0)
if x := args[0]; x.mode != typexpr {
check.errorf(x.pos(), "%s is not a type", x)
break
}
// instantiate typ
if typ := check.instantiate(tname.typ, tname.tparams, args); typ != nil {
// TODO(gri) this is probably not correct
def.setUnderlying(typ)
return typ
}
case *ast.ParenExpr:
return check.definedType(e.X, def)