1/*
2 * gomacro - A Go interpreter with Lisp-like macros
3 *
4 * Copyright (C) 2017-2019 Massimiliano Ghilardi
5 *
6 *     This Source Code Form is subject to the terms of the Mozilla Public
7 *     License, v. 2.0. If a copy of the MPL was not distributed with this
8 *     file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 *
11 * statement.go
12 *
13 *  Created on Apr 01, 2017
14 *      Author Massimiliano Ghilardi
15 */
16
17package fast
18
19import (
20	"go/ast"
21	"go/token"
22	r "reflect"
23	"sort"
24
25	. "github.com/cosmos72/gomacro/base"
26	"github.com/cosmos72/gomacro/base/output"
27	"github.com/cosmos72/gomacro/gls"
28)
29
30func stmtNop(env *Env) (Stmt, *Env) {
31	ip := env.IP + 1
32	env.IP = ip
33	return env.Code[ip], env
34}
35
36func popEnv(env *Env) (Stmt, *Env) {
37	outer := env.Outer
38	outer.IP = env.IP + 1
39	env.FreeEnv()
40	return outer.Code[outer.IP], outer
41}
42
43func (c *Comp) Stmt(in ast.Stmt) {
44	var labels []string
45	// DebugSource // codelen := len(c.Code.List)
46	for {
47		if in != nil {
48			c.Pos = in.Pos()
49			if isBreakpoint(in) {
50				c.append(c.breakpoint())
51				break
52			}
53		}
54		switch node := in.(type) {
55		case nil:
56		case *ast.AssignStmt:
57			c.Assign(node)
58		case *ast.BlockStmt:
59			c.Block(node)
60		case *ast.BranchStmt:
61			c.Branch(node)
62		case *ast.CaseClause:
63			c.misplacedCase(node, node.List == nil)
64		case *ast.CommClause:
65			c.misplacedCase(node, node.Comm == nil)
66		case *ast.DeclStmt:
67			c.Decl(node.Decl)
68		case *ast.DeferStmt:
69			c.Defer(node)
70		case *ast.EmptyStmt:
71			// nothing to do
72		case *ast.ExprStmt:
73			expr := c.expr(node.X, nil)
74			if !expr.Const() {
75				c.Append(expr.AsStmt(c), in.Pos())
76			}
77		case *ast.ForStmt:
78			c.For(node, labels)
79		case *ast.GoStmt:
80			c.Go(node)
81		case *ast.IfStmt:
82			c.If(node)
83		case *ast.IncDecStmt:
84			c.IncDec(node)
85		case *ast.LabeledStmt:
86			label := node.Label.Name
87			labels = append(labels, label)
88			ip := c.Code.Len()
89			if c.Labels == nil {
90				c.Labels = map[string]*int{label: &ip}
91			} else if addr := c.Labels[label]; addr != nil {
92				*addr = ip
93			} else {
94				c.Labels[label] = &ip
95			}
96			in = node.Stmt
97			continue
98		case *ast.RangeStmt:
99			c.Range(node, labels)
100		case *ast.ReturnStmt:
101			c.Return(node)
102		case *ast.SelectStmt:
103			c.Select(node, labels)
104		case *ast.SendStmt:
105			c.Send(node)
106		case *ast.SwitchStmt:
107			c.Switch(node, labels)
108		case *ast.TypeSwitchStmt:
109			c.TypeSwitch(node, labels)
110		default:
111			c.Errorf("unimplemented statement: %v // %T", node, node)
112		}
113		break
114	}
115	// DebugSource // c.showStatementsSource(in, codelen)
116}
117
118/* DebugSource */
119func (c *Comp) showStatementsSource(in ast.Stmt, startIP int) {
120	n1, n2 := len(c.Code.List), len(c.Code.DebugPos)
121	if n1 != n2 {
122		c.Warnf("code mismatch: len(c.Code.List) = %d differs from len(c.Code.DebugPos) = %d",
123			n1, n2)
124	}
125	g := &c.Globals
126	g.Fprintf(g.Stdout, "source for statement: %v // %T\n", in, in)
127	for ip := startIP; ip < n2; ip++ {
128		c.showStatementSource(ip)
129	}
130}
131
132/* DebugSource */
133func (c *Comp) showStatementSource(ip int) {
134	code := c.Code
135	list := code.List
136	debugp := code.DebugPos
137	g := &c.Globals
138	if ip < len(debugp) && g.Fileset != nil {
139		p := debugp[ip]
140		source, pos := g.Fileset.Source(p)
141		if ip < len(list) {
142			g.Fprintf(g.Stdout, "IP = % 3d: statement %p at [% 3d] %s\n", ip, list[ip], p, pos)
143		} else {
144			g.Fprintf(g.Stdout, "IP = % 3d: unknown statement at [% 3d] %s\n", ip, p, pos)
145		}
146		if len(source) != 0 {
147			g.Fprintf(g.Stdout, "%s\n", source)
148			c.showCaret(source, pos.Column)
149		}
150	}
151}
152
153var spaces = []byte("                                                                      ")
154
155func (c *Comp) showCaret(source string, col int) {
156	col--
157	n := len(source)
158	if col >= 0 && col < n && n >= 3 {
159		out := c.Globals.Stdout
160		chunk := len(spaces)
161		for col >= chunk {
162			out.Write(spaces)
163			col -= chunk
164		}
165		out.Write(spaces[:col])
166		out.Write([]byte("^^^\n"))
167	}
168}
169
170// Block compiles a block statement, i.e. { ... }
171func (c *Comp) Block(block *ast.BlockStmt) {
172	if block == nil || len(block.List) == 0 {
173		return
174	}
175	c.List(block.List)
176}
177
178// List compiles a slice of statements
179func (c *Comp) List(list []ast.Stmt) {
180	if len(list) == 0 {
181		c.Errorf("List invoked on empty statement list")
182	}
183	var nbinds [2]int // # of binds in the block
184
185	c2, locals := c.pushEnvIfLocalBinds(&nbinds, list...)
186
187	for _, node := range list {
188		c2.Stmt(node)
189	}
190
191	c2.popEnvIfLocalBinds(locals, &nbinds, list...)
192
193	// c.Debugf("List compiled. inner *Comp = %#v", c2)
194}
195
196// Branch compiles a break, continue, fallthrough or goto statement
197func (c *Comp) Branch(node *ast.BranchStmt) {
198	switch node.Tok {
199	case token.BREAK:
200		c.Break(node)
201	case token.CONTINUE:
202		c.Continue(node)
203	case token.FALLTHROUGH:
204		c.misplacedFallthrough()
205	case token.GOTO:
206		c.Goto(node)
207	default:
208		c.Errorf("unimplemented branch statement: %v <%v>", node, r.TypeOf(node))
209	}
210}
211
212// Break compiles a "break" statement
213func (c *Comp) Break(node *ast.BranchStmt) {
214	label := ""
215	if node.Label != nil {
216		label = node.Label.Name
217	}
218	upn := 0
219	// do not cross function boundaries
220	for o := c; o != nil && o.Func == nil; o = o.Outer {
221		if o.Loop != nil && o.Loop.Break != nil {
222			if len(label) == 0 || o.Loop.HasLabel(label) {
223				// only keep a reference to the jump target, NOT TO THE WHOLE *Comp!
224				c.jumpOut(upn, o.Loop.Break)
225				return
226			}
227		}
228		upn += o.UpCost // count how many Env:s we must exit at runtime
229	}
230	if len(label) != 0 {
231		c.Errorf("break label not defined: %v", label)
232	} else {
233		c.Errorf("break outside for/switch")
234	}
235}
236
237// Continue compiles a "continue" statement
238func (c *Comp) Continue(node *ast.BranchStmt) {
239	label := ""
240	if node.Label != nil {
241		label = node.Label.Name
242	}
243	upn := 0
244	// do not cross function boundaries
245	for o := c; o != nil && o.Func == nil; o = o.Outer {
246		if o.Loop != nil && o.Loop.Continue != nil {
247			if len(label) == 0 || o.Loop.HasLabel(label) {
248				// only keep a reference to the jump target, NOT TO THE WHOLE *Comp!
249				c.jumpOut(upn, o.Loop.Continue)
250				return
251			}
252		}
253		upn += o.UpCost // count how many Env:s we must exit at runtime
254	}
255	if len(label) != 0 {
256		c.Errorf("continue label not defined: %v", label)
257	} else {
258		c.Errorf("continue outside for")
259	}
260}
261
262// Goto compiles a "goto" statement
263func (c *Comp) Goto(node *ast.BranchStmt) {
264	if node.Label == nil {
265		c.Errorf("goto without label: %v", node)
266	}
267	label := node.Label.Name
268	upn := 0
269	// do not cross function boundaries
270	for o := c; o != nil && o.Func == nil; o = o.Outer {
271		if ip := o.Labels[label]; ip != nil {
272			// only keep a reference to the jump target, NOT TO THE WHOLE *Comp!
273			c.jumpOut(upn, ip)
274			return
275		}
276		upn += o.UpCost // count how many Env:s we must exit at runtime
277	}
278	c.Errorf("goto label not found: %v", label)
279}
280
281// Defer compiles a "defer" statement
282func (c *Comp) Defer(node *ast.DeferStmt) {
283	call := c.prepareCall(node.Call, nil)
284	fun := call.Fun.AsX1()
285	argfuns := call.MakeArgfunsX1()
286	ellipsis := call.Ellipsis
287	c.Append(func(env *Env) (Stmt, *Env) {
288		// Go specs: arguments of a defer call are evaluated immediately.
289		// the call itself is executed when the function containing defer returns,
290		// either normally or with a panic
291		f := fun(env)
292		if f.CanSet() {
293			f = f.Convert(f.Type()) // make a copy
294		}
295		args := make([]r.Value, len(argfuns))
296		for i, argfun := range argfuns {
297			v := argfun(env)
298			if v.CanSet() {
299				v = v.Convert(v.Type()) // make a copy
300			}
301			args[i] = v
302		}
303		env.IP++
304		run := env.Run
305		if ellipsis {
306			run.InstallDefer = func() {
307				f.CallSlice(args)
308			}
309		} else {
310			run.InstallDefer = func() {
311				f.Call(args)
312			}
313		}
314		run.Signals.Sync = SigDefer
315		return run.Interrupt, env
316	}, node.Pos())
317	c.Code.WithDefers = true
318}
319
320// jumpOut compiles a break or continue statement
321// ip is a pointer because the jump target may not be known yet... it will be filled later
322func (c *Comp) jumpOut(upn int, ip *int) {
323	var stmt Stmt
324	switch upn {
325	case 0:
326		stmt = func(env *Env) (Stmt, *Env) {
327			ip := *ip
328			env.IP = ip
329			return env.Code[ip], env
330		}
331	case 1:
332		stmt = func(env *Env) (Stmt, *Env) {
333			env = env.Outer
334			ip := *ip
335			env.IP = ip
336			return env.Code[ip], env
337		}
338	case 2:
339		stmt = func(env *Env) (Stmt, *Env) {
340			env = env.Outer.Outer
341			ip := *ip
342			env.IP = ip
343			return env.Code[ip], env
344		}
345	default:
346		stmt = func(env *Env) (Stmt, *Env) {
347			env = env.Outer.Outer.Outer
348			for i := 3; i < upn; i++ {
349				env = env.Outer
350			}
351			ip := *ip
352			env.IP = ip
353			return env.Code[ip], env
354		}
355	}
356	c.append(stmt)
357}
358
359// For compiles a "for" statement
360func (c *Comp) For(node *ast.ForStmt, labels []string) {
361	initLocals := false
362	var initBinds [2]int
363
364	c, initLocals = c.pushEnvIfLocalBinds(&initBinds, node.Init)
365	if node.Init != nil {
366		c.Stmt(node.Init)
367	}
368	flag, fun, err := true, (func(*Env) bool)(nil), false // "for { }" without a condition means "for true { }"
369	if node.Cond != nil {
370		pred := c.Expr(node.Cond, nil)
371		flag, fun, err = pred.TryAsPred()
372		if err {
373			c.invalidPred(node.Cond, pred)
374			return
375		}
376	}
377	var jump struct{ Cond, Post, Break int }
378	sort.Strings(labels)
379	// we need a fresh Comp here... created above by c.pushEnvIfLocalBinds()
380	c.Loop = &LoopInfo{
381		Continue:   &jump.Post,
382		Break:      &jump.Break,
383		ThisLabels: labels,
384	}
385
386	// compile the condition, if not a constant
387	jump.Cond = c.Code.Len()
388	if fun != nil {
389		stmt := func(env *Env) (Stmt, *Env) {
390			var ip int
391			if fun(env) {
392				ip = env.IP + 1
393				// Debugf("for: condition = true, iterating. IntBinds = %v", env.IntBinds)
394			} else {
395				// Debugf("for: condition = false, exiting. IntBinds = %v", env.IntBinds)
396				ip = jump.Break
397			}
398			env.IP = ip
399			return env.Code[ip], env
400		}
401		c.Append(stmt, node.Cond.Pos())
402	}
403	// compile the body
404	c.Block(node.Body)
405	// compile the post
406	if node.Post == nil {
407		jump.Post = jump.Cond // no post statement. "continue" jumps to the condition
408	} else {
409		jump.Post = c.Code.Len()
410		if containLocalBinds(node.Post) {
411			c.Errorf("invalid for: cannot declare new variables in post statement: %v", node.Post)
412		}
413		c.Stmt(node.Post)
414	}
415	c.Append(func(env *Env) (Stmt, *Env) {
416		// jump back to the condition
417		// Debugf("for: body executed, jumping back to condition. IntBinds = %v", env.IntBinds)
418		// time.Sleep(time.Second / 10)
419		ip := jump.Cond
420		env.IP = ip
421		return env.Code[ip], env
422	}, node.End()-1)
423	if fun == nil && !flag {
424		// "for false { }" means that body, post and jump back to condition are never executed...
425		// still compiled above (to check for errors) but drop the generated code
426		c.Code.Truncate(jump.Cond)
427	}
428	jump.Break = c.Code.Len()
429
430	c = c.popEnvIfLocalBinds(initLocals, &initBinds, node.Init)
431}
432
433// Go compiles a "go" statement i.e. a goroutine
434func (c *Comp) Go(node *ast.GoStmt) {
435	// we must create a new ThreadGlobals with a new Pool.
436	// Ideally, the new ThreadGlobals could be created inside the call,
437	// but that requires modifying the function being executed.
438	// Instead, we create the new ThreadGlobals here and wrap it into an "unnecessary" Env
439	// Thus we must create a corresponding "unnecessary" Comp and use it to compile the call
440	c2 := NewComp(c, &c.Code)
441
442	call := c2.prepareCall(node.Call, nil)
443	exprfun := call.Fun.AsX1()
444	argfunsX1 := call.MakeArgfunsX1()
445
446	var debugC *Comp
447	if c2.Globals.Options&OptDebugger != 0 {
448		// keep a reference to c2 only if needed
449		debugC = c2
450	}
451
452	stmt := func(env *Env) (Stmt, *Env) {
453		tg := env.Run
454		// create a new Env to hold the new ThreadGlobals (created in the goroutine below) and (initially empty) Pool
455		env2 := newEnv(tg, env, 0, 0)
456		env2.DebugComp = debugC
457
458		// env2.MarkUsedByClosure() // redundant, done by exprfun(env2) below
459
460		// function and arguments are evaluated in the caller's goroutine
461		// using the new Env: we compiled them with c2 => execute them with env2
462		funv := exprfun(env2)
463		argv := make([]r.Value, len(argfunsX1))
464		for i, argfun := range argfunsX1 {
465			argv[i] = argfun(env2)
466		}
467		// the call is executed in a new goroutine.
468		// make it easy and do not try to optimize this call.
469		go func() {
470			tg2 := tg.new(gls.GoID())
471			env2.Run = tg2
472			tg2.glsStore()
473			defer tg2.glsDel()
474
475			funv.Call(argv)
476		}()
477
478		env.IP++
479		return env.Code[env.IP], env
480	}
481	c2.Append(stmt, node.Pos())
482
483	// propagate back the compiled code
484	c.Code = c2.Code
485}
486
487// If compiles an "if" statement
488func (c *Comp) If(node *ast.IfStmt) {
489	var jump struct{ Then, Else, End int }
490
491	initLocals := false
492	var initBinds [2]int
493	c, initLocals = c.pushEnvIfLocalBinds(&initBinds, node.Init)
494	if node.Init != nil {
495		c.Stmt(node.Init)
496	}
497	pred := c.Expr(node.Cond, nil)
498	flag, fun, err := pred.TryAsPred()
499	if err {
500		c.invalidPred(node.Cond, pred)
501		return
502	}
503	if fun != nil {
504		stmt := func(env *Env) (Stmt, *Env) {
505			var ip int
506			if fun(env) {
507				ip = jump.Then
508			} else {
509				ip = jump.Else
510			}
511			env.IP = ip
512			return env.Code[ip], env
513		}
514		c.Append(stmt, node.Cond.Pos())
515	}
516	// compile 'then' branch
517	jump.Then = c.Code.Len()
518	c.Block(node.Body)
519	if fun == nil && !flag {
520		// 'then' branch is never executed...
521		// still compiled above (to check for errors) but drop the generated code
522		c.Code.Truncate(jump.Then)
523	}
524	// compile a 'goto' between 'then' and 'else' branches
525	if fun != nil && node.Else != nil {
526		c.Append(func(env *Env) (Stmt, *Env) {
527			// after executing 'then' branch, we must skip 'else' branch
528			env.IP = jump.End
529			return env.Code[jump.End], env
530		}, node.Else.Pos())
531	}
532	// compile 'else' branch
533	jump.Else = c.Code.Len()
534	if node.Else != nil {
535		// parser should guarantee Else to be a block or another "if"
536		// but macroexpansion can optimize away the block if it contains no declarations.
537		// still, better be safe and wrap the Else again in a block because:
538		// 1) catches improper macroexpander optimizations
539		// 2) there is no runtime performance penalty
540		xelse := node.Else
541		_, ok1 := xelse.(*ast.BlockStmt)
542		_, ok2 := xelse.(*ast.IfStmt)
543		if ok1 || ok2 {
544			c.Stmt(xelse)
545		} else {
546			c.Block(&ast.BlockStmt{List: []ast.Stmt{xelse}})
547		}
548		if fun == nil && flag {
549			// 'else' branch is never executed...
550			// still compiled above (to check for errors) but drop the generated code
551			c.Code.Truncate(jump.Else)
552		}
553	}
554	jump.End = c.Code.Len()
555
556	c = c.popEnvIfLocalBinds(initLocals, &initBinds, node.Init)
557}
558
559// IncDec compiles a "place++" or "place--" statement
560func (c *Comp) IncDec(node *ast.IncDecStmt) {
561	place := c.Place(node.X)
562	op := node.Tok
563	if op == token.DEC {
564		op = token.SUB
565	} else {
566		op = token.ADD
567	}
568	one := c.exprUntypedLit(untypedOne.Kind, untypedOne.Val)
569	c.SetPlace(place, op, one)
570}
571
572// Return compiles a "return" statement
573func (c *Comp) Return(node *ast.ReturnStmt) {
574	var cinfo *FuncInfo
575	var upn int
576	var cf *Comp
577	for cf = c; cf != nil; cf = cf.Outer {
578		if cf.Func != nil {
579			cinfo = cf.Func
580			break
581		}
582		upn += cf.UpCost // count how many Env:s we must exit at runtime
583	}
584	if cinfo == nil {
585		c.Errorf("return outside function")
586		return
587	}
588
589	resultBinds := cinfo.Result
590	resultExprs := node.Results
591	n := len(resultBinds)
592	switch len(resultExprs) {
593	case n:
594		// ok
595	case 1:
596		if n == 0 {
597			c.Errorf("return: expecting %d expressions, found %d: %v", n, len(resultExprs), node)
598		}
599		c.returnMultiValues(node, resultBinds, upn, resultExprs)
600		return
601	case 0:
602		if !cinfo.NamedResults {
603			// naked return requires results to have names
604			c.Errorf("return: expecting %d expressions, found %d: %v", n, len(resultExprs), node)
605			return
606		}
607		n = 0 // naked return. results are already set
608	default:
609		c.Errorf("return: expecting %d expressions, found %d: %v", n, len(resultExprs), node)
610		return
611	}
612
613	exprs := c.exprs(resultExprs)
614	for i := 0; i < n; i++ {
615		c.Pos = resultExprs[i].Pos()
616		c.SetVar(resultBinds[i].AsVar(upn, PlaceSettable), token.ASSIGN, exprs[i])
617	}
618	c.Append(stmtReturn, node.Pos())
619}
620
621// returnMultiValues compiles a "return foo()" statement where foo() returns multiple values
622func (c *Comp) returnMultiValues(node *ast.ReturnStmt, resultBinds []*Bind, upn int, exprs []ast.Expr) {
623	n := len(resultBinds)
624	e := c.ExprsMultipleValues(exprs, n)[0]
625	fun := e.AsXV(COptDefaults)
626	assigns := make([]func(*Env, r.Value), n)
627	for i := 0; i < n; i++ {
628		texpected := resultBinds[i].Type
629		tactual := e.Out(i)
630		if !tactual.AssignableTo(texpected) {
631			c.Errorf("incompatible types in assignment: %v = %v", texpected, tactual)
632		}
633		assigns[i] = c.varSetValue(resultBinds[i].AsVar(upn, PlaceSettable))
634	}
635	c.Append(func(env *Env) (Stmt, *Env) {
636		// no risk in evaluating fun() first: return binds are plain variables, not places with side effects
637		_, vals := fun(env)
638		for i, assign := range assigns {
639			assign(env, vals[i])
640		}
641		// append the return epilogue
642		env.IP++
643		g := env.Run
644		g.Signals.Sync = SigReturn
645		return g.Interrupt, env
646	}, node.Pos())
647}
648
649func stmtReturn(env *Env) (Stmt, *Env) {
650	env.IP++
651	g := env.Run
652	g.Signals.Sync = SigReturn
653	return g.Interrupt, env
654}
655
656// containLocalBinds return true if one or more of the given statements (but not their contents:
657// blocks are not examined) contain some function/variable declaration.
658// ignores types, constants and anything named "_"
659func containLocalBinds(list ...ast.Stmt) bool {
660	if len(list) == 0 {
661		output.Errorf("internal error: containLocalBinds() invoked on empty statement list")
662	}
663	for _, node := range list {
664		switch node := node.(type) {
665		case *ast.AssignStmt:
666			if node.Tok == token.DEFINE {
667				return true
668			}
669		case *ast.DeclStmt:
670			switch decl := node.Decl.(type) {
671			case *ast.FuncDecl:
672				// Go compiler forbids local functions... we allow them
673				if decl.Name != nil && decl.Name.Name != "_" {
674					return true
675				}
676			case *ast.GenDecl:
677				if decl.Tok != token.VAR {
678					continue
679				}
680				// found local variables... bail out unless they are all named "_"
681				for _, spec := range decl.Specs {
682					switch spec := spec.(type) {
683					case *ast.ValueSpec:
684						for _, ident := range spec.Names {
685							if ident.Name != "_" {
686								return true
687							}
688						}
689					}
690				}
691			}
692		case *ast.SelectStmt:
693			// Comp.Select() creates an unnamed bind
694			// to store the value received from channel.
695			if node.Body != nil && len(node.Body.List) != 0 {
696				return true
697			}
698		case nil:
699		}
700	}
701	return false
702}
703
704// pushEnvIfLocalBinds compiles a PushEnv statement if list contains local binds
705// returns the *Comp to use to compile statement list.
706func (c *Comp) pushEnvIfLocalBinds(nbind *[2]int, list ...ast.Stmt) (inner *Comp, locals bool) {
707	if len(list) == 0 {
708		inner.Errorf("internal error: pushEnvIfLocalBinds() invoked on empty statement list")
709	}
710	// 2. optimization: examine statements. if none of them is a function/variable declaration,
711	// no need to create a new *Env at runtime
712	// note: we still create a new *Comp at compile time to handle constant/type declarations
713	locals = containLocalBinds(list...)
714	return c.pushEnvIfFlag(nbind, locals)
715}
716
717// pushEnvIfDefine compiles a PushEnv statement if tok is token.DEFINE
718// returns the *Comp to use to compile statement list.
719func (c *Comp) pushEnvIfDefine(nbind *[2]int, tok token.Token) (inner *Comp, locals bool) {
720	return c.pushEnvIfFlag(nbind, tok == token.DEFINE)
721}
722
723// pushEnvIfFlag compiles a PushEnv statement if flag is true
724// returns the *Comp to use to compile statement list.
725func (c *Comp) pushEnvIfFlag(nbind *[2]int, flag bool) (*Comp, bool) {
726	var debugC *Comp
727	if flag {
728		// push new *Env at runtime. we will know # of binds in the block only later, so use a closure on them
729		c.append(func(env *Env) (Stmt, *Env) {
730			inner := NewEnv(env, nbind[0], nbind[1])
731			inner.DebugComp = debugC
732			inner.IP++
733			// Debugf("PushEnv(%p->%p), IP = %d of %d, pushed %d binds and %d intbinds", env, inner, inner.IP, nbinds[0], nbinds[1])
734			return inner.Code[inner.IP], inner
735		})
736	}
737	innerC := NewComp(c, &c.Code)
738	if flag {
739		if c.Globals.Options&OptDebugger != 0 {
740			// for debugger, inject the inner *Comp into the inner *Env
741			debugC = innerC
742		}
743	} else {
744		innerC.UpCost = 0
745		innerC.Depth--
746	}
747	return innerC, flag
748}
749
750// popEnvIfLocalBinds compiles a PopEnv statement if locals is true. also sets *nbinds and *nintbinds
751func (inner *Comp) popEnvIfLocalBinds(locals bool, nbinds *[2]int, list ...ast.Stmt) *Comp {
752	if len(list) == 0 {
753		inner.Errorf("internal error: popEnvIfLocalBinds() invoked on empty statement list")
754	}
755	c := inner.Outer
756	c.Code = inner.Code       // copy back accumulated code
757	nbinds[0] = inner.BindNum // we finally know these
758	nbinds[1] = inner.IntBindNum
759
760	if locals != (inner.BindNum != 0 || inner.IntBindNum != 0) {
761		c.Errorf(`internal error: containLocalBinds() returned %t, but block actually defined %d Binds and %d IntBinds:
762	Binds = %v
763	Block =
764%v`, locals, inner.BindNum, inner.IntBindNum, inner.Binds, &ast.BlockStmt{List: list})
765		return nil
766	}
767
768	if locals {
769		// pop *Env at runtime
770		c.append(popEnv)
771	}
772	return c
773}
774
775// popEnvIfLocalBinds compiles a PopEnv statement if flag is true. also sets *nbinds and *nintbinds
776func (inner *Comp) popEnvIfFlag(nbinds *[2]int, flag bool) *Comp {
777	c := inner.Outer
778	c.Code = inner.Code       // copy back accumulated code
779	nbinds[0] = inner.BindNum // we finally know these
780	nbinds[1] = inner.IntBindNum
781
782	if flag && nbinds[0] == 0 && nbinds[1] == 0 {
783		c.Debugf(`redundant popEnvIfFlag: flag is %t, but block actually defined %d Binds and %d IntBinds:
784	Binds = %v`, flag, nbinds[0], nbinds[1], inner.Binds)
785	} else if !flag && (nbinds[0] != 0 || nbinds[1] != 0) {
786		c.Errorf(`popEnvIfFlag internal error: flag is %t, but block actually defined %d Binds and %d IntBinds:
787	Binds = %v`, flag, nbinds[0], nbinds[1], inner.Binds)
788		return nil
789	}
790
791	if flag {
792		// pop *Env at runtime
793		c.append(popEnv)
794	}
795	return c
796}
797
798func (c *Comp) misplacedCase(node ast.Node, isdefault bool) {
799	label := "case"
800	if isdefault {
801		label = "default"
802	}
803	c.Errorf("misplaced %s: not inside switch or select: %v <%v>", label, node, r.TypeOf(node))
804}
805
806func (c *Comp) misplacedFallthrough() {
807	c.Errorf("misplaced fallthrough: not inside switch")
808}
809