git.delta.rocks / jrsonnet / refs/commits / e1572ba12014

difftreelog

refactor NumValue::{MIN,MAX}_SAFE_INTEGER constants

szxpmttlYaroslav Bolyukin2026-05-14parent: #c647485.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
3use jrsonnet_gcmodule::Trace;3use jrsonnet_gcmodule::Trace;
4use jrsonnet_interner::{IBytes, IStr};4use jrsonnet_interner::{IBytes, IStr};
5use jrsonnet_ir::NumValue;5use jrsonnet_ir::NumValue;
6pub use jrsonnet_ir::{MAX_SAFE_INTEGER, MIN_SAFE_INTEGER};
7use jrsonnet_types::{ComplexValType, ValType};6use jrsonnet_types::{ComplexValType, ValType};
87
9use crate::{8use crate::{
377}376}
378impl Typed for usize {377impl Typed for usize {
379 const TYPE: &'static ComplexValType =378 const TYPE: &'static ComplexValType =
380 &ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));379 &ComplexValType::BoundedNumber(Some(0.0), Some(NumValue::MAX_SAFE_INTEGER));
381}380}
382impl IntoUntyped for usize {381impl IntoUntyped for usize {
383 fn into_untyped(value: Self) -> Result<Val> {382 fn into_untyped(value: Self) -> Result<Val> {
modifiedcrates/jrsonnet-ir/src/lib.rsdiffbeforeafterboth
1#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]1#![allow(clippy::redundant_closure_call, clippy::derive_partial_eq_without_eq)]
2//! Jrsonnet AST definitions
23
3mod expr;4mod expr;
4use std::{cmp::Ordering, fmt, ops::Deref};5use std::{cmp::Ordering, fmt, ops::Deref};
17 SourcePathT, SourceUrl, SourceVirtual,18 SourcePathT, SourceUrl, SourceVirtual,
18};19};
19
20// It seels to be a wrong place for this kind of stuff, but as it would also be used for static analysis and
21// is already wanted for NumValue, I don't know a better place.
22#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]
23pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS)) - 1) as f64;
24#[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]
25pub const MIN_SAFE_INTEGER: f64 = (-((1i64 << (f64::MANTISSA_DIGITS)) - 1)) as f64;
2620
27/// Represents jsonnet number21/// Represents jsonnet number
28/// Jsonnet numbers are finite f64, with NaNs disallowed22/// Jsonnet numbers are finite f64, with NaNs disallowed
29#[derive(Acyclic, Clone, Copy)]23#[derive(Acyclic, Clone, Copy)]
30pub struct NumValue(f64);24pub struct NumValue(f64);
31impl NumValue {25impl NumValue {
26 /// Maximum safe integer value (`2**53 - 1`), see [`NumValue::truncate_for_bitwise`]
27 #[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]
28 pub const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS)) - 1) as f64;
29 /// Minimum safe integer value (`-(2**53 - 1)`), see [`NumValue::truncate_for_bitwise`]
30 #[expect(clippy::cast_precision_loss, reason = "checked to not overflow")]
31 pub const MIN_SAFE_INTEGER: f64 = (-((1i64 << (f64::MANTISSA_DIGITS)) - 1)) as f64;
32
32 /// Creates a [`NumValue`], if value is finite and not NaN33 /// Creates a [`NumValue`], if value is finite and not NaN
33 pub fn new(v: f64) -> Option<Self> {34 pub fn new(v: f64) -> Option<Self> {
36 }37 }
37 Some(Self(v))38 Some(Self(v))
38 }39 }
40 /// Get the number value
39 #[inline]41 #[inline]
40 pub const fn get(&self) -> f64 {42 pub const fn get(&self) -> f64 {
41 self.043 self.0
42 }44 }
45
46 /// In Jsonnet bitwise operations operate on integers,
47 /// this method performs a safe conversion for such operation, where we use the same definition for safe as JS:
48 ///
49 /// Double precision floating point format only has 52 bits to represent the mantissa,
50 /// so it can only safely represent integers between -(253 – 1) and 253 – 1.
51 /// "Safe" in this context refers to the ability to represent integers exactly and
52 /// to compare them correctly. For example, Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2
53 /// will evaluate to true, which is mathematically incorrect. See Number.isSafeInteger() for more information.
43 pub fn truncate_for_bitwise(self) -> Result<i64, ConvertNumValueError> {54 pub fn truncate_for_bitwise(self) -> Result<i64, ConvertNumValueError> {
44 if self.0 < MIN_SAFE_INTEGER || self.0 > MAX_SAFE_INTEGER {55 if self.0 < Self::MIN_SAFE_INTEGER || self.0 > Self::MAX_SAFE_INTEGER {
45 return Err(ConvertNumValueError::BitwiseSafeRange);56 return Err(ConvertNumValueError::BitwiseSafeRange);
46 }57 }
47 #[expect(clippy::cast_possible_truncation, reason = "intended")]58 #[expect(clippy::cast_possible_truncation, reason = "intended")]
98}109}
99impl_num!(i8, u8, i16, u16, i32, u32);110impl_num!(i8, u8, i16, u16, i32, u32);
100111
112/// Jsonnet numbers are finite f64, with NaNs disallowed,
113/// this enum represents errors that might occur in conversion from/to the [`NumValue`]
101#[derive(Clone, Copy, Debug, thiserror::Error, Acyclic)]114#[derive(Clone, Copy, Debug, thiserror::Error, Acyclic)]
102pub enum ConvertNumValueError {115pub enum ConvertNumValueError {
116 /// The value is too large
103 #[error("overflow")]117 #[error("overflow")]
104 Overflow,118 Overflow,
119 /// The value is too small
105 #[error("underflow")]120 #[error("underflow")]
106 Underflow,121 Underflow,
122 /// Jsonnet numbers can only be finite
107 #[error("non-finite")]123 #[error("non-finite")]
108 NonFinite,124 NonFinite,
125 /// Number value is out of safe integer range
109 #[error("float out of safe int range")]126 #[error("float out of safe int range")]
110 BitwiseSafeRange,127 BitwiseSafeRange,
111}128}
118 fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {135 fn try_from(value: $ty) -> Result<Self, ConvertNumValueError> {
119 #[expect(clippy::cast_precision_loss, reason = "precision loss is explicitly handled")]136 #[expect(clippy::cast_precision_loss, reason = "precision loss is explicitly handled")]
120 let value = value as f64;137 let value = value as f64;
121 if value < MIN_SAFE_INTEGER {138 if value < NumValue::MIN_SAFE_INTEGER {
122 return Err(ConvertNumValueError::Underflow)139 return Err(ConvertNumValueError::Underflow)
123 } else if value > MAX_SAFE_INTEGER {140 } else if value > NumValue::MAX_SAFE_INTEGER {
124 return Err(ConvertNumValueError::Overflow)141 return Err(ConvertNumValueError::Overflow)
125 }142 }
126 // Number is finite.143 // Number is finite.