Skip to content

Commit 10a200e

Browse files
ALTreegriesemer
authored andcommitted
cmd/compile: diagnose constant division by complex zero
When casting an ideal to complex{64,128}, for example during the evaluation of var a = complex64(0) / 1e-50 we want the compiler to report a division-by-zero error if a divisor would be zero after the cast. We already do this for floats; for example var b = float32(0) / 1e-50 generates a 'division by zero' error at compile time (because float32(1e-50) is zero, and the cast is done before performing the division). There's no such check in the path for complex{64,128} expressions, and no cast is performed before the division in the evaluation of var a = complex64(0) / 1e-50 which compiles just fine. This patch changes the convlit1 function so that complex ideals components (real and imag) are correctly truncated to float{32,64} when doing an ideal -> complex{64, 128} cast. Fixes #11674 Change-Id: Ic5f8ee3c8cfe4c3bb0621481792c96511723d151 Reviewed-on: https://go-review.googlesource.com/37891 Run-TryBot: Alberto Donizetti <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 49f4b5a commit 10a200e

File tree

4 files changed

+149
-4
lines changed

4 files changed

+149
-4
lines changed

src/cmd/compile/internal/gc/const.go

+39-4
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,37 @@ func truncfltlit(oldv *Mpflt, t *types.Type) *Mpflt {
160160
return fv
161161
}
162162

