Fix a units bug in multi-module projects (#5529)

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-02-27 15:46:41 +13:00
committed by GitHub
parent 8d9bba02d6
commit 12edb6375d
14 changed files with 282 additions and 120 deletions

View File

@ -351,16 +351,16 @@ export function kclErrorsByFilename(
const sourceRange: SourceRange = error.sourceRange const sourceRange: SourceRange = error.sourceRange
const fileIndex = sourceRange[2] const fileIndex = sourceRange[2]
const modulePath: ModulePath | undefined = filenames[fileIndex] const modulePath: ModulePath | undefined = filenames[fileIndex]
if (modulePath) { if (modulePath && modulePath.type === 'Local') {
let stdOrLocalPath = modulePath.value let localPath = modulePath.value
if (stdOrLocalPath) { if (localPath) {
// Build up an array of errors per file name // Build up an array of errors per file name
const value = fileNameToError.get(stdOrLocalPath) const value = fileNameToError.get(localPath)
if (!value) { if (!value) {
fileNameToError.set(stdOrLocalPath, [error]) fileNameToError.set(localPath, [error])
} else { } else {
value.push(error) value.push(error)
fileNameToError.set(stdOrLocalPath, [error]) fileNameToError.set(localPath, [error])
} }
} }
} }

View File

@ -93,8 +93,14 @@ impl ExecutorContext {
exec_state: &mut ExecState, exec_state: &mut ExecState,
exec_kind: ExecutionKind, exec_kind: ExecutionKind,
preserve_mem: bool, preserve_mem: bool,
) -> Result<(Option<KclValue>, EnvironmentRef), KclError> { path: &ModulePath,
) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
crate::log::log(format!("enter module {path}"));
let old_units = exec_state.length_unit(); let old_units = exec_state.length_unit();
let original_execution = self.engine.replace_execution_kind(exec_kind).await;
let mut local_state = ModuleState::new(&self.settings, path.std_path());
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
let no_prelude = self let no_prelude = self
.handle_annotations(program.inner_attrs.iter(), crate::execution::BodyType::Root, exec_state) .handle_annotations(program.inner_attrs.iter(), crate::execution::BodyType::Root, exec_state)
@ -103,7 +109,6 @@ impl ExecutorContext {
if !preserve_mem { if !preserve_mem {
exec_state.mut_memory().push_new_root_env(!no_prelude); exec_state.mut_memory().push_new_root_env(!no_prelude);
} }
let original_execution = self.engine.replace_execution_kind(exec_kind).await;
let result = self let result = self
.exec_block(program, exec_state, crate::execution::BodyType::Root) .exec_block(program, exec_state, crate::execution::BodyType::Root)
@ -115,12 +120,16 @@ impl ExecutorContext {
} else { } else {
exec_state.mut_memory().pop_env() exec_state.mut_memory().pop_env()
}; };
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
if !exec_kind.is_isolated() && new_units != old_units { if !exec_kind.is_isolated() && new_units != old_units {
self.engine.set_units(old_units.into(), Default::default()).await?; self.engine.set_units(old_units.into(), Default::default()).await?;
} }
self.engine.replace_execution_kind(original_execution).await; self.engine.replace_execution_kind(original_execution).await;
result.map(|result| (result, env_ref)) crate::log::log(format!("leave {path}"));
result.map(|result| (result, env_ref, local_state.module_exports))
} }
/// Execute an AST's program. /// Execute an AST's program.
@ -436,16 +445,10 @@ impl ExecutorContext {
source_range: SourceRange, source_range: SourceRange,
) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> { ) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
exec_state.global.mod_loader.enter_module(path); exec_state.global.mod_loader.enter_module(path);
let mut local_state = ModuleState::new(&self.settings, path.std_path()); let result = self.exec_module_body(program, exec_state, exec_kind, false, path).await;
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
let result = self.exec_module_body(program, exec_state, exec_kind, false).await;
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
exec_state.global.mod_loader.leave_module(path); exec_state.global.mod_loader.leave_module(path);
result result.map_err(|err| {
.map_err(|err| {
if let KclError::ImportCycle(_) = err { if let KclError::ImportCycle(_) = err {
// It was an import cycle. Keep the original message. // It was an import cycle. Keep the original message.
err.override_source_ranges(vec![source_range]) err.override_source_ranges(vec![source_range])
@ -460,7 +463,6 @@ impl ExecutorContext {
}) })
} }
}) })
.map(|(val, env)| (val, env, local_state.module_exports))
} }
#[async_recursion] #[async_recursion]
@ -1033,7 +1035,7 @@ impl Node<CallExpressionKw> {
// before running, and we will likely want to use the // before running, and we will likely want to use the
// return value. The call takes ownership of the args, // return value. The call takes ownership of the args,
// so we need to build the op before the call. // so we need to build the op before the call.
exec_state.mod_local.operations.push(op); exec_state.global.operations.push(op);
} }
result result
}?; }?;
@ -1055,10 +1057,7 @@ impl Node<CallExpressionKw> {
.iter() .iter()
.map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range))) .map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
.collect(); .collect();
exec_state exec_state.global.operations.push(Operation::UserDefinedFunctionCall {
.mod_local
.operations
.push(Operation::UserDefinedFunctionCall {
name: Some(fn_name.clone()), name: Some(fn_name.clone()),
function_source_range: func.function_def_source_range().unwrap_or_default(), function_source_range: func.function_def_source_range().unwrap_or_default(),
unlabeled_arg: args unlabeled_arg: args
@ -1092,10 +1091,7 @@ impl Node<CallExpressionKw> {
})?; })?;
// Track return operation. // Track return operation.
exec_state exec_state.global.operations.push(Operation::UserDefinedFunctionReturn);
.mod_local
.operations
.push(Operation::UserDefinedFunctionReturn);
Ok(result) Ok(result)
} }
@ -1174,7 +1170,7 @@ impl Node<CallExpression> {
// before running, and we will likely want to use the // before running, and we will likely want to use the
// return value. The call takes ownership of the args, // return value. The call takes ownership of the args,
// so we need to build the op before the call. // so we need to build the op before the call.
exec_state.mod_local.operations.push(op); exec_state.global.operations.push(op);
} }
result result
}?; }?;
@ -1190,10 +1186,7 @@ impl Node<CallExpression> {
let func = exec_state.memory().get(fn_name, source_range)?.clone(); let func = exec_state.memory().get(fn_name, source_range)?.clone();
// Track call operation. // Track call operation.
exec_state exec_state.global.operations.push(Operation::UserDefinedFunctionCall {
.mod_local
.operations
.push(Operation::UserDefinedFunctionCall {
name: Some(fn_name.clone()), name: Some(fn_name.clone()),
function_source_range: func.function_def_source_range().unwrap_or_default(), function_source_range: func.function_def_source_range().unwrap_or_default(),
unlabeled_arg: None, unlabeled_arg: None,
@ -1224,10 +1217,7 @@ impl Node<CallExpression> {
})?; })?;
// Track return operation. // Track return operation.
exec_state exec_state.global.operations.push(Operation::UserDefinedFunctionReturn);
.mod_local
.operations
.push(Operation::UserDefinedFunctionReturn);
Ok(result) Ok(result)
} }

