diff --git a/src/go/types/call.go b/src/go/types/call.go index 0e40df580b..03554c675c 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -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: diff --git a/src/go/types/check.go b/src/go/types/check.go index 007babdf9d..493b73986f 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -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 ) diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 9766772ea0..8c245753b0 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -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 diff --git a/src/go/types/testdata/typeinst.go2 b/src/go/types/testdata/typeinst.go2 index fb3b6e31b3..5aa4c44061 100644 --- a/src/go/types/testdata/typeinst.go2 +++ b/src/go/types/testdata/typeinst.go2 @@ -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 diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index c5c24e49d9..a9e6c8ef6c 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -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)