aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/exp/eval/expr.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/exp/eval/expr.go')
-rw-r--r--libgo/go/exp/eval/expr.go2015
1 files changed, 0 insertions, 2015 deletions
diff --git a/libgo/go/exp/eval/expr.go b/libgo/go/exp/eval/expr.go
deleted file mode 100644
index e65f476..0000000
--- a/libgo/go/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 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.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
-}