github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/gc/swt.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gc
     6  
     7  import (
     8  	"github.com/gagliardetto/golang-go/cmd/compile/internal/types"
     9  	"github.com/gagliardetto/golang-go/cmd/internal/src"
    10  	"sort"
    11  )
    12  
    13  // typecheckswitch typechecks a switch statement.
    14  func typecheckswitch(n *Node) {
    15  	typecheckslice(n.Ninit.Slice(), ctxStmt)
    16  	if n.Left != nil && n.Left.Op == OTYPESW {
    17  		typecheckTypeSwitch(n)
    18  	} else {
    19  		typecheckExprSwitch(n)
    20  	}
    21  }
    22  
    23  func typecheckTypeSwitch(n *Node) {
    24  	n.Left.Right = typecheck(n.Left.Right, ctxExpr)
    25  	t := n.Left.Right.Type
    26  	if t != nil && !t.IsInterface() {
    27  		yyerrorl(n.Pos, "cannot type switch on non-interface value %L", n.Left.Right)
    28  		t = nil
    29  	}
    30  
    31  	// We don't actually declare the type switch's guarded
    32  	// declaration itself. So if there are no cases, we won't
    33  	// notice that it went unused.
    34  	if v := n.Left.Left; v != nil && !v.isBlank() && n.List.Len() == 0 {
    35  		yyerrorl(v.Pos, "%v declared but not used", v.Sym)
    36  	}
    37  
    38  	var defCase, nilCase *Node
    39  	var ts typeSet
    40  	for _, ncase := range n.List.Slice() {
    41  		ls := ncase.List.Slice()
    42  		if len(ls) == 0 { // default:
    43  			if defCase != nil {
    44  				yyerrorl(ncase.Pos, "multiple defaults in switch (first at %v)", defCase.Line())
    45  			} else {
    46  				defCase = ncase
    47  			}
    48  		}
    49  
    50  		for i := range ls {
    51  			ls[i] = typecheck(ls[i], ctxExpr|ctxType)
    52  			n1 := ls[i]
    53  			if t == nil || n1.Type == nil {
    54  				continue
    55  			}
    56  
    57  			var missing, have *types.Field
    58  			var ptr int
    59  			switch {
    60  			case n1.isNil(): // case nil:
    61  				if nilCase != nil {
    62  					yyerrorl(ncase.Pos, "multiple nil cases in type switch (first at %v)", nilCase.Line())
    63  				} else {
    64  					nilCase = ncase
    65  				}
    66  			case n1.Op != OTYPE:
    67  				yyerrorl(ncase.Pos, "%L is not a type", n1)
    68  			case !n1.Type.IsInterface() && !implements(n1.Type, t, &missing, &have, &ptr) && !missing.Broke():
    69  				if have != nil && !have.Broke() {
    70  					yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
    71  						" (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)
    72  				} else if ptr != 0 {
    73  					yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
    74  						" (%v method has pointer receiver)", n.Left.Right, n1.Type, missing.Sym)
    75  				} else {
    76  					yyerrorl(ncase.Pos, "impossible type switch case: %L cannot have dynamic type %v"+
    77  						" (missing %v method)", n.Left.Right, n1.Type, missing.Sym)
    78  				}
    79  			}
    80  
    81  			if n1.Op == OTYPE {
    82  				ts.add(ncase.Pos, n1.Type)
    83  			}
    84  		}
    85  
    86  		if ncase.Rlist.Len() != 0 {
    87  			// Assign the clause variable's type.
    88  			vt := t
    89  			if len(ls) == 1 {
    90  				if ls[0].Op == OTYPE {
    91  					vt = ls[0].Type
    92  				} else if ls[0].Op != OLITERAL { // TODO(mdempsky): Should be !ls[0].isNil()
    93  					// Invalid single-type case;
    94  					// mark variable as broken.
    95  					vt = nil
    96  				}
    97  			}
    98  
    99  			// TODO(mdempsky): It should be possible to
   100  			// still typecheck the case body.
   101  			if vt == nil {
   102  				continue
   103  			}
   104  
   105  			nvar := ncase.Rlist.First()
   106  			nvar.Type = vt
   107  			nvar = typecheck(nvar, ctxExpr|ctxAssign)
   108  			ncase.Rlist.SetFirst(nvar)
   109  		}
   110  
   111  		typecheckslice(ncase.Nbody.Slice(), ctxStmt)
   112  	}
   113  }
   114  
   115  type typeSet struct {
   116  	m map[string][]typeSetEntry
   117  }
   118  
   119  type typeSetEntry struct {
   120  	pos src.XPos
   121  	typ *types.Type
   122  }
   123  
   124  func (s *typeSet) add(pos src.XPos, typ *types.Type) {
   125  	if s.m == nil {
   126  		s.m = make(map[string][]typeSetEntry)
   127  	}
   128  
   129  	// LongString does not uniquely identify types, so we need to
   130  	// disambiguate collisions with types.Identical.
   131  	// TODO(mdempsky): Add a method that *is* unique.
   132  	ls := typ.LongString()
   133  	prevs := s.m[ls]
   134  	for _, prev := range prevs {
   135  		if types.Identical(typ, prev.typ) {
   136  			yyerrorl(pos, "duplicate case %v in type switch\n\tprevious case at %s", typ, linestr(prev.pos))
   137  			return
   138  		}
   139  	}
   140  	s.m[ls] = append(prevs, typeSetEntry{pos, typ})
   141  }
   142  
   143  func typecheckExprSwitch(n *Node) {
   144  	t := types.Types[TBOOL]
   145  	if n.Left != nil {
   146  		n.Left = typecheck(n.Left, ctxExpr)
   147  		n.Left = defaultlit(n.Left, nil)
   148  		t = n.Left.Type
   149  	}
   150  
   151  	var nilonly string
   152  	if t != nil {
   153  		switch {
   154  		case t.IsMap():
   155  			nilonly = "map"
   156  		case t.Etype == TFUNC:
   157  			nilonly = "func"
   158  		case t.IsSlice():
   159  			nilonly = "slice"
   160  
   161  		case !IsComparable(t):
   162  			if t.IsStruct() {
   163  				yyerrorl(n.Pos, "cannot switch on %L (struct containing %v cannot be compared)", n.Left, IncomparableField(t).Type)
   164  			} else {
   165  				yyerrorl(n.Pos, "cannot switch on %L", n.Left)
   166  			}
   167  			t = nil
   168  		}
   169  	}
   170  
   171  	var defCase *Node
   172  	var cs constSet
   173  	for _, ncase := range n.List.Slice() {
   174  		ls := ncase.List.Slice()
   175  		if len(ls) == 0 { // default:
   176  			if defCase != nil {
   177  				yyerrorl(ncase.Pos, "multiple defaults in switch (first at %v)", defCase.Line())
   178  			} else {
   179  				defCase = ncase
   180  			}
   181  		}
   182  
   183  		for i := range ls {
   184  			setlineno(ncase)
   185  			ls[i] = typecheck(ls[i], ctxExpr)
   186  			ls[i] = defaultlit(ls[i], t)
   187  			n1 := ls[i]
   188  			if t == nil || n1.Type == nil {
   189  				continue
   190  			}
   191  
   192  			switch {
   193  			case nilonly != "" && !n1.isNil():
   194  				yyerrorl(ncase.Pos, "invalid case %v in switch (can only compare %s %v to nil)", n1, nilonly, n.Left)
   195  			case t.IsInterface() && !n1.Type.IsInterface() && !IsComparable(n1.Type):
   196  				yyerrorl(ncase.Pos, "invalid case %L in switch (incomparable type)", n1)
   197  			case assignop(n1.Type, t, nil) == 0 && assignop(t, n1.Type, nil) == 0:
   198  				if n.Left != nil {
   199  					yyerrorl(ncase.Pos, "invalid case %v in switch on %v (mismatched types %v and %v)", n1, n.Left, n1.Type, t)
   200  				} else {
   201  					yyerrorl(ncase.Pos, "invalid case %v in switch (mismatched types %v and bool)", n1, n1.Type)
   202  				}
   203  			}
   204  
   205  			// Don't check for duplicate bools. Although the spec allows it,
   206  			// (1) the compiler hasn't checked it in the past, so compatibility mandates it, and
   207  			// (2) it would disallow useful things like
   208  			//       case GOARCH == "arm" && GOARM == "5":
   209  			//       case GOARCH == "arm":
   210  			//     which would both evaluate to false for non-ARM compiles.
   211  			if !n1.Type.IsBoolean() {
   212  				cs.add(ncase.Pos, n1, "case", "switch")
   213  			}
   214  		}
   215  
   216  		typecheckslice(ncase.Nbody.Slice(), ctxStmt)
   217  	}
   218  }
   219  
   220  // walkswitch walks a switch statement.
   221  func walkswitch(sw *Node) {
   222  	// Guard against double walk, see #25776.
   223  	if sw.List.Len() == 0 && sw.Nbody.Len() > 0 {
   224  		return // Was fatal, but eliminating every possible source of double-walking is hard
   225  	}
   226  
   227  	if sw.Left != nil && sw.Left.Op == OTYPESW {
   228  		walkTypeSwitch(sw)
   229  	} else {
   230  		walkExprSwitch(sw)
   231  	}
   232  }
   233  
   234  // walkExprSwitch generates an AST implementing sw.  sw is an
   235  // expression switch.
   236  func walkExprSwitch(sw *Node) {
   237  	lno := setlineno(sw)
   238  
   239  	cond := sw.Left
   240  	sw.Left = nil
   241  
   242  	// convert switch {...} to switch true {...}
   243  	if cond == nil {
   244  		cond = nodbool(true)
   245  		cond = typecheck(cond, ctxExpr)
   246  		cond = defaultlit(cond, nil)
   247  	}
   248  
   249  	// Given "switch string(byteslice)",
   250  	// with all cases being side-effect free,
   251  	// use a zero-cost alias of the byte slice.
   252  	// Do this before calling walkexpr on cond,
   253  	// because walkexpr will lower the string
   254  	// conversion into a runtime call.
   255  	// See issue 24937 for more discussion.
   256  	if cond.Op == OBYTES2STR && allCaseExprsAreSideEffectFree(sw) {
   257  		cond.Op = OBYTES2STRTMP
   258  	}
   259  
   260  	cond = walkexpr(cond, &sw.Ninit)
   261  	if cond.Op != OLITERAL {
   262  		cond = copyexpr(cond, cond.Type, &sw.Nbody)
   263  	}
   264  
   265  	lineno = lno
   266  
   267  	s := exprSwitch{
   268  		exprname: cond,
   269  	}
   270  
   271  	var defaultGoto *Node
   272  	var body Nodes
   273  	for _, ncase := range sw.List.Slice() {
   274  		label := autolabel(".s")
   275  		jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
   276  
   277  		// Process case dispatch.
   278  		if ncase.List.Len() == 0 {
   279  			if defaultGoto != nil {
   280  				Fatalf("duplicate default case not detected during typechecking")
   281  			}
   282  			defaultGoto = jmp
   283  		}
   284  
   285  		for _, n1 := range ncase.List.Slice() {
   286  			s.Add(ncase.Pos, n1, jmp)
   287  		}
   288  
   289  		// Process body.
   290  		body.Append(npos(ncase.Pos, nodSym(OLABEL, nil, label)))
   291  		body.Append(ncase.Nbody.Slice()...)
   292  		if fall, pos := hasFall(ncase.Nbody.Slice()); !fall {
   293  			br := nod(OBREAK, nil, nil)
   294  			br.Pos = pos
   295  			body.Append(br)
   296  		}
   297  	}
   298  	sw.List.Set(nil)
   299  
   300  	if defaultGoto == nil {
   301  		br := nod(OBREAK, nil, nil)
   302  		br.Pos = br.Pos.WithNotStmt()
   303  		defaultGoto = br
   304  	}
   305  
   306  	s.Emit(&sw.Nbody)
   307  	sw.Nbody.Append(defaultGoto)
   308  	sw.Nbody.AppendNodes(&body)
   309  	walkstmtlist(sw.Nbody.Slice())
   310  }
   311  
   312  // An exprSwitch walks an expression switch.
   313  type exprSwitch struct {
   314  	exprname *Node // value being switched on
   315  
   316  	done    Nodes
   317  	clauses []exprClause
   318  }
   319  
   320  type exprClause struct {
   321  	pos    src.XPos
   322  	lo, hi *Node
   323  	jmp    *Node
   324  }
   325  
   326  func (s *exprSwitch) Add(pos src.XPos, expr, jmp *Node) {
   327  	c := exprClause{pos: pos, lo: expr, hi: expr, jmp: jmp}
   328  	if okforcmp[s.exprname.Type.Etype] && expr.Op == OLITERAL {
   329  		s.clauses = append(s.clauses, c)
   330  		return
   331  	}
   332  
   333  	s.flush()
   334  	s.clauses = append(s.clauses, c)
   335  	s.flush()
   336  }
   337  
   338  func (s *exprSwitch) Emit(out *Nodes) {
   339  	s.flush()
   340  	out.AppendNodes(&s.done)
   341  }
   342  
   343  func (s *exprSwitch) flush() {
   344  	cc := s.clauses
   345  	s.clauses = nil
   346  	if len(cc) == 0 {
   347  		return
   348  	}
   349  
   350  	// Caution: If len(cc) == 1, then cc[0] might not an OLITERAL.
   351  	// The code below is structured to implicitly handle this case
   352  	// (e.g., sort.Slice doesn't need to invoke the less function
   353  	// when there's only a single slice element).
   354  
   355  	if s.exprname.Type.IsString() && len(cc) >= 2 {
   356  		// Sort strings by length and then by value. It is
   357  		// much cheaper to compare lengths than values, and
   358  		// all we need here is consistency. We respect this
   359  		// sorting below.
   360  		sort.Slice(cc, func(i, j int) bool {
   361  			si := strlit(cc[i].lo)
   362  			sj := strlit(cc[j].lo)
   363  			if len(si) != len(sj) {
   364  				return len(si) < len(sj)
   365  			}
   366  			return si < sj
   367  		})
   368  
   369  		// runLen returns the string length associated with a
   370  		// particular run of exprClauses.
   371  		runLen := func(run []exprClause) int64 { return int64(len(strlit(run[0].lo))) }
   372  
   373  		// Collapse runs of consecutive strings with the same length.
   374  		var runs [][]exprClause
   375  		start := 0
   376  		for i := 1; i < len(cc); i++ {
   377  			if runLen(cc[start:]) != runLen(cc[i:]) {
   378  				runs = append(runs, cc[start:i])
   379  				start = i
   380  			}
   381  		}
   382  		runs = append(runs, cc[start:])
   383  
   384  		// Perform two-level binary search.
   385  		nlen := nod(OLEN, s.exprname, nil)
   386  		binarySearch(len(runs), &s.done,
   387  			func(i int) *Node {
   388  				return nod(OLE, nlen, nodintconst(runLen(runs[i-1])))
   389  			},
   390  			func(i int, nif *Node) {
   391  				run := runs[i]
   392  				nif.Left = nod(OEQ, nlen, nodintconst(runLen(run)))
   393  				s.search(run, &nif.Nbody)
   394  			},
   395  		)
   396  		return
   397  	}
   398  
   399  	sort.Slice(cc, func(i, j int) bool {
   400  		return compareOp(cc[i].lo.Val(), OLT, cc[j].lo.Val())
   401  	})
   402  
   403  	// Merge consecutive integer cases.
   404  	if s.exprname.Type.IsInteger() {
   405  		merged := cc[:1]
   406  		for _, c := range cc[1:] {
   407  			last := &merged[len(merged)-1]
   408  			if last.jmp == c.jmp && last.hi.Int64()+1 == c.lo.Int64() {
   409  				last.hi = c.lo
   410  			} else {
   411  				merged = append(merged, c)
   412  			}
   413  		}
   414  		cc = merged
   415  	}
   416  
   417  	s.search(cc, &s.done)
   418  }
   419  
   420  func (s *exprSwitch) search(cc []exprClause, out *Nodes) {
   421  	binarySearch(len(cc), out,
   422  		func(i int) *Node {
   423  			return nod(OLE, s.exprname, cc[i-1].hi)
   424  		},
   425  		func(i int, nif *Node) {
   426  			c := &cc[i]
   427  			nif.Left = c.test(s.exprname)
   428  			nif.Nbody.Set1(c.jmp)
   429  		},
   430  	)
   431  }
   432  
   433  func (c *exprClause) test(exprname *Node) *Node {
   434  	// Integer range.
   435  	if c.hi != c.lo {
   436  		low := nodl(c.pos, OGE, exprname, c.lo)
   437  		high := nodl(c.pos, OLE, exprname, c.hi)
   438  		return nodl(c.pos, OANDAND, low, high)
   439  	}
   440  
   441  	// Optimize "switch true { ...}" and "switch false { ... }".
   442  	if Isconst(exprname, CTBOOL) && !c.lo.Type.IsInterface() {
   443  		if exprname.Val().U.(bool) {
   444  			return c.lo
   445  		} else {
   446  			return nodl(c.pos, ONOT, c.lo, nil)
   447  		}
   448  	}
   449  
   450  	return nodl(c.pos, OEQ, exprname, c.lo)
   451  }
   452  
   453  func allCaseExprsAreSideEffectFree(sw *Node) bool {
   454  	// In theory, we could be more aggressive, allowing any
   455  	// side-effect-free expressions in cases, but it's a bit
   456  	// tricky because some of that information is unavailable due
   457  	// to the introduction of temporaries during order.
   458  	// Restricting to constants is simple and probably powerful
   459  	// enough.
   460  
   461  	for _, ncase := range sw.List.Slice() {
   462  		if ncase.Op != OCASE {
   463  			Fatalf("switch string(byteslice) bad op: %v", ncase.Op)
   464  		}
   465  		for _, v := range ncase.List.Slice() {
   466  			if v.Op != OLITERAL {
   467  				return false
   468  			}
   469  		}
   470  	}
   471  	return true
   472  }
   473  
   474  // hasFall reports whether stmts ends with a "fallthrough" statement.
   475  func hasFall(stmts []*Node) (bool, src.XPos) {
   476  	// Search backwards for the index of the fallthrough
   477  	// statement. Do not assume it'll be in the last
   478  	// position, since in some cases (e.g. when the statement
   479  	// list contains autotmp_ variables), one or more OVARKILL
   480  	// nodes will be at the end of the list.
   481  
   482  	i := len(stmts) - 1
   483  	for i >= 0 && stmts[i].Op == OVARKILL {
   484  		i--
   485  	}
   486  	if i < 0 {
   487  		return false, src.NoXPos
   488  	}
   489  	return stmts[i].Op == OFALL, stmts[i].Pos
   490  }
   491  
   492  // walkTypeSwitch generates an AST that implements sw, where sw is a
   493  // type switch.
   494  func walkTypeSwitch(sw *Node) {
   495  	var s typeSwitch
   496  	s.facename = sw.Left.Right
   497  	sw.Left = nil
   498  
   499  	s.facename = walkexpr(s.facename, &sw.Ninit)
   500  	s.facename = copyexpr(s.facename, s.facename.Type, &sw.Nbody)
   501  	s.okname = temp(types.Types[TBOOL])
   502  
   503  	// Get interface descriptor word.
   504  	// For empty interfaces this will be the type.
   505  	// For non-empty interfaces this will be the itab.
   506  	itab := nod(OITAB, s.facename, nil)
   507  
   508  	// For empty interfaces, do:
   509  	//     if e._type == nil {
   510  	//         do nil case if it exists, otherwise default
   511  	//     }
   512  	//     h := e._type.hash
   513  	// Use a similar strategy for non-empty interfaces.
   514  	ifNil := nod(OIF, nil, nil)
   515  	ifNil.Left = nod(OEQ, itab, nodnil())
   516  	lineno = lineno.WithNotStmt() // disable statement marks after the first check.
   517  	ifNil.Left = typecheck(ifNil.Left, ctxExpr)
   518  	ifNil.Left = defaultlit(ifNil.Left, nil)
   519  	// ifNil.Nbody assigned at end.
   520  	sw.Nbody.Append(ifNil)
   521  
   522  	// Load hash from type or itab.
   523  	dotHash := nodSym(ODOTPTR, itab, nil)
   524  	dotHash.Type = types.Types[TUINT32]
   525  	dotHash.SetTypecheck(1)
   526  	if s.facename.Type.IsEmptyInterface() {
   527  		dotHash.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
   528  	} else {
   529  		dotHash.Xoffset = int64(2 * Widthptr) // offset of hash in runtime.itab
   530  	}
   531  	dotHash.SetBounded(true) // guaranteed not to fault
   532  	s.hashname = copyexpr(dotHash, dotHash.Type, &sw.Nbody)
   533  
   534  	br := nod(OBREAK, nil, nil)
   535  	var defaultGoto, nilGoto *Node
   536  	var body Nodes
   537  	for _, ncase := range sw.List.Slice() {
   538  		var caseVar *Node
   539  		if ncase.Rlist.Len() != 0 {
   540  			caseVar = ncase.Rlist.First()
   541  		}
   542  
   543  		// For single-type cases, we initialize the case
   544  		// variable as part of the type assertion; but in
   545  		// other cases, we initialize it in the body.
   546  		singleType := ncase.List.Len() == 1 && ncase.List.First().Op == OTYPE
   547  
   548  		label := autolabel(".s")
   549  		jmp := npos(ncase.Pos, nodSym(OGOTO, nil, label))
   550  
   551  		if ncase.List.Len() == 0 { // default:
   552  			if defaultGoto != nil {
   553  				Fatalf("duplicate default case not detected during typechecking")
   554  			}
   555  			defaultGoto = jmp
   556  		}
   557  
   558  		for _, n1 := range ncase.List.Slice() {
   559  			if n1.isNil() { // case nil:
   560  				if nilGoto != nil {
   561  					Fatalf("duplicate nil case not detected during typechecking")
   562  				}
   563  				nilGoto = jmp
   564  				continue
   565  			}
   566  
   567  			if singleType {
   568  				s.Add(n1.Type, caseVar, jmp)
   569  			} else {
   570  				s.Add(n1.Type, nil, jmp)
   571  			}
   572  		}
   573  
   574  		body.Append(npos(ncase.Pos, nodSym(OLABEL, nil, label)))
   575  		if caseVar != nil && !singleType {
   576  			l := []*Node{
   577  				nodl(ncase.Pos, ODCL, caseVar, nil),
   578  				nodl(ncase.Pos, OAS, caseVar, s.facename),
   579  			}
   580  			typecheckslice(l, ctxStmt)
   581  			body.Append(l...)
   582  		}
   583  		body.Append(ncase.Nbody.Slice()...)
   584  		body.Append(br)
   585  	}
   586  	sw.List.Set(nil)
   587  
   588  	if defaultGoto == nil {
   589  		defaultGoto = br
   590  	}
   591  	if nilGoto == nil {
   592  		nilGoto = defaultGoto
   593  	}
   594  	ifNil.Nbody.Set1(nilGoto)
   595  
   596  	s.Emit(&sw.Nbody)
   597  	sw.Nbody.Append(defaultGoto)
   598  	sw.Nbody.AppendNodes(&body)
   599  
   600  	walkstmtlist(sw.Nbody.Slice())
   601  }
   602  
   603  // A typeSwitch walks a type switch.
   604  type typeSwitch struct {
   605  	// Temporary variables (i.e., ONAMEs) used by type switch dispatch logic:
   606  	facename *Node // value being type-switched on
   607  	hashname *Node // type hash of the value being type-switched on
   608  	okname   *Node // boolean used for comma-ok type assertions
   609  
   610  	done    Nodes
   611  	clauses []typeClause
   612  }
   613  
   614  type typeClause struct {
   615  	hash uint32
   616  	body Nodes
   617  }
   618  
   619  func (s *typeSwitch) Add(typ *types.Type, caseVar *Node, jmp *Node) {
   620  	var body Nodes
   621  	if caseVar != nil {
   622  		l := []*Node{
   623  			nod(ODCL, caseVar, nil),
   624  			nod(OAS, caseVar, nil),
   625  		}
   626  		typecheckslice(l, ctxStmt)
   627  		body.Append(l...)
   628  	} else {
   629  		caseVar = nblank
   630  	}
   631  
   632  	// cv, ok = iface.(type)
   633  	as := nod(OAS2, nil, nil)
   634  	as.List.Set2(caseVar, s.okname) // cv, ok =
   635  	dot := nod(ODOTTYPE, s.facename, nil)
   636  	dot.Type = typ // iface.(type)
   637  	as.Rlist.Set1(dot)
   638  	as = typecheck(as, ctxStmt)
   639  	as = walkexpr(as, &body)
   640  	body.Append(as)
   641  
   642  	// if ok { goto label }
   643  	nif := nod(OIF, nil, nil)
   644  	nif.Left = s.okname
   645  	nif.Nbody.Set1(jmp)
   646  	body.Append(nif)
   647  
   648  	if !typ.IsInterface() {
   649  		s.clauses = append(s.clauses, typeClause{
   650  			hash: typehash(typ),
   651  			body: body,
   652  		})
   653  		return
   654  	}
   655  
   656  	s.flush()
   657  	s.done.AppendNodes(&body)
   658  }
   659  
   660  func (s *typeSwitch) Emit(out *Nodes) {
   661  	s.flush()
   662  	out.AppendNodes(&s.done)
   663  }
   664  
   665  func (s *typeSwitch) flush() {
   666  	cc := s.clauses
   667  	s.clauses = nil
   668  	if len(cc) == 0 {
   669  		return
   670  	}
   671  
   672  	sort.Slice(cc, func(i, j int) bool { return cc[i].hash < cc[j].hash })
   673  
   674  	// Combine adjacent cases with the same hash.
   675  	merged := cc[:1]
   676  	for _, c := range cc[1:] {
   677  		last := &merged[len(merged)-1]
   678  		if last.hash == c.hash {
   679  			last.body.AppendNodes(&c.body)
   680  		} else {
   681  			merged = append(merged, c)
   682  		}
   683  	}
   684  	cc = merged
   685  
   686  	binarySearch(len(cc), &s.done,
   687  		func(i int) *Node {
   688  			return nod(OLE, s.hashname, nodintconst(int64(cc[i-1].hash)))
   689  		},
   690  		func(i int, nif *Node) {
   691  			// TODO(mdempsky): Omit hash equality check if
   692  			// there's only one type.
   693  			c := cc[i]
   694  			nif.Left = nod(OEQ, s.hashname, nodintconst(int64(c.hash)))
   695  			nif.Nbody.AppendNodes(&c.body)
   696  		},
   697  	)
   698  }
   699  
   700  // binarySearch constructs a binary search tree for handling n cases,
   701  // and appends it to out. It's used for efficiently implementing
   702  // switch statements.
   703  //
   704  // less(i) should return a boolean expression. If it evaluates true,
   705  // then cases before i will be tested; otherwise, cases i and later.
   706  //
   707  // base(i, nif) should setup nif (an OIF node) to test case i. In
   708  // particular, it should set nif.Left and nif.Nbody.
   709  func binarySearch(n int, out *Nodes, less func(i int) *Node, base func(i int, nif *Node)) {
   710  	const binarySearchMin = 4 // minimum number of cases for binary search
   711  
   712  	var do func(lo, hi int, out *Nodes)
   713  	do = func(lo, hi int, out *Nodes) {
   714  		n := hi - lo
   715  		if n < binarySearchMin {
   716  			for i := lo; i < hi; i++ {
   717  				nif := nod(OIF, nil, nil)
   718  				base(i, nif)
   719  				lineno = lineno.WithNotStmt()
   720  				nif.Left = typecheck(nif.Left, ctxExpr)
   721  				nif.Left = defaultlit(nif.Left, nil)
   722  				out.Append(nif)
   723  				out = &nif.Rlist
   724  			}
   725  			return
   726  		}
   727  
   728  		half := lo + n/2
   729  		nif := nod(OIF, nil, nil)
   730  		nif.Left = less(half)
   731  		lineno = lineno.WithNotStmt()
   732  		nif.Left = typecheck(nif.Left, ctxExpr)
   733  		nif.Left = defaultlit(nif.Left, nil)
   734  		do(lo, half, &nif.Nbody)
   735  		do(half, hi, &nif.Rlist)
   736  		out.Append(nif)
   737  	}
   738  
   739  	do(0, n, out)
   740  }