mirror of
https://github.com/golang/go
synced 2024-11-22 06:34:40 -07:00
Implement multi-valued functions, multi-valued return, and
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
This commit is contained in:
parent
eece85c9a7
commit
75760a4b5d
@ -35,6 +35,9 @@ type FuncDecl struct
|
|||||||
func (a *compiler) compileFunc(scope *Scope, decl *FuncDecl, body *ast.BlockStmt) (func (f *Frame) Func)
|
func (a *compiler) compileFunc(scope *Scope, decl *FuncDecl, body *ast.BlockStmt) (func (f *Frame) Func)
|
||||||
type exprCompiler struct
|
type exprCompiler struct
|
||||||
func (a *compiler) compileExpr(scope *Scope, expr ast.Expr, constant bool) *exprCompiler
|
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) compileType(scope *Scope, typ ast.Expr) Type
|
||||||
func (a *compiler) compileFuncType(scope *Scope, typ *ast.FuncType) *FuncDecl
|
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 codeBuf struct
|
||||||
|
type FuncType struct
|
||||||
// A funcCompiler captures information used throughout the compilation
|
// A funcCompiler captures information used throughout the compilation
|
||||||
// of a single function body.
|
// of a single function body.
|
||||||
type funcCompiler struct {
|
type funcCompiler struct {
|
||||||
*compiler;
|
*compiler;
|
||||||
outVars []*Variable;
|
fnType *FuncType;
|
||||||
// Whether the out variables are named. This affects what
|
// Whether the out variables are named. This affects what
|
||||||
// kinds of return statements are legal.
|
// kinds of return statements are legal.
|
||||||
outVarsNamed bool;
|
outVarsNamed bool;
|
||||||
|
@ -37,6 +37,7 @@ type exprCompiler struct {
|
|||||||
evalArray func(f *Frame) ArrayValue;
|
evalArray func(f *Frame) ArrayValue;
|
||||||
evalPtr func(f *Frame) Value;
|
evalPtr func(f *Frame) Value;
|
||||||
evalFunc func(f *Frame) Func;
|
evalFunc func(f *Frame) Func;
|
||||||
|
evalMulti func(f *Frame) []Value;
|
||||||
// Evaluate to the "address of" this value; that is, the
|
// Evaluate to the "address of" this value; that is, the
|
||||||
// settable Value object. nil for expressions whose address
|
// settable Value object. nil for expressions whose address
|
||||||
// cannot be taken.
|
// cannot be taken.
|
||||||
@ -179,6 +180,13 @@ func (a *exprCompiler) asFunc() (func(f *Frame) Func) {
|
|||||||
return a.evalFunc;
|
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
|
* 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,
|
// a.convertTo(t) converts the value of the analyzed expression a,
|
||||||
// which must be a constant, ideal number, to a new analyzed
|
// which must be a constant, ideal number, to a new analyzed
|
||||||
// expression with a constant value of type t.
|
// expression with a constant value of type t.
|
||||||
|
//
|
||||||
|
// TODO(austin) Rename to resolveIdeal or something?
|
||||||
func (a *exprCompiler) convertTo(t Type) *exprCompiler {
|
func (a *exprCompiler) convertTo(t Type) *exprCompiler {
|
||||||
if !a.t.isIdeal() {
|
if !a.t.isIdeal() {
|
||||||
log.Crashf("attempted to convert from %v, expected ideal", a.t);
|
log.Crashf("attempted to convert from %v, expected ideal", a.t);
|
||||||
@ -256,66 +266,193 @@ func (a *exprCompiler) convertTo(t Type) *exprCompiler {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mkAssign takes an optional expected l-value type, lt, and an
|
/*
|
||||||
// r-value expression compiler, r, and returns the expected l-value
|
* Assignments
|
||||||
// type and a function that evaluates the r-value and assigns it to
|
*/
|
||||||
// the l-value lv.
|
|
||||||
|
// An assignCompiler compiles assignment operations. Anything other
|
||||||
|
// than short declarations should use the compileAssign wrapper.
|
||||||
//
|
//
|
||||||
// If lt is non-nil, the returned l-value type will always be lt. If
|
// There are three valid types of assignment:
|
||||||
// lt is nil, mkAssign will infer and return the appropriate l-value
|
// 1) T = T
|
||||||
// type, or produce an error.
|
// Assigning a single expression with single-valued type to a
|
||||||
//
|
// single-valued type.
|
||||||
// errOp specifies the operation name to use for error messages, such
|
// 2) MT = T, T, ...
|
||||||
// as "assignment", or "function call". errPosName specifies the name
|
// Assigning multiple expressions with single-valued types to a
|
||||||
// to use for positions. errPos, if non-zero, specifies the position
|
// multi-valued type.
|
||||||
// of this assignment (for tuple assignments or function arguments).
|
// 3) MT = MT
|
||||||
//
|
// Assigning a single expression with multi-valued type to a
|
||||||
// If the assignment fails to typecheck, this generates an error
|
// multi-valued type.
|
||||||
// message and returns nil, nil.
|
type assignCompiler struct {
|
||||||
func mkAssign(lt Type, r *exprCompiler, errOp string, errPosName string, errPos int) (Type, func(lv Value, f *Frame)) {
|
*compiler;
|
||||||
// However, when [an ideal is] (used in an expression)
|
pos token.Position;
|
||||||
// assigned to a variable or typed constant, the destination
|
// The RHS expressions. This may include nil's for
|
||||||
// must be able to represent the assigned value.
|
// expressions that failed to compile.
|
||||||
if r.t.isIdeal() && (lt == nil || lt.isInteger() || lt.isFloat()) {
|
rs []*exprCompiler;
|
||||||
// If the type is absent and the corresponding
|
// The (possibly unary) MultiType of the RHS.
|
||||||
// expression is a constant expression of ideal
|
rmt *MultiType;
|
||||||
// integer or ideal float type, the type of the
|
// Whether this is an unpack assignment (case 3).
|
||||||
// declared variable is int or float respectively.
|
isUnpack bool;
|
||||||
if lt == nil {
|
// The operation name to use in error messages, such as
|
||||||
switch {
|
// "assignment" or "function call".
|
||||||
case r.t.isInteger():
|
errOp string;
|
||||||
lt = IntType;
|
// The name to use for positions in error messages, such as
|
||||||
case r.t.isFloat():
|
// "argument".
|
||||||
lt = FloatType;
|
errPosName string;
|
||||||
default:
|
}
|
||||||
log.Crashf("unexpected ideal type %v", r.t);
|
|
||||||
}
|
// Type check the RHS of an assignment, returning a new assignCompiler
|
||||||
}
|
// and indicating if the type check succeeded. This always returns an
|
||||||
r = r.convertTo(lt);
|
// assignCompiler with rmt set, but if type checking fails, slots in
|
||||||
if r == nil {
|
// the MultiType may be nil. If rs contains nil's, type checking will
|
||||||
return nil, nil;
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TOOD(austin) Deal with assignment special cases
|
// Create MultiType for RHS and check that all RHS expressions
|
||||||
|
// are single-valued.
|
||||||
if lt == nil {
|
rts := make([]Type, len(rs));
|
||||||
lt = r.t;
|
ok := true;
|
||||||
} else {
|
for i, r := range rs {
|
||||||
// Values of any type may always be assigned to
|
if r == nil {
|
||||||
// variables of compatible static type.
|
ok = false;
|
||||||
if lt.literal() != r.t.literal() {
|
continue;
|
||||||
if errPos == 0 {
|
|
||||||
r.diag("illegal operand types for %s\n\t%v\n\t%v", errOp, lt, r.t);
|
|
||||||
} else {
|
|
||||||
r.diag("illegal operand types in %s %d of %s\n\t%v\n\t%v", errPosName, errPos, errOp, lt, r.t);
|
|
||||||
}
|
|
||||||
return nil, nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, isMT := r.t.(*MultiType); isMT {
|
||||||
|
r.diag("multi-valued expression not allowed in %s", errOp);
|
||||||
|
ok = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rts[i] = r.t;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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 {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
bad = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bad {
|
||||||
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile
|
// 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);
|
a.diag("variable %s used in constant expression", x.Value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if def.Type == nil {
|
||||||
|
// Placeholder definition from an earlier error
|
||||||
|
return;
|
||||||
|
}
|
||||||
a.t = def.Type;
|
a.t = def.Type;
|
||||||
defidx := def.Index;
|
defidx := def.Index;
|
||||||
a.genIdentOp(dscope, defidx);
|
a.genIdentOp(dscope, defidx);
|
||||||
@ -611,51 +752,37 @@ func (a *exprCompiler) DoCallExpr(x *ast.CallExpr) {
|
|||||||
return;
|
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
|
// The arguments must be single-valued expressions assignment
|
||||||
// compatible with the parameters of F.
|
// compatible with the parameters of F.
|
||||||
afs := make([]func(lv Value, f *Frame), len(as));
|
//
|
||||||
for i := 0; i < len(as); i++ {
|
// XXX(Spec) The spec is wrong. It can also be a single
|
||||||
var at Type;
|
// multi-valued expression.
|
||||||
at, afs[i] = mkAssign(lt.In[i], as[i], "function call", "argument", i + 1);
|
assign := a.compileAssign(x.Pos(), NewMultiType(lt.In), as, "function call", "argument");
|
||||||
if at == nil {
|
if assign == nil {
|
||||||
bad = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if bad {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nResults := len(lt.Out);
|
nout := len(lt.Out);
|
||||||
if nResults != 1 {
|
switch nout {
|
||||||
log.Crashf("Multi-valued return type not implemented");
|
case 0:
|
||||||
|
a.t = EmptyType;
|
||||||
|
case 1:
|
||||||
|
a.t = lt.Out[0];
|
||||||
|
default:
|
||||||
|
a.t = NewMultiType(lt.Out);
|
||||||
}
|
}
|
||||||
a.t = lt.Out[0];
|
|
||||||
|
|
||||||
// Compile
|
// Compile
|
||||||
lf := l.asFunc();
|
lf := l.asFunc();
|
||||||
|
nin := len(lt.In);
|
||||||
call := func(f *Frame) []Value {
|
call := func(f *Frame) []Value {
|
||||||
fun := lf(f);
|
fun := lf(f);
|
||||||
fr := fun.NewFrame();
|
fr := fun.NewFrame();
|
||||||
for i, af := range afs {
|
assign(multiV(fr.Vars[0:nin]), f);
|
||||||
af(fr.Vars[i], f);
|
|
||||||
}
|
|
||||||
fun.Call(fr);
|
fun.Call(fr);
|
||||||
return fr.Vars[len(afs):len(afs)+nResults];
|
return fr.Vars[nin:nin+nout];
|
||||||
};
|
};
|
||||||
a.genFuncCall(call);
|
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) {
|
func (a *exprCompiler) DoStarExpr(x *ast.StarExpr) {
|
||||||
@ -1150,9 +1277,9 @@ func (a *exprCompiler) extractEffect() (func(f *Frame), *exprCompiler) {
|
|||||||
addr.t = tempType;
|
addr.t = tempType;
|
||||||
addr.genUnaryAddrOf(a);
|
addr.genUnaryAddrOf(a);
|
||||||
|
|
||||||
_, assign := mkAssign(tempType, addr, "", "", 0);
|
assign := a.compileAssign(a.pos, tempType, []*exprCompiler{addr}, "", "");
|
||||||
if assign == nil {
|
if assign == nil {
|
||||||
log.Crashf("extractEffect: mkAssign type check failed");
|
log.Crashf("compileAssign type check failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
effect := func(f *Frame) {
|
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) {
|
func (a *exprCompiler) genFuncCall(call func(f *Frame) []Value) {
|
||||||
|
a.exec = func(f *Frame) { call(f) };
|
||||||
switch _ := a.t.rep().(type) {
|
switch _ := a.t.rep().(type) {
|
||||||
case *boolType:
|
case *boolType:
|
||||||
a.evalBool = func(f *Frame) bool { return call(f)[0].(BoolValue).Get() };
|
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() };
|
a.evalPtr = func(f *Frame) Value { return call(f)[0].(PtrValue).Get() };
|
||||||
case *FuncType:
|
case *FuncType:
|
||||||
a.evalFunc = func(f *Frame) Func { return call(f)[0].(FuncValue).Get() };
|
a.evalFunc = func(f *Frame) Func { return call(f)[0].(FuncValue).Get() };
|
||||||
|
case *MultiType:
|
||||||
|
a.evalMulti = func(f *Frame) []Value { return call(f) };
|
||||||
default:
|
default:
|
||||||
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
|
log.Crashf("unexpected result type %v at %v", a.t, a.pos);
|
||||||
}
|
}
|
||||||
|
@ -91,21 +91,27 @@ func (a *stmtCompiler) doAssign(s *ast.AssignStmt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the assignment count
|
errOp := "assignment";
|
||||||
if len(s.Lhs) != len(s.Rhs) {
|
if s.Tok == token.DEFINE {
|
||||||
log.Crashf("Unbalanced assignment not implemented %v %v %v", len(s.Lhs), s.Tok, len(s.Rhs));
|
errOp = "definition";
|
||||||
|
}
|
||||||
|
ac, ok := a.checkAssign(s.Pos(), rs, "assignment", "value");
|
||||||
|
if !ok {
|
||||||
|
bad = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile left side and generate assigners
|
// 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
|
||||||
ls := make([]*exprCompiler, len(s.Lhs));
|
ls := make([]*exprCompiler, len(s.Lhs));
|
||||||
as := make([]func(lv Value, f *Frame), len(s.Lhs));
|
|
||||||
nDefs := 0;
|
nDefs := 0;
|
||||||
for i, le := range s.Lhs {
|
for i, le := range s.Lhs {
|
||||||
errPos := i + 1;
|
|
||||||
if len(s.Lhs) == 1 {
|
|
||||||
errPos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Tok == token.DEFINE {
|
if s.Tok == token.DEFINE {
|
||||||
// Check that it's an identifier
|
// Check that it's an identifier
|
||||||
ident, ok := le.(*ast.Ident);
|
ident, ok := le.(*ast.Ident);
|
||||||
@ -123,17 +129,39 @@ func (a *stmtCompiler) doAssign(s *ast.AssignStmt) {
|
|||||||
}
|
}
|
||||||
nDefs++;
|
nDefs++;
|
||||||
|
|
||||||
if rs[i] == nil {
|
// Compute the identifier's type from the RHS
|
||||||
// TODO(austin) Define a placeholder.
|
// type. We use the computed MultiType so we
|
||||||
continue;
|
// don't have to worry about unpacking.
|
||||||
}
|
|
||||||
|
|
||||||
// Generate assigner and get type
|
|
||||||
var lt Type;
|
var lt Type;
|
||||||
lt, as[i] = mkAssign(nil, rs[i], "assignment", "position", errPos);
|
switch {
|
||||||
if lt == nil {
|
case i >= len(ac.rmt.Elems):
|
||||||
bad = true;
|
// Define a placeholder. We already
|
||||||
continue;
|
// 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
|
// Define identifier
|
||||||
@ -155,16 +183,6 @@ func (a *stmtCompiler) doAssign(s *ast.AssignStmt) {
|
|||||||
bad = true;
|
bad = true;
|
||||||
continue;
|
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
|
// A short variable declaration may redeclare variables
|
||||||
@ -180,23 +198,58 @@ func (a *stmtCompiler) doAssign(s *ast.AssignStmt) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create assigner
|
||||||
|
var lt Type;
|
||||||
n := len(s.Lhs);
|
n := len(s.Lhs);
|
||||||
if n == 1 {
|
if n == 1 {
|
||||||
lf := ls[0].evalAddr;
|
lt = ls[0].t;
|
||||||
assign := as[0];
|
|
||||||
a.push(func(v *vm) { assign(lf(v.f), v.f) });
|
|
||||||
} else {
|
} else {
|
||||||
a.push(func(v *vm) {
|
lts := make([]Type, len(ls));
|
||||||
temps := make([]Value, n);
|
for i, l := range ls {
|
||||||
// Assign to temporaries
|
if l != nil {
|
||||||
for i := 0; i < n; i++ {
|
lts[i] = l.t;
|
||||||
// TODO(austin) Don't capture ls
|
|
||||||
temps[i] = ls[i].t.Zero();
|
|
||||||
as[i](temps[i], v.f);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
a.push(func(v *vm) { assign(lf(v.f), v.f) });
|
||||||
|
} 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) {
|
||||||
|
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
|
// Copy to destination
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i ++ {
|
||||||
ls[i].evalAddr(v.f).Assign(temps[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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_, assign := mkAssign(l.t, binop, "assignment", "", 0);
|
assign := a.compileAssign(s.Pos(), l.t, []*exprCompiler{binop}, "assignment", "value");
|
||||||
if assign == nil {
|
if assign == nil {
|
||||||
return;
|
log.Crashf("compileAssign type check failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
lf := l.evalAddr;
|
lf := l.evalAddr;
|
||||||
@ -280,58 +333,46 @@ func (a *stmtCompiler) DoReturnStmt(s *ast.ReturnStmt) {
|
|||||||
// return statement.
|
// return statement.
|
||||||
a.returned = true;
|
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.
|
// Simple case. Simply exit from the function.
|
||||||
a.push(func(v *vm) { v.pc = ^uint(0) });
|
a.push(func(v *vm) { v.pc = ^uint(0) });
|
||||||
a.err = false;
|
a.err = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(austin) Might be a call of a multi-valued function.
|
// Compile expressions
|
||||||
// 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
|
|
||||||
bad := false;
|
bad := false;
|
||||||
rs := make([]*exprCompiler, len(s.Results));
|
rs := make([]*exprCompiler, len(s.Results));
|
||||||
as := make([]func(lv Value, f *Frame), len(s.Results));
|
|
||||||
for i, re := range s.Results {
|
for i, re := range s.Results {
|
||||||
rs[i] = a.compileExpr(a.scope, re, false);
|
rs[i] = a.compileExpr(a.scope, re, false);
|
||||||
if rs[i] == nil {
|
if rs[i] == nil {
|
||||||
bad = true;
|
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 {
|
if bad {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save indexes of return values
|
// Create assigner
|
||||||
idxs := make([]int, len(s.Results));
|
|
||||||
for i, outVar := range a.outVars {
|
// However, if the expression list in the "return" statement
|
||||||
idxs[i] = outVar.Index;
|
// 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
|
// Compile
|
||||||
|
start := len(a.fnType.In);
|
||||||
|
nout := len(a.fnType.Out);
|
||||||
a.push(func(v *vm) {
|
a.push(func(v *vm) {
|
||||||
for i, assign := range as {
|
assign(multiV(v.activation.Vars[start:start+nout]), v.f);
|
||||||
assign(v.activation.Vars[idxs[i]], v.f);
|
|
||||||
}
|
|
||||||
v.pc = ^uint(0);
|
v.pc = ^uint(0);
|
||||||
});
|
});
|
||||||
a.err = false;
|
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 {
|
for i, t := range decl.Type.In {
|
||||||
bodyScope.DefineVar(decl.InNames[i].Value, t);
|
bodyScope.DefineVar(decl.InNames[i].Value, t);
|
||||||
}
|
}
|
||||||
outVars := make([]*Variable, len(decl.Type.Out));
|
|
||||||
for i, t := range decl.Type.Out {
|
for i, t := range decl.Type.Out {
|
||||||
if decl.OutNames[i] != nil {
|
if decl.OutNames[i] != nil {
|
||||||
outVars[i] = bodyScope.DefineVar(decl.OutNames[i].Value, t);
|
bodyScope.DefineVar(decl.OutNames[i].Value, t);
|
||||||
} else {
|
} else {
|
||||||
// TODO(austin) It would be nice to have a
|
// TODO(austin) Not technically a temp
|
||||||
// better way to define unnamed slots.
|
bodyScope.DefineTemp(t);
|
||||||
outVars[i] = bodyScope.DefineVar(":out" + strconv.Itoa(i), t);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create block context
|
// 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 {
|
if len(decl.OutNames) > 0 && decl.OutNames[0] != nil {
|
||||||
fc.outVarsNamed = true;
|
fc.outVarsNamed = true;
|
||||||
}
|
}
|
||||||
|
@ -45,8 +45,11 @@ type typeArrayMap map[uintptr] *typeArrayMapEntry
|
|||||||
func hashTypeArray(key []Type) uintptr {
|
func hashTypeArray(key []Type) uintptr {
|
||||||
hash := uintptr(0);
|
hash := uintptr(0);
|
||||||
for _, t := range key {
|
for _, t := range key {
|
||||||
addr := reflect.NewValue(t).Addr();
|
|
||||||
hash = hash * 33;
|
hash = hash * 33;
|
||||||
|
if t == nil {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addr := reflect.NewValue(t).Addr();
|
||||||
hash ^= addr;
|
hash ^= addr;
|
||||||
}
|
}
|
||||||
return hash;
|
return hash;
|
||||||
@ -153,7 +156,6 @@ type uintType struct {
|
|||||||
Bits uint;
|
Bits uint;
|
||||||
// true for uintptr, false for all others
|
// true for uintptr, false for all others
|
||||||
Ptr bool;
|
Ptr bool;
|
||||||
|
|
||||||
name string;
|
name string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +226,6 @@ type intType struct {
|
|||||||
|
|
||||||
// 0 for architecture-dependent types
|
// 0 for architecture-dependent types
|
||||||
Bits uint;
|
Bits uint;
|
||||||
|
|
||||||
name string;
|
name string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,10 +438,8 @@ func (t *stringType) Zero() Value
|
|||||||
|
|
||||||
type ArrayType struct {
|
type ArrayType struct {
|
||||||
commonType;
|
commonType;
|
||||||
|
|
||||||
Len int64;
|
Len int64;
|
||||||
Elem Type;
|
Elem Type;
|
||||||
|
|
||||||
lit Type;
|
lit Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,7 +594,12 @@ func typeListString(ts []Type, ns []*ast.Ident) string {
|
|||||||
if ns != nil && ns[i] != nil {
|
if ns != nil && ns[i] != nil {
|
||||||
s += ns[i].Value + " ";
|
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;
|
return s;
|
||||||
}
|
}
|
||||||
@ -708,3 +712,55 @@ func (t *NamedType) String() string {
|
|||||||
func (t *NamedType) Zero() Value {
|
func (t *NamedType) Zero() Value {
|
||||||
return t.def.Zero();
|
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
|
||||||
|
@ -536,6 +536,38 @@ func (t *FuncType) Zero() Value {
|
|||||||
return &funcV{nil};
|
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
|
* Universal constants
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user