Proper type-checking where a function requires a tag identifier not just a tag (#6848)

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-05-12 12:57:50 +12:00
committed by GitHub
parent c65190a158
commit 97594b9a9e
4 changed files with 24 additions and 13 deletions

View File

@ -72,6 +72,10 @@ impl RuntimeType {
RuntimeType::Primitive(PrimitiveType::Tag) RuntimeType::Primitive(PrimitiveType::Tag)
} }
pub fn tag_identifier() -> Self {
RuntimeType::Primitive(PrimitiveType::TagId)
}
pub fn bool() -> Self { pub fn bool() -> Self {
RuntimeType::Primitive(PrimitiveType::Boolean) RuntimeType::Primitive(PrimitiveType::Boolean)
} }
@ -337,6 +341,8 @@ pub enum PrimitiveType {
String, String,
Boolean, Boolean,
Tag, Tag,
// Annoyingly some functions only want a TagIdentifier, not any kind of tag.
TagId,
Sketch, Sketch,
Solid, Solid,
Plane, Plane,
@ -365,12 +371,14 @@ impl PrimitiveType {
PrimitiveType::Axis3d => "3d axes".to_owned(), PrimitiveType::Axis3d => "3d axes".to_owned(),
PrimitiveType::ImportedGeometry => "imported geometries".to_owned(), PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
PrimitiveType::Tag => "tags".to_owned(), PrimitiveType::Tag => "tags".to_owned(),
PrimitiveType::TagId => "tag identifiers".to_owned(),
} }
} }
fn subtype(&self, other: &PrimitiveType) -> bool { fn subtype(&self, other: &PrimitiveType) -> bool {
match (self, other) { match (self, other) {
(PrimitiveType::Number(n1), PrimitiveType::Number(n2)) => n1.subtype(n2), (PrimitiveType::Number(n1), PrimitiveType::Number(n2)) => n1.subtype(n2),
(PrimitiveType::TagId, PrimitiveType::Tag) => true,
(t1, t2) => t1 == t2, (t1, t2) => t1 == t2,
} }
} }
@ -386,6 +394,7 @@ impl fmt::Display for PrimitiveType {
PrimitiveType::String => write!(f, "string"), PrimitiveType::String => write!(f, "string"),
PrimitiveType::Boolean => write!(f, "bool"), PrimitiveType::Boolean => write!(f, "bool"),
PrimitiveType::Tag => write!(f, "tag"), PrimitiveType::Tag => write!(f, "tag"),
PrimitiveType::TagId => write!(f, "tag identifier"),
PrimitiveType::Sketch => write!(f, "Sketch"), PrimitiveType::Sketch => write!(f, "Sketch"),
PrimitiveType::Solid => write!(f, "Solid"), PrimitiveType::Solid => write!(f, "Solid"),
PrimitiveType::Plane => write!(f, "Plane"), PrimitiveType::Plane => write!(f, "Plane"),
@ -1159,6 +1168,10 @@ impl KclValue {
KclValue::ImportedGeometry { .. } => Ok(value.clone()), KclValue::ImportedGeometry { .. } => Ok(value.clone()),
_ => Err(self.into()), _ => Err(self.into()),
}, },
PrimitiveType::TagId => match value {
KclValue::TagIdentifier { .. } => Ok(value.clone()),
_ => Err(self.into()),
},
PrimitiveType::Tag => match value { PrimitiveType::Tag => match value {
KclValue::TagDeclarator { .. } | KclValue::TagIdentifier { .. } | KclValue::Uuid { .. } => { KclValue::TagDeclarator { .. } | KclValue::TagIdentifier { .. } | KclValue::Uuid { .. } => {
Ok(value.clone()) Ok(value.clone())

View File

@ -408,12 +408,9 @@ impl Args {
})?; })?;
T::from_kcl_val(&arg).ok_or_else(|| { T::from_kcl_val(&arg).ok_or_else(|| {
KclError::Semantic(KclErrorDetails { KclError::Internal(KclErrorDetails {
source_ranges: vec![self.source_range], source_ranges: vec![self.source_range],
message: format!( message: "Mismatch between type coercion and value extraction (this isn't your fault).\nTo assist in bug-reporting, expected type: {ty:?}; actual value: {arg:?}".to_owned(),
"This function expected the input argument to be {}",
ty.human_friendly_type(),
),
}) })
}) })
} }

View File

@ -14,7 +14,7 @@ use crate::{
/// Get the opposite edge to the edge given. /// Get the opposite edge to the edge given.
pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::edge(), exec_state)?; let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::tag_identifier(), exec_state)?;
let edge = inner_get_opposite_edge(input_edge, exec_state, args.clone()).await?; let edge = inner_get_opposite_edge(input_edge, exec_state, args.clone()).await?;
Ok(KclValue::Uuid { Ok(KclValue::Uuid {
@ -98,7 +98,7 @@ async fn inner_get_opposite_edge(
/// Get the next adjacent edge to the edge given. /// Get the next adjacent edge to the edge given.
pub async fn get_next_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn get_next_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::edge(), exec_state)?; let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::tag_identifier(), exec_state)?;
let edge = inner_get_next_adjacent_edge(input_edge, exec_state, args.clone()).await?; let edge = inner_get_next_adjacent_edge(input_edge, exec_state, args.clone()).await?;
Ok(KclValue::Uuid { Ok(KclValue::Uuid {
@ -191,7 +191,7 @@ async fn inner_get_next_adjacent_edge(
/// Get the previous adjacent edge to the edge given. /// Get the previous adjacent edge to the edge given.
pub async fn get_previous_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn get_previous_adjacent_edge(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::edge(), exec_state)?; let input_edge = args.get_unlabeled_kw_arg_typed("edge", &RuntimeType::tag_identifier(), exec_state)?;
let edge = inner_get_previous_adjacent_edge(input_edge, exec_state, args.clone()).await?; let edge = inner_get_previous_adjacent_edge(input_edge, exec_state, args.clone()).await?;
Ok(KclValue::Uuid { Ok(KclValue::Uuid {

View File

@ -4,11 +4,12 @@ description: Error from executing panic_repro_cube.kcl
--- ---
KCL Semantic error KCL Semantic error
× semantic: This function expected the input argument to be Edge × semantic: This function expected the input argument to be tag identifier
╭─[43:5] │ but it's actually of type Unique ID (uuid)
╭─[43:25]
42 │ // these double wrapped functions are the point of this test 42 │ // these double wrapped functions are the point of this test
43 │ getNextAdjacentEdge(getNextAdjacentEdge(seg01)), 43 │ getNextAdjacentEdge(getNextAdjacentEdge(seg01)),
· ───────────────────────┬─────────────────────── · ─────────────┬────────────
· ╰── tests/panic_repro_cube/input.kcl · ╰── tests/panic_repro_cube/input.kcl
44 │ getNextAdjacentEdge(getNextAdjacentEdge(seg02)) 44 │ getNextAdjacentEdge(getNextAdjacentEdge(seg02))
╰──── ╰────