From d014fca6d4924b15f0eccb3c8ca6a7f968d88f40 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 1 Jul 2020 13:26:41 -0700 Subject: [PATCH] [dev.go2go] go/types: fix assertion failure, better error message for inference failure Fixes #39976. Change-Id: Idc304a615193847337438e7024e43140472e7bae Reviewed-on: https://go-review.googlesource.com/c/go/+/240718 Reviewed-by: Robert Griesemer --- src/go/types/check_test.go | 1 + src/go/types/fixedbugs/issue39976.go2 | 16 +++++++++ src/go/types/infer.go | 50 +++++++++++++++++++++++++-- src/go/types/subst.go | 7 ++-- 4 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 src/go/types/fixedbugs/issue39976.go2 diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index 6e3004528d..d750cbdaa8 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -136,6 +136,7 @@ var tests = [][]string{ {"fixedbugs/issue39768.go2"}, {"fixedbugs/issue39938.go2"}, {"fixedbugs/issue39948.go2"}, + {"fixedbugs/issue39976.go2"}, } var fset = token.NewFileSet() diff --git a/src/go/types/fixedbugs/issue39976.go2 b/src/go/types/fixedbugs/issue39976.go2 new file mode 100644 index 0000000000..2413ebfbae --- /dev/null +++ b/src/go/types/fixedbugs/issue39976.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 + +type policy(type K, V) interface{} +type LRU(type K, V) struct{} + +func NewCache(type K, V)(p policy(K, V)) + +func _() { + var lru LRU(int, string) + NewCache(int, string)(&lru) + NewCache(& /* ERROR does not match policy\(K, V\) \(cannot infer K and V\) */ lru) +} diff --git a/src/go/types/infer.go b/src/go/types/infer.go index d187241c0d..1f5abc377f 100644 --- a/src/go/types/infer.go +++ b/src/go/types/infer.go @@ -7,7 +7,10 @@ package types -import "go/token" +import ( + "go/token" + "strings" +) // 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 params. If infer fails to @@ -20,7 +23,23 @@ 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 - targs, _ := u.x.types() + targs, failed := u.x.types() + if failed == 0 { + // The first type parameter couldn't be inferred. + // If none of them could be inferred, don't try + // to provide the inferred type in the error msg. + allFailed := true + for _, targ := range targs { + if targ != nil { + allFailed = false + break + } + } + if allFailed { + check.errorf(arg.pos(), "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeNamesString(tparams)) + return + } + } smap := makeSubstMap(tparams, targs) inferred := check.subst(arg.pos(), tpar, smap) if inferred != tpar { @@ -108,6 +127,33 @@ func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, a return targs } +// typeNamesString produces a string containing all the +// type names in list suitable for human consumption. +func typeNamesString(list []*TypeName) string { + // common cases + n := len(list) + switch n { + case 0: + return "" + case 1: + return list[0].name + case 2: + return list[0].name + " and " + list[1].name + } + + // general case (n > 2) + var b strings.Builder + for i, tname := range list[:n-1] { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(tname.name) + } + b.WriteString(", and ") + b.WriteString(list[n-1].name) + return b.String() +} + // 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 diff --git a/src/go/types/subst.go b/src/go/types/subst.go index 47622b7c39..587e35ba2b 100644 --- a/src/go/types/subst.go +++ b/src/go/types/subst.go @@ -22,6 +22,8 @@ type substMap struct { proj map[*TypeParam]Type } +// makeSubstMap creates a new substitution map mapping tpars[i] to targs[i]. +// If targs[i] is nil, tpars[i] is not substituted. func makeSubstMap(tpars []*TypeName, targs []Type) *substMap { assert(len(tpars) == len(targs)) proj := make(map[*TypeParam]Type, len(tpars)) @@ -29,10 +31,9 @@ func makeSubstMap(tpars []*TypeName, targs []Type) *substMap { // We must expand type arguments otherwise *Instance // types end up as components in composite types. // TODO(gri) explain why this causes problems, if it does - targ := expand(targs[i]) + targ := expand(targs[i]) // possibly nil targs[i] = targ - assert(targ != nil) - proj[tpar.typ.(*TypeParam)] = targs[i] + proj[tpar.typ.(*TypeParam)] = targ } return &substMap{targs, proj} }