diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index 2bb2e13090..6e3004528d 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -126,10 +126,11 @@ var tests = [][]string{ // TODO(gri) Eliminate the need to enumerate these tests here. // Should just traverse that directory. {"fixedbugs/issue39664.go2"}, - {"fixedbugs/issue39693.go2"}, {"fixedbugs/issue39680.go2"}, + {"fixedbugs/issue39693.go2"}, {"fixedbugs/issue39711.go2"}, {"fixedbugs/issue39723.go2"}, + {"fixedbugs/issue39725.go2"}, {"fixedbugs/issue39754.go2"}, {"fixedbugs/issue39755.go2"}, {"fixedbugs/issue39768.go2"}, diff --git a/src/go/types/fixedbugs/issue39725.go2 b/src/go/types/fixedbugs/issue39725.go2 new file mode 100644 index 0000000000..320dcd3bd9 --- /dev/null +++ b/src/go/types/fixedbugs/issue39725.go2 @@ -0,0 +1,16 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +func f1(type T1, T2)(T1, T2, struct{a T1; b T2}) +func _() { + f1(42, string("foo"), struct /* ERROR does not match inferred type struct\{a int; b string\} */ {a, b int}{}) +} + +// simplified test case from issue +func f2(type T)(_ []T, _ func(T)) +func _() { + f2([]string{}, func /* ERROR does not match inferred type func\(string\) */ (f []byte) {}) +} diff --git a/src/go/types/infer.go b/src/go/types/infer.go index c30b63c9fd..d187241c0d 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -20,13 +20,14 @@ func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, a errorf := func(kind string, tpar, targ Type, arg *operand) { // provide a better error message if we can - if tpar, _ := tpar.(*TypeParam); tpar != nil { - if inferred := u.x.at(tpar.index); inferred != nil { - check.errorf(arg.pos(), "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar) - return - } + targs, _ := u.x.types() + smap := makeSubstMap(tparams, targs) + inferred := check.subst(arg.pos(), tpar, smap) + if inferred != tpar { + check.errorf(arg.pos(), "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar) + } else { + check.errorf(arg.pos(), "%s %s of %s does not match %s", kind, targ, arg.expr, tpar) } - check.errorf(arg.pos(), "%s %s of %s does not match %s", kind, targ, arg.expr, tpar) } // Terminology: generic parameter = function parameter with a type-parameterized type @@ -96,24 +97,22 @@ func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, a // Collect type arguments and check if they all have been determined. // TODO(gri) consider moving this outside this function and then we won't need to pass in pos - var targs []Type // lazily allocated - for i, tpar := range tparams { - targ := u.x.at(i) - if targ == nil { - ppos := check.fset.Position(tpar.pos).String() - check.errorf(pos, "cannot infer %s (%s)", tpar.name, ppos) - return nil - } - if targs == nil { - targs = make([]Type, len(tparams)) - } - targs[i] = targ + targs, failed := u.x.types() + if failed >= 0 { + tpar := tparams[failed] + ppos := check.fset.Position(tpar.pos).String() + check.errorf(pos, "cannot infer %s (%s)", tpar.name, ppos) + return nil } return targs } // IsParameterized reports whether typ contains any type parameters. +// TODO(gri) This is not strictly correct. We only want the free +// type parameters for a given type. (At the moment, the only way +// to mix free and bound type parameters is through method type parameters +// on parameterized receiver types - need to investigate.) func IsParameterized(typ Type) bool { return isParameterized(typ, make(map[Type]bool)) } diff --git a/src/go/types/unify.go b/src/go/types/unify.go index 1598c3dc65..9115faa025 100644 --- a/src/go/types/unify.go +++ b/src/go/types/unify.go @@ -64,6 +64,23 @@ func (d *typeDesc) at(i int) Type { return nil } +// types returns the list of inferred types (via unification) for the type parameters +// described by d, and an index. If all types were inferred, the returned index is < 0. +// Otherwise, it is the index of the first type parameter which couldn't be inferred +// and for which list[index] is nil. +func (d *typeDesc) types() (list []Type, index int) { + list = make([]Type, len(d.tparams)) + index = -1 + for i := range d.tparams { + t := d.at(i) + list[i] = t + if index < 0 && t == nil { + index = i + } + } + return +} + // set sets the type typ inferred (via unification) for the i'th type parameter; typ must not be nil. // The index i must be a valid type parameter index: 0 <= i < len(d.tparams). func (d *typeDesc) set(i int, typ Type) {