diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index 47295914d00e5..d26e9a71a1158 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -200,77 +200,93 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b } case _Complex: - // complex(x, y realT) complexT - if !check.complexArg(x) { - return - } - + // complex(x, y floatT) complexT var y operand arg(&y, 1) if y.mode == invalid { return } - if !check.complexArg(&y) { - return - } - check.convertUntyped(x, y.typ) - if x.mode == invalid { - return + // Convert or check untyped arguments. + d := 0 + if isUntyped(x.typ) { + d |= 1 + } + if isUntyped(y.typ) { + d |= 2 + } + switch d { + case 0: + // x and y are typed => nothing to do + case 1: + // only x is untyped => convert to type of y + check.convertUntyped(x, y.typ) + case 2: + // only y is untyped => convert to type of x + check.convertUntyped(&y, x.typ) + case 3: + // x and y are untyped => + // 1) if both are constants, convert them to untyped + // floating-point numbers if possible, + // 2) if one of them is not constant (possible because + // it contains a shift that is yet untyped), convert + // both of them to float64 since they must have the + // same type to succeed + if x.mode == constant_ && y.mode == constant_ { + toFloat := func(x *operand) { + if isNumeric(x.typ) && constant.Sign(constant.Imag(x.val)) == 0 { + x.typ = Typ[UntypedFloat] + } + } + toFloat(x) + toFloat(&y) + } else { + check.convertUntyped(x, Typ[Float64]) + check.convertUntyped(&y, Typ[Float64]) + } } - check.convertUntyped(&y, x.typ) - if y.mode == invalid { + if x.mode == invalid || y.mode == invalid { return } + // both argument types must be identical if !Identical(x.typ, y.typ) { check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ) return } + // the argument types must be of floating-point type + if !isFloat(x.typ) { + check.invalidArg(x.pos(), "arguments have type %s, expected floating-point", x.typ) + return + } + + // if both arguments are constant, the result is a constant if x.mode == constant_ && y.mode == constant_ { x.val = constant.BinaryOp(x.val, token.ADD, constant.MakeImag(y.val)) } else { x.mode = value } - realT := x.typ - complexT := Typ[Invalid] - switch realT.Underlying().(*Basic).kind { + // determine result type + var res BasicKind + switch x.typ.Underlying().(*Basic).kind { case Float32: - complexT = Typ[Complex64] + res = Complex64 case Float64: - complexT = Typ[Complex128] - case UntypedInt, UntypedRune, UntypedFloat: - if x.mode == constant_ { - realT = defaultType(realT).(*Basic) - complexT = Typ[UntypedComplex] - } else { - // untyped but not constant; probably because one - // operand is a non-constant shift of untyped lhs - realT = Typ[Float64] - complexT = Typ[Complex128] - } + res = Complex128 + case UntypedFloat: + res = UntypedComplex default: - check.invalidArg(x.pos(), "float32 or float64 arguments expected") - return + unreachable() } + resTyp := Typ[res] - x.typ = complexT if check.Types != nil && x.mode != constant_ { - check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT)) + check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ, x.typ)) } - if x.mode != constant_ { - // The arguments have now their final types, which at run- - // time will be materialized. Update the expression trees. - // If the current types are untyped, the materialized type - // is the respective default type. - // (If the result is constant, the arguments are never - // materialized and there is nothing to do.) - check.updateExprType(x.expr, realT, true) - check.updateExprType(y.expr, realT, true) - } + x.typ = resTyp case _Copy: // copy(x, y []T) int @@ -333,8 +349,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b } case _Imag, _Real: - // imag(complexT) realT - // real(complexT) realT + // imag(complexT) floatT + // real(complexT) floatT if !isComplex(x.typ) { check.invalidArg(x.pos(), "%s must be a complex number", x) return @@ -616,12 +632,3 @@ func unparen(e ast.Expr) ast.Expr { e = p.X } } - -func (check *Checker) complexArg(x *operand) bool { - t, _ := x.typ.Underlying().(*Basic) - if t != nil && (t.info&IsFloat != 0 || t.kind == UntypedInt || t.kind == UntypedRune) { - return true - } - check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x) - return false -} diff --git a/src/go/types/testdata/builtins.src b/src/go/types/testdata/builtins.src index 9eb551dc109ec..94eb6d771db91 100644 --- a/src/go/types/testdata/builtins.src +++ b/src/go/types/testdata/builtins.src @@ -168,14 +168,14 @@ func complex1() { var c128 complex128 _ = complex() // ERROR not enough arguments _ = complex(1) // ERROR not enough arguments - _ = complex(true /* ERROR invalid argument */ , 0) - _ = complex(i32 /* ERROR invalid argument */ , 0) - _ = complex("foo" /* ERROR invalid argument */ , 0) - _ = complex(c64 /* ERROR invalid argument */ , 0) - _ = complex(0, true /* ERROR invalid argument */ ) - _ = complex(0, i32 /* ERROR invalid argument */ ) - _ = complex(0, "foo" /* ERROR invalid argument */ ) - _ = complex(0, c64 /* ERROR invalid argument */ ) + _ = complex(true /* ERROR mismatched types */ , 0) + _ = complex(i32 /* ERROR expected floating-point */ , 0) + _ = complex("foo" /* ERROR mismatched types */ , 0) + _ = complex(c64 /* ERROR expected floating-point */ , 0) + _ = complex(0 /* ERROR mismatched types */ , true) + _ = complex(0 /* ERROR expected floating-point */ , i32) + _ = complex(0 /* ERROR mismatched types */ , "foo") + _ = complex(0 /* ERROR expected floating-point */ , c64) _ = complex(f32, f32) _ = complex(f32, 1) _ = complex(f32, 1.0) @@ -202,6 +202,9 @@ func complex1() { const _ float32 = complex(1, 0) const _ complex64 = complex(1, 0) const _ complex128 = complex(1, 0) + const _ = complex(0i, 0i) + const _ = complex(0i, 0) + const _ int = 1.0 + complex(1, 0i) const _ int = complex /* ERROR int */ (1.1, 0) const _ float32 = complex /* ERROR float32 */ (1, 2)