Skip to content

Commit 5ba98b9

Browse files
griesemerRobert Griesemer
authored and
Robert Griesemer
committed
go/types, types2: report type mismatch error when conversion is impossible
Rather than reporting an impossible conversion error when mixing an untyped value with a pointer type in an operation, report a type mismatch error. This fixes a regression in error quality compared to pre-1.18. For the fix, clean up the implementation of canMix, add documentation, and give it a better name. Adjust test case for corresponding error code bacause we now get a better error message (and error code) for the old error code example. Fixes #57160. Change-Id: Ib96ce7cbc44db6905fa2f1c90a3769af609e101b Reviewed-on: https://go-review.googlesource.com/c/go/+/457055 Reviewed-by: Robert Findley <[email protected]> Run-TryBot: Robert Griesemer <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 61e2b8e commit 5ba98b9

File tree

4 files changed

+71
-13
lines changed

4 files changed

+71
-13
lines changed

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

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,26 +1103,50 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
11031103
return
11041104
}
11051105

1106-
// TODO(gri) make canMix more efficient - called for each binary operation
1107-
canMix := func(x, y *operand) bool {
1106+
// mayConvert reports whether the operands x and y may
1107+
// possibly have matching types after converting one
1108+
// untyped operand to the type of the other.
1109+
// If mayConvert returns true, we try to convert the
1110+
// operands to each other's types, and if that fails
1111+
// we report a conversion failure.
1112+
// If mayConvert returns false, we continue without an
1113+
// attempt at conversion, and if the operand types are
1114+
// not compatible, we report a type mismatch error.
1115+
mayConvert := func(x, y *operand) bool {
1116+
// If both operands are typed, there's no need for an implicit conversion.
1117+
if isTyped(x.typ) && isTyped(y.typ) {
1118+
return false
1119+
}
1120+
// An untyped operand may convert to its default type when paired with an empty interface
1121+
// TODO(gri) This should only matter for comparisons (the only binary operation that is
1122+
// valid with interfaces), but in that case the assignability check should take
1123+
// care of the conversion. Verify and possibly eliminate this extra test.
11081124
if isNonTypeParamInterface(x.typ) || isNonTypeParamInterface(y.typ) {
11091125
return true
11101126
}
1127+
// A boolean type can only convert to another boolean type.
11111128
if allBoolean(x.typ) != allBoolean(y.typ) {
11121129
return false
11131130
}
1131+
// A string type can only convert to another string type.
11141132
if allString(x.typ) != allString(y.typ) {
11151133
return false
11161134
}
1117-
if x.isNil() && !hasNil(y.typ) {
1118-
return false
1135+
// Untyped nil can only convert to a type that has a nil.
1136+
if x.isNil() {
1137+
return hasNil(y.typ)
1138+
}
1139+
if y.isNil() {
1140+
return hasNil(x.typ)
11191141
}
1120-
if y.isNil() && !hasNil(x.typ) {
1142+
// An untyped operand cannot convert to a pointer.
1143+
// TODO(gri) generalize to type parameters
1144+
if isPointer(x.typ) || isPointer(y.typ) {
11211145
return false
11221146
}
11231147
return true
11241148
}
1125-
if canMix(x, &y) {
1149+
if mayConvert(x, &y) {
11261150
check.convertUntyped(x, y.typ)
11271151
if x.mode == invalid {
11281152
return

src/go/types/expr.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,26 +1084,50 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token
10841084
return
10851085
}
10861086

1087-
// TODO(gri) make canMix more efficient - called for each binary operation
1088-
canMix := func(x, y *operand) bool {
1087+
// mayConvert reports whether the operands x and y may
1088+
// possibly have matching types after converting one
1089+
// untyped operand to the type of the other.
1090+
// If mayConvert returns true, we try to convert the
1091+
// operands to each other's types, and if that fails
1092+
// we report a conversion failure.
1093+
// If mayConvert returns false, we continue without an
1094+
// attempt at conversion, and if the operand types are
1095+
// not compatible, we report a type mismatch error.
1096+
mayConvert := func(x, y *operand) bool {
1097+
// If both operands are typed, there's no need for an implicit conversion.
1098+
if isTyped(x.typ) && isTyped(y.typ) {
1099+
return false
1100+
}
1101+
// An untyped operand may convert to its default type when paired with an empty interface
1102+
// TODO(gri) This should only matter for comparisons (the only binary operation that is
1103+
// valid with interfaces), but in that case the assignability check should take
1104+
// care of the conversion. Verify and possibly eliminate this extra test.
10891105
if isNonTypeParamInterface(x.typ) || isNonTypeParamInterface(y.typ) {
10901106
return true
10911107
}
1108+
// A boolean type can only convert to another boolean type.
10921109
if allBoolean(x.typ) != allBoolean(y.typ) {
10931110
return false
10941111
}
1112+
// A string type can only convert to another string type.
10951113
if allString(x.typ) != allString(y.typ) {
10961114
return false
10971115
}
1098-
if x.isNil() && !hasNil(y.typ) {
1099-
return false
1116+
// Untyped nil can only convert to a type that has a nil.
1117+
if x.isNil() {
1118+
return hasNil(y.typ)
1119+
}
1120+
if y.isNil() {
1121+
return hasNil(x.typ)
11001122
}
1101-
if y.isNil() && !hasNil(x.typ) {
1123+
// An untyped operand cannot convert to a pointer.
1124+
// TODO(gri) generalize to type parameters
1125+
if isPointer(x.typ) || isPointer(y.typ) {
11021126
return false
11031127
}
11041128
return true
11051129
}
1106-
if canMix(x, &y) {
1130+
if mayConvert(x, &y) {
11071131
check.convertUntyped(x, y.typ)
11081132
if x.mode == invalid {
11091133
return

src/internal/types/errors/codes.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,7 @@ const (
882882
// context in which it is used.
883883
//
884884
// Example:
885-
// var _ = 1 + new(int)
885+
// var _ = 1 + []int{}
886886
InvalidUntypedConversion
887887

888888
// BadOffsetofSyntax occurs when unsafe.Offsetof is called with an argument
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
func _(x *int) {
8+
_ = 0 < x // ERROR "invalid operation"
9+
_ = x < 0 // ERROR "invalid operation"
10+
}

0 commit comments

Comments
 (0)