// number = int_lit [ "p" int_lit ] . // func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) { // mantissa mant := exact.MakeFromLiteral(p.parseInt(), token.INT, 0) if mant == nil { panic("invalid mantissa") } if p.lit == "p" { // exponent (base 2) p.next() exp, err := strconv.ParseInt(p.parseInt(), 10, 0) if err != nil { p.error(err) } if exp < 0 { denom := exact.MakeInt64(1) denom = exact.Shift(denom, token.SHL, uint(-exp)) typ = types.Typ[types.UntypedFloat] val = exact.BinaryOp(mant, token.QUO, denom) return } if exp > 0 { mant = exact.Shift(mant, token.SHL, uint(exp)) } typ = types.Typ[types.UntypedFloat] val = mant return } typ = types.Typ[types.UntypedInt] val = mant return }
func (p *importer) ufloat() constant.Value { exp := p.int() x := constant.MakeFromBytes(p.bytes()) 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)) } 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: 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 }
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 constantBinaryOp(op token.Token, x, y constant.Value) (r constant.Value, err error) { defer func() { if ierr := recover(); ierr != nil { err = fmt.Errorf("%v", ierr) } }() switch op { case token.SHL, token.SHR: n, _ := constant.Uint64Val(y) r = constant.Shift(x, op, uint(n)) default: r = constant.BinaryOp(x, op, y) } return }
func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) { untypedx := isUntyped(x.typ) var xval constant.Value if x.mode == constant_ { xval = constant.ToInt(x.val) } if isInteger(x.typ) || untypedx && xval != nil && xval.Kind() == constant.Int { // The lhs is of integer type or an untyped constant representable // as an integer. Nothing to do. } else { // shift has no chance check.invalidOp(x.pos(), "shifted operand %s must be integer", x) x.mode = invalid return } // spec: "The right operand in a shift expression must have unsigned // integer type or be an untyped constant that can be converted to // unsigned integer type." switch { case isUnsigned(y.typ): // nothing to do case isUntyped(y.typ): check.convertUntyped(y, Typ[UntypedInt]) if y.mode == invalid { x.mode = invalid return } default: check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) x.mode = invalid return } if x.mode == constant_ { if y.mode == constant_ { // rhs must be an integer value yval := constant.ToInt(y.val) if yval.Kind() != constant.Int { check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) x.mode = invalid return } // rhs must be within reasonable bounds const shiftBound = 1023 - 1 + 52 // so we can express smallestFloat64 s, ok := constant.Uint64Val(yval) if !ok || s > shiftBound { check.invalidOp(y.pos(), "invalid shift count %s", y) x.mode = invalid return } // The lhs is representable as an integer but may not be an integer // (e.g., 2.0, an untyped float) - this can only happen for untyped // non-integer numeric constants. Correct the type so that the shift // result is of integer type. if !isInteger(x.typ) { x.typ = Typ[UntypedInt] } // x is a constant so xval != nil and it must be of Int kind. x.val = constant.Shift(xval, op, uint(s)) // Typed constants must be representable in // their type after each constant operation. if isTyped(x.typ) { if e != nil { x.expr = e // for better error message } check.representable(x, x.typ.Underlying().(*Basic)) } return } // non-constant shift with constant lhs if untypedx { // spec: "If the left operand of a non-constant shift // expression is an untyped constant, the type of the // constant is what it would be if the shift expression // were replaced by its left operand alone.". // // Delay operand checking until we know the final type // by marking the lhs expression as lhs shift operand. // // Usually (in correct programs), the lhs expression // is in the untyped map. However, it is possible to // create incorrect programs where the same expression // is evaluated twice (via a declaration cycle) such // that the lhs expression type is determined in the // first round and thus deleted from the map, and then // not found in the second round (double insertion of // the same expr node still just leads to one entry for // that node, and it can only be deleted once). // Be cautious and check for presence of entry. // Example: var e, f = int(1<<""[f]) // issue 11347 if info, found := check.untyped[x.expr]; found { info.isLhs = true check.untyped[x.expr] = info } // keep x's type x.mode = value return } } // constant rhs must be >= 0 if y.mode == constant_ && constant.Sign(y.val) < 0 { check.invalidOp(y.pos(), "shift count %s must not be negative", y) } // non-constant shift - lhs must be an integer if !isInteger(x.typ) { check.invalidOp(x.pos(), "shifted operand %s must be integer", x) x.mode = invalid return } x.mode = value }
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 }
func (check *Checker) shift(x, y *operand, op token.Token) { untypedx := isUntyped(x.typ) // The lhs must be of integer type or be representable // as an integer; otherwise the shift has no chance. if !x.isInteger() { check.invalidOp(x.pos(), "shifted operand %s must be integer", x) x.mode = invalid return } // spec: "The right operand in a shift expression must have unsigned // integer type or be an untyped constant that can be converted to // unsigned integer type." switch { case isInteger(y.typ) && isUnsigned(y.typ): // nothing to do case isUntyped(y.typ): check.convertUntyped(y, Typ[UntypedInt]) if y.mode == invalid { x.mode = invalid return } default: check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) x.mode = invalid return } if x.mode == constant { if y.mode == constant { // rhs must be an integer value if !y.isInteger() { check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y) x.mode = invalid return } // rhs must be within reasonable bounds const stupidShift = 1023 - 1 + 52 // so we can express smallestFloat64 s, ok := exact.Uint64Val(y.val) if !ok || s > stupidShift { check.invalidOp(y.pos(), "stupid shift count %s", y) x.mode = invalid return } // The lhs is representable as an integer but may not be an integer // (e.g., 2.0, an untyped float) - this can only happen for untyped // non-integer numeric constants. Correct the type so that the shift // result is of integer type. if !isInteger(x.typ) { x.typ = Typ[UntypedInt] } x.val = exact.Shift(x.val, op, uint(s)) return } // non-constant shift with constant lhs if untypedx { // spec: "If the left operand of a non-constant shift // expression is an untyped constant, the type of the // constant is what it would be if the shift expression // were replaced by its left operand alone.". // // Delay operand checking until we know the final type: // The lhs expression must be in the untyped map, mark // the entry as lhs shift operand. info, found := check.untyped[x.expr] assert(found) info.isLhs = true check.untyped[x.expr] = info // keep x's type x.mode = value return } } // constant rhs must be >= 0 if y.mode == constant && exact.Sign(y.val) < 0 { check.invalidOp(y.pos(), "shift count %s must not be negative", y) } // non-constant shift - lhs must be an integer if !isInteger(x.typ) { check.invalidOp(x.pos(), "shifted operand %s must be integer", x) x.mode = invalid return } x.mode = value }