]> Cypherpunks repositories - gostls13.git/commitdiff
Implement multi-valued functions, multi-valued return, and
authorAustin Clements <aclements@csail.mit.edu>
Tue, 28 Jul 2009 00:32:35 +0000 (17:32 -0700)
committerAustin Clements <aclements@csail.mit.edu>
Tue, 28 Jul 2009 00:32:35 +0000 (17:32 -0700)
unpacking for assignments, call arguments, and returns.  This
change revamps the whole assignment compilation system to be
multi-valued, using the new MultiType type and multiV value.
Function calls, returns, and assignments now share a lot of
code and produce very consistent error messages.

R=rsc
APPROVED=rsc
DELTA=510  (335 added, 74 deleted, 101 changed)
OCL=32248
CL=32258

usr/austin/eval/compiler.go
usr/austin/eval/expr.go
usr/austin/eval/stmt.go
usr/austin/eval/type.go
usr/austin/eval/value.go

index ab505dec1a3e4d58e30d5d232bb66caf21ac8b7a..47ff12f36e9b061a7c8a770d865ead9ba8c9f7bf 100644 (file)
@@ -35,6 +35,9 @@ type FuncDecl struct
 func (a *compiler) compileFunc(scope *Scope, decl *FuncDecl, body *ast.BlockStmt) (func (f *Frame) Func)
 type exprCompiler struct
 func (a *compiler) compileExpr(scope *Scope, expr ast.Expr, constant bool) *exprCompiler
+type assignCompiler struct
+func (a *compiler) checkAssign(pos token.Position, rs []*exprCompiler, errOp, errPosName string) (*assignCompiler, bool)
+func (a *compiler) compileAssign(pos token.Position, lt Type, rs []*exprCompiler, errOp, errPosName string) (func(lv Value, f *Frame))
 func (a *compiler) compileType(scope *Scope, typ ast.Expr) Type
 func (a *compiler) compileFuncType(scope *Scope, typ *ast.FuncType) *FuncDecl
 
@@ -42,11 +45,12 @@ func (a *compiler) compileArrayLen(scope *Scope, expr ast.Expr) (int64, bool)
 
 
 type codeBuf struct
