From b9e4867e8d78d36ce2dafb780cc1ae8c8a523434 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 30 Jul 2015 16:06:36 -0700 Subject: [PATCH] go/types: fix real(a) and imag(a) for untyped arguments Fixes #11947. Change-Id: I6225f96b8dea0cecb097f9c7452a1aa80ae4476d Reviewed-on: https://go-review.googlesource.com/12939 Reviewed-by: Alan Donovan --- src/go/types/builtins.go | 52 ++++++++++++++++++++++++------ src/go/types/testdata/builtins.src | 39 +++++++++++++++------- 2 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index d26e9a71a1..7857a446a9 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -207,7 +207,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b return } - // Convert or check untyped arguments. + // convert or check untyped arguments d := 0 if isUntyped(x.typ) { d |= 1 @@ -231,7 +231,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b // 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 + // same type to succeed (this will result in an error + // because shifts of floats are not permitted) if x.mode == constant_ && y.mode == constant_ { toFloat := func(x *operand) { if isNumeric(x.typ) && constant.Sign(constant.Imag(x.val)) == 0 { @@ -243,6 +244,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b } else { check.convertUntyped(x, Typ[Float64]) check.convertUntyped(&y, Typ[Float64]) + // x and y should be invalid now, but be conservative + // and check below } } if x.mode == invalid || y.mode == invalid { @@ -261,7 +264,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b return } - // if both arguments are constant, the result is a constant + // if both arguments are constants, 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 { @@ -351,10 +354,35 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b case _Imag, _Real: // imag(complexT) floatT // real(complexT) floatT + + // convert or check untyped argument + if isUntyped(x.typ) { + if x.mode == constant_ { + // an untyped constant number can alway be considered + // as a complex constant + if isNumeric(x.typ) { + x.typ = Typ[UntypedComplex] + } + } else { + // an untyped non-constant argument may appear if + // it contains a (yet untyped non-constant) shift + // epression: convert it to complex128 which will + // result in an error (shift of complex value) + check.convertUntyped(x, Typ[Complex128]) + // x should be invalid now, but be conservative and check + if x.mode == invalid { + return + } + } + } + + // the argument must be of complex type if !isComplex(x.typ) { - check.invalidArg(x.pos(), "%s must be a complex number", x) + check.invalidArg(x.pos(), "argument has type %s, expected complex type", x.typ) return } + + // if the argument is a constant, the result is a constant if x.mode == constant_ { if id == _Real { x.val = constant.Real(x.val) @@ -364,22 +392,26 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b } else { x.mode = value } - var k BasicKind + + // determine result type + var res BasicKind switch x.typ.Underlying().(*Basic).kind { case Complex64: - k = Float32 + res = Float32 case Complex128: - k = Float64 + res = Float64 case UntypedComplex: - k = UntypedFloat + res = UntypedFloat default: unreachable() } + resTyp := Typ[res] if check.Types != nil && x.mode != constant_ { - check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ)) + check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ)) } - x.typ = Typ[k] + + x.typ = resTyp case _Make: // make(T, n) diff --git a/src/go/types/testdata/builtins.src b/src/go/types/testdata/builtins.src index 94eb6d771d..1931d56848 100644 --- a/src/go/types/testdata/builtins.src +++ b/src/go/types/testdata/builtins.src @@ -307,9 +307,10 @@ func imag1() { var c128 complex128 _ = imag() // ERROR not enough arguments _ = imag(1, 2) // ERROR too many arguments - _ = imag(10 /* ERROR must be a complex number */) - _ = imag(2.7182818 /* ERROR must be a complex number */) - _ = imag("foo" /* ERROR must be a complex number */) + _ = imag(10) + _ = imag(2.7182818) + _ = imag("foo" /* ERROR expected complex */) + _ = imag('a') const _5 = imag(1 + 2i) assert(_5 == 2) f32 = _5 @@ -331,8 +332,16 @@ func imag1() { f32 = imag(x64) f64 = imag(x128) - var s []complex64 - _ = imag(s... /* ERROR invalid use of \.\.\. */ ) + var a []complex64 + _ = imag(a... /* ERROR invalid use of \.\.\. */ ) + + // if argument is untyped, result is untyped + const _ byte = imag(1.2 + 3i) + const _ complex128 = imag(1.2 + 3i) + + // lhs constant shift operands are typed as complex128 + var s uint + _ = imag(1 /* ERROR must be integer */ << s) } func imag2() { @@ -579,9 +588,9 @@ func real1() { var c128 complex128 _ = real() // ERROR not enough arguments _ = real(1, 2) // ERROR too many arguments - _ = real(10 /* ERROR must be a complex number */) - _ = real(2.7182818 /* ERROR must be a complex number */) - _ = real("foo" /* ERROR must be a complex number */) + _ = real(10) + _ = real(2.7182818) + _ = real("foo" /* ERROR expected complex */) const _5 = real(1 + 2i) assert(_5 == 1) f32 = _5 @@ -601,10 +610,18 @@ func real1() { var x128 C128 f32 = imag(x64) f64 = imag(x128) - - var s []complex64 - _ = real(s... /* ERROR invalid use of \.\.\. */ ) _, _ = f32, f64 + + var a []complex64 + _ = real(a... /* ERROR invalid use of \.\.\. */ ) + + // if argument is untyped, result is untyped + const _ byte = real(1 + 2.3i) + const _ complex128 = real(1 + 2.3i) + + // lhs constant shift operands are typed as complex128 + var s uint + _ = real(1 /* ERROR must be integer */ << s) } func real2() { -- 2.48.1