More numeric type propagations (#6221)

Last few numeric type propagations

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-04-09 11:46:54 +12:00
committed by GitHub
parent 83f74faaf7
commit 997f539a8c
10 changed files with 132 additions and 37 deletions

View File

@ -360,15 +360,6 @@ impl KclValue {
result
}
/// Put the number into a KCL value.
pub const fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
Self::Number {
value: f,
meta,
ty: NumericType::Unknown,
}
}
pub const fn from_number_with_type(f: f64, ty: NumericType, meta: Vec<Metadata>) -> Self {
Self::Number { value: f, meta, ty }
}

View File

@ -19,7 +19,7 @@ use crate::{
};
lazy_static::lazy_static! {
pub(super) static ref CHECK_NUMERIC_TYPES: bool = {
pub(crate) static ref CHECK_NUMERIC_TYPES: bool = {
let env_var = std::env::var("ZOO_NUM_TYS");
let Ok(env_var) = env_var else {
return false;
@ -416,6 +416,80 @@ impl NumericType {
}
}
pub fn combine_eq_array(input: &[TyF64]) -> (Vec<f64>, NumericType) {
use NumericType::*;
let mut result = input.iter().map(|t| t.n).collect();
let mut ty = Any;
// Invariant mismatch is true => ty is Known
let mut mismatch = false;
for i in input {
if i.ty == Any || ty == i.ty {
continue;
}
match (&ty, &i.ty) {
(Any, t) => {
ty = t.clone();
}
(_, Unknown) | (Default { .. }, Default { .. }) => return (result, Unknown),
// Known types and compatible, but needs adjustment.
(Known(UnitType::Length(_)), Known(UnitType::Length(_)))
| (Known(UnitType::Angle(_)), Known(UnitType::Angle(_))) => {
mismatch = true;
}
// Known but incompatible.
(Known(_), Known(_)) => return (result, Unknown),
// Known and unknown, no adjustment for counting numbers.
(Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
ty = Known(UnitType::Count);
}
(Known(UnitType::Length(l1)), Default { len: l2, .. }) => {
mismatch |= l1 != l2;
}
(Known(UnitType::Angle(a1)), Default { angle: a2, .. }) => {
mismatch |= a1 != a2;
}
(Default { len: l1, .. }, Known(UnitType::Length(l2))) => {
mismatch |= l1 != l2;
ty = Known(UnitType::Length(*l2));
}
(Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => {
mismatch |= a1 != a2;
ty = Known(UnitType::Angle(*a2));
}
(Unknown, _) | (_, Any) => unreachable!(),
}
}
if !mismatch {
return (result, ty);
}
result = result
.into_iter()
.zip(input)
.map(|(n, i)| match (&ty, &i.ty) {
(Known(UnitType::Length(l1)), Known(UnitType::Length(l2)) | Default { len: l2, .. }) => {
l2.adjust_to(n, *l1)
}
(Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2)) | Default { angle: a2, .. }) => {
a2.adjust_to(n, *a1)
}
_ => unreachable!(),
})
.collect();
(result, ty)
}
/// Combine two types for multiplication-like operations.
pub fn combine_mul(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
use NumericType::*;
@ -1847,11 +1921,16 @@ n = 10inch / 2mm
o = 3mm / 3
p = 3_ / 4
q = 4inch / 2_
r = min(0, 3, 42)
s = min(0, 3mm, -42)
t = min(100, 3in, 142mm)
u = min(3rad, 4in)
"#;
let result = parse_execute(program).await.unwrap();
if *CHECK_NUMERIC_TYPES {
assert_eq!(result.exec_state.errors().len(), 2);
assert_eq!(result.exec_state.errors().len(), 3);
} else {
assert!(result.exec_state.errors().is_empty());
}
@ -1875,5 +1954,10 @@ q = 4inch / 2_
assert_value_and_type("o", &result, 1.0, NumericType::mm());
assert_value_and_type("p", &result, 1.0, NumericType::count());
assert_value_and_type("q", &result, 2.0, NumericType::Known(UnitType::Length(UnitLen::Inches)));
assert_value_and_type("r", &result, 0.0, NumericType::default());
assert_value_and_type("s", &result, -42.0, NumericType::mm());
assert_value_and_type("t", &result, 3.0, NumericType::Known(UnitType::Length(UnitLen::Inches)));
assert_value_and_type("u", &result, 3.0, NumericType::Unknown);
}
}

View File

@ -523,15 +523,6 @@ impl Args {
})
}
pub(super) fn make_user_val_from_f64(&self, f: f64) -> KclValue {
KclValue::from_number(
f,
vec![Metadata {
source_range: self.source_range,
}],
)
}
pub(super) fn make_user_val_from_f64_with_type(&self, f: TyF64) -> KclValue {
KclValue::from_number_with_type(
f.n,

View File

@ -133,7 +133,7 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// ```no_run
/// // Declare a function that sketches a decagon.
/// fn decagon(radius) {
/// // Each side of the decagon is turned this many degrees from the previous angle.
/// // Each side of the decagon is turned this many radians from the previous angle.
/// stepAngle = (1/10) * TAU
///
/// // Start the decagon sketch at this point.

View File

@ -6,18 +6,31 @@ use kcl_derive_docs::stdlib;
use super::args::FromArgs;
use crate::{
errors::{KclError, KclErrorDetails},
execution::{types::NumericType, ExecState, KclValue},
execution::{
types::{self, NumericType},
ExecState, KclValue,
},
std::args::{Args, TyF64},
CompilationError,
};
/// Compute the remainder after dividing `num` by `div`.
/// If `num` is negative, the result will be too.
pub async fn rem(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let n = args.get_unlabeled_kw_arg("number to divide")?;
let d = args.get_kw_arg("divisor")?;
pub async fn rem(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let n: TyF64 = args.get_unlabeled_kw_arg("number to divide")?;
let d: TyF64 = args.get_kw_arg("divisor")?;
let (n, d, ty) = NumericType::combine_div(n, d);
if *types::CHECK_NUMERIC_TYPES && ty == NumericType::Unknown {
// TODO suggest how to fix this
exec_state.warn(CompilationError::err(
args.source_range,
"Remainder of numbers which have unknown or incompatible units.",
));
}
let remainder = inner_rem(n, d);
Ok(args.make_user_val_from_f64(remainder))
Ok(args.make_user_val_from_f64_with_type(TyF64::new(remainder, ty)))
}
/// Compute the remainder after dividing `num` by `div`.
@ -243,11 +256,19 @@ fn inner_ceil(num: f64) -> Result<f64, KclError> {
}
/// Compute the minimum of the given arguments.
pub async fn min(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let nums = args.get_number_array()?;
pub async fn min(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let nums = args.get_number_array_with_types()?;
let (nums, ty) = NumericType::combine_eq_array(&nums);
if *types::CHECK_NUMERIC_TYPES && ty == NumericType::Unknown {
// TODO suggest how to fix this
exec_state.warn(CompilationError::err(
args.source_range,
"Calling `min` on numbers which have unknown or incompatible units.",
));
}
let result = inner_min(nums);
Ok(args.make_user_val_from_f64(result))
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, ty)))
}
/// Compute the minimum of the given arguments.
@ -280,11 +301,19 @@ fn inner_min(args: Vec<f64>) -> f64 {
}
/// Compute the maximum of the given arguments.
pub async fn max(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let nums = args.get_number_array()?;
pub async fn max(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let nums = args.get_number_array_with_types()?;
let (nums, ty) = NumericType::combine_eq_array(&nums);
if *types::CHECK_NUMERIC_TYPES && ty == NumericType::Unknown {
// TODO suggest how to fix this
exec_state.warn(CompilationError::err(
args.source_range,
"Calling `max` on numbers which have unknown or incompatible units.",
));
}
let result = inner_max(nums);
Ok(args.make_user_val_from_f64(result))
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, ty)))
}
/// Compute the maximum of the given arguments.