diff --git a/crates/cairo-lang-lowering/src/borrow_check/test_data/closure b/crates/cairo-lang-lowering/src/borrow_check/test_data/closure index ec1f3980e04..7d74c872bb9 100644 --- a/crates/cairo-lang-lowering/src/borrow_check/test_data/closure +++ b/crates/cairo-lang-lowering/src/borrow_check/test_data/closure @@ -399,3 +399,45 @@ warning[E0001]: Unused variable. Consider ignoring by prefixing with `_`. //! > lowering_diagnostics //! > lowering + +//! > ========================================================================== + +//! > capturing two destructables variables of the same type. + +//! > test_runner_name +test_borrow_check + +//! > function +fn foo(a: Destructable, b: Destructable) { + let c = || { + let Destructable { } = a; + let Destructable { } = b; + }; + c.destruct(); +} + +//! > function_name +foo + +//! > module_code +struct Destructable {} + +impl MyDesruct of Destruct { + fn destruct(self: Destructable) nopanic { + let Destructable { } = self; + } +} + +//! > semantic_diagnostics + +//! > lowering_diagnostics + +//! > lowering +Parameters: v0: test::Destructable, v1: test::Destructable +blk0 (root): +Statements: + (v2: {closure@lib.cairo:9:13: 9:15}) <- struct_construct(v0{`a`}, v1{`b`}) + (v3: ()) <- Generated `core::traits::Destruct::destruct` for {closure@lib.cairo:9:13: 9:15}(v2{`c`}) + (v4: ()) <- struct_construct() +End: + Return(v4) diff --git a/crates/cairo-lang-lowering/src/lower/block_builder.rs b/crates/cairo-lang-lowering/src/lower/block_builder.rs index 59e7b0fd929..4e1db19e703 100644 --- a/crates/cairo-lang-lowering/src/lower/block_builder.rs +++ b/crates/cairo-lang-lowering/src/lower/block_builder.rs @@ -297,9 +297,22 @@ impl BlockBuilder { ) .collect(); + let TypeLongId::Closure(closure_id) = expr.ty.lookup_intern(ctx.db) else { + unreachable!("Closure Expr should have a Closure type."); + }; + + // Assert that the closure type matches the input we pass to it. + assert!( + closure_id + .captured_types + .iter() + .eq(inputs.iter().map(|var_usage| &ctx.variables[var_usage.var_id].ty)) + ); + let var_usage = generators::StructConstruct { inputs: inputs.clone(), ty: expr.ty, location } .add(ctx, &mut self.statements); + let closure_info = ClosureInfo { members, snapshots }; for (var_usage, member) in izip!(inputs, usage.usage.keys()) { diff --git a/crates/cairo-lang-semantic/src/expr/compute.rs b/crates/cairo-lang-semantic/src/expr/compute.rs index b890466e467..5d25b4dc59b 100644 --- a/crates/cairo-lang-semantic/src/expr/compute.rs +++ b/crates/cairo-lang-semantic/src/expr/compute.rs @@ -37,7 +37,6 @@ use cairo_lang_utils::{Intern, LookupIntern, OptionHelper, extract_matches, try_ use itertools::{Itertools, chain, zip_eq}; use num_bigint::BigInt; use num_traits::ToPrimitive; -use salsa::InternKey; use smol_str::SmolStr; use super::inference::canonic::ResultNoErrEx; @@ -1775,11 +1774,9 @@ fn compute_expr_closure_semantic( } let captured_types = chain!( + chain!(usage.usage.values(), usage.changes.values()).map(|item| item.ty()), usage.snap_usage.values().map(|item| wrap_in_snapshots(ctx.db, item.ty(), 1)), - chain!(usage.usage.values(), usage.changes.values()).map(|item| item.ty()) ) - .sorted_by_key(|ty| ty.as_intern_id()) - .dedup() .collect_vec(); let ty = TypeLongId::Closure(ClosureTypeLongId { diff --git a/crates/cairo-lang-semantic/src/items/imp.rs b/crates/cairo-lang-semantic/src/items/imp.rs index 0aabf8bc049..e73810f4935 100644 --- a/crates/cairo-lang-semantic/src/items/imp.rs +++ b/crates/cairo-lang-semantic/src/items/imp.rs @@ -1962,7 +1962,7 @@ pub fn find_closure_generated_candidate( let mem_trait_generic_params = |trait_id, neg_impl_trait: Option<_>| { let id = db.trait_generic_params(trait_id).unwrap().first().unwrap().id(); chain!( - closure_type_long.captured_types.iter().map(|ty| { + closure_type_long.captured_types.iter().unique().map(|ty| { GenericParam::Impl(GenericParamImpl { id, concrete_trait: Maybe::Ok(db.intern_concrete_trait(ConcreteTraitLongId { diff --git a/tests/bug_samples/issue7234.cairo b/tests/bug_samples/issue7234.cairo new file mode 100644 index 00000000000..221f7e489d1 --- /dev/null +++ b/tests/bug_samples/issue7234.cairo @@ -0,0 +1,33 @@ +// A function which takes a closure as an argument and calls it. +// denotes that F is a "Generic type parameter" +fn apply, impl func: core::ops::FnOnce, +Drop>(f: F) { + // ^ TODO: Try changing this to `Fn`. + + f(); +} +#[test] +fn main() { + // A non-copy type. + let greeting: ByteArray = "hello"; + let farewell: ByteArray = "goodbye"; + + // Capture 2 variables: `greeting` by snapshot and + // `farewell` by value. + let diary = || { + // `greeting` is by snapshot: requires `Fn`. + println!("I said {}.", greeting); + + // Using farewell by value requires `FnOnce`. + // Convert farewell to uppercase to demonstrate value capture through `into_iter` + let mut iter = farewell.into_iter(); + let uppercase: ByteArray = iter.map(|c| if c >= 'a' { + c - 32 + } else { + c + }).collect(); + println!("Then I screamed {}!", uppercase); + }; + + // Call the function which applies the closure. + apply(diary); +} diff --git a/tests/bug_samples/lib.cairo b/tests/bug_samples/lib.cairo index f380cc9d107..5451bd12df7 100644 --- a/tests/bug_samples/lib.cairo +++ b/tests/bug_samples/lib.cairo @@ -61,6 +61,7 @@ mod issue7083; mod issue7095; mod issue7155; mod issue7233; +mod issue7234; mod loop_break_in_match; mod loop_only_change; mod partial_param_local;