func (constantFolderVisitor) VisitPost(expr Expr) (retExpr Expr) { defer func() { // go/constant operations can panic for a number of reasons (like division // by zero), but it's difficult to preemptively detect when they will. It's // safest to just recover here without folding the expression and let // normalization or evaluation deal with error handling. if r := recover(); r != nil { retExpr = expr } }() switch t := expr.(type) { case *ParenExpr: if cv, ok := t.Expr.(*NumVal); ok { return cv } case *UnaryExpr: if cv, ok := t.Expr.(*NumVal); ok { if token, ok := unaryOpToToken[t.Operator]; ok { return &NumVal{Value: constant.UnaryOp(token, cv.Value, 0)} } if token, ok := unaryOpToTokenIntOnly[t.Operator]; ok { if intVal, ok := cv.asConstantInt(); ok { return &NumVal{Value: constant.UnaryOp(token, intVal, 0)} } } } case *BinaryExpr: l, okL := t.Left.(*NumVal) r, okR := t.Right.(*NumVal) if okL && okR { if token, ok := binaryOpToToken[t.Operator]; ok { return &NumVal{Value: constant.BinaryOp(l.Value, token, r.Value)} } if token, ok := binaryOpToTokenIntOnly[t.Operator]; ok { if lInt, ok := l.asConstantInt(); ok { if rInt, ok := r.asConstantInt(); ok { return &NumVal{Value: constant.BinaryOp(lInt, token, rInt)} } } } if token, ok := binaryShiftOpToToken[t.Operator]; ok { if lInt, ok := l.asConstantInt(); ok { if rInt64, err := r.asInt64(); err == nil && rInt64 >= 0 { return &NumVal{Value: constant.Shift(lInt, token, uint(rInt64))} } } } } case *ComparisonExpr: l, okL := t.Left.(*NumVal) r, okR := t.Right.(*NumVal) if okL && okR { if token, ok := comparisonOpToToken[t.Operator]; ok { return MakeDBool(DBool(constant.Compare(l.Value, token, r.Value))) } } } return expr }
// valString returns the string representation for the value v. // Setting floatFmt forces an integer value to be formatted in // normalized floating-point format. // TODO(gri) Move this code into package exact. func valString(v exact.Value, floatFmt bool) string { switch v.Kind() { case exact.Int: if floatFmt { return floatString(v) } case exact.Float: return floatString(v) case exact.Complex: re := exact.Real(v) im := exact.Imag(v) var s string if exact.Sign(re) != 0 { s = floatString(re) if exact.Sign(im) >= 0 { s += " + " } else { s += " - " im = exact.UnaryOp(token.SUB, im, 0) // negate im } } // im != 0, otherwise v would be exact.Int or exact.Float return s + floatString(im) + "i" } return v.String() }
// The unary expression e may be nil. It's passed in for better error messages only. func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) { switch op { case token.AND: // spec: "As an exception to the addressability // requirement x may also be a composite literal." if _, ok := unparen(x.expr).(*ast.CompositeLit); !ok && x.mode != variable { check.invalidOp(x.pos(), "cannot take address of %s", x) x.mode = invalid return } x.mode = value x.typ = &Pointer{base: x.typ} return case token.ARROW: typ, ok := x.typ.Underlying().(*Chan) if !ok { check.invalidOp(x.pos(), "cannot receive from non-channel %s", x) x.mode = invalid return } if typ.dir == SendOnly { check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x) x.mode = invalid return } x.mode = commaok x.typ = typ.elem check.hasCallOrRecv = true return } if !check.op(unaryOpPredicates, x, op) { x.mode = invalid return } if x.mode == constant_ { typ := x.typ.Underlying().(*Basic) var prec uint if isUnsigned(typ) { prec = uint(check.conf.sizeof(typ) * 8) } x.val = constant.UnaryOp(op, x.val, prec) // Typed constants must be representable in // their type after each constant operation. if isTyped(typ) { if e != nil { x.expr = e // for better error message } check.representable(x, typ) } return } x.mode = value // x.typ remains unchanged }
func constantUnaryOp(op token.Token, y constant.Value) (r constant.Value, err error) { defer func() { if ierr := recover(); ierr != nil { err = fmt.Errorf("%v", ierr) } }() r = constant.UnaryOp(op, y, 0) return }
func (p *importer) fraction() constant.Value { sign := p.int() if sign == 0 { return constant.MakeInt64(0) } x := constant.BinaryOp(p.ufloat(), token.QUO, p.ufloat()) if sign < 0 { x = constant.UnaryOp(token.SUB, x, 0) } return x }
func (p *importer) float() constant.Value { sign := p.int() if sign == 0 { return constant.MakeInt64(0) } exp := p.int() mant := []byte(p.string()) // big endian // remove leading 0's if any for len(mant) > 0 && mant[0] == 0 { mant = mant[1:] } // convert to little endian // TODO(gri) go/constant should have a more direct conversion function // (e.g., once it supports a big.Float based implementation) for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 { mant[i], mant[j] = mant[j], mant[i] } // adjust exponent (constant.MakeFromBytes creates an integer value, // but mant represents the mantissa bits such that 0.5 <= mant < 1.0) exp -= len(mant) << 3 if len(mant) > 0 { for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 { exp++ } } x := constant.MakeFromBytes(mant) switch { case exp < 0: d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp)) x = constant.BinaryOp(x, token.QUO, d) case exp > 0: x = constant.Shift(x, token.SHL, uint(exp)) } if sign < 0 { x = constant.UnaryOp(token.SUB, x, 0) } return x }
func (constantFolderVisitor) VisitPost(expr Expr) (retExpr Expr) { defer func() { // go/constant operations can panic for a number of reasons (like division // by zero), but it's difficult to preemptively detect when they will. It's // safest to just recover here without folding the expression and let // normalization or evaluation deal with error handling. if r := recover(); r != nil { retExpr = expr } }() switch t := expr.(type) { case *ParenExpr: switch cv := t.Expr.(type) { case *NumVal, *StrVal: return cv } case *UnaryExpr: switch cv := t.Expr.(type) { case *NumVal: if token, ok := unaryOpToToken[t.Operator]; ok { return &NumVal{Value: constant.UnaryOp(token, cv.Value, 0)} } if token, ok := unaryOpToTokenIntOnly[t.Operator]; ok { if intVal, ok := cv.asConstantInt(); ok { return &NumVal{Value: constant.UnaryOp(token, intVal, 0)} } } } case *BinaryExpr: switch l := t.Left.(type) { case *NumVal: if r, ok := t.Right.(*NumVal); ok { if token, ok := binaryOpToToken[t.Operator]; ok { return &NumVal{Value: constant.BinaryOp(l.Value, token, r.Value)} } if token, ok := binaryOpToTokenIntOnly[t.Operator]; ok { if lInt, ok := l.asConstantInt(); ok { if rInt, ok := r.asConstantInt(); ok { return &NumVal{Value: constant.BinaryOp(lInt, token, rInt)} } } } if token, ok := binaryShiftOpToToken[t.Operator]; ok { if lInt, ok := l.asConstantInt(); ok { if rInt64, err := r.asInt64(); err == nil && rInt64 >= 0 { return &NumVal{Value: constant.Shift(lInt, token, uint(rInt64))} } } } } case *StrVal: if r, ok := t.Right.(*StrVal); ok { switch t.Operator { case Concat: // When folding string-like constants, if either was byte-escaped, // the result is also considered byte escaped. return &StrVal{s: l.s + r.s, bytesEsc: l.bytesEsc || r.bytesEsc} } } } case *ComparisonExpr: switch l := t.Left.(type) { case *NumVal: if r, ok := t.Right.(*NumVal); ok { if token, ok := comparisonOpToToken[t.Operator]; ok { return MakeDBool(DBool(constant.Compare(l.Value, token, r.Value))) } } case *StrVal: // ComparisonExpr folding for String-like constants is not significantly different // from constant evalutation during normalization (because both should be exact, // unlike numeric comparisons). Still, folding these comparisons when possible here // can reduce the amount of work performed during type checking, can reduce necessary // allocations, and maintains symmetry with numeric constants. if r, ok := t.Right.(*StrVal); ok { switch t.Operator { case EQ: return MakeDBool(DBool(l.s == r.s)) case NE: return MakeDBool(DBool(l.s != r.s)) case LT: return MakeDBool(DBool(l.s < r.s)) case LE: return MakeDBool(DBool(l.s <= r.s)) case GT: return MakeDBool(DBool(l.s > r.s)) case GE: return MakeDBool(DBool(l.s >= r.s)) } } } } return expr }