[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 <gri@golang.org>
This commit is contained in:
Robert Griesemer 2020-07-01 13:26:41 -07:00
parent 6cf6bf162c
commit d014fca6d4
4 changed files with 69 additions and 5 deletions

View File

@ -136,6 +136,7 @@ var tests = [][]string{
{"fixedbugs/issue39768.go2"},
{"fixedbugs/issue39938.go2"},
{"fixedbugs/issue39948.go2"},
{"fixedbugs/issue39976.go2"},
}
var fset = token.NewFileSet()

View File

@ -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)
}

View File

@ -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

View File

@ -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}
}