From 336626bac4c62b617127d41dccae17eed0350b0f Mon Sep 17 00:00:00 2001 From: Keith Randall <khr@golang.org> Date: Fri, 14 Mar 2025 13:36:58 -0700 Subject: [PATCH] cmd/compile: ensure we evaluate side effects of len() arg For any len() which requires the evaluation of its arg (according to the spec). Update #72844 Change-Id: Id2b0bcc78073a6d5051abd000131dafdf65e7f26 Reviewed-on: https://go-review.googlesource.com/c/go/+/658097 Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Keith Randall <khr@google.com> --- src/cmd/compile/internal/ssagen/ssa.go | 25 ++++++++++++++++-------- src/cmd/compile/internal/walk/builtin.go | 5 +++-- test/fixedbugs/issue72844.go | 4 ++-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 80e91436bbf..0b77a1334ff 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -3469,19 +3469,28 @@ func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value { case ir.OLEN, ir.OCAP: n := n.(*ir.UnaryExpr) + // Note: all constant cases are handled by the frontend. If len or cap + // makes it here, we want the side effects of the argument. See issue 72844. + a := s.expr(n.X) + t := n.X.Type() switch { - case n.X.Type().IsSlice(): + case t.IsSlice(): op := ssa.OpSliceLen if n.Op() == ir.OCAP { op = ssa.OpSliceCap } - return s.newValue1(op, types.Types[types.TINT], s.expr(n.X)) - case n.X.Type().IsString(): // string; not reachable for OCAP - return s.newValue1(ssa.OpStringLen, types.Types[types.TINT], s.expr(n.X)) - case n.X.Type().IsMap(), n.X.Type().IsChan(): - return s.referenceTypeBuiltin(n, s.expr(n.X)) - default: // array - return s.constInt(types.Types[types.TINT], n.X.Type().NumElem()) + return s.newValue1(op, types.Types[types.TINT], a) + case t.IsString(): // string; not reachable for OCAP + return s.newValue1(ssa.OpStringLen, types.Types[types.TINT], a) + case t.IsMap(), t.IsChan(): + return s.referenceTypeBuiltin(n, a) + case t.IsArray(): + return s.constInt(types.Types[types.TINT], t.NumElem()) + case t.IsPtr() && t.Elem().IsArray(): + return s.constInt(types.Types[types.TINT], t.Elem().NumElem()) + default: + s.Fatalf("bad type in len/cap: %v", t) + return nil } case ir.OSPTR: diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go index 2e13daf8797..99cf2d784d8 100644 --- a/src/cmd/compile/internal/walk/builtin.go +++ b/src/cmd/compile/internal/walk/builtin.go @@ -278,12 +278,13 @@ func walkLenCap(n *ir.UnaryExpr, init *ir.Nodes) ir.Node { // replace len(*[10]int) with 10. // delayed until now to preserve side effects. t := n.X.Type() - if t.IsPtr() { t = t.Elem() } if t.IsArray() { - safeExpr(n.X, init) + // evaluate any side effects in n.X. See issue 72844. + appendWalkStmt(init, ir.NewAssignStmt(base.Pos, ir.BlankNode, n.X)) + con := ir.NewConstExpr(constant.MakeInt64(t.NumElem()), n) con.SetTypecheck(1) return con diff --git a/test/fixedbugs/issue72844.go b/test/fixedbugs/issue72844.go index 0322841ded1..65f1d342753 100644 --- a/test/fixedbugs/issue72844.go +++ b/test/fixedbugs/issue72844.go @@ -47,11 +47,11 @@ func testRange4() { } func main() { - //shouldPanic(testLen1) + shouldPanic(testLen1) shouldNotPanic(testLen2) shouldNotPanic(testLen3) shouldNotPanic(testLen4) - //shouldPanic(testRange1) + shouldPanic(testRange1) shouldNotPanic(testRange2) shouldNotPanic(testRange3) shouldNotPanic(testRange4) -- GitLab