diff --git a/compiler/qsc_rca/src/core.rs b/compiler/qsc_rca/src/core.rs index f5c496a9ae..60462f0d8a 100644 --- a/compiler/qsc_rca/src/core.rs +++ b/compiler/qsc_rca/src/core.rs @@ -449,7 +449,7 @@ impl<'a> Analyzer<'a> { callee_input_pattern_id, args_input_id, self.package_store, - fixed_args.as_ref().map_or(0, Vec::len), + fixed_args.as_ref().map(Vec::len), ); let application_instance = self.get_current_application_instance(); @@ -2509,17 +2509,28 @@ fn map_input_pattern_to_input_expressions( pat_id: StorePatId, expr_id: StoreExprId, package_store: &impl PackageStoreLookup, - skip_ahead: usize, + skip_ahead: Option, ) -> Vec { let pat = package_store.get_pat(pat_id); match &pat.kind { PatKind::Bind(_) | PatKind::Discard => vec![expr_id.expr], PatKind::Tuple(pats) => { // Map each one of the elements in the pattern to an expression in the tuple. - let pats = &pats[skip_ahead..]; + let pats = &pats[skip_ahead.unwrap_or_default()..]; let expr = package_store.get_expr(expr_id); if let ExprKind::Tuple(exprs) = &expr.kind { - assert!(pats.len() == exprs.len()); + let pats = if skip_ahead.is_some() && exprs.len() > 1 { + // When skip_ahead is not None we know we are processing a lambda, so the final pattern is itself a tuple. + // Unpack the tuple pattern to get the individual elements and map input to those. + let PatKind::Tuple(pats) = + &package_store.get_pat((pat_id.package, pats[0]).into()).kind + else { + panic!("expected tuple pattern for lambda receiving multiple arguments"); + }; + pats + } else { + pats + }; let mut input_param_exprs = Vec::::with_capacity(pats.len()); for (local_pat_id, local_expr_id) in pats.iter().zip(exprs.iter()) { let global_pat_id = StorePatId::from((pat_id.package, *local_pat_id)); @@ -2528,7 +2539,7 @@ fn map_input_pattern_to_input_expressions( global_pat_id, global_expr_id, package_store, - 0, + None, ); input_param_exprs.append(&mut sub_input_param_exprs); } diff --git a/compiler/qsc_rca/src/tests.rs b/compiler/qsc_rca/src/tests.rs index 419e6b2575..dba025b559 100644 --- a/compiler/qsc_rca/src/tests.rs +++ b/compiler/qsc_rca/src/tests.rs @@ -10,6 +10,7 @@ mod calls; mod cycles; mod ifs; mod intrinsics; +mod lambdas; mod loops; mod measurements; mod overrides; diff --git a/compiler/qsc_rca/src/tests/lambdas.rs b/compiler/qsc_rca/src/tests/lambdas.rs new file mode 100644 index 0000000000..35baefde1c --- /dev/null +++ b/compiler/qsc_rca/src/tests/lambdas.rs @@ -0,0 +1,219 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use super::{check_last_statement_compute_properties, CompilationContext}; +use expect_test::expect; + +#[test] +fn check_rca_for_classical_lambda_one_parameter() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + let myOp = a -> {}; + myOp(1)"#, + ); + let package_store_compute_properties = compilation_context.get_compute_properties(); + check_last_statement_compute_properties( + package_store_compute_properties, + &expect![[r#" + ApplicationsGeneratorSet: + inherent: Classical + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_classical_lambda_two_parameters() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + let myOp = (a, b) -> {}; + myOp(1, 2)"#, + ); + let package_store_compute_properties = compilation_context.get_compute_properties(); + check_last_statement_compute_properties( + package_store_compute_properties, + &expect![[r#" + ApplicationsGeneratorSet: + inherent: Classical + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_classical_lambda_one_parameter_one_capture() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + let x = 0.0; + let myOp = a -> {x}; + myOp(1)"#, + ); + let package_store_compute_properties = compilation_context.get_compute_properties(); + check_last_statement_compute_properties( + package_store_compute_properties, + &expect![[r#" + ApplicationsGeneratorSet: + inherent: Classical + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_classical_lambda_one_parameter_two_captures() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + let x = 0.0; + let y = 0.0; + let myOp = a -> {x+y}; + myOp(1)"#, + ); + let package_store_compute_properties = compilation_context.get_compute_properties(); + check_last_statement_compute_properties( + package_store_compute_properties, + &expect![[r#" + ApplicationsGeneratorSet: + inherent: Classical + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_classical_lambda_two_parameters_one_capture() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + let x = 0.0; + let myOp = (a, b) -> {x}; + myOp(1, 2)"#, + ); + let package_store_compute_properties = compilation_context.get_compute_properties(); + check_last_statement_compute_properties( + package_store_compute_properties, + &expect![[r#" + ApplicationsGeneratorSet: + inherent: Classical + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_classical_lambda_two_parameters_two_captures() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + let x = 0.0; + let y = 0.0; + let myOp = (a, b) -> {x+y}; + myOp(1, 2)"#, + ); + let package_store_compute_properties = compilation_context.get_compute_properties(); + check_last_statement_compute_properties( + package_store_compute_properties, + &expect![[r#" + ApplicationsGeneratorSet: + inherent: Classical + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_dynamic_lambda_two_classical_parameters_one_dynamic_capture() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + let x = { + use q = Qubit(); + if MResetZ(q) == One { + 1.0 + } else { + 0.0 + } + }; + let myOp = (a, b) -> {x}; + myOp(1, 2)"#, + ); + let package_store_compute_properties = compilation_context.get_compute_properties(); + check_last_statement_compute_properties( + package_store_compute_properties, + &expect![[r#" + ApplicationsGeneratorSet: + inherent: Quantum: QuantumProperties: + runtime_features: RuntimeFeatureFlags(UseOfDynamicDouble) + value_kind: Element(Dynamic) + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_dynamic_lambda_two_dynamic_parameters_one_classical_capture() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + let x = 0.0; + let myOp = (a, b) -> {a + x}; + let a = { + use q = Qubit(); + if MResetZ(q) == One { + 1.0 + } else { + 0.0 + } + }; + myOp(a, 2)"#, + ); + let package_store_compute_properties = compilation_context.get_compute_properties(); + check_last_statement_compute_properties( + package_store_compute_properties, + &expect![[r#" + ApplicationsGeneratorSet: + inherent: Quantum: QuantumProperties: + runtime_features: RuntimeFeatureFlags(UseOfDynamicBool | UseOfDynamicDouble) + value_kind: Element(Dynamic) + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_operation_lambda_two_parameters() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + use (q0, q1) = (Qubit(), Qubit()); + let myOp = (a, b) => MResetEachZ([a, b]); + myOp(q0, q1)"#, + ); + let package_store_compute_properties = compilation_context.get_compute_properties(); + check_last_statement_compute_properties( + package_store_compute_properties, + &expect![[r#" + ApplicationsGeneratorSet: + inherent: Quantum: QuantumProperties: + runtime_features: RuntimeFeatureFlags(0x0) + value_kind: Array(Content: Dynamic, Size: Static) + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_operation_lambda_two_parameters_with_controls() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + use (q0, q1) = (Qubit(), Qubit()); + use (qs0, qs1) = (Qubit[2], Qubit[2]); + let myOp = (a, b) => CNOT(a, b); + Controlled Controlled myOp(qs0, (qs1, (q0, q1)))"#, + ); + let package_store_compute_properties = compilation_context.get_compute_properties(); + check_last_statement_compute_properties( + package_store_compute_properties, + &expect![[r#" + ApplicationsGeneratorSet: + inherent: Quantum: QuantumProperties: + runtime_features: RuntimeFeatureFlags(UseOfDynamicBool) + value_kind: Element(Static) + dynamic_param_applications: "#]], + ); +}