From 76c1d58905926808eb474c33d6a2665b4636f966 Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 17:14:08 +0400 Subject: [PATCH 1/5] test: Add failing test for cross-module constant dependencies --- air-script/tests/codegen/winterfell.rs | 12 +++++++++++ .../cross_module_constants/constants_lib.air | 21 +++++++++++++++++++ .../cross_module_constants.air | 21 +++++++++++++++++++ .../cross_module_constants.rs | 2 ++ .../tests/cross_module_constants/mod.rs | 1 + air-script/tests/mod.rs | 2 ++ 6 files changed, 59 insertions(+) create mode 100644 air-script/tests/cross_module_constants/constants_lib.air create mode 100644 air-script/tests/cross_module_constants/cross_module_constants.air create mode 100644 air-script/tests/cross_module_constants/cross_module_constants.rs create mode 100644 air-script/tests/cross_module_constants/mod.rs diff --git a/air-script/tests/codegen/winterfell.rs b/air-script/tests/codegen/winterfell.rs index 2fa96ccb8..e2c9fc621 100644 --- a/air-script/tests/codegen/winterfell.rs +++ b/air-script/tests/codegen/winterfell.rs @@ -365,3 +365,15 @@ fn variables() { let expected = expect_file!["../variables/variables.rs"]; expected.assert_eq(&generated_air); } + +#[test] +fn cross_module_constants() { + // Test that constants used in comprehension iterables work across module boundaries + let generated_air = + Test::new("tests/cross_module_constants/cross_module_constants.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../cross_module_constants/cross_module_constants.rs"]; + expected.assert_eq(&generated_air); +} diff --git a/air-script/tests/cross_module_constants/constants_lib.air b/air-script/tests/cross_module_constants/constants_lib.air new file mode 100644 index 000000000..25ef19270 --- /dev/null +++ b/air-script/tests/cross_module_constants/constants_lib.air @@ -0,0 +1,21 @@ +mod constants_lib + +# Constants used in comprehension iterables +const WEIGHTS = [1, 2, 3, 4]; + +# Pure function that uses constants in a comprehension +fn weighted_sum(values: felt[4]) -> felt { + return sum([v * w for (v, w) in (values, WEIGHTS)]); +} + +# Another function that calls the first +fn compute_result(a: felt, b: felt, c: felt, d: felt) -> felt { + let values = [a, b, c, d]; + return weighted_sum(values); +} + +# Evaluator that uses the chain of functions +ev apply_computation([cols[5]]) { + let result = compute_result(cols[0], cols[1], cols[2], cols[3]); + enf cols[4] = result; +} diff --git a/air-script/tests/cross_module_constants/cross_module_constants.air b/air-script/tests/cross_module_constants/cross_module_constants.air new file mode 100644 index 000000000..87315ecd6 --- /dev/null +++ b/air-script/tests/cross_module_constants/cross_module_constants.air @@ -0,0 +1,21 @@ +def CrossModuleConstantsTest + +# Import evaluator that uses a chain: apply_computation -> compute_result -> weighted_sum -> WEIGHTS constant +use constants_lib::apply_computation; + +trace_columns { + main: [a, b, c, d, result], +} + +public_inputs { + expected: [1], +} + +boundary_constraints { + enf a.first = 0; +} + +integrity_constraints { + # Use imported evaluator that internally uses constants in comprehensions + enf apply_computation([a, b, c, d, result]); +} diff --git a/air-script/tests/cross_module_constants/cross_module_constants.rs b/air-script/tests/cross_module_constants/cross_module_constants.rs new file mode 100644 index 000000000..44bfcdb23 --- /dev/null +++ b/air-script/tests/cross_module_constants/cross_module_constants.rs @@ -0,0 +1,2 @@ +// This file will be overwritten by the test with generated code +// It serves as the expected output for the cross_module_constants test diff --git a/air-script/tests/cross_module_constants/mod.rs b/air-script/tests/cross_module_constants/mod.rs new file mode 100644 index 000000000..fa516c053 --- /dev/null +++ b/air-script/tests/cross_module_constants/mod.rs @@ -0,0 +1 @@ +mod cross_module_constants; diff --git a/air-script/tests/mod.rs b/air-script/tests/mod.rs index e96e603d8..822c1afcf 100644 --- a/air-script/tests/mod.rs +++ b/air-script/tests/mod.rs @@ -15,6 +15,8 @@ mod constant_in_range; #[allow(unused_variables, dead_code, unused_mut)] mod constants; #[allow(unused_variables, dead_code, unused_mut)] +mod cross_module_constants; +#[allow(unused_variables, dead_code, unused_mut)] mod constraint_comprehension; #[allow(unused_variables, dead_code, unused_mut)] mod evaluators; From 217ddc989458f3b23f2f13ec9d293cf5367a86bd Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 17:15:11 +0400 Subject: [PATCH 2/5] fix: Handle cross-module constant dependencies in dependency graph and MIR translation --- .../cross_module_constants.rs | 99 ++++++++++++++++++- mir/src/passes/translate.rs | 4 + parser/src/sema/semantic_analysis.rs | 6 +- 3 files changed, 104 insertions(+), 5 deletions(-) diff --git a/air-script/tests/cross_module_constants/cross_module_constants.rs b/air-script/tests/cross_module_constants/cross_module_constants.rs index 44bfcdb23..5de652283 100644 --- a/air-script/tests/cross_module_constants/cross_module_constants.rs +++ b/air-script/tests/cross_module_constants/cross_module_constants.rs @@ -1,2 +1,97 @@ -// This file will be overwritten by the test with generated code -// It serves as the expected output for the cross_module_constants test +use winter_air::{Air, AirContext, Assertion, AuxRandElements, EvaluationFrame, ProofOptions as WinterProofOptions, TransitionConstraintDegree, TraceInfo}; +use winter_math::fields::f64::BaseElement as Felt; +use winter_math::{ExtensionOf, FieldElement, ToElements}; +use winter_utils::{ByteWriter, Serializable}; + +pub struct PublicInputs { + expected: [Felt; 1], +} + +impl PublicInputs { + pub fn new(expected: [Felt; 1]) -> Self { + Self { expected } + } +} + +impl Serializable for PublicInputs { + fn write_into(&self, target: &mut W) { + self.expected.write_into(target); + } +} + +impl ToElements for PublicInputs { + fn to_elements(&self) -> Vec { + let mut elements = Vec::new(); + elements.extend_from_slice(&self.expected); + elements + } +} + +pub struct CrossModuleConstantsTest { + context: AirContext, + expected: [Felt; 1], +} + +impl CrossModuleConstantsTest { + pub fn last_step(&self) -> usize { + self.trace_length() - self.context().num_transition_exemptions() + } +} + +impl Air for CrossModuleConstantsTest { + type BaseField = Felt; + type PublicInputs = PublicInputs; + + fn context(&self) -> &AirContext { + &self.context + } + + fn new(trace_info: TraceInfo, public_inputs: PublicInputs, options: WinterProofOptions) -> Self { + let main_degrees = vec![TransitionConstraintDegree::new(1)]; + let aux_degrees = vec![]; + let num_main_assertions = 1; + let num_aux_assertions = 0; + + let context = AirContext::new_multi_segment( + trace_info, + main_degrees, + aux_degrees, + num_main_assertions, + num_aux_assertions, + options, + ) + .set_num_transition_exemptions(2); + Self { context, expected: public_inputs.expected } + } + + fn get_periodic_column_values(&self) -> Vec> { + vec![] + } + + fn get_assertions(&self) -> Vec> { + let mut result = Vec::new(); + result.push(Assertion::single(0, 0, Felt::ZERO)); + result + } + + fn get_aux_assertions>(&self, aux_rand_elements: &AuxRandElements) -> Vec> { + let mut result = Vec::new(); + result + } + + fn evaluate_transition>(&self, frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E]) { + let main_current = frame.current(); + let main_next = frame.next(); + result[0] = main_current[4] - (main_current[0] + main_current[1] * E::from(Felt::new(2_u64)) + main_current[2] * E::from(Felt::new(3_u64)) + main_current[3] * E::from(Felt::new(4_u64))); + } + + fn evaluate_aux_transition(&self, main_frame: &EvaluationFrame, aux_frame: &EvaluationFrame, _periodic_values: &[F], aux_rand_elements: &AuxRandElements, result: &mut [E]) + where F: FieldElement, + E: FieldElement + ExtensionOf, + { + let main_current = main_frame.current(); + let main_next = main_frame.next(); + let aux_current = aux_frame.current(); + let aux_next = aux_frame.next(); + } +} \ No newline at end of file diff --git a/mir/src/passes/translate.rs b/mir/src/passes/translate.rs index db342ecb7..6a631fe50 100644 --- a/mir/src/passes/translate.rs +++ b/mir/src/passes/translate.rs @@ -714,6 +714,10 @@ impl<'a> MirBuilder<'a> { }) .build(); Ok(node) + } else if let Some(constant) = self.program.constants.get(qual_ident) { + // Handle qualified constant references that weren't inlined + // (e.g., constants used in comprehension iterables across modules) + self.translate_const(&constant.value, access.span()) } else { // This is a qualified reference that should have been eliminated // during inlining or constant propagation, but somehow slipped through. diff --git a/parser/src/sema/semantic_analysis.rs b/parser/src/sema/semantic_analysis.rs index 09d89426f..8bda93fc3 100644 --- a/parser/src/sema/semantic_analysis.rs +++ b/parser/src/sema/semantic_analysis.rs @@ -444,9 +444,9 @@ impl VisitMut for SemanticAnalysis<'_> { self.current_module.clone().unwrap(), NamespacedIdentifier::Function(function.name), ); - let current_item_node_index = self.deps_graph.add_node(current_item); - for (referenced_item, ref_type) in self.referenced.iter() { - let referenced_item_node_index = self.deps_graph.add_node(referenced_item.clone()); + let current_item_node_index = self.get_node_index_or_add(¤t_item); + for (referenced_item, ref_type) in self.referenced.clone().iter() { + let referenced_item_node_index = self.get_node_index_or_add(referenced_item); self.deps_graph.add_edge( current_item_node_index, referenced_item_node_index, From 164844f7798baa126d5dd9c46259cf65434ef561 Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:41:54 +0400 Subject: [PATCH 3/5] test: Add failing test for comprehension periodic binding scoping --- air-script/tests/codegen/winterfell.rs | 13 +++ .../comprehension_periodic_binding.air | 19 ++++ .../comprehension_periodic_binding.rs | 97 +++++++++++++++++++ .../comprehension_periodic_binding/lib.air | 16 +++ .../comprehension_periodic_binding/mod.rs | 3 + air-script/tests/mod.rs | 2 + 6 files changed, 150 insertions(+) create mode 100644 air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.air create mode 100644 air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs create mode 100644 air-script/tests/comprehension_periodic_binding/lib.air create mode 100644 air-script/tests/comprehension_periodic_binding/mod.rs diff --git a/air-script/tests/codegen/winterfell.rs b/air-script/tests/codegen/winterfell.rs index e2c9fc621..3e8e30911 100644 --- a/air-script/tests/codegen/winterfell.rs +++ b/air-script/tests/codegen/winterfell.rs @@ -377,3 +377,16 @@ fn cross_module_constants() { let expected = expect_file!["../cross_module_constants/cross_module_constants.rs"]; expected.assert_eq(&generated_air); } + +#[test] +fn comprehension_periodic_binding() { + // Test that comprehension bindings over periodic columns are typed as Local, not PeriodicColumn + // This pattern is used when iterating over a vector containing periodic column references + let generated_air = + Test::new("tests/comprehension_periodic_binding/comprehension_periodic_binding.air".to_string()) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = expect_file!["../comprehension_periodic_binding/comprehension_periodic_binding.rs"]; + expected.assert_eq(&generated_air); +} diff --git a/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.air b/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.air new file mode 100644 index 000000000..a21bbcbd5 --- /dev/null +++ b/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.air @@ -0,0 +1,19 @@ +def ComprehensionPeriodicBindingTest + +use lib::test_comprehension; + +trace_columns { + main: [a, b], +} + +public_inputs { + stack_inputs: [1], +} + +boundary_constraints { + enf a.first = 0; +} + +integrity_constraints { + enf test_comprehension([a, b]); +} diff --git a/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs b/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs new file mode 100644 index 000000000..9e56fa9e2 --- /dev/null +++ b/air-script/tests/comprehension_periodic_binding/comprehension_periodic_binding.rs @@ -0,0 +1,97 @@ +use winter_air::{Air, AirContext, Assertion, AuxRandElements, EvaluationFrame, ProofOptions as WinterProofOptions, TransitionConstraintDegree, TraceInfo}; +use winter_math::fields::f64::BaseElement as Felt; +use winter_math::{ExtensionOf, FieldElement, ToElements}; +use winter_utils::{ByteWriter, Serializable}; + +pub struct PublicInputs { + stack_inputs: [Felt; 1], +} + +impl PublicInputs { + pub fn new(stack_inputs: [Felt; 1]) -> Self { + Self { stack_inputs } + } +} + +impl Serializable for PublicInputs { + fn write_into(&self, target: &mut W) { + self.stack_inputs.write_into(target); + } +} + +impl ToElements for PublicInputs { + fn to_elements(&self) -> Vec { + let mut elements = Vec::new(); + elements.extend_from_slice(&self.stack_inputs); + elements + } +} + +pub struct ComprehensionPeriodicBindingTest { + context: AirContext, + stack_inputs: [Felt; 1], +} + +impl ComprehensionPeriodicBindingTest { + pub fn last_step(&self) -> usize { + self.trace_length() - self.context().num_transition_exemptions() + } +} + +impl Air for ComprehensionPeriodicBindingTest { + type BaseField = Felt; + type PublicInputs = PublicInputs; + + fn context(&self) -> &AirContext { + &self.context + } + + fn new(trace_info: TraceInfo, public_inputs: PublicInputs, options: WinterProofOptions) -> Self { + let main_degrees = vec![TransitionConstraintDegree::with_cycles(1, vec![2, 2])]; + let aux_degrees = vec![]; + let num_main_assertions = 1; + let num_aux_assertions = 0; + + let context = AirContext::new_multi_segment( + trace_info, + main_degrees, + aux_degrees, + num_main_assertions, + num_aux_assertions, + options, + ) + .set_num_transition_exemptions(2); + Self { context, stack_inputs: public_inputs.stack_inputs } + } + + fn get_periodic_column_values(&self) -> Vec> { + vec![vec![Felt::ONE, Felt::new(2)], vec![Felt::new(3), Felt::new(4)]] + } + + fn get_assertions(&self) -> Vec> { + let mut result = Vec::new(); + result.push(Assertion::single(0, 0, Felt::ZERO)); + result + } + + fn get_aux_assertions>(&self, aux_rand_elements: &AuxRandElements) -> Vec> { + let mut result = Vec::new(); + result + } + + fn evaluate_transition>(&self, frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E]) { + let main_current = frame.current(); + let main_next = frame.next(); + result[0] = main_next[0] - (main_current[0] * periodic_values[0] + main_current[1] * periodic_values[1]); + } + + fn evaluate_aux_transition(&self, main_frame: &EvaluationFrame, aux_frame: &EvaluationFrame, _periodic_values: &[F], aux_rand_elements: &AuxRandElements, result: &mut [E]) + where F: FieldElement, + E: FieldElement + ExtensionOf, + { + let main_current = main_frame.current(); + let main_next = main_frame.next(); + let aux_current = aux_frame.current(); + let aux_next = aux_frame.next(); + } +} \ No newline at end of file diff --git a/air-script/tests/comprehension_periodic_binding/lib.air b/air-script/tests/comprehension_periodic_binding/lib.air new file mode 100644 index 000000000..8667dc9ae --- /dev/null +++ b/air-script/tests/comprehension_periodic_binding/lib.air @@ -0,0 +1,16 @@ +mod lib + +periodic_columns { + k0: [1, 2], + k1: [3, 4], +} + +ev test_comprehension([a, b]) { + # Create local variable holding periodic column references + let cols = [k0, k1]; + let vals = [a, b]; + + # Iterate over the local variable - binding 'k' gets typed as PeriodicColumn + # but it's actually a local variable holding a value + enf a' = sum([x * k for (x, k) in (vals, cols)]); +} diff --git a/air-script/tests/comprehension_periodic_binding/mod.rs b/air-script/tests/comprehension_periodic_binding/mod.rs new file mode 100644 index 000000000..83244b98e --- /dev/null +++ b/air-script/tests/comprehension_periodic_binding/mod.rs @@ -0,0 +1,3 @@ +#[rustfmt::skip] +#[allow(clippy::all)] +mod comprehension_periodic_binding; diff --git a/air-script/tests/mod.rs b/air-script/tests/mod.rs index 822c1afcf..2d7b05307 100644 --- a/air-script/tests/mod.rs +++ b/air-script/tests/mod.rs @@ -11,6 +11,8 @@ mod buses; #[allow(unused_variables, dead_code, unused_mut)] mod computed_indices; #[allow(unused_variables, dead_code, unused_mut)] +mod comprehension_periodic_binding; +#[allow(unused_variables, dead_code, unused_mut)] mod constant_in_range; #[allow(unused_variables, dead_code, unused_mut)] mod constants; From d645907b097e979989147069209951c58e8b012b Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:41:59 +0400 Subject: [PATCH 4/5] fix: Convert comprehension bindings to Local type for proper scoping --- parser/src/sema/semantic_analysis.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/parser/src/sema/semantic_analysis.rs b/parser/src/sema/semantic_analysis.rs index 8bda93fc3..b4bf9597e 100644 --- a/parser/src/sema/semantic_analysis.rs +++ b/parser/src/sema/semantic_analysis.rs @@ -633,6 +633,15 @@ impl VisitMut for SemanticAnalysis<'_> { 0, ))))) .expect("unexpected scalar iterable"); + // Comprehension bindings are local variables holding values, not direct + // references to module-level declarations like periodic columns or constants. + // Convert these to Local bindings to ensure proper scoping. + let binding_ty = match binding_ty { + BindingType::PeriodicColumn(_) | BindingType::Constant(_) => { + BindingType::Local(binding_ty.ty().unwrap_or(Type::Felt)) + }, + other => other, + }; binding_tys.push((binding, iterable.span(), Some(binding_ty))); }, Err(InvalidAccessError::InvalidBinding) => { From 7ddb26c7bf1b7c82110fc604aa8d4a5654e2b793 Mon Sep 17 00:00:00 2001 From: al <82364884+Al-Kindi-0@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:19:33 +0400 Subject: [PATCH 5/5] misc. fixes --- air-script/tests/codegen/winterfell.rs | 16 +++++++++------- ...odule_constants.rs => cross_mod_constants.rs} | 0 air-script/tests/cross_module_constants/mod.rs | 4 +++- air-script/tests/mod.rs | 8 ++++---- mir/src/passes/translate.rs | 9 +++++---- parser/src/ast/expression.rs | 6 +++--- parser/src/ast/trace.rs | 2 +- parser/src/lexer/mod.rs | 7 +++---- parser/src/parser/grammar.lalrpop | 4 ++-- parser/src/parser/tests/mod.rs | 4 ++-- 10 files changed, 32 insertions(+), 28 deletions(-) rename air-script/tests/cross_module_constants/{cross_module_constants.rs => cross_mod_constants.rs} (100%) diff --git a/air-script/tests/codegen/winterfell.rs b/air-script/tests/codegen/winterfell.rs index 3e8e30911..efd1538d2 100644 --- a/air-script/tests/codegen/winterfell.rs +++ b/air-script/tests/codegen/winterfell.rs @@ -374,7 +374,7 @@ fn cross_module_constants() { .transpile(Target::Winterfell) .unwrap(); - let expected = expect_file!["../cross_module_constants/cross_module_constants.rs"]; + let expected = expect_file!["../cross_module_constants/cross_mod_constants.rs"]; expected.assert_eq(&generated_air); } @@ -382,11 +382,13 @@ fn cross_module_constants() { fn comprehension_periodic_binding() { // Test that comprehension bindings over periodic columns are typed as Local, not PeriodicColumn // This pattern is used when iterating over a vector containing periodic column references - let generated_air = - Test::new("tests/comprehension_periodic_binding/comprehension_periodic_binding.air".to_string()) - .transpile(Target::Winterfell) - .unwrap(); - - let expected = expect_file!["../comprehension_periodic_binding/comprehension_periodic_binding.rs"]; + let generated_air = Test::new( + "tests/comprehension_periodic_binding/comprehension_periodic_binding.air".to_string(), + ) + .transpile(Target::Winterfell) + .unwrap(); + + let expected = + expect_file!["../comprehension_periodic_binding/comprehension_periodic_binding.rs"]; expected.assert_eq(&generated_air); } diff --git a/air-script/tests/cross_module_constants/cross_module_constants.rs b/air-script/tests/cross_module_constants/cross_mod_constants.rs similarity index 100% rename from air-script/tests/cross_module_constants/cross_module_constants.rs rename to air-script/tests/cross_module_constants/cross_mod_constants.rs diff --git a/air-script/tests/cross_module_constants/mod.rs b/air-script/tests/cross_module_constants/mod.rs index fa516c053..e10ee53c7 100644 --- a/air-script/tests/cross_module_constants/mod.rs +++ b/air-script/tests/cross_module_constants/mod.rs @@ -1 +1,3 @@ -mod cross_module_constants; +#[rustfmt::skip] +#[allow(clippy::all)] +mod cross_mod_constants; diff --git a/air-script/tests/mod.rs b/air-script/tests/mod.rs index 2d7b05307..e54fb7fdd 100644 --- a/air-script/tests/mod.rs +++ b/air-script/tests/mod.rs @@ -9,18 +9,18 @@ mod bitwise; #[allow(unused_variables, dead_code, unused_mut)] mod buses; #[allow(unused_variables, dead_code, unused_mut)] -mod computed_indices; -#[allow(unused_variables, dead_code, unused_mut)] mod comprehension_periodic_binding; #[allow(unused_variables, dead_code, unused_mut)] +mod computed_indices; +#[allow(unused_variables, dead_code, unused_mut)] mod constant_in_range; #[allow(unused_variables, dead_code, unused_mut)] mod constants; #[allow(unused_variables, dead_code, unused_mut)] -mod cross_module_constants; -#[allow(unused_variables, dead_code, unused_mut)] mod constraint_comprehension; #[allow(unused_variables, dead_code, unused_mut)] +mod cross_module_constants; +#[allow(unused_variables, dead_code, unused_mut)] mod evaluators; #[allow(unused_variables, dead_code, unused_mut)] mod fibonacci; diff --git a/mir/src/passes/translate.rs b/mir/src/passes/translate.rs index 6a631fe50..e1e54acf9 100644 --- a/mir/src/passes/translate.rs +++ b/mir/src/passes/translate.rs @@ -695,7 +695,7 @@ impl<'a> MirBuilder<'a> { // At this point during compilation, fully-qualified identifiers can only possibly refer // to a periodic column, as all functions have been inlined, and constants propagated. ast::ResolvableIdentifier::Resolved(qual_ident) => { - if let Some(pc) = self.mir.periodic_columns.get(&qual_ident).cloned() { + if let Some(pc) = self.mir.periodic_columns.get(qual_ident).cloned() { let node = Value::builder() .value(SpannedMirValue { span: access.span(), @@ -706,7 +706,7 @@ impl<'a> MirBuilder<'a> { }) .build(); Ok(node) - } else if let Some(bus) = self.mir.constraint_graph().get_bus_link(&qual_ident) { + } else if let Some(bus) = self.mir.constraint_graph().get_bus_link(qual_ident) { let node = Value::builder() .value(SpannedMirValue { span: access.span(), @@ -739,7 +739,7 @@ impl<'a> MirBuilder<'a> { }, // This must be one of public inputs or trace columns ast::ResolvableIdentifier::Global(ident) | ast::ResolvableIdentifier::Local(ident) => { - self.translate_symbol_access_global_or_local(&ident, access) + self.translate_symbol_access_global_or_local(ident, access) }, // These should have been eliminated by previous compiler passes ast::ResolvableIdentifier::Unresolved(_ident) => { @@ -1067,7 +1067,8 @@ impl<'a> MirBuilder<'a> { access: &'a ast::SymbolAccess, ) -> Option> { // If it's a slice access, we need to create a vector of MirAccessType::Index - if let AccessType::Slice(ast::RangeExpr { start, end, .. }) = &access.access_type { + if let AccessType::Slice(range) = &access.access_type { + let ast::RangeExpr { start, end, .. } = range.as_ref(); let ( ast::RangeBound::Const(Span { item: start, .. }), ast::RangeBound::Const(Span { item: end, .. }), diff --git a/parser/src/ast/expression.rs b/parser/src/ast/expression.rs index 97f26fa94..1ad12bb80 100644 --- a/parser/src/ast/expression.rs +++ b/parser/src/ast/expression.rs @@ -865,7 +865,7 @@ pub enum AccessType { #[default] Default, /// Access binds a sub-slice of a vector - Slice(RangeExpr), + Slice(Box), /// Access binds the value at a specific index of an aggregate value (i.e. vector or matrix) /// /// The result type may be either a scalar or a vector, depending on the type of the aggregate @@ -1073,7 +1073,7 @@ impl SymbolAccess { Err(InvalidAccessError::IndexOutOfBounds) }, Type::Vector(_) => Ok(Self { - access_type: AccessType::Slice(shifted), + access_type: AccessType::Slice(Box::new(shifted.clone())), ty: Some(Type::Vector(rlen)), ..self.clone() }), @@ -1081,7 +1081,7 @@ impl SymbolAccess { Err(InvalidAccessError::IndexOutOfBounds) }, Type::Matrix(_, cols) => Ok(Self { - access_type: AccessType::Slice(shifted), + access_type: AccessType::Slice(Box::new(shifted)), ty: Some(Type::Matrix(rlen, cols)), ..self.clone() }), diff --git a/parser/src/ast/trace.rs b/parser/src/ast/trace.rs index 5b26a05d0..71fab9071 100644 --- a/parser/src/ast/trace.rs +++ b/parser/src/ast/trace.rs @@ -287,7 +287,7 @@ impl TraceBinding { let range_expr1 = range_expr1.to_slice_range(); let combined_range = (range_expr.start + range_expr1.start)..(range_expr.end + range_expr1.end); - AccessType::Slice(combined_range.into()) + AccessType::Slice(Box::new(combined_range.into())) }, (AccessType::Slice(range_expr), AccessType::Index(index_expr)) => { let range_expr_usize = range_expr.to_slice_range(); diff --git a/parser/src/lexer/mod.rs b/parser/src/lexer/mod.rs index f354cf6f4..8265300ae 100644 --- a/parser/src/lexer/mod.rs +++ b/parser/src/lexer/mod.rs @@ -644,10 +644,9 @@ where match num.parse::() { Ok(i) => Token::Num(i), - Err(err) => Token::Error(LexicalError::InvalidInt { - span: self.span(), - reason: err.kind().clone(), - }), + Err(err) => { + Token::Error(LexicalError::InvalidInt { span: self.span(), reason: *err.kind() }) + }, } } } diff --git a/parser/src/parser/grammar.lalrpop b/parser/src/parser/grammar.lalrpop index b06578f68..e05958009 100644 --- a/parser/src/parser/grammar.lalrpop +++ b/parser/src/parser/grammar.lalrpop @@ -560,7 +560,7 @@ SymbolAccessBaseSpanned: Span<(Identifier, AccessType)> = { SymbolAccessBase: (Identifier, AccessType) = { => (ident, AccessType::Default), - "[" "]" => (ident, AccessType::Slice(range)), + "[" "]" => (ident, AccessType::Slice(Box::new(range))), => (ident, AccessType::Index(idx)), => (ident, AccessType::Matrix(row, col)), // accessing an identifier used in a section declaration, like a named trace segment, e.g. $main @@ -611,7 +611,7 @@ Iterables: Vec = { Iterable: Expr = { => Expr::SymbolAccess(SymbolAccess::new(ident.span(), ident, AccessType::Default, 0)), => Expr::Range(range), - "[" "]" => Expr::SymbolAccess(SymbolAccess::new(span!(l, r), ident, AccessType::Slice(range), 0)), + "[" "]" => Expr::SymbolAccess(SymbolAccess::new(span!(l, r), ident, AccessType::Slice(Box::new(range)), 0)), => if let ScalarExpr::Call(call) = function_call { Expr::Call(call) } else { diff --git a/parser/src/parser/tests/mod.rs b/parser/src/parser/tests/mod.rs index aa1a162e1..1b18c60fc 100644 --- a/parser/src/parser/tests/mod.rs +++ b/parser/src/parser/tests/mod.rs @@ -426,7 +426,7 @@ macro_rules! slice { ScalarExpr::SymbolAccess(SymbolAccess { span: miden_diagnostics::SourceSpan::UNKNOWN, name: ResolvableIdentifier::Unresolved(NamespacedIdentifier::Binding(ident!($name))), - access_type: AccessType::Slice($range.into()), + access_type: AccessType::Slice(Box::new($range.into())), offset: 0, ty: None, }) @@ -436,7 +436,7 @@ macro_rules! slice { ScalarExpr::SymbolAccess(SymbolAccess { span: miden_diagnostics::SourceSpan::UNKNOWN, name: ResolvableIdentifier::Local(ident!($name)), - access_type: AccessType::Slice($range.into()), + access_type: AccessType::Slice(Box::new($range.into())), offset: 0, ty: Some($ty), })