163+
// truncate Real and Imag parts of Mpcplx to 32-bit or 64-bit
164+
// precision, according to type; return truncated value. In case of
165+
// overflow, calls yyerror but does not truncate the input value.
166+
func trunccmplxlit(oldv *Mpcplx, t *types.Type) *Mpcplx {
167+
if t == nil {
168+
return oldv
169+
}
170+
171+
if overflow(Val{oldv}, t) {
172+
// Avoid setting to Inf if there was an overflow. It's never
173+
// useful, and it'll cause spourious and confusing 'constant Inf
174+
// overflows float32' errors down the road.
175+
return oldv
176+
}
177+
178+
cv := newMpcmplx()
179+
180+
switch t.Etype {
181+
case TCOMPLEX64:
182+
cv.Real.SetFloat64(oldv.Real.Float32())
183+
cv.Imag.SetFloat64(oldv.Imag.Float32())
184+
case TCOMPLEX128:
185+
cv.Real.SetFloat64(oldv.Real.Float64())
186+
cv.Imag.SetFloat64(oldv.Imag.Float64())
187+
default:
188+
Fatalf("trunccplxlit: unexpected Etype %v", t.Etype)
189+
}
190+
191+
return cv
192+
}
193+
163194
// canReuseNode indicates whether it is known to be safe
164195
// to reuse a Node.
165196
type canReuseNode bool
@@ -361,7 +392,7 @@ func convlit1(n *Node, t *types.Type, explicit bool, reuse canReuseNode) *Node {
361392
fallthrough
362393

363394
case CTCPLX:
364-
overflow(n.Val(), t)
395+
n.SetVal(Val{trunccmplxlit(n.Val().U.(*Mpcplx), t)})
365396
}
366397
} else if et == types.TSTRING && (ct == CTINT || ct == CTRUNE) && explicit {
367398
n.SetVal(tostr(n.Val()))
@@ -519,21 +550,25 @@ func doesoverflow(v Val, t *types.Type) bool {
519550
return false
520551
}
521552

522-
func overflow(v Val, t *types.Type) {
553+
func overflow(v Val, t *types.Type) bool {
523554
// v has already been converted
524555
// to appropriate form for t.
525556
if t == nil || t.Etype == TIDEAL {
526-
return
557+
return false
527558
}
528559

529560
// Only uintptrs may be converted to unsafe.Pointer, which cannot overflow.
530561
if t.Etype == TUNSAFEPTR {
531-
return
562+
return false
532563
}
533564

534565
if doesoverflow(v, t) {
535566
yyerror("constant %v overflows %v", v, t)
567+
return true
536568
}
569+
570+
return false
571+
537572
}
538573

539574
func tostr(v Val) Val {

src/cmd/compile/internal/gc/mpfloat.go

+7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ func newMpflt() *Mpflt {
3737
return &a
3838
}
3939

40+
func newMpcmplx() *Mpcplx {
41+
var a Mpcplx
42+
a.Real = *newMpflt()
43+
a.Imag = *newMpflt()
44+
return &a
45+
}
46+
4047
func (a *Mpflt) SetInt(b *Mpint) {
4148
if b.checkOverflow(0) {
4249
// sign doesn't really matter but copy anyway
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2017 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package gc
6+
7+
import "testing"
8+
9+
var f52want float64 = 1.0 / (1 << 52)
10+
var f53want float64 = 1.0 / (1 << 53)
11+
12+
func TestTruncFlt(t *testing.T) {
13+
const f52 = 1 + 1.0/(1<<52)
14+
const f53 = 1 + 1.0/(1<<53)
15+
16+
if got := f52 - 1; got != f52want {
17+
t.Errorf("f52-1 = %g, want %g", got, f52want)
18+
}
19+
if got := float64(f52) - 1; got != f52want {
20+
t.Errorf("float64(f52)-1 = %g, want %g", got, f52want)
21+
}
22+
if got := f53 - 1; got != f53want {
23+
t.Errorf("f53-1 = %g, want %g", got, f53want)
24+
}
25+
if got := float64(f53) - 1; got != 0 {
26+
t.Errorf("float64(f53)-1 = %g, want 0", got)
27+
}
28+
}
29+
30+
func TestTruncCmplx(t *testing.T) {
31+
const r52 = complex(1+1.0/(1<<52), 0)
32+
const r53 = complex(1+1.0/(1<<53), 0)
33+
34+
if got := real(r52 - 1); got != f52want {
35+
t.Errorf("real(r52-1) = %g, want %g", got, f52want)
36+
}
37+
if got := real(complex128(r52) - 1); got != f52want {
38+
t.Errorf("real(complex128(r52)-1) = %g, want %g", got, f52want)
39+
}
40+
if got := real(r53 - 1); got != f53want {
41+
t.Errorf("real(r53-1) = %g, want %g", got, f53want)
42+
}
43+
if got := real(complex128(r53) - 1); got != 0 {
44+
t.Errorf("real(complex128(r53)-1) = %g, want 0", got)
45+
}
46+
47+
const i52 = complex(0, 1+1.0/(1<<52))
48+
const i53 = complex(0, 1+1.0/(1<<53))
49+
50+
if got := imag(i52 - 1i); got != f52want {
51+
t.Errorf("imag(i52-1i) = %g, want %g", got, f52want)
52+
}
53+
if got := imag(complex128(i52) - 1i); got != f52want {
54+
t.Errorf("imag(complex128(i52)-1i) = %g, want %g", got, f52want)
55+
}
56+
if got := imag(i53 - 1i); got != f53want {
57+
t.Errorf("imag(i53-1i) = %g, want %g", got, f53want)
58+
}
59+
if got := imag(complex128(i53) - 1i); got != 0 {
60+
t.Errorf("imag(complex128(i53)-1i) = %g, want 0", got)
61+
}
62+
63+
}

test/fixedbugs/issue11674.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// errorcheck
2+
3+
// Copyright 2017 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Issue 11674: cmd/compile: does not diagnose constant division by
8+
// zero
9+
10+
package p
11+
12+
const x complex64 = 0
13+
const y complex128 = 0
14+
15+
var _ = x / 1e-20
16+
var _ = x / 1e-50 // ERROR "complex division by zero"
17+
var _ = x / 1e-1000 // ERROR "complex division by zero"
18+
var _ = x / 1e-20i
19+
var _ = x / 1e-50i // ERROR "complex division by zero"
20+
var _ = x / 1e-1000i // ERROR "complex division by zero"
21+
22+
var _ = x / 1e-45 // smallest positive float32
23+
24+
var _ = x / (1e-20 + 1e-20i)
25+
var _ = x / (1e-50 + 1e-20i)
26+
var _ = x / (1e-20 + 1e-50i)
27+
var _ = x / (1e-50 + 1e-50i) // ERROR "complex division by zero"
28+
var _ = x / (1e-1000 + 1e-1000i) // ERROR "complex division by zero"
29+
30+
var _ = y / 1e-50
31+
var _ = y / 1e-1000 // ERROR "complex division by zero"
32+
var _ = y / 1e-50i
33+
var _ = y / 1e-1000i // ERROR "complex division by zero"
34+
35+
var _ = y / 5e-324 // smallest positive float64
36+
37+
var _ = y / (1e-50 + 1e-50)
38+
var _ = y / (1e-1000 + 1e-50i)
39+
var _ = y / (1e-50 + 1e-1000i)
40+
var _ = y / (1e-1000 + 1e-1000i) // ERROR "complex division by zero"

0 commit comments

Comments
 (0)