View File

@ -724,7 +724,7 @@ impl ExecutorContext {
KclErrorWithOutputs::new( KclErrorWithOutputs::new(
e, e,
exec_state.mod_local.operations.clone(), exec_state.global.operations.clone(),
exec_state.global.artifact_commands.clone(), exec_state.global.artifact_commands.clone(),
exec_state.global.artifact_graph.clone(), exec_state.global.artifact_graph.clone(),
module_id_to_module_path, module_id_to_module_path,
@ -760,7 +760,13 @@ impl ExecutorContext {
.await?; .await?;
let exec_result = self let exec_result = self
.exec_module_body(program, exec_state, ExecutionKind::Normal, preserve_mem) .exec_module_body(
program,
exec_state,
ExecutionKind::Normal,
preserve_mem,
&ModulePath::Main,
)
.await; .await;
// Move the artifact commands and responses to simplify cache management // Move the artifact commands and responses to simplify cache management
@ -782,7 +788,7 @@ impl ExecutorContext {
) { ) {
Ok(artifact_graph) => { Ok(artifact_graph) => {
exec_state.global.artifact_graph = artifact_graph; exec_state.global.artifact_graph = artifact_graph;
exec_result.map(|(_, env_ref)| env_ref) exec_result.map(|(_, env_ref, _)| env_ref)
} }
Err(err) => { Err(err) => {
// Prefer the exec error. // Prefer the exec error.

View File

@ -49,6 +49,9 @@ pub(super) struct GlobalState {
pub artifact_responses: IndexMap<Uuid, WebSocketResponse>, pub artifact_responses: IndexMap<Uuid, WebSocketResponse>,
/// Output artifact graph. /// Output artifact graph.
pub artifact_graph: ArtifactGraph, pub artifact_graph: ArtifactGraph,
/// Operations that have been performed in execution order, for display in
/// the Feature Tree.
pub operations: Vec<Operation>,
/// Module loader. /// Module loader.
pub mod_loader: ModuleLoader, pub mod_loader: ModuleLoader,
/// Errors and warnings. /// Errors and warnings.
@ -62,9 +65,6 @@ pub(super) struct ModuleState {
pub pipe_value: Option<KclValue>, pub pipe_value: Option<KclValue>,
/// Identifiers that have been exported from the current module. /// Identifiers that have been exported from the current module.
pub module_exports: Vec<String>, pub module_exports: Vec<String>,
/// Operations that have been performed in execution order, for display in
/// the Feature Tree.
pub operations: Vec<Operation>,
/// Settings specified from annotations. /// Settings specified from annotations.
pub settings: MetaSettings, pub settings: MetaSettings,
} }
@ -119,7 +119,7 @@ impl ExecState {
.find_all_in_env(main_ref, |_| true) .find_all_in_env(main_ref, |_| true)
.map(|(k, v)| (k.clone(), v.clone())) .map(|(k, v)| (k.clone(), v.clone()))
.collect(), .collect(),
operations: self.mod_local.operations, operations: self.global.operations,
artifacts: self.global.artifacts, artifacts: self.global.artifacts,
artifact_commands: self.global.artifact_commands, artifact_commands: self.global.artifact_commands,
artifact_graph: self.global.artifact_graph, artifact_graph: self.global.artifact_graph,
@ -224,6 +224,7 @@ impl GlobalState {
artifact_commands: Default::default(), artifact_commands: Default::default(),
artifact_responses: Default::default(), artifact_responses: Default::default(),
artifact_graph: Default::default(), artifact_graph: Default::default(),
operations: Default::default(),
mod_loader: Default::default(), mod_loader: Default::default(),
errors: Default::default(), errors: Default::default(),
}; };
@ -252,7 +253,6 @@ impl ModuleState {
ModuleState { ModuleState {
pipe_value: Default::default(), pipe_value: Default::default(),
module_exports: Default::default(), module_exports: Default::default(),
operations: Default::default(),
settings: MetaSettings { settings: MetaSettings {
default_length_units: exec_settings.units.into(), default_length_units: exec_settings.units.into(),
default_angle_units: Default::default(), default_angle_units: Default::default(),

View File

@ -122,6 +122,8 @@ pub enum ModuleRepr {
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash, ts_rs::TS)] #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash, ts_rs::TS)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum ModulePath { pub enum ModulePath {
// The main file of the project.
Main,
Local { value: PathBuf }, Local { value: PathBuf },
Std { value: String }, Std { value: String },
} }
@ -136,8 +138,8 @@ impl ModulePath {
pub(crate) fn std_path(&self) -> Option<String> { pub(crate) fn std_path(&self) -> Option<String> {
match self { match self {
ModulePath::Local { value: _ } => None,
ModulePath::Std { value: p } => Some(p.clone()), ModulePath::Std { value: p } => Some(p.clone()),
_ => None,
} }
} }
@ -152,6 +154,7 @@ impl ModulePath {
}) })
}) })
.map(str::to_owned), .map(str::to_owned),
ModulePath::Main => unreachable!(),
} }
} }
@ -179,6 +182,7 @@ impl ModulePath {
impl fmt::Display for ModulePath { impl fmt::Display for ModulePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
ModulePath::Main => write!(f, "main"),
ModulePath::Local { value: path } => path.display().fmt(f), ModulePath::Local { value: path } => path.display().fmt(f),
ModulePath::Std { value: s } => write!(f, "std::{s}"), ModulePath::Std { value: s } => write!(f, "std::{s}"),
} }

