summaryrefslogtreecommitdiff
path: root/src/pkg/exp/eval/expr.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/exp/eval/expr.go')
-rw-r--r--src/pkg/exp/eval/expr.go2007
1 files changed, 2007 insertions, 0 deletions
diff --git a/src/pkg/exp/eval/expr.go b/src/pkg/exp/eval/expr.go
new file mode 100644
index 000000000..ea4fc082b
--- /dev/null
+++ b/src/pkg/exp/eval/expr.go
@@ -0,0 +1,2007 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package eval
+
+import (
+ "bignum";
+ "go/ast";
+ "go/token";
+ "log";
+ "strconv";
+ "strings";
+ "os";
+)
+
+// An expr is the result of compiling an expression. It stores the
+// type of the expression and its evaluator function.
+type expr struct {
+ *exprInfo;
+ t Type;
+
+ // Evaluate this node as the given type.
+ eval interface{};
+
+ // Map index expressions permit special forms of assignment,
+ // for which we need to know the Map and key.
+ evalMapValue func(t *Thread) (Map, interface{});
+
+ // Evaluate to the "address of" this value; that is, the
+ // settable Value object. nil for expressions whose address
+ // cannot be taken.
+ evalAddr func(t *Thread) Value;
+
+ // Execute this expression as a statement. Only expressions
+ // that are valid expression statements should set this.
+ exec func(t *Thread);
+
+ // If this expression is a type, this is its compiled type.
+ // This is only permitted in the function position of a call
+ // expression. In this case, t should be nil.
+ valType Type;
+
+ // A short string describing this expression for error
+ // messages.
+ desc string;
+}
+
+// exprInfo stores information needed to compile any expression node.
+// Each expr also stores its exprInfo so further expressions can be
+// compiled from it.
+type exprInfo struct {
+ *compiler;
+ pos token.Position;
+}
+
+func (a *exprInfo) newExpr(t Type, desc string) *expr {
+ return &expr{exprInfo: a, t: t, desc: desc};
+}
+
+func (a *exprInfo) diag(format string, args ...) {
+ a.diagAt(&a.pos, format, args);
+}
+
+func (a *exprInfo) diagOpType(op token.Token, vt Type) {
+ a.diag("illegal operand type for '%v' operator\n\t%v", op, vt);
+}
+
+func (a *exprInfo) diagOpTypes(op token.Token, lt Type, rt Type) {
+ a.diag("illegal operand types for '%v' operator\n\t%v\n\t%v", op, lt, rt);
+}
+
+/*
+ * Common expression manipulations
+ */
+
+// 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 *expr) convertTo(t Type) *expr {
+ if !a.t.isIdeal() {
+ log.Crashf("attempted to convert from %v, expected ideal", a.t);
+ }
+
+ var rat *bignum.Rational;
+
+ // XXX(Spec) The spec says "It is erroneous".
+ //
+ // It is an error to assign a value with a non-zero fractional
+ // part to an integer, or if the assignment would overflow or
+ // underflow, or in general if the value cannot be represented
+ // by the type of the variable.
+ switch a.t {
+ case IdealFloatType:
+ rat = a.asIdealFloat()();
+ if t.isInteger() && !rat.IsInt() {
+ a.diag("constant %v truncated to integer", ratToString(rat));
+ return nil;
+ }
+ case IdealIntType:
+ i := a.asIdealInt()();
+ rat = bignum.MakeRat(i, bignum.Nat(1));
+ default:
+ log.Crashf("unexpected ideal type %v", a.t);
+ }
+
+ // Check bounds
+ if t, ok := t.lit().(BoundedType); ok {
+ if rat.Cmp(t.minVal()) < 0 {
+ a.diag("constant %v underflows %v", ratToString(rat), t);
+ return nil;
+ }
+ if rat.Cmp(t.maxVal()) > 0 {
+ a.diag("constant %v overflows %v", ratToString(rat), t);
+ return nil;
+ }
+ }
+
+ // Convert rat to type t.
+ res := a.newExpr(t, a.desc);
+ switch t := t.lit().(type) {
+ case *uintType:
+ n, d := rat.Value();
+ f := n.Quo(bignum.MakeInt(false, d));
+ v := f.Abs().Value();
+ res.eval = func(*Thread) uint64 { return v };
+ case *intType:
+ n, d := rat.Value();
+ f := n.Quo(bignum.MakeInt(false, d));
+ v := f.Value();
+ res.eval = func(*Thread) int64 { return v };
+ case *idealIntType:
+ n, d := rat.Value();
+ f := n.Quo(bignum.MakeInt(false, d));
+ res.eval = func() *bignum.Integer { return f };
+ case *floatType:
+ n, d := rat.Value();
+ v := float64(n.Value())/float64(d.Value());
+ res.eval = func(*Thread) float64 { return v };
+ case *idealFloatType:
+ res.eval = func() *bignum.Rational { return rat };
+ default:
+ log.Crashf("cannot convert to type %T", t);
+ }
+
+ return res;
+}
+
+// convertToInt converts this expression to an integer, if possible,
+// or produces an error if not. This accepts ideal ints, uints, and
+// ints. If max is not -1, produces an error if possible if the value
+// exceeds max. If negErr is not "", produces an error if possible if
+// the value is negative.
+func (a *expr) convertToInt(max int64, negErr string, errOp string) *expr {
+ switch a.t.lit().(type) {
+ case *idealIntType:
+ val := a.asIdealInt()();
+ if negErr != "" && val.IsNeg() {
+ a.diag("negative %s: %s", negErr, val);
+ return nil;
+ }
+ bound := max;
+ if negErr == "slice" {
+ bound++;
+ }
+ if max != -1 && val.Cmp(bignum.Int(bound)) >= 0 {
+ a.diag("index %s exceeds length %d", val, max);
+ return nil;
+ }
+ return a.convertTo(IntType);
+
+ case *uintType:
+ // Convert to int
+ na := a.newExpr(IntType, a.desc);
+ af := a.asUint();
+ na.eval = func(t *Thread) int64 {
+ return int64(af(t));
+ };
+ return na;
+
+ case *intType:
+ // Good as is
+ return a;
+ }
+
+ a.diag("illegal operand type for %s\n\t%v", errOp, a.t);
+ return nil;
+}
+
+// derefArray returns an expression of array type if the given
+// expression is a *array type. Otherwise, returns the given
+// expression.
+func (a *expr) derefArray() *expr {
+ if pt, ok := a.t.lit().(*PtrType); ok {
+ if _, ok := pt.Elem.lit().(*ArrayType); ok {
+ deref := a.compileStarExpr(a);
+ if deref == nil {
+ log.Crashf("failed to dereference *array");
+ }
+ return deref;
+ }
+ }
+ return a;
+}
+
+/*
+ * Assignments
+ */
+
+// An assignCompiler compiles assignment operations. Anything other
+// than short declarations should use the compileAssign wrapper.
+//
+// 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 []*expr;
+ // The (possibly unary) MultiType of the RHS.
+ rmt *MultiType;
+ // Whether this is an unpack assignment (case 3).
+ isUnpack bool;
+ // Whether map special assignment forms are allowed.
+ allowMap bool;
+ // Whether this is a "r, ok = a[x]" assignment.
+ isMapUnpack 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 []*expr, 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;
+ }
+ }
+
+ // 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 {
+ 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;
+ }
+
+ c.rmt = NewMultiType(rts);
+ return c, ok;
+}
+
+func (a *assignCompiler) allowMapForms(nls int) {
+ a.allowMap = true;
+
+ // Update unpacking info if this is r, ok = a[x]
+ if nls == 2 && len(a.rs) == 1 && a.rs[0] != nil && a.rs[0].evalMapValue != nil {
+ a.isUnpack = true;
+ a.rmt = NewMultiType([]Type {a.rs[0].t, BoolType});
+ a.isMapUnpack = true;
+ }
+}
+
+// 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(b *block, lt Type) (func(Value, *Thread)) {
+ 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;
+
+ // If this is an unpack, create a temporary to store the
+ // multi-value and replace the RHS with expressions to pull
+ // out values from the temporary. Technically, this is only
+ // necessary when we need to perform assignment conversions.
+ var effect func(*Thread);
+ if isUnpack {
+ // This leaks a slot, but is definitely safe.
+ temp := b.DefineTemp(a.rmt);
+ tempIdx := temp.Index;
+ if tempIdx < 0 {
+ panicln("tempidx", tempIdx);
+ }
+ if a.isMapUnpack {
+ rf := a.rs[0].evalMapValue;
+ vt := a.rmt.Elems[0];
+ effect = func(t *Thread) {
+ m, k := rf(t);
+ v := m.Elem(t, k);
+ found := boolV(true);
+ if v == nil {
+ found = boolV(false);
+ v = vt.Zero();
+ }
+ t.f.Vars[tempIdx] = multiV([]Value {v, &found});
+ };
+ } else {
+ rf := a.rs[0].asMulti();
+ effect = func(t *Thread) {
+ t.f.Vars[tempIdx] = multiV(rf(t));
+ };
+ }
+ orig := a.rs[0];
+ a.rs = make([]*expr, len(a.rmt.Elems));
+ for i, t := range a.rmt.Elems {
+ if t.isIdeal() {
+ log.Crashf("Right side of unpack contains ideal: %s", rmt);
+ }
+ a.rs[i] = orig.newExpr(t, orig.desc);
+ index := i;
+ a.rs[i].genValue(func(t *Thread) Value { return t.f.Vars[tempIdx].(multiV)[index] });
+ }
+ }
+ // Now len(a.rs) == len(a.rmt) and we've reduced any unpacking
+ // to multi-assignment.
+
+ // TODO(austin) Deal with assignment special cases.
+
+ // Values of any type may always be assigned to variables of
+ // compatible static type.
+ for i, lt := range lmt.Elems {
+ 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() {
+ a.rs[i] = a.rs[i].convertTo(lmt.Elems[i]);
+ if a.rs[i] == nil {
+ bad = true;
+ continue;
+ }
+ rt = a.rs[i].t;
+ }
+
+ // A pointer p to an array can be assigned to a slice
+ // variable v with compatible element type if the type
+ // of p or v is unnamed.
+ if rpt, ok := rt.lit().(*PtrType); ok {
+ if at, ok := rpt.Elem.lit().(*ArrayType); ok {
+ if lst, ok := lt.lit().(*SliceType); ok {
+ if lst.Elem.compat(at.Elem, false) && (rt.lit() == Type(rt) || lt.lit() == Type(lt)) {
+ rf := a.rs[i].asPtr();
+ a.rs[i] = a.rs[i].newExpr(lt, a.rs[i].desc);
+ len := at.Len;
+ a.rs[i].eval = func(t *Thread) Slice {
+ return Slice{rf(t).(ArrayValue), len, len};
+ };
+ rt = a.rs[i].t;
+ }
+ }
+ }
+ }
+
+ if !lt.compat(rt, false) {
+ 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
+ if !isMT {
+ // Case 1
+ return genAssign(lt, a.rs[0]);
+ }
+ // Case 2 or 3
+ as := make([]func(lv Value, t *Thread), len(a.rs));
+ for i, r := range a.rs {
+ as[i] = genAssign(lmt.Elems[i], r);
+ }
+ return func(lv Value, t *Thread) {
+ if effect != nil {
+ effect(t);
+ }
+ lmv := lv.(multiV);
+ for i, a := range as {
+ a(lmv[i], t);
+ }
+ };
+}
+
+// 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, b *block, lt Type, rs []*expr, errOp, errPosName string) (func(Value, *Thread)) {
+ ac, ok := a.checkAssign(pos, rs, errOp, errPosName);
+ if !ok {
+ return nil;
+ }
+ return ac.compile(b, lt);
+}
+
+/*
+ * Expression compiler
+ */
+
+// An exprCompiler stores information used throughout the compilation
+// of a single expression. It does not embed funcCompiler because
+// expressions can appear at top level.
+type exprCompiler struct {
+ *compiler;
+ // The block this expression is being compiled in.
+ block *block;
+ // Whether this expression is used in a constant context.
+ constant bool;
+}
+
+// compile compiles an expression AST. callCtx should be true if this
+// AST is in the function position of a function call node; it allows
+// the returned expression to be a type or a built-in function (which
+// otherwise result in errors).
+func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr {
+ ei := &exprInfo{a.compiler, x.Pos()};
+
+ switch x := x.(type) {
+ // Literals
+ case *ast.BasicLit:
+ switch x.Kind {
+ case token.INT:
+ return ei.compileIntLit(string(x.Value));
+ case token.FLOAT:
+ return ei.compileFloatLit(string(x.Value));
+ case token.CHAR:
+ return ei.compileCharLit(string(x.Value));
+ case token.STRING:
+ return ei.compileStringLit(string(x.Value));
+ default:
+ log.Crashf("unexpected basic literal type %v", x.Kind);
+ }
+
+ case *ast.CompositeLit:
+ goto notimpl;
+
+ case *ast.FuncLit:
+ decl := ei.compileFuncType(a.block, x.Type);
+ if decl == nil {
+ // TODO(austin) Try compiling the body,
+ // perhaps with dummy argument definitions
+ return nil;
+ }
+ fn := ei.compileFunc(a.block, decl, x.Body);
+ if fn == nil {
+ return nil;
+ }
+ if a.constant {
+ a.diagAt(x, "function literal used in constant expression");
+ return nil;
+ }
+ return ei.compileFuncLit(decl, fn);
+
+ // Types
+ case *ast.ArrayType:
+ // TODO(austin) Use a multi-type case
+ goto typeexpr;
+
+ case *ast.ChanType:
+ goto typeexpr;
+
+ case *ast.Ellipsis:
+ goto typeexpr;
+
+ case *ast.FuncType:
+ goto typeexpr;
+
+ case *ast.InterfaceType:
+ goto typeexpr;
+
+ case *ast.MapType:
+ goto typeexpr;
+
+ // Remaining expressions
+ case *ast.BadExpr:
+ // Error already reported by parser
+ a.silentErrors++;
+ return nil;
+
+ case *ast.BinaryExpr:
+ l, r := a.compile(x.X, false), a.compile(x.Y, false);
+ if l == nil || r == nil {
+ return nil;
+ }
+ return ei.compileBinaryExpr(x.Op, l, r);
+
+ case *ast.CallExpr:
+ l := a.compile(x.Fun, true);
+ args := make([]*expr, len(x.Args));
+ bad := false;
+ for i, arg := range x.Args {
+ if i == 0 && l != nil && (l.t == Type(makeType) || l.t == Type(newType)) {
+ argei := &exprInfo{a.compiler, arg.Pos()};
+ args[i] = argei.exprFromType(a.compileType(a.block, arg));
+ } else {
+ args[i] = a.compile(arg, false);
+ }
+ if args[i] == nil {
+ bad = true;
+ }
+ }
+ if bad || l == nil {
+ return nil;
+ }
+ if a.constant {
+ a.diagAt(x, "function call in constant context");
+ return nil;
+ }
+
+ if l.valType != nil {
+ a.diagAt(x, "type conversions not implemented");
+ return nil;
+ } else if ft, ok := l.t.(*FuncType); ok && ft.builtin != "" {
+ return ei.compileBuiltinCallExpr(a.block, ft, args);
+ } else {
+ return ei.compileCallExpr(a.block, l, args);
+ }
+
+ case *ast.Ident:
+ return ei.compileIdent(a.block, a.constant, callCtx, x.Value);
+
+ case *ast.IndexExpr:
+ if x.End != nil {
+ arr := a.compile(x.X, false);
+ lo := a.compile(x.Index, false);
+ hi := a.compile(x.End, false);
+ if arr == nil || lo == nil || hi == nil {
+ return nil;
+ }
+ return ei.compileSliceExpr(arr, lo, hi);
+ }
+ l, r := a.compile(x.X, false), a.compile(x.Index, false);
+ if l == nil || r == nil {
+ return nil;
+ }
+ return ei.compileIndexExpr(l, r);
+
+ case *ast.KeyValueExpr:
+ goto notimpl;
+
+ case *ast.ParenExpr:
+ return a.compile(x.X, callCtx);
+
+ case *ast.SelectorExpr:
+ v := a.compile(x.X, false);
+ if v == nil {
+ return nil;
+ }
+ return ei.compileSelectorExpr(v, x.Sel.Value);
+
+ case *ast.StarExpr:
+ // We pass down our call context because this could be
+ // a pointer type (and thus a type conversion)
+ v := a.compile(x.X, callCtx);
+ if v == nil {
+ return nil;
+ }
+ if v.valType != nil {
+ // Turns out this was a pointer type, not a dereference
+ return ei.exprFromType(NewPtrType(v.valType));
+ }
+ return ei.compileStarExpr(v);
+
+ case *ast.StringList:
+ strings := make([]*expr, len(x.Strings));
+ bad := false;
+ for i, s := range x.Strings {
+ strings[i] = a.compile(s, false);
+ if strings[i] == nil {
+ bad = true;
+ }
+ }
+ if bad {
+ return nil;
+ }
+ return ei.compileStringList(strings);
+
+ case *ast.StructType:
+ goto notimpl;
+
+ case *ast.TypeAssertExpr:
+ goto notimpl;
+
+ case *ast.UnaryExpr:
+ v := a.compile(x.X, false);
+ if v == nil {
+ return nil;
+ }
+ return ei.compileUnaryExpr(x.Op, v);
+ }
+ log.Crashf("unexpected ast node type %T", x);
+ panic();
+
+typeexpr:
+ if !callCtx {
+ a.diagAt(x, "type used as expression");
+ return nil;
+ }
+ return ei.exprFromType(a.compileType(a.block, x));
+
+notimpl:
+ a.diagAt(x, "%T expression node not implemented", x);
+ return nil;
+}
+
+func (a *exprInfo) exprFromType(t Type) *expr {
+ if t == nil {
+ return nil;
+ }
+ expr := a.newExpr(nil, "type");
+ expr.valType = t;
+ return expr;
+}
+
+func (a *exprInfo) compileIdent(b *block, constant bool, callCtx bool, name string) *expr {
+ bl, level, def := b.Lookup(name);
+ if def == nil {
+ a.diag("%s: undefined", name);
+ return nil;
+ }
+ switch def := def.(type) {
+ case *Constant:
+ expr := a.newExpr(def.Type, "constant");
+ if ft, ok := def.Type.(*FuncType); ok && ft.builtin != "" {
+ // XXX(Spec) I don't think anything says that
+ // built-in functions can't be used as values.
+ if !callCtx {
+ a.diag("built-in function %s cannot be used as a value", ft.builtin);
+ return nil;
+ }
+ // Otherwise, we leave the evaluators empty
+ // because this is handled specially
+ } else {
+ expr.genConstant(def.Value);
+ }
+ return expr;
+ case *Variable:
+ if constant {
+ a.diag("variable %s used in constant expression", name);
+ return nil;
+ }
+ if bl.global {
+ return a.compileGlobalVariable(def);
+ }
+ return a.compileVariable(level, def);
+ case Type:
+ if callCtx {
+ return a.exprFromType(def);
+ }
+ a.diag("type %v used as expression", name);
+ return nil;
+ }
+ log.Crashf("name %s has unknown type %T", name, def);
+ panic();
+}
+
+func (a *exprInfo) compileVariable(level int, v *Variable) *expr {
+ if v.Type == nil {
+ // Placeholder definition from an earlier error
+ a.silentErrors++;
+ return nil;
+ }
+ expr := a.newExpr(v.Type, "variable");
+ expr.genIdentOp(level, v.Index);
+ return expr;
+}
+
+func (a *exprInfo) compileGlobalVariable(v *Variable) *expr {
+ if v.Type == nil {
+ // Placeholder definition from an earlier error
+ a.silentErrors++;
+ return nil;
+ }
+ if v.Init == nil {
+ v.Init = v.Type.Zero();
+ }
+ expr := a.newExpr(v.Type, "variable");
+ val := v.Init;
+ expr.genValue(func(t *Thread) Value { return val });
+ return expr;
+}
+
+func (a *exprInfo) compileIdealInt(i *bignum.Integer, desc string) *expr {
+ expr := a.newExpr(IdealIntType, desc);
+ expr.eval = func() *bignum.Integer { return i };
+ return expr;
+}
+
+func (a *exprInfo) compileIntLit(lit string) *expr {
+ i, _, _ := bignum.IntFromString(lit, 0);
+ return a.compileIdealInt(i, "integer literal");
+}
+
+func (a *exprInfo) compileCharLit(lit string) *expr {
+ if lit[0] != '\'' {
+ // Caught by parser
+ a.silentErrors++;
+ return nil;
+ }
+ v, _, tail, err := strconv.UnquoteChar(lit[1:len(lit)], '\'');
+ if err != nil || tail != "'" {
+ // Caught by parser
+ a.silentErrors++;
+ return nil;
+ }
+ return a.compileIdealInt(bignum.Int(int64(v)), "character literal");
+}
+
+func (a *exprInfo) compileFloatLit(lit string) *expr {
+ f, _, n := bignum.RatFromString(lit, 0);
+ if n != len(lit) {
+ log.Crashf("malformed float literal %s at %v passed parser", lit, a.pos);
+ }
+ expr := a.newExpr(IdealFloatType, "float literal");
+ expr.eval = func() *bignum.Rational { return f };
+ return expr;
+}
+
+func (a *exprInfo) compileString(s string) *expr {
+ // Ideal strings don't have a named type but they are
+ // compatible with type string.
+
+ // TODO(austin) Use unnamed string type.
+ expr := a.newExpr(StringType, "string literal");
+ expr.eval = func(*Thread) string { return s };
+ return expr;
+}
+
+func (a *exprInfo) compileStringLit(lit string) *expr {
+ s, err := strconv.Unquote(lit);
+ if err != nil {
+ a.diag("illegal string literal, %v", err);
+ return nil;
+ }
+ return a.compileString(s);
+}
+
+func (a *exprInfo) compileStringList(list []*expr) *expr {
+ ss := make([]string, len(list));
+ for i, s := range list {
+ ss[i] = s.asString()(nil);
+ }
+ return a.compileString(strings.Join(ss, ""));
+}
+
+func (a *exprInfo) compileFuncLit(decl *FuncDecl, fn func(*Thread) Func) *expr {
+ expr := a.newExpr(decl.Type, "function literal");
+ expr.eval = fn;
+ return expr;
+}
+
+func (a *exprInfo) compileSelectorExpr(v *expr, name string) *expr {
+ // mark marks a field that matches the selector name. It
+ // tracks the best depth found so far and whether more than
+ // one field has been found at that depth.
+ bestDepth := -1;
+ ambig := false;
+ amberr := "";
+ mark := func(depth int, pathName string) {
+ switch {
+ case bestDepth == -1 || depth < bestDepth:
+ bestDepth = depth;
+ ambig = false;
+ amberr = "";
+
+ case depth == bestDepth:
+ ambig = true;
+
+ default:
+ log.Crashf("Marked field at depth %d, but already found one at depth %d", depth, bestDepth);
+ }
+ amberr += "\n\t" + pathName[1:len(pathName)];
+ };
+
+ visited := make(map[Type] bool);
+
+ // find recursively searches for the named field, starting at
+ // type t. If it finds the named field, it returns a function
+ // which takes an expr that represents a value of type 't' and
+ // returns an expr that retrieves the named field. We delay
+ // expr construction to avoid producing lots of useless expr's
+ // as we search.
+ //
+ // TODO(austin) Now that the expression compiler works on
+ // semantic values instead of AST's, there should be a much
+ // better way of doing this.
+ var find func(Type, int, string) (func (*expr) *expr);
+ find = func(t Type, depth int, pathName string) (func (*expr) *expr) {
+ // Don't bother looking if we've found something shallower
+ if bestDepth != -1 && bestDepth < depth {
+ return nil;
+ }
+
+ // Don't check the same type twice and avoid loops
+ if _, ok := visited[t]; ok {
+ return nil;
+ }
+ visited[t] = true;
+
+ // Implicit dereference
+ deref := false;
+ if ti, ok := t.(*PtrType); ok {
+ deref = true;
+ t = ti.Elem;
+ }
+
+ // If it's a named type, look for methods
+ if ti, ok := t.(*NamedType); ok {
+ _, ok := ti.methods[name];
+ if ok {
+ mark(depth, pathName + "." + name);
+ log.Crash("Methods not implemented");
+ }
+ t = ti.Def;
+ }
+
+ // If it's a struct type, check fields and embedded types
+ var builder func(*expr) *expr;
+ if t, ok := t.(*StructType); ok {
+ for i, f := range t.Elems {
+ var sub func(*expr) *expr;
+ switch {
+ case f.Name == name:
+ mark(depth, pathName + "." + name);
+ sub = func(e *expr) *expr { return e };
+
+ case f.Anonymous:
+ sub = find(f.Type, depth+1, pathName + "." + f.Name);
+ if sub == nil {
+ continue;
+ }
+
+ default:
+ continue;
+ }
+
+ // We found something. Create a
+ // builder for accessing this field.
+ ft := f.Type;
+ index := i;
+ builder = func(parent *expr) *expr {
+ if deref {
+ parent = a.compileStarExpr(parent);
+ }
+ expr := a.newExpr(ft, "selector expression");
+ pf := parent.asStruct();
+ evalAddr := func(t *Thread) Value {
+ return pf(t).Field(t, index);
+ };
+ expr.genValue(evalAddr);
+ return sub(expr);
+ };
+ }
+ }
+
+ return builder;
+ };
+
+ builder := find(v.t, 0, "");
+ if builder == nil {
+ a.diag("type %v has no field or method %s", v.t, name);
+ return nil;
+ }
+ if ambig {
+ a.diag("field %s is ambiguous in type %v%s", name, v.t, amberr);
+ return nil;
+ }
+
+ return builder(v);
+}
+
+func (a *exprInfo) compileSliceExpr(arr, lo, hi *expr) *expr {
+ // Type check object
+ arr = arr.derefArray();
+
+ var at Type;
+ var maxIndex int64 = -1;
+
+ switch lt := arr.t.lit().(type) {
+ case *ArrayType:
+ at = NewSliceType(lt.Elem);
+ maxIndex = lt.Len;
+
+ case *SliceType:
+ at = lt;
+
+ case *stringType:
+ at = lt;
+
+ default:
+ a.diag("cannot slice %v", arr.t);
+ return nil;
+ }
+
+ // Type check index and convert to int
+ // XXX(Spec) It's unclear if ideal floats with no
+ // fractional part are allowed here. 6g allows it. I
+ // believe that's wrong.
+ lo = lo.convertToInt(maxIndex, "slice", "slice");
+ hi = hi.convertToInt(maxIndex, "slice", "slice");
+ if lo == nil || hi == nil {
+ return nil;
+ }
+
+ expr := a.newExpr(at, "slice expression");
+
+ // Compile
+ lof := lo.asInt();
+ hif := hi.asInt();
+ switch lt := arr.t.lit().(type) {
+ case *ArrayType:
+ arrf := arr.asArray();
+ bound := lt.Len;
+ expr.eval = func(t *Thread) Slice {
+ arr, lo, hi := arrf(t), lof(t), hif(t);
+ if lo > hi || hi > bound || lo < 0 {
+ t.Abort(SliceError{lo, hi, bound});
+ }
+ return Slice{arr.Sub(lo, bound - lo), hi - lo, bound - lo}
+ };
+
+ case *SliceType:
+ arrf := arr.asSlice();
+ expr.eval = func(t *Thread) Slice {
+ arr, lo, hi := arrf(t), lof(t), hif(t);
+ if lo > hi || hi > arr.Cap || lo < 0 {
+ t.Abort(SliceError{lo, hi, arr.Cap});
+ }
+ return Slice{arr.Base.Sub(lo, arr.Cap - lo), hi - lo, arr.Cap - lo}
+ };
+
+ case *stringType:
+ arrf := arr.asString();
+ // TODO(austin) This pulls over the whole string in a
+ // remote setting, instead of creating a substring backed
+ // by remote memory.
+ expr.eval = func(t *Thread) string {
+ arr, lo, hi := arrf(t), lof(t), hif(t);
+ if lo > hi || hi > int64(len(arr)) || lo < 0 {
+ t.Abort(SliceError{lo, hi, int64(len(arr))});
+ }
+ return arr[lo:hi];
+ }
+
+ default:
+ log.Crashf("unexpected left operand type %T", arr.t.lit());
+ }
+
+ return expr;
+}
+
+func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
+ // Type check object
+ l = l.derefArray();
+
+ var at Type;
+ intIndex := false;
+ var maxIndex int64 = -1;
+
+ switch lt := l.t.lit().(type) {
+ case *ArrayType:
+ at = lt.Elem;
+ intIndex = true;
+ maxIndex = lt.Len;
+
+ case *SliceType:
+ at = lt.Elem;
+ intIndex = true;
+
+ case *stringType:
+ at = Uint8Type;
+ intIndex = true;
+
+ case *MapType:
+ at = lt.Elem;
+ if r.t.isIdeal() {
+ r = r.convertTo(lt.Key);
+ if r == nil {
+ return nil;
+ }
+ }
+ if !lt.Key.compat(r.t, false) {
+ a.diag("cannot use %s as index into %s", r.t, lt);
+ return nil;
+ }
+
+ default:
+ a.diag("cannot index into %v", l.t);
+ return nil;
+ }
+
+ // Type check index and convert to int if necessary
+ if intIndex {
+ // XXX(Spec) It's unclear if ideal floats with no
+ // fractional part are allowed here. 6g allows it. I
+ // believe that's wrong.
+ r = r.convertToInt(maxIndex, "index", "index");
+ if r == nil {
+ return nil;
+ }
+ }
+
+ expr := a.newExpr(at, "index expression");
+
+ // Compile
+ switch lt := l.t.lit().(type) {
+ case *ArrayType:
+ lf := l.asArray();
+ rf := r.asInt();
+ bound := lt.Len;
+ expr.genValue(func(t *Thread) Value {
+ l, r := lf(t), rf(t);
+ if r < 0 || r >= bound {
+ t.Abort(IndexError{r, bound});
+ }
+ return l.Elem(t, r);
+ });
+
+ case *SliceType:
+ lf := l.asSlice();
+ rf := r.asInt();
+ expr.genValue(func(t *Thread) Value {
+ l, r := lf(t), rf(t);
+ if l.Base == nil {
+ t.Abort(NilPointerError{});
+ }
+ if r < 0 || r >= l.Len {
+ t.Abort(IndexError{r, l.Len});
+ }
+ return l.Base.Elem(t, r);
+ });
+
+ case *stringType:
+ lf := l.asString();
+ rf := r.asInt();
+ // TODO(austin) This pulls over the whole string in a
+ // remote setting, instead of just the one character.
+ expr.eval = func(t *Thread) uint64 {
+ l, r := lf(t), rf(t);
+ if r < 0 || r >= int64(len(l)) {
+ t.Abort(IndexError{r, int64(len(l))});
+ }
+ return uint64(l[r]);
+ }
+
+ case *MapType:
+ lf := l.asMap();
+ rf := r.asInterface();
+ expr.genValue(func(t *Thread) Value {
+ m := lf(t);
+ k := rf(t);
+ if m == nil {
+ t.Abort(NilPointerError{});
+ }
+ e := m.Elem(t, k);
+ if e == nil {
+ t.Abort(KeyError{k});
+ }
+ return e;
+ });
+ // genValue makes things addressable, but map values
+ // aren't addressable.
+ expr.evalAddr = nil;
+ expr.evalMapValue = func(t *Thread) (Map, interface{}) {
+ // TODO(austin) Key check? nil check?
+ return lf(t), rf(t);
+ };
+
+ default:
+ log.Crashf("unexpected left operand type %T", l.t.lit());
+ }
+
+ return expr;
+}
+
+func (a *exprInfo) compileCallExpr(b *block, l *expr, as []*expr) *expr {
+ // TODO(austin) Variadic functions.
+
+ // Type check
+
+ // XXX(Spec) Calling a named function type is okay. I really
+ // think there needs to be a general discussion of named
+ // types. A named type creates a new, distinct type, but the
+ // type of that type is still whatever it's defined to. Thus,
+ // in "type Foo int", Foo is still an integer type and in
+ // "type Foo func()", Foo is a function type.
+ lt, ok := l.t.lit().(*FuncType);
+ if !ok {
+ a.diag("cannot call non-function type %v", l.t);
+ return nil;
+ }
+
+ // The arguments must be single-valued expressions assignment
+ // compatible with the parameters of F.
+ //
+ // XXX(Spec) The spec is wrong. It can also be a single
+ // multi-valued expression.
+ nin := len(lt.In);
+ assign := a.compileAssign(a.pos, b, NewMultiType(lt.In), as, "function call", "argument");
+ if assign == nil {
+ return nil;
+ }
+
+ var t Type;
+ nout := len(lt.Out);
+ switch nout {
+ case 0:
+ t = EmptyType;
+ case 1:
+ t = lt.Out[0];
+ default:
+ t = NewMultiType(lt.Out);
+ }
+ expr := a.newExpr(t, "function call");
+
+ // Gather argument and out types to initialize frame variables
+ vts := make([]Type, nin + nout);
+ for i, t := range lt.In {
+ vts[i] = t;
+ }
+ for i, t := range lt.Out {
+ vts[i+nin] = t;
+ }
+
+ // Compile
+ lf := l.asFunc();
+ call := func(t *Thread) []Value {
+ fun := lf(t);
+ fr := fun.NewFrame();
+ for i, t := range vts {
+ fr.Vars[i] = t.Zero();
+ }
+ assign(multiV(fr.Vars[0:nin]), t);
+ oldf := t.f;
+ t.f = fr;
+ fun.Call(t);
+ t.f = oldf;
+ return fr.Vars[nin:nin+nout];
+ };
+ expr.genFuncCall(call);
+
+ return expr;
+}
+
+func (a *exprInfo) compileBuiltinCallExpr(b *block, ft *FuncType, as []*expr) *expr {
+ checkCount := func(min, max int) bool {
+ if len(as) < min {
+ a.diag("not enough arguments to %s", ft.builtin);
+ return false;
+ } else if len(as) > max {
+ a.diag("too many arguments to %s", ft.builtin);
+ return false;
+ }
+ return true;
+ };
+
+ switch ft {
+ case capType:
+ if !checkCount(1, 1) {
+ return nil;
+ }
+ arg := as[0].derefArray();
+ expr := a.newExpr(IntType, "function call");
+ switch t := arg.t.lit().(type) {
+ case *ArrayType:
+ // TODO(austin) It would be nice if this could
+ // be a constant int.
+ v := t.Len;
+ expr.eval = func(t *Thread) int64 {
+ return v;
+ };
+
+ case *SliceType:
+ vf := arg.asSlice();
+ expr.eval = func(t *Thread) int64 {
+ return vf(t).Cap;
+ };
+
+ //case *ChanType:
+
+ default:
+ a.diag("illegal argument type for cap function\n\t%v", arg.t);
+ return nil;
+ }
+ return expr;
+
+ case lenType:
+ if !checkCount(1, 1) {
+ return nil;
+ }
+ arg := as[0].derefArray();
+ expr := a.newExpr(IntType, "function call");
+ switch t := arg.t.lit().(type) {
+ case *stringType:
+ vf := arg.asString();
+ expr.eval = func(t *Thread) int64 {
+ return int64(len(vf(t)));
+ };
+
+ case *ArrayType:
+ // TODO(austin) It would be nice if this could
+ // be a constant int.
+ v := t.Len;
+ expr.eval = func(t *Thread) int64 {
+ return v;
+ };
+
+ case *SliceType:
+ vf := arg.asSlice();
+ expr.eval = func(t *Thread) int64 {
+ return vf(t).Len;
+ };
+
+ case *MapType:
+ vf := arg.asMap();
+ expr.eval = func(t *Thread) int64 {
+ // XXX(Spec) What's the len of an
+ // uninitialized map?
+ m := vf(t);
+ if m == nil {
+ return 0;
+ }
+ return m.Len(t);
+ };
+
+ //case *ChanType:
+
+ default:
+ a.diag("illegal argument type for len function\n\t%v", arg.t);
+ return nil;
+ }
+ return expr;
+
+ case makeType:
+ if !checkCount(1, 3) {
+ return nil;
+ }
+ // XXX(Spec) What are the types of the
+ // arguments? Do they have to be ints? 6g
+ // accepts any integral type.
+ var lenexpr, capexpr *expr;
+ var lenf, capf func(*Thread) int64;
+ if len(as) > 1 {
+ lenexpr = as[1].convertToInt(-1, "length", "make function");
+ if lenexpr == nil {
+ return nil;
+ }
+ lenf = lenexpr.asInt();
+ }
+ if len(as) > 2 {
+ capexpr = as[2].convertToInt(-1, "capacity", "make function");
+ if capexpr == nil {
+ return nil;
+ }
+ capf = capexpr.asInt();
+ }
+
+ switch t := as[0].valType.lit().(type) {
+ case *SliceType:
+ // A new, initialized slice value for a given
+ // element type T is made using the built-in
+ // function make, which takes a slice type and
+ // parameters specifying the length and
+ // optionally the capacity.
+ if !checkCount(2, 3) {
+ return nil;
+ }
+ et := t.Elem;
+ expr := a.newExpr(t, "function call");
+ expr.eval = func(t *Thread) Slice {
+ l := lenf(t);
+ // XXX(Spec) What if len or cap is
+ // negative? The runtime panics.
+ if l < 0 {
+ t.Abort(NegativeLengthError{l});
+ }
+ c := l;
+ if capf != nil {
+ c = capf(t);
+ if c < 0 {
+ t.Abort(NegativeCapacityError{c});
+ }
+ // XXX(Spec) What happens if
+ // len > cap? The runtime
+ // sets cap to len.
+ if l > c {
+ c = l;
+ }
+ }
+ base := arrayV(make([]Value, c));
+ for i := int64(0); i < c; i++ {
+ base[i] = et.Zero();
+ }
+ return Slice{&base, l, c};
+ };
+ return expr;
+
+ case *MapType:
+ // A new, empty map value is made using the
+ // built-in function make, which takes the map
+ // type and an optional capacity hint as
+ // arguments.
+ if !checkCount(1, 2) {
+ return nil;
+ }
+ expr := a.newExpr(t, "function call");
+ expr.eval = func(t *Thread) Map {
+ if lenf == nil {
+ return make(evalMap);
+ }
+ l := lenf(t);
+ return make(evalMap, l);
+ };
+ return expr;
+
+ //case *ChanType:
+
+ default:
+ a.diag("illegal argument type for make function\n\t%v", as[0].valType);
+ return nil;
+ }
+
+ case closeType, closedType:
+ a.diag("built-in function %s not implemented", ft.builtin);
+ return nil;
+
+ case newType:
+ if !checkCount(1, 1) {
+ return nil;
+ }
+
+ t := as[0].valType;
+ expr := a.newExpr(NewPtrType(t), "new");
+ expr.eval = func(*Thread) Value {
+ return t.Zero();
+ };
+ return expr;
+
+ case panicType, paniclnType, printType, printlnType:
+ evals := make([]func(*Thread)interface{}, len(as));
+ for i, x := range as {
+ evals[i] = x.asInterface();
+ }
+ spaces := ft == paniclnType || ft == printlnType;
+ newline := ft != printType;
+ printer := func(t *Thread) {
+ for i, eval := range evals {
+ if i > 0 && spaces {
+ print(" ");
+ }
+ v := eval(t);
+ type stringer interface { String() string }
+ switch v1 := v.(type) {
+ case bool:
+ print(v1);
+ case uint64:
+ print(v1);
+ case int64:
+ print(v1);
+ case float64:
+ print(v1);
+ case string:
+ print(v1);
+ case stringer:
+ print(v1.String());
+ default:
+ print("???");
+ }
+ }
+ if newline {
+ print("\n");
+ }
+ };
+ expr := a.newExpr(EmptyType, "print");
+ expr.exec = printer;
+ if ft == panicType || ft == paniclnType {
+ expr.exec = func(t *Thread) {
+ printer(t);
+ t.Abort(os.NewError("panic"));
+ }
+ }
+ return expr;
+ }
+
+ log.Crashf("unexpected built-in function '%s'", ft.builtin);
+ panic();
+}
+
+func (a *exprInfo) compileStarExpr(v *expr) *expr {
+ switch vt := v.t.lit().(type) {
+ case *PtrType:
+ expr := a.newExpr(vt.Elem, "indirect expression");
+ vf := v.asPtr();
+ expr.genValue(func(t *Thread) Value {
+ v := vf(t);
+ if v == nil {
+ t.Abort(NilPointerError{});
+ }
+ return v;
+ });
+ return expr;
+ }
+
+ a.diagOpType(token.MUL, v.t);
+ return nil;
+}
+
+var unaryOpDescs = make(map[token.Token] string)
+
+func (a *exprInfo) compileUnaryExpr(op token.Token, v *expr) *expr {
+ // Type check
+ var t Type;
+ switch op {
+ case token.ADD, token.SUB:
+ if !v.t.isInteger() && !v.t.isFloat() {
+ a.diagOpType(op, v.t);
+ return nil;
+ }
+ t = v.t;
+
+ case token.NOT:
+ if !v.t.isBoolean() {
+ a.diagOpType(op, v.t);
+ return nil;
+ }
+ t = BoolType;
+
+ case token.XOR:
+ if !v.t.isInteger() {
+ a.diagOpType(op, v.t);
+ return nil;
+ }
+ t = v.t;
+
+ case token.AND:
+ // The unary prefix address-of operator & generates
+ // the address of its operand, which must be a
+ // variable, pointer indirection, field selector, or
+ // array or slice indexing operation.
+ if v.evalAddr == nil {
+ a.diag("cannot take the address of %s", v.desc);
+ return nil;
+ }
+
+ // TODO(austin) Implement "It is illegal to take the
+ // address of a function result variable" once I have
+ // function result variables.
+
+ t = NewPtrType(v.t);
+
+ case token.ARROW:
+ log.Crashf("Unary op %v not implemented", op);
+
+ default:
+ log.Crashf("unknown unary operator %v", op);
+ }
+
+ desc, ok := unaryOpDescs[op];
+ if !ok {
+ desc = "unary " + op.String() + " expression";
+ unaryOpDescs[op] = desc;
+ }
+
+ // Compile
+ expr := a.newExpr(t, desc);
+ switch op {
+ case token.ADD:
+ // Just compile it out
+ expr = v;
+ expr.desc = desc;
+
+ case token.SUB:
+ expr.genUnaryOpNeg(v);
+
+ case token.NOT:
+ expr.genUnaryOpNot(v);
+
+ case token.XOR:
+ expr.genUnaryOpXor(v);
+
+ case token.AND:
+ vf := v.evalAddr;
+ expr.eval = func(t *Thread) Value { return vf(t) };
+
+ default:
+ log.Crashf("Compilation of unary op %v not implemented", op);
+ }
+
+ return expr;
+}
+
+var binOpDescs = make(map[token.Token] string)
+
+func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr {
+ // Save the original types of l.t and r.t for error messages.
+ origlt := l.t;
+ origrt := r.t;
+
+ // XXX(Spec) What is the exact definition of a "named type"?
+
+ // XXX(Spec) Arithmetic operators: "Integer types" apparently
+ // means all types compatible with basic integer types, though
+ // this is never explained. Likewise for float types, etc.
+ // This relates to the missing explanation of named types.
+
+ // XXX(Spec) Operators: "If both operands are ideal numbers,
+ // the conversion is to ideal floats if one of the operands is
+ // an ideal float (relevant for / and %)." How is that
+ // relevant only for / and %? If I add an ideal int and an
+ // ideal float, I get an ideal float.
+
+ if op != token.SHL && op != token.SHR {
+ // Except in shift expressions, if one operand has
+ // numeric type and the other operand is an ideal
+ // number, the ideal number is converted to match the
+ // type of the other operand.
+ if (l.t.isInteger() || l.t.isFloat()) && !l.t.isIdeal() && r.t.isIdeal() {
+ r = r.convertTo(l.t);
+ } else if (r.t.isInteger() || r.t.isFloat()) && !r.t.isIdeal() && l.t.isIdeal() {
+ l = l.convertTo(r.t);
+ }
+ if l == nil || r == nil {
+ return nil;
+ }
+
+ // Except in shift expressions, if both operands are
+ // ideal numbers and one is an ideal float, the other
+ // is converted to ideal float.
+ if l.t.isIdeal() && r.t.isIdeal() {
+ if l.t.isInteger() && r.t.isFloat() {
+ l = l.convertTo(r.t);
+ } else if l.t.isFloat() && r.t.isInteger() {
+ r = r.convertTo(l.t);
+ }
+ if l == nil || r == nil {
+ return nil;
+ }
+ }
+ }
+
+ // Useful type predicates
+ // TODO(austin) CL 33668 mandates identical types except for comparisons.
+ compat := func() bool {
+ return l.t.compat(r.t, false);
+ };
+ integers := func() bool {
+ return l.t.isInteger() && r.t.isInteger();
+ };
+ floats := func() bool {
+ return l.t.isFloat() && r.t.isFloat();
+ };
+ strings := func() bool {
+ // TODO(austin) Deal with named types
+ return l.t == StringType && r.t == StringType;
+ };
+ booleans := func() bool {
+ return l.t.isBoolean() && r.t.isBoolean();
+ };
+
+ // Type check
+ var t Type;
+ switch op {
+ case token.ADD:
+ if !compat() || (!integers() && !floats() && !strings()) {
+ a.diagOpTypes(op, origlt, origrt);
+ return nil;
+ }
+ t = l.t;
+
+ case token.SUB, token.MUL, token.QUO:
+ if !compat() || (!integers() && !floats()) {
+ a.diagOpTypes(op, origlt, origrt);
+ return nil;
+ }
+ t = l.t;
+
+ case token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
+ if !compat() || !integers() {
+ a.diagOpTypes(op, origlt, origrt);
+ return nil;
+ }
+ t = l.t;
+
+ case token.SHL, token.SHR:
+ // XXX(Spec) Is it okay for the right operand to be an
+ // ideal float with no fractional part? "The right
+ // operand in a shift operation must be always be of
+ // unsigned integer type or an ideal number that can
+ // be safely converted into an unsigned integer type
+ // (§Arithmetic operators)" suggests so and 6g agrees.
+
+ if !l.t.isInteger() || !(r.t.isInteger() || r.t.isIdeal()) {
+ a.diagOpTypes(op, origlt, origrt);
+ return nil;
+ }
+
+ // The right operand in a shift operation must be
+ // always be of unsigned integer type or an ideal
+ // number that can be safely converted into an
+ // unsigned integer type.
+ if r.t.isIdeal() {
+ r2 := r.convertTo(UintType);
+ if r2 == nil {
+ return nil;
+ }
+
+ // If the left operand is not ideal, convert
+ // the right to not ideal.
+ if !l.t.isIdeal() {
+ r = r2;
+ }
+
+ // If both are ideal, but the right side isn't
+ // an ideal int, convert it to simplify things.
+ if l.t.isIdeal() && !r.t.isInteger() {
+ r = r.convertTo(IdealIntType);
+ if r == nil {
+ log.Crashf("conversion to uintType succeeded, but conversion to idealIntType failed");
+ }
+ }
+ } else if _, ok := r.t.lit().(*uintType); !ok {
+ a.diag("right operand of shift must be unsigned");
+ return nil;
+ }
+
+ if l.t.isIdeal() && !r.t.isIdeal() {
+ // XXX(Spec) What is the meaning of "ideal >>
+ // non-ideal"? Russ says the ideal should be
+ // converted to an int. 6g propagates the
+ // type down from assignments as a hint.
+
+ l = l.convertTo(IntType);
+ if l == nil {
+ return nil;
+ }
+ }
+
+ // At this point, we should have one of three cases:
+ // 1) uint SHIFT uint
+ // 2) int SHIFT uint
+ // 3) ideal int SHIFT ideal int
+
+ t = l.t;
+
+ case token.LOR, token.LAND:
+ if !booleans() {
+ return nil;
+ }
+ // XXX(Spec) There's no mention of *which* boolean
+ // type the logical operators return. From poking at
+ // 6g, it appears to be the named boolean type, NOT
+ // the type of the left operand, and NOT an unnamed
+ // boolean type.
+
+ t = BoolType;
+
+ case token.ARROW:
+ // The operands in channel sends differ in type: one
+ // is always a channel and the other is a variable or
+ // value of the channel's element type.
+ log.Crash("Binary op <- not implemented");
+ t = BoolType;
+
+ case token.LSS, token.GTR, token.LEQ, token.GEQ:
+ // XXX(Spec) It's really unclear what types which
+ // comparison operators apply to. I feel like the
+ // text is trying to paint a Venn diagram for me,
+ // which it's really pretty simple: <, <=, >, >= apply
+ // only to numeric types and strings. == and != apply
+ // to everything except arrays and structs, and there
+ // are some restrictions on when it applies to slices.
+
+ if !compat() || (!integers() && !floats() && !strings()) {
+ a.diagOpTypes(op, origlt, origrt);
+ return nil;
+ }
+ t = BoolType;
+
+ case token.EQL, token.NEQ:
+ // XXX(Spec) The rules for type checking comparison
+ // operators are spread across three places that all
+ // partially overlap with each other: the Comparison
+ // Compatibility section, the Operators section, and
+ // the Comparison Operators section. The Operators
+ // section should just say that operators require
+ // identical types (as it does currently) except that
+ // there a few special cases for comparison, which are
+ // described in section X. Currently it includes just
+ // one of the four special cases. The Comparison
+ // Compatibility section and the Comparison Operators
+ // section should either be merged, or at least the
+ // Comparison Compatibility section should be
+ // exclusively about type checking and the Comparison
+ // Operators section should be exclusively about
+ // semantics.
+
+ // XXX(Spec) Comparison operators: "All comparison
+ // operators apply to basic types except bools." This
+ // is very difficult to parse. It's explained much
+ // better in the Comparison Compatibility section.
+
+ // XXX(Spec) Comparison compatibility: "Function
+ // values are equal if they refer to the same
+ // function." is rather vague. It should probably be
+ // similar to the way the rule for map values is
+ // written: Function values are equal if they were
+ // created by the same execution of a function literal
+ // or refer to the same function declaration. This is
+ // *almost* but not quite waht 6g implements. If a
+ // function literals does not capture any variables,
+ // then multiple executions of it will result in the
+ // same closure. Russ says he'll change that.
+
+ // TODO(austin) Deal with remaining special cases
+
+ if !compat() {
+ a.diagOpTypes(op, origlt, origrt);
+ return nil;
+ }
+ // Arrays and structs may not be compared to anything.
+ switch l.t.(type) {
+ case *ArrayType, *StructType:
+ a.diagOpTypes(op, origlt, origrt);
+ return nil;
+ }
+ t = BoolType;
+
+ default:
+ log.Crashf("unknown binary operator %v", op);
+ }
+
+ desc, ok := binOpDescs[op];
+ if !ok {
+ desc = op.String() + " expression";
+ binOpDescs[op] = desc;
+ }
+
+ // Check for ideal divide by zero
+ switch op {
+ case token.QUO, token.REM:
+ if r.t.isIdeal() {
+ if (r.t.isInteger() && r.asIdealInt()().IsZero()) ||
+ (r.t.isFloat() && r.asIdealFloat()().IsZero()) {
+ a.diag("divide by zero");
+ return nil;
+ }
+ }
+ }
+
+ // Compile
+ expr := a.newExpr(t, desc);
+ switch op {
+ case token.ADD:
+ expr.genBinOpAdd(l, r);
+
+ case token.SUB:
+ expr.genBinOpSub(l, r);
+
+ case token.MUL:
+ expr.genBinOpMul(l, r);
+
+ case token.QUO:
+ expr.genBinOpQuo(l, r);
+
+ case token.REM:
+ expr.genBinOpRem(l, r);
+
+ case token.AND:
+ expr.genBinOpAnd(l, r);
+
+ case token.OR:
+ expr.genBinOpOr(l, r);
+
+ case token.XOR:
+ expr.genBinOpXor(l, r);
+
+ case token.AND_NOT:
+ expr.genBinOpAndNot(l, r);
+
+ case token.SHL:
+ if l.t.isIdeal() {
+ lv := l.asIdealInt()();
+ rv := r.asIdealInt()();
+ const maxShift = 99999;
+ if rv.Cmp(bignum.Int(maxShift)) > 0 {
+ a.diag("left shift by %v; exceeds implementation limit of %v", rv, maxShift);
+ expr.t = nil;
+ return nil;
+ }
+ val := lv.Shl(uint(rv.Value()));
+ expr.eval = func() *bignum.Integer { return val };
+ } else {
+ expr.genBinOpShl(l, r);
+ }
+
+ case token.SHR:
+ if l.t.isIdeal() {
+ lv := l.asIdealInt()();
+ rv := r.asIdealInt()();
+ val := lv.Shr(uint(rv.Value()));
+ expr.eval = func() *bignum.Integer { return val };
+ } else {
+ expr.genBinOpShr(l, r);
+ }
+
+ case token.LSS:
+ expr.genBinOpLss(l, r);
+
+ case token.GTR:
+ expr.genBinOpGtr(l, r);
+
+ case token.LEQ:
+ expr.genBinOpLeq(l, r);
+
+ case token.GEQ:
+ expr.genBinOpGeq(l, r);
+
+ case token.EQL:
+ expr.genBinOpEql(l, r);
+
+ case token.NEQ:
+ expr.genBinOpNeq(l, r);
+
+ case token.LAND:
+ expr.genBinOpLogAnd(l, r);
+
+ case token.LOR:
+ expr.genBinOpLogOr(l, r);
+
+ default:
+ log.Crashf("Compilation of binary op %v not implemented", op);
+ }
+
+ return expr;
+}
+
+// TODO(austin) This is a hack to eliminate a circular dependency
+// between type.go and expr.go
+func (a *compiler) compileArrayLen(b *block, expr ast.Expr) (int64, bool) {
+ lenExpr := a.compileExpr(b, true, expr);
+ if lenExpr == nil {
+ return 0, false;
+ }
+
+ // XXX(Spec) Are ideal floats with no fractional part okay?
+ if lenExpr.t.isIdeal() {
+ lenExpr = lenExpr.convertTo(IntType);
+ if lenExpr == nil {
+ return 0, false;
+ }
+ }
+
+ if !lenExpr.t.isInteger() {
+ a.diagAt(expr, "array size must be an integer");
+ return 0, false;
+ }
+
+ switch lenExpr.t.lit().(type) {
+ case *intType:
+ return lenExpr.asInt()(nil), true;
+ case *uintType:
+ return int64(lenExpr.asUint()(nil)), true;
+ }
+ log.Crashf("unexpected integer type %T", lenExpr.t);
+ return 0, false;
+}
+
+func (a *compiler) compileExpr(b *block, constant bool, expr ast.Expr) *expr {
+ ec := &exprCompiler{a, b, constant};
+ nerr := a.numError();
+ e := ec.compile(expr, false);
+ if e == nil && nerr == a.numError() {
+ log.Crashf("expression compilation failed without reporting errors");
+ }
+ return e;
+}
+
+// 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" (or "temp :=
+// &expr" and "*temp" for addressable exprs). 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.
+func (a *expr) extractEffect(b *block, errOp string) (func(*Thread), *expr) {
+ // Create "&a" if a is addressable
+ rhs := a;
+ if a.evalAddr != nil {
+ rhs = a.compileUnaryExpr(token.AND, rhs);
+ }
+
+ // Create temp
+ ac, ok := a.checkAssign(a.pos, []*expr{rhs}, errOp, "");
+ if !ok {
+ return nil, nil;
+ }
+ if len(ac.rmt.Elems) != 1 {
+ a.diag("multi-valued expression not allowed in %s", errOp);
+ return nil, nil;
+ }
+ tempType := ac.rmt.Elems[0];
+ if tempType.isIdeal() {
+ // It's too bad we have to duplicate this rule.
+ switch {
+ case tempType.isInteger():
+ tempType = IntType;
+ case tempType.isFloat():
+ tempType = FloatType;
+ default:
+ log.Crashf("unexpected ideal type %v", tempType);
+ }
+ }
+ temp := b.DefineTemp(tempType);
+ tempIdx := temp.Index;
+
+ // Create "temp := rhs"
+ assign := ac.compile(b, tempType);
+ if assign == nil {
+ log.Crashf("compileAssign type check failed");
+ }
+
+ effect := func(t *Thread) {
+ tempVal := tempType.Zero();
+ t.f.Vars[tempIdx] = tempVal;
+ assign(tempVal, t);
+ };
+
+ // Generate "temp" or "*temp"
+ getTemp := a.compileVariable(0, temp);
+ if a.evalAddr == nil {
+ return effect, getTemp;
+ }
+
+ deref := a.compileStarExpr(getTemp);
+ if deref == nil {
+ return nil, nil;
+ }
+ return effect, deref;
+}