func (v *visitor) wrapSwitch(_switch *ast.SwitchStmt, identList []*ast.Ident) (block *ast.BlockStmt) { block = astPrintf(` { godebug.Line(ctx, %s, %s) %s scope := %s.EnteringNewChildScope() _ = scope // placeholder _ = scope // placeholder }`, v.scopeVar, pos2lineString(_switch.Pos()), _switch.Init, v.scopeVar)[0].(*ast.BlockStmt) block.List[3] = newDeclareCall(idents.scope, identList) _switch.Init = nil block.List[4] = _switch return block }
func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) { // Create implicit scope around switch bc := a.enterChild() defer bc.exit() // Compile init statement, if any if s.Init != nil { bc.compileStmt(s.Init) } // Compile condition, if any, and extract its effects var cond *expr condbc := bc.enterChild() if s.Tag != nil { e := condbc.compileExpr(condbc.block, false, s.Tag) if e != nil { var effect func(*Thread) effect, cond = e.extractEffect(condbc.block, "switch") a.push(effect) } } // Count cases ncases := 0 hasDefault := false for _, c := range s.Body.List { clause, ok := c.(*ast.CaseClause) if !ok { a.diagAt(clause.Pos(), "switch statement must contain case clauses") continue } if clause.List == nil { if hasDefault { a.diagAt(clause.Pos(), "switch statement contains more than one default case") } hasDefault = true } else { ncases += len(clause.List) } } // Compile case expressions cases := make([]func(*Thread) bool, ncases) i := 0 for _, c := range s.Body.List { clause, ok := c.(*ast.CaseClause) if !ok { continue } for _, v := range clause.List { e := condbc.compileExpr(condbc.block, false, v) switch { case e == nil: // Error reported by compileExpr case cond == nil && !e.t.isBoolean(): a.diagAt(v.Pos(), "'case' condition must be boolean") case cond == nil: cases[i] = e.asBool() case cond != nil: // Create comparison // TOOD(austin) This produces bad error messages compare := e.compileBinaryExpr(token.EQL, cond, e) if compare != nil { cases[i] = compare.asBool() } } i++ } } // Emit condition casePCs := make([]*uint, ncases+1) endPC := badPC a.flow.put(false, false, casePCs) a.push(func(t *Thread) { for i, c := range cases { if c(t) { t.pc = *casePCs[i] return } } t.pc = *casePCs[ncases] }) condbc.exit() // Compile cases i = 0 for _, c := range s.Body.List { clause, ok := c.(*ast.CaseClause) if !ok { continue } // Save jump PC's pc := a.nextPC() if clause.List != nil { for _ = range clause.List { casePCs[i] = &pc i++ } } else { // Default clause casePCs[ncases] = &pc } // Compile body fall := false for j, s := range clause.Body { if br, ok := s.(*ast.BranchStmt); ok && br.Tok == token.FALLTHROUGH { // println("Found fallthrough"); // It may be used only as the final // non-empty statement in a case or // default clause in an expression // "switch" statement. for _, s2 := range clause.Body[j+1:] { // XXX(Spec) 6g also considers // empty blocks to be empty // statements. if _, ok := s2.(*ast.EmptyStmt); !ok { a.diagAt(s.Pos(), "fallthrough statement must be final statement in case") break } } fall = true } else { bc.compileStmt(s) } } // Jump out of switch, unless there was a fallthrough if !fall { a.flow.put1(false, &endPC) a.push(func(v *Thread) { v.pc = endPC }) } } // Get end PC endPC = a.nextPC() if !hasDefault { casePCs[ncases] = &endPC } }