Skip to content

Commit 549a88f

Browse files
griesemergopherbot
authored andcommitted
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 <[email protected]> Auto-Submit: Robert Griesemer <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent d45d502 commit 549a88f

File tree

5 files changed

+94
-6
lines changed

5 files changed

+94
-6
lines changed

src/cmd/compile/internal/types2/call.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,16 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
242242
// signature may be generic
243243
cgocall := x.mode == cgofunc
244244

245-
// a type parameter may be "called" if all types have the same signature
246-
sig, _ := coreType(x.typ).(*Signature)
245+
// If the operand type is a type parameter, all types in its type set
246+
// must have a shared underlying type, which must be a signature.
247+
var cause string
248+
sig, _ := sharedUnder(check, x.typ, &cause).(*Signature)
247249
if sig == nil {
248-
check.errorf(x, InvalidCall, invalidOp+"cannot call non-function %s", x)
250+
if cause != "" {
251+
check.errorf(x, InvalidCall, invalidOp+"cannot call %s: %s", x, cause)
252+
} else {
253+
check.errorf(x, InvalidCall, invalidOp+"cannot call non-function %s", x)
254+
}
249255
x.mode = invalid
250256
x.expr = call
251257
return statement

src/cmd/compile/internal/types2/under.go

+32
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,38 @@ func typeset(t Type, yield func(t, u Type) bool) {
4040
yield(t, under(t))
4141
}
4242

43+
// If t is not a type parameter, sharedUnder returns the underlying type.
44+
// If t is a type parameter, sharedUnder returns the single underlying
45+
// type of all types in its type set if it exists.
46+
// Otherwise the result is nil, and *cause reports the error if a non-nil
47+
// cause is provided.
48+
// The check parameter is only used if *cause reports an error; it may be nil.
49+
func sharedUnder(check *Checker, t Type, cause *string) Type {
50+
var s, su Type
51+
52+
bad := func(s string) bool {
53+
if cause != nil {
54+
*cause = s
55+
}
56+
su = nil
57+
return false
58+
}
59+
60+
typeset(t, func(t, u Type) bool {
61+
if u == nil {
62+
return bad("no specific type")
63+
}
64+
if su != nil && !Identical(su, u) {
65+
return bad(check.sprintf("%s and %s have different underlying types", s, t))
66+
}
67+
// su == nil || Identical(su, u)
68+
s, su = t, u
69+
return true
70+
})
71+
72+
return su
73+
}
74+
4375
// If t is not a type parameter, sharedUnderOrChan returns the underlying type;
4476
// if that type is a channel type it must permit receive operations.
4577
// If t is a type parameter, sharedUnderOrChan returns the single underlying

src/go/types/call.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,16 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
244244
// signature may be generic
245245
cgocall := x.mode == cgofunc
246246

247-
// a type parameter may be "called" if all types have the same signature
248-
sig, _ := coreType(x.typ).(*Signature)
247+
// If the operand type is a type parameter, all types in its type set
248+
// must have a shared underlying type, which must be a signature.
249+
var cause string
250+
sig, _ := sharedUnder(check, x.typ, &cause).(*Signature)
249251
if sig == nil {
250-
check.errorf(x, InvalidCall, invalidOp+"cannot call non-function %s", x)
252+
if cause != "" {
253+
check.errorf(x, InvalidCall, invalidOp+"cannot call %s: %s", x, cause)
254+
} else {
255+
check.errorf(x, InvalidCall, invalidOp+"cannot call non-function %s", x)
256+
}
251257
x.mode = invalid
252258
x.expr = call
253259
return statement

src/go/types/under.go

+32
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/internal/types/testdata/check/lookup1.go

+12
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,15 @@ func _() {
7171
_ = x.Form // ERROR "x.Form undefined (type big.Float has no field or method Form, but does have unexported field form)"
7272
_ = x.FOrm // ERROR "x.FOrm undefined (type big.Float has no field or method FOrm)"
7373
}
74+
75+
func _[P any](x P) {
76+
x /* ERROR "cannot call x (variable of type P constrained by any): no specific type" */ ()
77+
}
78+
79+
func _[P int](x P) {
80+
x /* ERROR "cannot call non-function x (variable of type P constrained by int)" */ ()
81+
}
82+
83+
func _[P int | string](x P) {
84+
x /* ERROR "cannot call x (variable of type P constrained by int | string): int and string have different underlying types" */ ()
85+
}

0 commit comments

Comments
 (0)