go/types, types2: better error messages for certain type mismatches

When an untyped operand of a (typically binary) operation does not
match the type of the operand and an implicit conversion is not
possible, the error message should report a "type mismatch".

The type-checkers mostly did so, but not for untyped numeric types
to other types (e.g. an untyped int vs a function); in those cases
it reported that the (impossible) conversion failed.

Fix this for numeric types.
This also improves the position and messages for some incorrect
min/max built-in calls.

Fixes #73428.

Change-Id: I8af071918b73fcc72f16cc61858d7baca57fc259
Reviewed-on: https://go-review.googlesource.com/c/go/+/682495
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Mark Freeman <mark@golang.org>
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2025-06-17 17:09:27 -07:00 committed by Gopher Robot
parent 2ddf542e4c
commit cae45167b7
8 changed files with 30 additions and 7 deletions

View file

@ -895,6 +895,10 @@ func (check *Checker) matchTypes(x, y *operand) {
if isTyped(x.typ) && isTyped(y.typ) { if isTyped(x.typ) && isTyped(y.typ) {
return false return false
} }
// A numeric type can only convert to another numeric type.
if allNumeric(x.typ) != allNumeric(y.typ) {
return false
}
// An untyped operand may convert to its default type when paired with an empty interface // An untyped operand may convert to its default type when paired with an empty interface
// TODO(gri) This should only matter for comparisons (the only binary operation that is // TODO(gri) This should only matter for comparisons (the only binary operation that is
// valid with interfaces), but in that case the assignability check should take // valid with interfaces), but in that case the assignability check should take

View file

@ -887,6 +887,10 @@ func (check *Checker) matchTypes(x, y *operand) {
if isTyped(x.typ) && isTyped(y.typ) { if isTyped(x.typ) && isTyped(y.typ) {
return false return false
} }
// A numeric type can only convert to another numeric type.
if allNumeric(x.typ) != allNumeric(y.typ) {
return false
}
// An untyped operand may convert to its default type when paired with an empty interface // An untyped operand may convert to its default type when paired with an empty interface
// TODO(gri) This should only matter for comparisons (the only binary operation that is // TODO(gri) This should only matter for comparisons (the only binary operation that is
// valid with interfaces), but in that case the assignability check should take // valid with interfaces), but in that case the assignability check should take

View file

@ -881,7 +881,9 @@ const (
// context in which it is used. // context in which it is used.
// //
// Example: // Example:
// var _ = 1 + []int{} // func f[T ~int8 | ~int16 | ~int32 | ~int64](x T) T {
// return x + 1024
// }
InvalidUntypedConversion InvalidUntypedConversion
// BadOffsetofSyntax occurs when unsafe.Offsetof is called with an argument // BadOffsetofSyntax occurs when unsafe.Offsetof is called with an argument

View file

@ -211,7 +211,7 @@ func _[
var x2 P2 var x2 P2
_ = max(x2) _ = max(x2)
_ = max(x2, x2) _ = max(x2, x2)
_ = max(1, 2 /* ERROR "cannot convert 2 (untyped int constant) to type P2" */, x2) // error at 2 because max is 2 _ = max(1, 2, x2 /* ERROR "mismatched types untyped int (previous argument) and P2 (type of x2)" */ )
_ = max(x1, x2 /* ERROR "mismatched types P1 (previous argument) and P2 (type of x2)" */ ) _ = max(x1, x2 /* ERROR "mismatched types P1 (previous argument) and P2 (type of x2)" */ )
} }
@ -232,7 +232,7 @@ func _[
var x2 P2 var x2 P2
_ = min(x2) _ = min(x2)
_ = min(x2, x2) _ = min(x2, x2)
_ = min(1 /* ERROR "cannot convert 1 (untyped int constant) to type P2" */ , 2, x2) // error at 1 because min is 1 _ = min(1, 2, x2 /* ERROR "mismatched types untyped int (previous argument) and P2 (type of x2)" */ )
_ = min(x1, x2 /* ERROR "mismatched types P1 (previous argument) and P2 (type of x2)" */ ) _ = min(x1, x2 /* ERROR "mismatched types P1 (previous argument) and P2 (type of x2)" */ )
} }

View file

@ -201,7 +201,7 @@ func interfaces() {
var s11 S11 var s11 S11
var s2 S2 var s2 S2
_ = i == 0 /* ERROR "cannot convert" */ _ = i == 0 /* ERROR "invalid operation: i == 0 (mismatched types interface{m() int} and untyped int)" */
_ = i == s1 /* ERROR "mismatched types" */ _ = i == s1 /* ERROR "mismatched types" */
_ = i == &s1 _ = i == &s1
_ = i == &s11 _ = i == &s11

View file

@ -13,5 +13,5 @@ var s struct{ x int }
func _() { func _() {
f(s.y /* ERROR "s.y undefined" */) f(s.y /* ERROR "s.y undefined" */)
f(1 /* ERROR "cannot convert 1" */ / s) f(1 /* ERROR "invalid operation: 1 / s (mismatched types untyped int and struct{x int})" */ / s)
} }

View file

@ -0,0 +1,13 @@
// Copyright 2025 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
func f() {}
const c = 0
var v int
var _ = f < c // ERROR "invalid operation: f < c (mismatched types func() and untyped int)"
var _ = f < v // ERROR "invalid operation: f < v (mismatched types func() and int)"

View file

@ -31,7 +31,7 @@ var (
var ( var (
_ = b + 1 // ERROR "invalid operation.*mismatched types.*bool and untyped int" _ = b + 1 // ERROR "invalid operation.*mismatched types.*bool and untyped int"
_ = i + false // ERROR "invalid operation.*mismatched types.*int and untyped bool" _ = i + false // ERROR "invalid operation.*mismatched types.*int and untyped bool"
_ = iface + 1 // ERROR "invalid operation.*mismatched types.*interface *{} and int" _ = iface + 1 // ERROR "invalid operation.*mismatched types.*interface *{} and untyped int"
_ = iface + 1.0 // ERROR "invalid operation.*mismatched types.*interface *{} and float64" _ = iface + 1.0 // ERROR "invalid operation.*mismatched types.*interface *{} and untyped float"
_ = iface + false // ERROR "invalid operation.*mismatched types.*interface *{} and bool" _ = iface + false // ERROR "invalid operation.*mismatched types.*interface *{} and bool"
) )