// Copyright 2009 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 gc import ( "cmd/compile/internal/types" "fmt" "sort" ) const ( // expression switch switchKindExpr = iota // switch a {...} or switch 5 {...} switchKindTrue // switch true {...} or switch {...} switchKindFalse // switch false {...} ) const ( binarySearchMin = 4 // minimum number of cases for binary search integerRangeMin = 2 // minimum size of integer ranges ) // An exprSwitch walks an expression switch. type exprSwitch struct { exprname *Node // node for the expression being switched on kind int // kind of switch statement (switchKind*) } // A typeSwitch walks a type switch. type typeSwitch struct { hashname *Node // node for the hash of the type of the variable being switched on facename *Node // node for the concrete type of the variable being switched on okname *Node // boolean node used for comma-ok type assertions } // A caseClause is a single case clause in a switch statement. type caseClause struct { node *Node // points at case statement ordinal int // position in switch hash uint32 // hash of a type switch // isconst indicates whether this case clause is a constant, // for the purposes of the switch code generation. // For expression switches, that's generally literals (case 5:, not case x:). // For type switches, that's concrete types (case time.Time:), not interfaces (case io.Reader:). isconst bool } // caseClauses are all the case clauses in a switch statement. type caseClauses struct { list []caseClause // general cases defjmp *Node // OGOTO for default case or OBREAK if no default case present niljmp *Node // OGOTO for nil type case in a type switch } // typecheckswitch typechecks a switch statement. func typecheckswitch(n *Node) { typecheckslice(n.Ninit.Slice(), Etop) var nilonly string var top int var t *types.Type if n.Left != nil && n.Left.Op == OTYPESW { // type switch top = Etype n.Left.Right = typecheck(n.Left.Right, Erv) t = n.Left.Right.Type if t != nil && !t.IsInterface() { yyerrorl(n.Pos, "cannot type switch on non-interface value %L", n.Left.Right) } } else { // expression switch top = Erv if n.Left != nil { n.Left = typecheck(n.Left, Erv) n.Left = defaultlit(n.Left, nil) t = n.Left.Type } else { t = types.Types[TBOOL] } if t != nil { switch { case !okforeq[t.Etype]: yyerrorl(n.Pos, "cannot switch on %L", n.Left) case t.IsSlice(): nilonly = "slice" case t.IsArray() && !IsComparable(t): yyerrorl(n.Pos, "cannot switch on %L", n.Left) case t.IsStruct(): if f := IncomparableField(t); f != nil { yyerrorl(n.Pos, "cannot switch on %L (struct containing %v cannot be compared)", n.Left, f.Type) } case t.Etype == TFUNC: nilonly = "func" case t.IsMap(): nilonly = "map" } } } n.Type = t var def, niltype *Node for _, ncase := range n.List.Slice() { if ncase.List.Len() == 0 { // default if def != nil { setlineno(ncase) yyerrorl(ncase.Pos, "multiple defaults in switch (first at %v)", def.Line()) } else { def = ncase } } else { ls := ncase.List.Slice() for i1, n1 := range ls { setlineno(n1) ls[i1] = typecheck(ls[i1], Erv|Etype) n1 = ls[i1] if n1.Type == nil || t == nil { continue } setlineno(ncase) switch top { // expression switch case Erv: ls[i1] = defaultlit(ls[i1], t) n1 = ls[i1] switch { case n1.Op == OTYPE: yyerrorl(ncase.Pos, "type %v is not an expression", n1.Type) case n1.Type != nil && assignop(n1.Type, t, nil) == 0 && assignop(t, n1.Type, nil) == 0: if n.Left != nil { yyerrorl(ncase.Pos, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t) } else { yyerrorl(ncase.Pos, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type) } case nilonly != "" && !isnil(n1): yyerrorl(ncase.Pos, "invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left) case t.IsInterface() && !n1.Type.IsInterface() && !IsComparable(n1.Type): yyerrorl(ncase.Pos, "invalid case %L in switch (incomparable type)", n1) } // type switch case Etype: var missing, have *types.Field var ptr int switch { case n1.Op == OLITERAL && n1.Type.IsKind(TNIL): // case nil: if niltype != nil { yyerrorl(ncase.Pos, "multiple nil cases in type switch (first at %v)", niltype.Line()) } else { niltype = ncase } case n1.Op != OTYPE && n1.Type != nil: // should this be ||? yyerrorl(ncase.Pos, "%L is not a type", n1) // reset to original type n1 = n.Left.Right ls[i1] = n1 case !n1.Type.IsInterface() && t.IsInterface() && !implements(n1.Type, t, &missing, &have, &ptr): if have != nil && !missing.Broke() && !have.Broke() { yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+ " (wrong type for %v method)\n\thave %v%S\n\twant %v%S", n.Left.Right, n1.Type, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type) } else if !missing.Broke() { if ptr != 0 { yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+ " (%v method has pointer receiver)", n.Left.Right, n1.Type, missing.Sym) } else { yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+ " (missing %v method)", n.Left.Right, n1.Type, missing.Sym) } } } } } } if n.Type == nil || n.Type.IsUntyped() { // if the value we're switching on has no type or is untyped, // we've already printed an error and don't need to continue // typechecking the body return } if top == Etype { ll := ncase.List if ncase.Rlist.Len() != 0 { nvar := ncase.Rlist.First() if ll.Len() == 1 && ll.First().Type != nil && !ll.First().Type.IsKind(TNIL) { // single entry type switch nvar.Type = ll.First().Type } else { // multiple entry type switch or default nvar.Type = n.Type } nvar = typecheck(nvar, Erv|Easgn) ncase.Rlist.SetFirst(nvar) } } typecheckslice(ncase.Nbody.Slice(), Etop) } switch top { // expression switch case Erv: checkDupExprCases(n.Left, n.List.Slice()) } } // walkswitch walks a switch statement. func walkswitch(sw *Node) { // convert switch {...} to switch true {...} if sw.Left == nil { sw.Left = nodbool(true) sw.Left = typecheck(sw.Left, Erv) } if sw.Left.Op == OTYPESW { var s typeSwitch s.walk(sw) } else { var s exprSwitch s.walk(sw) } } // walk generates an AST implementing sw. // sw is an expression switch. // The AST is generally of the form of a linear // search using if..goto, although binary search // is used with long runs of constants. func (s *exprSwitch) walk(sw *Node) { casebody(sw, nil) cond := sw.Left sw.Left = nil s.kind = switchKindExpr if Isconst(cond, CTBOOL) { s.kind = switchKindTrue if !cond.Val().U.(bool) { s.kind = switchKindFalse } } cond = walkexpr(cond, &sw.Ninit) t := sw.Type if t == nil { return } // convert the switch into OIF statements var cas []*Node if s.kind == switchKindTrue || s.kind == switchKindFalse { s.exprname = nodbool(s.kind == switchKindTrue) } else if consttype(cond) > 0 { // leave constants to enable dead code elimination (issue 9608) s.exprname = cond } else { s.exprname = temp(cond.Type) cas = []*Node{nod(OAS, s.exprname, cond)} typecheckslice(cas, Etop) } // Enumerate the cases and prepare the default case. clauses := s.genCaseClauses(sw.List.Slice()) sw.List.Set(nil) cc := clauses.list // handle the cases in order for len(cc) > 0 { run := 1 if okforcmp[t.Etype] && cc[0].isconst { // do binary search on runs of constants for ; run < len(cc) && cc[run].isconst; run++ { } // sort and compile constants sort.Sort(caseClauseByConstVal(cc[:run])) } a := s.walkCases(cc[:run]) cas = append(cas, a) cc = cc[run:] } // handle default case if nerrors == 0 { cas = append(cas, clauses.defjmp) sw.Nbody.Prepend(cas...) walkstmtlist(sw.Nbody.Slice()) } } // walkCases generates an AST implementing the cases in cc. func (s *exprSwitch) walkCases(cc []caseClause) *Node { if len(cc) < binarySearchMin { // linear search var cas []*Node for _, c := range cc { n := c.node lno := setlineno(n) a := nod(OIF, nil, nil) if rng := n.List.Slice(); rng != nil { // Integer range. // exprname is a temp or a constant, // so it is safe to evaluate twice. // In most cases, this conjunction will be // rewritten by walkinrange into a single comparison. low := nod(OGE, s.exprname, rng[0]) high := nod(OLE, s.exprname, rng[1]) a.Left = nod(OANDAND, low, high) a.Left = typecheck(a.Left, Erv) a.Left = walkexpr(a.Left, nil) // give walk the opportunity to optimize the range check } else if (s.kind != switchKindTrue && s.kind != switchKindFalse) || assignop(n.Left.Type, s.exprname.Type, nil) == OCONVIFACE || assignop(s.exprname.Type, n.Left.Type, nil) == OCONVIFACE { a.Left = nod(OEQ, s.exprname, n.Left) // if name == val a.Left = typecheck(a.Left, Erv) } else if s.kind == switchKindTrue { a.Left = n.Left // if val } else { // s.kind == switchKindFalse a.Left = nod(ONOT, n.Left, nil) // if !val a.Left = typecheck(a.Left, Erv) } a.Nbody.Set1(n.Right) // goto l cas = append(cas, a) lineno = lno } return liststmt(cas) } // find the middle and recur half := len(cc) / 2 a := nod(OIF, nil, nil) n := cc[half-1].node var mid *Node if rng := n.List.Slice(); rng != nil { mid = rng[1] // high end of range } else { mid = n.Left } le := nod(OLE, s.exprname, mid) if Isconst(mid, CTSTR) { // Search by length and then by value; see caseClauseByConstVal. lenlt := nod(OLT, nod(OLEN, s.exprname, nil), nod(OLEN, mid, nil)) leneq := nod(OEQ, nod(OLEN, s.exprname, nil), nod(OLEN, mid, nil)) a.Left = nod(OOROR, lenlt, nod(OANDAND, leneq, le)) } else { a.Left = le } a.Left = typecheck(a.Left, Erv) a.Nbody.Set1(s.walkCases(cc[:half])) a.Rlist.Set1(s.walkCases(cc[half:])) return a } // casebody builds separate lists of statements and cases. // It makes labels between cases and statements // and deals with fallthrough, break, and unreachable statements. func casebody(sw *Node, typeswvar *Node) { if sw.List.Len() == 0 { return } lno := setlineno(sw) var cas []*Node // cases var stat []*Node // statements var def *Node // defaults br := nod(OBREAK, nil, nil) for _, n := range sw.List.Slice() { setlineno(n) if n.Op != OXCASE { Fatalf("casebody %v", n.Op) } n.Op = OCASE needvar := n.List.Len() != 1 || n.List.First().Op == OLITERAL jmp := nod(OGOTO, autolabel(".s"), nil) switch n.List.Len() { case 0: // default if def != nil { yyerrorl(n.Pos, "more than one default case") } // reuse original default case n.Right = jmp def = n case 1: // one case -- reuse OCASE node n.Left = n.List.First() n.Right = jmp n.List.Set(nil) cas = append(cas, n) default: // Expand multi-valued cases and detect ranges of integer cases. if typeswvar != nil || sw.Left.Type.IsInterface() || !n.List.First().Type.IsInteger() || n.List.Len() < integerRangeMin { // Can't use integer ranges. Expand each case into a separate node. for _, n1 := range n.List.Slice() { cas = append(cas, nod(OCASE, n1, jmp)) } break } // Find integer ranges within runs of constants. s := n.List.Slice() j := 0 for j < len(s) { // Find a run of constants. var run int for run = j; run < len(s) && Isconst(s[run], CTINT); run++ { } if run-j >= integerRangeMin { // Search for integer ranges in s[j:run]. // Typechecking is done, so all values are already in an appropriate range. search := s[j:run] sort.Sort(constIntNodesByVal(search)) for beg, end := 0, 1; end <= len(search); end++ { if end < len(search) && search[end].Int64() == search[end-1].Int64()+1 { continue } if end-beg >= integerRangeMin { // Record range in List. c := nod(OCASE, nil, jmp) c.List.Set2(search[beg], search[end-1]) cas = append(cas, c) } else { // Not large enough for range; record separately. for _, n := range search[beg:end] { cas = append(cas, nod(OCASE, n, jmp)) } } beg = end } j = run } // Advance to next constant, adding individual non-constant // or as-yet-unhandled constant cases as we go. for ; j < len(s) && (j < run || !Isconst(s[j], CTINT)); j++ { cas = append(cas, nod(OCASE, s[j], jmp)) } } } stat = append(stat, nod(OLABEL, jmp.Left, nil)) if typeswvar != nil && needvar && n.Rlist.Len() != 0 { l := []*Node{ nod(ODCL, n.Rlist.First(), nil), nod(OAS, n.Rlist.First(), typeswvar), } typecheckslice(l, Etop) stat = append(stat, l...) } stat = append(stat, n.Nbody.Slice()...) // Search backwards for the index of the fallthrough // statement. Do not assume it'll be in the last // position, since in some cases (e.g. when the statement // list contains autotmp_ variables), one or more OVARKILL // nodes will be at the end of the list. fallIndex := len(stat) - 1 for stat[fallIndex].Op == OVARKILL { fallIndex-- } last := stat[fallIndex] if last.Op != OFALL { stat = append(stat, br) } } stat = append(stat, br) if def != nil { cas = append(cas, def) } sw.List.Set(cas) sw.Nbody.Set(stat) lineno = lno } // genCaseClauses generates the caseClauses value for clauses. func (s *exprSwitch) genCaseClauses(clauses []*Node) caseClauses { var cc caseClauses for _, n := range clauses { if n.Left == nil && n.List.Len() == 0 { // default case if cc.defjmp != nil { Fatalf("duplicate default case not detected during typechecking") } cc.defjmp = n.Right continue } c := caseClause{node: n, ordinal: len(cc.list)} if n.List.Len() > 0 { c.isconst = true } switch consttype(n.Left) { case CTFLT, CTINT, CTRUNE, CTSTR: c.isconst = true } cc.list = append(cc.list, c) } if cc.defjmp == nil { cc.defjmp = nod(OBREAK, nil, nil) } return cc } // genCaseClauses generates the caseClauses value for clauses. func (s *typeSwitch) genCaseClauses(clauses []*Node) caseClauses { var cc caseClauses for _, n := range clauses { switch { case n.Left == nil: // default case if cc.defjmp != nil { Fatalf("duplicate default case not detected during typechecking") } cc.defjmp = n.Right continue case n.Left.Op == OLITERAL: // nil case in type switch if cc.niljmp != nil { Fatalf("duplicate nil case not detected during typechecking") } cc.niljmp = n.Right continue } // general case c := caseClause{ node: n, ordinal: len(cc.list), isconst: !n.Left.Type.IsInterface(), hash: typehash(n.Left.Type), } cc.list = append(cc.list, c) } if cc.defjmp == nil { cc.defjmp = nod(OBREAK, nil, nil) } // diagnose duplicate cases s.checkDupCases(cc.list) return cc } func (s *typeSwitch) checkDupCases(cc []caseClause) { if len(cc) < 2 { return } // We store seen types in a map keyed by type hash. // It is possible, but very unlikely, for multiple distinct types to have the same hash. seen := make(map[uint32][]*Node) // To avoid many small allocations of length 1 slices, // also set up a single large slice to slice into. nn := make([]*Node, 0, len(cc)) Outer: for _, c := range cc { prev, ok := seen[c.hash] if !ok { // First entry for this hash. nn = append(nn, c.node) seen[c.hash] = nn[len(nn)-1 : len(nn) : len(nn)] continue } for _, n := range prev { if eqtype(n.Left.Type, c.node.Left.Type) { yyerrorl(c.node.Pos, "duplicate case %v in type switch\n\tprevious case at %v", c.node.Left.Type, n.Line()) // avoid double-reporting errors continue Outer } } seen[c.hash] = append(seen[c.hash], c.node) } } func checkDupExprCases(exprname *Node, clauses []*Node) { // boolean (naked) switch, nothing to do. if exprname == nil { return } // The common case is that s's expression is not an interface. // In that case, all constant clauses have the same type, // so checking for duplicates can be done solely by value. if !exprname.Type.IsInterface() { seen := make(map[interface{}]*Node) for _, ncase := range clauses { for _, n := range ncase.List.Slice() { // Can't check for duplicates that aren't constants, per the spec. Issue 15896. // Don't check for duplicate bools. Although the spec allows it, // (1) the compiler hasn't checked it in the past, so compatibility mandates it, and // (2) it would disallow useful things like // case GOARCH == "arm" && GOARM == "5": // case GOARCH == "arm": // which would both evaluate to false for non-ARM compiles. if ct := consttype(n); ct == 0 || ct == CTBOOL { continue } val := n.Val().Interface() prev, dup := seen[val] if !dup { seen[val] = n continue } yyerrorl(ncase.Pos, "duplicate case %s in switch\n\tprevious case at %v", nodeAndVal(n), prev.Line()) } } return } // s's expression is an interface. This is fairly rare, so keep this simple. // Duplicates are only duplicates if they have the same type and the same value. type typeVal struct { typ string val interface{} } seen := make(map[typeVal]*Node) for _, ncase := range clauses { for _, n := range ncase.List.Slice() { if ct := consttype(n); ct == 0 || ct == CTBOOL { continue } tv := typeVal{ typ: n.Type.LongString(), val: n.Val().Interface(), } prev, dup := seen[tv] if !dup { seen[tv] = n continue } yyerrorl(ncase.Pos, "duplicate case %s in switch\n\tprevious case at %v", nodeAndVal(n), prev.Line()) } } } func nodeAndVal(n *Node) string { show := n.String() val := n.Val().Interface() if s := fmt.Sprintf("%#v", val); show != s { show += " (value " + s + ")" } return show } // walk generates an AST that implements sw, // where sw is a type switch. // The AST is generally of the form of a linear // search using if..goto, although binary search // is used with long runs of concrete types. func (s *typeSwitch) walk(sw *Node) { cond := sw.Left sw.Left = nil if cond == nil { sw.List.Set(nil) return } if cond.Right == nil { yyerrorl(sw.Pos, "type switch must have an assignment") return } cond.Right = walkexpr(cond.Right, &sw.Ninit) if !cond.Right.Type.IsInterface() { yyerrorl(sw.Pos, "type switch must be on an interface") return } var cas []*Node // predeclare temporary variables and the boolean var s.facename = temp(cond.Right.Type) a := nod(OAS, s.facename, cond.Right) a = typecheck(a, Etop) cas = append(cas, a) s.okname = temp(types.Types[TBOOL]) s.okname = typecheck(s.okname, Erv) s.hashname = temp(types.Types[TUINT32]) s.hashname = typecheck(s.hashname, Erv) // set up labels and jumps casebody(sw, s.facename) clauses := s.genCaseClauses(sw.List.Slice()) sw.List.Set(nil) def := clauses.defjmp // For empty interfaces, do: // if e._type == nil { // do nil case if it exists, otherwise default // } // h := e._type.hash // Use a similar strategy for non-empty interfaces. // Get interface descriptor word. // For empty interfaces this will be the type. // For non-empty interfaces this will be the itab. itab := nod(OITAB, s.facename, nil) // Check for nil first. i := nod(OIF, nil, nil) i.Left = nod(OEQ, itab, nodnil()) if clauses.niljmp != nil { // Do explicit nil case right here. i.Nbody.Set1(clauses.niljmp) } else { // Jump to default case. lbl := autolabel(".s") i.Nbody.Set1(nod(OGOTO, lbl, nil)) // Wrap default case with label. blk := nod(OBLOCK, nil, nil) blk.List.Set2(nod(OLABEL, lbl, nil), def) def = blk } i.Left = typecheck(i.Left, Erv) cas = append(cas, i) // Load hash from type or itab. h := nodSym(ODOTPTR, itab, nil) h.Type = types.Types[TUINT32] h.SetTypecheck(1) if cond.Right.Type.IsEmptyInterface() { h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type } else { h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime.itab } h.SetBounded(true) // guaranteed not to fault a = nod(OAS, s.hashname, h) a = typecheck(a, Etop) cas = append(cas, a) cc := clauses.list // insert type equality check into each case block for _, c := range cc { c.node.Right = s.typeone(c.node) } // generate list of if statements, binary search for constant sequences for len(cc) > 0 { if !cc[0].isconst { n := cc[0].node cas = append(cas, n.Right) cc = cc[1:] continue } // identify run of constants var run int for run = 1; run < len(cc) && cc[run].isconst; run++ { } // sort by hash sort.Sort(caseClauseByType(cc[:run])) // for debugging: linear search if false { for i := 0; i < run; i++ { n := cc[i].node cas = append(cas, n.Right) } continue } // combine adjacent cases with the same hash ncase := 0 for i := 0; i < run; i++ { ncase++ hash := []*Node{cc[i].node.Right} for j := i + 1; j < run && cc[i].hash == cc[j].hash; j++ { hash = append(hash, cc[j].node.Right) } cc[i].node.Right = liststmt(hash) } // binary search among cases to narrow by hash cas = append(cas, s.walkCases(cc[:ncase])) cc = cc[ncase:] } // handle default case if nerrors == 0 { cas = append(cas, def) sw.Nbody.Prepend(cas...) sw.List.Set(nil) walkstmtlist(sw.Nbody.Slice()) } } // typeone generates an AST that jumps to the // case body if the variable is of type t. func (s *typeSwitch) typeone(t *Node) *Node { var name *Node var init Nodes if t.Rlist.Len() == 0 { name = nblank nblank = typecheck(nblank, Erv|Easgn) } else { name = t.Rlist.First() init.Append(nod(ODCL, name, nil)) a := nod(OAS, name, nil) a = typecheck(a, Etop) init.Append(a) } a := nod(OAS2, nil, nil) a.List.Set2(name, s.okname) // name, ok = b := nod(ODOTTYPE, s.facename, nil) b.Type = t.Left.Type // interface.(type) a.Rlist.Set1(b) a = typecheck(a, Etop) a = walkexpr(a, &init) init.Append(a) c := nod(OIF, nil, nil) c.Left = s.okname c.Nbody.Set1(t.Right) // if ok { goto l } init.Append(c) return init.asblock() } // walkCases generates an AST implementing the cases in cc. func (s *typeSwitch) walkCases(cc []caseClause) *Node { if len(cc) < binarySearchMin { var cas []*Node for _, c := range cc { n := c.node if !c.isconst { Fatalf("typeSwitch walkCases") } a := nod(OIF, nil, nil) a.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash))) a.Left = typecheck(a.Left, Erv) a.Nbody.Set1(n.Right) cas = append(cas, a) } return liststmt(cas) } // find the middle and recur half := len(cc) / 2 a := nod(OIF, nil, nil) a.Left = nod(OLE, s.hashname, nodintconst(int64(cc[half-1].hash))) a.Left = typecheck(a.Left, Erv) a.Nbody.Set1(s.walkCases(cc[:half])) a.Rlist.Set1(s.walkCases(cc[half:])) return a } // caseClauseByConstVal sorts clauses by constant value to enable binary search. type caseClauseByConstVal []caseClause func (x caseClauseByConstVal) Len() int { return len(x) } func (x caseClauseByConstVal) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x caseClauseByConstVal) Less(i, j int) bool { // n1 and n2 might be individual constants or integer ranges. // We have checked for duplicates already, // so ranges can be safely represented by any value in the range. n1 := x[i].node var v1 interface{} if s := n1.List.Slice(); s != nil { v1 = s[0].Val().U } else { v1 = n1.Left.Val().U } n2 := x[j].node var v2 interface{} if s := n2.List.Slice(); s != nil { v2 = s[0].Val().U } else { v2 = n2.Left.Val().U } switch v1 := v1.(type) { case *Mpflt: return v1.Cmp(v2.(*Mpflt)) < 0 case *Mpint: return v1.Cmp(v2.(*Mpint)) < 0 case string: // Sort strings by length and then by value. // It is much cheaper to compare lengths than values, // and all we need here is consistency. // We respect this sorting in exprSwitch.walkCases. a := v1 b := v2.(string) if len(a) != len(b) { return len(a) < len(b) } return a < b } Fatalf("caseClauseByConstVal passed bad clauses %v < %v", x[i].node.Left, x[j].node.Left) return false } type caseClauseByType []caseClause func (x caseClauseByType) Len() int { return len(x) } func (x caseClauseByType) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x caseClauseByType) Less(i, j int) bool { c1, c2 := x[i], x[j] // sort by hash code, then ordinal (for the rare case of hash collisions) if c1.hash != c2.hash { return c1.hash < c2.hash } return c1.ordinal < c2.ordinal } type constIntNodesByVal []*Node func (x constIntNodesByVal) Len() int { return len(x) } func (x constIntNodesByVal) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x constIntNodesByVal) Less(i, j int) bool { return x[i].Val().U.(*Mpint).Cmp(x[j].Val().U.(*Mpint)) < 0 }