golang/go

cmd/compile: does not diagnose constant division by zero

dvyukov opened this issue · 4 comments

gc produces an error for c and d, but not for a and b:

package a
var a = complex64(0)/1e-1000
var b = complex64(0)/1e-47
var c = float32(0)/1e-1000
var d = float32(0)/1e-47
go.go:4: division by zero
go.go:5: division by zero

gotype produces errors for all 4 variables.

go version devel +9b04852 Sat Jul 11 00:08:50 2015 +0000 linux/amd64

The spec is not super-clear, but this appears to be a compiler bug: The untyped constants (1e-47, 1e-1000) are converted to float32/complex64 values before the operation, and the conversion results in silent underflow to 0, hence the div-by-zero error.

For comparison, in

package main
const _ = int8(-128) + 128

( http://play.golang.org/p/YrUx9sSX3w ) the compiler does exactly that: it converts the untyped 128 into an int8 which is not possible and thus returns an error.

Analysis: in convlit1 function inside const.go. What happens for a float constant is that the node is converted to float using

n.SetVal(Val{truncfltlit(n.Val().U.(*Mpflt), t)})

Note the call to truncfltlit, that truncates a float literal to float32 or float64 precision. For the above snippet, both the denominators get truncated to 0. After that we enter the evconst function, and the following check fails

mpcmpfltc(rv.U.(*Mpflt), 0) == 0

here, because rv is 0, and the compiler calls Yyerror("division by zero").

For complex values, the compiler executes

n.SetVal(tocplx(n.Val()))

unfortunately, the tocplx function does not truncate float values. It just does

mpmovefltflt(&c.Real, v.U.(*Mpflt))

i.e. it moves the value of the real part to c. For that reason evconst actually gets 1e-1000, so the following check

mpcmpfltc(&rv.U.(*Mpcplx).Real, 0) == 0 && mpcmpfltc(&rv.U.(*Mpcplx).Imag, 0) == 0

here passes, because rv (the real part) in not zero, and the

Yyerror("complex division by zero")

lines is not called.

I have a simple patch for this (we just need to call truncfltlit after the tocplx call in convlit1) that passes all.bash. I will email it if nobody else already have a CL for this.

CL https://golang.org/cl/37891 mentions this issue.