diff --git a/docs/kcl-lang/numeric.md b/docs/kcl-lang/numeric.md index 5e8650aa0..728c9dfa1 100644 --- a/docs/kcl-lang/numeric.md +++ b/docs/kcl-lang/numeric.md @@ -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 diff --git a/rust/kcl-lib/src/execution/exec_ast.rs b/rust/kcl-lib/src/execution/exec_ast.rs index 5d67cf2d7..8d24a573a 100644 --- a/rust/kcl-lib/src/execution/exec_ast.rs +++ b/rust/kcl-lib/src/execution/exec_ast.rs @@ -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()); + } } diff --git a/rust/kcl-lib/src/execution/types.rs b/rust/kcl-lib/src/execution/types.rs index 0298a2f35..675f66742 100644 --- a/rust/kcl-lib/src/execution/types.rs +++ b/rust/kcl-lib/src/execution/types.rs @@ -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(), diff --git a/rust/kcl-lib/src/std/math.rs b/rust/kcl-lib/src/std/math.rs index 4afe30ab4..27ec48397 100644 --- a/rust/kcl-lib/src/std/math.rs +++ b/rust/kcl-lib/src/std/math.rs @@ -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 Result { 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())))