]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: fix real(a) and imag(a) for untyped arguments
authorRobert Griesemer <gri@golang.org>
Thu, 30 Jul 2015 23:06:36 +0000 (16:06 -0700)
committerRobert Griesemer <gri@golang.org>
Wed, 26 Aug 2015 21:16:31 +0000 (21:16 +0000)
Fixes #11947.

Change-Id: I6225f96b8dea0cecb097f9c7452a1aa80ae4476d
Reviewed-on: https://go-review.googlesource.com/12939
Reviewed-by: Alan Donovan <adonovan@google.com>
src/go/types/builtins.go
src/go/types/testdata/builtins.src

index d26e9a71a115825dd63c9ea2ab0c3c9b1120b0b8..7857a446a9fffea9214bb3862f9829119dc0b7e6 100644 (file)
@@ -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)
index 94eb6d771db91c883a8c4dad94a50cb01f33861a..1931d568485f625059411089c874389c342e1e2a 100644 (file)
@@ -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() {