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