View File

@ -1,7 +1,6 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
description: Operations executed add_lots.kcl description: Operations executed add_lots.kcl
snapshot_kind: text
--- ---
[ [
{ {

View File

@ -305,18 +305,6 @@ description: Artifact commands assembly_non_default_units.kcl
"unit": "in" "unit": "in"
} }
}, },
{
"cmdId": "[uuid]",
"range": [
0,
33,
5
],
"command": {
"type": "set_scene_units",
"unit": "in"
}
},
{ {
"cmdId": "[uuid]", "cmdId": "[uuid]",
"range": [ "range": [
@ -435,18 +423,6 @@ description: Artifact commands assembly_non_default_units.kcl
"path_id": "[uuid]" "path_id": "[uuid]"
} }
}, },
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
},
{ {
"cmdId": "[uuid]", "cmdId": "[uuid]",
"range": [ "range": [
@ -576,17 +552,5 @@ description: Artifact commands assembly_non_default_units.kcl
"type": "close_path", "type": "close_path",
"path_id": "[uuid]" "path_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [
0,
0,
0
],
"command": {
"type": "set_scene_units",
"unit": "mm"
}
} }
] ]

View File

@ -2,4 +2,51 @@
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
description: Operations executed assembly_non_default_units.kcl description: Operations executed assembly_non_default_units.kcl
--- ---
[] [
{
"labeledArgs": {
"data": {
"value": {
"type": "String",
"value": "XZ"
},
"sourceRange": [
186,
190,
3
]
}
},
"name": "startSketchOn",
"sourceRange": [
172,
191,
3
],
"type": "StdLibCall",
"unlabeledArg": null
},
{
"labeledArgs": {
"data": {
"value": {
"type": "String",
"value": "XZ"
},
"sourceRange": [
103,
107,
4
]
}
},
"name": "startSketchOn",
"sourceRange": [
89,
108,
4
],
"type": "StdLibCall",
"unlabeledArg": null
}
]

