Skip to content

Commit

Permalink
[release-branch.go1.20] cmd/compile: do not report division by error …
Browse files Browse the repository at this point in the history
…during typecheck

types2 have already errored about any spec-required overflows, and
division by zero. CL 469595 unintentionally fixed typecheck not to error
about overflows, but zero division is still be checked during tcArith.
This causes unsafe operations with variable size failed to compile,
instead of raising runtime error.

This CL also making change to typecheck.EvalConst, to stop evaluating
literal shifts or binary operators where {over,under}flows can happen.
For go1.21, typecheck.EvalConst is removed entirely, but that change is
too large to backport.

See discussion in CL 501735 for more details.

Fixes #60675

Change-Id: I7bea2821099556835c920713397f7c5d8a4025ac
Reviewed-on: https://go-review.googlesource.com/c/go/+/501735
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/503855
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
  • Loading branch information
cuonglm authored and gopherbot committed Jun 19, 2023
1 parent 95f377d commit 63ad2b5
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 43 deletions.
38 changes: 2 additions & 36 deletions src/cmd/compile/internal/typecheck/const.go
Expand Up @@ -367,29 +367,7 @@ func EvalConst(n ir.Node) ir.Node {
}

case ir.OADD, ir.OSUB, ir.OMUL, ir.ODIV, ir.OMOD, ir.OOR, ir.OXOR, ir.OAND, ir.OANDNOT:
n := n.(*ir.BinaryExpr)
nl, nr := n.X, n.Y
if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
rval := nr.Val()

// check for divisor underflow in complex division (see issue 20227)
if n.Op() == ir.ODIV && n.Type().IsComplex() && constant.Sign(square(constant.Real(rval))) == 0 && constant.Sign(square(constant.Imag(rval))) == 0 {
base.Errorf("complex division by zero")
n.SetType(nil)
return n
}
if (n.Op() == ir.ODIV || n.Op() == ir.OMOD) && constant.Sign(rval) == 0 {
base.Errorf("division by zero")
n.SetType(nil)
return n
}

tok := tokenForOp[n.Op()]
if n.Op() == ir.ODIV && n.Type().IsInteger() {
tok = token.QUO_ASSIGN // integer division
}
return OrigConst(n, constant.BinaryOp(nl.Val(), tok, rval))
}
return n

case ir.OOROR, ir.OANDAND:
n := n.(*ir.LogicalExpr)
Expand All @@ -406,19 +384,7 @@ func EvalConst(n ir.Node) ir.Node {
}

case ir.OLSH, ir.ORSH:
n := n.(*ir.BinaryExpr)
nl, nr := n.X, n.Y
if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
// shiftBound from go/types; "so we can express smallestFloat64" (see issue #44057)
const shiftBound = 1023 - 1 + 52
s, ok := constant.Uint64Val(nr.Val())
if !ok || s > shiftBound {
base.Errorf("invalid shift count %v", nr)
n.SetType(nil)
break
}
return OrigConst(n, constant.Shift(toint(nl.Val()), tokenForOp[n.Op()], uint(s)))
}
return n

case ir.OCONV, ir.ORUNESTR:
n := n.(*ir.ConvExpr)
Expand Down
7 changes: 0 additions & 7 deletions src/cmd/compile/internal/typecheck/expr.go
Expand Up @@ -184,13 +184,6 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type)
}
}

if (op == ir.ODIV || op == ir.OMOD) && ir.IsConst(r, constant.Int) {
if constant.Sign(r.Val()) == 0 {
base.Errorf("division by zero")
return l, r, nil
}
}

return l, r, t
}

Expand Down
50 changes: 50 additions & 0 deletions test/fixedbugs/issue60601.go
@@ -0,0 +1,50 @@
// run

// Copyright 2023 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 main

import (
"strings"
"unsafe"
)

func shift[T any]() int64 {
return 1 << unsafe.Sizeof(*new(T))
}

func div[T any]() uintptr {
return 1 / unsafe.Sizeof(*new(T))
}

func add[T any]() int64 {
return 1<<63 - 1 + int64(unsafe.Sizeof(*new(T)))
}

func main() {
shift[[62]byte]()
shift[[63]byte]()
shift[[64]byte]()
shift[[100]byte]()
shift[[1e6]byte]()

add[[1]byte]()
shouldPanic("divide by zero", func() { div[[0]byte]() })
}

func shouldPanic(str string, f func()) {
defer func() {
err := recover()
if err == nil {
panic("did not panic")
}
s := err.(error).Error()
if !strings.Contains(s, str) {
panic("got panic " + s + ", want " + str)
}
}()

f()
}

0 comments on commit 63ad2b5

Please sign in to comment.