+type FuncType struct
 // A funcCompiler captures information used throughout the compilation
 // of a single function body.
 type funcCompiler struct {
        *compiler;
-       outVars []*Variable;
+       fnType *FuncType;
        // Whether the out variables are named.  This affects what
        // kinds of return statements are legal.
        outVarsNamed bool;
index 758558ff59012eeaa1ca5a4f95fc7fc8e074aa24..309caa0abf4c0ba6d50a0851d3dc10f630159a35 100644 (file)
@@ -37,6 +37,7 @@ type exprCompiler struct {
        evalArray func(f *Frame) ArrayValue;
        evalPtr func(f *Frame) Value;
        evalFunc func(f *Frame) Func;
+       evalMulti func(f *Frame) []Value;
        // Evaluate to the "address of" this value; that is, the
        // settable Value object.  nil for expressions whose address
        // cannot be taken.
@@ -179,6 +180,13 @@ func (a *exprCompiler) asFunc() (func(f *Frame) Func) {
        return a.evalFunc;
 }
 
+func (a *exprCompiler) asMulti() (func(f *Frame) []Value) {
+       if a.evalMulti == nil {
+               log.Crashf("tried to get %v node as MultiType", a.t);
+       }
+       return a.evalMulti;
+}
+
 /*
  * Common expression manipulations
  */
@@ -186,6 +194,8 @@ func (a *exprCompiler) asFunc() (func(f *Frame) Func) {
 // a.convertTo(t) converts the value of the analyzed expression a,
 // which must be a constant, ideal number, to a new analyzed
 // expression with a constant value of type t.
+//
+// TODO(austin) Rename to resolveIdeal or something?
 func (a *exprCompiler) convertTo(t Type) *exprCompiler {
        if !a.t.isIdeal() {
                log.Crashf("attempted to convert from %v, expected ideal", a.t);
@@ -256,66 +266,193 @@ func (a *exprCompiler) convertTo(t Type) *exprCompiler {
        return res;
 }
 
-// mkAssign takes an optional expected l-value type, lt, and an
-// r-value expression compiler, r, and returns the expected l-value
-// type and a function that evaluates the r-value and assigns it to
-// the l-value lv.
-//
-// If lt is non-nil, the returned l-value type will always be lt.  If
-// lt is nil, mkAssign will infer and return the appropriate l-value
-// type, or produce an error.
-//
-// errOp specifies the operation name to use for error messages, such
-// as "assignment", or "function call".  errPosName specifies the name
-// to use for positions.  errPos, if non-zero, specifies the position
-// of this assignment (for tuple assignments or function arguments).
+/*
+ * Assignments
+ */
+
+// An assignCompiler compiles assignment operations.  Anything other
+// than short declarations should use the compileAssign wrapper.
 //
-// If the assignment fails to typecheck, this generates an error
-// message and returns nil, nil.
-func mkAssign(lt Type, r *exprCompiler, errOp string, errPosName string, errPos int) (Type, func(lv Value, f *Frame)) {
-       // However, when [an ideal is] (used in an expression)
-       // assigned to a variable or typed constant, the destination
-       // must be able to represent the assigned value.
-       if r.t.isIdeal() && (lt == nil || lt.isInteger() || lt.isFloat()) {
-               // If the type is absent and the corresponding
-               // expression is a constant expression of ideal
-               // integer or ideal float type, the type of the
-               // declared variable is int or float respectively.
-               if lt == nil {
-                       switch {
-                       case r.t.isInteger():
-                               lt = IntType;
-                       case r.t.isFloat():
-                               lt = FloatType;
-                       default:
-                               log.Crashf("unexpected ideal type %v", r.t);
-                       }
+// There are three valid types of assignment:
+// 1) T = T
+//    Assigning a single expression with single-valued type to a
+//    single-valued type.
+// 2) MT = T, T, ...
+//    Assigning multiple expressions with single-valued types to a
+//    multi-valued type.
+// 3) MT = MT
+//    Assigning a single expression with multi-valued type to a
+//    multi-valued type.
+type assignCompiler struct {
+       *compiler;
+       pos token.Position;
+       // The RHS expressions.  This may include nil's for
+       // expressions that failed to compile.
+       rs []*exprCompiler;
+       // The (possibly unary) MultiType of the RHS.
+       rmt *MultiType;
+       // Whether this is an unpack assignment (case 3).
+       isUnpack bool;
+       // The operation name to use in error messages, such as
+       // "assignment" or "function call".
+       errOp string;
+       // The name to use for positions in error messages, such as
+       // "argument".
+       errPosName string;
+}
+
+// Type check the RHS of an assignment, returning a new assignCompiler
+// and indicating if the type check succeeded.  This always returns an
+// assignCompiler with rmt set, but if type checking fails, slots in
+// the MultiType may be nil.  If rs contains nil's, type checking will
+// fail and these expressions given a nil type.
+func (a *compiler) checkAssign(pos token.Position, rs []*exprCompiler, errOp, errPosName string) (*assignCompiler, bool) {
+       c := &assignCompiler{
+               compiler: a,
+               pos: pos,
+               rs: rs,
+               errOp: errOp,
+               errPosName: errPosName,
+       };
+
+       // Is this an unpack?
+       if len(rs) == 1 && rs[0] != nil {
+               if rmt, isUnpack := rs[0].t.(*MultiType); isUnpack {
+                       c.rmt = rmt;
+                       c.isUnpack = true;
+                       return c, true;
                }
-               r = r.convertTo(lt);
+       }
+
+       // Create MultiType for RHS and check that all RHS expressions
+       // are single-valued.
+       rts := make([]Type, len(rs));
+       ok := true;
+       for i, r := range rs {
                if r == nil {
-                       return nil, nil;
+                       ok = false;
+                       continue;
+               }
+
+               if _, isMT := r.t.(*MultiType); isMT {
+                       r.diag("multi-valued expression not allowed in %s", errOp);
+                       ok = false;
+                       continue;
                }
+
+               rts[i] = r.t;
        }
 
-       // TOOD(austin) Deal with assignment special cases
+       c.rmt = NewMultiType(rts);
+       return c, ok;
+}
+
+// compile type checks and compiles an assignment operation, returning
+// a function that expects an l-value and the frame in which to
+// evaluate the RHS expressions.  The l-value must have exactly the
+// type given by lt.  Returns nil if type checking fails.
+func (a *assignCompiler) compile(lt Type) (func(lv Value, f *Frame)) {
+       lmt, isMT := lt.(*MultiType);
+       rmt, isUnpack := a.rmt, a.isUnpack;
+
+       // Create unary MultiType for single LHS
+       if !isMT {
+               lmt = NewMultiType([]Type{lt});
+       }
+
+       // Check that the assignment count matches
+       lcount := len(lmt.Elems);
+       rcount := len(rmt.Elems);
+       if lcount != rcount {
+               msg := "not enough";
+               pos := a.pos;
+               if rcount > lcount {
+                       msg = "too many";
+                       if lcount > 0 {
+                               pos = a.rs[lcount-1].pos;
+                       }
+               }
+               a.diagAt(&pos, "%s %ss for %s\n\t%s\n\t%s", msg, a.errPosName, a.errOp, lt, rmt);
+               return nil;
+       }
+
+       bad := false;
+
+       // TODO(austin) Deal with assignment special cases.  This is
+       // tricky in the unpack case, since some of the conversions
+       // can apply to single types within the multi-type.
+
+       // Values of any type may always be assigned to variables of
+       // compatible static type.
+       for i, lt := range lmt.Elems {
+               // Check each type individually so we can produce a
+               // better error message.
+               rt := rmt.Elems[i];
+
+               // When [an ideal is] (used in an expression) assigned
+               // to a variable or typed constant, the destination
+               // must be able to represent the assigned value.
+               if rt.isIdeal() {
+                       if isUnpack {
+                               log.Crashf("Right side of unpack contains ideal: %s", rmt);
+                       }
+                       a.rs[i] = a.rs[i].convertTo(lmt.Elems[i]);
+                       if a.rs[i] == nil {
+                               bad = true;
+                               continue;
+                       }
+                       rt = a.rs[i].t;
+               }
 
-       if lt == nil {
-               lt = r.t;
-       } else {
-               // Values of any type may always be assigned to
-               // variables of compatible static type.
-               if lt.literal() != r.t.literal() {
-                       if errPos == 0 {
-                               r.diag("illegal operand types for %s\n\t%v\n\t%v", errOp, lt, r.t);
+               if lt.literal() != rt.literal() {
+                       if len(a.rs) == 1 {
+                               a.rs[0].diag("illegal operand types for %s\n\t%v\n\t%v", a.errOp, lt, rt);
                        } else {
-                               r.diag("illegal operand types in %s %d of %s\n\t%v\n\t%v", errPosName, errPos, errOp, lt, r.t);
+                               a.rs[i].diag("illegal operand types in %s %d of %s\n\t%v\n\t%v", a.errPosName, i+1, a.errOp, lt, rt);
                        }
-                       return nil, nil;
+                       bad = true;
                }
        }
+       if bad {
+               return nil;
+       }
 
        // Compile
-       return lt, genAssign(lt, r);
+       switch {
+       case !isMT:
+               // Case 1
+               return genAssign(lt, a.rs[0]);
+       case !isUnpack:
+               // Case 2
+               as := make([]func(lv Value, f *Frame), len(a.rs));
+               for i, r := range a.rs {
+                       as[i] = genAssign(lmt.Elems[i], r);
+               }
+               return func(lv Value, f *Frame) {
+                       lmv := lv.(multiV);
+                       for i, a := range as {
+                               a(lmv[i], f);
+                       }
+               };
+       default:
+               // Case 3
+               rf := a.rs[0].asMulti();
+               return func(lv Value, f *Frame) {
+                       lv.Assign(multiV(rf(f)));
+               };
+       }
+       panic();
+}
+
+// compileAssign compiles an assignment operation without the full
+// generality of an assignCompiler.  See assignCompiler for a
+// description of the arguments.
+func (a *compiler) compileAssign(pos token.Position, lt Type, rs []*exprCompiler, errOp, errPosName string) (func(lv Value, f *Frame)) {
+       ac, ok := a.checkAssign(pos, rs, errOp, errPosName);
+       if !ok {
+               return nil;
+       }
+       return ac.compile(lt);
 }
 
 /*
@@ -342,6 +479,10 @@ func (a *exprCompiler) DoIdent(x *ast.Ident) {
                        a.diag("variable %s used in constant expression", x.Value);
                        return;
                }
+               if def.Type == nil {
+                       // Placeholder definition from an earlier error
+                       return;
+               }
                a.t = def.Type;
                defidx := def.Index;
                a.genIdentOp(dscope, defidx);
@@ -611,51 +752,37 @@ func (a *exprCompiler) DoCallExpr(x *ast.CallExpr) {
                return;
        }
 
-       if len(as) != len(lt.In) {
-               msg := "too many";
-               if len(as) < len(lt.In) {
-                       msg = "not enough";
-               }
-               a.diag("%s arguments to call\n\t%s\n\t%s", msg, typeListString(lt.In, nil), typeListString(ats, nil));
-               return;
-       }
-
        // The arguments must be single-valued expressions assignment
        // compatible with the parameters of F.
-       afs := make([]func(lv Value, f *Frame), len(as));
-       for i := 0; i < len(as); i++ {
-               var at Type;
-               at, afs[i] = mkAssign(lt.In[i], as[i], "function call", "argument", i + 1);
-               if at == nil {
-                       bad = true;
-               }
-       }
-       if bad {
+       //
+       // XXX(Spec) The spec is wrong.  It can also be a single
+       // multi-valued expression.
+       assign := a.compileAssign(x.Pos(), NewMultiType(lt.In), as, "function call", "argument");
+       if assign == nil {
                return;
        }
 
-       nResults := len(lt.Out);
-       if nResults != 1 {
-               log.Crashf("Multi-valued return type not implemented");
+       nout := len(lt.Out);
+       switch nout {
+       case 0:
+               a.t = EmptyType;
+       case 1:
+               a.t = lt.Out[0];
+       default:
+               a.t = NewMultiType(lt.Out);
        }
-       a.t = lt.Out[0];
 
        // Compile
        lf := l.asFunc();
+       nin := len(lt.In);
        call := func(f *Frame) []Value {
                fun := lf(f);
                fr := fun.NewFrame();
-               for i, af := range afs {
-                       af(fr.Vars[i], f);
-               }
+               assign(multiV(fr.Vars[0:nin]), f);
                fun.Call(fr);
-               return fr.Vars[len(afs):len(afs)+nResults];
+               return fr.Vars[nin:nin+nout];
        };
        a.genFuncCall(call);
-
-       // Function calls, method calls, and channel operations can
-       // appear in statement context.
-       a.exec = func(f *Frame) { call(f) };
 }
 
 func (a *exprCompiler) DoStarExpr(x *ast.StarExpr) {
@@ -1150,9 +1277,9 @@ func (a *exprCompiler) extractEffect() (func(f *Frame), *exprCompiler) {
        addr.t = tempType;
        addr.genUnaryAddrOf(a);
 
-       _, assign := mkAssign(tempType, addr, "", "", 0);
+       assign := a.compileAssign(a.pos, tempType, []*exprCompiler{addr}, "", "");
        if assign == nil {
-               log.Crashf("extractEffect: mkAssign type check failed");
+               log.Crashf("compileAssign type check failed");
        }
 
        effect := func(f *Frame) {
@@ -1311,6 +1438,7 @@ func (a *exprCompiler) genIndexArray(l *exprCompiler, r *exprCompiler) {
 }
 
 func (a *exprCompiler) genFuncCall(call func(f *Frame) []Value) {
+       a.exec = func(f *Frame) { call(f) };
        switch _ := a.t.rep().(type) {
        case *boolType:
                a.evalBool = func(f *Frame) bool { return call(f)[0].(BoolValue).Get() };
@@ -1328,6 +1456,8 @@ func (a *exprCompiler) genFuncCall(call func(f *Frame) []Value) {
                a.evalPtr = func(f *Frame) Value { return call(f)[0].(PtrValue).Get() };
        case *FuncType:
                a.evalFunc = func(f *Frame) Func { return call(f)[0].(FuncValue).Get() };
+       case *MultiType:
+               a.evalMulti = func(f *Frame) []Value { return call(f) };
        default:
                log.Crashf("unexpected result type %v at %v", a.t, a.pos);
        }
index 6e3b2a50741d8daebae1b70b3406d3e976bef899..629d77434fbcd5eb152217489b0c3ce510176cb3 100644 (file)
@@ -91,21 +91,27 @@ func (a *stmtCompiler) doAssign(s *ast.AssignStmt) {
                }
        }
 
-       // Check the assignment count
-       if len(s.Lhs) != len(s.Rhs) {
-               log.Crashf("Unbalanced assignment not implemented %v %v %v", len(s.Lhs), s.Tok, len(s.Rhs));
+       errOp := "assignment";
+       if s.Tok == token.DEFINE {
+               errOp = "definition";
+       }
+       ac, ok := a.checkAssign(s.Pos(), rs, "assignment", "value");
+       if !ok {
+               bad = true;
+       }
+
+       // If this is a definition and the LHS is too big, we won't be
+       // able to produce the usual error message because we can't
+       // begin to infer the types of the LHS.
+       if s.Tok == token.DEFINE && len(s.Lhs) > len(ac.rmt.Elems) {
+               a.diag("not enough values for definition");
+               bad = true;
        }
 
-       // Compile left side and generate assigners
+       // Compile left side
        ls := make([]*exprCompiler, len(s.Lhs));
-       as := make([]func(lv Value, f *Frame), len(s.Lhs));
        nDefs := 0;
        for i, le := range s.Lhs {
-               errPos := i + 1;
-               if len(s.Lhs) == 1 {
-                       errPos = 0;
-               }
-
                if s.Tok == token.DEFINE {
                        // Check that it's an identifier
                        ident, ok := le.(*ast.Ident);
@@ -123,17 +129,39 @@ func (a *stmtCompiler) doAssign(s *ast.AssignStmt) {
                        }
                        nDefs++;
 
-                       if rs[i] == nil {
-                               // TODO(austin) Define a placeholder.
-                               continue;
-                       }
-
-                       // Generate assigner and get type
+                       // Compute the identifier's type from the RHS
+                       // type.  We use the computed MultiType so we
+                       // don't have to worry about unpacking.
                        var lt Type;
-                       lt, as[i] = mkAssign(nil, rs[i], "assignment", "position", errPos);
-                       if lt == nil {
-                               bad = true;
-                               continue;
+                       switch {
+                       case i >= len(ac.rmt.Elems):
+                               // Define a placeholder.  We already
+                               // gave the "not enough" error above.
+                               lt = nil;
+
+                       case ac.rmt.Elems[i] == nil:
+                               // We gave the error when we compiled
+                               // the RHS.
+                               lt = nil;
+
+                       case ac.rmt.Elems[i].isIdeal():
+                               // If the type is absent and the
+                               // corresponding expression is a
+                               // constant expression of ideal
+                               // integer or ideal float type, the
+                               // type of the declared variable is
+                               // int or float respectively.
+                               switch {
+                               case ac.rmt.Elems[i].isInteger():
+                                       lt = IntType;
+                               case ac.rmt.Elems[i].isFloat():
+                                       lt = FloatType;
+                               default:
+                                       log.Crashf("unexpected ideal type %v", rs[i].t);
+                               }
+
+                       default:
+                               lt = ac.rmt.Elems[i];
                        }
 
                        // Define identifier
@@ -155,16 +183,6 @@ func (a *stmtCompiler) doAssign(s *ast.AssignStmt) {
                        bad = true;
                        continue;
                }
-
-               // Generate assigner
-               if as[i] == nil {
-                       var lt Type;
-                       lt, as[i] = mkAssign(ls[i].t, rs[i], "assignment", "position", errPos);
-                       if lt == nil {
-                               bad = true;
-                               continue;
-                       }
-               }
        }
 
        // A short variable declaration may redeclare variables
@@ -180,23 +198,58 @@ func (a *stmtCompiler) doAssign(s *ast.AssignStmt) {
                return;
        }
 
+       // Create assigner
+       var lt Type;
        n := len(s.Lhs);
        if n == 1 {
+               lt = ls[0].t;
+       } else {
+               lts := make([]Type, len(ls));
+               for i, l := range ls {
+                       if l != nil {
+                               lts[i] = l.t;
+                       }
+               }
+               lt = NewMultiType(lts);
+       }
+       assign := ac.compile(lt);
+       if assign == nil {
+               return;
+       }
+
+       // Compile
+       if n == 1 {
+               // Don't need temporaries and can avoid []Value.
                lf := ls[0].evalAddr;
-               assign := as[0];
                a.push(func(v *vm) { assign(lf(v.f), v.f) });
-       } else {
+       } else if s.Tok == token.DEFINE && nDefs == n {
+               // Don't need temporaries
+               lfs := make([]func(*Frame) Value, n);
+               for i, l := range ls {
+                       lfs[i] = l.evalAddr;
+               }
                a.push(func(v *vm) {
-                       temps := make([]Value, n);
-                       // Assign to temporaries
-                       for i := 0; i < n; i++ {
-                               // TODO(austin) Don't capture ls
-                               temps[i] = ls[i].t.Zero();
-                               as[i](temps[i], v.f);
+                       dest := make([]Value, n);
+                       for i, lf := range lfs {
+                               dest[i] = lf(v.f);
                        }
+                       assign(multiV(dest), v.f);
+               });
+       } else {
+               // Need temporaries
+               lmt := lt.(*MultiType);
+               lfs := make([]func(*Frame) Value, n);
+               for i, l := range ls {
+                       lfs[i] = l.evalAddr;
+               }
+               a.push(func(v *vm) {
+                       temp := lmt.Zero().(multiV);
+                       assign(temp, v.f);
                        // Copy to destination
-                       for i := 0; i < n; i++ {
-                               ls[i].evalAddr(v.f).Assign(temps[i]);
+                       for i := 0; i < n; i ++ {
+                               // TODO(austin) Need to evaluate LHS
+                               // before RHS
+                               lfs[i](v.f).Assign(temp[i]);
                        }
                });
        }
@@ -244,9 +297,9 @@ func (a *stmtCompiler) doAssignOp(s *ast.AssignStmt) {
                return;
        }
 
-       _, assign := mkAssign(l.t, binop, "assignment", "", 0);
+       assign := a.compileAssign(s.Pos(), l.t, []*exprCompiler{binop}, "assignment", "value");
        if assign == nil {
-               return;
+               log.Crashf("compileAssign type check failed");
        }
 
        lf := l.evalAddr;
@@ -280,58 +333,46 @@ func (a *stmtCompiler) DoReturnStmt(s *ast.ReturnStmt) {
        // return statement.
        a.returned = true;
 
-       if len(s.Results) == 0 && (len(a.outVars) == 0 || a.outVarsNamed) {
+       if len(s.Results) == 0 && (len(a.fnType.Out) == 0 || a.outVarsNamed) {
                // Simple case.  Simply exit from the function.
                a.push(func(v *vm) { v.pc = ^uint(0) });
                a.err = false;
                return;
        }
 
-       // TODO(austin) Might be a call of a multi-valued function.
-       // It might be possible to combine this code with the
-       // assignment code.
-       if len(s.Results) != len(a.outVars) {
-               a.diag("Unbalanced return not implemented");
-               return;
-       }
-
-       // Compile expressions and create assigners
+       // Compile expressions
        bad := false;
        rs := make([]*exprCompiler, len(s.Results));
-       as := make([]func(lv Value, f *Frame), len(s.Results));
        for i, re := range s.Results {
                rs[i] = a.compileExpr(a.scope, re, false);
                if rs[i] == nil {
                        bad = true;
-                       continue;
-               }
-
-               errPos := i + 1;
-               if len(s.Results) == 1 {
-                       errPos = 0;
-               }
-               var lt Type;
-               lt, as[i] = mkAssign(a.outVars[i].Type, rs[i], "return", "value", errPos);
-               if as[i] == nil {
-                       bad = true;
                }
        }
-
        if bad {
                return;
        }
 
-       // Save indexes of return values
-       idxs := make([]int, len(s.Results));
-       for i, outVar := range a.outVars {
-               idxs[i] = outVar.Index;
+       // Create assigner
+
+       // However, if the expression list in the "return" statement
+       // is a single call to a multi-valued function, the values
+       // returned from the called function will be returned from
+       // this one.
+       assign := a.compileAssign(s.Pos(), NewMultiType(a.fnType.Out), rs, "return", "value");
+       if assign == nil {
+               return;
        }
 
+       // XXX(Spec) "The result types of the current function and the
+       // called function must match."  Match is fuzzy.  It should
+       // say that they must be assignment compatible.
+
        // Compile
+       start := len(a.fnType.In);
+       nout := len(a.fnType.Out);
        a.push(func(v *vm) {
-               for i, assign := range as {
-                       assign(v.activation.Vars[idxs[i]], v.f);
-               }
+               assign(multiV(v.activation.Vars[start:start+nout]), v.f);
                v.pc = ^uint(0);
        });
        a.err = false;
@@ -410,19 +451,17 @@ func (a *compiler) compileFunc(scope *Scope, decl *FuncDecl, body *ast.BlockStmt
        for i, t := range decl.Type.In {
                bodyScope.DefineVar(decl.InNames[i].Value, t);
        }
-       outVars := make([]*Variable, len(decl.Type.Out));
        for i, t := range decl.Type.Out {
                if decl.OutNames[i] != nil {
-                       outVars[i] = bodyScope.DefineVar(decl.OutNames[i].Value, t);
+                       bodyScope.DefineVar(decl.OutNames[i].Value, t);
                } else {
-                       // TODO(austin) It would be nice to have a
-                       // better way to define unnamed slots.
-                       outVars[i] = bodyScope.DefineVar(":out" + strconv.Itoa(i), t);
+                       // TODO(austin) Not technically a temp
+                       bodyScope.DefineTemp(t);
                }
        }
 
        // Create block context
-       fc := &funcCompiler{a, outVars, false, newCodeBuf(), false};
+       fc := &funcCompiler{a, decl.Type, false, newCodeBuf(), false};
        if len(decl.OutNames) > 0 && decl.OutNames[0] != nil {
                fc.outVarsNamed = true;
        }
index f204845e1df6267d592eb596e62140364521ce88..2a5f22e1be41eef9042afc6a3f178ac29577404d 100644 (file)
@@ -45,8 +45,11 @@ type typeArrayMap map[uintptr] *typeArrayMapEntry
 func hashTypeArray(key []Type) uintptr {
        hash := uintptr(0);
        for _, t := range key {
-               addr := reflect.NewValue(t).Addr();
                hash = hash * 33;
+               if t == nil {
+                       continue;
+               }
+               addr := reflect.NewValue(t).Addr();
                hash ^= addr;
        }
        return hash;
@@ -153,7 +156,6 @@ type uintType struct {
        Bits uint;
        // true for uintptr, false for all others
        Ptr bool;
-
        name string;
 }
 
@@ -224,7 +226,6 @@ type intType struct {
 
        // 0 for architecture-dependent types
        Bits uint;
-
        name string;
 }
 
@@ -437,10 +438,8 @@ func (t *stringType) Zero() Value
 
 type ArrayType struct {
        commonType;
-
        Len int64;
        Elem Type;
-
        lit Type;
 }
 
@@ -595,7 +594,12 @@ func typeListString(ts []Type, ns []*ast.Ident) string {
                if ns != nil && ns[i] != nil {
                        s += ns[i].Value + " ";
                }
-               s += t.String();
+               if t == nil {
+                       // Some places use nil types to represent errors
+                       s += "<none>";
+               } else {
+                       s += t.String();
+               }
        }
        return s;
 }
@@ -708,3 +712,55 @@ func (t *NamedType) String() string {
 func (t *NamedType) Zero() Value {
        return t.def.Zero();
 }
+
+/*
+ * Multi-valued type
+ */
+
+// MultiType is a special type used for multi-valued expressions, akin
+// to a tuple type.  It's not generally accessible within the
+// language.
+type MultiType struct {
+       commonType;
+       Elems []Type;
+       lit Type;
+}
+
+var multiTypes = newTypeArrayMap()
+
+func NewMultiType(elems []Type) *MultiType {
+       if t := multiTypes.Get(elems); t != nil {
+               return t.(*MultiType);
+       }
+
+       t := &MultiType{commonType{}, elems, nil};
+       multiTypes.Put(elems, t);
+       return t;
+}
+
+var EmptyType Type = NewMultiType([]Type{});
+
+func (t *MultiType) literal() Type {
+       if t.lit == nil {
+               elems := make([]Type, len(t.Elems));
+               for i, e := range t.Elems {
+                       elems[i] = e.literal();
+               }
+
+               t.lit = NewMultiType(elems);
+       }
+       return t.lit;
+}
+
+func (t *MultiType) rep() Type {
+       return t;
+}
+
+func (t *MultiType) String() string {
+       if len(t.Elems) == 0 {
+               return "<none>";
+       }
+       return typeListString(t.Elems, nil);
+}
+
+func (t *MultiType) Zero() Value
index b050448a7e1a56d72ba838000e7503d149e17765..de5813e6d1b5c38614ca4b43e1be2f67f9534726 100644 (file)
@@ -536,6 +536,38 @@ func (t *FuncType) Zero() Value {
        return &funcV{nil};
 }
 
+/*
+ * Multi-values
+ */
+
+type multiV []Value
+
+func (v multiV) String() string {
+       res := "(";
+       for i, v := range v {
+               if i > 0 {
+                       res += ", ";
+               }
+               res += v.String();
+       }
+       return res + ")";
+}
+
+func (v multiV) Assign(o Value) {
+       omv := o.(multiV);
+       for i := range v {
+               v[i].Assign(omv[i]);
+       }
+}
+
+func (t *MultiType) Zero() Value {
+       res := make([]Value, len(t.Elems));
+       for i := 0; i < len(t.Elems); i++ {
+               res[i] = t.Elems[i].Zero();
+       }
+       return multiV(res);
+}
+
 /*
  * Universal constants
  */