From e59c4519f6d63abb314026249a852fbf6fcf7943 Mon Sep 17 00:00:00 2001 From: jcastil0 Date: Wed, 25 Mar 2026 21:09:14 +0800 Subject: [PATCH 1/2] bugfix(lowering): propagate snapshot pop success values --- .../src/optimizations/const_folding.rs | 27 +- .../src/optimizations/const_folding_test.rs | 326 +++++++++++++++++- 2 files changed, 345 insertions(+), 8 deletions(-) diff --git a/crates/cairo-lang-lowering/src/optimizations/const_folding.rs b/crates/cairo-lang-lowering/src/optimizations/const_folding.rs index 46980a110e2..e7e8025caaf 100644 --- a/crates/cairo-lang-lowering/src/optimizations/const_folding.rs +++ b/crates/cairo-lang-lowering/src/optimizations/const_folding.rs @@ -1325,18 +1325,35 @@ impl<'db, 'mt> ConstFoldingContext<'db, 'mt> { let var_info = self.var_info.get(&info.inputs[0].var_id)?; let desnapped = try_extract_matches!(var_info.as_ref(), VarInfo::Snapshot)?; let element_var_infos = try_extract_matches!(desnapped.as_ref(), VarInfo::Array)?; - // TODO(orizi): Propagate success values as well. - if element_var_infos.is_empty() { + let wrap_snapshot_array = |var_infos: Vec>>>| { + VarInfo::Snapshot(VarInfo::Array(var_infos).into()) + }; + if let Some((element_var_info, remaining_var_infos)) = if id + == self.array_snapshot_pop_front + { + element_var_infos.split_first().map(|(first, rest)| (first.clone(), rest.to_vec())) + } else { + element_var_infos.split_last().map(|(last, rest)| (last.clone(), rest.to_vec())) + } { + let arm = &info.arms[0]; + self.var_info + .insert(arm.var_ids[0], wrap_snapshot_array(remaining_var_infos).into()); + if let Some(element_var_info) = element_var_info { + self.var_info.insert( + arm.var_ids[1], + VarInfo::Box(VarInfo::Snapshot(element_var_info).into()).into(), + ); + } + None + } else { let arm = &info.arms[1]; - self.var_info.insert(arm.var_ids[0], VarInfo::Array(vec![]).into()); + self.var_info.insert(arm.var_ids[0], wrap_snapshot_array(vec![]).into()); Some(BlockEnd::Goto( arm.block_id, VarRemapping { remapping: FromIterator::from_iter([(arm.var_ids[0], info.inputs[0])]), }, )) - } else { - None } } else { None diff --git a/crates/cairo-lang-lowering/src/optimizations/const_folding_test.rs b/crates/cairo-lang-lowering/src/optimizations/const_folding_test.rs index eea926bdf68..6f7609b5a08 100644 --- a/crates/cairo-lang-lowering/src/optimizations/const_folding_test.rs +++ b/crates/cairo-lang-lowering/src/optimizations/const_folding_test.rs @@ -1,15 +1,31 @@ use cairo_lang_debug::DebugWithDb; +use cairo_lang_semantic::corelib::{ + CorelibSemantic, core_array_felt252_ty, core_box_ty, get_usize_ty, option_none_variant, + option_some_variant, +}; use cairo_lang_semantic::db::SemanticGroup; -use cairo_lang_semantic::test_utils::setup_test_function; +use cairo_lang_semantic::helper::ModuleHelper; +use cairo_lang_semantic::items::constant::{ConstValue, ConstValueId}; +use cairo_lang_semantic::test_utils::{setup_test_function, setup_test_function_ex}; +use cairo_lang_semantic::{GenericArgumentId, MatchArmSelector, TypeLongId}; use cairo_lang_test_utils::parse_test_file::TestRunnerResult; +use cairo_lang_utils::Intern; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; +use num_bigint::BigInt; +use salsa::Database; -use crate::LoweringStage; +use super::const_folding; use crate::db::LoweringGroup; use crate::fmt::LoweredFormatter; -use crate::ids::ConcreteFunctionWithBodyId; +use crate::ids::{ConcreteFunctionWithBodyId, SemanticFunctionIdEx}; +use crate::objects::blocks::BlocksBuilder; use crate::optimizations::strategy::OptimizationPhase; use crate::test_utils::LoweringDatabaseForTesting; +use crate::{ + Block, BlockEnd, Lowered, LoweringStage, MatchArm, MatchExternInfo, MatchInfo, Statement, + StatementCall, StatementConst, StatementDesnap, StatementSnapshot, StatementUnbox, VarUsage, + Variable, VariableArena, +}; cairo_lang_test_utils::test_file_test!( const_folding, @@ -70,3 +86,307 @@ fn test_match_optimizer( ("lowering_diagnostics".into(), lowering_diagnostics.format(db)), ])) } + +fn alloc_var<'db>( + db: &'db dyn Database, + variables: &mut VariableArena<'db>, + ty: cairo_lang_semantic::TypeId<'db>, + location: crate::ids::LocationId<'db>, +) -> crate::VariableId { + variables.alloc(Variable::with_default_context(db, ty, location)) +} + +fn int_const<'db>( + db: &'db dyn Database, + ty: cairo_lang_semantic::TypeId<'db>, + value: i64, +) -> ConstValueId<'db> { + ConstValueId::from_int(db, ty, &BigInt::from(value)) +} + +fn assert_int_const<'db>(db: &'db dyn Database, stmt: &Statement<'db>, expected: i64) { + let Statement::Const(StatementConst { value, boxed: false, .. }) = stmt else { + panic!("Expected const statement, got {stmt:?}"); + }; + let ConstValue::Int(actual, _) = value.long(db) else { + panic!("Expected integer const, got {:?}", value.long(db)); + }; + assert_eq!(actual, &BigInt::from(expected)); +} + +#[test] +fn const_folding_propagates_snapshot_pop_front_success_outputs() { + let db = &mut LoweringDatabaseForTesting::default(); + let (test_function, _) = + setup_test_function_ex(db, "fn foo() -> usize { 0 }", "foo", "", None, None).split(); + let function_id = + ConcreteFunctionWithBodyId::from_semantic(db, test_function.concrete_function_id); + let template = db.lowered_body(function_id, LoweringStage::PreOptimizations).unwrap().clone(); + let location = template.signature.location; + + let felt_ty = db.core_info().felt252; + let usize_ty = get_usize_ty(db); + let array_ty = core_array_felt252_ty(db); + let snapshot_array_ty = TypeLongId::Snapshot(array_ty).intern(db); + let snapshot_felt_ty = TypeLongId::Snapshot(felt_ty).intern(db); + let box_snapshot_felt_ty = core_box_ty(db, snapshot_felt_ty); + + let array_module = ModuleHelper::core(db).submodule("array"); + let generic_args = vec![GenericArgumentId::Type(felt_ty)]; + let array_new = array_module.function_id("array_new", generic_args.clone()).lowered(db); + let array_append = array_module.function_id("array_append", generic_args.clone()).lowered(db); + let array_snapshot_pop_front = + array_module.function_id("array_snapshot_pop_front", generic_args.clone()).lowered(db); + let array_len = array_module.function_id("array_len", generic_args).lowered(db); + + let mut variables = VariableArena::default(); + let arr0 = alloc_var(db, &mut variables, array_ty, location); + let one = alloc_var(db, &mut variables, felt_ty, location); + let arr1 = alloc_var(db, &mut variables, array_ty, location); + let arr2 = alloc_var(db, &mut variables, array_ty, location); + let snapshot = alloc_var(db, &mut variables, snapshot_array_ty, location); + let success_arr = alloc_var(db, &mut variables, snapshot_array_ty, location); + let success_box = alloc_var(db, &mut variables, box_snapshot_felt_ty, location); + let failure_arr = alloc_var(db, &mut variables, snapshot_array_ty, location); + let len_out = alloc_var(db, &mut variables, usize_ty, location); + let failure_out = alloc_var(db, &mut variables, usize_ty, location); + + let root = Block { + statements: vec![ + Statement::Call(StatementCall { + function: array_new, + inputs: vec![], + with_coupon: false, + outputs: vec![arr0], + location, + is_specialization_base_call: false, + }), + Statement::Const(StatementConst::new_flat(int_const(db, felt_ty, 1), one)), + Statement::Call(StatementCall { + function: array_append, + inputs: vec![ + VarUsage { var_id: arr0, location }, + VarUsage { var_id: one, location }, + ], + with_coupon: false, + outputs: vec![arr1], + location, + is_specialization_base_call: false, + }), + Statement::Snapshot(StatementSnapshot::new( + VarUsage { var_id: arr1, location }, + arr2, + snapshot, + )), + ], + end: BlockEnd::Match { + info: MatchInfo::Extern(MatchExternInfo { + function: array_snapshot_pop_front, + inputs: vec![VarUsage { var_id: snapshot, location }], + arms: vec![ + MatchArm { + arm_selector: MatchArmSelector::VariantId(option_some_variant( + db, + box_snapshot_felt_ty, + )), + block_id: crate::BlockId(1), + var_ids: vec![success_arr, success_box], + }, + MatchArm { + arm_selector: MatchArmSelector::VariantId(option_none_variant( + db, + box_snapshot_felt_ty, + )), + block_id: crate::BlockId(2), + var_ids: vec![failure_arr], + }, + ], + location, + }), + }, + }; + let success = Block { + statements: vec![Statement::Call(StatementCall { + function: array_len, + inputs: vec![VarUsage { var_id: success_arr, location }], + with_coupon: false, + outputs: vec![len_out], + location, + is_specialization_base_call: false, + })], + end: BlockEnd::Return(vec![VarUsage { var_id: len_out, location }], location), + }; + let failure = Block { + statements: vec![Statement::Const(StatementConst::new_flat( + int_const(db, usize_ty, 99), + failure_out, + ))], + end: BlockEnd::Return(vec![VarUsage { var_id: failure_out, location }], location), + }; + + let mut lowered = Lowered { + diagnostics: template.diagnostics.clone(), + signature: template.signature.clone(), + variables, + blocks: BlocksBuilder(vec![root, success, failure]).build().unwrap(), + parameters: vec![], + }; + + const_folding(db, function_id, &mut lowered); + + assert_int_const(db, &lowered.blocks[crate::BlockId(1)].statements[0], 0); +} + +#[test] +fn const_folding_propagates_snapshot_pop_back_success_outputs() { + let db = &mut LoweringDatabaseForTesting::default(); + let (test_function, _) = + setup_test_function_ex(db, "fn foo() -> felt252 { 0 }", "foo", "", None, None).split(); + let function_id = + ConcreteFunctionWithBodyId::from_semantic(db, test_function.concrete_function_id); + let template = db.lowered_body(function_id, LoweringStage::PreOptimizations).unwrap().clone(); + let location = template.signature.location; + + let felt_ty = db.core_info().felt252; + let array_ty = core_array_felt252_ty(db); + let snapshot_array_ty = TypeLongId::Snapshot(array_ty).intern(db); + let snapshot_felt_ty = TypeLongId::Snapshot(felt_ty).intern(db); + let box_snapshot_felt_ty = core_box_ty(db, snapshot_felt_ty); + + let core = ModuleHelper::core(db); + let array_module = core.submodule("array"); + let generic_args = vec![GenericArgumentId::Type(felt_ty)]; + let array_new = array_module.function_id("array_new", generic_args.clone()).lowered(db); + let array_append = array_module.function_id("array_append", generic_args.clone()).lowered(db); + let array_snapshot_pop_back = + array_module.function_id("array_snapshot_pop_back", generic_args).lowered(db); + let felt_add = core.function_id("felt252_add", vec![]).lowered(db); + + let mut variables = VariableArena::default(); + let arr0 = alloc_var(db, &mut variables, array_ty, location); + let one = alloc_var(db, &mut variables, felt_ty, location); + let arr1 = alloc_var(db, &mut variables, array_ty, location); + let two = alloc_var(db, &mut variables, felt_ty, location); + let arr2 = alloc_var(db, &mut variables, array_ty, location); + let arr3 = alloc_var(db, &mut variables, array_ty, location); + let snapshot = alloc_var(db, &mut variables, snapshot_array_ty, location); + let success_arr = alloc_var(db, &mut variables, snapshot_array_ty, location); + let success_box = alloc_var(db, &mut variables, box_snapshot_felt_ty, location); + let failure_arr = alloc_var(db, &mut variables, snapshot_array_ty, location); + let snapshot_value = alloc_var(db, &mut variables, snapshot_felt_ty, location); + let value = alloc_var(db, &mut variables, felt_ty, location); + let addend = alloc_var(db, &mut variables, felt_ty, location); + let sum = alloc_var(db, &mut variables, felt_ty, location); + let failure_out = alloc_var(db, &mut variables, felt_ty, location); + + let root = Block { + statements: vec![ + Statement::Call(StatementCall { + function: array_new, + inputs: vec![], + with_coupon: false, + outputs: vec![arr0], + location, + is_specialization_base_call: false, + }), + Statement::Const(StatementConst::new_flat(int_const(db, felt_ty, 1), one)), + Statement::Call(StatementCall { + function: array_append, + inputs: vec![ + VarUsage { var_id: arr0, location }, + VarUsage { var_id: one, location }, + ], + with_coupon: false, + outputs: vec![arr1], + location, + is_specialization_base_call: false, + }), + Statement::Const(StatementConst::new_flat(int_const(db, felt_ty, 2), two)), + Statement::Call(StatementCall { + function: array_append, + inputs: vec![ + VarUsage { var_id: arr1, location }, + VarUsage { var_id: two, location }, + ], + with_coupon: false, + outputs: vec![arr2], + location, + is_specialization_base_call: false, + }), + Statement::Snapshot(StatementSnapshot::new( + VarUsage { var_id: arr2, location }, + arr3, + snapshot, + )), + ], + end: BlockEnd::Match { + info: MatchInfo::Extern(MatchExternInfo { + function: array_snapshot_pop_back, + inputs: vec![VarUsage { var_id: snapshot, location }], + arms: vec![ + MatchArm { + arm_selector: MatchArmSelector::VariantId(option_some_variant( + db, + box_snapshot_felt_ty, + )), + block_id: crate::BlockId(1), + var_ids: vec![success_arr, success_box], + }, + MatchArm { + arm_selector: MatchArmSelector::VariantId(option_none_variant( + db, + box_snapshot_felt_ty, + )), + block_id: crate::BlockId(2), + var_ids: vec![failure_arr], + }, + ], + location, + }), + }, + }; + let success = Block { + statements: vec![ + Statement::Unbox(StatementUnbox { + input: VarUsage { var_id: success_box, location }, + output: snapshot_value, + }), + Statement::Desnap(StatementDesnap { + input: VarUsage { var_id: snapshot_value, location }, + output: value, + }), + Statement::Const(StatementConst::new_flat(int_const(db, felt_ty, 1), addend)), + Statement::Call(StatementCall { + function: felt_add, + inputs: vec![ + VarUsage { var_id: value, location }, + VarUsage { var_id: addend, location }, + ], + with_coupon: false, + outputs: vec![sum], + location, + is_specialization_base_call: false, + }), + ], + end: BlockEnd::Return(vec![VarUsage { var_id: sum, location }], location), + }; + let failure = Block { + statements: vec![Statement::Const(StatementConst::new_flat( + int_const(db, felt_ty, 0), + failure_out, + ))], + end: BlockEnd::Return(vec![VarUsage { var_id: failure_out, location }], location), + }; + + let mut lowered = Lowered { + diagnostics: template.diagnostics.clone(), + signature: template.signature.clone(), + variables, + blocks: BlocksBuilder(vec![root, success, failure]).build().unwrap(), + parameters: vec![], + }; + + const_folding(db, function_id, &mut lowered); + + assert_int_const(db, lowered.blocks[crate::BlockId(1)].statements.last().unwrap(), 3); +} From 3a8ba0cd6d446903e9c9c0e82fd521f3729ff8f5 Mon Sep 17 00:00:00 2001 From: jcastil0 Date: Sat, 28 Mar 2026 21:43:13 +0800 Subject: [PATCH 2/2] test(lowering): move snapshot pop coverage to const_folding test data --- .../src/optimizations/const_folding_test.rs | 326 +----------------- .../src/optimizations/test_data/const_folding | 242 +++++++++++++ 2 files changed, 245 insertions(+), 323 deletions(-) diff --git a/crates/cairo-lang-lowering/src/optimizations/const_folding_test.rs b/crates/cairo-lang-lowering/src/optimizations/const_folding_test.rs index 6f7609b5a08..eea926bdf68 100644 --- a/crates/cairo-lang-lowering/src/optimizations/const_folding_test.rs +++ b/crates/cairo-lang-lowering/src/optimizations/const_folding_test.rs @@ -1,31 +1,15 @@ use cairo_lang_debug::DebugWithDb; -use cairo_lang_semantic::corelib::{ - CorelibSemantic, core_array_felt252_ty, core_box_ty, get_usize_ty, option_none_variant, - option_some_variant, -}; use cairo_lang_semantic::db::SemanticGroup; -use cairo_lang_semantic::helper::ModuleHelper; -use cairo_lang_semantic::items::constant::{ConstValue, ConstValueId}; -use cairo_lang_semantic::test_utils::{setup_test_function, setup_test_function_ex}; -use cairo_lang_semantic::{GenericArgumentId, MatchArmSelector, TypeLongId}; +use cairo_lang_semantic::test_utils::setup_test_function; use cairo_lang_test_utils::parse_test_file::TestRunnerResult; -use cairo_lang_utils::Intern; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; -use num_bigint::BigInt; -use salsa::Database; -use super::const_folding; +use crate::LoweringStage; use crate::db::LoweringGroup; use crate::fmt::LoweredFormatter; -use crate::ids::{ConcreteFunctionWithBodyId, SemanticFunctionIdEx}; -use crate::objects::blocks::BlocksBuilder; +use crate::ids::ConcreteFunctionWithBodyId; use crate::optimizations::strategy::OptimizationPhase; use crate::test_utils::LoweringDatabaseForTesting; -use crate::{ - Block, BlockEnd, Lowered, LoweringStage, MatchArm, MatchExternInfo, MatchInfo, Statement, - StatementCall, StatementConst, StatementDesnap, StatementSnapshot, StatementUnbox, VarUsage, - Variable, VariableArena, -}; cairo_lang_test_utils::test_file_test!( const_folding, @@ -86,307 +70,3 @@ fn test_match_optimizer( ("lowering_diagnostics".into(), lowering_diagnostics.format(db)), ])) } - -fn alloc_var<'db>( - db: &'db dyn Database, - variables: &mut VariableArena<'db>, - ty: cairo_lang_semantic::TypeId<'db>, - location: crate::ids::LocationId<'db>, -) -> crate::VariableId { - variables.alloc(Variable::with_default_context(db, ty, location)) -} - -fn int_const<'db>( - db: &'db dyn Database, - ty: cairo_lang_semantic::TypeId<'db>, - value: i64, -) -> ConstValueId<'db> { - ConstValueId::from_int(db, ty, &BigInt::from(value)) -} - -fn assert_int_const<'db>(db: &'db dyn Database, stmt: &Statement<'db>, expected: i64) { - let Statement::Const(StatementConst { value, boxed: false, .. }) = stmt else { - panic!("Expected const statement, got {stmt:?}"); - }; - let ConstValue::Int(actual, _) = value.long(db) else { - panic!("Expected integer const, got {:?}", value.long(db)); - }; - assert_eq!(actual, &BigInt::from(expected)); -} - -#[test] -fn const_folding_propagates_snapshot_pop_front_success_outputs() { - let db = &mut LoweringDatabaseForTesting::default(); - let (test_function, _) = - setup_test_function_ex(db, "fn foo() -> usize { 0 }", "foo", "", None, None).split(); - let function_id = - ConcreteFunctionWithBodyId::from_semantic(db, test_function.concrete_function_id); - let template = db.lowered_body(function_id, LoweringStage::PreOptimizations).unwrap().clone(); - let location = template.signature.location; - - let felt_ty = db.core_info().felt252; - let usize_ty = get_usize_ty(db); - let array_ty = core_array_felt252_ty(db); - let snapshot_array_ty = TypeLongId::Snapshot(array_ty).intern(db); - let snapshot_felt_ty = TypeLongId::Snapshot(felt_ty).intern(db); - let box_snapshot_felt_ty = core_box_ty(db, snapshot_felt_ty); - - let array_module = ModuleHelper::core(db).submodule("array"); - let generic_args = vec![GenericArgumentId::Type(felt_ty)]; - let array_new = array_module.function_id("array_new", generic_args.clone()).lowered(db); - let array_append = array_module.function_id("array_append", generic_args.clone()).lowered(db); - let array_snapshot_pop_front = - array_module.function_id("array_snapshot_pop_front", generic_args.clone()).lowered(db); - let array_len = array_module.function_id("array_len", generic_args).lowered(db); - - let mut variables = VariableArena::default(); - let arr0 = alloc_var(db, &mut variables, array_ty, location); - let one = alloc_var(db, &mut variables, felt_ty, location); - let arr1 = alloc_var(db, &mut variables, array_ty, location); - let arr2 = alloc_var(db, &mut variables, array_ty, location); - let snapshot = alloc_var(db, &mut variables, snapshot_array_ty, location); - let success_arr = alloc_var(db, &mut variables, snapshot_array_ty, location); - let success_box = alloc_var(db, &mut variables, box_snapshot_felt_ty, location); - let failure_arr = alloc_var(db, &mut variables, snapshot_array_ty, location); - let len_out = alloc_var(db, &mut variables, usize_ty, location); - let failure_out = alloc_var(db, &mut variables, usize_ty, location); - - let root = Block { - statements: vec![ - Statement::Call(StatementCall { - function: array_new, - inputs: vec![], - with_coupon: false, - outputs: vec![arr0], - location, - is_specialization_base_call: false, - }), - Statement::Const(StatementConst::new_flat(int_const(db, felt_ty, 1), one)), - Statement::Call(StatementCall { - function: array_append, - inputs: vec![ - VarUsage { var_id: arr0, location }, - VarUsage { var_id: one, location }, - ], - with_coupon: false, - outputs: vec![arr1], - location, - is_specialization_base_call: false, - }), - Statement::Snapshot(StatementSnapshot::new( - VarUsage { var_id: arr1, location }, - arr2, - snapshot, - )), - ], - end: BlockEnd::Match { - info: MatchInfo::Extern(MatchExternInfo { - function: array_snapshot_pop_front, - inputs: vec![VarUsage { var_id: snapshot, location }], - arms: vec![ - MatchArm { - arm_selector: MatchArmSelector::VariantId(option_some_variant( - db, - box_snapshot_felt_ty, - )), - block_id: crate::BlockId(1), - var_ids: vec![success_arr, success_box], - }, - MatchArm { - arm_selector: MatchArmSelector::VariantId(option_none_variant( - db, - box_snapshot_felt_ty, - )), - block_id: crate::BlockId(2), - var_ids: vec![failure_arr], - }, - ], - location, - }), - }, - }; - let success = Block { - statements: vec![Statement::Call(StatementCall { - function: array_len, - inputs: vec![VarUsage { var_id: success_arr, location }], - with_coupon: false, - outputs: vec![len_out], - location, - is_specialization_base_call: false, - })], - end: BlockEnd::Return(vec![VarUsage { var_id: len_out, location }], location), - }; - let failure = Block { - statements: vec![Statement::Const(StatementConst::new_flat( - int_const(db, usize_ty, 99), - failure_out, - ))], - end: BlockEnd::Return(vec![VarUsage { var_id: failure_out, location }], location), - }; - - let mut lowered = Lowered { - diagnostics: template.diagnostics.clone(), - signature: template.signature.clone(), - variables, - blocks: BlocksBuilder(vec![root, success, failure]).build().unwrap(), - parameters: vec![], - }; - - const_folding(db, function_id, &mut lowered); - - assert_int_const(db, &lowered.blocks[crate::BlockId(1)].statements[0], 0); -} - -#[test] -fn const_folding_propagates_snapshot_pop_back_success_outputs() { - let db = &mut LoweringDatabaseForTesting::default(); - let (test_function, _) = - setup_test_function_ex(db, "fn foo() -> felt252 { 0 }", "foo", "", None, None).split(); - let function_id = - ConcreteFunctionWithBodyId::from_semantic(db, test_function.concrete_function_id); - let template = db.lowered_body(function_id, LoweringStage::PreOptimizations).unwrap().clone(); - let location = template.signature.location; - - let felt_ty = db.core_info().felt252; - let array_ty = core_array_felt252_ty(db); - let snapshot_array_ty = TypeLongId::Snapshot(array_ty).intern(db); - let snapshot_felt_ty = TypeLongId::Snapshot(felt_ty).intern(db); - let box_snapshot_felt_ty = core_box_ty(db, snapshot_felt_ty); - - let core = ModuleHelper::core(db); - let array_module = core.submodule("array"); - let generic_args = vec![GenericArgumentId::Type(felt_ty)]; - let array_new = array_module.function_id("array_new", generic_args.clone()).lowered(db); - let array_append = array_module.function_id("array_append", generic_args.clone()).lowered(db); - let array_snapshot_pop_back = - array_module.function_id("array_snapshot_pop_back", generic_args).lowered(db); - let felt_add = core.function_id("felt252_add", vec![]).lowered(db); - - let mut variables = VariableArena::default(); - let arr0 = alloc_var(db, &mut variables, array_ty, location); - let one = alloc_var(db, &mut variables, felt_ty, location); - let arr1 = alloc_var(db, &mut variables, array_ty, location); - let two = alloc_var(db, &mut variables, felt_ty, location); - let arr2 = alloc_var(db, &mut variables, array_ty, location); - let arr3 = alloc_var(db, &mut variables, array_ty, location); - let snapshot = alloc_var(db, &mut variables, snapshot_array_ty, location); - let success_arr = alloc_var(db, &mut variables, snapshot_array_ty, location); - let success_box = alloc_var(db, &mut variables, box_snapshot_felt_ty, location); - let failure_arr = alloc_var(db, &mut variables, snapshot_array_ty, location); - let snapshot_value = alloc_var(db, &mut variables, snapshot_felt_ty, location); - let value = alloc_var(db, &mut variables, felt_ty, location); - let addend = alloc_var(db, &mut variables, felt_ty, location); - let sum = alloc_var(db, &mut variables, felt_ty, location); - let failure_out = alloc_var(db, &mut variables, felt_ty, location); - - let root = Block { - statements: vec![ - Statement::Call(StatementCall { - function: array_new, - inputs: vec![], - with_coupon: false, - outputs: vec![arr0], - location, - is_specialization_base_call: false, - }), - Statement::Const(StatementConst::new_flat(int_const(db, felt_ty, 1), one)), - Statement::Call(StatementCall { - function: array_append, - inputs: vec![ - VarUsage { var_id: arr0, location }, - VarUsage { var_id: one, location }, - ], - with_coupon: false, - outputs: vec![arr1], - location, - is_specialization_base_call: false, - }), - Statement::Const(StatementConst::new_flat(int_const(db, felt_ty, 2), two)), - Statement::Call(StatementCall { - function: array_append, - inputs: vec![ - VarUsage { var_id: arr1, location }, - VarUsage { var_id: two, location }, - ], - with_coupon: false, - outputs: vec![arr2], - location, - is_specialization_base_call: false, - }), - Statement::Snapshot(StatementSnapshot::new( - VarUsage { var_id: arr2, location }, - arr3, - snapshot, - )), - ], - end: BlockEnd::Match { - info: MatchInfo::Extern(MatchExternInfo { - function: array_snapshot_pop_back, - inputs: vec![VarUsage { var_id: snapshot, location }], - arms: vec![ - MatchArm { - arm_selector: MatchArmSelector::VariantId(option_some_variant( - db, - box_snapshot_felt_ty, - )), - block_id: crate::BlockId(1), - var_ids: vec![success_arr, success_box], - }, - MatchArm { - arm_selector: MatchArmSelector::VariantId(option_none_variant( - db, - box_snapshot_felt_ty, - )), - block_id: crate::BlockId(2), - var_ids: vec![failure_arr], - }, - ], - location, - }), - }, - }; - let success = Block { - statements: vec![ - Statement::Unbox(StatementUnbox { - input: VarUsage { var_id: success_box, location }, - output: snapshot_value, - }), - Statement::Desnap(StatementDesnap { - input: VarUsage { var_id: snapshot_value, location }, - output: value, - }), - Statement::Const(StatementConst::new_flat(int_const(db, felt_ty, 1), addend)), - Statement::Call(StatementCall { - function: felt_add, - inputs: vec![ - VarUsage { var_id: value, location }, - VarUsage { var_id: addend, location }, - ], - with_coupon: false, - outputs: vec![sum], - location, - is_specialization_base_call: false, - }), - ], - end: BlockEnd::Return(vec![VarUsage { var_id: sum, location }], location), - }; - let failure = Block { - statements: vec![Statement::Const(StatementConst::new_flat( - int_const(db, felt_ty, 0), - failure_out, - ))], - end: BlockEnd::Return(vec![VarUsage { var_id: failure_out, location }], location), - }; - - let mut lowered = Lowered { - diagnostics: template.diagnostics.clone(), - signature: template.signature.clone(), - variables, - blocks: BlocksBuilder(vec![root, success, failure]).build().unwrap(), - parameters: vec![], - }; - - const_folding(db, function_id, &mut lowered); - - assert_int_const(db, lowered.blocks[crate::BlockId(1)].statements.last().unwrap(), 3); -} diff --git a/crates/cairo-lang-lowering/src/optimizations/test_data/const_folding b/crates/cairo-lang-lowering/src/optimizations/test_data/const_folding index 40cf443fb0d..fdd12a6f6c9 100644 --- a/crates/cairo-lang-lowering/src/optimizations/test_data/const_folding +++ b/crates/cairo-lang-lowering/src/optimizations/test_data/const_folding @@ -6075,6 +6075,248 @@ End: //! > ========================================================================== +//! > Snapshot pop_front propagates success values. + +//! > test_runner_name +test_match_optimizer + +//! > function_code +fn foo() -> felt252 { + let mut snapshot = array![1, 2].span().snapshot; + let Some(v) = array_snapshot_pop_front(ref snapshot) else { + return 0; + }; + let Some(w) = array_snapshot_pop_front(ref snapshot) else { + return 100; + }; + *v.unbox() + *w.unbox() +} + +//! > function_name +foo + +//! > module_code +use core::array::array_snapshot_pop_front; + +//! > semantic_diagnostics + +//! > before +Parameters: +blk0 (root): +Statements: + (v0: core::array::Array::) <- core::array::array_new::() + (v1: core::felt252) <- 1 + (v2: core::array::Array::) <- core::array::array_append::(v0, v1) + (v3: core::felt252) <- 2 + (v4: core::array::Array::) <- core::array::array_append::(v2, v3) + (v5: core::array::Array::, v6: @core::array::Array::) <- snapshot(v4) +End: + Match(match core::array::array_snapshot_pop_front::(v6) { + Option::Some(v7, v8) => blk1, + Option::None(v9) => blk4, + }) + +blk1: +Statements: +End: + Match(match core::array::array_snapshot_pop_front::(v7) { + Option::Some(v10, v11) => blk2, + Option::None(v12) => blk3, + }) + +blk2: +Statements: + (v13: @core::felt252) <- unbox(v8) + (v14: core::felt252) <- desnap(v13) + (v15: @core::felt252) <- unbox(v11) + (v16: core::felt252) <- desnap(v15) + (v17: core::felt252) <- core::felt252_add(v14, v16) +End: + Return(v17) + +blk3: +Statements: + (v18: core::felt252) <- 100 +End: + Return(v18) + +blk4: +Statements: + (v19: core::felt252) <- 0 +End: + Return(v19) + +//! > after +Parameters: +blk0 (root): +Statements: + (v0: core::array::Array::) <- core::array::array_new::() + (v1: core::felt252) <- 1 + (v2: core::array::Array::) <- core::array::array_append::(v0, v1) + (v3: core::felt252) <- 2 + (v4: core::array::Array::) <- core::array::array_append::(v2, v3) + (v5: core::array::Array::, v6: @core::array::Array::) <- snapshot(v4) +End: + Match(match core::array::array_snapshot_pop_front::(v6) { + Option::Some(v7, v8) => blk1, + Option::None(v9) => blk4, + }) + +blk1: +Statements: +End: + Match(match core::array::array_snapshot_pop_front::(v7) { + Option::Some(v10, v11) => blk2, + Option::None(v12) => blk3, + }) + +blk2: +Statements: + (v13: @core::felt252) <- unbox(v8) + (v14: core::felt252) <- desnap(v13) + (v15: @core::felt252) <- unbox(v11) + (v16: core::felt252) <- desnap(v15) + (v17: core::felt252) <- 3 +End: + Return(v17) + +blk3: +Statements: + (v18: core::felt252) <- 100 +End: + Return(v18) + +blk4: +Statements: + (v19: core::felt252) <- 0 +End: + Return(v19) + +//! > lowering_diagnostics + +//! > ========================================================================== + +//! > Snapshot pop_back propagates success values. + +//! > test_runner_name +test_match_optimizer + +//! > function_code +fn foo() -> felt252 { + let mut snapshot = array![1, 2].span().snapshot; + let Some(v) = array_snapshot_pop_back(ref snapshot) else { + return 0; + }; + let Some(w) = array_snapshot_pop_back(ref snapshot) else { + return 100; + }; + *v.unbox() + *w.unbox() +} + +//! > function_name +foo + +//! > module_code +use core::array::array_snapshot_pop_back; + +//! > semantic_diagnostics + +//! > before +Parameters: +blk0 (root): +Statements: + (v0: core::array::Array::) <- core::array::array_new::() + (v1: core::felt252) <- 1 + (v2: core::array::Array::) <- core::array::array_append::(v0, v1) + (v3: core::felt252) <- 2 + (v4: core::array::Array::) <- core::array::array_append::(v2, v3) + (v5: core::array::Array::, v6: @core::array::Array::) <- snapshot(v4) +End: + Match(match core::array::array_snapshot_pop_back::(v6) { + Option::Some(v7, v8) => blk1, + Option::None(v9) => blk4, + }) + +blk1: +Statements: +End: + Match(match core::array::array_snapshot_pop_back::(v7) { + Option::Some(v10, v11) => blk2, + Option::None(v12) => blk3, + }) + +blk2: +Statements: + (v13: @core::felt252) <- unbox(v8) + (v14: core::felt252) <- desnap(v13) + (v15: @core::felt252) <- unbox(v11) + (v16: core::felt252) <- desnap(v15) + (v17: core::felt252) <- core::felt252_add(v14, v16) +End: + Return(v17) + +blk3: +Statements: + (v18: core::felt252) <- 100 +End: + Return(v18) + +blk4: +Statements: + (v19: core::felt252) <- 0 +End: + Return(v19) + +//! > after +Parameters: +blk0 (root): +Statements: + (v0: core::array::Array::) <- core::array::array_new::() + (v1: core::felt252) <- 1 + (v2: core::array::Array::) <- core::array::array_append::(v0, v1) + (v3: core::felt252) <- 2 + (v4: core::array::Array::) <- core::array::array_append::(v2, v3) + (v5: core::array::Array::, v6: @core::array::Array::) <- snapshot(v4) +End: + Match(match core::array::array_snapshot_pop_back::(v6) { + Option::Some(v7, v8) => blk1, + Option::None(v9) => blk4, + }) + +blk1: +Statements: +End: + Match(match core::array::array_snapshot_pop_back::(v7) { + Option::Some(v10, v11) => blk2, + Option::None(v12) => blk3, + }) + +blk2: +Statements: + (v13: @core::felt252) <- unbox(v8) + (v14: core::felt252) <- desnap(v13) + (v15: @core::felt252) <- unbox(v11) + (v16: core::felt252) <- desnap(v15) + (v17: core::felt252) <- 3 +End: + Return(v17) + +blk3: +Statements: + (v18: core::felt252) <- 100 +End: + Return(v18) + +blk4: +Statements: + (v19: core::felt252) <- 0 +End: + Return(v19) + +//! > lowering_diagnostics + +//! > ========================================================================== + //! > Panic with byte array value. //! > test_runner_name