Skip to content

Commit b9e4867

Browse files
committed
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 <[email protected]>
1 parent a34b8cb commit b9e4867

File tree

2 files changed

+70
-21
lines changed

2 files changed

+70
-21
lines changed

src/go/types/builtins.go

+42-10
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
207207
return
208208
}
209209

210-
// Convert or check untyped arguments.
210+
// convert or check untyped arguments
211211
d := 0
212212
if isUntyped(x.typ) {
213213
d |= 1
@@ -231,7 +231,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
231231
// 2) if one of them is not constant (possible because
232232
// it contains a shift that is yet untyped), convert
233233
// both of them to float64 since they must have the
234-
// same type to succeed
234+
// same type to succeed (this will result in an error
235+
// because shifts of floats are not permitted)
235236
if x.mode == constant_ && y.mode == constant_ {
236237
toFloat := func(x *operand) {
237238
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
243244
} else {
244245
check.convertUntyped(x, Typ[Float64])
245246
check.convertUntyped(&y, Typ[Float64])
247+
// x and y should be invalid now, but be conservative
248+
// and check below
246249
}
247250
}
248251
if x.mode == invalid || y.mode == invalid {
@@ -261,7 +264,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
261264
return
262265
}
263266

264-
// if both arguments are constant, the result is a constant
267+
// if both arguments are constants, the result is a constant
265268
if x.mode == constant_ && y.mode == constant_ {
266269
x.val = constant.BinaryOp(x.val, token.ADD, constant.MakeImag(y.val))
267270
} else {
@@ -351,10 +354,35 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
351354
case _Imag, _Real:
352355
// imag(complexT) floatT
353356
// real(complexT) floatT
357+
358+
// convert or check untyped argument
359+
if isUntyped(x.typ) {
360+
if x.mode == constant_ {
361+
// an untyped constant number can alway be considered
362+
// as a complex constant
363+
if isNumeric(x.typ) {
364+
x.typ = Typ[UntypedComplex]
365+
}
366+
} else {
367+
// an untyped non-constant argument may appear if
368+
// it contains a (yet untyped non-constant) shift
369+
// epression: convert it to complex128 which will
370+
// result in an error (shift of complex value)
371+
check.convertUntyped(x, Typ[Complex128])
372+
// x should be invalid now, but be conservative and check
373+
if x.mode == invalid {
374+
return
375+
}
376+
}
377+
}
378+
379+
// the argument must be of complex type
354380
if !isComplex(x.typ) {
355-
check.invalidArg(x.pos(), "%s must be a complex number", x)
381+
check.invalidArg(x.pos(), "argument has type %s, expected complex type", x.typ)
356382
return
357383
}
384+
385+
// if the argument is a constant, the result is a constant
358386
if x.mode == constant_ {
359387
if id == _Real {
360388
x.val = constant.Real(x.val)
@@ -364,22 +392,26 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
364392
} else {
365393
x.mode = value
366394
}
367-
var k BasicKind
395+
396+
// determine result type
397+
var res BasicKind
368398
switch x.typ.Underlying().(*Basic).kind {
369399
case Complex64:
370-
k = Float32
400+
res = Float32
371401
case Complex128:
372-
k = Float64
402+
res = Float64
373403
case UntypedComplex:
374-
k = UntypedFloat
404+
res = UntypedFloat
375405
default:
376406
unreachable()
377407
}
408+
resTyp := Typ[res]
378409

379410
if check.Types != nil && x.mode != constant_ {
380-
check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ))
411+
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ))
381412
}
382-
x.typ = Typ[k]
413+
414+
x.typ = resTyp
383415

384416
case _Make:
385417
// make(T, n)

src/go/types/testdata/builtins.src

+28-11
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,10 @@ func imag1() {
307307
var c128 complex128
308308
_ = imag() // ERROR not enough arguments
309309
_ = imag(1, 2) // ERROR too many arguments
310-
_ = imag(10 /* ERROR must be a complex number */)
311-
_ = imag(2.7182818 /* ERROR must be a complex number */)
312-
_ = imag("foo" /* ERROR must be a complex number */)
310+
_ = imag(10)
311+
_ = imag(2.7182818)
312+
_ = imag("foo" /* ERROR expected complex */)
313+
_ = imag('a')
313314
const _5 = imag(1 + 2i)
314315
assert(_5 == 2)
315316
f32 = _5
@@ -331,8 +332,16 @@ func imag1() {
331332
f32 = imag(x64)
332333
f64 = imag(x128)
333334

334-
var s []complex64
335-
_ = imag(s... /* ERROR invalid use of \.\.\. */ )
335+
var a []complex64
336+
_ = imag(a... /* ERROR invalid use of \.\.\. */ )
337+
338+
// if argument is untyped, result is untyped
339+
const _ byte = imag(1.2 + 3i)
340+
const _ complex128 = imag(1.2 + 3i)
341+
342+
// lhs constant shift operands are typed as complex128
343+
var s uint
344+
_ = imag(1 /* ERROR must be integer */ << s)
336345
}
337346

338347
func imag2() {
@@ -579,9 +588,9 @@ func real1() {
579588
var c128 complex128
580589
_ = real() // ERROR not enough arguments
581590
_ = real(1, 2) // ERROR too many arguments
582-
_ = real(10 /* ERROR must be a complex number */)
583-
_ = real(2.7182818 /* ERROR must be a complex number */)
584-
_ = real("foo" /* ERROR must be a complex number */)
591+
_ = real(10)
592+
_ = real(2.7182818)
593+
_ = real("foo" /* ERROR expected complex */)
585594
const _5 = real(1 + 2i)
586595
assert(_5 == 1)
587596
f32 = _5
@@ -601,10 +610,18 @@ func real1() {
601610
var x128 C128
602611
f32 = imag(x64)
603612
f64 = imag(x128)
604-
605-
var s []complex64
606-
_ = real(s... /* ERROR invalid use of \.\.\. */ )
607613
_, _ = f32, f64
614+
615+
var a []complex64
616+
_ = real(a... /* ERROR invalid use of \.\.\. */ )
617+
618+
// if argument is untyped, result is untyped
619+
const _ byte = real(1 + 2.3i)
620+
const _ complex128 = real(1 + 2.3i)
621+
622+
// lhs constant shift operands are typed as complex128
623+
var s uint
624+
_ = real(1 /* ERROR must be integer */ << s)
608625
}
609626

610627
func real2() {

0 commit comments

Comments
 (0)