View File

@ -1,7 +1,6 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
description: Operations executed basic_fillet_cube_next_adjacent.kcl description: Operations executed basic_fillet_cube_next_adjacent.kcl
snapshot_kind: text
--- ---
[ [
{ {

View File

@ -1,7 +1,6 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
description: Operations executed fillet-and-shell.kcl description: Operations executed fillet-and-shell.kcl
snapshot_kind: text
--- ---
[ [
{ {

View File

@ -1,7 +1,6 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
description: Operations executed i_shape.kcl description: Operations executed i_shape.kcl
snapshot_kind: text
--- ---
[ [
{ {

View File

@ -1,6 +1,70 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
description: Operations executed import_function_not_sketch.kcl description: Operations executed import_function_not_sketch.kcl
snapshot_kind: text
--- ---
[] [
{
"labeledArgs": {
"data": {
"value": {
"type": "String",
"value": "XY"
},
"sourceRange": [
66,
70,
3
]
}
},
"name": "startSketchOn",
"sourceRange": [
52,
71,
3
],
"type": "StdLibCall",
"unlabeledArg": null
},
{
"labeledArgs": {
"data": {
"value": {
"type": "Object",
"value": {
"axis": {
"type": "String",
"value": "y"
}
}
},
"sourceRange": [
312,
326,
3
]
},
"sketch": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": [
328,
329,
3
]
}
},
"name": "revolve",
"sourceRange": [
304,
330,
3
],
"type": "StdLibCall",
"unlabeledArg": null
}
]

View File

@ -2,4 +2,28 @@
source: kcl/src/simulation_tests.rs source: kcl/src/simulation_tests.rs
description: Operations executed import_side_effect.kcl description: Operations executed import_side_effect.kcl
--- ---
[] [
{
"labeledArgs": {
"data": {
"value": {
"type": "String",
"value": "XY"
},
"sourceRange": [
95,
99,
3
]
}
},
"name": "startSketchOn",
"sourceRange": [
81,
100,
3
],
"type": "StdLibCall",
"unlabeledArg": null
}
]

View File

@ -3,6 +3,73 @@ source: kcl/src/simulation_tests.rs
description: Operations executed import_whole.kcl description: Operations executed import_whole.kcl
--- ---
[ [
{
"labeledArgs": {
"data": {
"value": {
"type": "String",
"value": "XY"
},
"sourceRange": [
50,
54,
3
]
}
},
"name": "startSketchOn",
"sourceRange": [
36,
55,
3
],
"type": "StdLibCall",
"unlabeledArg": null
},
{
"labeledArgs": {
"length": {
"value": {
"type": "Number",
"value": 10.0,
"ty": {
"type": "Default",
"len": {
"type": "Inches"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": [
127,
129,
3
]
}
},
"name": "extrude",
"sourceRange": [
110,
130,
3
],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": [
0,
0,
0
]
}
},
{ {
"labeledArgs": { "labeledArgs": {
"faces": { "faces": {