Skip to content

Commit 9906c99

Browse files
authored
Do not coerce unknown numbers and preserve known units if present (#6961)
Signed-off-by: Nick Cameron <[email protected]>
1 parent 48d6a21 commit 9906c99

File tree

3 files changed

+65
-15
lines changed

3 files changed

+65
-15
lines changed

rust/kcl-lib/src/execution/exec_ast.rs

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -736,21 +736,35 @@ fn apply_ascription(
736736
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
737737
.map_err(|e| KclError::Semantic(e.into()))?;
738738

739-
if let KclValue::Number { value, meta, .. } = value {
740-
// If the number has unknown units but the user is explicitly specifying them, treat the value as having had it's units erased,
741-
// rather than forcing the user to explicitly erase them.
742-
KclValue::Number {
743-
ty: NumericType::Any,
744-
value: *value,
745-
meta: meta.clone(),
739+
let mut value = value.clone();
740+
741+
// If the number has unknown units but the user is explicitly specifying them, treat the value as having had it's units erased,
742+
// rather than forcing the user to explicitly erase them.
743+
if let KclValue::Number { value: n, meta, .. } = &value {
744+
if let RuntimeType::Primitive(PrimitiveType::Number(num)) = &ty {
745+
if num.is_fully_specified() {
746+
value = KclValue::Number {
747+
ty: NumericType::Any,
748+
value: *n,
749+
meta: meta.clone(),
750+
};
751+
}
746752
}
747-
.coerce(&ty, exec_state)
748-
} else {
749-
value.coerce(&ty, exec_state)
750753
}
751-
.map_err(|_| {
754+
755+
value.coerce(&ty, exec_state).map_err(|_| {
756+
let suggestion = if ty == RuntimeType::length() {
757+
", you might try coercing to a fully specified numeric type such as `number(mm)`"
758+
} else if ty == RuntimeType::angle() {
759+
", you might try coercing to a fully specified numeric type such as `number(deg)`"
760+
} else {
761+
""
762+
};
752763
KclError::Semantic(KclErrorDetails {
753-
message: format!("could not coerce {} value to type {}", value.human_friendly_type(), ty),
764+
message: format!(
765+
"could not coerce {} value to type {ty}{suggestion}",
766+
value.human_friendly_type()
767+
),
754768
source_ranges: vec![source_range],
755769
})
756770
})
@@ -2767,4 +2781,29 @@ startSketchOn(XY)
27672781
// Make sure we get a useful error message and not an engine error.
27682782
assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
27692783
}
2784+
2785+
#[tokio::test(flavor = "multi_thread")]
2786+
async fn coerce_unknown_to_length() {
2787+
let ast = r#"x = 2mm * 2mm
2788+
y = x: number(Length)"#;
2789+
let e = parse_execute(ast).await.unwrap_err();
2790+
assert!(
2791+
e.message().contains("could not coerce"),
2792+
"Error message: '{}'",
2793+
e.message()
2794+
);
2795+
2796+
let ast = r#"x = 2mm
2797+
y = x: number(Length)"#;
2798+
let result = parse_execute(ast).await.unwrap();
2799+
let mem = result.exec_state.stack();
2800+
let num = mem
2801+
.memory
2802+
.get_from("y", result.mem_env, SourceRange::default(), 0)
2803+
.unwrap()
2804+
.as_ty_f64()
2805+
.unwrap();
2806+
assert_eq!(num.n, 2.0);
2807+
assert_eq!(num.ty, NumericType::mm());
2808+
}
27702809
}

rust/kcl-lib/src/execution/types.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,17 @@ impl NumericType {
664664
)
665665
}
666666

667+
pub fn is_fully_specified(&self) -> bool {
668+
!matches!(
669+
self,
670+
NumericType::Unknown
671+
| NumericType::Known(UnitType::Angle(UnitAngle::Unknown))
672+
| NumericType::Known(UnitType::Length(UnitLen::Unknown))
673+
| NumericType::Any
674+
| NumericType::Default { .. }
675+
)
676+
}
677+
667678
fn example_ty(&self) -> Option<String> {
668679
match self {
669680
Self::Known(t) if !self.is_unknown() => Some(t.to_string()),

rust/kcl-lib/src/std/args.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ impl TyF64 {
102102
t => unreachable!("expected length, found {t:?}"),
103103
};
104104

105-
assert_ne!(len, UnitLen::Unknown);
105+
debug_assert_ne!(len, UnitLen::Unknown);
106106

107107
len.adjust_to(self.n, units).0
108108
}
@@ -114,7 +114,7 @@ impl TyF64 {
114114
_ => unreachable!(),
115115
};
116116

117-
assert_ne!(angle, UnitAngle::Unknown);
117+
debug_assert_ne!(angle, UnitAngle::Unknown);
118118

119119
angle.adjust_to(self.n, UnitAngle::Degrees).0
120120
}
@@ -126,7 +126,7 @@ impl TyF64 {
126126
_ => unreachable!(),
127127
};
128128

129-
assert_ne!(angle, UnitAngle::Unknown);
129+
debug_assert_ne!(angle, UnitAngle::Unknown);
130130

131131
angle.adjust_to(self.n, UnitAngle::Radians).0
132132
}

0 commit comments

Comments
 (0)