github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/src/cmd/vet/internal/cfg/builder.go (about)

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