diff --git a/crates/nu-command/tests/commands/math/mod.rs b/crates/nu-command/tests/commands/math/mod.rs index 7918b3262a..cde6a2da35 100644 --- a/crates/nu-command/tests/commands/math/mod.rs +++ b/crates/nu-command/tests/commands/math/mod.rs @@ -268,6 +268,42 @@ fn modulo() { assert_eq!(actual.out, "1"); } +#[test] +fn floor_div_mod() { + let actual = nu!("let q = 8 // -3; let r = 8 mod -3; 8 == $q * -3 + $r"); + assert_eq!(actual.out, "true"); + + let actual = nu!("let q = -8 // 3; let r = -8 mod 3; -8 == $q * 3 + $r"); + assert_eq!(actual.out, "true"); +} + +#[test] +fn floor_div_mod_overflow() { + let actual = nu!(format!("{} // -1", i64::MIN)); + assert!(actual.err.contains("overflow")); + + let actual = nu!(format!("{} mod -1", i64::MIN)); + assert!(actual.err.contains("overflow")); +} + +#[test] +fn floor_div_mod_zero() { + let actual = nu!("1 // 0"); + assert!(actual.err.contains("zero")); + + let actual = nu!("1 mod 0"); + assert!(actual.err.contains("zero")); +} + +#[test] +fn floor_div_mod_large_num() { + let actual = nu!(format!("{} // {}", i64::MAX, i64::MAX / 2)); + assert_eq!(actual.out, "2"); + + let actual = nu!(format!("{} mod {}", i64::MAX, i64::MAX / 2)); + assert_eq!(actual.out, "1"); +} + #[test] fn unit_multiplication_math() { let actual = nu!(pipeline( diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index bf268dac1a..1b66258ded 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -280,15 +280,15 @@ pub fn math_result_type( } }, Operator::Math(Math::Divide) => match (&lhs.ty, &rhs.ty) { - (Type::Int, Type::Int) => (Type::Int, None), + (Type::Int, Type::Int) => (Type::Float, None), (Type::Float, Type::Int) => (Type::Float, None), (Type::Int, Type::Float) => (Type::Float, None), (Type::Float, Type::Float) => (Type::Float, None), - (Type::Number, Type::Number) => (Type::Number, None), - (Type::Number, Type::Int) => (Type::Number, None), - (Type::Int, Type::Number) => (Type::Number, None), - (Type::Number, Type::Float) => (Type::Number, None), - (Type::Float, Type::Number) => (Type::Number, None), + (Type::Number, Type::Number) => (Type::Float, None), + (Type::Number, Type::Int) => (Type::Float, None), + (Type::Int, Type::Number) => (Type::Float, None), + (Type::Number, Type::Float) => (Type::Float, None), + (Type::Float, Type::Number) => (Type::Float, None), (Type::Filesize, Type::Filesize) => (Type::Float, None), (Type::Filesize, Type::Int) => (Type::Filesize, None), (Type::Filesize, Type::Float) => (Type::Filesize, None), @@ -379,9 +379,9 @@ pub fn math_result_type( }, Operator::Math(Math::FloorDivision) => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Int, None), - (Type::Float, Type::Int) => (Type::Int, None), - (Type::Int, Type::Float) => (Type::Int, None), - (Type::Float, Type::Float) => (Type::Int, None), + (Type::Float, Type::Int) => (Type::Float, None), + (Type::Int, Type::Float) => (Type::Float, None), + (Type::Float, Type::Float) => (Type::Float, None), (Type::Number, Type::Number) => (Type::Number, None), (Type::Number, Type::Int) => (Type::Number, None), (Type::Int, Type::Number) => (Type::Number, None), diff --git a/crates/nu-protocol/src/errors/shell_error.rs b/crates/nu-protocol/src/errors/shell_error.rs index 024c515b09..96678daef5 100644 --- a/crates/nu-protocol/src/errors/shell_error.rs +++ b/crates/nu-protocol/src/errors/shell_error.rs @@ -37,12 +37,13 @@ pub enum ShellError { /// Check the inputs to the operation and add guards for their sizes. /// Integers are generally of size i64, floats are generally f64. #[error("Operator overflow.")] - #[diagnostic(code(nu::shell::operator_overflow), help("{help}"))] + #[diagnostic(code(nu::shell::operator_overflow))] OperatorOverflow { msg: String, #[label = "{msg}"] span: Span, - help: String, + #[help] + help: Option, }, /// The pipelined input into a command was not of the expected type. For example, it might diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index ea7e258f3f..29f75d157d 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -2425,7 +2425,11 @@ impl Value { if let Some(val) = lhs.checked_add(*rhs) { Ok(Value::int(val, span)) } else { - Err(ShellError::OperatorOverflow { msg: "add operation overflowed".into(), span, help: "Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into() }) + Err(ShellError::OperatorOverflow { + msg: "add operation overflowed".into(), + span, + help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()), + }) } } (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => { @@ -2448,7 +2452,7 @@ impl Value { Err(ShellError::OperatorOverflow { msg: "addition operation overflowed".into(), span, - help: "".into(), + help: None, }) } } @@ -2459,7 +2463,7 @@ impl Value { Err(ShellError::OperatorOverflow { msg: "add operation overflowed".into(), span, - help: "".into(), + help: None, }) } } @@ -2470,7 +2474,7 @@ impl Value { Err(ShellError::OperatorOverflow { msg: "add operation overflowed".into(), span, - help: "".into(), + help: None, }) } } @@ -2534,7 +2538,11 @@ impl Value { if let Some(val) = lhs.checked_sub(*rhs) { Ok(Value::int(val, span)) } else { - Err(ShellError::OperatorOverflow { msg: "subtraction operation overflowed".into(), span, help: "Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into() }) + Err(ShellError::OperatorOverflow { + msg: "subtraction operation overflowed".into(), + span, + help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()), + }) } } (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => { @@ -2555,7 +2563,7 @@ impl Value { Err(ShellError::OperatorOverflow { msg: "subtraction operation overflowed".into(), span, - help: "".into(), + help: None, }) } } @@ -2565,7 +2573,7 @@ impl Value { _ => Err(ShellError::OperatorOverflow { msg: "subtraction operation overflowed".into(), span, - help: "".into(), + help: None, }), } } @@ -2576,7 +2584,7 @@ impl Value { Err(ShellError::OperatorOverflow { msg: "subtraction operation overflowed".into(), span, - help: "".into(), + help: None, }) } } @@ -2587,7 +2595,7 @@ impl Value { Err(ShellError::OperatorOverflow { msg: "add operation overflowed".into(), span, - help: "".into(), + help: None, }) } } @@ -2612,7 +2620,11 @@ impl Value { if let Some(val) = lhs.checked_mul(*rhs) { Ok(Value::int(val, span)) } else { - Err(ShellError::OperatorOverflow { msg: "multiply operation overflowed".into(), span, help: "Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into() }) + Err(ShellError::OperatorOverflow { + msg: "multiply operation overflowed".into(), + span, + help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()), + }) } } (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => { @@ -2664,14 +2676,10 @@ impl Value { pub fn div(&self, op: Span, rhs: &Value, span: Span) -> Result { match (self, rhs) { (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => { - if *rhs != 0 { - if lhs % rhs == 0 { - Ok(Value::int(lhs / rhs, span)) - } else { - Ok(Value::float((*lhs as f64) / (*rhs as f64), span)) - } - } else { + if *rhs == 0 { Err(ShellError::DivisionByZero { span: op }) + } else { + Ok(Value::float(*lhs as f64 / *rhs as f64, span)) } } (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => { @@ -2696,57 +2704,73 @@ impl Value { } } (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => { - if *rhs != 0 { - if lhs % rhs == 0 { - Ok(Value::int(lhs / rhs, span)) - } else { - Ok(Value::float((*lhs as f64) / (*rhs as f64), span)) - } - } else { + if *rhs == 0 { Err(ShellError::DivisionByZero { span: op }) + } else { + Ok(Value::float(*lhs as f64 / *rhs as f64, span)) } } (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::filesize( - ((*lhs as f64) / (*rhs as f64)) as i64, - span, - )) - } else { + if let Some(val) = lhs.checked_div(*rhs) { + Ok(Value::filesize(val, span)) + } else if *rhs == 0 { Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) } } (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => { if *rhs != 0.0 { - Ok(Value::filesize((*lhs as f64 / rhs) as i64, span)) + let val = *lhs as f64 / rhs; + if i64::MIN as f64 <= val && val <= i64::MAX as f64 { + Ok(Value::filesize(val as i64, span)) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) + } } else { Err(ShellError::DivisionByZero { span: op }) } } (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => { - if *rhs != 0 { - if lhs % rhs == 0 { - Ok(Value::int(lhs / rhs, span)) - } else { - Ok(Value::float((*lhs as f64) / (*rhs as f64), span)) - } - } else { + if *rhs == 0 { Err(ShellError::DivisionByZero { span: op }) + } else { + Ok(Value::float(*lhs as f64 / *rhs as f64, span)) } } (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::duration( - ((*lhs as f64) / (*rhs as f64)) as i64, - span, - )) - } else { + if let Some(val) = lhs.checked_div(*rhs) { + Ok(Value::duration(val, span)) + } else if *rhs == 0 { Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) } } (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => { if *rhs != 0.0 { - Ok(Value::duration(((*lhs as f64) / rhs) as i64, span)) + let val = *lhs as f64 / rhs; + if i64::MIN as f64 <= val && val <= i64::MAX as f64 { + Ok(Value::duration(val as i64, span)) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) + } } else { Err(ShellError::DivisionByZero { span: op }) } @@ -2765,122 +2789,301 @@ impl Value { } } - pub fn floor_div(&self, op: Span, rhs: &Value, span: Span) -> Result { + pub fn modulo(&self, op: Span, rhs: &Value, span: Span) -> Result { + // Based off the unstable `div_floor` function in the std library. + fn checked_mod_i64(dividend: i64, divisor: i64) -> Option { + let remainder = dividend.checked_rem(divisor)?; + if (remainder > 0 && divisor < 0) || (remainder < 0 && divisor > 0) { + // Note that `remainder + divisor` cannot overflow, because `remainder` and + // `divisor` have opposite signs. + Some(remainder + divisor) + } else { + Some(remainder) + } + } + + fn checked_mod_f64(dividend: f64, divisor: f64) -> Option { + if divisor == 0.0 { + None + } else { + let remainder = dividend % divisor; + if (remainder > 0.0 && divisor < 0.0) || (remainder < 0.0 && divisor > 0.0) { + Some(remainder + divisor) + } else { + Some(remainder) + } + } + } + match (self, rhs) { (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::int( - (*lhs as f64 / *rhs as f64) - .clamp(i64::MIN as f64, i64::MAX as f64) - .floor() as i64, - span, - )) - } else { + if let Some(val) = checked_mod_i64(*lhs, *rhs) { + Ok(Value::int(val, span)) + } else if *rhs == 0 { Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "modulo operation overflowed".into(), + span, + help: None, + }) } } (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => { - if *rhs != 0.0 { - Ok(Value::int( - (*lhs as f64 / *rhs) - .clamp(i64::MIN as f64, i64::MAX as f64) - .floor() as i64, - span, - )) + if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) { + Ok(Value::float(val, span)) } else { Err(ShellError::DivisionByZero { span: op }) } } (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::int( - (*lhs / *rhs as f64) - .clamp(i64::MIN as f64, i64::MAX as f64) - .floor() as i64, - span, - )) + if let Some(val) = checked_mod_f64(*lhs, *rhs as f64) { + Ok(Value::float(val, span)) } else { Err(ShellError::DivisionByZero { span: op }) } } (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => { - if *rhs != 0.0 { - Ok(Value::int( - (lhs / rhs).clamp(i64::MIN as f64, i64::MAX as f64).floor() as i64, - span, - )) + if let Some(val) = checked_mod_f64(*lhs, *rhs) { + Ok(Value::float(val, span)) } else { Err(ShellError::DivisionByZero { span: op }) } } (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::int( - (*lhs as f64 / *rhs as f64) - .clamp(i64::MIN as f64, i64::MAX as f64) - .floor() as i64, - span, - )) - } else { + if let Some(val) = checked_mod_i64(*lhs, *rhs) { + Ok(Value::filesize(val, span)) + } else if *rhs == 0 { Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "modulo operation overflowed".into(), + span, + help: None, + }) } } (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::filesize( - ((*lhs as f64) / (*rhs as f64)) - .clamp(i64::MIN as f64, i64::MAX as f64) - .floor() as i64, - span, - )) - } else { + if let Some(val) = checked_mod_i64(*lhs, *rhs) { + Ok(Value::filesize(val, span)) + } else if *rhs == 0 { Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "modulo operation overflowed".into(), + span, + help: None, + }) } } (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => { - if *rhs != 0.0 { - Ok(Value::filesize( - (*lhs as f64 / *rhs) - .clamp(i64::MIN as f64, i64::MAX as f64) - .floor() as i64, - span, - )) + if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) { + if i64::MIN as f64 <= val && val <= i64::MAX as f64 { + Ok(Value::filesize(val as i64, span)) + } else { + Err(ShellError::OperatorOverflow { + msg: "modulo operation overflowed".into(), + span, + help: None, + }) + } } else { Err(ShellError::DivisionByZero { span: op }) } } (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::int( - (*lhs as f64 / *rhs as f64) - .clamp(i64::MIN as f64, i64::MAX as f64) - .floor() as i64, - span, - )) - } else { + if let Some(val) = checked_mod_i64(*lhs, *rhs) { + Ok(Value::duration(val, span)) + } else if *rhs == 0 { Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) } } (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::duration( - (*lhs as f64 / *rhs as f64) - .clamp(i64::MIN as f64, i64::MAX as f64) - .floor() as i64, + if let Some(val) = checked_mod_i64(*lhs, *rhs) { + Ok(Value::duration(val, span)) + } else if *rhs == 0 { + Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), span, - )) + help: None, + }) + } + } + (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => { + if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) { + if i64::MIN as f64 <= val && val <= i64::MAX as f64 { + Ok(Value::duration(val as i64, span)) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) + } } else { Err(ShellError::DivisionByZero { span: op }) } } - (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => { - if *rhs != 0.0 { - Ok(Value::duration( - (*lhs as f64 / *rhs) - .clamp(i64::MIN as f64, i64::MAX as f64) - .floor() as i64, + (Value::Custom { val: lhs, .. }, rhs) => { + lhs.operation(span, Operator::Math(Math::Modulo), op, rhs) + } + + _ => Err(ShellError::OperatorMismatch { + op_span: op, + lhs_ty: self.get_type().to_string(), + lhs_span: self.span(), + rhs_ty: rhs.get_type().to_string(), + rhs_span: rhs.span(), + }), + } + } + + pub fn floor_div(&self, op: Span, rhs: &Value, span: Span) -> Result { + // Taken from the unstable `div_floor` function in the std library. + fn checked_div_floor_i64(dividend: i64, divisor: i64) -> Option { + let quotient = dividend.checked_div(divisor)?; + let remainder = dividend.checked_rem(divisor)?; + if (remainder > 0 && divisor < 0) || (remainder < 0 && divisor > 0) { + // Note that `quotient - 1` cannot overflow, because: + // `quotient` would have to be `i64::MIN` + // => `divisor` would have to be `1` + // => `remainder` would have to be `0` + // But `remainder == 0` is excluded from the check above. + Some(quotient - 1) + } else { + Some(quotient) + } + } + + fn checked_div_floor_f64(dividend: f64, divisor: f64) -> Option { + if divisor == 0.0 { + None + } else { + Some((dividend / divisor).floor()) + } + } + + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + if let Some(val) = checked_div_floor_i64(*lhs, *rhs) { + Ok(Value::int(val, span)) + } else if *rhs == 0 { + Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), span, - )) + help: None, + }) + } + } + (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => { + if let Some(val) = checked_div_floor_f64(*lhs as f64, *rhs) { + Ok(Value::float(val, span)) + } else { + Err(ShellError::DivisionByZero { span: op }) + } + } + (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + if let Some(val) = checked_div_floor_f64(*lhs, *rhs as f64) { + Ok(Value::float(val, span)) + } else { + Err(ShellError::DivisionByZero { span: op }) + } + } + (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => { + if let Some(val) = checked_div_floor_f64(*lhs, *rhs) { + Ok(Value::float(val, span)) + } else { + Err(ShellError::DivisionByZero { span: op }) + } + } + (Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => { + if let Some(val) = checked_div_floor_i64(*lhs, *rhs) { + Ok(Value::int(val, span)) + } else if *rhs == 0 { + Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) + } + } + (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + if let Some(val) = checked_div_floor_i64(*lhs, *rhs) { + Ok(Value::filesize(val, span)) + } else if *rhs == 0 { + Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) + } + } + (Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => { + if let Some(val) = checked_div_floor_f64(*lhs as f64, *rhs) { + if i64::MIN as f64 <= val && val <= i64::MAX as f64 { + Ok(Value::filesize(val as i64, span)) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) + } + } else { + Err(ShellError::DivisionByZero { span: op }) + } + } + (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => { + if let Some(val) = checked_div_floor_i64(*lhs, *rhs) { + Ok(Value::int(val, span)) + } else if *rhs == 0 { + Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) + } + } + (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + if let Some(val) = checked_div_floor_i64(*lhs, *rhs) { + Ok(Value::duration(val, span)) + } else if *rhs == 0 { + Err(ShellError::DivisionByZero { span: op }) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) + } + } + (Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => { + if let Some(val) = checked_div_floor_f64(*lhs as f64, *rhs) { + if i64::MIN as f64 <= val && val <= i64::MAX as f64 { + Ok(Value::duration(val as i64, span)) + } else { + Err(ShellError::OperatorOverflow { + msg: "division operation overflowed".into(), + span, + help: None, + }) + } } else { Err(ShellError::DivisionByZero { span: op }) } @@ -2888,7 +3091,6 @@ impl Value { (Value::Custom { val: lhs, .. }, rhs) => { lhs.operation(self.span(), Operator::Math(Math::Divide), op, rhs) } - _ => Err(ShellError::OperatorMismatch { op_span: op, lhs_ty: self.get_type().to_string(), @@ -3334,7 +3536,7 @@ impl Value { msg: "right operand to bit-shl exceeds available bits in underlying data" .into(), span, - help: format!("Limit operand to 0 <= rhs < {}", i64::BITS), + help: Some(format!("Limit operand to 0 <= rhs < {}", i64::BITS)), }) } } @@ -3363,7 +3565,7 @@ impl Value { msg: "right operand to bit-shr exceeds available bits in underlying data" .into(), span, - help: format!("Limit operand to 0 <= rhs < {}", i64::BITS), + help: Some(format!("Limit operand to 0 <= rhs < {}", i64::BITS)), }) } } @@ -3434,57 +3636,6 @@ impl Value { } } - pub fn modulo(&self, op: Span, rhs: &Value, span: Span) -> Result { - match (self, rhs) { - (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::int(lhs % rhs, span)) - } else { - Err(ShellError::DivisionByZero { span: op }) - } - } - (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => { - if *rhs != 0.0 { - Ok(Value::float(*lhs as f64 % *rhs, span)) - } else { - Err(ShellError::DivisionByZero { span: op }) - } - } - (Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::float(*lhs % *rhs as f64, span)) - } else { - Err(ShellError::DivisionByZero { span: op }) - } - } - (Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => { - if *rhs != 0.0 { - Ok(Value::float(lhs % rhs, span)) - } else { - Err(ShellError::DivisionByZero { span: op }) - } - } - (Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => { - if *rhs != 0 { - Ok(Value::duration(lhs % rhs, span)) - } else { - Err(ShellError::DivisionByZero { span: op }) - } - } - (Value::Custom { val: lhs, .. }, rhs) => { - lhs.operation(span, Operator::Math(Math::Modulo), op, rhs) - } - - _ => Err(ShellError::OperatorMismatch { - op_span: op, - lhs_ty: self.get_type().to_string(), - lhs_span: self.span(), - rhs_ty: rhs.get_type().to_string(), - rhs_span: rhs.span(), - }), - } - } - pub fn and(&self, op: Span, rhs: &Value, span: Span) -> Result { match (self, rhs) { (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => { @@ -3545,7 +3696,11 @@ impl Value { if let Some(val) = lhs.checked_pow(*rhs as u32) { Ok(Value::int(val, span)) } else { - Err(ShellError::OperatorOverflow { msg: "pow operation overflowed".into(), span, help: "Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into() }) + Err(ShellError::OperatorOverflow { + msg: "pow operation overflowed".into(), + span, + help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()), + }) } } (Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {