mirror of https://github.com/golang/go.git
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:
parent
fac6c330ac
commit
8142528bfd
|
|
@ -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.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue