Commit 36ca5fde authored by Austin Clements's avatar Austin Clements

Implement labels, goto, labeled break, and labeled continue.

Return checking is now done as a general flow check at the end
of function compilation, since break and goto complicated the
way I was doing return checking before.  Goto-over-declaration
checking is also done as a final flow check.

Temporary variables used for effect extraction are now
actually temporary.  Otherwise "op=", "++", and "--" appear as
declarations that cannot be jumped over.

R=rsc
APPROVED=rsc
DELTA=421  (344 added, 38 deleted, 39 changed)
OCL=32527
CL=32535
parent 6ccebe08
......@@ -44,7 +44,27 @@ func (a *compiler) compileFuncType(b *block, typ *ast.FuncType) *FuncDecl
func (a *compiler) compileArrayLen(b *block, expr ast.Expr) (int64, bool)
type label struct {
name string;
desc string;
// The PC goto statements should jump to, or nil if this label
// cannot be goto'd (such as an anonymous for loop label).
gotoPC *uint;
// The PC break statements should jump to, or nil if a break
// statement is invalid.
breakPC *uint;
// The PC continue statements should jump to, or nil if a
// continue statement is invalid.
continuePC *uint;
// The position where this label was resolved. If it has not
// been resolved yet, an invalid position.
resolved token.Position;
// The position where this label was first jumped to.
used token.Position;
}
type codeBuf struct
type flowBuf struct
type FuncType struct
// A funcCompiler captures information used throughout the compilation
// of a single function body.
......@@ -55,22 +75,21 @@ type funcCompiler struct {
// kinds of return statements are legal.
outVarsNamed bool;
*codeBuf;
flow *flowBuf;
labels map[string] *label;
err bool;
}
func (a *funcCompiler) checkLabels()
// A blockCompiler captures information used throughout the compilation
// of a single block within a function.
type blockCompiler struct {
*funcCompiler;
block *block;
returned bool;
// The PC break statements should jump to, or nil if a break
// statement is invalid.
breakPC *uint;
// The PC continue statements should jump to, or nil if a
// continue statement is invalid.
continuePC *uint;
// The label of this block, used for finding break and
// continue labels.
label *label;
// The blockCompiler for the block enclosing this one, or nil
// for a function-level block.
parent *blockCompiler;
......
......@@ -178,7 +178,7 @@ func (b *block) enterChild() *block
func (b *block) exit()
func (b *block) ChildScope() *Scope
func (b *block) DefineVar(name string, t Type) *Variable
func (b *block) DefineTemp(t Type) *Variable
func (b *block) DefineSlot(t Type) *Variable
func (b *block) DefineConst(name string, t Type, v Value) *Constant
func (b *block) DefineType(name string, t Type) Type
func (b *block) Lookup(name string) (level int, def Def)
......
......@@ -1324,7 +1324,10 @@ func (a *compiler) compileExpr(b *block, expr ast.Expr, constant bool) *exprComp
// extractEffect separates out any effects that the expression may
// have, returning a function that will perform those effects and a
// new exprCompiler that is guaranteed to be side-effect free. These
// are the moral equivalents of "temp := &expr" and "*temp".
// are the moral equivalents of "temp := &expr" and "*temp". Because
// this creates a temporary variable, the caller should create a
// temporary block for the compilation of this expression and the
// evaluation of the results.
//
// Implementation limit: The expression must be addressable.
func (a *exprCompiler) extractEffect() (func(f *Frame), *exprCompiler) {
......@@ -1337,9 +1340,7 @@ func (a *exprCompiler) extractEffect() (func(f *Frame), *exprCompiler) {
// Create temporary
tempBlock := a.block;
tempType := NewPtrType(a.t);
// TODO(austin) These temporaries accumulate in the scope. We
// could enter a temporary block, but the caller has to exit it.
temp := tempBlock.DefineTemp(tempType);
temp := tempBlock.DefineSlot(tempType);
tempIdx := temp.Index;
// Generate "temp := &e"
......
......@@ -51,14 +51,14 @@ func (b *block) DefineVar(name string, t Type) *Variable {
if _, ok := b.defs[name]; ok {
return nil;
}
v := b.DefineTemp(t);
v := b.DefineSlot(t);
if v != nil {
b.defs[name] = v;
}
return v;
}
func (b *block) DefineTemp(t Type) *Variable {
func (b *block) DefineSlot(t Type) *Variable {
if b.inner != nil {
log.Crash("Failed to exit child block before defining variable");
}
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment