mirror of https://github.com/golang/go.git
go/types: fix type inference bugs for untyped arguments
Change-Id: Ic98be8b102521090108d288514ee2cf016a7bb46
This commit is contained in:
parent
d03ba9f74c
commit
a65d9b58a9
|
|
@ -10,54 +10,74 @@ package types
|
|||
import "go/token"
|
||||
|
||||
// infer returns the list of actual type arguments for the given list of type parameters tparams
|
||||
// by inferring them from the actual arguments args for the parameters pars. If infer fails to
|
||||
// by inferring them from the actual arguments args for the parameters params. If infer fails to
|
||||
// determine all type arguments, an error is reported and the result is nil.
|
||||
func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, args []*operand) []Type {
|
||||
assert(params.Len() == len(args))
|
||||
|
||||
// targs is the list of inferred type parameter types.
|
||||
targs := make([]Type, len(tparams))
|
||||
|
||||
// determine indices of type-parametrized parameters
|
||||
// Terminology: TPP = type-parameterized function parameter
|
||||
|
||||
// 1st pass: Unify parameter and argument types for TPPs with typed arguments
|
||||
// and collect the indices of TPPs with untyped arguments.
|
||||
var indices []int
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
par := params.At(i).typ
|
||||
if isParameterized(par) {
|
||||
indices = append(indices, i)
|
||||
for i, arg := range args {
|
||||
par := params.At(i)
|
||||
if isParameterized(par.typ) {
|
||||
if arg.mode == invalid {
|
||||
// TODO(gri) we might still be able to infer all targs by
|
||||
// simply ignoring (continue) invalid args
|
||||
return nil // error was reported earlier
|
||||
}
|
||||
if isTyped(arg.typ) {
|
||||
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, check.subst(par.typ, tparams, targs),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
indices = append(indices, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1st pass: unify parameter and argument types for typed arguments
|
||||
// Some of the TPPs with untyped arguments may have been given a type
|
||||
// indirectly via a TPP with a typed argument; we can ignore those now.
|
||||
j := 0
|
||||
for _, i := range indices {
|
||||
arg := args[i]
|
||||
if arg.mode == invalid {
|
||||
// TODO(gri) we might still be able to infer all targs by
|
||||
// simply ignoring (continue) invalid args
|
||||
return nil // error was reported earlier
|
||||
}
|
||||
if isUntyped(arg.typ) {
|
||||
continue // handled in 2nd pass
|
||||
}
|
||||
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, check.subst(par.typ, tparams, targs))
|
||||
// Since untyped types are all basic (i.e., unstructured) types, an
|
||||
// untyped argument will never match a structured parameter type; the
|
||||
// only parameter type it can possibly match against is a *TypeParam.
|
||||
// Thus, only keep the indices of TPPs that are unstructured and which
|
||||
// don't have a type inferred yet.
|
||||
if tpar, _ := par.typ.(*TypeParam); tpar != nil && targs[tpar.index] == nil {
|
||||
indices[j] = i
|
||||
j++
|
||||
}
|
||||
}
|
||||
indices = indices[:j]
|
||||
|
||||
// 2nd pass: Unify parameter and default argument types for remaining TPPs.
|
||||
for _, i := range indices {
|
||||
par := params.At(i)
|
||||
arg := args[i]
|
||||
targ := Default(arg.typ)
|
||||
// The default type for an untyped nil is untyped nil. We must not
|
||||
// infer an untyped nil type as type parameter type. Ignore untyped
|
||||
// nil by making sure all default argument types are typed.
|
||||
if isTyped(targ) && !check.identical0(par.typ, targ, true, nil, 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, tparams, targs),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 2nd pass: unify parameter and default argument types for remaining parametrized parameter types with untyped arguments
|
||||
for _, i := range indices {
|
||||
arg := args[i]
|
||||
if isTyped(arg.typ) {
|
||||
continue // handled in 1st pass
|
||||
}
|
||||
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, check.subst(par.typ, tparams, targs))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// check if all type parameters have been determined
|
||||
// Check if all type parameters have been determined.
|
||||
// TODO(gri) consider moving this outside this function and then we won't need to pass in pos
|
||||
for i, t := range targs {
|
||||
if t == nil {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ func reducer(x float64, y int) float64 {
|
|||
}
|
||||
|
||||
var reduced1 = Reduce(int, float64)(input, 0, reducer)
|
||||
var reduced2 = Reduce(input, 1.0, reducer) // using type inference
|
||||
var reduced2 = Reduce(input, 1i /* ERROR overflows */, reducer) // using type inference
|
||||
var reduced2 = Reduce(input, 1, reducer) // using type inference
|
||||
|
||||
func filter(x int) bool {
|
||||
return x&1 != 0
|
||||
|
|
|
|||
|
|
@ -1,18 +1,13 @@
|
|||
package p
|
||||
|
||||
func Reduce(type T1, T2)(s []T1, initializer T2, f func(T2, T1) T2) T2 {
|
||||
r := initializer
|
||||
for _, v := range s {
|
||||
r = f(r, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
func f(type T)([]*T) int
|
||||
|
||||
func reducer(float64, int) float64 {
|
||||
return 0
|
||||
}
|
||||
var _ = f(nil) // ERROR cannot infer
|
||||
|
||||
var _ = Reduce([]int{}, 1.0, reducer)
|
||||
func g(type T)(T) int
|
||||
|
||||
// TODO(gri) investigate - seems like type inference should accept 1 here
|
||||
// var _ = Reduce([]int{}, 1, reducer)
|
||||
var _ = g(nil) // ERROR cannot infer
|
||||
|
||||
func h(type T)(T, T) int
|
||||
|
||||
var _ = h(nil /* ERROR cannot convert */, uintptr(1))
|
||||
|
|
|
|||
|
|
@ -82,12 +82,16 @@ var _ = f4(int, float32, complex128)(1, 2)
|
|||
func f5(type A, B, C)(A, []*B, struct{f []C}) int
|
||||
|
||||
var _ = f5(int, float32, complex128)(0, nil, struct{f []complex128}{})
|
||||
var _ = f5(0, nil /* ERROR untyped nil */, struct{f []complex128}{}) // <<<< TODO(gri) need better error message
|
||||
var _ = f5(0, nil, struct{f []complex128}{}) // ERROR cannot infer
|
||||
var _ = f5(0, []*float32{new(float32)()}, struct{f []complex128}{})
|
||||
|
||||
func f6(type A)(A, []A) int
|
||||
|
||||
var _ = f6(0, nil /* ERROR does not match */ ) // <<<< TODO(gri) should this be accepted?
|
||||
var _ = f6(0, nil)
|
||||
|
||||
func f6nil(type A)(A) int
|
||||
|
||||
var _ = f6nil(nil) // ERROR cannot infer
|
||||
|
||||
// type inference with variadic functions
|
||||
|
||||
|
|
@ -99,6 +103,8 @@ var _ int = f7(1, 2)
|
|||
var _ int = f7([]int{}...)
|
||||
var _ int = f7 /* ERROR cannot use */ ([]float64{}...)
|
||||
var _ float64 = f7([]float64{}...)
|
||||
var _ = f7(float64)(1, 2.3)
|
||||
var _ = f7(float64(1), 2.3)
|
||||
var _ = f7(1, 2.3 /* ERROR does not match */ )
|
||||
var _ = f7(1.2, 3 /* ERROR does not match */ )
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue