github.com/gotranspile/cxgo@v0.3.7/c_stmt.go (about)

     1  package cxgo
     2  
     3  import (
     4  	"go/ast"
     5  	"go/token"
     6  
     7  	"github.com/gotranspile/cxgo/types"
     8  )
     9  
    10  const optimizeStatements = false
    11  
    12  type CStmt interface {
    13  	Node
    14  	AsStmt() []GoStmt
    15  	Uses() []types.Usage
    16  }
    17  
    18  type CStmtFunc func(s CStmt) ([]CStmt, bool)
    19  
    20  type CCompStmt interface {
    21  	CStmt
    22  	EachStmt(fnc CStmtFunc) bool
    23  }
    24  
    25  type CStmtConv interface {
    26  	ToStmt() []CStmt
    27  }
    28  
    29  func cEachBlockStmt(fnc func([]CStmt), stmts []CStmt) {
    30  	fnc(stmts)
    31  	for _, s := range stmts {
    32  		switch s := s.(type) {
    33  		case *BlockStmt:
    34  			cEachBlockStmt(fnc, s.Stmts)
    35  		case *CIfStmt:
    36  			cEachBlockStmt(fnc, s.Then.Stmts)
    37  			if s.Else != nil {
    38  				cEachBlockStmt(fnc, []CStmt{s.Else})
    39  			}
    40  		case *CForStmt:
    41  			cEachBlockStmt(fnc, s.Body.Stmts)
    42  		case *CSwitchStmt:
    43  			for _, c := range s.Cases {
    44  				cEachBlockStmt(fnc, c.Stmts)
    45  			}
    46  		}
    47  	}
    48  }
    49  
    50  func cEachStmt(fnc func(CStmt) bool, stmts []CStmt) {
    51  	for _, s := range stmts {
    52  		if !fnc(s) {
    53  			continue
    54  		}
    55  		c, ok := s.(CCompStmt)
    56  		if !ok {
    57  			continue
    58  		}
    59  		c.EachStmt(func(s CStmt) ([]CStmt, bool) {
    60  			cEachStmt(fnc, []CStmt{s})
    61  			return nil, false
    62  		})
    63  	}
    64  }
    65  
    66  func cReplaceEachStmt(fnc CStmtFunc, stmts []CStmt) ([]CStmt, bool) {
    67  	b := &BlockStmt{Stmts: stmts}
    68  	var replace CStmtFunc
    69  	replace = func(s CStmt) ([]CStmt, bool) {
    70  		if out, mod := fnc(s); mod {
    71  			return out, mod
    72  		}
    73  		if c, ok := s.(CCompStmt); ok {
    74  			c.EachStmt(replace)
    75  		}
    76  		return nil, false
    77  	}
    78  	if b.EachStmt(replace) {
    79  		return b.Stmts, true
    80  	}
    81  	return stmts, false
    82  }
    83  
    84  func cExprStmt(x Expr) []GoStmt {
    85  	if s, ok := x.(CStmt); ok {
    86  		return s.AsStmt()
    87  	}
    88  	switch x := x.(type) {
    89  	case CStmt:
    90  		return x.AsStmt()
    91  	case IntLit:
    92  		return nil
    93  	}
    94  	return []GoStmt{exprStmt(x.AsExpr())}
    95  }
    96  
    97  func cOneStmt(x CStmt) GoStmt {
    98  	arr := x.AsStmt()
    99  	if len(arr) != 1 {
   100  		panic("should only one element")
   101  	}
   102  	return arr[0]
   103  }
   104  
   105  var (
   106  	_ CStmt = &CExprStmt{}
   107  )
   108  
   109  func NewCExprStmt(e Expr) []CStmt {
   110  	e = cUnwrap(e)
   111  	if m, ok := e.(*CMultiExpr); ok {
   112  		out := make([]CStmt, 0, len(m.Exprs))
   113  		for _, e := range m.Exprs {
   114  			out = append(out, NewCExprStmt(e)...)
   115  		}
   116  		return out
   117  	}
   118  	if s, ok := e.(CStmtConv); ok {
   119  		return s.ToStmt()
   120  	}
   121  	if ie, ok := e.(Ident); ok {
   122  		return []CStmt{&UnusedVar{Name: ie.Identifier()}}
   123  	}
   124  	return []CStmt{&CExprStmt{Expr: e}}
   125  }
   126  
   127  func NewCExprStmt1(e Expr) CStmt {
   128  	if e == nil {
   129  		return nil
   130  	}
   131  	e = cUnwrap(e)
   132  	if s, ok := e.(CStmtConv); ok {
   133  		arr := s.ToStmt()
   134  		if len(arr) == 1 {
   135  			return arr[0]
   136  		}
   137  	}
   138  	return &CExprStmt{Expr: e}
   139  }
   140  
   141  type CExprStmt struct {
   142  	Expr Expr
   143  }
   144  
   145  func (s *CExprStmt) Visit(v Visitor) {
   146  	v(s.Expr)
   147  }
   148  
   149  func (s *CExprStmt) CType() types.Type {
   150  	return s.Expr.CType(nil)
   151  }
   152  
   153  func (s *CExprStmt) AsExpr() GoExpr {
   154  	return s.Expr.AsExpr()
   155  }
   156  
   157  func (s *CExprStmt) AsStmt() []GoStmt {
   158  	return cExprStmt(s.Expr)
   159  }
   160  
   161  func (s *CExprStmt) Uses() []types.Usage {
   162  	return types.UseRead(s.Expr)
   163  }
   164  
   165  type CLabelStmt struct {
   166  	Label string
   167  }
   168  
   169  func (s *CLabelStmt) Visit(v Visitor) {}
   170  
   171  func (s *CLabelStmt) AsStmt() []GoStmt {
   172  	return []GoStmt{&ast.LabeledStmt{
   173  		Label: ident(s.Label),
   174  		Stmt:  &ast.EmptyStmt{Implicit: true},
   175  	}}
   176  }
   177  
   178  func (s *CLabelStmt) Uses() []types.Usage {
   179  	return nil
   180  }
   181  
   182  func (g *translator) NewCaseStmt(exp Expr, stmts ...CStmt) *CCaseStmt {
   183  	return &CCaseStmt{g: g, Expr: exp, Stmts: stmts}
   184  }
   185  
   186  type CCaseStmt struct {
   187  	g     *translator
   188  	Expr  Expr
   189  	Stmts []CStmt
   190  }
   191  
   192  func (s *CCaseStmt) Visit(v Visitor) {
   193  	v(s.Expr)
   194  	for _, st := range s.Stmts {
   195  		v(st)
   196  	}
   197  }
   198  
   199  func (s *CCaseStmt) GoCaseClause() *ast.CaseClause {
   200  	stmts := s.g.NewCBlock(s.Stmts...).GoBlockStmt()
   201  	if s.Expr == nil {
   202  		return &ast.CaseClause{
   203  			Body: stmts.List,
   204  		}
   205  	}
   206  	return &ast.CaseClause{
   207  		List: []GoExpr{s.Expr.AsExpr()},
   208  		Body: stmts.List,
   209  	}
   210  }
   211  
   212  func (s *CCaseStmt) AsStmt() []GoStmt {
   213  	return []GoStmt{s.GoCaseClause()}
   214  }
   215  
   216  func (s *CCaseStmt) Uses() []types.Usage {
   217  	var list []types.Usage
   218  	if s.Expr != nil {
   219  		list = append(list, types.UseRead(s.Expr)...)
   220  	}
   221  	for _, c := range s.Stmts {
   222  		list = append(list, c.Uses()...)
   223  	}
   224  	return list
   225  }
   226  
   227  func (g *translator) NewCIfStmt(cond BoolExpr, then []CStmt, els IfElseStmt) *CIfStmt {
   228  	then = g.NewCBlock(then...).Stmts
   229  	//if els != nil {
   230  	//	elss := g.NewCBlock(els).Stmts
   231  	//	if len(elss) != 0 && len(then) == 1 {
   232  	//		// TODO: check if both conditions has no side-effects
   233  	//		_, ok := then[0].(*CIfStmt)
   234  	//		_, ok2 := elss[0].(*CIfStmt)
   235  	//		// "then" branch contains a single if statement
   236  	//		// and "else" contains something else
   237  	//		if ok && !ok2 {
   238  	//			// invert if condition and swap branches
   239  	//			return g.NewCIfStmt(g.cNot(cond), elss, then[0])
   240  	//		}
   241  	//	}
   242  	//	if len(elss) == 1 {
   243  	//		if eif, ok := elss[0].(*CIfStmt); ok {
   244  	//			els = eif
   245  	//		}
   246  	//	}
   247  	//}
   248  	return &CIfStmt{
   249  		g:    g,
   250  		Cond: cond,
   251  		Then: g.NewCBlock(then...),
   252  		Else: els,
   253  	}
   254  }
   255  
   256  var _ CCompStmt = &CIfStmt{}
   257  
   258  type IfElseStmt interface {
   259  	CStmt
   260  	isElseStmt()
   261  }
   262  
   263  type CIfStmt struct {
   264  	g    *translator
   265  	Cond BoolExpr
   266  	Then *BlockStmt
   267  	Else IfElseStmt
   268  }
   269  
   270  func (s *CIfStmt) isElseStmt() {}
   271  
   272  func (s *CIfStmt) Visit(v Visitor) {
   273  	v(s.Cond)
   274  	v(s.Then)
   275  	v(s.Else)
   276  }
   277  
   278  func (g *translator) toElseStmt(st ...CStmt) IfElseStmt {
   279  	if len(st) == 0 {
   280  		return nil
   281  	}
   282  	if len(st) == 1 {
   283  		if e, ok := st[0].(IfElseStmt); ok {
   284  			return e
   285  		}
   286  	}
   287  	return g.newBlockStmt(st...)
   288  }
   289  
   290  func (s *CIfStmt) EachStmt(fnc CStmtFunc) bool {
   291  	mod := s.Then.EachStmt(fnc)
   292  	if s.Else == nil {
   293  		return mod
   294  	}
   295  	els := s.g.cBlockCast(s.Else)
   296  	mod2 := els.EachStmt(fnc)
   297  	if len(els.Stmts) == 0 {
   298  		s.Else = nil
   299  	} else if len(els.Stmts) == 1 {
   300  		s.Else = s.g.toElseStmt(els.Stmts[0])
   301  	} else {
   302  		s.Else = els
   303  	}
   304  	return mod || mod2
   305  }
   306  
   307  func (s *CIfStmt) AsStmt() []GoStmt {
   308  	cond := s.Cond.AsExpr()
   309  	then := s.Then.GoBlockStmt().List
   310  	var els []GoStmt
   311  	if s.Else != nil {
   312  		els = s.Else.AsStmt()
   313  	}
   314  	return []GoStmt{
   315  		ifelse(cond, then, els),
   316  	}
   317  }
   318  
   319  func (s *CIfStmt) Uses() []types.Usage {
   320  	var list []types.Usage
   321  	list = append(list, types.UseRead(s.Cond)...)
   322  	list = append(list, s.Then.Uses()...)
   323  	if s.Else != nil {
   324  		list = append(list, s.Else.Uses()...)
   325  	}
   326  	return list
   327  }
   328  
   329  func (g *translator) NewCSwitchStmt(cond Expr, stmts []CStmt) *CSwitchStmt {
   330  	sw := &CSwitchStmt{g: g, Cond: cond}
   331  	sw.addStmts(stmts)
   332  	// TODO: fix branches (break, fallthrough)
   333  	return sw
   334  }
   335  
   336  var _ CCompStmt = &CSwitchStmt{}
   337  
   338  type CSwitchStmt struct {
   339  	g     *translator
   340  	Cond  Expr
   341  	Cases []*CCaseStmt
   342  }
   343  
   344  func (s *CSwitchStmt) Visit(v Visitor) {
   345  	v(s.Cond)
   346  	for _, c := range s.Cases {
   347  		v(c)
   348  	}
   349  }
   350  
   351  func (s *CSwitchStmt) addStmts(stmts []CStmt) {
   352  	for _, st := range s.g.NewCBlock(stmts...).Stmts {
   353  		if c, ok := st.(*CCaseStmt); ok {
   354  			sub := c.Stmts
   355  			c.Stmts = nil
   356  			s.Cases = append(s.Cases, c)
   357  			if len(sub) != 0 {
   358  				s.addStmts(sub)
   359  			}
   360  		} else {
   361  			last := s.Cases[len(s.Cases)-1]
   362  			last.Stmts = append(last.Stmts, st)
   363  		}
   364  	}
   365  }
   366  
   367  func (s *CSwitchStmt) EachStmt(fnc CStmtFunc) bool {
   368  	gmod := false
   369  	for _, c := range s.Cases {
   370  		var mod bool
   371  		c.Stmts, mod = eachStmtIn(c.Stmts, fnc)
   372  		gmod = gmod || mod
   373  	}
   374  	return gmod
   375  }
   376  
   377  func (s *CSwitchStmt) AsStmt() []GoStmt {
   378  	var stmts []GoStmt
   379  	for i, c := range s.Cases {
   380  		cs := c.GoCaseClause()
   381  		sub := cs.Body
   382  		if len(sub) == 0 {
   383  			if i != len(s.Cases)-1 {
   384  				sub = []GoStmt{&ast.BranchStmt{Tok: token.FALLTHROUGH}}
   385  			}
   386  		} else {
   387  			switch b := sub[len(sub)-1].(type) {
   388  			case *ast.BranchStmt:
   389  				if b.Tok == token.BREAK {
   390  					sub = sub[:len(sub)-1]
   391  				}
   392  			case *ast.ReturnStmt:
   393  			default:
   394  				if i != len(s.Cases)-1 {
   395  					sub = append(sub, &ast.BranchStmt{Tok: token.FALLTHROUGH})
   396  				}
   397  			}
   398  		}
   399  		cs.Body = sub
   400  		stmts = append(stmts, cs)
   401  	}
   402  	return []GoStmt{&ast.SwitchStmt{
   403  		Tag:  s.Cond.AsExpr(),
   404  		Body: block(stmts...),
   405  	}}
   406  }
   407  
   408  func (s *CSwitchStmt) Uses() []types.Usage {
   409  	var list []types.Usage
   410  	list = append(list, types.UseRead(s.Cond)...)
   411  	for _, c := range s.Cases {
   412  		list = append(list, c.Uses()...)
   413  	}
   414  	return list
   415  }
   416  
   417  func (g *translator) NewCForDeclStmt(init CDecl, cond BoolExpr, iter Expr, stmts []CStmt) *CForStmt {
   418  	if cIsTrue(cond) {
   419  		cond = nil
   420  	}
   421  	f := &CForStmt{
   422  		Init: g.NewCDeclStmt1(init),
   423  		Cond: cond,
   424  		Iter: NewCExprStmt1(iter),
   425  	}
   426  	if body := g.NewCBlock(stmts...); body != nil {
   427  		f.Body = *body
   428  	}
   429  	return f
   430  }
   431  
   432  func (g *translator) NewCForStmt(init Expr, cond BoolExpr, iter Expr, stmts []CStmt) *CForStmt {
   433  	if cIsTrue(cond) {
   434  		cond = nil
   435  	}
   436  	f := &CForStmt{
   437  		Init: NewCExprStmt1(init),
   438  		Cond: cond,
   439  		Iter: NewCExprStmt1(iter),
   440  	}
   441  	if body := g.NewCBlock(stmts...); body != nil {
   442  		f.Body = *body
   443  	}
   444  	return f
   445  }
   446  
   447  var _ CCompStmt = &CForStmt{}
   448  
   449  type CForStmt struct {
   450  	Init CStmt
   451  	Cond Expr
   452  	Iter CStmt
   453  	Body BlockStmt
   454  }
   455  
   456  func (s *CForStmt) Visit(v Visitor) {
   457  	v(s.Init)
   458  	v(s.Cond)
   459  	v(s.Iter)
   460  	v(&s.Body)
   461  }
   462  
   463  func (s *CForStmt) EachStmt(fnc CStmtFunc) bool {
   464  	return s.Body.EachStmt(fnc)
   465  }
   466  
   467  func (s *CForStmt) AsStmt() []GoStmt {
   468  	var init GoStmt
   469  	if s.Init != nil {
   470  		// if we have only one init decl with one spec -> convert to inline for decl
   471  		init = cOneStmt(s.Init)
   472  		if s, ok := init.(*ast.DeclStmt); ok {
   473  			if g, ok := s.Decl.(*ast.GenDecl); ok && g.Tok == token.VAR && len(g.Specs) == 1 {
   474  				if sp, ok := g.Specs[0].(*ast.ValueSpec); ok && len(sp.Names) == len(sp.Values) {
   475  					var (
   476  						lhs []ast.Expr
   477  						rhs []ast.Expr
   478  					)
   479  					for i := range sp.Names {
   480  						lhs = append(lhs, sp.Names[i])
   481  						rhs = append(rhs, &ast.CallExpr{Fun: sp.Type, Args: []ast.Expr{sp.Values[i]}})
   482  					}
   483  					init = &ast.AssignStmt{
   484  						Lhs: lhs,
   485  						Tok: token.DEFINE,
   486  						Rhs: rhs,
   487  					}
   488  				}
   489  			}
   490  		}
   491  	}
   492  	var iter GoStmt
   493  	if s.Iter != nil {
   494  		iter = cOneStmt(s.Iter)
   495  	}
   496  	var cond GoExpr
   497  	if s.Cond != nil {
   498  		cond = s.Cond.AsExpr()
   499  	}
   500  	return []GoStmt{&ast.ForStmt{
   501  		Init: init,
   502  		Cond: cond,
   503  		Post: iter,
   504  		Body: s.Body.GoBlockStmt(),
   505  	}}
   506  }
   507  
   508  func (s *CForStmt) Uses() []types.Usage {
   509  	var list []types.Usage
   510  	if s.Init != nil {
   511  		list = append(list, s.Init.Uses()...)
   512  	}
   513  	if s.Cond != nil {
   514  		list = append(list, types.UseRead(s.Cond)...)
   515  	}
   516  	if s.Iter != nil {
   517  		list = append(list, s.Iter.Uses()...)
   518  	}
   519  	list = append(list, s.Body.Uses()...)
   520  	return list
   521  }
   522  
   523  // forCanBreak returns true in case a for statement can be exited at some point to continue execution of the function.
   524  // This means that it will return false for loops that return from the function directly.
   525  func (g *translator) forCanBreak(s *CForStmt) bool {
   526  	if s.Cond != nil {
   527  		v, ok := cIsBoolConst(s.Cond)
   528  		if !ok {
   529  			// not a constant, so should break at some point... most probably
   530  			return true
   531  		}
   532  		if !v {
   533  			// breaks instantly
   534  			return true
   535  		}
   536  	}
   537  	// "infinite", but can still break manually, so find break statements
   538  	return g.hasBreaks(&s.Body)
   539  }
   540  
   541  // switchCanBreak returns true in case a switch statement can be exited at some point to continue execution of the function.
   542  // This means that it will return false for switches that return from all the branches.
   543  func (g *translator) switchCanBreak(s *CSwitchStmt) bool {
   544  	if len(s.Cases) == 0 {
   545  		return true
   546  	}
   547  	for i, c := range s.Cases {
   548  		if len(c.Stmts) == 0 {
   549  			if i == len(c.Stmts)-1 {
   550  				// last is empty - will exit
   551  				return true
   552  			}
   553  			// fallthrough - ignore
   554  			continue
   555  		}
   556  		if !g.allBranchesJump(c.Stmts[len(c.Stmts)-1]) {
   557  			return true
   558  		}
   559  	}
   560  	return false
   561  }
   562  
   563  func (g *translator) NewCDoWhileStmt(cond Expr, stmts []CStmt) CStmt {
   564  	stmts = append(stmts, g.NewCIfStmt(
   565  		g.cNot(cond), []CStmt{&CBreakStmt{}}, nil,
   566  	))
   567  	return g.NewCForStmt(nil, nil, nil, stmts)
   568  }
   569  
   570  type CGotoStmt struct {
   571  	Label string
   572  }
   573  
   574  func (s *CGotoStmt) Visit(v Visitor) {}
   575  
   576  func (s *CGotoStmt) AsStmt() []GoStmt {
   577  	return []GoStmt{&ast.BranchStmt{Tok: token.GOTO, Label: ident(s.Label)}}
   578  }
   579  
   580  func (s *CGotoStmt) Uses() []types.Usage {
   581  	return nil
   582  }
   583  
   584  func countContinue(stmts ...CStmt) int {
   585  	var cnt int
   586  	cEachStmt(func(s CStmt) bool {
   587  		switch s.(type) {
   588  		case *CContinueStmt:
   589  			cnt++
   590  		case *CForStmt:
   591  			return false
   592  		}
   593  		return true
   594  	}, stmts)
   595  	return cnt
   596  }
   597  
   598  func countBreak(stmts ...CStmt) int {
   599  	var cnt int
   600  	cEachStmt(func(s CStmt) bool {
   601  		switch s.(type) {
   602  		case *CBreakStmt:
   603  			cnt++
   604  		case *CForStmt:
   605  			return false
   606  		}
   607  		return true
   608  	}, stmts)
   609  	return cnt
   610  }
   611  
   612  type CContinueStmt struct{}
   613  
   614  func (s *CContinueStmt) Visit(v Visitor) {}
   615  
   616  func (s *CContinueStmt) AsStmt() []GoStmt {
   617  	return []GoStmt{&ast.BranchStmt{Tok: token.CONTINUE}}
   618  }
   619  
   620  func (s *CContinueStmt) Uses() []types.Usage {
   621  	return nil
   622  }
   623  
   624  type CBreakStmt struct{}
   625  
   626  func (s *CBreakStmt) Visit(v Visitor) {}
   627  
   628  func (s *CBreakStmt) AsStmt() []GoStmt {
   629  	return []GoStmt{&ast.BranchStmt{Tok: token.BREAK}}
   630  }
   631  
   632  func (s *CBreakStmt) Uses() []types.Usage {
   633  	return nil
   634  }
   635  
   636  func (g *translator) NewReturnStmt(x Expr, rtyp types.Type) []CStmt {
   637  	x = cUnwrap(x)
   638  	switch x := x.(type) {
   639  	case *CTernaryExpr:
   640  		// return (v ? a : b) -> if v { return a } return b
   641  		var stmts []CStmt
   642  		stmts = append(stmts, g.NewCIfStmt(x.Cond, g.NewReturnStmt(x.Then, rtyp), nil))
   643  		stmts = append(stmts, g.NewReturnStmt(x.Else, rtyp)...)
   644  		return stmts
   645  	}
   646  	if rtyp != nil {
   647  		x = g.cCast(rtyp, x)
   648  	}
   649  	return []CStmt{&CReturnStmt{
   650  		Expr: x,
   651  	}}
   652  }
   653  
   654  func (g *translator) NewZeroReturnStmt(rtyp types.Type) []CStmt {
   655  	return g.NewReturnStmt(g.ZeroValue(rtyp), rtyp)
   656  }
   657  
   658  type CReturnStmt struct {
   659  	Expr Expr
   660  }
   661  
   662  func (s *CReturnStmt) Visit(v Visitor) {
   663  	v(s.Expr)
   664  }
   665  
   666  func (s *CReturnStmt) AsStmt() []GoStmt {
   667  	if s.Expr == nil {
   668  		return []GoStmt{returnStmt()}
   669  	}
   670  	return []GoStmt{returnStmt(s.Expr.AsExpr())}
   671  }
   672  
   673  func (s *CReturnStmt) Uses() []types.Usage {
   674  	var list []types.Usage
   675  	if s.Expr != nil {
   676  		list = append(list, types.UseRead(s.Expr)...)
   677  	}
   678  	return list
   679  }
   680  
   681  func (g *translator) setReturnType(ret types.Type, stmts []CStmt) {
   682  	if ret == nil {
   683  		return
   684  	}
   685  	for _, s := range stmts {
   686  		switch s := s.(type) {
   687  		case *CReturnStmt:
   688  			s.Expr = g.cCast(ret, s.Expr)
   689  		case *BlockStmt:
   690  			g.setReturnType(ret, s.Stmts)
   691  		case *CIfStmt:
   692  			g.setReturnType(ret, s.Then.Stmts)
   693  			if s.Else != nil {
   694  				g.setReturnType(ret, []CStmt{s.Else})
   695  			}
   696  		case *CForStmt:
   697  			g.setReturnType(ret, s.Body.Stmts)
   698  		case *CSwitchStmt:
   699  			for _, c := range s.Cases {
   700  				g.setReturnType(ret, c.Stmts)
   701  			}
   702  		}
   703  	}
   704  }
   705  
   706  func (g *translator) cBlockCast(s CStmt) *BlockStmt {
   707  	if b, ok := s.(*BlockStmt); ok {
   708  		return b
   709  	}
   710  	return g.newBlockStmt(s)
   711  }
   712  
   713  func stmtHasDecl(stmts ...CStmt) bool {
   714  	for _, st := range stmts {
   715  		switch st.(type) {
   716  		case *CDeclStmt:
   717  			return true
   718  		}
   719  	}
   720  	return false
   721  }
   722  
   723  func flattenBlocks(stmts []CStmt) ([]CStmt, bool) {
   724  	mod := false
   725  	for i := 0; i < len(stmts); i++ {
   726  		s, ok := stmts[i].(*BlockStmt)
   727  		if !ok || stmtHasDecl(s.Stmts...) {
   728  			continue
   729  		}
   730  		arr := append([]CStmt{}, stmts[:i]...)
   731  		arr = append(arr, s.Stmts...)
   732  		arr = append(arr, stmts[i+1:]...)
   733  		stmts = arr
   734  		mod = true
   735  		i += len(s.Stmts) - 1
   736  	}
   737  	return stmts, mod
   738  }
   739  
   740  func (g *translator) isReturnOrExit(s CStmt) bool {
   741  	switch s := s.(type) {
   742  	case *CReturnStmt:
   743  		return true
   744  	case *CExprStmt:
   745  		switch e := s.Expr.(type) {
   746  		case *CallExpr:
   747  			if id, ok := e.Fun.(Ident); ok {
   748  				switch id.Identifier() {
   749  				case g.env.Go().OsExitFunc(),
   750  					g.env.Go().PanicFunc():
   751  					return true
   752  				}
   753  			}
   754  		}
   755  	}
   756  	return false
   757  }
   758  
   759  func isEmptyReturn(s CStmt) bool {
   760  	r, ok := s.(*CReturnStmt)
   761  	return ok && r.Expr == nil
   762  }
   763  
   764  func (g *translator) isHardJump(s CStmt) bool {
   765  	switch s.(type) {
   766  	case *CReturnStmt, *CGotoStmt:
   767  		return true
   768  	}
   769  	return false
   770  }
   771  
   772  func (g *translator) isJump(s CStmt) bool {
   773  	if g.isHardJump(s) {
   774  		return true
   775  	}
   776  	switch s.(type) {
   777  	case *CContinueStmt, *CBreakStmt:
   778  		return true
   779  	}
   780  	return false
   781  }
   782  
   783  func (g *translator) allBranchesJump(s CStmt) bool {
   784  	if g.isJump(s) {
   785  		return true
   786  	}
   787  	switch s := s.(type) {
   788  	case *BlockStmt:
   789  		if len(s.Stmts) == 0 {
   790  			return false
   791  		}
   792  		return g.allBranchesJump(s.Stmts[len(s.Stmts)-1])
   793  	case *CIfStmt:
   794  		if !g.allBranchesJump(s.Then) {
   795  			return false
   796  		}
   797  		if s.Else == nil {
   798  			return false // yes, in other cases we'll miss false condition jump
   799  		}
   800  		return g.allBranchesJump(s.Else)
   801  	case *CSwitchStmt:
   802  		return !g.switchCanBreak(s)
   803  	case *CForStmt:
   804  		return !g.forCanBreak(s)
   805  	}
   806  	return false
   807  }
   808  
   809  func dupStmts(stmts []CStmt) []CStmt {
   810  	out, _ := cReplaceEachStmt(func(s CStmt) ([]CStmt, bool) {
   811  		_, ok := s.(*CLabelStmt)
   812  		return nil, ok
   813  	}, stmts)
   814  	return out
   815  }
   816  
   817  // invertLastIf finds an if statements that can be inverted.
   818  // It checks in an statement body is smaller than all the other statements
   819  // that follow this if.
   820  // This function can only work in function bodies, because it relies on the returns.
   821  func (g *translator) invertLastIf(stmts []CStmt) ([]CStmt, bool) {
   822  	n := len(stmts)
   823  	if n < 2 {
   824  		return stmts, false
   825  	}
   826  	var (
   827  		ind   = -1
   828  		iff   *CIfStmt
   829  		isRet = false
   830  	)
   831  	for i := n - 1; i > 0; i-- {
   832  		f, ok := stmts[i].(*CIfStmt)
   833  		if !ok || f.Else != nil || len(f.Then.Stmts) == 0 {
   834  			continue
   835  		}
   836  		fn := len(f.Then.Stmts)
   837  		isRet = g.isHardJump(f.Then.Stmts[fn-1])
   838  		if isRet && fn == 1 {
   839  			continue
   840  		}
   841  		if cost := stmtCost(stmts[i+1:]...); !isRet && cost > 2 {
   842  			continue
   843  		} else if cost >= stmtCost(f.Then.Stmts...) {
   844  			continue
   845  		}
   846  		ind, iff = i, f
   847  		break
   848  	}
   849  	if iff == nil {
   850  		return stmts, false
   851  	}
   852  	ret := append([]CStmt{}, stmts[ind+1:]...)
   853  	if len(ret) == 0 || !g.isHardJump(ret[len(ret)-1]) {
   854  		ret = append(ret, &CReturnStmt{})
   855  	}
   856  	iff.Cond = g.cNot(iff.Cond)
   857  	b := iff.Then
   858  	iff.Then = g.NewCBlock(append([]CStmt{}, stmts[ind+1:]...)...)
   859  	if n := len(iff.Then.Stmts); n == 0 || !g.isHardJump(iff.Then.Stmts[n-1]) {
   860  		if l, ok := ret[0].(*CLabelStmt); ok {
   861  			iff.Then.Stmts = append(iff.Then.Stmts, &CGotoStmt{Label: l.Label})
   862  		} else {
   863  			iff.Then.Stmts = append(iff.Then.Stmts, dupStmts(ret)...)
   864  		}
   865  	}
   866  	stmts = append(stmts[:ind+1], b.Stmts...)
   867  	if !g.isHardJump(stmts[len(stmts)-1]) {
   868  		stmts = append(stmts, ret...)
   869  	}
   870  	if isEmptyReturn(stmts[len(stmts)-1]) {
   871  		stmts = stmts[:len(stmts)-1]
   872  	}
   873  	return stmts, true
   874  }
   875  
   876  // moveLastReturnToLastIf finds an if statement with else branch, that is before the return and
   877  // moves the return to all the if branches.
   878  // It helps apply other simplifications later.
   879  func (g *translator) moveLastReturnToLastIf(stmts []CStmt) ([]CStmt, bool) {
   880  	n := len(stmts)
   881  	if n < 2 {
   882  		return stmts, false
   883  	}
   884  	ret, ok := stmts[n-1].(*CReturnStmt)
   885  	if !ok {
   886  		return stmts, false
   887  	}
   888  	iff, ok := stmts[n-2].(*CIfStmt)
   889  	if !ok || iff.Else == nil {
   890  		return stmts, false
   891  	}
   892  	stmts = stmts[:n-1]
   893  	n--
   894  	allReturns := true
   895  	mod := false
   896  	for iff != nil {
   897  		if !g.isJump(iff.Then.Stmts[len(iff.Then.Stmts)-1]) {
   898  			iff.Then.Stmts = append(iff.Then.Stmts, ret)
   899  			mod = true
   900  		}
   901  		if iff.Else == nil {
   902  			allReturns = false
   903  			break
   904  		}
   905  		els := iff.Else
   906  		switch els := els.(type) {
   907  		case *BlockStmt:
   908  			if !g.isJump(els.Stmts[len(els.Stmts)-1]) {
   909  				els.Stmts = append(els.Stmts, ret)
   910  				mod = true
   911  			}
   912  			iff = nil
   913  		case *CIfStmt:
   914  			iff = els
   915  		default:
   916  			if !g.isJump(els) {
   917  				iff.Else = g.NewCBlock(els, ret)
   918  				mod = true
   919  			}
   920  			iff = nil
   921  		}
   922  	}
   923  	if !allReturns {
   924  		stmts = append(stmts, ret)
   925  	}
   926  	return stmts, mod
   927  }
   928  
   929  // inlineElseInLastReturn finds a return in the end and inlines its else branch
   930  // if all the branches contain jumps at the end.
   931  func (g *translator) inlineElseInLastReturn(stmts []CStmt) ([]CStmt, bool) {
   932  	n := len(stmts)
   933  	if n < 1 {
   934  		return stmts, false
   935  	}
   936  	iff, ok := stmts[n-1].(*CIfStmt)
   937  	if !ok || iff.Else == nil {
   938  		return stmts, false
   939  	}
   940  	if !g.allBranchesJump(iff.Then) || !g.allBranchesJump(iff.Else) {
   941  		return stmts, false
   942  	}
   943  	els := iff.Else
   944  	iff.Else = nil
   945  	switch els := els.(type) {
   946  	case *BlockStmt:
   947  		stmts = append(stmts, els.Stmts...)
   948  	default:
   949  		stmts = append(stmts, els)
   950  	}
   951  	return stmts, true
   952  }
   953  
   954  func (g *translator) inlineSmallGotos(stmts []CStmt) ([]CStmt, bool) {
   955  	const maxCost = 5
   956  	// first, collect all paths from the label
   957  	// we only care about direct paths that lead to a "hard jump" (return or another goto)
   958  	labels := make(map[string][]CStmt)
   959  	seen := make(map[*CLabelStmt]struct{})
   960  	cEachBlockStmt(func(stmts []CStmt) {
   961  		for i, s := range stmts {
   962  			l, ok := s.(*CLabelStmt)
   963  			if !ok {
   964  				continue
   965  			} else if _, ok := seen[l]; ok {
   966  				continue
   967  			}
   968  			seen[l] = struct{}{}
   969  			cnt := 0
   970  			for j, s := range stmts[i:] {
   971  				if g.isHardJump(s) {
   972  					cnt = j + 1
   973  					break
   974  				}
   975  			}
   976  			if cnt == 0 {
   977  				continue
   978  			}
   979  			body := append([]CStmt{}, stmts[i+1:i+cnt]...)
   980  			if stmtCost(body...) > maxCost {
   981  				continue
   982  			}
   983  			labels[l.Label] = body
   984  		}
   985  	}, stmts)
   986  	seen = nil
   987  	// rescan label bodies and replace gotos in them as well
   988  	for name, body := range labels {
   989  		mod, del := false, false
   990  		body, _ = cReplaceEachStmt(func(s CStmt) ([]CStmt, bool) {
   991  			if del {
   992  				return nil, false
   993  			}
   994  			g, ok := s.(*CGotoStmt)
   995  			if !ok {
   996  				return nil, false
   997  			}
   998  			if name == g.Label {
   999  				// loop - delete the label
  1000  				delete(labels, name)
  1001  				del = true
  1002  				return nil, false
  1003  			}
  1004  			if body := labels[g.Label]; len(body) != 0 {
  1005  				mod = true
  1006  				return body, true
  1007  			}
  1008  			return nil, false
  1009  		}, body)
  1010  		if mod && !del {
  1011  			labels[name] = body
  1012  		}
  1013  	}
  1014  	if len(labels) == 0 {
  1015  		return stmts, false
  1016  	}
  1017  	// replace gotos with those paths and remove labels
  1018  	stmts, _ = cReplaceEachStmt(func(s CStmt) ([]CStmt, bool) {
  1019  		if l, ok := s.(*CLabelStmt); ok {
  1020  			if len(labels[l.Label]) != 0 {
  1021  				return nil, true
  1022  			}
  1023  		}
  1024  		g, ok := s.(*CGotoStmt)
  1025  		if !ok {
  1026  			return nil, false
  1027  		}
  1028  		if body := labels[g.Label]; len(body) != 0 {
  1029  			return body, true
  1030  		}
  1031  		return nil, false
  1032  	}, stmts)
  1033  	return stmts, true
  1034  }
  1035  
  1036  func (g *translator) NewCBlock(stmts ...CStmt) *BlockStmt {
  1037  	if len(stmts) == 1 {
  1038  		if b, ok := stmts[0].(*BlockStmt); ok {
  1039  			return b
  1040  		}
  1041  	}
  1042  	// TODO:
  1043  	//if len(stmts) >= 2 {
  1044  	//	l := len(stmts)
  1045  	//	ret, ok1 := stmts[l-1].(*ast.ReturnStmt)
  1046  	//	ifs, ok2 := stmts[l-2].(*ast.IfStmt)
  1047  	//	if ok1 && ok2 && ifs.Else == nil && len(ifs.Body.List) > 1 {
  1048  	//		ifs.Cond = not(ifs.Cond)
  1049  	//		body := ifs.Body.List
  1050  	//		ifs.Body.List = []GoStmt{ret}
  1051  	//		if _, ok := body[len(body)-1].(*ast.ReturnStmt); !ok {
  1052  	//			body = append(body, ret)
  1053  	//		}
  1054  	//		stmts = append(stmts[:l-1], body...)
  1055  	//		return block(stmts...)
  1056  	//	}
  1057  	//}
  1058  	for {
  1059  		var mod bool
  1060  		stmts, mod = flattenBlocks(stmts)
  1061  		if mod {
  1062  			continue
  1063  		}
  1064  		if optimizeStatements {
  1065  			stmts, mod = replaceBreaks(stmts)
  1066  			if mod {
  1067  				continue
  1068  			}
  1069  			stmts, mod = rebuildFors(stmts)
  1070  			if mod {
  1071  				continue
  1072  			}
  1073  		}
  1074  		break
  1075  	}
  1076  	return g.newBlockStmt(stmts...)
  1077  }
  1078  
  1079  func isIfReturn(s CStmt) (Expr, *CReturnStmt, bool) {
  1080  	iff, ok := s.(*CIfStmt)
  1081  	if !ok || iff.Else != nil || len(iff.Then.Stmts) != 1 {
  1082  		return nil, nil, false
  1083  	}
  1084  	ret, ok := iff.Then.Stmts[0].(*CReturnStmt)
  1085  	if !ok {
  1086  		return nil, nil, false
  1087  	}
  1088  	return iff.Cond, ret, true
  1089  }
  1090  
  1091  //func tryMergeForPreCond(cond Expr, ret *CReturnStmt, st CStmt) bool {
  1092  //	forr, ok := st.(*CForStmt)
  1093  //	if !ok || forr.Cond != nil || forr.Iter != nil || forr.Body == nil {
  1094  //		return false
  1095  //	}
  1096  //	n := len(forr.Body.Stmts)
  1097  //	if n == 0 {
  1098  //		return false
  1099  //	}
  1100  //	cond2, ret2, ok := isIfReturn(forr.Body.Stmts[n-1])
  1101  //	if !ok {
  1102  //		return false
  1103  //	}
  1104  //	if countContinue(forr.Body.Stmts...) != 0 {
  1105  //		return false
  1106  //	}
  1107  //	if
  1108  //}
  1109  
  1110  func revFindInfiniteFor(stmts []CStmt) int {
  1111  	for i := len(stmts) - 1; i >= 0; i-- {
  1112  		if f, ok := stmts[i].(*CForStmt); ok && f.Cond == nil {
  1113  			return i
  1114  		}
  1115  	}
  1116  	return -1
  1117  }
  1118  
  1119  func replaceBreaks(stmts []CStmt) ([]CStmt, bool) {
  1120  	n := len(stmts)
  1121  	if n < 2 {
  1122  		return stmts, false
  1123  	}
  1124  	_, ok := stmts[n-1].(*CReturnStmt)
  1125  	if !ok {
  1126  		return stmts, false
  1127  	}
  1128  	for i := n - 1; i >= 0 && n-i < 5; i = revFindInfiniteFor(stmts[:i]) {
  1129  		forr, ok := stmts[i].(*CForStmt)
  1130  		if !ok {
  1131  			continue
  1132  		}
  1133  		part := stmts[i+1:]
  1134  		b := countBreak(&forr.Body)
  1135  		if b != 1 {
  1136  			continue
  1137  		}
  1138  		r := 0
  1139  		var replace CStmtFunc
  1140  		replace = func(s CStmt) ([]CStmt, bool) {
  1141  			switch s := s.(type) {
  1142  			case *CBreakStmt:
  1143  				r++
  1144  				return part, true
  1145  			case *CForStmt:
  1146  				return nil, false
  1147  			case CCompStmt:
  1148  				s.EachStmt(replace)
  1149  			}
  1150  			return nil, false
  1151  		}
  1152  		forr.EachStmt(replace)
  1153  		if r != b {
  1154  			panic(r)
  1155  		}
  1156  		return stmts[:i+1], true
  1157  	}
  1158  	return stmts, false
  1159  }
  1160  
  1161  func rebuildFors(stmts []CStmt) ([]CStmt, bool) {
  1162  	n := len(stmts)
  1163  	if n < 2 {
  1164  		return stmts, false
  1165  	}
  1166  	//for i, s := range stmts[:n-1] {
  1167  	//	j := i+1
  1168  	//	s2 := stmts[j]
  1169  	//	if cond, ret, ok := isIfReturn(s); ok && tryMergeForPreCond(cond, ret, s2) {
  1170  	//		stmts = append(stmts[:i], stmts[i+1:]...)
  1171  	//		continue rules
  1172  	//	}
  1173  	//}
  1174  	return stmts, false
  1175  	// TODO:
  1176  	//for i, s := range stmts[:len(stmts)-1] {
  1177  	//	as, ok := s.(*ast.AssignStmt)
  1178  	//	if !ok || len(as.Lhs) != 1 || as.Tok != token.ASSIGN {
  1179  	//		continue
  1180  	//	}
  1181  	//	x, ok := as.Lhs[0].(*ast.Ident)
  1182  	//	if !ok {
  1183  	//		continue
  1184  	//	}
  1185  	//	if lit, ok := as.Rhs[0].(*ast.BasicLit); !ok || lit.Kind != token.INT {
  1186  	//		continue
  1187  	//	}
  1188  	//	loop, ok := stmts[i+1].(*ast.ForStmt)
  1189  	//	if !ok || loop.Init != nil || loop.Cond != nil || loop.Post != nil || loop.Body == nil || len(loop.Body.List) < 2 {
  1190  	//		continue
  1191  	//	}
  1192  	//	body := loop.Body.List
  1193  	//	l := len(body)
  1194  	//	inc, ok := body[l-2].(*ast.IncDecStmt)
  1195  	//	if !ok {
  1196  	//		continue
  1197  	//	} else if x2, ok := inc.X.(*ast.Ident); !ok || x2.Name != x.Name {
  1198  	//		continue
  1199  	//	}
  1200  	//	ifc, ok := body[l-1].(*ast.IfStmt)
  1201  	//	if !ok || ifc.Init != nil || ifc.Else != nil || len(ifc.Body.List) != 1 {
  1202  	//		continue
  1203  	//	} else if b, ok := ifc.Body.List[0].(*ast.BranchStmt); !ok || b.Tok != token.BREAK || b.Label != nil {
  1204  	//		continue
  1205  	//	}
  1206  	//	loop.Init = as
  1207  	//	loop.Cond = not(ifc.Cond)
  1208  	//	loop.Post = inc
  1209  	//	loop.Body.List = body[:l-2]
  1210  	//	return append(stmts[:i], rebuildFors(stmts[i+1:])...)
  1211  	//}
  1212  }
  1213  
  1214  var _ CCompStmt = &BlockStmt{}
  1215  
  1216  func (g *translator) newBlockStmt(stmts ...CStmt) *BlockStmt {
  1217  	return &BlockStmt{
  1218  		g:     g,
  1219  		Stmts: stmts,
  1220  	}
  1221  }
  1222  
  1223  type BlockStmt struct {
  1224  	g     *translator
  1225  	Stmts []CStmt
  1226  }
  1227  
  1228  func (s *BlockStmt) isElseStmt() {}
  1229  
  1230  func (s *BlockStmt) Visit(v Visitor) {
  1231  	for _, st := range s.Stmts {
  1232  		v(st)
  1233  	}
  1234  }
  1235  
  1236  func eachStmtIn(stmts []CStmt, fnc CStmtFunc) ([]CStmt, bool) {
  1237  	gmod := false
  1238  	for i := 0; i < len(stmts); i++ {
  1239  		arr, mod := fnc(stmts[i])
  1240  		if !mod {
  1241  			continue
  1242  		}
  1243  		if !gmod {
  1244  			stmts = append([]CStmt{}, stmts...)
  1245  		}
  1246  		gmod = true
  1247  		if d := len(arr); d == 0 {
  1248  			stmts = append(stmts[:i], stmts[i+1:]...)
  1249  			i--
  1250  		} else if d == 1 {
  1251  			stmts[i] = arr[0]
  1252  		} else if n := len(stmts); i == n-1 {
  1253  			stmts = append(stmts[:i], arr...)
  1254  			break
  1255  		} else {
  1256  			tmp := append([]CStmt{}, stmts[:i]...)
  1257  			tmp = append(tmp, arr...)
  1258  			tmp = append(tmp, stmts[i+1:]...)
  1259  			stmts = tmp
  1260  			i += len(arr) - 1
  1261  		}
  1262  	}
  1263  	return stmts, gmod
  1264  }
  1265  
  1266  func (s *BlockStmt) EachStmt(fnc CStmtFunc) bool {
  1267  	var mod bool
  1268  	s.Stmts, mod = eachStmtIn(s.Stmts, fnc)
  1269  	return mod
  1270  }
  1271  
  1272  func (s *BlockStmt) In(ft *types.FuncType) *BlockStmt {
  1273  	if ft.Return() != nil {
  1274  		s.g.setReturnType(ft.Return(), s.Stmts)
  1275  	}
  1276  	stmts := s.Stmts
  1277  	for optimizeStatements {
  1278  		var mod bool
  1279  		stmts, mod = s.g.moveLastReturnToLastIf(stmts)
  1280  		if mod {
  1281  			continue
  1282  		}
  1283  		stmts, mod = s.g.inlineElseInLastReturn(stmts)
  1284  		if mod {
  1285  			continue
  1286  		}
  1287  		stmts, mod = s.g.invertLastIf(stmts)
  1288  		if mod {
  1289  			continue
  1290  		}
  1291  		stmts, mod = s.g.inlineSmallGotos(stmts)
  1292  		if mod {
  1293  			continue
  1294  		}
  1295  		break
  1296  	}
  1297  	s.Stmts = stmts
  1298  	return s
  1299  }
  1300  
  1301  func asStmts(arr []CStmt) []GoStmt {
  1302  	var out []GoStmt
  1303  	for _, s := range arr {
  1304  		out = append(out, s.AsStmt()...)
  1305  	}
  1306  	return out
  1307  }
  1308  
  1309  func (s *BlockStmt) GoBlockStmt() *ast.BlockStmt {
  1310  	if s == nil {
  1311  		return nil
  1312  	}
  1313  	return block(asStmts(s.Stmts)...)
  1314  }
  1315  
  1316  func (s *BlockStmt) AsStmt() []GoStmt {
  1317  	return []GoStmt{s.GoBlockStmt()}
  1318  }
  1319  
  1320  func (s *BlockStmt) Uses() []types.Usage {
  1321  	var list []types.Usage
  1322  	for _, s := range s.Stmts {
  1323  		list = append(list, s.Uses()...)
  1324  	}
  1325  	return list
  1326  }
  1327  
  1328  func (g *translator) NewCDeclStmt1(decl CDecl) CStmt {
  1329  	return &CDeclStmt{Decl: decl}
  1330  }
  1331  
  1332  func (g *translator) NewCDeclStmt(decl CDecl) []CStmt {
  1333  	switch d := decl.(type) {
  1334  	case *CVarDecl:
  1335  		for i, v := range d.Inits {
  1336  			switch v.(type) {
  1337  			case *CTernaryExpr:
  1338  				var stmts []CStmt
  1339  				if pre := d.Inits[:i:i]; len(pre) > 0 {
  1340  					dc := *d
  1341  					dc.Inits = pre
  1342  					dc.Names = dc.Names[:i:i]
  1343  					stmts = append(stmts, g.NewCDeclStmt(&dc)...)
  1344  				}
  1345  				dt := *d
  1346  				dt.Inits = nil
  1347  				dt.Names = []*types.Ident{d.Names[i]}
  1348  				stmts = append(stmts, g.NewCDeclStmt(&dt)...)
  1349  				stmts = append(stmts, g.NewCAssignStmt(IdentExpr{d.Names[i]}, "", v)...)
  1350  				if post := d.Inits[i+1:]; len(post) > 0 {
  1351  					dc := *d
  1352  					dc.Inits = post
  1353  					dc.Names = dc.Names[i+1:]
  1354  					stmts = append(stmts, g.NewCDeclStmt(&dc)...)
  1355  				}
  1356  				return stmts
  1357  			}
  1358  		}
  1359  	}
  1360  	return []CStmt{&CDeclStmt{Decl: decl}}
  1361  }
  1362  
  1363  type CDeclStmt struct {
  1364  	Decl CDecl
  1365  }
  1366  
  1367  func (s *CDeclStmt) Visit(v Visitor) {
  1368  	v(s.Decl)
  1369  }
  1370  
  1371  func (s *CDeclStmt) AsStmt() []GoStmt {
  1372  	var out []GoStmt
  1373  	for _, d := range s.Decl.AsDecl() {
  1374  		out = append(out, &ast.DeclStmt{Decl: d})
  1375  	}
  1376  	return out
  1377  }
  1378  
  1379  func (s *CDeclStmt) Uses() []types.Usage {
  1380  	return s.Decl.Uses()
  1381  }
  1382  
  1383  func (g *translator) NewCIncStmt(x Expr, decr bool) *CIncrStmt {
  1384  	return &CIncrStmt{
  1385  		g:    g,
  1386  		Expr: x,
  1387  		Decr: decr,
  1388  	}
  1389  }
  1390  
  1391  type CIncrStmt struct {
  1392  	g    *translator
  1393  	Expr Expr
  1394  	Decr bool
  1395  }
  1396  
  1397  func (s *CIncrStmt) Visit(v Visitor) {
  1398  	v(s.Expr)
  1399  }
  1400  
  1401  func (s *CIncrStmt) AsStmt() []GoStmt {
  1402  	if s.Expr.CType(nil).Kind().IsPtr() {
  1403  		var arg Expr
  1404  		if s.Decr {
  1405  			arg = cIntLit(-1)
  1406  		} else {
  1407  			arg = cIntLit(+1)
  1408  		}
  1409  		x := cPtrOffset(s.g.ToPointer(s.Expr), arg)
  1410  		return asStmts(s.g.NewCAssignStmt(s.Expr, "", x))
  1411  	}
  1412  	var tok token.Token
  1413  	if s.Decr {
  1414  		tok = token.DEC
  1415  	} else {
  1416  		tok = token.INC
  1417  	}
  1418  	return []GoStmt{&ast.IncDecStmt{
  1419  		X:   s.Expr.AsExpr(),
  1420  		Tok: tok,
  1421  	}}
  1422  }
  1423  
  1424  func (s *CIncrStmt) Uses() []types.Usage {
  1425  	var list []types.Usage
  1426  	list = append(list, types.UseRead(s.Expr)...)
  1427  	list = append(list, types.UseWrite(s.Expr)...)
  1428  	return list
  1429  }
  1430  
  1431  func (g *translator) NewCAssignStmtP(x Expr, op BinaryOp, y Expr) *CAssignStmt {
  1432  	x = cUnwrap(x)
  1433  	y = cUnwrap(y)
  1434  	r := g.cCast(x.CType(nil), y)
  1435  	return &CAssignStmt{
  1436  		g:     g,
  1437  		Left:  x,
  1438  		Op:    op,
  1439  		Right: r,
  1440  	}
  1441  }
  1442  
  1443  func (g *translator) NewCAssignStmt(x Expr, op BinaryOp, y Expr) []CStmt {
  1444  	x = cUnwrap(x)
  1445  	y = cUnwrap(y)
  1446  	if !x.HasSideEffects() {
  1447  		switch y := unwrapCasts(y).(type) {
  1448  		case *CUnaryExpr:
  1449  			switch z := unwrapCasts(y.Expr).(type) {
  1450  			case *CTernaryExpr:
  1451  				// v = -(x ? a : b) -> if x { v = -a } else { v = -b }
  1452  				return []CStmt{
  1453  					g.NewCIfStmt(z.Cond,
  1454  						g.NewCAssignStmt(x, op, g.NewCUnaryExpr(y.Op, z.Then)),
  1455  						g.toElseStmt(g.NewCAssignStmt(x, op, g.NewCUnaryExpr(y.Op, z.Else))...),
  1456  					),
  1457  				}
  1458  			}
  1459  		case *CTernaryExpr:
  1460  			// v = (x ? a : b) -> if x { v = a } else { v = b }
  1461  			return []CStmt{
  1462  				g.NewCIfStmt(y.Cond,
  1463  					g.NewCAssignStmt(x, op, y.Then),
  1464  					g.toElseStmt(g.NewCAssignStmt(x, op, y.Else)...),
  1465  				),
  1466  			}
  1467  		}
  1468  	}
  1469  	if x.CType(nil).Kind().IsPtr() {
  1470  		switch op {
  1471  		case BinOpAdd:
  1472  			if e, ok := y.(*IntToPtr); ok {
  1473  				y = e.X
  1474  			}
  1475  			op = ""
  1476  			y = cPtrOffset(g.ToPointer(x), y)
  1477  		case BinOpSub:
  1478  			if e, ok := y.(*IntToPtr); ok {
  1479  				y = e.X
  1480  			}
  1481  			op = ""
  1482  			y = g.NewCUnaryExpr(UnaryMinus, y)
  1483  			y = cPtrOffset(g.ToPointer(x), y)
  1484  		}
  1485  	}
  1486  	return []CStmt{&CAssignStmt{
  1487  		g:     g,
  1488  		Left:  x,
  1489  		Op:    op,
  1490  		Right: g.cCast(x.CType(nil), y),
  1491  	}}
  1492  }
  1493  
  1494  type CAssignStmt struct {
  1495  	g     *translator
  1496  	Left  Expr
  1497  	Op    BinaryOp
  1498  	Right Expr
  1499  }
  1500  
  1501  func (s *CAssignStmt) Visit(v Visitor) {
  1502  	v(s.Left)
  1503  	v(s.Right)
  1504  }
  1505  
  1506  func (s *CAssignStmt) AsStmt() []GoStmt {
  1507  	x := s.Left.AsExpr()
  1508  	y := s.Right.AsExpr()
  1509  	return []GoStmt{
  1510  		assignTok(x, s.Op.GoAssignToken(), y),
  1511  	}
  1512  }
  1513  
  1514  func (s *CAssignStmt) Uses() []types.Usage {
  1515  	var list []types.Usage
  1516  	list = append(list, types.UseWrite(s.Left)...)
  1517  	list = append(list, types.UseRead(s.Right)...)
  1518  	return list
  1519  }
  1520  
  1521  type UnusedVar struct {
  1522  	Name *types.Ident
  1523  }
  1524  
  1525  func (s *UnusedVar) Visit(v Visitor) {
  1526  }
  1527  
  1528  func (s *UnusedVar) AsStmt() []GoStmt {
  1529  	return []GoStmt{
  1530  		assign(ident("_"), s.Name.GoIdent()),
  1531  	}
  1532  }
  1533  
  1534  func (s *UnusedVar) Uses() []types.Usage {
  1535  	return []types.Usage{{Ident: s.Name, Access: types.AccessRead}}
  1536  }
  1537  
  1538  func stmtCost(stmts ...CStmt) int {
  1539  	c := 0
  1540  	for _, s := range stmts {
  1541  		if s == nil {
  1542  			continue
  1543  		}
  1544  		switch s := s.(type) {
  1545  		case *CForStmt:
  1546  			c += 3 + stmtCost(&s.Body)
  1547  		case *CSwitchStmt:
  1548  			c++
  1549  			for _, s2 := range s.Cases {
  1550  				c += stmtCost(s2.Stmts...)
  1551  			}
  1552  		case *CIfStmt:
  1553  			c += 1 + stmtCost(s.Then) + stmtCost(s.Else)
  1554  		case *CCaseStmt:
  1555  			c += 1 + stmtCost(s.Stmts...)
  1556  		case *BlockStmt:
  1557  			c += stmtCost(s.Stmts...)
  1558  		case *CDeclStmt:
  1559  			c += 2
  1560  		default:
  1561  			c++
  1562  		}
  1563  	}
  1564  	return c
  1565  }
  1566  
  1567  func (g *translator) hasReturns(stmt ...CStmt) bool {
  1568  	for _, st := range stmt {
  1569  		if g.isReturnOrExit(st) {
  1570  			return true
  1571  		}
  1572  		switch st := st.(type) {
  1573  		case *BlockStmt:
  1574  			if g.hasReturns(st.Stmts...) {
  1575  				return true
  1576  			}
  1577  		case *CIfStmt:
  1578  			if g.hasReturns(st.Then) {
  1579  				return true
  1580  			}
  1581  			if st.Else != nil {
  1582  				if g.hasReturns(st.Else) {
  1583  					return true
  1584  				}
  1585  			}
  1586  		case *CForStmt:
  1587  			if g.hasReturns(&st.Body) {
  1588  				return true
  1589  			}
  1590  		case *CSwitchStmt:
  1591  			for _, c := range st.Cases {
  1592  				if g.hasReturns(c.Stmts...) {
  1593  					return true
  1594  				}
  1595  			}
  1596  		}
  1597  	}
  1598  	return false
  1599  }
  1600  
  1601  func (g *translator) hasBreaks(stmt ...CStmt) bool {
  1602  	for _, st := range stmt {
  1603  		switch st := st.(type) {
  1604  		case *CBreakStmt:
  1605  			return true
  1606  		case *CGotoStmt:
  1607  			// TODO: check if it causes exit from the loop
  1608  		case *BlockStmt:
  1609  			if g.hasBreaks(st.Stmts...) {
  1610  				return true
  1611  			}
  1612  		case *CIfStmt:
  1613  			if g.hasBreaks(st.Then) {
  1614  				return true
  1615  			}
  1616  			if st.Else != nil {
  1617  				if g.hasBreaks(st.Else) {
  1618  					return true
  1619  				}
  1620  			}
  1621  		}
  1622  	}
  1623  	return false
  1624  }
  1625  
  1626  func (g *translator) hasBreaksOrReturns(stmt ...CStmt) bool {
  1627  	for _, st := range stmt {
  1628  		if g.hasReturns(st) {
  1629  			return true
  1630  		}
  1631  		switch st := st.(type) {
  1632  		case *CBreakStmt:
  1633  			return true
  1634  		case *CGotoStmt:
  1635  			// TODO: check if it causes exit from the loop
  1636  		case *BlockStmt:
  1637  			if g.hasBreaksOrReturns(st.Stmts...) {
  1638  				return true
  1639  			}
  1640  		case *CIfStmt:
  1641  			if g.hasBreaksOrReturns(st.Then) {
  1642  				return true
  1643  			}
  1644  			if st.Else != nil {
  1645  				if g.hasBreaksOrReturns(st.Else) {
  1646  					return true
  1647  				}
  1648  			}
  1649  		case *CForStmt:
  1650  			// don't consider breaks since it's a new loop
  1651  			if g.hasReturns(&st.Body) {
  1652  				return true
  1653  			}
  1654  		case *CSwitchStmt:
  1655  			// don't consider breaks since they affect the switch
  1656  			for _, c := range st.Cases {
  1657  				if g.hasReturns(c.Stmts...) {
  1658  					return true
  1659  				}
  1660  			}
  1661  		}
  1662  	}
  1663  	return false
  1664  }