n.Right = walkexpr(n.Right, &ll)
n.Right = addinit(n.Right, ll.Slice())
- n = walkinrange(n, init)
case OPRINT, OPRINTN:
n = walkprint(n, init)
return n.Left.Type.IsInteger() && n.Right.Type.IsInteger()
}
-// walkinrange optimizes integer-in-range checks, such as 4 <= x && x < 10.
-// n must be an OANDAND or OOROR node.
-// The result of walkinrange MUST be assigned back to n, e.g.
-// n.Left = walkinrange(n.Left)
-func walkinrange(n *Node, init *Nodes) *Node {
- // We are looking for something equivalent to a opl b OP b opr c, where:
- // * a, b, and c have integer type
- // * b is side-effect-free
- // * opl and opr are each < or ≤
- // * OP is &&
- l := n.Left
- r := n.Right
- if !l.isIntOrdering() || !r.isIntOrdering() {
- return n
- }
-
- // Find b, if it exists, and rename appropriately.
- // Input is: l.Left l.Op l.Right ANDAND/OROR r.Left r.Op r.Right
- // Output is: a opl b(==x) ANDAND/OROR b(==x) opr c
- a, opl, b := l.Left, l.Op, l.Right
- x, opr, c := r.Left, r.Op, r.Right
- for i := 0; ; i++ {
- if samesafeexpr(b, x) {
- break
- }
- if i == 3 {
- // Tried all permutations and couldn't find an appropriate b == x.
- return n
- }
- if i&1 == 0 {
- a, opl, b = b, brrev(opl), a
- } else {
- x, opr, c = c, brrev(opr), x
- }
- }
-
- // If n.Op is ||, apply de Morgan.
- // Negate the internal ops now; we'll negate the top level op at the end.
- // Henceforth assume &&.
- negateResult := n.Op == OOROR
- if negateResult {
- opl = brcom(opl)
- opr = brcom(opr)
- }
-
- cmpdir := func(o Op) int {
- switch o {
- case OLE, OLT:
- return -1
- case OGE, OGT:
- return +1
- }
- Fatalf("walkinrange cmpdir %v", o)
- return 0
- }
- if cmpdir(opl) != cmpdir(opr) {
- // Not a range check; something like b < a && b < c.
- return n
- }
-
- switch opl {
- case OGE, OGT:
- // We have something like a > b && b ≥ c.
- // Switch and reverse ops and rename constants,
- // to make it look like a ≤ b && b < c.
- a, c = c, a
- opl, opr = brrev(opr), brrev(opl)
- }
-
- // We must ensure that c-a is non-negative.
- // For now, require a and c to be constants.
- // In the future, we could also support a == 0 and c == len/cap(...).
- // Unfortunately, by this point, most len/cap expressions have been
- // stored into temporary variables.
- if !Isconst(a, CTINT) || !Isconst(c, CTINT) {
- return n
- }
-
- // Ensure that Int64() does not overflow on a and c (it'll happen
- // for any const above 2**63; see issue #27143).
- if !a.CanInt64() || !c.CanInt64() {
- return n
- }
-
- if opl == OLT {
- // We have a < b && ...
- // We need a ≤ b && ... to safely use unsigned comparison tricks.
- // If a is not the maximum constant for b's type,
- // we can increment a and switch to ≤.
- if a.Int64() >= maxintval[b.Type.Etype].Int64() {
- return n
- }
- a = nodintconst(a.Int64() + 1)
- opl = OLE
- }
-
- bound := c.Int64() - a.Int64()
- if bound < 0 {
- // Bad news. Something like 5 <= x && x < 3.
- // Rare in practice, and we still need to generate side-effects,
- // so just leave it alone.
- return n
- }
-
- // We have a ≤ b && b < c (or a ≤ b && b ≤ c).
- // This is equivalent to (a-a) ≤ (b-a) && (b-a) < (c-a),
- // which is equivalent to 0 ≤ (b-a) && (b-a) < (c-a),
- // which is equivalent to uint(b-a) < uint(c-a).
- ut := b.Type.ToUnsigned()
- lhs := conv(nod(OSUB, b, a), ut)
- rhs := nodintconst(bound)
- if negateResult {
- // Negate top level.
- opr = brcom(opr)
- }
- cmp := nod(opr, lhs, rhs)
- cmp.Pos = n.Pos
- cmp = addinit(cmp, l.Ninit.Slice())
- cmp = addinit(cmp, r.Ninit.Slice())
- // Typecheck the AST rooted at cmp...
- cmp = typecheck(cmp, ctxExpr)
- // ...but then reset cmp's type to match n's type.
- cmp.Type = n.Type
- cmp = walkexpr(cmp, init)
- return cmp
-}
-
// return 1 if integer n must be in range [0, max), 0 otherwise
func bounded(n *Node, max int64) bool {
if n.Type == nil || !n.Type.IsInteger() {