From 72f448cb48069ddeb0c30e726fc3b9d113aaf51e Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 17 May 2023 21:10:44 -0700 Subject: [PATCH] go/types, types2: factor out type matching in binary operations Change-Id: Ica61698b6ba00687dcd133245bfc3d87808c7bca Reviewed-on: https://go-review.googlesource.com/c/go/+/496096 Run-TryBot: Robert Griesemer TryBot-Result: Gopher Robot Auto-Submit: Robert Griesemer Reviewed-by: Robert Findley Reviewed-by: Robert Griesemer --- src/cmd/compile/internal/types2/expr.go | 116 +++++++++++++----------- src/go/types/expr.go | 116 +++++++++++++----------- 2 files changed, 126 insertions(+), 106 deletions(-) diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index e69f2e4c101..6904de1a0bf 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -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 diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 898c5627857..69cf008b99a 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -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