mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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 <alb.donizetti@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
49f4b5a4f5
commit
10a200e560
4 changed files with 149 additions and 4 deletions
|
|
@ -160,6 +160,37 @@ func truncfltlit(oldv *Mpflt, t *types.Type) *Mpflt {
|
||||||
return fv
|
return fv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// truncate Real and Imag parts of Mpcplx to 32-bit or 64-bit
|
||||||
|
// precision, according to type; return truncated value. In case of
|
||||||
|
// overflow, calls yyerror but does not truncate the input value.
|
||||||
|
func trunccmplxlit(oldv *Mpcplx, t *types.Type) *Mpcplx {
|
||||||
|
if t == nil {
|
||||||
|
return oldv
|
||||||
|
}
|
||||||
|
|
||||||
|
if overflow(Val{oldv}, t) {
|
||||||
|
// Avoid setting to Inf if there was an overflow. It's never
|
||||||
|
// useful, and it'll cause spourious and confusing 'constant Inf
|
||||||
|
// overflows float32' errors down the road.
|
||||||
|
return oldv
|
||||||
|
}
|
||||||
|
|
||||||
|
cv := newMpcmplx()
|
||||||
|
|
||||||
|
switch t.Etype {
|
||||||
|
case TCOMPLEX64:
|
||||||
|
cv.Real.SetFloat64(oldv.Real.Float32())
|
||||||
|
cv.Imag.SetFloat64(oldv.Imag.Float32())
|
||||||
|
case TCOMPLEX128:
|
||||||
|
cv.Real.SetFloat64(oldv.Real.Float64())
|
||||||
|
cv.Imag.SetFloat64(oldv.Imag.Float64())
|
||||||
|
default:
|
||||||
|
Fatalf("trunccplxlit: unexpected Etype %v", t.Etype)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cv
|
||||||
|
}
|
||||||
|
|
||||||
// canReuseNode indicates whether it is known to be safe
|
// canReuseNode indicates whether it is known to be safe
|
||||||
// to reuse a Node.
|
// to reuse a Node.
|
||||||
type canReuseNode bool
|
type canReuseNode bool
|
||||||
|
|
@ -361,7 +392,7 @@ func convlit1(n *Node, t *types.Type, explicit bool, reuse canReuseNode) *Node {
|
||||||
fallthrough
|
fallthrough
|
||||||
|
|
||||||
case CTCPLX:
|
case CTCPLX:
|
||||||
overflow(n.Val(), t)
|
n.SetVal(Val{trunccmplxlit(n.Val().U.(*Mpcplx), t)})
|
||||||
}
|
}
|
||||||
} else if et == types.TSTRING && (ct == CTINT || ct == CTRUNE) && explicit {
|
} else if et == types.TSTRING && (ct == CTINT || ct == CTRUNE) && explicit {
|
||||||
n.SetVal(tostr(n.Val()))
|
n.SetVal(tostr(n.Val()))
|
||||||
|
|
@ -519,21 +550,25 @@ func doesoverflow(v Val, t *types.Type) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func overflow(v Val, t *types.Type) {
|
func overflow(v Val, t *types.Type) bool {
|
||||||
// v has already been converted
|
// v has already been converted
|
||||||
// to appropriate form for t.
|
// to appropriate form for t.
|
||||||
if t == nil || t.Etype == TIDEAL {
|
if t == nil || t.Etype == TIDEAL {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only uintptrs may be converted to unsafe.Pointer, which cannot overflow.
|
// Only uintptrs may be converted to unsafe.Pointer, which cannot overflow.
|
||||||
if t.Etype == TUNSAFEPTR {
|
if t.Etype == TUNSAFEPTR {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if doesoverflow(v, t) {
|
if doesoverflow(v, t) {
|
||||||
yyerror("constant %v overflows %v", v, t)
|
yyerror("constant %v overflows %v", v, t)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func tostr(v Val) Val {
|
func tostr(v Val) Val {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,13 @@ func newMpflt() *Mpflt {
|
||||||
return &a
|
return &a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newMpcmplx() *Mpcplx {
|
||||||
|
var a Mpcplx
|
||||||
|
a.Real = *newMpflt()
|
||||||
|
a.Imag = *newMpflt()
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Mpflt) SetInt(b *Mpint) {
|
func (a *Mpflt) SetInt(b *Mpint) {
|
||||||
if b.checkOverflow(0) {
|
if b.checkOverflow(0) {
|
||||||
// sign doesn't really matter but copy anyway
|
// sign doesn't really matter but copy anyway
|
||||||
|
|
|
||||||
63
src/cmd/compile/internal/gc/truncconst_test.go
Normal file
63
src/cmd/compile/internal/gc/truncconst_test.go
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gc
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var f52want float64 = 1.0 / (1 << 52)
|
||||||
|
var f53want float64 = 1.0 / (1 << 53)
|
||||||
|
|
||||||
|
func TestTruncFlt(t *testing.T) {
|
||||||
|
const f52 = 1 + 1.0/(1<<52)
|
||||||
|
const f53 = 1 + 1.0/(1<<53)
|
||||||
|
|
||||||
|
if got := f52 - 1; got != f52want {
|
||||||
|
t.Errorf("f52-1 = %g, want %g", got, f52want)
|
||||||
|
}
|
||||||
|
if got := float64(f52) - 1; got != f52want {
|
||||||
|
t.Errorf("float64(f52)-1 = %g, want %g", got, f52want)
|
||||||
|
}
|
||||||
|
if got := f53 - 1; got != f53want {
|
||||||
|
t.Errorf("f53-1 = %g, want %g", got, f53want)
|
||||||
|
}
|
||||||
|
if got := float64(f53) - 1; got != 0 {
|
||||||
|
t.Errorf("float64(f53)-1 = %g, want 0", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTruncCmplx(t *testing.T) {
|
||||||
|
const r52 = complex(1+1.0/(1<<52), 0)
|
||||||
|
const r53 = complex(1+1.0/(1<<53), 0)
|
||||||
|
|
||||||
|
if got := real(r52 - 1); got != f52want {
|
||||||
|
t.Errorf("real(r52-1) = %g, want %g", got, f52want)
|
||||||
|
}
|
||||||
|
if got := real(complex128(r52) - 1); got != f52want {
|
||||||
|
t.Errorf("real(complex128(r52)-1) = %g, want %g", got, f52want)
|
||||||
|
}
|
||||||
|
if got := real(r53 - 1); got != f53want {
|
||||||
|
t.Errorf("real(r53-1) = %g, want %g", got, f53want)
|
||||||
|
}
|
||||||
|
if got := real(complex128(r53) - 1); got != 0 {
|
||||||
|
t.Errorf("real(complex128(r53)-1) = %g, want 0", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
const i52 = complex(0, 1+1.0/(1<<52))
|
||||||
|
const i53 = complex(0, 1+1.0/(1<<53))
|
||||||
|
|
||||||
|
if got := imag(i52 - 1i); got != f52want {
|
||||||
|
t.Errorf("imag(i52-1i) = %g, want %g", got, f52want)
|
||||||
|
}
|
||||||
|
if got := imag(complex128(i52) - 1i); got != f52want {
|
||||||
|
t.Errorf("imag(complex128(i52)-1i) = %g, want %g", got, f52want)
|
||||||
|
}
|
||||||
|
if got := imag(i53 - 1i); got != f53want {
|
||||||
|
t.Errorf("imag(i53-1i) = %g, want %g", got, f53want)
|
||||||
|
}
|
||||||
|
if got := imag(complex128(i53) - 1i); got != 0 {
|
||||||
|
t.Errorf("imag(complex128(i53)-1i) = %g, want 0", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
40
test/fixedbugs/issue11674.go
Normal file
40
test/fixedbugs/issue11674.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
// errorcheck
|
||||||
|
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Issue 11674: cmd/compile: does not diagnose constant division by
|
||||||
|
// zero
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
const x complex64 = 0
|
||||||
|
const y complex128 = 0
|
||||||
|
|
||||||
|
var _ = x / 1e-20
|
||||||
|
var _ = x / 1e-50 // ERROR "complex division by zero"
|
||||||
|
var _ = x / 1e-1000 // ERROR "complex division by zero"
|
||||||
|
var _ = x / 1e-20i
|
||||||
|
var _ = x / 1e-50i // ERROR "complex division by zero"
|
||||||
|
var _ = x / 1e-1000i // ERROR "complex division by zero"
|
||||||
|
|
||||||
|
var _ = x / 1e-45 // smallest positive float32
|
||||||
|
|
||||||
|
var _ = x / (1e-20 + 1e-20i)
|
||||||
|
var _ = x / (1e-50 + 1e-20i)
|
||||||
|
var _ = x / (1e-20 + 1e-50i)
|
||||||
|
var _ = x / (1e-50 + 1e-50i) // ERROR "complex division by zero"
|
||||||
|
var _ = x / (1e-1000 + 1e-1000i) // ERROR "complex division by zero"
|
||||||
|
|
||||||
|
var _ = y / 1e-50
|
||||||
|
var _ = y / 1e-1000 // ERROR "complex division by zero"
|
||||||
|
var _ = y / 1e-50i
|
||||||
|
var _ = y / 1e-1000i // ERROR "complex division by zero"
|
||||||
|
|
||||||
|
var _ = y / 5e-324 // smallest positive float64
|
||||||
|
|
||||||
|
var _ = y / (1e-50 + 1e-50)
|
||||||
|
var _ = y / (1e-1000 + 1e-50i)
|
||||||
|
var _ = y / (1e-50 + 1e-1000i)
|
||||||
|
var _ = y / (1e-1000 + 1e-1000i) // ERROR "complex division by zero"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue