0
1
mirror of https://github.com/golang/go synced 2025-05-30 15:30:51 +00:00

go/types, types2: better error messages for calls

Provide the exact error cause instead of reporting a missing
core type.

For .

Change-Id: I34bd401115742883cb6aef7997477473b2464abb
Reviewed-on: https://go-review.googlesource.com/c/go/+/651256
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer
2025-02-20 16:23:11 -08:00
committed by Gopher Robot
parent d45d502fbb
commit 549a88fa53
5 changed files with 94 additions and 6 deletions
src
cmd
compile
internal
go
internal
types
testdata

@ -242,10 +242,16 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
// signature may be generic
cgocall := x.mode == cgofunc
// a type parameter may be "called" if all types have the same signature
sig, _ := coreType(x.typ).(*Signature)
// If the operand type is a type parameter, all types in its type set
// must have a shared underlying type, which must be a signature.
var cause string
sig, _ := sharedUnder(check, x.typ, &cause).(*Signature)
if sig == nil {
check.errorf(x, InvalidCall, invalidOp+"cannot call non-function %s", x)
if cause != "" {
check.errorf(x, InvalidCall, invalidOp+"cannot call %s: %s", x, cause)
} else {
check.errorf(x, InvalidCall, invalidOp+"cannot call non-function %s", x)
}
x.mode = invalid
x.expr = call
return statement

@ -40,6 +40,38 @@ func typeset(t Type, yield func(t, u Type) bool) {
yield(t, under(t))
}
// If t is not a type parameter, sharedUnder returns the underlying type.
// If t is a type parameter, sharedUnder returns the single underlying
// type of all types in its type set if it exists.
// Otherwise the result is nil, and *cause reports the error if a non-nil
// cause is provided.
// The check parameter is only used if *cause reports an error; it may be nil.
func sharedUnder(check *Checker, t Type, cause *string) Type {
var s, su Type
bad := func(s string) bool {
if cause != nil {
*cause = s
}
su = nil
return false
}
typeset(t, func(t, u Type) bool {
if u == nil {
return bad("no specific type")
}
if su != nil && !Identical(su, u) {
return bad(check.sprintf("%s and %s have different underlying types", s, t))
}
// su == nil || Identical(su, u)
s, su = t, u
return true
})
return su
}
// If t is not a type parameter, sharedUnderOrChan returns the underlying type;
// if that type is a channel type it must permit receive operations.
// If t is a type parameter, sharedUnderOrChan returns the single underlying

@ -244,10 +244,16 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
// signature may be generic
cgocall := x.mode == cgofunc
// a type parameter may be "called" if all types have the same signature
sig, _ := coreType(x.typ).(*Signature)
// If the operand type is a type parameter, all types in its type set
// must have a shared underlying type, which must be a signature.
var cause string
sig, _ := sharedUnder(check, x.typ, &cause).(*Signature)
if sig == nil {
check.errorf(x, InvalidCall, invalidOp+"cannot call non-function %s", x)
if cause != "" {
check.errorf(x, InvalidCall, invalidOp+"cannot call %s: %s", x, cause)
} else {
check.errorf(x, InvalidCall, invalidOp+"cannot call non-function %s", x)
}
x.mode = invalid
x.expr = call
return statement

@ -43,6 +43,38 @@ func typeset(t Type, yield func(t, u Type) bool) {
yield(t, under(t))
}
// If t is not a type parameter, sharedUnder returns the underlying type.
// If t is a type parameter, sharedUnder returns the single underlying
// type of all types in its type set if it exists.
// Otherwise the result is nil, and *cause reports the error if a non-nil
// cause is provided.
// The check parameter is only used if *cause reports an error; it may be nil.
func sharedUnder(check *Checker, t Type, cause *string) Type {
var s, su Type
bad := func(s string) bool {
if cause != nil {
*cause = s
}
su = nil
return false
}
typeset(t, func(t, u Type) bool {
if u == nil {
return bad("no specific type")
}
if su != nil && !Identical(su, u) {
return bad(check.sprintf("%s and %s have different underlying types", s, t))
}
// su == nil || Identical(su, u)
s, su = t, u
return true
})
return su
}
// If t is not a type parameter, sharedUnderOrChan returns the underlying type;
// if that type is a channel type it must permit receive operations.
// If t is a type parameter, sharedUnderOrChan returns the single underlying

@ -71,3 +71,15 @@ func _() {
_ = x.Form // ERROR "x.Form undefined (type big.Float has no field or method Form, but does have unexported field form)"
_ = x.FOrm // ERROR "x.FOrm undefined (type big.Float has no field or method FOrm)"
}
func _[P any](x P) {
x /* ERROR "cannot call x (variable of type P constrained by any): no specific type" */ ()
}
func _[P int](x P) {
x /* ERROR "cannot call non-function x (variable of type P constrained by int)" */ ()
}
func _[P int | string](x P) {
x /* ERROR "cannot call x (variable of type P constrained by int | string): int and string have different underlying types" */ ()
}