Do not coerce unknown numbers and preserve known units if present (#6961)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
@ -736,21 +736,35 @@ fn apply_ascription(
|
|||||||
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
|
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
|
||||||
.map_err(|e| KclError::Semantic(e.into()))?;
|
.map_err(|e| KclError::Semantic(e.into()))?;
|
||||||
|
|
||||||
if let KclValue::Number { value, meta, .. } = value {
|
let mut value = value.clone();
|
||||||
|
|
||||||
// If the number has unknown units but the user is explicitly specifying them, treat the value as having had it's units erased,
|
// If the number has unknown units but the user is explicitly specifying them, treat the value as having had it's units erased,
|
||||||
// rather than forcing the user to explicitly erase them.
|
// rather than forcing the user to explicitly erase them.
|
||||||
KclValue::Number {
|
if let KclValue::Number { value: n, meta, .. } = &value {
|
||||||
|
if let RuntimeType::Primitive(PrimitiveType::Number(num)) = &ty {
|
||||||
|
if num.is_fully_specified() {
|
||||||
|
value = KclValue::Number {
|
||||||
ty: NumericType::Any,
|
ty: NumericType::Any,
|
||||||
value: *value,
|
value: *n,
|
||||||
meta: meta.clone(),
|
meta: meta.clone(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
.coerce(&ty, exec_state)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value.coerce(&ty, exec_state).map_err(|_| {
|
||||||
|
let suggestion = if ty == RuntimeType::length() {
|
||||||
|
", you might try coercing to a fully specified numeric type such as `number(mm)`"
|
||||||
|
} else if ty == RuntimeType::angle() {
|
||||||
|
", you might try coercing to a fully specified numeric type such as `number(deg)`"
|
||||||
} else {
|
} else {
|
||||||
value.coerce(&ty, exec_state)
|
""
|
||||||
}
|
};
|
||||||
.map_err(|_| {
|
|
||||||
KclError::Semantic(KclErrorDetails {
|
KclError::Semantic(KclErrorDetails {
|
||||||
message: format!("could not coerce {} value to type {}", value.human_friendly_type(), ty),
|
message: format!(
|
||||||
|
"could not coerce {} value to type {ty}{suggestion}",
|
||||||
|
value.human_friendly_type()
|
||||||
|
),
|
||||||
source_ranges: vec![source_range],
|
source_ranges: vec![source_range],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -2767,4 +2781,29 @@ startSketchOn(XY)
|
|||||||
// Make sure we get a useful error message and not an engine error.
|
// Make sure we get a useful error message and not an engine error.
|
||||||
assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
|
assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn coerce_unknown_to_length() {
|
||||||
|
let ast = r#"x = 2mm * 2mm
|
||||||
|
y = x: number(Length)"#;
|
||||||
|
let e = parse_execute(ast).await.unwrap_err();
|
||||||
|
assert!(
|
||||||
|
e.message().contains("could not coerce"),
|
||||||
|
"Error message: '{}'",
|
||||||
|
e.message()
|
||||||
|
);
|
||||||
|
|
||||||
|
let ast = r#"x = 2mm
|
||||||
|
y = x: number(Length)"#;
|
||||||
|
let result = parse_execute(ast).await.unwrap();
|
||||||
|
let mem = result.exec_state.stack();
|
||||||
|
let num = mem
|
||||||
|
.memory
|
||||||
|
.get_from("y", result.mem_env, SourceRange::default(), 0)
|
||||||
|
.unwrap()
|
||||||
|
.as_ty_f64()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(num.n, 2.0);
|
||||||
|
assert_eq!(num.ty, NumericType::mm());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -664,6 +664,17 @@ impl NumericType {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_fully_specified(&self) -> bool {
|
||||||
|
!matches!(
|
||||||
|
self,
|
||||||
|
NumericType::Unknown
|
||||||
|
| NumericType::Known(UnitType::Angle(UnitAngle::Unknown))
|
||||||
|
| NumericType::Known(UnitType::Length(UnitLen::Unknown))
|
||||||
|
| NumericType::Any
|
||||||
|
| NumericType::Default { .. }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn example_ty(&self) -> Option<String> {
|
fn example_ty(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
Self::Known(t) if !self.is_unknown() => Some(t.to_string()),
|
Self::Known(t) if !self.is_unknown() => Some(t.to_string()),
|
||||||
|
@ -102,7 +102,7 @@ impl TyF64 {
|
|||||||
t => unreachable!("expected length, found {t:?}"),
|
t => unreachable!("expected length, found {t:?}"),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_ne!(len, UnitLen::Unknown);
|
debug_assert_ne!(len, UnitLen::Unknown);
|
||||||
|
|
||||||
len.adjust_to(self.n, units).0
|
len.adjust_to(self.n, units).0
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ impl TyF64 {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_ne!(angle, UnitAngle::Unknown);
|
debug_assert_ne!(angle, UnitAngle::Unknown);
|
||||||
|
|
||||||
angle.adjust_to(self.n, UnitAngle::Degrees).0
|
angle.adjust_to(self.n, UnitAngle::Degrees).0
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ impl TyF64 {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_ne!(angle, UnitAngle::Unknown);
|
debug_assert_ne!(angle, UnitAngle::Unknown);
|
||||||
|
|
||||||
angle.adjust_to(self.n, UnitAngle::Radians).0
|
angle.adjust_to(self.n, UnitAngle::Radians).0
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user