]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: factor out type matching in binary operations
authorRobert Griesemer <gri@golang.org>
Thu, 18 May 2023 04:10:44 +0000 (21:10 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 18 May 2023 18:40:06 +0000 (18:40 +0000)
Change-Id: Ica61698b6ba00687dcd133245bfc3d87808c7bca
Reviewed-on: https://go-review.googlesource.com/c/go/+/496096
Run-TryBot: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
src/cmd/compile/internal/types2/expr.go
src/go/types/expr.go

index e69f2e4c101a5842b5c8e762216923f3a5ddc6ca..6904de1a0bfc092a17401337aa4b6ae2305d59a3 100644 (file)
@@ -815,59 +815,9 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
                return
        }
 
-       // mayConvert reports whether the operands x and y may
-       // possibly have matching types after converting one
-       // untyped operand to the type of the other.
-       // If mayConvert returns true, we try to convert the
-       // operands to each other's types, and if that fails
-       // we report a conversion failure.
-       // If mayConvert returns false, we continue without an
-       // attempt at conversion, and if the operand types are
-       // not compatible, we report a type mismatch error.
-       mayConvert := func(x, y *operand) bool {
-               // If both operands are typed, there's no need for an implicit conversion.
-               if isTyped(x.typ) && isTyped(y.typ) {
-                       return false
-               }
-               // 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
-               //           valid with interfaces), but in that case the assignability check should take
-               //           care of the conversion. Verify and possibly eliminate this extra test.
-               if isNonTypeParamInterface(x.typ) || isNonTypeParamInterface(y.typ) {
-                       return true
-               }
-               // A boolean type can only convert to another boolean type.
-               if allBoolean(x.typ) != allBoolean(y.typ) {
-                       return false
-               }
-               // A string type can only convert to another string type.
-               if allString(x.typ) != allString(y.typ) {
-                       return false
-               }
-               // Untyped nil can only convert to a type that has a nil.
-               if x.isNil() {
-                       return hasNil(y.typ)
-               }
-               if y.isNil() {
-                       return hasNil(x.typ)
-               }
-               // An untyped operand cannot convert to a pointer.
-               // TODO(gri) generalize to type parameters
-               if isPointer(x.typ) || isPointer(y.typ) {
-                       return false
-               }
-               return true
-       }
-       if mayConvert(x, &y) {
-               check.convertUntyped(x, y.typ)
-               if x.mode == invalid {
-                       return
-               }
-               check.convertUntyped(&y, x.typ)
-               if y.mode == invalid {
-                       x.mode = invalid
-                       return
-               }
+       check.matchTypes(x, &y)
+       if x.mode == invalid {
+               return
        }
 
        if isComparison(op) {
@@ -936,6 +886,66 @@ func (check *Checker) binary(x *operand, e syntax.Expr, lhs, rhs syntax.Expr, op
        // x.typ is unchanged
 }
 
+// matchTypes attempts to convert any untyped types x and y such that they match.
+// If an error occurs, x.mode is set to invalid.
+func (check *Checker) matchTypes(x, y *operand) {
+       // mayConvert reports whether the operands x and y may
+       // possibly have matching types after converting one
+       // untyped operand to the type of the other.
+       // If mayConvert returns true, we try to convert the
+       // operands to each other's types, and if that fails
+       // we report a conversion failure.
+       // If mayConvert returns false, we continue without an
+       // attempt at conversion, and if the operand types are
+       // not compatible, we report a type mismatch error.
+       mayConvert := func(x, y *operand) bool {
+               // If both operands are typed, there's no need for an implicit conversion.
+               if isTyped(x.typ) && isTyped(y.typ) {
+                       return false
+               }
+               // 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
+               //           valid with interfaces), but in that case the assignability check should take
+               //           care of the conversion. Verify and possibly eliminate this extra test.
+               if isNonTypeParamInterface(x.typ) || isNonTypeParamInterface(y.typ) {
+                       return true
+               }
+               // A boolean type can only convert to another boolean type.
+               if allBoolean(x.typ) != allBoolean(y.typ) {
+                       return false
+               }
+               // A string type can only convert to another string type.
+               if allString(x.typ) != allString(y.typ) {
+                       return false
+               }
+               // Untyped nil can only convert to a type that has a nil.
+               if x.isNil() {
+                       return hasNil(y.typ)
+               }
+               if y.isNil() {
+                       return hasNil(x.typ)
+               }
+               // An untyped operand cannot convert to a pointer.
+               // TODO(gri) generalize to type parameters
+               if isPointer(x.typ) || isPointer(y.typ) {
+                       return false
+               }
+               return true
+       }
+
+       if mayConvert(x, y) {
+               check.convertUntyped(x, y.typ)
+               if x.mode == invalid {
+                       return
+               }
+               check.convertUntyped(y, x.typ)
+               if y.mode == invalid {
+                       x.mode = invalid
+                       return
+               }
+       }
+}
+
 // exprKind describes the kind of an expression; the kind
 // determines if an expression is valid in 'statement context'.
 type exprKind int
index 898c5627857659b3fa9b5d2b5d774d45a5ae8291..69cf008b99af212449c3ef610a5967f61d6b37f9 100644 (file)
@@ -797,59 +797,9 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token
                return
        }
 
-       // mayConvert reports whether the operands x and y may
-       // possibly have matching types after converting one
-       // untyped operand to the type of the other.
-       // If mayConvert returns true, we try to convert the
-       // operands to each other's types, and if that fails
-       // we report a conversion failure.
-       // If mayConvert returns false, we continue without an
-       // attempt at conversion, and if the operand types are
-       // not compatible, we report a type mismatch error.
-       mayConvert := func(x, y *operand) bool {
-               // If both operands are typed, there's no need for an implicit conversion.
-               if isTyped(x.typ) && isTyped(y.typ) {
-                       return false
-               }
-               // 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
-               //           valid with interfaces), but in that case the assignability check should take
-               //           care of the conversion. Verify and possibly eliminate this extra test.
-               if isNonTypeParamInterface(x.typ) || isNonTypeParamInterface(y.typ) {
-                       return true
-               }
-               // A boolean type can only convert to another boolean type.
-               if allBoolean(x.typ) != allBoolean(y.typ) {
-                       return false
-               }
-               // A string type can only convert to another string type.
-               if allString(x.typ) != allString(y.typ) {
-                       return false
-               }
-               // Untyped nil can only convert to a type that has a nil.
-               if x.isNil() {
-                       return hasNil(y.typ)
-               }
-               if y.isNil() {
-                       return hasNil(x.typ)
-               }
-               // An untyped operand cannot convert to a pointer.
-               // TODO(gri) generalize to type parameters
-               if isPointer(x.typ) || isPointer(y.typ) {
-                       return false
-               }
-               return true
-       }
-       if mayConvert(x, &y) {
-               check.convertUntyped(x, y.typ)
-               if x.mode == invalid {
-                       return
-               }
-               check.convertUntyped(&y, x.typ)
-               if y.mode == invalid {
-                       x.mode = invalid
-                       return
-               }
+       check.matchTypes(x, &y)
+       if x.mode == invalid {
+               return
        }
 
        if isComparison(op) {
@@ -921,6 +871,66 @@ func (check *Checker) binary(x *operand, e ast.Expr, lhs, rhs ast.Expr, op token
        // x.typ is unchanged
 }
 
+// matchTypes attempts to convert any untyped types x and y such that they match.
+// If an error occurs, x.mode is set to invalid.
+func (check *Checker) matchTypes(x, y *operand) {
+       // mayConvert reports whether the operands x and y may
+       // possibly have matching types after converting one
+       // untyped operand to the type of the other.
+       // If mayConvert returns true, we try to convert the
+       // operands to each other's types, and if that fails
+       // we report a conversion failure.
+       // If mayConvert returns false, we continue without an
+       // attempt at conversion, and if the operand types are
+       // not compatible, we report a type mismatch error.
+       mayConvert := func(x, y *operand) bool {
+               // If both operands are typed, there's no need for an implicit conversion.
+               if isTyped(x.typ) && isTyped(y.typ) {
+                       return false
+               }
+               // 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
+               //           valid with interfaces), but in that case the assignability check should take
+               //           care of the conversion. Verify and possibly eliminate this extra test.
+               if isNonTypeParamInterface(x.typ) || isNonTypeParamInterface(y.typ) {
+                       return true
+               }
+               // A boolean type can only convert to another boolean type.
+               if allBoolean(x.typ) != allBoolean(y.typ) {
+                       return false
+               }
+               // A string type can only convert to another string type.
+               if allString(x.typ) != allString(y.typ) {
+                       return false
+               }
+               // Untyped nil can only convert to a type that has a nil.
+               if x.isNil() {
+                       return hasNil(y.typ)
+               }
+               if y.isNil() {
+                       return hasNil(x.typ)
+               }
+               // An untyped operand cannot convert to a pointer.
+               // TODO(gri) generalize to type parameters
+               if isPointer(x.typ) || isPointer(y.typ) {
+                       return false
+               }
+               return true
+       }
+
+       if mayConvert(x, y) {
+               check.convertUntyped(x, y.typ)
+               if x.mode == invalid {
+                       return
+               }
+               check.convertUntyped(y, x.typ)
+               if y.mode == invalid {
+                       x.mode = invalid
+                       return
+               }
+       }
+}
+
 // exprKind describes the kind of an expression; the kind
 // determines if an expression is valid in 'statement context'.
 type exprKind int