Commit 9f6b21ca authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: fix ICE from invalid operations on float/complex constants

Typechecking treats all untyped numbers as integers for the purposes
of validating operators. However, when I refactoring constant
operation evalution in golang.org/cl/139901, I mistakenly interpreted
that the only invalid case that needed to be preserved was % (modulo)
on floating-point values.

This CL restores the other remaining cases that were dropped from that
CL. It also uses the phrasing "invalid operation" instead of "illegal
constant expression" for better consistency with the rest of
cmd/compile and with go/types.

Lastly, this CL extends setconst to recognize failed constant folding
(e.g., division by zero) so that we can properly mark those
expressions as broken rather than continuing forward with bogus values
that might lead to further spurious errors.

Fixes #31060.

Change-Id: I1ab6491371925e22bc8b95649f1a0eed010abca6
Reviewed-on: https://go-review.googlesource.com/c/go/+/169719
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent c1e60b6e
...@@ -838,15 +838,13 @@ Outer: ...@@ -838,15 +838,13 @@ Outer:
case ODIV: case ODIV:
if y.CmpInt64(0) == 0 { if y.CmpInt64(0) == 0 {
yyerror("division by zero") yyerror("division by zero")
u.SetOverflow() return Val{}
break
} }
u.Quo(y) u.Quo(y)
case OMOD: case OMOD:
if y.CmpInt64(0) == 0 { if y.CmpInt64(0) == 0 {
yyerror("division by zero") yyerror("division by zero")
u.SetOverflow() return Val{}
break
} }
u.Rem(y) u.Rem(y)
case OOR: case OOR:
...@@ -877,13 +875,13 @@ Outer: ...@@ -877,13 +875,13 @@ Outer:
case ODIV: case ODIV:
if y.CmpFloat64(0) == 0 { if y.CmpFloat64(0) == 0 {
yyerror("division by zero") yyerror("division by zero")
u.SetFloat64(1) return Val{}
break
} }
u.Quo(y) u.Quo(y)
case OMOD: case OMOD, OOR, OAND, OANDNOT, OXOR:
// TODO(mdempsky): Move to typecheck. // TODO(mdempsky): Move to typecheck; see #31060.
yyerror("illegal constant expression: floating-point %% operation") yyerror("invalid operation: operator %v not defined on untyped float", op)
return Val{}
default: default:
break Outer break Outer
} }
...@@ -907,9 +905,12 @@ Outer: ...@@ -907,9 +905,12 @@ Outer:
case ODIV: case ODIV:
if !u.Div(y) { if !u.Div(y) {
yyerror("complex division by zero") yyerror("complex division by zero")
u.Real.SetFloat64(1) return Val{}
u.Imag.SetFloat64(0)
} }
case OMOD, OOR, OAND, OANDNOT, OXOR:
// TODO(mdempsky): Move to typecheck; see #31060.
yyerror("invalid operation: operator %v not defined on untyped complex", op)
return Val{}
default: default:
break Outer break Outer
} }
...@@ -956,19 +957,31 @@ func unaryOp(op Op, x Val, t *types.Type) Val { ...@@ -956,19 +957,31 @@ func unaryOp(op Op, x Val, t *types.Type) Val {
} }
case OBITNOT: case OBITNOT:
x := x.U.(*Mpint) switch x.Ctype() {
case CTINT, CTRUNE:
x := x.U.(*Mpint)
u := new(Mpint) u := new(Mpint)
u.Rune = x.Rune u.Rune = x.Rune
if t.IsSigned() || t.IsUntyped() { if t.IsSigned() || t.IsUntyped() {
// Signed values change sign. // Signed values change sign.
u.SetInt64(-1) u.SetInt64(-1)
} else { } else {
// Unsigned values invert their bits. // Unsigned values invert their bits.
u.Set(maxintval[t.Etype]) u.Set(maxintval[t.Etype])
}
u.Xor(x)
return Val{U: u}
case CTFLT:
// TODO(mdempsky): Move to typecheck; see #31060.
yyerror("invalid operation: operator %v not defined on untyped float", op)
return Val{}
case CTCPLX:
// TODO(mdempsky): Move to typecheck; see #31060.
yyerror("invalid operation: operator %v not defined on untyped complex", op)
return Val{}
} }
u.Xor(x)
return Val{U: u}
case ONOT: case ONOT:
return Val{U: !x.U.(bool)} return Val{U: !x.U.(bool)}
...@@ -1001,6 +1014,12 @@ func shiftOp(x Val, op Op, y Val) Val { ...@@ -1001,6 +1014,12 @@ func shiftOp(x Val, op Op, y Val) Val {
// setconst rewrites n as an OLITERAL with value v. // setconst rewrites n as an OLITERAL with value v.
func setconst(n *Node, v Val) { func setconst(n *Node, v Val) {
// If constant folding failed, mark n as broken and give up.
if v.U == nil {
n.Type = nil
return
}
// Ensure n.Orig still points to a semantically-equivalent // Ensure n.Orig still points to a semantically-equivalent
// expression after we rewrite n into a constant. // expression after we rewrite n into a constant.
if n.Orig == n { if n.Orig == n {
......
...@@ -68,7 +68,7 @@ var ( ...@@ -68,7 +68,7 @@ var (
c3 float64 = float64(Big) * Big // ERROR "overflow" c3 float64 = float64(Big) * Big // ERROR "overflow"
c4 = Big * Big // ERROR "overflow" c4 = Big * Big // ERROR "overflow"
c5 = Big / 0 // ERROR "division by zero" c5 = Big / 0 // ERROR "division by zero"
c6 = 1000 % 1e3 // ERROR "floating-point % operation|expected integer type" c6 = 1000 % 1e3 // ERROR "invalid operation|expected integer type"
) )
func f(int) func f(int)
......
// errorcheck
// Copyright 2019 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 p
const (
f = 1.0
c = 1.0i
_ = ^f // ERROR "invalid operation|expected integer"
_ = ^c // ERROR "invalid operation|expected integer"
_ = f % f // ERROR "invalid operation|expected integer"
_ = c % c // ERROR "invalid operation|expected integer"
_ = f & f // ERROR "invalid operation|expected integer"
_ = c & c // ERROR "invalid operation|expected integer"
_ = f | f // ERROR "invalid operation|expected integer"
_ = c | c // ERROR "invalid operation|expected integer"
_ = f ^ f // ERROR "invalid operation|expected integer"
_ = c ^ c // ERROR "invalid operation|expected integer"
_ = f &^ f // ERROR "invalid operation|expected integer"
_ = c &^ c // ERROR "invalid operation|expected integer"
)
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