Commit b9e4867e authored by Robert Griesemer's avatar Robert Griesemer

go/types: fix real(a) and imag(a) for untyped arguments

Fixes #11947.

Change-Id: I6225f96b8dea0cecb097f9c7452a1aa80ae4476d
Reviewed-on: https://go-review.googlesource.com/12939Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent a34b8cb7
...@@ -207,7 +207,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b ...@@ -207,7 +207,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return return
} }
// Convert or check untyped arguments. // convert or check untyped arguments
d := 0 d := 0
if isUntyped(x.typ) { if isUntyped(x.typ) {
d |= 1 d |= 1
...@@ -231,7 +231,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b ...@@ -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 // 2) if one of them is not constant (possible because
// it contains a shift that is yet untyped), convert // it contains a shift that is yet untyped), convert
// both of them to float64 since they must have the // 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_ { if x.mode == constant_ && y.mode == constant_ {
toFloat := func(x *operand) { toFloat := func(x *operand) {
if isNumeric(x.typ) && constant.Sign(constant.Imag(x.val)) == 0 { 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 ...@@ -243,6 +244,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
} else { } else {
check.convertUntyped(x, Typ[Float64]) check.convertUntyped(x, Typ[Float64])
check.convertUntyped(&y, 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 { if x.mode == invalid || y.mode == invalid {
...@@ -261,7 +264,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b ...@@ -261,7 +264,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return 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_ { if x.mode == constant_ && y.mode == constant_ {
x.val = constant.BinaryOp(x.val, token.ADD, constant.MakeImag(y.val)) x.val = constant.BinaryOp(x.val, token.ADD, constant.MakeImag(y.val))
} else { } else {
...@@ -351,10 +354,35 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b ...@@ -351,10 +354,35 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Imag, _Real: case _Imag, _Real:
// imag(complexT) floatT // imag(complexT) floatT
// real(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) { 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 return
} }
// if the argument is a constant, the result is a constant
if x.mode == constant_ { if x.mode == constant_ {
if id == _Real { if id == _Real {
x.val = constant.Real(x.val) x.val = constant.Real(x.val)
...@@ -364,22 +392,26 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b ...@@ -364,22 +392,26 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
} else { } else {
x.mode = value x.mode = value
} }
var k BasicKind
// determine result type
var res BasicKind
switch x.typ.Underlying().(*Basic).kind { switch x.typ.Underlying().(*Basic).kind {
case Complex64: case Complex64:
k = Float32 res = Float32
case Complex128: case Complex128:
k = Float64 res = Float64
case UntypedComplex: case UntypedComplex:
k = UntypedFloat res = UntypedFloat
default: default:
unreachable() unreachable()
} }
resTyp := Typ[res]
if check.Types != nil && x.mode != constant_ { 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: case _Make:
// make(T, n) // make(T, n)
......
...@@ -307,9 +307,10 @@ func imag1() { ...@@ -307,9 +307,10 @@ func imag1() {
var c128 complex128 var c128 complex128
_ = imag() // ERROR not enough arguments _ = imag() // ERROR not enough arguments
_ = imag(1, 2) // ERROR too many arguments _ = imag(1, 2) // ERROR too many arguments
_ = imag(10 /* ERROR must be a complex number */) _ = imag(10)
_ = imag(2.7182818 /* ERROR must be a complex number */) _ = imag(2.7182818)
_ = imag("foo" /* ERROR must be a complex number */) _ = imag("foo" /* ERROR expected complex */)
_ = imag('a')
const _5 = imag(1 + 2i) const _5 = imag(1 + 2i)
assert(_5 == 2) assert(_5 == 2)
f32 = _5 f32 = _5
...@@ -331,8 +332,16 @@ func imag1() { ...@@ -331,8 +332,16 @@ func imag1() {
f32 = imag(x64) f32 = imag(x64)
f64 = imag(x128) f64 = imag(x128)
var s []complex64 var a []complex64
_ = imag(s... /* ERROR invalid use of \.\.\. */ ) _ = 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() { func imag2() {
...@@ -579,9 +588,9 @@ func real1() { ...@@ -579,9 +588,9 @@ func real1() {
var c128 complex128 var c128 complex128
_ = real() // ERROR not enough arguments _ = real() // ERROR not enough arguments
_ = real(1, 2) // ERROR too many arguments _ = real(1, 2) // ERROR too many arguments
_ = real(10 /* ERROR must be a complex number */) _ = real(10)
_ = real(2.7182818 /* ERROR must be a complex number */) _ = real(2.7182818)
_ = real("foo" /* ERROR must be a complex number */) _ = real("foo" /* ERROR expected complex */)
const _5 = real(1 + 2i) const _5 = real(1 + 2i)
assert(_5 == 1) assert(_5 == 1)
f32 = _5 f32 = _5
...@@ -601,10 +610,18 @@ func real1() { ...@@ -601,10 +610,18 @@ func real1() {
var x128 C128 var x128 C128
f32 = imag(x64) f32 = imag(x64)
f64 = imag(x128) f64 = imag(x128)
var s []complex64
_ = real(s... /* ERROR invalid use of \.\.\. */ )
_, _ = f32, f64 _, _ = 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() { func real2() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment