diff --git a/src/go/types/api.go b/src/go/types/api.go index 4283fb9c98..fb585fdd0c 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -171,10 +171,10 @@ type Info struct { // qualified identifiers are collected in the Uses map. Types map[ast.Expr]TypeAndValue - // Inferred maps calls of parameterized functions which use - // type inferrence to the inferred signature of the function - // called. - Inferred map[*ast.CallExpr]*Signature + // Inferred maps calls of parameterized functions that use + // type inferrence to the inferred type arguments and signature + // of the function called. + Inferred map[*ast.CallExpr]Inferred // Defs maps identifiers to the objects they define (including // package names, dots "." of dot-imports, and blank "_" identifiers). @@ -332,6 +332,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 []Type + Sig *Signature +} + // An Initializer describes a package-level variable, or a list of variables in case // of a multi-valued initialization expression, and the corresponding initialization // expression. diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index 5f7095b0c9..170f819522 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -308,48 +308,111 @@ func TestTypesInfo(t *testing.T) { func TestInferredInfo(t *testing.T) { var tests = []struct { - src string - fun string - sig string + src string + fun string + targs []string + sig string }{ - {`package p0; func f(type T)(T); func _() { f(42) }`, `f`, `func(int)`}, - {`package p1; func f(type T)(T) T; func _() { f('@') }`, `f`, `func(rune) rune`}, - {`package p2; func f(type T)(...T) T; func _() { f(0i) }`, `f`, `func(...complex128) complex128`}, - {`package p3; func f(type A, B, C)(A, *B, []C); func _() { f(1.2, new(string), []byte{}) }`, `f`, `func(float64, *string, []byte)`}, - {`package p4; func f(type A, B)(A, *B, ...[]B); func _() { f(1.2, new(byte)) }`, `f`, `func(float64, *byte, ...[]byte)`}, + {`package p0; func f(type T)(T); func _() { f(42) }`, + `f`, + []string{`int`}, + `func(int)`, + }, + {`package p1; func f(type T)(T) T; func _() { f('@') }`, + `f`, + []string{`rune`}, + `func(rune) rune`, + }, + {`package p2; func f(type T)(...T) T; func _() { f(0i) }`, + `f`, + []string{`complex128`}, + `func(...complex128) complex128`, + }, + {`package p3; func f(type A, B, C)(A, *B, []C); func _() { f(1.2, new(string), []byte{}) }`, + `f`, + []string{`float64`, `string`, `byte`}, + `func(float64, *string, []byte)`, + }, + {`package p4; func f(type A, B)(A, *B, ...[]B); func _() { f(1.2, new(byte)) }`, + `f`, + []string{`float64`, `byte`}, + `func(float64, *byte, ...[]byte)`, + }, // we don't know how to translate these but we can type-check them - {`package q0; type T struct{}; func (T) m(type P)(P); func _(x T) { x.m(42) }`, `x.m`, `func(int)`}, - {`package q1; type T struct{}; func (T) m(type P)(P) P; func _(x T) { x.m(42) }`, `x.m`, `func(int) int`}, - {`package q2; type T struct{}; func (T) m(type P)(...P) P; func _(x T) { x.m(42) }`, `x.m`, `func(...int) int`}, - {`package q3; type T struct{}; func (T) m(type A, B, C)(A, *B, []C); func _(x T) { x.m(1.2, new(string), []byte{}) }`, `x.m`, `func(float64, *string, []byte)`}, - {`package q4; type T struct{}; func (T) m(type A, B)(A, *B, ...[]B); func _(x T) { x.m(1.2, new(byte)) }`, `x.m`, `func(float64, *byte, ...[]byte)`}, + {`package q0; type T struct{}; func (T) m(type P)(P); func _(x T) { x.m(42) }`, + `x.m`, + []string{`int`}, + `func(int)`, + }, + {`package q1; type T struct{}; func (T) m(type P)(P) P; func _(x T) { x.m(42) }`, + `x.m`, + []string{`int`}, + `func(int) int`, + }, + {`package q2; type T struct{}; func (T) m(type P)(...P) P; func _(x T) { x.m(42) }`, + `x.m`, + []string{`int`}, + `func(...int) int`, + }, + {`package q3; type T struct{}; func (T) m(type A, B, C)(A, *B, []C); func _(x T) { x.m(1.2, new(string), []byte{}) }`, + `x.m`, + []string{`float64`, `string`, `byte`}, + `func(float64, *string, []byte)`, + }, + {`package q4; type T struct{}; func (T) m(type A, B)(A, *B, ...[]B); func _(x T) { x.m(1.2, new(byte)) }`, + `x.m`, + []string{`float64`, `byte`}, + `func(float64, *byte, ...[]byte)`, + }, - {`package r0; type T(type P) struct{}; func (_ T(P)) m(type Q)(Q); func _(type P)(x T(P)) { x.m(42) }`, `x.m`, `func(int)`}, - {`package r1; type T interface{ m(type P)(P) }; func _(x T) { x.m(4.2) }`, `x.m`, `func(float64)`}, + {`package r0; type T(type P) struct{}; func (_ T(P)) m(type Q)(Q); func _(type P)(x T(P)) { x.m(42) }`, + `x.m`, + []string{`int`}, + `func(int)`, + }, + {`package r1; type T interface{ m(type P)(P) }; func _(x T) { x.m(4.2) }`, + `x.m`, + []string{`float64`}, + `func(float64)`, + }, } for _, test := range tests { - info := Info{Inferred: make(map[*ast.CallExpr]*Signature)} + info := Info{Inferred: make(map[*ast.CallExpr]Inferred)} name, err := mayTypecheck(t, "InferredInfo", test.src, &info) if err != nil { t.Errorf("package %s: %v", name, err) continue } - // look for inferred signature + // look for inferred type arguments and signature + var targs []Type var sig *Signature - for call, typ := range info.Inferred { + for call, inf := range info.Inferred { if ExprString(call.Fun) == test.fun { - sig = typ + targs = inf.Targs + sig = inf.Sig break } } - if sig == nil { - t.Errorf("package %s: no signature found for %s", name, test.fun) + if targs == nil { + t.Errorf("package %s: no inferred information found for %s", name, test.fun) continue } + // check that type arguments are correct + if len(targs) != len(test.targs) { + t.Errorf("package %s: got %d type arguments; want %d", name, len(targs), len(test.targs)) + continue + } + 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]) + 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) diff --git a/src/go/types/call.go b/src/go/types/call.go index 808855e9f8..0b76233b1a 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -324,7 +324,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, args []*oper // compute result signature rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature) assert(rsig.tparams == nil) // signature is not generic anymore - check.recordInferred(call, rsig) + check.recordInferred(call, targs, rsig) // Optimization: Only if the parameter list was adjusted do we // need to compute it from the adjusted list; otherwise we can diff --git a/src/go/types/check.go b/src/go/types/check.go index 2fc96bf3dd..f68476bd9c 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -396,11 +396,11 @@ func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) { } } -func (check *Checker) recordInferred(call *ast.CallExpr, sig *Signature) { +func (check *Checker) recordInferred(call *ast.CallExpr, targs []Type, sig *Signature) { assert(call != nil) assert(sig != nil) if m := check.Inferred; m != nil { - m[call] = sig + m[call] = Inferred{targs, sig} } }