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