case OSLICE, OSLICESTR, OSLICEARR:
p.op(OSLICE)
p.expr(n.Left)
- p.expr(n.Right)
+ low, high, _ := n.SliceBounds()
+ p.exprsOrNil(low, high)
case OSLICE3, OSLICE3ARR:
p.op(OSLICE3)
p.expr(n.Left)
- p.expr(n.Right)
+ low, high, max := n.SliceBounds()
+ p.exprsOrNil(low, high)
+ p.expr(max)
case OCOPY, OCOMPLEX:
p.op(op)
// case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
// unreachable - mapped to cases below by exporter
- case OINDEX, OSLICE, OSLICE3:
+ case OINDEX:
return Nod(op, p.expr(), p.expr())
+ case OSLICE, OSLICE3:
+ n := Nod(op, p.expr(), nil)
+ low, high := p.exprsOrNil()
+ var max *Node
+ if n.Op.IsSlice3() {
+ max = p.expr()
+ }
+ n.SetSliceBounds(low, high, max)
+ return n
+
case OCOPY, OCOMPLEX:
n := builtinCall(op)
n.List.Set([]*Node{p.expr(), p.expr()})
x.Xoffset -= 2 * int64(Widthptr)
}
- var x1, x2, x3 *Node // unevaluated index arguments
- x1 = n.Right.Left
- switch n.Op {
- default:
- x2 = n.Right.Right
- case OSLICE3, OSLICE3ARR:
- x2 = n.Right.Right.Left
- x3 = n.Right.Right.Right
- }
+ x1, x2, x3 := n.SliceBounds() // unevaluated index arguments
// load computes src into targ, but if src refers to the len or cap of n.Left,
// load copies those from xlen, xcap, loading xlen if needed.
f += fmt.Sprintf(".(%v)", n.Type)
return f
- case OINDEX,
- OINDEXMAP,
- OSLICE,
- OSLICESTR,
- OSLICEARR,
- OSLICE3,
- OSLICE3ARR:
- var f string
- f += exprfmt(n.Left, nprec)
- f += fmt.Sprintf("[%v]", n.Right)
- return f
+ case OINDEX, OINDEXMAP:
+ return fmt.Sprintf("%s[%v]", exprfmt(n.Left, nprec), n.Right)
+
+ case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:
+ var buf bytes.Buffer
+ buf.WriteString(exprfmt(n.Left, nprec))
+ buf.WriteString("[")
+ low, high, max := n.SliceBounds()
+ if low != nil {
+ buf.WriteString(low.String())
+ }
+ buf.WriteString(":")
+ if high != nil {
+ buf.WriteString(high.String())
+ }
+ if n.Op.IsSlice3() {
+ buf.WriteString(":")
+ if max != nil {
+ buf.WriteString(max.String())
+ }
+ }
+ buf.WriteString("]")
+ return buf.String()
case OCOPY, OCOMPLEX:
return fmt.Sprintf("%v(%v, %v)", Oconv(n.Op, FmtSharp), n.Left, n.Right)
vararrtype := typArray(varargtype.Elem(), int64(varargcount))
as.Right = Nod(OCOMPLIT, nil, typenod(vararrtype))
as.Right.List.Set(varargs)
- as.Right = Nod(OSLICE, as.Right, Nod(OKEY, nil, nil))
+ as.Right = Nod(OSLICE, as.Right, nil)
}
as = typecheck(as, Etop)
n = ordercopyexpr(n, n.Type, order, 0)
}
- case OSLICE, OSLICEARR, OSLICESTR:
+ case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
n.Left = orderexpr(n.Left, order, nil)
- n.Right.Left = orderexpr(n.Right.Left, order, nil)
- n.Right.Left = ordercheapexpr(n.Right.Left, order)
- n.Right.Right = orderexpr(n.Right.Right, order, nil)
- n.Right.Right = ordercheapexpr(n.Right.Right, order)
- if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
- n = ordercopyexpr(n, n.Type, order, 0)
- }
-
- case OSLICE3, OSLICE3ARR:
- n.Left = orderexpr(n.Left, order, nil)
- n.Right.Left = orderexpr(n.Right.Left, order, nil)
- n.Right.Left = ordercheapexpr(n.Right.Left, order)
- n.Right.Right.Left = orderexpr(n.Right.Right.Left, order, nil)
- n.Right.Right.Left = ordercheapexpr(n.Right.Right.Left, order)
- n.Right.Right.Right = orderexpr(n.Right.Right.Right, order, nil)
- n.Right.Right.Right = ordercheapexpr(n.Right.Right.Right, order)
+ low, high, max := n.SliceBounds()
+ low = orderexpr(low, order, nil)
+ low = ordercheapexpr(low, order)
+ high = orderexpr(high, order, nil)
+ high = ordercheapexpr(high, order)
+ max = orderexpr(max, order, nil)
+ max = ordercheapexpr(max, order)
+ n.SetSliceBounds(low, high, max)
if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
n = ordercopyexpr(n, n.Type, order, 0)
}
}
x = Nod(OINDEX, x, i)
case 1:
- i := index[0]
- j := index[1]
- x = Nod(OSLICE, x, Nod(OKEY, i, j))
+ x = Nod(OSLICE, x, nil)
+ x.SetSliceBounds(index[0], index[1], nil)
case 2:
- i := index[0]
- j := index[1]
- k := index[2]
- if j == nil {
+ if index[1] == nil {
Yyerror("middle index required in 3-index slice")
}
- if k == nil {
+ if index[2] == nil {
Yyerror("final index required in 3-index slice")
}
- x = Nod(OSLICE3, x, Nod(OKEY, i, Nod(OKEY, j, k)))
+ x = Nod(OSLICE3, x, nil)
+ x.SetSliceBounds(index[0], index[1], index[2])
default:
panic("unreachable")
case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR, OSLICESTR:
instrumentnode(&n.Left, init, 0, 0)
- instrumentnode(&n.Right, init, 0, 0)
+ low, high, max := n.SliceBounds()
+ instrumentnode(&low, init, 0, 0)
+ instrumentnode(&high, init, 0, 0)
+ instrumentnode(&max, init, 0, 0)
+ n.SetSliceBounds(low, high, max)
goto ret
case OKEY:
arraylit(ctxt, 2, n, vstat, init)
// copy static to slice
- a := Nod(OSLICE, vstat, Nod(OKEY, nil, nil))
+ a := Nod(OSLICE, vstat, nil)
a = Nod(OAS, var_, a)
a = typecheck(a, Etop)
}
// make slice out of heap (6)
- a = Nod(OAS, var_, Nod(OSLICE, vauto, Nod(OKEY, nil, nil)))
+ a = Nod(OAS, var_, Nod(OSLICE, vauto, nil))
a = typecheck(a, Etop)
a = orderstmtinplace(a)
fallthrough
case OSLICEARR:
- if nr.Right.Op != OKEY || nr.Right.Left != nil || nr.Right.Right != nil {
+ low, high, _ := nr.SliceBounds()
+ if low != nil || high != nil {
return false
}
nr = nr.Left
if rhs != nil && (rhs.Op == OSLICE || rhs.Op == OSLICE3 || rhs.Op == OSLICESTR) && samesafeexpr(rhs.Left, n.Left) {
// We're assigning a slicing operation back to its source.
// Don't write back fields we aren't changing. See issue #14855.
- i := rhs.Right.Left
- var j, k *Node
- if rhs.Op == OSLICE3 {
- j = rhs.Right.Right.Left
- k = rhs.Right.Right.Right
- } else {
- j = rhs.Right.Right
- }
+ i, j, k := rhs.SliceBounds()
if i != nil && (i.Op == OLITERAL && i.Val().Ctype() == CTINT && i.Int64() == 0) {
// [0:...] is the same as [:...]
i = nil
}
return s.newValue2(ssa.OpIMake, n.Type, tab, data)
- case OSLICE, OSLICEARR:
+ case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR:
v := s.expr(n.Left)
- var i, j *ssa.Value
- if n.Right.Left != nil {
- i = s.extendIndex(s.expr(n.Right.Left))
+ var i, j, k *ssa.Value
+ low, high, max := n.SliceBounds()
+ if low != nil {
+ i = s.extendIndex(s.expr(low))
}
- if n.Right.Right != nil {
- j = s.extendIndex(s.expr(n.Right.Right))
+ if high != nil {
+ j = s.extendIndex(s.expr(high))
}
- p, l, c := s.slice(n.Left.Type, v, i, j, nil)
+ if max != nil {
+ k = s.extendIndex(s.expr(max))
+ }
+ p, l, c := s.slice(n.Left.Type, v, i, j, k)
return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
+
case OSLICESTR:
v := s.expr(n.Left)
var i, j *ssa.Value
- if n.Right.Left != nil {
- i = s.extendIndex(s.expr(n.Right.Left))
+ low, high, _ := n.SliceBounds()
+ if low != nil {
+ i = s.extendIndex(s.expr(low))
}
- if n.Right.Right != nil {
- j = s.extendIndex(s.expr(n.Right.Right))
+ if high != nil {
+ j = s.extendIndex(s.expr(high))
}
p, l, _ := s.slice(n.Left.Type, v, i, j, nil)
return s.newValue2(ssa.OpStringMake, n.Type, p, l)
- case OSLICE3, OSLICE3ARR:
- v := s.expr(n.Left)
- var i *ssa.Value
- if n.Right.Left != nil {
- i = s.extendIndex(s.expr(n.Right.Left))
- }
- j := s.extendIndex(s.expr(n.Right.Right.Left))
- k := s.extendIndex(s.expr(n.Right.Right.Right))
- p, l, c := s.slice(n.Left.Type, v, i, j, k)
- return s.newValue3(ssa.OpSliceMake, n.Type, p, l, c)
case OCALLFUNC:
if isIntrinsicCall1(n) {
return false
}
+// SliceBounds returns n's slice bounds: low, high, and max in expr[low:high:max].
+// n must be a slice expression. max is nil if n is a simple slice expression.
+func (n *Node) SliceBounds() (low, high, max *Node) {
+ switch n.Op {
+ case OSLICE, OSLICEARR, OSLICESTR:
+ if n.Right == nil {
+ return nil, nil, nil
+ }
+ if n.Right.Op != OKEY {
+ Fatalf("SliceBounds right %s", opnames[n.Right.Op])
+ }
+ return n.Right.Left, n.Right.Right, nil
+ case OSLICE3, OSLICE3ARR:
+ if n.Right.Op != OKEY || n.Right.Right.Op != OKEY {
+ Fatalf("SliceBounds right %s %s", opnames[n.Right.Op], opnames[n.Right.Right.Op])
+ }
+ return n.Right.Left, n.Right.Right.Left, n.Right.Right.Right
+ }
+ Fatalf("SliceBounds op %s: %v", n.Op, n)
+ return nil, nil, nil
+}
+
+// SetSliceBounds sets n's slice bounds, where n is a slice expression.
+// n must be a slice expression. If max is non-nil, n must be a full slice expression.
+func (n *Node) SetSliceBounds(low, high, max *Node) {
+ switch n.Op {
+ case OSLICE, OSLICEARR, OSLICESTR:
+ if max != nil {
+ Fatalf("SetSliceBounds %s given three bounds", n.Op)
+ }
+ if n.Right == nil {
+ n.Right = Nod(OKEY, low, high)
+ return
+ }
+ n.Right.Left = low
+ n.Right.Right = high
+ return
+ case OSLICE3, OSLICE3ARR:
+ if n.Right == nil {
+ n.Right = Nod(OKEY, low, Nod(OKEY, high, max))
+ }
+ n.Right.Left = low
+ n.Right.Right.Left = high
+ n.Right.Right.Right = max
+ return
+ }
+ Fatalf("SetSliceBounds op %s: %v", n.Op, n)
+}
+
+// IsSlice3 reports whether o is a slice3 op (OSLICE3, OSLICE3ARR).
+// o must be a slicing op.
+func (o Op) IsSlice3() bool {
+ switch o {
+ case OSLICE, OSLICEARR, OSLICESTR:
+ return false
+ case OSLICE3, OSLICE3ARR:
+ return true
+ }
+ Fatalf("IsSlice3 op %v", o)
+ return false
+}
+
// Is a conversion between t1 and t2 a no-op?
func Noconv(t1 *Type, t2 *Type) bool {
e1 := Simtype[t1.Etype]
n.Type = nil
break OpSwitch
- case OSLICE:
+ case OSLICE, OSLICE3:
ok |= Erv
n.Left = typecheck(n.Left, top)
- n.Right.Left = typecheck(n.Right.Left, Erv)
- n.Right.Right = typecheck(n.Right.Right, Erv)
+ low, high, max := n.SliceBounds()
+ hasmax := n.Op.IsSlice3()
+ low = typecheck(low, Erv)
+ high = typecheck(high, Erv)
+ max = typecheck(max, Erv)
n.Left = defaultlit(n.Left, nil)
- n.Right.Left = indexlit(n.Right.Left)
- n.Right.Right = indexlit(n.Right.Right)
+ low = indexlit(low)
+ high = indexlit(high)
+ max = indexlit(max)
+ n.SetSliceBounds(low, high, max)
l := n.Left
if l.Type.IsArray() {
if !islvalue(n.Left) {
}
var tp *Type
if t.IsString() {
+ if hasmax {
+ Yyerror("invalid operation %v (3-index slice of string)", n)
+ n.Type = nil
+ return n
+ }
n.Type = t
n.Op = OSLICESTR
} else if t.IsPtr() && t.Elem().IsArray() {
tp = t.Elem()
n.Type = typSlice(tp.Elem())
dowidth(n.Type)
- n.Op = OSLICEARR
- } else if t.IsSlice() {
- n.Type = t
- } else {
- Yyerror("cannot slice %v (type %v)", l, t)
- n.Type = nil
- return n
- }
-
- lo := n.Right.Left
- if lo != nil && !checksliceindex(l, lo, tp) {
- n.Type = nil
- return n
- }
- hi := n.Right.Right
- if hi != nil && !checksliceindex(l, hi, tp) {
- n.Type = nil
- return n
- }
- if !checksliceconst(lo, hi) {
- n.Type = nil
- return n
- }
- break OpSwitch
-
- case OSLICE3:
- ok |= Erv
- n.Left = typecheck(n.Left, top)
- n.Right.Left = typecheck(n.Right.Left, Erv)
- n.Right.Right.Left = typecheck(n.Right.Right.Left, Erv)
- n.Right.Right.Right = typecheck(n.Right.Right.Right, Erv)
- n.Left = defaultlit(n.Left, nil)
- n.Right.Left = indexlit(n.Right.Left)
- n.Right.Right.Left = indexlit(n.Right.Right.Left)
- n.Right.Right.Right = indexlit(n.Right.Right.Right)
- l := n.Left
- if l.Type.IsArray() {
- if !islvalue(n.Left) {
- Yyerror("invalid operation %v (slice of unaddressable value)", n)
- n.Type = nil
- return n
+ if hasmax {
+ n.Op = OSLICE3ARR
+ } else {
+ n.Op = OSLICEARR
}
-
- n.Left = Nod(OADDR, n.Left, nil)
- n.Left.Implicit = true
- n.Left = typecheck(n.Left, Erv)
- l = n.Left
- }
-
- t := l.Type
- if t == nil {
- n.Type = nil
- return n
- }
- if t.IsString() {
- Yyerror("invalid operation %v (3-index slice of string)", n)
- n.Type = nil
- return n
- }
-
- var tp *Type
- if t.IsPtr() && t.Elem().IsArray() {
- tp = t.Elem()
- n.Type = typSlice(tp.Elem())
- dowidth(n.Type)
- n.Op = OSLICE3ARR
} else if t.IsSlice() {
n.Type = t
} else {
return n
}
- lo := n.Right.Left
- if lo != nil && !checksliceindex(l, lo, tp) {
+ if low != nil && !checksliceindex(l, low, tp) {
n.Type = nil
return n
}
- mid := n.Right.Right.Left
- if mid != nil && !checksliceindex(l, mid, tp) {
+ if high != nil && !checksliceindex(l, high, tp) {
n.Type = nil
return n
}
- hi := n.Right.Right.Right
- if hi != nil && !checksliceindex(l, hi, tp) {
+ if max != nil && !checksliceindex(l, max, tp) {
n.Type = nil
return n
}
- if !checksliceconst(lo, hi) || !checksliceconst(lo, mid) || !checksliceconst(mid, hi) {
+ if !checksliceconst(low, high) || !checksliceconst(low, max) || !checksliceconst(high, max) {
n.Type = nil
return n
}
case ORECV:
Fatalf("walkexpr ORECV") // should see inside OAS only
- case OSLICE, OSLICEARR, OSLICESTR:
+ case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
n.Left = walkexpr(n.Left, init)
- n.Right.Left = walkexpr(n.Right.Left, init)
- if n.Right.Left != nil && iszero(n.Right.Left) {
- // Reduce x[0:j] to x[:j].
- n.Right.Left = nil
- }
- n.Right.Right = walkexpr(n.Right.Right, init)
- n = reduceSlice(n)
-
- case OSLICE3, OSLICE3ARR:
- n.Left = walkexpr(n.Left, init)
- n.Right.Left = walkexpr(n.Right.Left, init)
- if n.Right.Left != nil && iszero(n.Right.Left) {
- // Reduce x[0:j:k] to x[:j:k].
- n.Right.Left = nil
- }
- n.Right.Right.Left = walkexpr(n.Right.Right.Left, init)
- n.Right.Right.Right = walkexpr(n.Right.Right.Right, init)
-
- r := n.Right.Right.Right
- if r != nil && r.Op == OCAP && samesafeexpr(n.Left, r.Left) {
- // Reduce x[i:j:cap(x)] to x[i:j].
- n.Right.Right = n.Right.Right.Left
- if n.Op == OSLICE3 {
- n.Op = OSLICE
- } else {
- n.Op = OSLICEARR
+ low, high, max := n.SliceBounds()
+ low = walkexpr(low, init)
+ if low != nil && iszero(low) {
+ // Reduce x[0:j] to x[:j] and x[0:j:k] to x[:j:k].
+ low = nil
+ }
+ high = walkexpr(high, init)
+ max = walkexpr(max, init)
+ n.SetSliceBounds(low, high, max)
+ if n.Op.IsSlice3() {
+ if max != nil && max.Op == OCAP && samesafeexpr(n.Left, max.Left) {
+ // Reduce x[i:j:cap(x)] to x[i:j].
+ if n.Op == OSLICE3 {
+ n.Op = OSLICE
+ } else {
+ n.Op = OSLICEARR
+ }
+ n = reduceSlice(n)
}
+ } else {
n = reduceSlice(n)
}
a := Nod(OAS, var_, nil) // zero temp
a = typecheck(a, Etop)
init.Append(a)
- r := Nod(OSLICE, var_, Nod(OKEY, nil, l)) // arr[:l]
- r = conv(r, n.Type) // in case n.Type is named.
+ r := Nod(OSLICE, var_, nil) // arr[:l]
+ r.SetSliceBounds(nil, l, nil)
+ r = conv(r, n.Type) // in case n.Type is named.
r = typecheck(r, Erv)
r = walkexpr(r, init)
n = r
return n
}
+// TODO(josharian): combine this with its caller and simplify
func reduceSlice(n *Node) *Node {
- r := n.Right.Right
- if r != nil && r.Op == OLEN && samesafeexpr(n.Left, r.Left) {
+ low, high, max := n.SliceBounds()
+ if high != nil && high.Op == OLEN && samesafeexpr(n.Left, high.Left) {
// Reduce x[i:len(x)] to x[i:].
- n.Right.Right = nil
+ high = nil
}
- if (n.Op == OSLICE || n.Op == OSLICESTR) && n.Right.Left == nil && n.Right.Right == nil {
+ n.SetSliceBounds(low, high, max)
+ if (n.Op == OSLICE || n.Op == OSLICESTR) && low == nil && high == nil {
// Reduce x[:] to x.
if Debug_slice > 0 {
Warn("slice: omit slice operation")
l = append(l, nif)
// s = s[:n]
- nt := Nod(OSLICE, s, Nod(OKEY, nil, nn))
+ nt := Nod(OSLICE, s, nil)
+ nt.SetSliceBounds(nil, nn, nil)
nt.Etype = 1
l = append(l, Nod(OAS, s, nt))
if haspointers(l1.Type.Elem()) {
// copy(s[len(l1):], l2)
- nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), nil))
-
+ nptr1 := Nod(OSLICE, s, nil)
+ nptr1.SetSliceBounds(Nod(OLEN, l1, nil), nil, nil)
nptr1.Etype = 1
nptr2 := l2
fn := syslook("typedslicecopy")
} else if instrumenting {
// rely on runtime to instrument copy.
// copy(s[len(l1):], l2)
- nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), nil))
-
+ nptr1 := Nod(OSLICE, s, nil)
+ nptr1.SetSliceBounds(Nod(OLEN, l1, nil), nil, nil)
nptr1.Etype = 1
nptr2 := l2
var fn *Node
nn := temp(Types[TINT])
l = append(l, Nod(OAS, nn, Nod(OLEN, ns, nil))) // n = len(s)
- nx = Nod(OSLICE, ns, Nod(OKEY, nil, Nod(OADD, nn, na))) // ...s[:n+argc]
+ nx = Nod(OSLICE, ns, nil) // ...s[:n+argc]
+ nx.SetSliceBounds(nil, Nod(OADD, nn, na), nil)
nx.Etype = 1
l = append(l, Nod(OAS, ns, nx)) // s = s[:n+argc]