diff options
| author | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:11:55 +0200 | 
|---|---|---|
| committer | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:11:55 +0200 | 
| commit | 80f18fc933cf3f3e829c5455a1023d69f7b86e52 (patch) | |
| tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /src/pkg/exp/eval/expr.go | |
| parent | 28592ee1ea1f5cdffcf85472f9de0285d928cf12 (diff) | |
| download | golang-80f18fc933cf3f3e829c5455a1023d69f7b86e52.tar.gz | |
Imported Upstream version 60
Diffstat (limited to 'src/pkg/exp/eval/expr.go')
| -rw-r--r-- | src/pkg/exp/eval/expr.go | 2015 | 
1 files changed, 0 insertions, 2015 deletions
| diff --git a/src/pkg/exp/eval/expr.go b/src/pkg/exp/eval/expr.go deleted file mode 100644 index 14a0659b6..000000000 --- a/src/pkg/exp/eval/expr.go +++ /dev/null @@ -1,2015 +0,0 @@ -// 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 ( -	"big" -	"fmt" -	"go/ast" -	"go/token" -	"log" -	"strconv" -	"strings" -	"os" -) - -var ( -	idealZero = big.NewInt(0) -	idealOne  = big.NewInt(1) -) - -// 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.Pos -} - -func (a *exprInfo) newExpr(t Type, desc string) *expr { -	return &expr{exprInfo: a, t: t, desc: desc} -} - -func (a *exprInfo) diag(format string, args ...interface{}) { -	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.Panicf("attempted to convert from %v, expected ideal", a.t) -	} - -	var rat *big.Rat - -	// 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", rat.FloatString(6)) -			return nil -		} -	case IdealIntType: -		i := a.asIdealInt()() -		rat = new(big.Rat).SetInt(i) -	default: -		log.Panicf("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", rat.FloatString(6), t) -			return nil -		} -		if rat.Cmp(t.maxVal()) > 0 { -			a.diag("constant %v overflows %v", rat.FloatString(6), t) -			return nil -		} -	} - -	// Convert rat to type t. -	res := a.newExpr(t, a.desc) -	switch t := t.lit().(type) { -	case *uintType: -		n, d := rat.Num(), rat.Denom() -		f := new(big.Int).Quo(n, d) -		f = f.Abs(f) -		v := uint64(f.Int64()) -		res.eval = func(*Thread) uint64 { return v } -	case *intType: -		n, d := rat.Num(), rat.Denom() -		f := new(big.Int).Quo(n, d) -		v := f.Int64() -		res.eval = func(*Thread) int64 { return v } -	case *idealIntType: -		n, d := rat.Num(), rat.Denom() -		f := new(big.Int).Quo(n, d) -		res.eval = func() *big.Int { return f } -	case *floatType: -		n, d := rat.Num(), rat.Denom() -		v := float64(n.Int64()) / float64(d.Int64()) -		res.eval = func(*Thread) float64 { return v } -	case *idealFloatType: -		res.eval = func() *big.Rat { return rat } -	default: -		log.Panicf("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.Sign() < 0 { -			a.diag("negative %s: %s", negErr, val) -			return nil -		} -		bound := max -		if negErr == "slice" { -			bound++ -		} -		if max != -1 && val.Cmp(big.NewInt(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.Panicf("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.Pos -	// 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.Pos, 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 { -			panic(fmt.Sprintln("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.Panicf("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.Pos, 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.Panicf("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.Pos(), "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.Pos(), "function call in constant context") -			return nil -		} - -		if l.valType != nil { -			a.diagAt(x.Pos(), "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.Name) - -	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: -		var lo, hi *expr -		arr := a.compile(x.X, false) -		if x.Low == nil { -			// beginning was omitted, so we need to provide it -			ei := &exprInfo{a.compiler, x.Pos()} -			lo = ei.compileIntLit("0") -		} else { -			lo = a.compile(x.Low, false) -		} -		if x.High == nil { -			// End was omitted, so we need to compute len(x.X) -			ei := &exprInfo{a.compiler, x.Pos()} -			hi = ei.compileBuiltinCallExpr(a.block, lenType, []*expr{arr}) -		} else { -			hi = a.compile(x.High, 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.Name) - -	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.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.Panicf("unexpected ast node type %T", x) -	panic("unreachable") - -typeexpr: -	if !callCtx { -		a.diagAt(x.Pos(), "type used as expression") -		return nil -	} -	return ei.exprFromType(a.compileType(a.block, x)) - -notimpl: -	a.diagAt(x.Pos(), "%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.Panicf("name %s has unknown type %T", name, def) -	panic("unreachable") -} - -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 *big.Int, desc string) *expr { -	expr := a.newExpr(IdealIntType, desc) -	expr.eval = func() *big.Int { return i } -	return expr -} - -func (a *exprInfo) compileIntLit(lit string) *expr { -	i, _ := new(big.Int).SetString(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(big.NewInt(int64(v)), "character literal") -} - -func (a *exprInfo) compileFloatLit(lit string) *expr { -	f, ok := new(big.Rat).SetString(lit) -	if !ok { -		log.Panicf("malformed float literal %s at %v passed parser", lit, a.pos) -	} -	expr := a.newExpr(IdealFloatType, "float literal") -	expr.eval = func() *big.Rat { 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.Panicf("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 visited[t] { -			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.Panic("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.Panicf("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.Panicf("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) -	copy(vts, lt.In) -	copy(vts[nin:], lt.Out) - -	// 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 copyType: -		if !checkCount(2, 2) { -			return nil -		} -		src := as[1] -		dst := as[0] -		if src.t != dst.t { -			a.diag("arguments to built-in function 'copy' must have same type\nsrc: %s\ndst: %s\n", src.t, dst.t) -			return nil -		} -		if _, ok := src.t.lit().(*SliceType); !ok { -			a.diag("src argument to 'copy' must be a slice (got: %s)", src.t) -			return nil -		} -		if _, ok := dst.t.lit().(*SliceType); !ok { -			a.diag("dst argument to 'copy' must be a slice (got: %s)", dst.t) -			return nil -		} -		expr := a.newExpr(IntType, "function call") -		srcf := src.asSlice() -		dstf := dst.asSlice() -		expr.eval = func(t *Thread) int64 { -			src, dst := srcf(t), dstf(t) -			nelems := src.Len -			if nelems > dst.Len { -				nelems = dst.Len -			} -			dst.Base.Sub(0, nelems).Assign(t, src.Base.Sub(0, nelems)) -			return nelems -		} -		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, printType, printlnType: -		evals := make([]func(*Thread) interface{}, len(as)) -		for i, x := range as { -			evals[i] = x.asInterface() -		} -		spaces := 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 { -			expr.exec = func(t *Thread) { -				printer(t) -				t.Abort(os.NewError("panic")) -			} -		} -		return expr -	} - -	log.Panicf("unexpected built-in function '%s'", ft.builtin) -	panic("unreachable") -} - -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.Panicf("Unary op %v not implemented", op) - -	default: -		log.Panicf("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.Panicf("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.Panicf("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.Panic("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 what 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.Panicf("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()().Sign() == 0) || -				(r.t.isFloat() && r.asIdealFloat()().Sign() == 0) { -				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(big.NewInt(maxShift)) > 0 { -				a.diag("left shift by %v; exceeds implementation limit of %v", rv, maxShift) -				expr.t = nil -				return nil -			} -			val := new(big.Int).Lsh(lv, uint(rv.Int64())) -			expr.eval = func() *big.Int { return val } -		} else { -			expr.genBinOpShl(l, r) -		} - -	case token.SHR: -		if l.t.isIdeal() { -			lv := l.asIdealInt()() -			rv := r.asIdealInt()() -			val := new(big.Int).Rsh(lv, uint(rv.Int64())) -			expr.eval = func() *big.Int { 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.Panicf("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.Pos(), "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.Panicf("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.Panicf("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 = Float64Type -		default: -			log.Panicf("unexpected ideal type %v", tempType) -		} -	} -	temp := b.DefineTemp(tempType) -	tempIdx := temp.Index - -	// Create "temp := rhs" -	assign := ac.compile(b, tempType) -	if assign == nil { -		log.Panicf("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 -} | 
