Skip to content

Commit

Permalink
cmd/compile: implement slice-to-array conversions
Browse files Browse the repository at this point in the history
The conversion T(x) is implemented as *(*T)(x). Accordingly, runtime
panic messages for (*T)(x) are made more general.

Fixes #46505.

Change-Id: I76317c0878b6a5908299506d392eed50d7ef6523
Reviewed-on: https://go-review.googlesource.com/c/go/+/430415
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Jenny Rakoczy <jenny@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
  • Loading branch information
mdempsky committed Sep 19, 2022
1 parent 29153be commit ceffdc8
Show file tree
Hide file tree
Showing 15 changed files with 210 additions and 148 deletions.
4 changes: 4 additions & 0 deletions src/cmd/compile/internal/escape/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
case ir.OITAB, ir.OIDATA, ir.OSPTR:
n := n.(*ir.UnaryExpr)
e.expr(k, n.X)
case ir.OSLICE2ARR:
// Converting a slice to array is effectively a deref.
n := n.(*ir.ConvExpr)
e.expr(k.deref(n, "slice-to-array"), n.X)
case ir.OSLICE2ARRPTR:
// the slice pointer flows directly to the result
n := n.(*ir.ConvExpr)
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/compile/internal/ir/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ func (n *ConvExpr) SetOp(op Op) {
switch op {
default:
panic(n.no("SetOp " + op.String()))
case OCONV, OCONVIFACE, OCONVIDATA, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR, OSLICE2ARRPTR:
case OCONV, OCONVIFACE, OCONVIDATA, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR, OSLICE2ARR, OSLICE2ARRPTR:
n.op = op
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/cmd/compile/internal/ir/fmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ var OpPrec = []int{
OPRINT: 8,
ORUNESTR: 8,
OSIZEOF: 8,
OSLICE2ARR: 8,
OSLICE2ARRPTR: 8,
OSTR2BYTES: 8,
OSTR2RUNES: 8,
Expand Down Expand Up @@ -753,6 +754,7 @@ func exprFmt(n Node, s fmt.State, prec int) {
OSTR2BYTES,
OSTR2RUNES,
ORUNESTR,
OSLICE2ARR,
OSLICE2ARRPTR:
n := n.(*ConvExpr)
if n.Type() == nil || n.Type().Sym() == nil {
Expand Down
1 change: 1 addition & 0 deletions src/cmd/compile/internal/ir/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const (
OSTR2BYTES // Type(X) (Type is []byte, X is a string)
OSTR2BYTESTMP // Type(X) (Type is []byte, X is a string, ephemeral)
OSTR2RUNES // Type(X) (Type is []rune, X is a string)
OSLICE2ARR // Type(X) (Type is [N]T, X is a []T)
OSLICE2ARRPTR // Type(X) (Type is *[N]T, X is a []T)
// X = Y or (if Def=true) X := Y
// If Def, then Init includes a DCL node for X.
Expand Down
273 changes: 137 additions & 136 deletions src/cmd/compile/internal/ir/op_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/cmd/compile/internal/typecheck/iexport.go
Original file line number Diff line number Diff line change
Expand Up @@ -1974,7 +1974,7 @@ func (w *exportWriter) expr(n ir.Node) {
w.expr(n.Y)
w.typ(n.Type())

case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR:
case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARR, ir.OSLICE2ARRPTR:
n := n.(*ir.ConvExpr)
w.op(n.Op())
w.pos(n.Pos())
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/compile/internal/typecheck/iimport.go
Original file line number Diff line number Diff line change
Expand Up @@ -1484,7 +1484,7 @@ func (r *importReader) node() ir.Node {
n.SetType(r.typ())
return n

case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR:
case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARR, ir.OSLICE2ARRPTR:
n := ir.NewConvExpr(r.pos(), op, r.typ(), r.expr())
n.SetImplicit(r.bool())
return n
Expand Down
13 changes: 9 additions & 4 deletions src/cmd/compile/internal/typecheck/subr.go
Original file line number Diff line number Diff line change
Expand Up @@ -579,11 +579,16 @@ func Convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) {
return ir.OCONVNOP, ""
}

// 11. src is a slice and dst is a pointer-to-array.
// 11. src is a slice and dst is an array or pointer-to-array.
// They must have same element type.
if src.IsSlice() && dst.IsPtr() && dst.Elem().IsArray() &&
types.Identical(src.Elem(), dst.Elem().Elem()) {
return ir.OSLICE2ARRPTR, ""
if src.IsSlice() {
if dst.IsArray() && types.Identical(src.Elem(), dst.Elem()) {
return ir.OSLICE2ARR, ""
}
if dst.IsPtr() && dst.Elem().IsArray() &&
types.Identical(src.Elem(), dst.Elem().Elem()) {
return ir.OSLICE2ARRPTR, ""
}
}

return ir.OXXX, ""
Expand Down
18 changes: 18 additions & 0 deletions src/cmd/compile/internal/walk/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,3 +503,21 @@ func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {

return cheap
}

// walkSliceToArray walks an OSLICE2ARR expression.
func walkSliceToArray(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
// Replace T(x) with *(*T)(x).
conv := typecheck.Expr(ir.NewConvExpr(base.Pos, ir.OCONV, types.NewPtr(n.Type()), n.X)).(*ir.ConvExpr)
deref := typecheck.Expr(ir.NewStarExpr(base.Pos, conv)).(*ir.StarExpr)

// The OSLICE2ARRPTR conversion handles checking the slice length,
// so the dereference can't fail.
//
// However, this is more than just an optimization: if T is a
// zero-length array, then x (and thus (*T)(x)) can be nil, but T(x)
// should *not* panic. So suppressing the nil check here is
// necessary for correctness in that case.
deref.SetBounded(true)

return walkExpr(deref, init)
}
4 changes: 4 additions & 0 deletions src/cmd/compile/internal/walk/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
n := n.(*ir.ConvExpr)
return walkConv(n, init)

case ir.OSLICE2ARR:
n := n.(*ir.ConvExpr)
return walkSliceToArray(n, init)

case ir.OSLICE2ARRPTR:
n := n.(*ir.ConvExpr)
n.X = walkExpr(n.X, init)
Expand Down
Loading

0 comments on commit ceffdc8

Please sign in to comment.