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 #70128. 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:
committed by
Gopher Robot
parent
d45d502fbb
commit
549a88fa53
src
@ -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
|
||||
|
12
src/internal/types/testdata/check/lookup1.go
vendored
12
src/internal/types/testdata/check/lookup1.go
vendored
@ -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" */ ()
|
||||
}
|
||||
|
Reference in New Issue
Block a user