_linters/src/honnef.co/go/tools/ssa/builder.go
func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
	// A blocking select of a single case degenerates to a
	// simple send or receive.
	// TODO(adonovan): opt: is this optimization worth its weight?
	if len(s.Body.List) == 1 {
		clause := s.Body.List[0].(*ast.CommClause)
		if clause.Comm != nil {
			b.stmt(fn, clause.Comm)
			done := fn.newBasicBlock("select.done")
			if label != nil {
				label._break = done
			}
			fn.targets = &targets{
				tail:   fn.targets,
				_break: done,
			}
			b.stmtList(fn, clause.Body)
			fn.targets = fn.targets.tail
			emitJump(fn, done)
			fn.currentBlock = done
			return
		}
	}

	// First evaluate all channels in all cases, and find
	// the directions of each state.
	var states []*SelectState
	blocking := true
	debugInfo := fn.debugInfo()
	for _, clause := range s.Body.List {
		var st *SelectState
		switch comm := clause.(*ast.CommClause).Comm.(type) {
		case nil: // default case
			blocking = false
			continue

		case *ast.SendStmt: // ch<- i
			ch := b.expr(fn, comm.Chan)
			st = &SelectState{
				Dir:  types.SendOnly,
				Chan: ch,
				Send: emitConv(fn, b.expr(fn, comm.Value),
					ch.Type().Underlying().(*types.Chan).Elem()),
				Pos: comm.Arrow,
			}
			if debugInfo {
				st.DebugNode = comm
			}

		case *ast.AssignStmt: // x := <-ch
			recv := unparen(comm.Rhs[0]).(*ast.UnaryExpr)
			st = &SelectState{
				Dir:  types.RecvOnly,
				Chan: b.expr(fn, recv.X),
				Pos:  recv.OpPos,
			}
			if debugInfo {
				st.DebugNode = recv
			}

		case *ast.ExprStmt: // <-ch
			recv := unparen(comm.X).(*ast.UnaryExpr)
			st = &SelectState{
				Dir:  types.RecvOnly,
				Chan: b.expr(fn, recv.X),
				Pos:  recv.OpPos,
			}
			if debugInfo {
				st.DebugNode = recv
			}
		}
		states = append(states, st)
	}

	// We dispatch on the (fair) result of Select using a
	// sequential if-else chain, in effect:
	//
	// idx, recvOk, r0...r_n-1 := select(...)
	// if idx == 0 {  // receive on channel 0  (first receive => r0)
	//     x, ok := r0, recvOk
	//     ...state0...
	// } else if v == 1 {   // send on channel 1
	//     ...state1...
	// } else {
	//     ...default...
	// }
	sel := &Select{
		States:   states,
		Blocking: blocking,
	}
	sel.setPos(s.Select)
	var vars []*types.Var
	vars = append(vars, varIndex, varOk)
	for _, st := range states {
		if st.Dir == types.RecvOnly {
			tElem := st.Chan.Type().Underlying().(*types.Chan).Elem()
			vars = append(vars, anonVar(tElem))
		}
	}
	sel.setType(types.NewTuple(vars...))

	fn.emit(sel)
	idx := emitExtract(fn, sel, 0)

	done := fn.newBasicBlock("select.done")
	if label != nil {
		label._break = done
	}

	var defaultBody *[]ast.Stmt
	state := 0
	r := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV
	for _, cc := range s.Body.List {
		clause := cc.(*ast.CommClause)
		if clause.Comm == nil {
			defaultBody = &clause.Body
			continue
		}
		body := fn.newBasicBlock("select.body")
		next := fn.newBasicBlock("select.next")
		emitIf(fn, emitCompare(fn, token.EQL, idx, intConst(int64(state)), token.NoPos), body, next)
		fn.currentBlock = body
		fn.targets = &targets{
			tail:   fn.targets,
			_break: done,
		}
		switch comm := clause.Comm.(type) {
		case *ast.ExprStmt: // <-ch
			if debugInfo {
				v := emitExtract(fn, sel, r)
				emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)
			}
			r++

		case *ast.AssignStmt: // x := <-states[state].Chan
			if comm.Tok == token.DEFINE {
				fn.addLocalForIdent(comm.Lhs[0].(*ast.Ident))
			}
			x := b.addr(fn, comm.Lhs[0], false) // non-escaping
			v := emitExtract(fn, sel, r)
			if debugInfo {
				emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)
			}
			x.store(fn, v)

			if len(comm.Lhs) == 2 { // x, ok := ...
				if comm.Tok == token.DEFINE {
					fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident))
				}
				ok := b.addr(fn, comm.Lhs[1], false) // non-escaping
				ok.store(fn, emitExtract(fn, sel, 1))
			}
			r++
		}
		b.stmtList(fn, clause.Body)
		fn.targets = fn.targets.tail
		emitJump(fn, done)
		fn.currentBlock = next
		state++
	}
	if defaultBody != nil {
		fn.targets = &targets{
			tail:   fn.targets,
			_break: done,
		}
		b.stmtList(fn, *defaultBody)
		fn.targets = fn.targets.tail
	} else {
		// A blocking select must match some case.
		// (This should really be a runtime.errorString, not a string.)
		fn.emit(&Panic{
			X: emitConv(fn, stringConst("blocking select matched no case"), tEface),
		})
		fn.currentBlock = fn.newBasicBlock("unreachable")
	}
	emitJump(fn, done)
	fn.currentBlock = done
}
func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
	// A blocking select of a single case degenerates to a
	// simple send or receive.
	// TODO(adonovan): opt: is this optimization worth its weight?
	if len(s.Body.List) == 1 {
		clause := s.Body.List[0].(*ast.CommClause)
		if clause.Comm != nil {
			b.stmt(fn, clause.Comm)
			done := fn.newBasicBlock("select.done")
			if label != nil {
				label._break = done
			}
			fn.targets = &targets{
				tail:   fn.targets,
				_break: done,
			}
			b.stmtList(fn, clause.Body)
			fn.targets = fn.targets.tail
			emitJump(fn, done)
			fn.currentBlock = done
			return
		}
	}

	// First evaluate all channels in all cases, and find
	// the directions of each state.
	var states []*SelectState
	blocking := true
	debugInfo := fn.debugInfo()
	for _, clause := range s.Body.List {
		var st *SelectState
		switch comm := clause.(*ast.CommClause).Comm.(type) {
		case nil: // default case
			blocking = false
			continue

		case *ast.SendStmt: // ch<- i
			ch := b.expr(fn, comm.Chan)
			st = &SelectState{
				Dir:  types.SendOnly,
				Chan: ch,
				Send: emitConv(fn, b.expr(fn, comm.Value),
					ch.Type().Underlying().(*types.Chan).Elem()),
				Pos: comm.Arrow,
			}
			if debugInfo {
				st.DebugNode = comm
			}

		case *ast.AssignStmt: // x := <-ch
			recv := unparen(comm.Rhs[0]).(*ast.UnaryExpr)
			st = &SelectState{
				Dir:  types.RecvOnly,
				Chan: b.expr(fn, recv.X),
				Pos:  recv.OpPos,
			}
			if debugInfo {
				st.DebugNode = recv
			}

		case *ast.ExprStmt: // <-ch
			recv := unparen(comm.X).(*ast.UnaryExpr)
			st = &SelectState{
				Dir:  types.RecvOnly,
				Chan: b.expr(fn, recv.X),
				Pos:  recv.OpPos,
			}
			if debugInfo {
				st.DebugNode = recv
			}
		}
		states = append(states, st)
	}

	// We dispatch on the (fair) result of Select using a
	// sequential if-else chain, in effect:
	//
	// idx, recvOk, r0...r_n-1 := select(...)
	// if idx == 0 {  // receive on channel 0  (first receive => r0)
	//     x, ok := r0, recvOk
	//     ...state0...
	// } else if v == 1 {   // send on channel 1
	//     ...state1...
	// } else {
	//     ...default...
	// }
	sel := &Select{
		States:   states,
		Blocking: blocking,
	}
	sel.setPos(s.Select)
	var vars []*types.Var
	vars = append(vars, varIndex, varOk)
	for _, st := range states {
		if st.Dir == types.RecvOnly {
			tElem := st.Chan.Type().Underlying().(*types.Chan).Elem()
			vars = append(vars, anonVar(tElem))
		}
	}
	sel.setType(types.NewTuple(vars...))

	fn.emit(sel)
	idx := emitExtract(fn, sel, 0)

	done := fn.newBasicBlock("select.done")
	if label != nil {
		label._break = done
	}

	var defaultBody *[]ast.Stmt
	state := 0
	r := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV
	for _, cc := range s.Body.List {
		clause := cc.(*ast.CommClause)
		if clause.Comm == nil {
			defaultBody = &clause.Body
			continue
		}
		body := fn.newBasicBlock("select.body")
		next := fn.newBasicBlock("select.next")
		emitIf(fn, emitCompare(fn, token.EQL, idx, intConst(int64(state)), token.NoPos), body, next)
		fn.currentBlock = body
		fn.targets = &targets{
			tail:   fn.targets,
			_break: done,
		}
		switch comm := clause.Comm.(type) {
		case *ast.ExprStmt: // <-ch
			if debugInfo {
				v := emitExtract(fn, sel, r)
				emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)
			}
			r++

		case *ast.AssignStmt: // x := <-states[state].Chan
			if comm.Tok == token.DEFINE {
				fn.addLocalForIdent(comm.Lhs[0].(*ast.Ident))
			}
			x := b.addr(fn, comm.Lhs[0], false) // non-escaping
			v := emitExtract(fn, sel, r)
			if debugInfo {
				emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)
			}
			x.store(fn, v)

			if len(comm.Lhs) == 2 { // x, ok := ...
				if comm.Tok == token.DEFINE {
					fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident))
				}
				ok := b.addr(fn, comm.Lhs[1], false) // non-escaping
				ok.store(fn, emitExtract(fn, sel, 1))
			}
			r++
		}
		b.stmtList(fn, clause.Body)
		fn.targets = fn.targets.tail
		emitJump(fn, done)
		fn.currentBlock = next
		state++
	}
	if defaultBody != nil {
		fn.targets = &targets{
			tail:   fn.targets,
			_break: done,
		}
		b.stmtList(fn, *defaultBody)
		fn.targets = fn.targets.tail
	} else {
		// A blocking select must match some case.
		// (This should really be a runtime.errorString, not a string.)
		fn.emit(&Panic{
			X: emitConv(fn, stringConst("blocking select matched no case"), tEface),
		})
		fn.currentBlock = fn.newBasicBlock("unreachable")
	}
	emitJump(fn, done)
	fn.currentBlock = done
}
func ( b * builder ) selectStmt ( fn * Function , s * ast . SelectStmt , label * lblock ) { // A blocking select of a single case degenerates to a // simple send or receive. // TODO(adonovan): opt: is this optimization worth its weight? if len ( s . Body . List ) == 1 { clause := s . Body . List [ 0 ] . ( * ast . CommClause ) if clause . Comm != nil { b . stmt ( fn , clause . Comm ) done := fn . newBasicBlock ( " " ) if label != nil { label . _break = done } fn . targets = & targets { tail : fn . targets , _break : done , } b . stmtList ( fn , clause . Body ) fn . targets = fn . targets . tail emitJump ( fn , done ) fn . currentBlock = done return } } // First evaluate all channels in all cases, and find // the directions of each state. var states [ ] * SelectState blocking := true debugInfo := fn . debugInfo ( ) for _ , clause := range s . Body . List { var st * SelectState switch comm := clause . ( * ast . CommClause ) . Comm . ( type ) { case nil : // default case blocking = false continue case * ast . SendStmt : // ch<- i ch := b . expr ( fn , comm . Chan ) st = & SelectState { Dir : types . SendOnly , Chan : ch , Send : emitConv ( fn , b . expr ( fn , comm . Value ) , ch . Type ( ) . Underlying ( ) . ( * types . Chan ) . Elem ( ) ) , Pos : comm . Arrow , } if debugInfo { st . DebugNode = comm } case * ast . AssignStmt : // x := <-ch recv := unparen ( comm . Rhs [ 0 ] ) . ( * ast . UnaryExpr ) st = & SelectState { Dir : types . RecvOnly , Chan : b . expr ( fn , recv . X ) , Pos : recv . OpPos , } if debugInfo { st . DebugNode = recv } case * ast . ExprStmt : // <-ch recv := unparen ( comm . X ) . ( * ast . UnaryExpr ) st = & SelectState { Dir : types . RecvOnly , Chan : b . expr ( fn , recv . X ) , Pos : recv . OpPos , } if debugInfo { st . DebugNode = recv } } states = append ( states , st ) } // We dispatch on the (fair) result of Select using a // sequential if-else chain, in effect: // // idx, recvOk, r0...r_n-1 := select(...) // if idx == 0 {  // receive on channel 0  (first receive => r0) //     x, ok := r0, recvOk //     ...state0... // } else if v == 1 {   // send on channel 1 //     ...state1... // } else { //     ...default... // } sel := & Select { States : states , Blocking : blocking , } sel . setPos ( s . Select ) var vars [ ] * types . Var vars = append ( vars , varIndex , varOk ) for _ , st := range states { if st . Dir == types . RecvOnly { tElem := st . Chan . Type ( ) . Underlying ( ) . ( * types . Chan ) . Elem ( ) vars = append ( vars , anonVar ( tElem ) ) } } sel . setType ( types . NewTuple ( vars ... ) ) fn . emit ( sel ) idx := emitExtract ( fn , sel , 0 ) done := fn . newBasicBlock ( " " ) if label != nil { label . _break = done } var defaultBody * [ ] ast . Stmt state := 0 r := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV for _ , cc := range s . Body . List { clause := cc . ( * ast . CommClause ) if clause . Comm == nil { defaultBody = & clause . Body continue } body := fn . newBasicBlock ( " " ) next := fn . newBasicBlock ( " " ) emitIf ( fn , emitCompare ( fn , token . EQL , idx , intConst ( int64 ( state ) ) , token . NoPos ) , body , next ) fn . currentBlock = body fn . targets = & targets { tail : fn . targets , _break : done , } switch comm := clause . Comm . ( type ) { case * ast . ExprStmt : // <-ch if debugInfo { v := emitExtract ( fn , sel , r ) emitDebugRef ( fn , states [ state ] . DebugNode . ( ast . Expr ) , v , false ) } r ++ case * ast . AssignStmt : // x := <-states[state].Chan if comm . Tok == token . DEFINE { fn . addLocalForIdent ( comm . Lhs [ 0 ] . ( * ast . Ident ) ) } x := b . addr ( fn , comm . Lhs [ 0 ] , false ) // non-escaping v := emitExtract ( fn , sel , r ) if debugInfo { emitDebugRef ( fn , states [ state ] . DebugNode . ( ast . Expr ) , v , false ) } x . store ( fn , v ) if len ( comm . Lhs ) == 2 { // x, ok := ... if comm . Tok == token . DEFINE { fn . addLocalForIdent ( comm . Lhs [ 1 ] . ( * ast . Ident ) ) } ok := b . addr ( fn , comm . Lhs [ 1 ] , false ) // non-escaping ok . store ( fn , emitExtract ( fn , sel , 1 ) ) } r ++ } b . stmtList ( fn , clause . Body ) fn . targets = fn . targets . tail emitJump ( fn , done ) fn . currentBlock = next state ++ } if defaultBody != nil { fn . targets = & targets { tail : fn . targets , _break : done , } b . stmtList ( fn , * defaultBody ) fn . targets = fn . targets . tail } else { // A blocking select must match some case. // (This should really be a runtime.errorString, not a string.) fn . emit ( & Panic { X : emitConv ( fn , stringConst ( " " ) , tEface ) , } ) fn . currentBlock = fn . newBasicBlock ( " " ) } emitJump ( fn , done ) fn . currentBlock = done }
// selectStmt emits to fn code for the select statement s, optionally
// labelled by label.
//
selectStmt emits to fn code for the select statement s optionally labelled by label .