GoPLS Viewer

Home|gopls/go/cfg/builder.go
1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package cfg
6
7// This file implements the CFG construction pass.
8
9import (
10    "fmt"
11    "go/ast"
12    "go/token"
13)
14
15type builder struct {
16    cfg       *CFG
17    mayReturn func(*ast.CallExprbool
18    current   *Block
19    lblocks   map[*ast.Object]*lblock // labeled blocks
20    targets   *targets                // linked stack of branch targets
21}
22
23func (b *builderstmt(_s ast.Stmt) {
24    // The label of the current statement.  If non-nil, its _goto
25    // target is always set; its _break and _continue are set only
26    // within the body of switch/typeswitch/select/for/range.
27    // It is effectively an additional default-nil parameter of stmt().
28    var label *lblock
29start:
30    switch s := _s.(type) {
31    case *ast.BadStmt,
32        *ast.SendStmt,
33        *ast.IncDecStmt,
34        *ast.GoStmt,
35        *ast.DeferStmt,
36        *ast.EmptyStmt,
37        *ast.AssignStmt:
38        // No effect on control flow.
39        b.add(s)
40
41    case *ast.ExprStmt:
42        b.add(s)
43        if callok := s.X.(*ast.CallExpr); ok && !b.mayReturn(call) {
44            // Calls to panic, os.Exit, etc, never return.
45            b.current = b.newBlock("unreachable.call")
46        }
47
48    case *ast.DeclStmt:
49        // Treat each var ValueSpec as a separate statement.
50        d := s.Decl.(*ast.GenDecl)
51        if d.Tok == token.VAR {
52            for _spec := range d.Specs {
53                if specok := spec.(*ast.ValueSpec); ok {
54                    b.add(spec)
55                }
56            }
57        }
58
59    case *ast.LabeledStmt:
60        label = b.labeledBlock(s.Label)
61        b.jump(label._goto)
62        b.current = label._goto
63        _s = s.Stmt
64        goto start // effectively: tailcall stmt(g, s.Stmt, label)
65
66    case *ast.ReturnStmt:
67        b.add(s)
68        b.current = b.newBlock("unreachable.return")
69
70    case *ast.BranchStmt:
71        b.branchStmt(s)
72
73    case *ast.BlockStmt:
74        b.stmtList(s.List)
75
76    case *ast.IfStmt:
77        if s.Init != nil {
78            b.stmt(s.Init)
79        }
80        then := b.newBlock("if.then")
81        done := b.newBlock("if.done")
82        _else := done
83        if s.Else != nil {
84            _else = b.newBlock("if.else")
85        }
86        b.add(s.Cond)
87        b.ifelse(then_else)
88        b.current = then
89        b.stmt(s.Body)
90        b.jump(done)
91
92        if s.Else != nil {
93            b.current = _else
94            b.stmt(s.Else)
95            b.jump(done)
96        }
97
98        b.current = done
99
100    case *ast.SwitchStmt:
101        b.switchStmt(slabel)
102
103    case *ast.TypeSwitchStmt:
104        b.typeSwitchStmt(slabel)
105
106    case *ast.SelectStmt:
107        b.selectStmt(slabel)
108
109    case *ast.ForStmt:
110        b.forStmt(slabel)
111
112    case *ast.RangeStmt:
113        b.rangeStmt(slabel)
114
115    default:
116        panic(fmt.Sprintf("unexpected statement kind: %T"s))
117    }
118}
119
120func (b *builderstmtList(list []ast.Stmt) {
121    for _s := range list {
122        b.stmt(s)
123    }
124}
125
126func (b *builderbranchStmt(s *ast.BranchStmt) {
127    var block *Block
128    switch s.Tok {
129    case token.BREAK:
130        if s.Label != nil {
131            if lb := b.labeledBlock(s.Label); lb != nil {
132                block = lb._break
133            }
134        } else {
135            for t := b.targetst != nil && block == nilt = t.tail {
136                block = t._break
137            }
138        }
139
140    case token.CONTINUE:
141        if s.Label != nil {
142            if lb := b.labeledBlock(s.Label); lb != nil {
143                block = lb._continue
144            }
145        } else {
146            for t := b.targetst != nil && block == nilt = t.tail {
147                block = t._continue
148            }
149        }
150
151    case token.FALLTHROUGH:
152        for t := b.targetst != nil && block == nilt = t.tail {
153            block = t._fallthrough
154        }
155
156    case token.GOTO:
157        if s.Label != nil {
158            block = b.labeledBlock(s.Label)._goto
159        }
160    }
161    if block == nil {
162        block = b.newBlock("undefined.branch")
163    }
164    b.jump(block)
165    b.current = b.newBlock("unreachable.branch")
166}
167
168func (b *builderswitchStmt(s *ast.SwitchStmtlabel *lblock) {
169    if s.Init != nil {
170        b.stmt(s.Init)
171    }
172    if s.Tag != nil {
173        b.add(s.Tag)
174    }
175    done := b.newBlock("switch.done")
176    if label != nil {
177        label._break = done
178    }
179    // We pull the default case (if present) down to the end.
180    // But each fallthrough label must point to the next
181    // body block in source order, so we preallocate a
182    // body block (fallthru) for the next case.
183    // Unfortunately this makes for a confusing block order.
184    var defaultBody *[]ast.Stmt
185    var defaultFallthrough *Block
186    var fallthrudefaultBlock *Block
187    ncases := len(s.Body.List)
188    for iclause := range s.Body.List {
189        body := fallthru
190        if body == nil {
191            body = b.newBlock("switch.body"// first case only
192        }
193
194        // Preallocate body block for the next case.
195        fallthru = done
196        if i+1 < ncases {
197            fallthru = b.newBlock("switch.body")
198        }
199
200        cc := clause.(*ast.CaseClause)
201        if cc.List == nil {
202            // Default case.
203            defaultBody = &cc.Body
204            defaultFallthrough = fallthru
205            defaultBlock = body
206            continue
207        }
208
209        var nextCond *Block
210        for _cond := range cc.List {
211            nextCond = b.newBlock("switch.next")
212            b.add(cond// one half of the tag==cond condition
213            b.ifelse(bodynextCond)
214            b.current = nextCond
215        }
216        b.current = body
217        b.targets = &targets{
218            tail:         b.targets,
219            _break:       done,
220            _fallthroughfallthru,
221        }
222        b.stmtList(cc.Body)
223        b.targets = b.targets.tail
224        b.jump(done)
225        b.current = nextCond
226    }
227    if defaultBlock != nil {
228        b.jump(defaultBlock)
229        b.current = defaultBlock
230        b.targets = &targets{
231            tail:         b.targets,
232            _break:       done,
233            _fallthroughdefaultFallthrough,
234        }
235        b.stmtList(*defaultBody)
236        b.targets = b.targets.tail
237    }
238    b.jump(done)
239    b.current = done
240}
241
242func (b *buildertypeSwitchStmt(s *ast.TypeSwitchStmtlabel *lblock) {
243    if s.Init != nil {
244        b.stmt(s.Init)
245    }
246    if s.Assign != nil {
247        b.add(s.Assign)
248    }
249
250    done := b.newBlock("typeswitch.done")
251    if label != nil {
252        label._break = done
253    }
254    var default_ *ast.CaseClause
255    for _clause := range s.Body.List {
256        cc := clause.(*ast.CaseClause)
257        if cc.List == nil {
258            default_ = cc
259            continue
260        }
261        body := b.newBlock("typeswitch.body")
262        var next *Block
263        for _casetype := range cc.List {
264            next = b.newBlock("typeswitch.next")
265            // casetype is a type, so don't call b.add(casetype).
266            // This block logically contains a type assertion,
267            // x.(casetype), but it's unclear how to represent x.
268            _ = casetype
269            b.ifelse(bodynext)
270            b.current = next
271        }
272        b.current = body
273        b.typeCaseBody(ccdone)
274        b.current = next
275    }
276    if default_ != nil {
277        b.typeCaseBody(default_done)
278    } else {
279        b.jump(done)
280    }
281    b.current = done
282}
283
284func (b *buildertypeCaseBody(cc *ast.CaseClausedone *Block) {
285    b.targets = &targets{
286        tail:   b.targets,
287        _breakdone,
288    }
289    b.stmtList(cc.Body)
290    b.targets = b.targets.tail
291    b.jump(done)
292}
293
294func (b *builderselectStmt(s *ast.SelectStmtlabel *lblock) {
295    // First evaluate channel expressions.
296    // TODO(adonovan): fix: evaluate only channel exprs here.
297    for _clause := range s.Body.List {
298        if comm := clause.(*ast.CommClause).Commcomm != nil {
299            b.stmt(comm)
300        }
301    }
302
303    done := b.newBlock("select.done")
304    if label != nil {
305        label._break = done
306    }
307
308    var defaultBody *[]ast.Stmt
309    for _cc := range s.Body.List {
310        clause := cc.(*ast.CommClause)
311        if clause.Comm == nil {
312            defaultBody = &clause.Body
313            continue
314        }
315        body := b.newBlock("select.body")
316        next := b.newBlock("select.next")
317        b.ifelse(bodynext)
318        b.current = body
319        b.targets = &targets{
320            tail:   b.targets,
321            _breakdone,
322        }
323        switch comm := clause.Comm.(type) {
324        case *ast.ExprStmt// <-ch
325            // nop
326        case *ast.AssignStmt// x := <-states[state].Chan
327            b.add(comm.Lhs[0])
328        }
329        b.stmtList(clause.Body)
330        b.targets = b.targets.tail
331        b.jump(done)
332        b.current = next
333    }
334    if defaultBody != nil {
335        b.targets = &targets{
336            tail:   b.targets,
337            _breakdone,
338        }
339        b.stmtList(*defaultBody)
340        b.targets = b.targets.tail
341        b.jump(done)
342    }
343    b.current = done
344}
345
346func (b *builderforStmt(s *ast.ForStmtlabel *lblock) {
347    //    ...init...
348    //      jump loop
349    // loop:
350    //      if cond goto body else done
351    // body:
352    //      ...body...
353    //      jump post
354    // post:                 (target of continue)
355    //      ...post...
356    //      jump loop
357    // done:                                 (target of break)
358    if s.Init != nil {
359        b.stmt(s.Init)
360    }
361    body := b.newBlock("for.body")
362    done := b.newBlock("for.done"// target of 'break'
363    loop := body                   // target of back-edge
364    if s.Cond != nil {
365        loop = b.newBlock("for.loop")
366    }
367    cont := loop // target of 'continue'
368    if s.Post != nil {
369        cont = b.newBlock("for.post")
370    }
371    if label != nil {
372        label._break = done
373        label._continue = cont
374    }
375    b.jump(loop)
376    b.current = loop
377    if loop != body {
378        b.add(s.Cond)
379        b.ifelse(bodydone)
380        b.current = body
381    }
382    b.targets = &targets{
383        tail:      b.targets,
384        _break:    done,
385        _continuecont,
386    }
387    b.stmt(s.Body)
388    b.targets = b.targets.tail
389    b.jump(cont)
390
391    if s.Post != nil {
392        b.current = cont
393        b.stmt(s.Post)
394        b.jump(loop// back-edge
395    }
396    b.current = done
397}
398
399func (b *builderrangeStmt(s *ast.RangeStmtlabel *lblock) {
400    b.add(s.X)
401
402    if s.Key != nil {
403        b.add(s.Key)
404    }
405    if s.Value != nil {
406        b.add(s.Value)
407    }
408
409    //      ...
410    // loop:                                   (target of continue)
411    //     if ... goto body else done
412    // body:
413    //      ...
414    //     jump loop
415    // done:                                   (target of break)
416
417    loop := b.newBlock("range.loop")
418    b.jump(loop)
419    b.current = loop
420
421    body := b.newBlock("range.body")
422    done := b.newBlock("range.done")
423    b.ifelse(bodydone)
424    b.current = body
425
426    if label != nil {
427        label._break = done
428        label._continue = loop
429    }
430    b.targets = &targets{
431        tail:      b.targets,
432        _break:    done,
433        _continueloop,
434    }
435    b.stmt(s.Body)
436    b.targets = b.targets.tail
437    b.jump(loop// back-edge
438    b.current = done
439}
440
441// -------- helpers --------
442
443// Destinations associated with unlabeled for/switch/select stmts.
444// We push/pop one of these as we enter/leave each construct and for
445// each BranchStmt we scan for the innermost target of the right type.
446type targets struct {
447    tail         *targets // rest of stack
448    _break       *Block
449    _continue    *Block
450    _fallthrough *Block
451}
452
453// Destinations associated with a labeled block.
454// We populate these as labels are encountered in forward gotos or
455// labeled statements.
456type lblock struct {
457    _goto     *Block
458    _break    *Block
459    _continue *Block
460}
461
462// labeledBlock returns the branch target associated with the
463// specified label, creating it if needed.
464func (b *builderlabeledBlock(label *ast.Ident) *lblock {
465    lb := b.lblocks[label.Obj]
466    if lb == nil {
467        lb = &lblock{_gotob.newBlock(label.Name)}
468        if b.lblocks == nil {
469            b.lblocks = make(map[*ast.Object]*lblock)
470        }
471        b.lblocks[label.Obj] = lb
472    }
473    return lb
474}
475
476// newBlock appends a new unconnected basic block to b.cfg's block
477// slice and returns it.
478// It does not automatically become the current block.
479// comment is an optional string for more readable debugging output.
480func (b *buildernewBlock(comment string) *Block {
481    g := b.cfg
482    block := &Block{
483        Index:   int32(len(g.Blocks)),
484        commentcomment,
485    }
486    block.Succs = block.succs2[:0]
487    g.Blocks = append(g.Blocksblock)
488    return block
489}
490
491func (b *builderadd(n ast.Node) {
492    b.current.Nodes = append(b.current.Nodesn)
493}
494
495// jump adds an edge from the current block to the target block,
496// and sets b.current to nil.
497func (b *builderjump(target *Block) {
498    b.current.Succs = append(b.current.Succstarget)
499    b.current = nil
500}
501
502// ifelse emits edges from the current block to the t and f blocks,
503// and sets b.current to nil.
504func (b *builderifelse(tf *Block) {
505    b.current.Succs = append(b.current.Succstf)
506    b.current = nil
507}
508
MembersX
token
builder.mayReturn
builder.forStmt.loop
builder.branchStmt.BlockStmt.BlockStmt.lb
builder.typeSwitchStmt.s
builder.switchStmt.RangeStmt_4072.clause
builder.typeSwitchStmt.label
builder.selectStmt.RangeStmt_6743.BlockStmt.next
builder.rangeStmt.s
builder.selectStmt.RangeStmt_6743.cc
builder.labeledBlock.label
builder.newBlock
builder.switchStmt.b
builder.typeSwitchStmt.RangeStmt_5490.BlockStmt.RangeStmt_5675.casetype
builder.cfg
builder.typeSwitchStmt.done
builder.ifelse
fmt
builder.stmt.BlockStmt._else
builder.typeSwitchStmt
builder.typeCaseBody
builder.switchStmt.ncases
builder.typeSwitchStmt.RangeStmt_5490.BlockStmt.body
builder.selectStmt.label
builder.targets
builder.branchStmt
builder.forStmt.s
builder.forStmt.label
targets.tail
targets._fallthrough
builder.lblocks
builder.stmtList.RangeStmt_2549.s
builder.rangeStmt.label
lblock
builder.switchStmt.RangeStmt_4072.BlockStmt.body
builder.typeCaseBody.done
builder.add
builder.ifelse.b
builder.selectStmt.s
builder.branchStmt.block
builder.switchStmt
targets
builder.rangeStmt.body
builder.stmtList.b
builder.typeSwitchStmt.RangeStmt_5490.clause
builder.rangeStmt.loop
builder.branchStmt.BlockStmt.BlockStmt.t
builder.newBlock.b
builder.jump
builder.add.n
builder.jump.b
builder.switchStmt.s
builder.typeSwitchStmt.RangeStmt_5490.BlockStmt.next
builder.stmt.BlockStmt.done
builder.switchStmt.defaultFallthrough
builder.typeCaseBody.b
builder.selectStmt.RangeStmt_6743.BlockStmt.body
builder.current
builder.stmt
builder.rangeStmt.b
builder.add.b
builder.newBlock.g
builder.switchStmt.defaultBlock
builder.labeledBlock.b
builder.typeCaseBody.cc
builder.stmtList
builder.branchStmt.s
builder.branchStmt.BlockStmt.t
builder.switchStmt.done
builder.switchStmt.fallthru
builder.selectStmt.b
builder.selectStmt.RangeStmt_6514.BlockStmt.comm
builder.forStmt.done
targets._break
builder.stmtList.list
builder.typeSwitchStmt.b
builder.rangeStmt.done
builder.rangeStmt
builder.typeSwitchStmt.default_
builder
builder.switchStmt.label
builder.jump.target
builder.switchStmt.RangeStmt_4072.BlockStmt.nextCond
builder.forStmt.cont
builder.labeledBlock
builder.ifelse.f
builder.switchStmt.RangeStmt_4072.i
builder.selectStmt
builder.selectStmt.done
builder.forStmt.b
builder.stmt.BlockStmt.then
builder.forStmt.body
builder.newBlock.block
builder.forStmt
targets._continue
lblock._break
ast
builder.stmt.label
builder.stmt.BlockStmt.BlockStmt.RangeStmt_1330.spec
builder.switchStmt.RangeStmt_4072.BlockStmt.RangeStmt_4535.cond
lblock._continue
builder.stmt._s
builder.branchStmt.b
builder.ifelse.t
lblock._goto
builder.newBlock.comment
builder.stmt.b
builder.selectStmt.RangeStmt_6514.clause
Members
X