Some improvements to math::sqrt (#6853)

* Treat number as any rather than default

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Don't square root negative numbers

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-05-12 16:47:01 +12:00
committed by GitHub
parent bbe89f56a7
commit 8445080d7a
4 changed files with 31 additions and 4 deletions

View File

@ -28,6 +28,8 @@ Any of the suffixes described above can be used meaning that values with that ty
You can also use `number(Length)`, `number(Angle)`, or `number(Count)`. These types mean a number with any length, angle, or unitless (count) units, respectively (note that `number(_)` and `number(Count)` are equivalent since there is only one kind of unitless-ness).
Using just `number` means accepting any kind of number, even where the units are unknown by KCL.
## Function calls

View File

@ -2694,4 +2694,13 @@ sketch001 = startSketchOn(XY)
let ast = r#"foo = tan(0): number(rad) - 4deg"#;
parse_execute(ast).await.unwrap();
}
#[tokio::test(flavor = "multi_thread")]
async fn neg_sqrt() {
let ast = r#"bad = sqrt(-2)"#;
let e = parse_execute(ast).await.unwrap_err();
// Make sure we get a useful error message and not an engine error.
assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
}
}

View File

@ -173,9 +173,13 @@ impl RuntimeType {
AstPrimitiveType::Any => RuntimeType::Primitive(PrimitiveType::Any),
AstPrimitiveType::String => RuntimeType::Primitive(PrimitiveType::String),
AstPrimitiveType::Boolean => RuntimeType::Primitive(PrimitiveType::Boolean),
AstPrimitiveType::Number(suffix) => RuntimeType::Primitive(PrimitiveType::Number(
NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
)),
AstPrimitiveType::Number(suffix) => {
let ty = match suffix {
NumericSuffix::None => NumericType::Any,
_ => NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
};
RuntimeType::Primitive(PrimitiveType::Number(ty))
}
AstPrimitiveType::Named(name) => Self::from_alias(&name.name, exec_state, source_range)?,
AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
})
@ -682,6 +686,7 @@ impl NumericType {
// We don't have enough information to coerce.
(Unknown, _) => Err(CoercionError::from(val).with_explicit(self.example_ty().unwrap_or("mm".to_owned()))),
(_, Unknown) => Err(val.into()),
(Any, _) => Ok(KclValue::Number {
value: *value,
ty: self.clone(),

View File

@ -3,7 +3,7 @@
use anyhow::Result;
use crate::{
errors::KclError,
errors::{KclError, KclErrorDetails},
execution::{
types::{ArrayLen, NumericType, RuntimeType},
ExecState, KclValue,
@ -54,6 +54,17 @@ pub async fn tan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
/// Compute the square root of a number.
pub async fn sqrt(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
if input.n < 0.0 {
return Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![args.source_range],
message: format!(
"Attempt to take square root (`sqrt`) of a number less than zero ({})",
input.n
),
}));
}
let result = input.n.sqrt();
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))