Plan 9 from Bell Labs’s /usr/web/sources/contrib/ericvh/go-plan9/src/pkg/exp/eval/expr.go

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


// 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:
		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.SliceExpr:
		end := x.End;
		if end == nil {
			// TODO: set end to len(x.X)
			panic("unimplemented")
		}
		arr := a.compile(x.X, false);
		lo := a.compile(x.Index, false);
		hi := a.compile(end, false);
		if arr == nil || lo == nil || hi == nil {
			return nil
		}
		return ei.compileSliceExpr(arr, lo, hi);

	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:], '\'');
	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:];
	};

	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;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.