github.com/goplus/gox@v1.14.13-0.20240308130321-6ff7f61cfae8/stmt.go (about)

     1  /*
     2   Copyright 2021 The GoPlus Authors (goplus.org)
     3   Licensed under the Apache License, Version 2.0 (the "License");
     4   you may not use this file except in compliance with the License.
     5   You may obtain a copy of the License at
     6       http://www.apache.org/licenses/LICENSE-2.0
     7   Unless required by applicable law or agreed to in writing, software
     8   distributed under the License is distributed on an "AS IS" BASIS,
     9   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10   See the License for the specific language governing permissions and
    11   limitations under the License.
    12  */
    13  
    14  package gox
    15  
    16  import (
    17  	"go/ast"
    18  	"go/token"
    19  	"go/types"
    20  	"log"
    21  
    22  	"github.com/goplus/gox/internal"
    23  )
    24  
    25  type controlFlow interface {
    26  	Then(cb *CodeBuilder, src ...ast.Node)
    27  }
    28  
    29  // ----------------------------------------------------------------------------
    30  //
    31  // block
    32  //
    33  //	...
    34  //
    35  // end
    36  type blockStmt struct {
    37  	old codeBlockCtx
    38  }
    39  
    40  func (p *blockStmt) End(cb *CodeBuilder, src ast.Node) {
    41  	stmts, flows := cb.endBlockStmt(&p.old)
    42  	cb.current.flows |= flows
    43  	cb.emitStmt(&ast.BlockStmt{List: stmts})
    44  }
    45  
    46  // ----------------------------------------------------------------------------
    47  //
    48  // vblock
    49  //
    50  //	...
    51  //
    52  // end
    53  type vblockStmt struct {
    54  	old vblockCtx
    55  }
    56  
    57  func (p *vblockStmt) End(cb *CodeBuilder, src ast.Node) {
    58  	cb.endVBlockStmt(&p.old)
    59  }
    60  
    61  // ----------------------------------------------------------------------------
    62  //
    63  // if init; cond then
    64  //
    65  //	...
    66  //
    67  // else
    68  //
    69  //	...
    70  //
    71  // end
    72  type ifStmt struct {
    73  	init ast.Stmt
    74  	cond ast.Expr
    75  	body *ast.BlockStmt
    76  	old  codeBlockCtx
    77  	old2 codeBlockCtx
    78  }
    79  
    80  func (p *ifStmt) Then(cb *CodeBuilder, src ...ast.Node) {
    81  	cond := cb.stk.Pop()
    82  	if !types.AssignableTo(cond.Type, types.Typ[types.Bool]) {
    83  		cb.panicCodeError(getPos(src), "non-boolean condition in if statement")
    84  	}
    85  	p.cond = cond.Val
    86  	switch stmts := cb.clearBlockStmt(); len(stmts) {
    87  	case 0:
    88  		// nothing to do
    89  	case 1:
    90  		p.init = stmts[0]
    91  	default:
    92  		panic("if statement has too many init statements")
    93  	}
    94  	cb.startBlockStmt(p, src, "if body", &p.old2)
    95  }
    96  
    97  func (p *ifStmt) Else(cb *CodeBuilder, src ...ast.Node) {
    98  	if p.body != nil {
    99  		panic("else statement already exists")
   100  	}
   101  
   102  	stmts, flows := cb.endBlockStmt(&p.old2)
   103  	cb.current.flows |= flows
   104  
   105  	p.body = &ast.BlockStmt{List: stmts}
   106  	cb.startBlockStmt(p, src, "else body", &p.old2)
   107  }
   108  
   109  func (p *ifStmt) End(cb *CodeBuilder, src ast.Node) {
   110  	stmts, flows := cb.endBlockStmt(&p.old2)
   111  	cb.current.flows |= flows
   112  
   113  	var blockStmt = &ast.BlockStmt{List: stmts}
   114  	var el ast.Stmt
   115  	if p.body != nil { // if..else
   116  		el = blockStmt
   117  		if len(stmts) == 1 { // if..else if
   118  			if stmt, ok := stmts[0].(*ast.IfStmt); ok {
   119  				el = stmt
   120  			}
   121  		}
   122  	} else { // if without else
   123  		p.body = blockStmt
   124  	}
   125  	cb.endBlockStmt(&p.old)
   126  	cb.emitStmt(&ast.IfStmt{Init: p.init, Cond: p.cond, Body: p.body, Else: el})
   127  }
   128  
   129  // ----------------------------------------------------------------------------
   130  //
   131  // switch init; tag then
   132  //
   133  //	case expr1, expr2, ..., exprN then
   134  //	  ...
   135  //	  end
   136  //
   137  //	case expr1, expr2, ..., exprM then
   138  //	  ...
   139  //	  end
   140  //
   141  //	defaultThen
   142  //	  ...
   143  //	  end
   144  //
   145  // end
   146  type switchStmt struct {
   147  	init ast.Stmt
   148  	tag  *internal.Elem
   149  	old  codeBlockCtx
   150  }
   151  
   152  func (p *switchStmt) Then(cb *CodeBuilder, src ...ast.Node) {
   153  	if cb.stk.Len() == cb.current.base {
   154  		panic("use None() for empty switch tag")
   155  	}
   156  	p.tag = cb.stk.Pop()
   157  	switch stmts := cb.clearBlockStmt(); len(stmts) {
   158  	case 0:
   159  		// nothing to do
   160  	case 1:
   161  		p.init = stmts[0]
   162  	default:
   163  		panic("switch statement has too many init statements")
   164  	}
   165  }
   166  
   167  func (p *switchStmt) Case(cb *CodeBuilder, src ...ast.Node) {
   168  	stmt := &caseStmt{tag: p.tag}
   169  	cb.startBlockStmt(stmt, src, "case statement", &stmt.old)
   170  }
   171  
   172  func (p *switchStmt) End(cb *CodeBuilder, src ast.Node) {
   173  	if p.tag == nil {
   174  		return
   175  	}
   176  	stmts, flows := cb.endBlockStmt(&p.old)
   177  	cb.current.flows |= (flows &^ flowFlagBreak)
   178  
   179  	body := &ast.BlockStmt{List: stmts}
   180  	cb.emitStmt(&ast.SwitchStmt{Init: p.init, Tag: checkParenExpr(p.tag.Val), Body: body})
   181  }
   182  
   183  type caseStmt struct {
   184  	tag  *internal.Elem
   185  	list []ast.Expr
   186  	old  codeBlockCtx
   187  }
   188  
   189  func (p *caseStmt) Then(cb *CodeBuilder, src ...ast.Node) {
   190  	n := cb.stk.Len() - cb.current.base
   191  	if n > 0 {
   192  		p.list = make([]ast.Expr, n)
   193  		for i, arg := range cb.stk.GetArgs(n) {
   194  			if p.tag.Val != nil { // switch tag {...}
   195  				if !ComparableTo(cb.pkg, arg, p.tag) {
   196  					src, pos := cb.loadExpr(arg.Src)
   197  					cb.panicCodeErrorf(
   198  						pos, "cannot use %s (type %v) as type %v", src, arg.Type, types.Default(p.tag.Type))
   199  				}
   200  			} else { // switch {...}
   201  				if !types.AssignableTo(arg.Type, types.Typ[types.Bool]) && arg.Type != TyEmptyInterface {
   202  					src, pos := cb.loadExpr(arg.Src)
   203  					cb.panicCodeErrorf(pos, "cannot use %s (type %v) as type bool", src, arg.Type)
   204  				}
   205  			}
   206  			p.list[i] = arg.Val
   207  		}
   208  		cb.stk.PopN(n)
   209  	}
   210  }
   211  
   212  func (p *caseStmt) Fallthrough(cb *CodeBuilder) {
   213  	cb.emitStmt(&ast.BranchStmt{Tok: token.FALLTHROUGH})
   214  }
   215  
   216  func (p *caseStmt) End(cb *CodeBuilder, src ast.Node) {
   217  	body, flows := cb.endBlockStmt(&p.old)
   218  	cb.current.flows |= flows
   219  	cb.emitStmt(&ast.CaseClause{List: p.list, Body: body})
   220  }
   221  
   222  // ----------------------------------------------------------------------------
   223  //
   224  // select
   225  // commCase commStmt1 then
   226  //
   227  //	...
   228  //	end
   229  //
   230  // commCase commStmt2 then
   231  //
   232  //	...
   233  //	end
   234  //
   235  // commDefaultThen
   236  //
   237  //	...
   238  //	end
   239  //
   240  // end
   241  type selectStmt struct {
   242  	old codeBlockCtx
   243  }
   244  
   245  func (p *selectStmt) CommCase(cb *CodeBuilder, src ...ast.Node) {
   246  	stmt := &commCase{}
   247  	cb.startBlockStmt(stmt, src, "comm case statement", &stmt.old)
   248  }
   249  
   250  func (p *selectStmt) End(cb *CodeBuilder, src ast.Node) {
   251  	stmts, flows := cb.endBlockStmt(&p.old)
   252  	cb.current.flows |= (flows &^ flowFlagBreak)
   253  	cb.emitStmt(&ast.SelectStmt{Body: &ast.BlockStmt{List: stmts}})
   254  }
   255  
   256  type commCase struct {
   257  	old  codeBlockCtx
   258  	comm ast.Stmt
   259  }
   260  
   261  func (p *commCase) Then(cb *CodeBuilder, src ...ast.Node) {
   262  	switch len(cb.current.stmts) {
   263  	case 1:
   264  		p.comm = cb.popStmt()
   265  	case 0:
   266  	default:
   267  		panic("multi commStmt in comm clause?")
   268  	}
   269  }
   270  
   271  func (p *commCase) End(cb *CodeBuilder, src ast.Node) {
   272  	body, flows := cb.endBlockStmt(&p.old)
   273  	cb.current.flows |= flows
   274  	cb.emitStmt(&ast.CommClause{Comm: p.comm, Body: body})
   275  }
   276  
   277  // ----------------------------------------------------------------------------
   278  //
   279  // typeSwitch(name) init; expr typeAssertThen
   280  // typeCase type1, type2, ... typeN then
   281  //
   282  //	...
   283  //	end
   284  //
   285  // typeCase type1, type2, ... typeM then
   286  //
   287  //	...
   288  //	end
   289  //
   290  // typeDefaultThen
   291  //
   292  //	...
   293  //	end
   294  //
   295  // end
   296  type typeSwitchStmt struct {
   297  	init  ast.Stmt
   298  	name  string
   299  	x     ast.Expr
   300  	xSrc  ast.Node
   301  	xType *types.Interface
   302  	old   codeBlockCtx
   303  }
   304  
   305  func (p *typeSwitchStmt) TypeAssertThen(cb *CodeBuilder) {
   306  	switch stmts := cb.clearBlockStmt(); len(stmts) {
   307  	case 0:
   308  		// nothing to do
   309  	case 1:
   310  		p.init = stmts[0]
   311  	default:
   312  		panic("TODO: type switch statement has too many init statements")
   313  	}
   314  	x := cb.stk.Pop()
   315  	xType, ok := cb.checkInterface(x.Type)
   316  	if !ok {
   317  		panic("TODO: can't type assert on non interface expr")
   318  	}
   319  	p.x, p.xSrc, p.xType = x.Val, x.Src, xType
   320  }
   321  
   322  func (p *typeSwitchStmt) TypeCase(cb *CodeBuilder, src ...ast.Node) {
   323  	stmt := &typeCaseStmt{pss: p}
   324  	cb.startBlockStmt(stmt, src, "type case statement", &stmt.old)
   325  }
   326  
   327  func (p *typeSwitchStmt) End(cb *CodeBuilder, src ast.Node) {
   328  	stmts, flows := cb.endBlockStmt(&p.old)
   329  	cb.current.flows |= (flows &^ flowFlagBreak)
   330  
   331  	body := &ast.BlockStmt{List: stmts}
   332  	var assign ast.Stmt
   333  	x := &ast.TypeAssertExpr{X: p.x}
   334  	if p.name != "" {
   335  		assign = &ast.AssignStmt{
   336  			Tok: token.DEFINE,
   337  			Lhs: []ast.Expr{ident(p.name)},
   338  			Rhs: []ast.Expr{x},
   339  		}
   340  	} else {
   341  		assign = &ast.ExprStmt{X: x}
   342  	}
   343  	cb.emitStmt(&ast.TypeSwitchStmt{Init: p.init, Assign: assign, Body: body})
   344  }
   345  
   346  type typeCaseStmt struct {
   347  	pss  *typeSwitchStmt
   348  	list []ast.Expr
   349  	old  codeBlockCtx
   350  }
   351  
   352  func (p *typeCaseStmt) Then(cb *CodeBuilder, src ...ast.Node) {
   353  	var typ types.Type
   354  	pss := p.pss
   355  	n := cb.stk.Len() - cb.current.base
   356  	if n > 0 {
   357  		p.list = make([]ast.Expr, n)
   358  		args := cb.stk.GetArgs(n)
   359  		for i, arg := range args {
   360  			typ = arg.Type
   361  			if tt, ok := typ.(*TypeType); ok {
   362  				typ = tt.Type()
   363  				if missing := cb.missingMethod(typ, pss.xType); missing != "" {
   364  					xsrc, _ := cb.loadExpr(pss.xSrc)
   365  					pos := getSrcPos(arg.Src)
   366  					cb.panicCodeErrorf(
   367  						pos, "impossible type switch case: %s (type %v) cannot have dynamic type %v (missing %s method)",
   368  						xsrc, pss.xType, typ, missing)
   369  				}
   370  			} else if typ != types.Typ[types.UntypedNil] {
   371  				src, pos := cb.loadExpr(arg.Src)
   372  				cb.panicCodeErrorf(pos, "%s (type %v) is not a type", src, typ)
   373  			}
   374  			p.list[i] = arg.Val
   375  		}
   376  		cb.stk.PopN(n)
   377  	}
   378  	if pss.name != "" {
   379  		if n != 1 { // default, or case with multi expr
   380  			typ = pss.xType
   381  		}
   382  		name := types.NewParam(token.NoPos, cb.pkg.Types, pss.name, typ)
   383  		cb.current.scope.Insert(name)
   384  	}
   385  }
   386  
   387  func (p *typeCaseStmt) End(cb *CodeBuilder, src ast.Node) {
   388  	body, flows := cb.endBlockStmt(&p.old)
   389  	cb.current.flows |= flows
   390  	cb.emitStmt(&ast.CaseClause{List: p.list, Body: body})
   391  }
   392  
   393  // ----------------------------------------------------------------------------
   394  
   395  type loopBodyHandler struct {
   396  	handle func(body *ast.BlockStmt, kind int)
   397  }
   398  
   399  func (p *loopBodyHandler) SetBodyHandler(handle func(body *ast.BlockStmt, kind int)) {
   400  	p.handle = handle
   401  }
   402  
   403  func (p *loopBodyHandler) handleFor(body *ast.BlockStmt, kind int) *ast.BlockStmt {
   404  	if p.handle != nil {
   405  		p.handle(body, kind)
   406  	}
   407  	return body
   408  }
   409  
   410  func InsertStmtFront(body *ast.BlockStmt, stmt ast.Stmt) {
   411  	list := append(body.List, nil)
   412  	copy(list[1:], list)
   413  	list[0] = stmt
   414  	body.List = list
   415  }
   416  
   417  // ----------------------------------------------------------------------------
   418  //
   419  // for init; cond then
   420  //
   421  //	body
   422  //	post
   423  //
   424  // end
   425  type forStmt struct {
   426  	init ast.Stmt
   427  	cond ast.Expr
   428  	body *ast.BlockStmt
   429  	old  codeBlockCtx
   430  	old2 codeBlockCtx
   431  	loopBodyHandler
   432  }
   433  
   434  func (p *forStmt) Then(cb *CodeBuilder, src ...ast.Node) {
   435  	cond := cb.stk.Pop()
   436  	if cond.Val != nil {
   437  		if !types.AssignableTo(cond.Type, types.Typ[types.Bool]) {
   438  			panic("TODO: for statement condition is not a boolean expr")
   439  		}
   440  		p.cond = cond.Val
   441  	}
   442  	switch stmts := cb.clearBlockStmt(); len(stmts) {
   443  	case 0:
   444  		// nothing to do
   445  	case 1:
   446  		p.init = stmts[0]
   447  	default:
   448  		panic("TODO: for condition has too many init statements")
   449  	}
   450  	cb.startBlockStmt(p, src, "for body", &p.old2)
   451  }
   452  
   453  func (p *forStmt) Post(cb *CodeBuilder) {
   454  	stmts, flows := cb.endBlockStmt(&p.old2)
   455  	cb.current.flows |= (flows &^ (flowFlagBreak | flowFlagContinue))
   456  	p.body = &ast.BlockStmt{List: stmts}
   457  }
   458  
   459  func (p *forStmt) End(cb *CodeBuilder, src ast.Node) {
   460  	var post ast.Stmt
   461  	if p.body != nil { // has post stmt
   462  		stmts, _ := cb.endBlockStmt(&p.old)
   463  		if len(stmts) != 1 {
   464  			panic("TODO: too many post statements")
   465  		}
   466  		post = stmts[0]
   467  	} else { // no post
   468  		stmts, flows := cb.endBlockStmt(&p.old2)
   469  		cb.current.flows |= (flows &^ (flowFlagBreak | flowFlagContinue))
   470  		p.body = &ast.BlockStmt{List: stmts}
   471  		cb.endBlockStmt(&p.old)
   472  	}
   473  	cb.emitStmt(&ast.ForStmt{
   474  		Init: p.init, Cond: p.cond, Post: post, Body: p.handleFor(p.body, 0),
   475  	})
   476  }
   477  
   478  // ----------------------------------------------------------------------------
   479  //
   480  // forRange names... exprX rangeAssignThen
   481  //
   482  //	...
   483  //
   484  // end
   485  //
   486  // forRange exprKey exprVal exprX rangeAssignThen
   487  //
   488  //	...
   489  //
   490  // end
   491  type forRangeStmt struct {
   492  	names []string
   493  	stmt  *ast.RangeStmt
   494  	x     *internal.Elem
   495  	old   codeBlockCtx
   496  	kvt   []types.Type
   497  	udt   int // 0: non-udt, 2: (elem,ok), 3: (key,elem,ok)
   498  	loopBodyHandler
   499  }
   500  
   501  func (p *forRangeStmt) RangeAssignThen(cb *CodeBuilder, pos token.Pos) {
   502  	if names := p.names; names != nil { // for k, v := range XXX {
   503  		var val ast.Expr
   504  		switch len(names) {
   505  		case 1:
   506  		case 2:
   507  			val = ident(names[1])
   508  		default:
   509  			cb.panicCodeError(pos, "too many variables in range")
   510  		}
   511  		x := cb.stk.Pop()
   512  		pkg, scope := cb.pkg, cb.current.scope
   513  		typs := p.getKeyValTypes(cb, x.Type)
   514  		if typs == nil {
   515  			src, _ := cb.loadExpr(x.Src)
   516  			cb.panicCodeErrorf(pos, "cannot range over %v (type %v)", src, x.Type)
   517  		}
   518  		if typs[1] == nil { // chan
   519  			if names[0] == "_" && len(names) > 1 {
   520  				names[0], val = names[1], nil
   521  				names = names[:1]
   522  			}
   523  		}
   524  		for i, name := range names {
   525  			if name == "_" {
   526  				continue
   527  			}
   528  			if scope.Insert(types.NewVar(token.NoPos, pkg.Types, name, typs[i])) != nil {
   529  				log.Panicln("TODO: variable already defined -", name)
   530  			}
   531  		}
   532  		if p.udt != 0 {
   533  			p.x = x
   534  		}
   535  		p.stmt = &ast.RangeStmt{
   536  			Key:   ident(names[0]),
   537  			Value: val,
   538  			Tok:   token.DEFINE,
   539  			X:     x.Val,
   540  		}
   541  	} else { // for k, v = range XXX {
   542  		var key, val, x internal.Elem
   543  		n := cb.stk.Len() - cb.current.base
   544  		args := cb.stk.GetArgs(n)
   545  		switch n {
   546  		case 1:
   547  			x = *args[0]
   548  		case 2:
   549  			key, x = *args[0], *args[1]
   550  		case 3:
   551  			key, val, x = *args[0], *args[1], *args[2]
   552  		default:
   553  			cb.panicCodeError(pos, "too many variables in range")
   554  		}
   555  		cb.stk.PopN(n)
   556  		typs := p.getKeyValTypes(cb, x.Type)
   557  		if typs == nil {
   558  			src, _ := cb.loadExpr(x.Src)
   559  			cb.panicCodeErrorf(pos, "cannot range over %v (type %v)", src, x.Type)
   560  		}
   561  		if p.udt != 0 {
   562  			p.x = &x
   563  		}
   564  		p.stmt = &ast.RangeStmt{
   565  			Key:   key.Val,
   566  			Value: val.Val,
   567  			X:     x.Val,
   568  		}
   569  		if n > 1 {
   570  			p.stmt.Tok = token.ASSIGN
   571  			checkAssign(cb.pkg, &key, typs[0], "range")
   572  			if val.Val != nil {
   573  				checkAssign(cb.pkg, &val, typs[1], "range")
   574  			}
   575  		}
   576  	}
   577  	p.stmt.For = pos
   578  }
   579  
   580  func (p *forRangeStmt) getKeyValTypes(cb *CodeBuilder, typ types.Type) []types.Type {
   581  retry:
   582  	switch t := typ.(type) {
   583  	case *types.Slice:
   584  		return []types.Type{types.Typ[types.Int], t.Elem()}
   585  	case *types.Map:
   586  		return []types.Type{t.Key(), t.Elem()}
   587  	case *types.Array:
   588  		return []types.Type{types.Typ[types.Int], t.Elem()}
   589  	case *types.Pointer:
   590  		switch e := t.Elem().(type) {
   591  		case *types.Array:
   592  			return []types.Type{types.Typ[types.Int], e.Elem()}
   593  		case *types.Named:
   594  			if kv, ok := p.checkUdt(cb, e); ok {
   595  				return kv
   596  			}
   597  		}
   598  	case *types.Chan:
   599  		return []types.Type{t.Elem(), nil}
   600  	case *types.Basic:
   601  		if (t.Info() & types.IsString) != 0 {
   602  			return []types.Type{types.Typ[types.Int], types.Typ[types.Rune]}
   603  		}
   604  	case *types.Named:
   605  		if kv, ok := p.checkUdt(cb, t); ok {
   606  			return kv
   607  		}
   608  		typ = cb.getUnderlying(t)
   609  		goto retry
   610  	}
   611  	return nil
   612  }
   613  
   614  func (p *forRangeStmt) checkUdt(cb *CodeBuilder, o *types.Named) ([]types.Type, bool) {
   615  	if sig := findMethodType(cb, o, nameGopEnum); sig != nil {
   616  		enumRet := sig.Results()
   617  		params := sig.Params()
   618  		switch params.Len() {
   619  		case 0:
   620  			// iter := obj.Gop_Enum()
   621  			// key, val, ok := iter.Next()
   622  		case 1:
   623  			// obj.Gop_Enum(func(key K, val V) { ... })
   624  			if enumRet.Len() != 0 {
   625  				return nil, false
   626  			}
   627  			typ := params.At(0).Type()
   628  			if t, ok := typ.(*types.Signature); ok && t.Results().Len() == 0 {
   629  				kv := t.Params()
   630  				n := kv.Len()
   631  				if n > 0 {
   632  					p.kvt = []types.Type{kv.At(0).Type(), nil}
   633  					if n > 1 {
   634  						n = 2
   635  						p.kvt[1] = kv.At(1).Type()
   636  					}
   637  					p.udt = -n
   638  					return p.kvt, true
   639  				}
   640  			}
   641  			fallthrough
   642  		default:
   643  			return nil, false
   644  		}
   645  		if enumRet.Len() == 1 {
   646  			typ := enumRet.At(0).Type()
   647  			if t, ok := typ.(*types.Pointer); ok {
   648  				typ = t.Elem()
   649  			}
   650  			if it, ok := typ.(*types.Named); ok {
   651  				if next := findMethodType(cb, it, "Next"); next != nil {
   652  					ret := next.Results()
   653  					typs := make([]types.Type, 2)
   654  					n := ret.Len()
   655  					switch n {
   656  					case 2: // elem, ok
   657  						typs[0] = ret.At(0).Type()
   658  					case 3: // key, elem, ok
   659  						typs[0], typs[1] = ret.At(0).Type(), ret.At(1).Type()
   660  					default:
   661  						return nil, false
   662  					}
   663  					if ret.At(n-1).Type() == types.Typ[types.Bool] {
   664  						p.udt = n
   665  						return typs, true
   666  					}
   667  				}
   668  			}
   669  		}
   670  	}
   671  	return nil, false
   672  }
   673  
   674  func findMethodType(cb *CodeBuilder, o *types.Named, name string) mthdSignature {
   675  	for i, n := 0, o.NumMethods(); i < n; i++ {
   676  		method := o.Method(i)
   677  		if method.Name() == name {
   678  			return method.Type().(*types.Signature)
   679  		}
   680  	}
   681  	if bti := cb.getBuiltinTI(o); bti != nil {
   682  		return bti.lookupByName(name)
   683  	}
   684  	return nil
   685  }
   686  
   687  const (
   688  	cantUseFlows = "can't use return/continue/break/goto in for range of udt.Gop_Enum(callback)"
   689  )
   690  
   691  func (p *forRangeStmt) End(cb *CodeBuilder, src ast.Node) {
   692  	if p.stmt == nil {
   693  		return
   694  	}
   695  	stmts, flows := cb.endBlockStmt(&p.old)
   696  	cb.current.flows |= (flows &^ (flowFlagBreak | flowFlagContinue))
   697  	if n := p.udt; n == 0 {
   698  		p.stmt.Body = p.handleFor(&ast.BlockStmt{List: stmts}, 1)
   699  		cb.emitStmt(p.stmt)
   700  	} else if n > 0 {
   701  		cb.stk.Push(p.x)
   702  		cb.MemberVal(nameGopEnum).Call(0)
   703  		callEnum := cb.stk.Pop().Val
   704  		/*
   705  			for _gop_it := X.Gop_Enum();; {
   706  				var _gop_ok bool
   707  				k, v, _gop_ok = _gop_it.Next()
   708  				if !_gop_ok {
   709  					break
   710  				}
   711  				...
   712  			}
   713  		*/
   714  		lhs := make([]ast.Expr, n)
   715  		lhs[0] = p.stmt.Key
   716  		lhs[1] = p.stmt.Value
   717  		lhs[n-1] = identGopOk
   718  		if lhs[0] == nil { // bugfix: for range udt { ... }
   719  			lhs[0] = underscore
   720  			if p.stmt.Tok == token.ILLEGAL {
   721  				p.stmt.Tok = token.ASSIGN
   722  			}
   723  		} else {
   724  			// for _ = range udt { ... }
   725  			if ki, ok := lhs[0].(*ast.Ident); ok && ki.Name == "_" {
   726  				if n == 2 {
   727  					p.stmt.Tok = token.ASSIGN
   728  				} else {
   729  					// for _ , _ = range udt { ... }
   730  					if vi, ok := lhs[1].(*ast.Ident); ok && vi.Name == "_" {
   731  						p.stmt.Tok = token.ASSIGN
   732  					}
   733  				}
   734  			}
   735  		}
   736  		body := make([]ast.Stmt, len(stmts)+3)
   737  		body[0] = stmtGopOkDecl
   738  		body[1] = &ast.AssignStmt{Lhs: lhs, Tok: p.stmt.Tok, Rhs: exprIterNext}
   739  		body[2] = stmtBreakIfNotGopOk
   740  		copy(body[3:], stmts)
   741  		stmt := &ast.ForStmt{
   742  			Init: &ast.AssignStmt{
   743  				Lhs: []ast.Expr{identGopIt},
   744  				Tok: token.DEFINE,
   745  				Rhs: []ast.Expr{callEnum},
   746  			},
   747  			Body: p.handleFor(&ast.BlockStmt{List: body}, 2),
   748  		}
   749  		cb.emitStmt(stmt)
   750  	} else {
   751  		/*
   752  			X.Gop_Enum(func(k K, v V) {
   753  				...
   754  			})
   755  		*/
   756  		if flows != 0 {
   757  			cb.panicCodeError(p.stmt.For, cantUseFlows)
   758  		}
   759  		n = -n
   760  		def := p.stmt.Tok == token.DEFINE
   761  		args := make([]*ast.Field, n)
   762  		if def {
   763  			args[0] = &ast.Field{
   764  				Names: []*ast.Ident{p.stmt.Key.(*ast.Ident)},
   765  				Type:  toType(cb.pkg, p.kvt[0]),
   766  			}
   767  			if n > 1 {
   768  				args[1] = &ast.Field{
   769  					Names: []*ast.Ident{p.stmt.Value.(*ast.Ident)},
   770  					Type:  toType(cb.pkg, p.kvt[1]),
   771  				}
   772  			}
   773  		} else {
   774  			panic("TODO: for range udt assign")
   775  		}
   776  		stmt := &ast.ExprStmt{
   777  			X: &ast.CallExpr{
   778  				Fun: &ast.SelectorExpr{X: p.stmt.X, Sel: identGopEnum},
   779  				Args: []ast.Expr{
   780  					&ast.FuncLit{
   781  						Type: &ast.FuncType{Params: &ast.FieldList{List: args}},
   782  						Body: p.handleFor(&ast.BlockStmt{List: stmts}, -1),
   783  					},
   784  				},
   785  			},
   786  		}
   787  		cb.emitStmt(stmt)
   788  	}
   789  }
   790  
   791  var (
   792  	nameGopEnum  = "Gop_Enum"
   793  	identGopOk   = ident("_gop_ok")
   794  	identGopIt   = ident("_gop_it")
   795  	identGopEnum = ident(nameGopEnum)
   796  )
   797  
   798  var (
   799  	stmtGopOkDecl = &ast.DeclStmt{
   800  		Decl: &ast.GenDecl{
   801  			Tok: token.VAR,
   802  			Specs: []ast.Spec{
   803  				&ast.ValueSpec{Names: []*ast.Ident{identGopOk}, Type: ident("bool")},
   804  			},
   805  		},
   806  	}
   807  	stmtBreakIfNotGopOk = &ast.IfStmt{
   808  		Cond: &ast.UnaryExpr{Op: token.NOT, X: identGopOk},
   809  		Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.BREAK}}},
   810  	}
   811  	exprIterNext = []ast.Expr{
   812  		&ast.CallExpr{
   813  			Fun: &ast.SelectorExpr{X: identGopIt, Sel: ident("Next")},
   814  		},
   815  	}
   816  )
   817  
   818  // ----------------------------------------------------------------------------