[dev.cc] cmd/internal/gc, cmd/new6g etc: convert from cmd/gc, cmd/6g etc

First draft of converted Go compiler, using rsc.io/c2go rev 83d795a.

Change-Id: I29f4c7010de07d2ff1947bbca9865879d83c32c3
Reviewed-on: https://go-review.googlesource.com/4851
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/src/cmd/internal/gc/swt.go b/src/cmd/internal/gc/swt.go
new file mode 100644
index 0000000..cf1f7d4
--- /dev/null
+++ b/src/cmd/internal/gc/swt.go
@@ -0,0 +1,1028 @@
+// 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/internal/obj"
+	"fmt"
+)
+
+const (
+	Snorm = 0 + iota
+	Strue
+	Sfalse
+	Stype
+	Tdefault
+	Texprconst
+	Texprvar
+	Ttypenil
+	Ttypeconst
+	Ttypevar
+	Ncase = 4
+)
+
+type Case struct {
+	node    *Node
+	hash    uint32
+	type_   uint8
+	diag    uint8
+	ordinal uint16
+	link    *Case
+}
+
+var C *Case
+
+func dumpcase(c0 *Case) {
+	var c *Case
+
+	for c = c0; c != nil; c = c.link {
+		switch c.type_ {
+		case Tdefault:
+			fmt.Printf("case-default\n")
+			fmt.Printf("\tord=%d\n", c.ordinal)
+
+		case Texprconst:
+			fmt.Printf("case-exprconst\n")
+			fmt.Printf("\tord=%d\n", c.ordinal)
+
+		case Texprvar:
+			fmt.Printf("case-exprvar\n")
+			fmt.Printf("\tord=%d\n", c.ordinal)
+			fmt.Printf("\top=%v\n", Oconv(int(c.node.Left.Op), 0))
+
+		case Ttypenil:
+			fmt.Printf("case-typenil\n")
+			fmt.Printf("\tord=%d\n", c.ordinal)
+
+		case Ttypeconst:
+			fmt.Printf("case-typeconst\n")
+			fmt.Printf("\tord=%d\n", c.ordinal)
+			fmt.Printf("\thash=%x\n", c.hash)
+
+		case Ttypevar:
+			fmt.Printf("case-typevar\n")
+			fmt.Printf("\tord=%d\n", c.ordinal)
+
+		default:
+			fmt.Printf("case-???\n")
+			fmt.Printf("\tord=%d\n", c.ordinal)
+			fmt.Printf("\top=%v\n", Oconv(int(c.node.Left.Op), 0))
+			fmt.Printf("\thash=%x\n", c.hash)
+		}
+	}
+
+	fmt.Printf("\n")
+}
+
+func ordlcmp(c1 *Case, c2 *Case) int {
+	// sort default first
+	if c1.type_ == Tdefault {
+		return -1
+	}
+	if c2.type_ == Tdefault {
+		return +1
+	}
+
+	// sort nil second
+	if c1.type_ == Ttypenil {
+		return -1
+	}
+	if c2.type_ == Ttypenil {
+		return +1
+	}
+
+	// sort by ordinal
+	if c1.ordinal > c2.ordinal {
+		return +1
+	}
+	if c1.ordinal < c2.ordinal {
+		return -1
+	}
+	return 0
+}
+
+func exprcmp(c1 *Case, c2 *Case) int {
+	var ct int
+	var n int
+	var n1 *Node
+	var n2 *Node
+
+	// sort non-constants last
+	if c1.type_ != Texprconst {
+		return +1
+	}
+	if c2.type_ != Texprconst {
+		return -1
+	}
+
+	n1 = c1.node.Left
+	n2 = c2.node.Left
+
+	// sort by type (for switches on interface)
+	ct = int(n1.Val.Ctype)
+
+	if ct != int(n2.Val.Ctype) {
+		return ct - int(n2.Val.Ctype)
+	}
+	if !Eqtype(n1.Type, n2.Type) {
+		if n1.Type.Vargen > n2.Type.Vargen {
+			return +1
+		} else {
+			return -1
+		}
+	}
+
+	// sort by constant value
+	n = 0
+
+	switch ct {
+	case CTFLT:
+		n = mpcmpfltflt(n1.Val.U.Fval, n2.Val.U.Fval)
+
+	case CTINT,
+		CTRUNE:
+		n = Mpcmpfixfix(n1.Val.U.Xval, n2.Val.U.Xval)
+
+	case CTSTR:
+		n = cmpslit(n1, n2)
+	}
+
+	return n
+}
+
+func typecmp(c1 *Case, c2 *Case) int {
+	// sort non-constants last
+	if c1.type_ != Ttypeconst {
+		return +1
+	}
+	if c2.type_ != Ttypeconst {
+		return -1
+	}
+
+	// sort by hash code
+	if c1.hash > c2.hash {
+		return +1
+	}
+	if c1.hash < c2.hash {
+		return -1
+	}
+
+	// sort by ordinal so duplicate error
+	// happens on later case.
+	if c1.ordinal > c2.ordinal {
+		return +1
+	}
+	if c1.ordinal < c2.ordinal {
+		return -1
+	}
+	return 0
+}
+
+func csort(l *Case, f func(*Case, *Case) int) *Case {
+	var l1 *Case
+	var l2 *Case
+	var le *Case
+
+	if l == nil || l.link == nil {
+		return l
+	}
+
+	l1 = l
+	l2 = l
+	for {
+		l2 = l2.link
+		if l2 == nil {
+			break
+		}
+		l2 = l2.link
+		if l2 == nil {
+			break
+		}
+		l1 = l1.link
+	}
+
+	l2 = l1.link
+	l1.link = nil
+	l1 = csort(l, f)
+	l2 = csort(l2, f)
+
+	/* set up lead element */
+	if f(l1, l2) < 0 {
+		l = l1
+		l1 = l1.link
+	} else {
+		l = l2
+		l2 = l2.link
+	}
+
+	le = l
+
+	for {
+		if l1 == nil {
+			for l2 != nil {
+				le.link = l2
+				le = l2
+				l2 = l2.link
+			}
+
+			le.link = nil
+			break
+		}
+
+		if l2 == nil {
+			for l1 != nil {
+				le.link = l1
+				le = l1
+				l1 = l1.link
+			}
+
+			break
+		}
+
+		if f(l1, l2) < 0 {
+			le.link = l1
+			le = l1
+			l1 = l1.link
+		} else {
+			le.link = l2
+			le = l2
+			l2 = l2.link
+		}
+	}
+
+	le.link = nil
+	return l
+}
+
+var newlabel_swt_label int
+
+func newlabel_swt() *Node {
+	newlabel_swt_label++
+	namebuf = fmt.Sprintf("%.6d", newlabel_swt_label)
+	return newname(Lookup(namebuf))
+}
+
+/*
+ * build separate list of statements and cases
+ * make labels between cases and statements
+ * deal with fallthrough, break, unreachable statements
+ */
+func casebody(sw *Node, typeswvar *Node) {
+	var n *Node
+	var c *Node
+	var last *Node
+	var def *Node
+	var cas *NodeList
+	var stat *NodeList
+	var l *NodeList
+	var lc *NodeList
+	var go_ *Node
+	var br *Node
+	var lno int32
+	var needvar int32
+
+	if sw.List == nil {
+		return
+	}
+
+	lno = setlineno(sw)
+
+	cas = nil  // cases
+	stat = nil // statements
+	def = nil  // defaults
+	br = Nod(OBREAK, nil, nil)
+
+	for l = sw.List; l != nil; l = l.Next {
+		n = l.N
+		setlineno(n)
+		if n.Op != OXCASE {
+			Fatal("casebody %v", Oconv(int(n.Op), 0))
+		}
+		n.Op = OCASE
+		needvar = int32(bool2int(count(n.List) != 1 || n.List.N.Op == OLITERAL))
+
+		go_ = Nod(OGOTO, newlabel_swt(), nil)
+		if n.List == nil {
+			if def != nil {
+				Yyerror("more than one default case")
+			}
+
+			// reuse original default case
+			n.Right = go_
+
+			def = n
+		}
+
+		if n.List != nil && n.List.Next == nil {
+			// one case - reuse OCASE node.
+			c = n.List.N
+
+			n.Left = c
+			n.Right = go_
+			n.List = nil
+			cas = list(cas, n)
+		} else {
+			// expand multi-valued cases
+			for lc = n.List; lc != nil; lc = lc.Next {
+				c = lc.N
+				cas = list(cas, Nod(OCASE, c, go_))
+			}
+		}
+
+		stat = list(stat, Nod(OLABEL, go_.Left, nil))
+		if typeswvar != nil && needvar != 0 && n.Nname != nil {
+			var l *NodeList
+
+			l = list1(Nod(ODCL, n.Nname, nil))
+			l = list(l, Nod(OAS, n.Nname, typeswvar))
+			typechecklist(l, Etop)
+			stat = concat(stat, l)
+		}
+
+		stat = concat(stat, n.Nbody)
+
+		// botch - shouldn't fall thru declaration
+		last = stat.End.N
+
+		if last.Xoffset == n.Xoffset && last.Op == OXFALL {
+			if typeswvar != nil {
+				setlineno(last)
+				Yyerror("cannot fallthrough in type switch")
+			}
+
+			if l.Next == nil {
+				setlineno(last)
+				Yyerror("cannot fallthrough final case in switch")
+			}
+
+			last.Op = OFALL
+		} else {
+			stat = list(stat, br)
+		}
+	}
+
+	stat = list(stat, br)
+	if def != nil {
+		cas = list(cas, def)
+	}
+
+	sw.List = cas
+	sw.Nbody = stat
+	lineno = lno
+}
+
+func mkcaselist(sw *Node, arg int) *Case {
+	var n *Node
+	var c *Case
+	var c1 *Case
+	var c2 *Case
+	var l *NodeList
+	var ord int
+
+	c = nil
+	ord = 0
+
+	for l = sw.List; l != nil; l = l.Next {
+		n = l.N
+		c1 = new(Case)
+		c1.link = c
+		c = c1
+
+		ord++
+		if int(uint16(ord)) != ord {
+			Fatal("too many cases in switch")
+		}
+		c.ordinal = uint16(ord)
+		c.node = n
+
+		if n.Left == nil {
+			c.type_ = Tdefault
+			continue
+		}
+
+		switch arg {
+		case Stype:
+			c.hash = 0
+			if n.Left.Op == OLITERAL {
+				c.type_ = Ttypenil
+				continue
+			}
+
+			if Istype(n.Left.Type, TINTER) != 0 {
+				c.type_ = Ttypevar
+				continue
+			}
+
+			c.hash = typehash(n.Left.Type)
+			c.type_ = Ttypeconst
+			continue
+
+		case Snorm,
+			Strue,
+			Sfalse:
+			c.type_ = Texprvar
+			c.hash = typehash(n.Left.Type)
+			switch consttype(n.Left) {
+			case CTFLT,
+				CTINT,
+				CTRUNE,
+				CTSTR:
+				c.type_ = Texprconst
+			}
+
+			continue
+		}
+	}
+
+	if c == nil {
+		return nil
+	}
+
+	// sort by value and diagnose duplicate cases
+	switch arg {
+	case Stype:
+		c = csort(c, typecmp)
+		for c1 = c; c1 != nil; c1 = c1.link {
+			for c2 = c1.link; c2 != nil && c2.hash == c1.hash; c2 = c2.link {
+				if c1.type_ == Ttypenil || c1.type_ == Tdefault {
+					break
+				}
+				if c2.type_ == Ttypenil || c2.type_ == Tdefault {
+					break
+				}
+				if !Eqtype(c1.node.Left.Type, c2.node.Left.Type) {
+					continue
+				}
+				yyerrorl(int(c2.node.Lineno), "duplicate case %v in type switch\n\tprevious case at %v", Tconv(c2.node.Left.Type, 0), c1.node.Line())
+			}
+		}
+
+	case Snorm,
+		Strue,
+		Sfalse:
+		c = csort(c, exprcmp)
+		for c1 = c; c1.link != nil; c1 = c1.link {
+			if exprcmp(c1, c1.link) != 0 {
+				continue
+			}
+			setlineno(c1.link.node)
+			Yyerror("duplicate case %v in switch\n\tprevious case at %v", Nconv(c1.node.Left, 0), c1.node.Line())
+		}
+	}
+
+	// put list back in processing order
+	c = csort(c, ordlcmp)
+
+	return c
+}
+
+var exprname *Node
+
+func exprbsw(c0 *Case, ncase int, arg int) *Node {
+	var cas *NodeList
+	var a *Node
+	var n *Node
+	var c *Case
+	var i int
+	var half int
+	var lno int
+
+	cas = nil
+	if ncase < Ncase {
+		for i = 0; i < ncase; i++ {
+			n = c0.node
+			lno = int(setlineno(n))
+
+			if (arg != Strue && arg != Sfalse) || assignop(n.Left.Type, exprname.Type, nil) == OCONVIFACE || assignop(exprname.Type, n.Left.Type, nil) == OCONVIFACE {
+				a = Nod(OIF, nil, nil)
+				a.Ntest = Nod(OEQ, exprname, n.Left) // if name == val
+				typecheck(&a.Ntest, Erv)
+				a.Nbody = list1(n.Right) // then goto l
+			} else if arg == Strue {
+				a = Nod(OIF, nil, nil)
+				a.Ntest = n.Left         // if val
+				a.Nbody = list1(n.Right) // then goto l // arg == Sfalse
+			} else {
+				a = Nod(OIF, nil, nil)
+				a.Ntest = Nod(ONOT, n.Left, nil) // if !val
+				typecheck(&a.Ntest, Erv)
+				a.Nbody = list1(n.Right) // then goto l
+			}
+
+			cas = list(cas, a)
+			c0 = c0.link
+			lineno = int32(lno)
+		}
+
+		return liststmt(cas)
+	}
+
+	// find the middle and recur
+	c = c0
+
+	half = ncase >> 1
+	for i = 1; i < half; i++ {
+		c = c.link
+	}
+	a = Nod(OIF, nil, nil)
+	a.Ntest = Nod(OLE, exprname, c.node.Left)
+	typecheck(&a.Ntest, Erv)
+	a.Nbody = list1(exprbsw(c0, half, arg))
+	a.Nelse = list1(exprbsw(c.link, ncase-half, arg))
+	return a
+}
+
+/*
+ * normal (expression) switch.
+ * rebuild case statements into if .. goto
+ */
+func exprswitch(sw *Node) {
+	var def *Node
+	var cas *NodeList
+	var a *Node
+	var c0 *Case
+	var c *Case
+	var c1 *Case
+	var t *Type
+	var arg int
+	var ncase int
+
+	casebody(sw, nil)
+
+	arg = Snorm
+	if Isconst(sw.Ntest, CTBOOL) != 0 {
+		arg = Strue
+		if sw.Ntest.Val.U.Bval == 0 {
+			arg = Sfalse
+		}
+	}
+
+	walkexpr(&sw.Ntest, &sw.Ninit)
+	t = sw.Type
+	if t == nil {
+		return
+	}
+
+	/*
+	 * convert the switch into OIF statements
+	 */
+	exprname = nil
+
+	cas = nil
+	if arg == Strue || arg == Sfalse {
+		exprname = Nodbool(bool2int(arg == Strue))
+	} else if consttype(sw.Ntest) >= 0 {
+		// leave constants to enable dead code elimination (issue 9608)
+		exprname = sw.Ntest
+	} else {
+		exprname = temp(sw.Ntest.Type)
+		cas = list1(Nod(OAS, exprname, sw.Ntest))
+		typechecklist(cas, Etop)
+	}
+
+	c0 = mkcaselist(sw, arg)
+	if c0 != nil && c0.type_ == Tdefault {
+		def = c0.node.Right
+		c0 = c0.link
+	} else {
+		def = Nod(OBREAK, nil, nil)
+	}
+
+loop:
+	if c0 == nil {
+		cas = list(cas, def)
+		sw.Nbody = concat(cas, sw.Nbody)
+		sw.List = nil
+		walkstmtlist(sw.Nbody)
+		return
+	}
+
+	// deal with the variables one-at-a-time
+	if !(okforcmp[t.Etype] != 0) || c0.type_ != Texprconst {
+		a = exprbsw(c0, 1, arg)
+		cas = list(cas, a)
+		c0 = c0.link
+		goto loop
+	}
+
+	// do binary search on run of constants
+	ncase = 1
+
+	for c = c0; c.link != nil; c = c.link {
+		if c.link.type_ != Texprconst {
+			break
+		}
+		ncase++
+	}
+
+	// break the chain at the count
+	c1 = c.link
+
+	c.link = nil
+
+	// sort and compile constants
+	c0 = csort(c0, exprcmp)
+
+	a = exprbsw(c0, ncase, arg)
+	cas = list(cas, a)
+
+	c0 = c1
+	goto loop
+}
+
+var hashname *Node
+
+var facename *Node
+
+var boolname *Node
+
+func typeone(t *Node) *Node {
+	var init *NodeList
+	var a *Node
+	var b *Node
+	var var_ *Node
+
+	var_ = t.Nname
+	init = nil
+	if var_ == nil {
+		typecheck(&nblank, Erv|Easgn)
+		var_ = nblank
+	} else {
+		init = list1(Nod(ODCL, var_, nil))
+	}
+
+	a = Nod(OAS2, nil, nil)
+	a.List = list(list1(var_), boolname) // var,bool =
+	b = Nod(ODOTTYPE, facename, nil)
+	b.Type = t.Left.Type // interface.(type)
+	a.Rlist = list1(b)
+	typecheck(&a, Etop)
+	init = list(init, a)
+
+	b = Nod(OIF, nil, nil)
+	b.Ntest = boolname
+	b.Nbody = list1(t.Right) // if bool { goto l }
+	a = liststmt(list(init, b))
+	return a
+}
+
+func typebsw(c0 *Case, ncase int) *Node {
+	var cas *NodeList
+	var a *Node
+	var n *Node
+	var c *Case
+	var i int
+	var half int
+
+	cas = nil
+
+	if ncase < Ncase {
+		for i = 0; i < ncase; i++ {
+			n = c0.node
+			if c0.type_ != Ttypeconst {
+				Fatal("typebsw")
+			}
+			a = Nod(OIF, nil, nil)
+			a.Ntest = Nod(OEQ, hashname, Nodintconst(int64(c0.hash)))
+			typecheck(&a.Ntest, Erv)
+			a.Nbody = list1(n.Right)
+			cas = list(cas, a)
+			c0 = c0.link
+		}
+
+		return liststmt(cas)
+	}
+
+	// find the middle and recur
+	c = c0
+
+	half = ncase >> 1
+	for i = 1; i < half; i++ {
+		c = c.link
+	}
+	a = Nod(OIF, nil, nil)
+	a.Ntest = Nod(OLE, hashname, Nodintconst(int64(c.hash)))
+	typecheck(&a.Ntest, Erv)
+	a.Nbody = list1(typebsw(c0, half))
+	a.Nelse = list1(typebsw(c.link, ncase-half))
+	return a
+}
+
+/*
+ * convert switch of the form
+ *	switch v := i.(type) { case t1: ..; case t2: ..; }
+ * into if statements
+ */
+func typeswitch(sw *Node) {
+	var def *Node
+	var cas *NodeList
+	var hash *NodeList
+	var a *Node
+	var n *Node
+	var c *Case
+	var c0 *Case
+	var c1 *Case
+	var ncase int
+	var t *Type
+	var v Val
+
+	if sw.Ntest == nil {
+		return
+	}
+	if sw.Ntest.Right == nil {
+		setlineno(sw)
+		Yyerror("type switch must have an assignment")
+		return
+	}
+
+	walkexpr(&sw.Ntest.Right, &sw.Ninit)
+	if !(Istype(sw.Ntest.Right.Type, TINTER) != 0) {
+		Yyerror("type switch must be on an interface")
+		return
+	}
+
+	cas = nil
+
+	/*
+	 * predeclare temporary variables
+	 * and the boolean var
+	 */
+	facename = temp(sw.Ntest.Right.Type)
+
+	a = Nod(OAS, facename, sw.Ntest.Right)
+	typecheck(&a, Etop)
+	cas = list(cas, a)
+
+	casebody(sw, facename)
+
+	boolname = temp(Types[TBOOL])
+	typecheck(&boolname, Erv)
+
+	hashname = temp(Types[TUINT32])
+	typecheck(&hashname, Erv)
+
+	t = sw.Ntest.Right.Type
+	if isnilinter(t) != 0 {
+		a = syslook("efacethash", 1)
+	} else {
+		a = syslook("ifacethash", 1)
+	}
+	argtype(a, t)
+	a = Nod(OCALL, a, nil)
+	a.List = list1(facename)
+	a = Nod(OAS, hashname, a)
+	typecheck(&a, Etop)
+	cas = list(cas, a)
+
+	c0 = mkcaselist(sw, Stype)
+	if c0 != nil && c0.type_ == Tdefault {
+		def = c0.node.Right
+		c0 = c0.link
+	} else {
+		def = Nod(OBREAK, nil, nil)
+	}
+
+	/*
+	 * insert if statement into each case block
+	 */
+	for c = c0; c != nil; c = c.link {
+		n = c.node
+		switch c.type_ {
+		case Ttypenil:
+			v.Ctype = CTNIL
+			a = Nod(OIF, nil, nil)
+			a.Ntest = Nod(OEQ, facename, nodlit(v))
+			typecheck(&a.Ntest, Erv)
+			a.Nbody = list1(n.Right) // if i==nil { goto l }
+			n.Right = a
+
+		case Ttypevar,
+			Ttypeconst:
+			n.Right = typeone(n)
+		}
+	}
+
+	/*
+	 * generate list of if statements, binary search for constant sequences
+	 */
+	for c0 != nil {
+		if c0.type_ != Ttypeconst {
+			n = c0.node
+			cas = list(cas, n.Right)
+			c0 = c0.link
+			continue
+		}
+
+		// identify run of constants
+		c = c0
+		c1 = c
+
+		for c.link != nil && c.link.type_ == Ttypeconst {
+			c = c.link
+		}
+		c0 = c.link
+		c.link = nil
+
+		// sort by hash
+		c1 = csort(c1, typecmp)
+
+		// for debugging: linear search
+		if false {
+			for c = c1; c != nil; c = c.link {
+				n = c.node
+				cas = list(cas, n.Right)
+			}
+
+			continue
+		}
+
+		// combine adjacent cases with the same hash
+		ncase = 0
+
+		for c = c1; c != nil; c = c.link {
+			ncase++
+			hash = list1(c.node.Right)
+			for c.link != nil && c.link.hash == c.hash {
+				hash = list(hash, c.link.node.Right)
+				c.link = c.link.link
+			}
+
+			c.node.Right = liststmt(hash)
+		}
+
+		// binary search among cases to narrow by hash
+		cas = list(cas, typebsw(c1, ncase))
+	}
+
+	if nerrors == 0 {
+		cas = list(cas, def)
+		sw.Nbody = concat(cas, sw.Nbody)
+		sw.List = nil
+		walkstmtlist(sw.Nbody)
+	}
+}
+
+func walkswitch(sw *Node) {
+	/*
+	 * reorder the body into (OLIST, cases, statements)
+	 * cases have OGOTO into statements.
+	 * both have inserted OBREAK statements
+	 */
+	if sw.Ntest == nil {
+		sw.Ntest = Nodbool(1)
+		typecheck(&sw.Ntest, Erv)
+	}
+
+	if sw.Ntest.Op == OTYPESW {
+		typeswitch(sw)
+
+		//dump("sw", sw);
+		return
+	}
+
+	exprswitch(sw)
+
+	// Discard old AST elements after a walk. They can confuse racewealk.
+	sw.Ntest = nil
+
+	sw.List = nil
+}
+
+/*
+ * type check switch statement
+ */
+func typecheckswitch(n *Node) {
+	var top int
+	var lno int
+	var ptr int
+	var nilonly string
+	var t *Type
+	var badtype *Type
+	var missing *Type
+	var have *Type
+	var l *NodeList
+	var ll *NodeList
+	var ncase *Node
+	var nvar *Node
+	var def *Node
+
+	lno = int(lineno)
+	typechecklist(n.Ninit, Etop)
+	nilonly = ""
+
+	if n.Ntest != nil && n.Ntest.Op == OTYPESW {
+		// type switch
+		top = Etype
+
+		typecheck(&n.Ntest.Right, Erv)
+		t = n.Ntest.Right.Type
+		if t != nil && t.Etype != TINTER {
+			Yyerror("cannot type switch on non-interface value %v", Nconv(n.Ntest.Right, obj.FmtLong))
+		}
+	} else {
+		// value switch
+		top = Erv
+
+		if n.Ntest != nil {
+			typecheck(&n.Ntest, Erv)
+			defaultlit(&n.Ntest, nil)
+			t = n.Ntest.Type
+		} else {
+			t = Types[TBOOL]
+		}
+		if t != nil {
+			if !(okforeq[t.Etype] != 0) {
+				Yyerror("cannot switch on %v", Nconv(n.Ntest, obj.FmtLong))
+			} else if t.Etype == TARRAY && !(Isfixedarray(t) != 0) {
+				nilonly = "slice"
+			} else if t.Etype == TARRAY && Isfixedarray(t) != 0 && algtype1(t, nil) == ANOEQ {
+				Yyerror("cannot switch on %v", Nconv(n.Ntest, obj.FmtLong))
+			} else if t.Etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ {
+				Yyerror("cannot switch on %v (struct containing %v cannot be compared)", Nconv(n.Ntest, obj.FmtLong), Tconv(badtype, 0))
+			} else if t.Etype == TFUNC {
+				nilonly = "func"
+			} else if t.Etype == TMAP {
+				nilonly = "map"
+			}
+		}
+	}
+
+	n.Type = t
+
+	def = nil
+	for l = n.List; l != nil; l = l.Next {
+		ncase = l.N
+		setlineno(n)
+		if ncase.List == nil {
+			// default
+			if def != nil {
+				Yyerror("multiple defaults in switch (first at %v)", def.Line())
+			} else {
+				def = ncase
+			}
+		} else {
+			for ll = ncase.List; ll != nil; ll = ll.Next {
+				setlineno(ll.N)
+				typecheck(&ll.N, Erv|Etype)
+				if ll.N.Type == nil || t == nil {
+					continue
+				}
+				setlineno(ncase)
+				switch top {
+				case Erv: // expression switch
+					defaultlit(&ll.N, t)
+
+					if ll.N.Op == OTYPE {
+						Yyerror("type %v is not an expression", Tconv(ll.N.Type, 0))
+					} else if ll.N.Type != nil && !(assignop(ll.N.Type, t, nil) != 0) && !(assignop(t, ll.N.Type, nil) != 0) {
+						if n.Ntest != nil {
+							Yyerror("invalid case %v in switch on %v (mismatched types %v and %v)", Nconv(ll.N, 0), Nconv(n.Ntest, 0), Tconv(ll.N.Type, 0), Tconv(t, 0))
+						} else {
+							Yyerror("invalid case %v in switch (mismatched types %v and bool)", Nconv(ll.N, 0), Tconv(ll.N.Type, 0))
+						}
+					} else if nilonly != "" && !(Isconst(ll.N, CTNIL) != 0) {
+						Yyerror("invalid case %v in switch (can only compare %s %v to nil)", Nconv(ll.N, 0), nilonly, Nconv(n.Ntest, 0))
+					}
+
+				case Etype: // type switch
+					if ll.N.Op == OLITERAL && Istype(ll.N.Type, TNIL) != 0 {
+					} else if ll.N.Op != OTYPE && ll.N.Type != nil { // should this be ||?
+						Yyerror("%v is not a type", Nconv(ll.N, obj.FmtLong))
+
+						// reset to original type
+						ll.N = n.Ntest.Right
+					} else if ll.N.Type.Etype != TINTER && t.Etype == TINTER && !(implements(ll.N.Type, t, &missing, &have, &ptr) != 0) {
+						if have != nil && !(missing.Broke != 0) && !(have.Broke != 0) {
+							Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (wrong type for %v method)\n\thave %v%v\n\twant %v%v", Nconv(n.Ntest.Right, obj.FmtLong), Tconv(ll.N.Type, 0), Sconv(missing.Sym, 0), Sconv(have.Sym, 0), Tconv(have.Type, obj.FmtShort), Sconv(missing.Sym, 0), Tconv(missing.Type, obj.FmtShort))
+						} else if !(missing.Broke != 0) {
+							Yyerror("impossible type switch case: %v cannot have dynamic type %v"+" (missing %v method)", Nconv(n.Ntest.Right, obj.FmtLong), Tconv(ll.N.Type, 0), Sconv(missing.Sym, 0))
+						}
+					}
+				}
+			}
+		}
+
+		if top == Etype && n.Type != nil {
+			ll = ncase.List
+			nvar = ncase.Nname
+			if nvar != nil {
+				if ll != nil && ll.Next == nil && ll.N.Type != nil && !(Istype(ll.N.Type, TNIL) != 0) {
+					// single entry type switch
+					nvar.Ntype = typenod(ll.N.Type)
+				} else {
+					// multiple entry type switch or default
+					nvar.Ntype = typenod(n.Type)
+				}
+
+				typecheck(&nvar, Erv|Easgn)
+				ncase.Nname = nvar
+			}
+		}
+
+		typechecklist(ncase.Nbody, Etop)
+	}
+
+	lineno = int32(lno)
+}