case OXCASE:
                types.Markdcl()
                n := nodl(p.pos(), OXCASE, nil, nil)
-               n.Xoffset = int64(types.Block)
                n.List.Set(p.exprList())
                // TODO(gri) eventually we must declare variables for type switch
                // statements (type switch statements are not yet exported)
        // case OFALL:
        //      unreachable - mapped to OXFALL case below by exporter
 
-       case OXFALL:
-               n := nodl(p.pos(), OXFALL, nil, nil)
-               n.Xoffset = int64(types.Block)
+       case OFALL:
+               n := nodl(p.pos(), OFALL, nil, nil)
                return n
 
        case OBREAK, OCONTINUE:
 
 }
 
 func (p *noder) stmts(stmts []syntax.Stmt) []*Node {
+       return p.stmtsFall(stmts, false)
+}
+
+func (p *noder) stmtsFall(stmts []syntax.Stmt, fallOK bool) []*Node {
        var nodes []*Node
-       for _, stmt := range stmts {
-               s := p.stmt(stmt)
+       for i, stmt := range stmts {
+               s := p.stmtFall(stmt, fallOK && i+1 == len(stmts))
                if s == nil {
                } else if s.Op == OBLOCK && s.Ninit.Len() == 0 {
                        nodes = append(nodes, s.List.Slice()...)
 }
 
 func (p *noder) stmt(stmt syntax.Stmt) *Node {
+       return p.stmtFall(stmt, false)
+}
+
+func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) *Node {
        p.lineno(stmt)
        switch stmt := stmt.(type) {
        case *syntax.EmptyStmt:
                return nil
        case *syntax.LabeledStmt:
-               return p.labeledStmt(stmt)
+               return p.labeledStmt(stmt, fallOK)
        case *syntax.BlockStmt:
                l := p.blockStmt(stmt)
                if len(l) == 0 {
                case syntax.Continue:
                        op = OCONTINUE
                case syntax.Fallthrough:
-                       op = OXFALL
+                       if !fallOK {
+                               yyerror("fallthrough statement out of place")
+                       }
+                       op = OFALL
                case syntax.Goto:
                        op = OGOTO
                default:
                if stmt.Label != nil {
                        n.Left = p.newname(stmt.Label)
                }
-               if op == OXFALL {
-                       n.Xoffset = int64(types.Block)
-               }
                return n
        case *syntax.CallStmt:
                var op Op
        }
 
        tswitch := n.Left
-       if tswitch != nil && (tswitch.Op != OTYPESW || tswitch.Left == nil) {
+       if tswitch != nil && tswitch.Op != OTYPESW {
                tswitch = nil
        }
        n.List.Set(p.caseClauses(stmt.Body, tswitch, stmt.Rbrace))
                if clause.Cases != nil {
                        n.List.Set(p.exprList(clause.Cases))
                }
-               if tswitch != nil {
+               if tswitch != nil && tswitch.Left != nil {
                        nn := newname(tswitch.Left.Sym)
                        declare(nn, dclcontext)
                        n.Rlist.Set1(nn)
                        // keep track of the instances for reporting unused
                        nn.Name.Defn = tswitch
                }
-               n.Xoffset = int64(types.Block)
-               n.Nbody.Set(p.stmts(clause.Body))
+
+               // Trim trailing empty statements. We omit them from
+               // the Node AST anyway, and it's easier to identify
+               // out-of-place fallthrough statements without them.
+               body := clause.Body
+               for len(body) > 0 {
+                       if _, ok := body[len(body)-1].(*syntax.EmptyStmt); !ok {
+                               break
+                       }
+                       body = body[:len(body)-1]
+               }
+
+               n.Nbody.Set(p.stmtsFall(body, true))
+               if l := n.Nbody.Len(); l > 0 && n.Nbody.Index(l-1).Op == OFALL {
+                       if tswitch != nil {
+                               yyerror("cannot fallthrough in type switch")
+                       }
+                       if i+1 == len(clauses) {
+                               yyerror("cannot fallthrough final case in switch")
+                       }
+               }
+
                nodes = append(nodes, n)
        }
        if len(clauses) > 0 {
        return nodes
 }
 
-func (p *noder) labeledStmt(label *syntax.LabeledStmt) *Node {
+func (p *noder) labeledStmt(label *syntax.LabeledStmt, fallOK bool) *Node {
        lhs := p.nod(label, OLABEL, p.newname(label.Label), nil)
 
        var ls *Node
        if label.Stmt != nil { // TODO(mdempsky): Should always be present.
-               ls = p.stmt(label.Stmt)
+               ls = p.stmtFall(label.Stmt, fallOK)
        }
 
        lhs.Name.Defn = ls
 
        var def *Node    // defaults
        br := nod(OBREAK, nil, nil)
 
-       for i, n := range sw.List.Slice() {
+       for _, n := range sw.List.Slice() {
                setlineno(n)
                if n.Op != OXCASE {
                        Fatalf("casebody %v", n.Op)
                        fallIndex--
                }
                last := stat[fallIndex]
-
-               // botch - shouldn't fall through declaration
-               if last.Xoffset == n.Xoffset && last.Op == OXFALL {
-                       if typeswvar != nil {
-                               setlineno(last)
-                               yyerror("cannot fallthrough in type switch")
-                       }
-
-                       if i+1 >= sw.List.Len() {
-                               setlineno(last)
-                               yyerror("cannot fallthrough final case in switch")
-                       }
-
-                       last.Op = OFALL
-               } else {
+               if last.Op != OFALL {
                        stat = append(stat, br)
                }
        }
 
        // - ONAME nodes that refer to local variables use it to identify their stack frame position.
        // - ODOT, ODOTPTR, and OINDREGSP use it to indicate offset relative to their base address.
        // - OSTRUCTKEY uses it to store the named field's offset.
-       // - OXCASE and OXFALL use it to validate the use of fallthrough.
        // - Named OLITERALs use it to to store their ambient iota value.
        // Possibly still more uses. If you find any, document them.
        Xoffset int64
        OCONTINUE // continue
        ODEFER    // defer Left (Left must be call)
        OEMPTY    // no-op (empty statement)
-       OFALL     // fallthrough (after processing)
-       OXFALL    // fallthrough (before processing)
+       _         // placeholder to appease toolstash
+       OFALL     // fallthrough
        OFOR      // for Ninit; Left; Right { Nbody }
        OFORUNTIL // for Ninit; Left; Right { Nbody } ; test applied after executing body, not before
        OGOTO     // goto Left