go/types: correctly substitute types when instantiating parametrized types

- use the right type when instantiating a type
- substitute defined types when underlying types are changing
- improved printing of parametrized types
- improved printing of instantiated types
- more top-level trace output

Change-Id: Ie8a65c9cc51e80925d3f580f54dcc6c0b5abe4c6
This commit is contained in:
Robert Griesemer 2019-07-03 22:45:00 -07:00
parent fac6c330ac
commit 8142528bfd
7 changed files with 86 additions and 15 deletions

View File

@ -161,6 +161,7 @@ func (check *Checker) exprOrTypeList(elist []ast.Expr) (xlist []*operand, ok boo
}
func (check *Checker) instantiate(typ Type, tparams []*TypeName, args []*operand) Type {
assert(typ != nil)
n := len(args)
if n != len(tparams) {
check.errorf(args[n-1].pos(), "got %d type arguments but want %d", n, len(tparams))
@ -176,7 +177,7 @@ func (check *Checker) instantiate(typ Type, tparams []*TypeName, args []*operand
targs[i] = a.typ
}
// result is instantiated typ
return subst(typ, targs)
return check.subst(typ, targs)
}
func (check *Checker) exprList(elist []ast.Expr, allowCommaOk bool) (xlist []*operand, commaOk bool) {
@ -301,8 +302,8 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, args []*oper
if targs == nil {
return
}
rsig = subst(sig, targs).(*Signature)
params = subst(params, targs).(*Tuple)
rsig = check.subst(sig, targs).(*Signature)
params = check.subst(params, targs).(*Tuple)
// TODO(gri) Optimization: We don't need to check arguments
// from which we inferred parameter types.
}

View File

@ -8,6 +8,7 @@ package types
import (
"errors"
"fmt"
"go/ast"
"go/constant"
"go/token"
@ -257,21 +258,34 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
defer check.handleBailout(&err)
print := func(msg string) {
if trace {
fmt.Println(msg)
}
}
print("== initFiles ==")
check.initFiles(files)
print("== collectObjects ==")
check.collectObjects()
print("== packagetObjects ==")
check.packageObjects()
print("== processDelayed ==")
check.processDelayed(0) // incl. all functions
check.processFinals()
print("== initOrder ==")
check.initOrder()
if !check.conf.DisableUnusedImportCheck {
print("== unusedImports ==")
check.unusedImports()
}
print("== recordUntyped ==")
check.recordUntyped()
check.pkg.complete = true

View File

@ -39,7 +39,7 @@ func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, a
}
par := params.At(i)
if !check.identical0(par.typ, arg.typ, true, nil, targs) {
check.errorf(arg.pos(), "type %s for %s does not match %s = %s", arg.typ, arg.expr, par.typ, subst(par.typ, targs))
check.errorf(arg.pos(), "type %s for %s does not match %s = %s", arg.typ, arg.expr, par.typ, check.subst(par.typ, targs))
return nil
}
}
@ -52,7 +52,7 @@ func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, a
}
par := params.At(i)
if !check.identical0(par.typ, Default(arg.typ), true, nil, targs) {
check.errorf(arg.pos(), "default type %s for %s does not match %s = %s", Default(arg.typ), arg.expr, par.typ, subst(par.typ, targs))
check.errorf(arg.pos(), "default type %s for %s does not match %s = %s", Default(arg.typ), arg.expr, par.typ, check.subst(par.typ, targs))
return nil
}
}

View File

@ -426,6 +426,16 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
if _, ok := typ.(*Basic); ok {
return
}
if tname.IsParametrized() {
fmt.Fprint(buf, "(type ")
for i, p := range tname.tparams {
if i > 0 {
fmt.Fprint(buf, ", ")
}
fmt.Fprintf(buf, "%s", p.name)
}
fmt.Fprint(buf, ")")
}
if tname.IsAlias() {
buf.WriteString(" =")
} else {

View File

@ -8,15 +8,20 @@
package types
func subst(typ Type, targs []Type) Type {
import (
"bytes"
)
func (check *Checker) subst(typ Type, targs []Type) Type {
if len(targs) == 0 {
return typ
}
s := subster{targs, make(map[Type]Type)}
s := subster{check, targs, make(map[Type]Type)}
return s.typ(typ)
}
type subster struct {
check *Checker
targs []Type
cache map[Type]Type
}
@ -90,8 +95,17 @@ func (s *subster) typ(typ Type) (res Type) {
}
case *Named:
// TODO(gri) is this correct?
// nothing to do
underlying := s.typ(t.underlying)
if underlying != t.underlying {
// create a new named type - for now use printed type in name
// TODO(gri) use type map to map types to indices
if len(t.methods) > 0 {
panic("cannot handle instantiation of types with methods yet")
}
// TODO(gri) what is the correct position to use here?
obj := NewTypeName(t.obj.pos, s.check.pkg, t.obj.name+typesString(s.targs), nil)
return NewNamed(obj, underlying, nil) // TODO(gri) provide correct method list
}
case *TypeParam:
if targ := s.targs[t.index]; targ != nil {
@ -145,3 +159,16 @@ func (s *subster) varList(in []*Var) (out []*Var, copied bool) {
}
return
}
func typesString(targs []Type) string {
var buf bytes.Buffer
buf.WriteByte('(')
for i, arg := range targs {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(TypeString(arg, nil))
}
buf.WriteByte(')')
return buf.String()
}

View File

@ -20,6 +20,11 @@ type List(type P) []P
type A1(type P) = P
type A2(type P) = struct {
f P
g int
}
type A3(type P) = struct {
f P
g myInt // myInt should still be in scope chain
}
@ -37,8 +42,14 @@ 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
var _ A1(int) = x
var _ A1(float32) = x // ERROR cannot use x .* as float32
var _ T2(*int) = A2(*int){}
var _ T2(*int) = A2 /* ERROR cannot use */ (int){}
// TODO(gri) cannot handle this yet due because instantiated types are not yet canonicalized
//var _ T2(int) = T2(int){}
// TODO
// think about instantiated types (they are defined types unless alias)

View File

@ -262,7 +262,7 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
// 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.
// instantiation expressions.
// e.Fun must be a type name
var tname *TypeName
@ -290,6 +290,10 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
break
}
// typecheck tname (see check.ident for details)
check.objDecl(tname, def)
assert(tname.typ != nil)
// 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) {
@ -312,12 +316,16 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type {
}
// 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
typ := check.instantiate(tname.typ, tname.tparams, args)
if typ == nil {
break // error was reported by check.instatiate
}
if trace {
check.trace(args[0].pos(), "instantiated %s -> %s", tname, typ)
}
return typ
case *ast.ParenExpr:
return check.definedType(e.X, def)