mirror of
https://github.com/golang/go
synced 2024-11-02 11:50:30 +00:00
cmd/compile/internal/types2: record all instances, not just inferred instances
This is a port of CL 349629 from go/types to types2, adjusted to make it work for types2. It also includes the necessary compiler changes, provided by mdempsky. Change-Id: If8de174cee9c69df0d0642fcec1ee7622b7c3852 Reviewed-on: https://go-review.googlesource.com/c/go/+/351455 Trust: Robert Griesemer <gri@golang.org> Trust: Dan Scales <danscales@google.com> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Dan Scales <danscales@google.com>
This commit is contained in:
parent
583eeaae50
commit
73418bca34
9 changed files with 207 additions and 174 deletions
|
@ -114,86 +114,27 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
|
|||
|
||||
case *syntax.CallExpr:
|
||||
fun := g.expr(expr.Fun)
|
||||
|
||||
// The key for the Inferred map is the CallExpr (if inferring
|
||||
// types required the function arguments) or the IndexExpr below
|
||||
// (if types could be inferred without the function arguments).
|
||||
if inferred, ok := g.info.Inferred[expr]; ok && inferred.TArgs.Len() > 0 {
|
||||
// This is the case where inferring types required the
|
||||
// types of the function arguments.
|
||||
targs := make([]ir.Node, inferred.TArgs.Len())
|
||||
for i := range targs {
|
||||
targs[i] = ir.TypeNode(g.typ(inferred.TArgs.At(i)))
|
||||
}
|
||||
if fun.Op() == ir.OFUNCINST {
|
||||
if len(fun.(*ir.InstExpr).Targs) < len(targs) {
|
||||
// Replace explicit type args with the full list that
|
||||
// includes the additional inferred type args.
|
||||
// Substitute the type args for the type params in
|
||||
// the generic function's type.
|
||||
fun.(*ir.InstExpr).Targs = targs
|
||||
newt := g.substType(fun.(*ir.InstExpr).X.Type(), fun.(*ir.InstExpr).X.Type().TParams(), targs)
|
||||
typed(newt, fun)
|
||||
}
|
||||
} else {
|
||||
// Create a function instantiation here, given there
|
||||
// are only inferred type args (e.g. min(5,6), where
|
||||
// min is a generic function). Substitute the type
|
||||
// args for the type params in the generic function's
|
||||
// type.
|
||||
inst := ir.NewInstExpr(pos, ir.OFUNCINST, fun, targs)
|
||||
newt := g.substType(fun.Type(), fun.Type().TParams(), targs)
|
||||
typed(newt, inst)
|
||||
fun = inst
|
||||
}
|
||||
|
||||
}
|
||||
return Call(pos, g.typ(typ), fun, g.exprs(expr.ArgList), expr.HasDots)
|
||||
|
||||
case *syntax.IndexExpr:
|
||||
var targs []ir.Node
|
||||
|
||||
if inferred, ok := g.info.Inferred[expr]; ok && inferred.TArgs.Len() > 0 {
|
||||
// This is the partial type inference case where the types
|
||||
// can be inferred from other type arguments without using
|
||||
// the types of the function arguments.
|
||||
targs = make([]ir.Node, inferred.TArgs.Len())
|
||||
for i := range targs {
|
||||
targs[i] = ir.TypeNode(g.typ(inferred.TArgs.At(i)))
|
||||
}
|
||||
} else if _, ok := expr.Index.(*syntax.ListExpr); ok {
|
||||
targs = g.exprList(expr.Index)
|
||||
} else {
|
||||
index := g.expr(expr.Index)
|
||||
if index.Op() != ir.OTYPE {
|
||||
args := unpackListExpr(expr.Index)
|
||||
if len(args) == 1 {
|
||||
tv, ok := g.info.Types[args[0]]
|
||||
assert(ok)
|
||||
if tv.IsValue() {
|
||||
// This is just a normal index expression
|
||||
n := Index(pos, g.typ(typ), g.expr(expr.X), index)
|
||||
n := Index(pos, g.typ(typ), g.expr(expr.X), g.expr(args[0]))
|
||||
if !g.delayTransform() {
|
||||
// transformIndex will modify n.Type() for OINDEXMAP.
|
||||
transformIndex(n)
|
||||
}
|
||||
return n
|
||||
}
|
||||
// This is generic function instantiation with a single type
|
||||
targs = []ir.Node{index}
|
||||
}
|
||||
// This is a generic function instantiation (e.g. min[int]).
|
||||
// Generic type instantiation is handled in the type
|
||||
// section of expr() above (using g.typ).
|
||||
x := g.expr(expr.X)
|
||||
if x.Op() != ir.ONAME || x.Type().Kind() != types.TFUNC {
|
||||
panic("Incorrect argument for generic func instantiation")
|
||||
}
|
||||
n := ir.NewInstExpr(pos, ir.OFUNCINST, x, targs)
|
||||
newt := g.typ(typ)
|
||||
// Substitute the type args for the type params in the uninstantiated
|
||||
// function's type. If there aren't enough type args, then the rest
|
||||
// will be inferred at the call node, so don't try the substitution yet.
|
||||
if x.Type().TParams().NumFields() == len(targs) {
|
||||
newt = g.substType(g.typ(typ), x.Type().TParams(), targs)
|
||||
}
|
||||
typed(newt, n)
|
||||
return n
|
||||
|
||||
// expr.Index is a list of type args, so we ignore it, since types2 has
|
||||
// already provided this info with the Info.Instances map.
|
||||
return g.expr(expr.X)
|
||||
|
||||
case *syntax.SelectorExpr:
|
||||
// Qualified identifier.
|
||||
|
|
|
@ -59,7 +59,7 @@ func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) {
|
|||
Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
|
||||
Implicits: make(map[syntax.Node]types2.Object),
|
||||
Scopes: make(map[syntax.Node]*types2.Scope),
|
||||
Inferred: make(map[syntax.Expr]types2.Inferred),
|
||||
Instances: make(map[*syntax.Name]types2.Instance),
|
||||
// expand as needed
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,10 @@ func (g *irgen) def(name *syntax.Name) (*ir.Name, types2.Object) {
|
|||
return g.obj(obj), obj
|
||||
}
|
||||
|
||||
// use returns the Name node associated with the use of name. The returned node
|
||||
// will have the correct type and be marked as typechecked.
|
||||
func (g *irgen) use(name *syntax.Name) *ir.Name {
|
||||
// use returns the Name or InstExpr node associated with the use of name,
|
||||
// possibly instantiated by type arguments. The returned node will have
|
||||
// the correct type and be marked as typechecked.
|
||||
func (g *irgen) use(name *syntax.Name) ir.Node {
|
||||
obj2, ok := g.info.Uses[name]
|
||||
if !ok {
|
||||
base.FatalfAt(g.pos(name), "unknown name %v", name)
|
||||
|
@ -36,6 +37,20 @@ func (g *irgen) use(name *syntax.Name) *ir.Name {
|
|||
obj.SetTypecheck(1)
|
||||
obj.SetType(obj.Defn.Type())
|
||||
}
|
||||
|
||||
if obj.Class == ir.PFUNC {
|
||||
if inst, ok := g.info.Instances[name]; ok {
|
||||
// This is the case where inferring types required the
|
||||
// types of the function arguments.
|
||||
targs := make([]ir.Node, inst.TypeArgs.Len())
|
||||
for i := range targs {
|
||||
targs[i] = ir.TypeNode(g.typ(inst.TypeArgs.At(i)))
|
||||
}
|
||||
typ := g.substType(obj.Type(), obj.Type().TParams(), targs)
|
||||
return typed(typ, ir.NewInstExpr(g.pos(name), ir.OFUNCINST, obj, targs))
|
||||
}
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
|
|
|
@ -337,7 +337,7 @@ func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
|
|||
w.typ(typ.Elem())
|
||||
|
||||
case *types2.Signature:
|
||||
assert(typ.TypeParams() == nil)
|
||||
base.Assertf(typ.TypeParams() == nil, "unexpected type params: %v", typ)
|
||||
w.code(typeSignature)
|
||||
w.signature(typ)
|
||||
|
||||
|
@ -1158,11 +1158,16 @@ func (w *writer) optLabel(label *syntax.Name) {
|
|||
func (w *writer) expr(expr syntax.Expr) {
|
||||
expr = unparen(expr) // skip parens; unneeded after typecheck
|
||||
|
||||
obj, targs := lookupObj(w.p.info, expr)
|
||||
obj, inst := lookupObj(w.p.info, expr)
|
||||
targs := inst.TypeArgs
|
||||
|
||||
if tv, ok := w.p.info.Types[expr]; ok {
|
||||
// TODO(mdempsky): Be more judicious about which types are marked as "needed".
|
||||
w.needType(tv.Type)
|
||||
if inst.Type != nil {
|
||||
w.needType(inst.Type)
|
||||
} else {
|
||||
w.needType(tv.Type)
|
||||
}
|
||||
|
||||
if tv.IsType() {
|
||||
w.code(exprType)
|
||||
|
@ -1303,16 +1308,7 @@ func (w *writer) expr(expr syntax.Expr) {
|
|||
}
|
||||
}
|
||||
|
||||
if inf, ok := w.p.info.Inferred[expr]; ok {
|
||||
obj, _ := lookupObj(w.p.info, expr.Fun)
|
||||
assert(obj != nil)
|
||||
|
||||
// As if w.expr(expr.Fun), but using inf.TArgs instead.
|
||||
w.code(exprName)
|
||||
w.obj(obj, inf.TArgs)
|
||||
} else {
|
||||
w.expr(expr.Fun)
|
||||
}
|
||||
w.expr(expr.Fun)
|
||||
w.bool(false) // not a method call (i.e., normal function call)
|
||||
}
|
||||
|
||||
|
@ -1756,31 +1752,17 @@ func isGlobal(obj types2.Object) bool {
|
|||
}
|
||||
|
||||
// lookupObj returns the object that expr refers to, if any. If expr
|
||||
// is an explicit instantiation of a generic object, then the type
|
||||
// arguments are returned as well.
|
||||
func lookupObj(info *types2.Info, expr syntax.Expr) (obj types2.Object, targs *types2.TypeList) {
|
||||
// is an explicit instantiation of a generic object, then the instance
|
||||
// object is returned as well.
|
||||
func lookupObj(info *types2.Info, expr syntax.Expr) (obj types2.Object, inst types2.Instance) {
|
||||
if index, ok := expr.(*syntax.IndexExpr); ok {
|
||||
if inf, ok := info.Inferred[index]; ok {
|
||||
targs = inf.TArgs
|
||||
} else {
|
||||
args := unpackListExpr(index.Index)
|
||||
|
||||
if len(args) == 1 {
|
||||
tv, ok := info.Types[args[0]]
|
||||
assert(ok)
|
||||
if tv.IsValue() {
|
||||
return // normal index expression
|
||||
}
|
||||
args := unpackListExpr(index.Index)
|
||||
if len(args) == 1 {
|
||||
tv, ok := info.Types[args[0]]
|
||||
assert(ok)
|
||||
if tv.IsValue() {
|
||||
return // normal index expression
|
||||
}
|
||||
|
||||
list := make([]types2.Type, len(args))
|
||||
for i, arg := range args {
|
||||
tv, ok := info.Types[arg]
|
||||
assert(ok)
|
||||
assert(tv.IsType())
|
||||
list[i] = tv.Type
|
||||
}
|
||||
targs = types2.NewTypeList(list)
|
||||
}
|
||||
|
||||
expr = index.X
|
||||
|
@ -1795,7 +1777,8 @@ func lookupObj(info *types2.Info, expr syntax.Expr) (obj types2.Object, targs *t
|
|||
}
|
||||
|
||||
if name, ok := expr.(*syntax.Name); ok {
|
||||
obj, _ = info.Uses[name]
|
||||
obj = info.Uses[name]
|
||||
inst = info.Instances[name]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -214,11 +214,19 @@ type Info struct {
|
|||
// qualified identifiers are collected in the Uses map.
|
||||
Types map[syntax.Expr]TypeAndValue
|
||||
|
||||
// Inferred maps calls of parameterized functions that use
|
||||
// type inference to the inferred type arguments and signature
|
||||
// of the function called. The recorded "call" expression may be
|
||||
// an *ast.CallExpr (as in f(x)), or an *ast.IndexExpr (s in f[T]).
|
||||
Inferred map[syntax.Expr]Inferred
|
||||
// Instances maps identifiers denoting parameterized types or functions to
|
||||
// their type arguments and instantiated type.
|
||||
//
|
||||
// For example, Instances will map the identifier for 'T' in the type
|
||||
// instantiation T[int, string] to the type arguments [int, string] and
|
||||
// resulting instantiated *Named type. Given a parameterized function
|
||||
// func F[A any](A), Instances will map the identifier for 'F' in the call
|
||||
// expression F(int(1)) to the inferred type arguments [int], and resulting
|
||||
// instantiated *Signature.
|
||||
//
|
||||
// Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs
|
||||
// results in an equivalent of Instances[id].Type.
|
||||
Instances map[*syntax.Name]Instance
|
||||
|
||||
// Defs maps identifiers to the objects they define (including
|
||||
// package names, dots "." of dot-imports, and blank "_" identifiers).
|
||||
|
@ -375,11 +383,13 @@ func (tv TypeAndValue) HasOk() bool {
|
|||
return tv.mode == commaok || tv.mode == mapindex
|
||||
}
|
||||
|
||||
// Inferred reports the inferred type arguments and signature
|
||||
// for a parameterized function call that uses type inference.
|
||||
type Inferred struct {
|
||||
TArgs *TypeList
|
||||
Sig *Signature
|
||||
// Instance reports the type arguments and instantiated type for type and
|
||||
// function instantiations. For type instantiations, Type will be of dynamic
|
||||
// type *Named. For function instantiations, Type will be of dynamic type
|
||||
// *Signature.
|
||||
type Instance struct {
|
||||
TypeArgs *TypeList
|
||||
Type Type
|
||||
}
|
||||
|
||||
// An Initializer describes a package-level variable, or a list of variables in case
|
||||
|
|
|
@ -382,12 +382,12 @@ func TestTypesInfo(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestInferredInfo(t *testing.T) {
|
||||
func TestInstanceInfo(t *testing.T) {
|
||||
var tests = []struct {
|
||||
src string
|
||||
fun string
|
||||
name string
|
||||
targs []string
|
||||
sig string
|
||||
typ string
|
||||
}{
|
||||
{genericPkg + `p0; func f[T any](T) {}; func _() { f(42) }`,
|
||||
`f`,
|
||||
|
@ -417,33 +417,33 @@ func TestInferredInfo(t *testing.T) {
|
|||
|
||||
// we don't know how to translate these but we can type-check them
|
||||
{genericPkg + `q0; type T struct{}; func (T) m[P any](P) {}; func _(x T) { x.m(42) }`,
|
||||
`x.m`,
|
||||
`m`,
|
||||
[]string{`int`},
|
||||
`func(int)`,
|
||||
},
|
||||
{genericPkg + `q1; type T struct{}; func (T) m[P any](P) P { panic(0) }; func _(x T) { x.m(42) }`,
|
||||
`x.m`,
|
||||
`m`,
|
||||
[]string{`int`},
|
||||
`func(int) int`,
|
||||
},
|
||||
{genericPkg + `q2; type T struct{}; func (T) m[P any](...P) P { panic(0) }; func _(x T) { x.m(42) }`,
|
||||
`x.m`,
|
||||
`m`,
|
||||
[]string{`int`},
|
||||
`func(...int) int`,
|
||||
},
|
||||
{genericPkg + `q3; type T struct{}; func (T) m[A, B, C any](A, *B, []C) {}; func _(x T) { x.m(1.2, new(string), []byte{}) }`,
|
||||
`x.m`,
|
||||
`m`,
|
||||
[]string{`float64`, `string`, `byte`},
|
||||
`func(float64, *string, []byte)`,
|
||||
},
|
||||
{genericPkg + `q4; type T struct{}; func (T) m[A, B any](A, *B, ...[]B) {}; func _(x T) { x.m(1.2, new(byte)) }`,
|
||||
`x.m`,
|
||||
`m`,
|
||||
[]string{`float64`, `byte`},
|
||||
`func(float64, *byte, ...[]byte)`,
|
||||
},
|
||||
|
||||
{genericPkg + `r0; type T[P any] struct{}; func (_ T[P]) m[Q any](Q) {}; func _[P any](x T[P]) { x.m(42) }`,
|
||||
`x.m`,
|
||||
`m`,
|
||||
[]string{`int`},
|
||||
`func(int)`,
|
||||
},
|
||||
|
@ -480,66 +480,130 @@ func TestInferredInfo(t *testing.T) {
|
|||
[]string{`string`, `*string`},
|
||||
`func() string`,
|
||||
},
|
||||
{genericPkg + `t2; type C[T any] interface{~chan<- T}; func f[T any, P C[T]]() []T { return nil }; func _() { _ = f[int] }`,
|
||||
{genericPkg + `t2; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`},
|
||||
`func() []int`,
|
||||
[]string{`string`, `*string`},
|
||||
`func() string`,
|
||||
},
|
||||
{genericPkg + `t3; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
|
||||
`func() []int`,
|
||||
},
|
||||
{genericPkg + `t4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
|
||||
`func() []int`,
|
||||
},
|
||||
{genericPkg + `i0; import lib "generic_lib"; func _() { lib.F(42) }`,
|
||||
`F`,
|
||||
[]string{`int`},
|
||||
`func(int)`,
|
||||
},
|
||||
{genericPkg + `type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`struct{x int}`,
|
||||
},
|
||||
{genericPkg + `type1; type T[P interface{~int}] struct{ x P }; var _ (T[int])`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`struct{x int}`,
|
||||
},
|
||||
{genericPkg + `type2; type T[P interface{~int}] struct{ x P }; var _ T[(int)]`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`struct{x int}`,
|
||||
},
|
||||
{genericPkg + `type3; type T[P1 interface{~[]P2}, P2 any] struct{ x P1; y P2 }; var _ T[[]int, int]`,
|
||||
`T`,
|
||||
[]string{`[]int`, `int`},
|
||||
`struct{x []int; y int}`,
|
||||
},
|
||||
{genericPkg + `type4; import lib "generic_lib"; var _ lib.T[int]`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`[]int`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
info := Info{Inferred: make(map[syntax.Expr]Inferred)}
|
||||
name, err := mayTypecheck(t, "InferredInfo", test.src, &info)
|
||||
if err != nil {
|
||||
t.Errorf("package %s: %v", name, err)
|
||||
continue
|
||||
}
|
||||
const lib = `package generic_lib
|
||||
|
||||
// look for inferred type arguments and signature
|
||||
var targs *TypeList
|
||||
var sig *Signature
|
||||
for call, inf := range info.Inferred {
|
||||
var fun syntax.Expr
|
||||
switch x := call.(type) {
|
||||
case *syntax.CallExpr:
|
||||
fun = x.Fun
|
||||
case *syntax.IndexExpr:
|
||||
fun = x.X
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected call expression type %T", call))
|
||||
func F[P any](P) {}
|
||||
|
||||
type T[P any] []P
|
||||
`
|
||||
|
||||
imports := make(testImporter)
|
||||
conf := Config{Importer: imports}
|
||||
instances := make(map[*syntax.Name]Instance)
|
||||
uses := make(map[*syntax.Name]Object)
|
||||
makePkg := func(src string) *Package {
|
||||
f, err := parseSrc("p.go", src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if syntax.String(fun) == test.fun {
|
||||
targs = inf.TArgs
|
||||
sig = inf.Sig
|
||||
pkg, err := conf.Check("", []*syntax.File{f}, &Info{Instances: instances, Uses: uses})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
imports[pkg.Name()] = pkg
|
||||
return pkg
|
||||
}
|
||||
makePkg(lib)
|
||||
pkg := makePkg(test.src)
|
||||
|
||||
// look for instance information
|
||||
var targs []Type
|
||||
var typ Type
|
||||
for ident, inst := range instances {
|
||||
if syntax.String(ident) == test.name {
|
||||
for i := 0; i < inst.TypeArgs.Len(); i++ {
|
||||
targs = append(targs, inst.TypeArgs.At(i))
|
||||
}
|
||||
typ = inst.Type
|
||||
|
||||
// Check that we can find the corresponding parameterized type.
|
||||
ptype := uses[ident].Type()
|
||||
lister, _ := ptype.(interface{ TypeParams() *TypeParamList })
|
||||
if lister == nil || lister.TypeParams().Len() == 0 {
|
||||
t.Errorf("package %s: info.Types[%v] = %v, want parameterized type", pkg.Name(), ident, ptype)
|
||||
continue
|
||||
}
|
||||
|
||||
// Verify the invariant that re-instantiating the generic type with
|
||||
// TypeArgs results in an equivalent type.
|
||||
inst2, err := Instantiate(nil, ptype, targs, true)
|
||||
if err != nil {
|
||||
t.Errorf("Instantiate(%v, %v) failed: %v", ptype, targs, err)
|
||||
}
|
||||
if !Identical(inst.Type, inst2) {
|
||||
t.Errorf("%v and %v are not identical", inst.Type, inst2)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if targs == nil {
|
||||
t.Errorf("package %s: no inferred information found for %s", name, test.fun)
|
||||
t.Errorf("package %s: no instance information found for %s", pkg.Name(), test.name)
|
||||
continue
|
||||
}
|
||||
|
||||
// check that type arguments are correct
|
||||
if targs.Len() != len(test.targs) {
|
||||
t.Errorf("package %s: got %d type arguments; want %d", name, targs.Len(), len(test.targs))
|
||||
if len(targs) != len(test.targs) {
|
||||
t.Errorf("package %s: got %d type arguments; want %d", pkg.Name(), len(targs), len(test.targs))
|
||||
continue
|
||||
}
|
||||
for i := 0; i < targs.Len(); i++ {
|
||||
targ := targs.At(i)
|
||||
for i, targ := range targs {
|
||||
if got := targ.String(); got != test.targs[i] {
|
||||
t.Errorf("package %s, %d. type argument: got %s; want %s", name, i, got, test.targs[i])
|
||||
t.Errorf("package %s, %d. type argument: got %s; want %s", pkg.Name(), i, got, test.targs[i])
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// check that signature is correct
|
||||
if got := sig.String(); got != test.sig {
|
||||
t.Errorf("package %s: got %s; want %s", name, got, test.sig)
|
||||
// check that the types match
|
||||
if got := typ.Underlying().String(); got != test.typ {
|
||||
t.Errorf("package %s: got %s; want %s", pkg.Name(), got, test.typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,6 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
|
|||
return
|
||||
}
|
||||
|
||||
// if we don't have enough type arguments, try type inference
|
||||
inferred := false
|
||||
if got < want {
|
||||
targs = check.infer(inst.Pos(), sig.TypeParams().list(), targs, nil, nil)
|
||||
if targs == nil {
|
||||
|
@ -49,7 +47,6 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
|
|||
return
|
||||
}
|
||||
got = len(targs)
|
||||
inferred = true
|
||||
}
|
||||
assert(got == want)
|
||||
|
||||
|
@ -62,9 +59,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
|
|||
// instantiate function signature
|
||||
res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
|
||||
assert(res.TypeParams().Len() == 0) // signature is not generic anymore
|
||||
if inferred {
|
||||
check.recordInferred(inst, targs, res)
|
||||
}
|
||||
check.recordInstance(inst.X, targs, res)
|
||||
x.typ = res
|
||||
x.mode = value
|
||||
x.expr = inst
|
||||
|
@ -346,7 +341,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
|
|||
// compute result signature
|
||||
rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
|
||||
assert(rsig.TypeParams().Len() == 0) // signature is not generic anymore
|
||||
check.recordInferred(call, targs, rsig)
|
||||
check.recordInstance(call.Fun, targs, rsig)
|
||||
|
||||
// Optimization: Only if the parameter list was adjusted do we
|
||||
// need to compute it from the adjusted list; otherwise we can
|
||||
|
|
|
@ -415,14 +415,38 @@ func (check *Checker) recordCommaOkTypes(x syntax.Expr, a [2]Type) {
|
|||
}
|
||||
}
|
||||
|
||||
func (check *Checker) recordInferred(call syntax.Expr, targs []Type, sig *Signature) {
|
||||
assert(call != nil)
|
||||
assert(sig != nil)
|
||||
if m := check.Inferred; m != nil {
|
||||
m[call] = Inferred{NewTypeList(targs), sig}
|
||||
// recordInstance records instantiation information into check.Info, if the
|
||||
// Instances map is non-nil. The given expr must be an ident, selector, or
|
||||
// index (list) expr with ident or selector operand.
|
||||
//
|
||||
// TODO(rfindley): the expr parameter is fragile. See if we can access the
|
||||
// instantiated identifier in some other way.
|
||||
func (check *Checker) recordInstance(expr syntax.Expr, targs []Type, typ Type) {
|
||||
ident := instantiatedIdent(expr)
|
||||
assert(ident != nil)
|
||||
assert(typ != nil)
|
||||
if m := check.Instances; m != nil {
|
||||
m[ident] = Instance{NewTypeList(targs), typ}
|
||||
}
|
||||
}
|
||||
|
||||
func instantiatedIdent(expr syntax.Expr) *syntax.Name {
|
||||
var selOrIdent syntax.Expr
|
||||
switch e := expr.(type) {
|
||||
case *syntax.IndexExpr:
|
||||
selOrIdent = e.X
|
||||
case *syntax.SelectorExpr, *syntax.Name:
|
||||
selOrIdent = e
|
||||
}
|
||||
switch x := selOrIdent.(type) {
|
||||
case *syntax.Name:
|
||||
return x
|
||||
case *syntax.SelectorExpr:
|
||||
return x.Sel
|
||||
}
|
||||
panic("instantiated ident not found")
|
||||
}
|
||||
|
||||
func (check *Checker) recordDef(id *syntax.Name, obj Object) {
|
||||
assert(id != nil)
|
||||
if m := check.Defs; m != nil {
|
||||
|
|
|
@ -408,6 +408,7 @@ func (check *Checker) instantiatedType(x syntax.Expr, targsx []syntax.Expr, def
|
|||
|
||||
typ := check.instantiate(x.Pos(), base, targs, posList)
|
||||
def.setUnderlying(typ)
|
||||
check.recordInstance(x, targs, typ)
|
||||
|
||||
// make sure we check instantiation works at least once
|
||||
// and that the resulting type is valid
|
||||
|
|
Loading…
Reference in a new issue