_linters/src/honnef.co/go/tools/ssa/builder.go
func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lblock) {
	// We treat TypeSwitchStmt like a sequential if-else chain.
	// Multiway dispatch can be recovered later by ssautil.Switches().

	// Typeswitch lowering:
	//
	// var x X
	// switch y := x.(type) {
	// case T1, T2: S1                  // >1 	(y := x)
	// case nil:    SN                  // nil 	(y := x)
	// default:     SD                  // 0 types 	(y := x)
	// case T3:     S3                  // 1 type 	(y := x.(T3))
	// }
	//
	//      ...s.Init...
	// 	x := eval x
	// .caseT1:
	// 	t1, ok1 := typeswitch,ok x <T1>
	// 	if ok1 then goto S1 else goto .caseT2
	// .caseT2:
	// 	t2, ok2 := typeswitch,ok x <T2>
	// 	if ok2 then goto S1 else goto .caseNil
	// .S1:
	//      y := x
	// 	...S1...
	// 	goto done
	// .caseNil:
	// 	if t2, ok2 := typeswitch,ok x <T2>
	// 	if x == nil then goto SN else goto .caseT3
	// .SN:
	//      y := x
	// 	...SN...
	// 	goto done
	// .caseT3:
	// 	t3, ok3 := typeswitch,ok x <T3>
	// 	if ok3 then goto S3 else goto default
	// .S3:
	//      y := t3
	// 	...S3...
	// 	goto done
	// .default:
	//      y := x
	// 	...SD...
	// 	goto done
	// .done:

	if s.Init != nil {
		b.stmt(fn, s.Init)
	}

	var x Value
	switch ass := s.Assign.(type) {
	case *ast.ExprStmt: // x.(type)
		x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X)
	case *ast.AssignStmt: // y := x.(type)
		x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
	}

	done := fn.newBasicBlock("typeswitch.done")
	if label != nil {
		label._break = done
	}
	var default_ *ast.CaseClause
	for _, clause := range s.Body.List {
		cc := clause.(*ast.CaseClause)
		if cc.List == nil {
			default_ = cc
			continue
		}
		body := fn.newBasicBlock("typeswitch.body")
		var next *BasicBlock
		var casetype types.Type
		var ti Value // ti, ok := typeassert,ok x <Ti>
		for _, cond := range cc.List {
			next = fn.newBasicBlock("typeswitch.next")
			casetype = fn.Pkg.typeOf(cond)
			var condv Value
			if casetype == tUntypedNil {
				condv = emitCompare(fn, token.EQL, x, nilConst(x.Type()), token.NoPos)
				ti = x
			} else {
				yok := emitTypeTest(fn, x, casetype, cc.Case)
				ti = emitExtract(fn, yok, 0)
				condv = emitExtract(fn, yok, 1)
			}
			emitIf(fn, condv, body, next)
			fn.currentBlock = next
		}
		if len(cc.List) != 1 {
			ti = x
		}
		fn.currentBlock = body
		b.typeCaseBody(fn, cc, ti, done)
		fn.currentBlock = next
	}
	if default_ != nil {
		b.typeCaseBody(fn, default_, x, done)
	} else {
		emitJump(fn, done)
	}
	fn.currentBlock = done
}
func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lblock) {
	// We treat TypeSwitchStmt like a sequential if-else chain.
	// Multiway dispatch can be recovered later by ssautil.Switches().

	// Typeswitch lowering:
	//
	// var x X
	// switch y := x.(type) {
	// case T1, T2: S1                  // >1 	(y := x)
	// case nil:    SN                  // nil 	(y := x)
	// default:     SD                  // 0 types 	(y := x)
	// case T3:     S3                  // 1 type 	(y := x.(T3))
	// }
	//
	//      ...s.Init...
	// 	x := eval x
	// .caseT1:
	// 	t1, ok1 := typeswitch,ok x <T1>
	// 	if ok1 then goto S1 else goto .caseT2
	// .caseT2:
	// 	t2, ok2 := typeswitch,ok x <T2>
	// 	if ok2 then goto S1 else goto .caseNil
	// .S1:
	//      y := x
	// 	...S1...
	// 	goto done
	// .caseNil:
	// 	if t2, ok2 := typeswitch,ok x <T2>
	// 	if x == nil then goto SN else goto .caseT3
	// .SN:
	//      y := x
	// 	...SN...
	// 	goto done
	// .caseT3:
	// 	t3, ok3 := typeswitch,ok x <T3>
	// 	if ok3 then goto S3 else goto default
	// .S3:
	//      y := t3
	// 	...S3...
	// 	goto done
	// .default:
	//      y := x
	// 	...SD...
	// 	goto done
	// .done:

	if s.Init != nil {
		b.stmt(fn, s.Init)
	}

	var x Value
	switch ass := s.Assign.(type) {
	case *ast.ExprStmt: // x.(type)
		x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X)
	case *ast.AssignStmt: // y := x.(type)
		x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
	}

	done := fn.newBasicBlock("typeswitch.done")
	if label != nil {
		label._break = done
	}
	var default_ *ast.CaseClause
	for _, clause := range s.Body.List {
		cc := clause.(*ast.CaseClause)
		if cc.List == nil {
			default_ = cc
			continue
		}
		body := fn.newBasicBlock("typeswitch.body")
		var next *BasicBlock
		var casetype types.Type
		var ti Value // ti, ok := typeassert,ok x <Ti>
		for _, cond := range cc.List {
			next = fn.newBasicBlock("typeswitch.next")
			casetype = fn.Pkg.typeOf(cond)
			var condv Value
			if casetype == tUntypedNil {
				condv = emitCompare(fn, token.EQL, x, nilConst(x.Type()), token.NoPos)
				ti = x
			} else {
				yok := emitTypeTest(fn, x, casetype, cc.Case)
				ti = emitExtract(fn, yok, 0)
				condv = emitExtract(fn, yok, 1)
			}
			emitIf(fn, condv, body, next)
			fn.currentBlock = next
		}
		if len(cc.List) != 1 {
			ti = x
		}
		fn.currentBlock = body
		b.typeCaseBody(fn, cc, ti, done)
		fn.currentBlock = next
	}
	if default_ != nil {
		b.typeCaseBody(fn, default_, x, done)
	} else {
		emitJump(fn, done)
	}
	fn.currentBlock = done
}
func ( b * builder ) typeSwitchStmt ( fn * Function , s * ast . TypeSwitchStmt , label * lblock ) { // We treat TypeSwitchStmt like a sequential if-else chain. // Multiway dispatch can be recovered later by ssautil.Switches(). // Typeswitch lowering: // // var x X // switch y := x.(type) { // case T1, T2: S1                  // >1 	(y := x) // case nil:    SN                  // nil 	(y := x) // default:     SD                  // 0 types 	(y := x) // case T3:     S3                  // 1 type 	(y := x.(T3)) // } // //      ...s.Init... // 	x := eval x // .caseT1: // 	t1, ok1 := typeswitch,ok x <T1> // 	if ok1 then goto S1 else goto .caseT2 // .caseT2: // 	t2, ok2 := typeswitch,ok x <T2> // 	if ok2 then goto S1 else goto .caseNil // .S1: //      y := x // 	...S1... // 	goto done // .caseNil: // 	if t2, ok2 := typeswitch,ok x <T2> // 	if x == nil then goto SN else goto .caseT3 // .SN: //      y := x // 	...SN... // 	goto done // .caseT3: // 	t3, ok3 := typeswitch,ok x <T3> // 	if ok3 then goto S3 else goto default // .S3: //      y := t3 // 	...S3... // 	goto done // .default: //      y := x // 	...SD... // 	goto done // .done: if s . Init != nil { b . stmt ( fn , s . Init ) } var x Value switch ass := s . Assign . ( type ) { case * ast . ExprStmt : // x.(type) x = b . expr ( fn , unparen ( ass . X ) . ( * ast . TypeAssertExpr ) . X ) case * ast . AssignStmt : // y := x.(type) x = b . expr ( fn , unparen ( ass . Rhs [ 0 ] ) . ( * ast . TypeAssertExpr ) . X ) } done := fn . newBasicBlock ( " " ) if label != nil { label . _break = done } var default_ * ast . CaseClause for _ , clause := range s . Body . List { cc := clause . ( * ast . CaseClause ) if cc . List == nil { default_ = cc continue } body := fn . newBasicBlock ( " " ) var next * BasicBlock var casetype types . Type var ti Value // ti, ok := typeassert,ok x <Ti> for _ , cond := range cc . List { next = fn . newBasicBlock ( " " ) casetype = fn . Pkg . typeOf ( cond ) var condv Value if casetype == tUntypedNil { condv = emitCompare ( fn , token . EQL , x , nilConst ( x . Type ( ) ) , token . NoPos ) ti = x } else { yok := emitTypeTest ( fn , x , casetype , cc . Case ) ti = emitExtract ( fn , yok , 0 ) condv = emitExtract ( fn , yok , 1 ) } emitIf ( fn , condv , body , next ) fn . currentBlock = next } if len ( cc . List ) != 1 { ti = x } fn . currentBlock = body b . typeCaseBody ( fn , cc , ti , done ) fn . currentBlock = next } if default_ != nil { b . typeCaseBody ( fn , default_ , x , done ) } else { emitJump ( fn , done ) } fn . currentBlock = done }
// typeSwitchStmt emits to fn code for the type switch statement s, optionally
// labelled by label.
//
typeSwitchStmt emits to fn code for the type switch statement s optionally labelled by label .