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

View as plain text