diff --git a/source/compiler/qsc/src/codegen/tests.rs b/source/compiler/qsc/src/codegen/tests.rs index 4bb6d037c97..667546597e1 100644 --- a/source/compiler/qsc/src/codegen/tests.rs +++ b/source/compiler/qsc/src/codegen/tests.rs @@ -5388,3 +5388,51 @@ fn chemistry_like_iqpe_with_udt_capture_closure_generates_base_profile_qir() { "expected threaded Controlled X capture in QIR:\n{qir}" ); } + +#[test] +fn foreign_table_lookup_callable_generates_qir() { + let source = indoc::indoc! {r#" + operation Invoke() : Unit { + use address = Qubit[1]; + use output = Qubit[1]; + Std.TableLookup.Select([[false], [true]], address, output); + + // Adjoint of Select does not work until support for static sized, dynamic content arrays is added. + // See related issue: https://github.com/microsoft/qdk/issues/3388 + // Adjoint Std.TableLookup.Select([[false], [true]], address, output); + } + "#}; + for profile in [Profile::AdaptiveRI, Profile::AdaptiveRIF, Profile::Adaptive] { + let capabilities = profile.into(); + let (mut store, package_id, items) = + compile_and_locate_items(source, &[("Invoke", true)], capabilities); + let root_sources = + source_map_from_source("namespace DriverEmpty { function Anchor() : Unit {} }"); + let root_dependencies = vec![(package_id, Some(Arc::from("ReproLib")))]; + let (root_unit, root_errors) = crate::compile::compile( + &store, + &root_dependencies, + root_sources, + PackageType::Lib, + capabilities, + LanguageFeatures::default(), + ); + assert!( + root_errors.is_empty(), + "{profile:?} root compilation failed: {root_errors:?}" + ); + store.insert(root_unit); + + let qir = callable_args_to_qir( + &store, + package_id, + items["Invoke"], + &Value::unit(), + capabilities, + ); + assert!( + qir.contains("define i64 @ENTRYPOINT__main()"), + "{profile:?} callable should emit an entry point, got:\n{qir}" + ); + } +} diff --git a/source/compiler/qsc_rca/src/core.rs b/source/compiler/qsc_rca/src/core.rs index 06b562423b3..a6158e792f5 100644 --- a/source/compiler/qsc_rca/src/core.rs +++ b/source/compiler/qsc_rca/src/core.rs @@ -1542,7 +1542,25 @@ impl<'a> Analyzer<'a> { let mut args_compute_kinds = Vec::::with_capacity(args.len()); for arg_expr_id in args { let arg_compute_kind = application_instance.get_expr_compute_kind(*arg_expr_id); - args_compute_kinds.push(*arg_compute_kind); + let arg_ty = &self.get_expr(*arg_expr_id).ty; + if matches!( + arg_compute_kind, + ComputeKind::Dynamic { + value_kind: ValueKind::Constant, + .. + } + ) && !is_any_result(arg_ty) + && !is_any_array(arg_ty) + { + // If the argument is expected to have a constant value, we can consider it static across function boundaries. + // This matches the behavior in partial evaluation, captured in source/compiler/qsc_partial_eval/src/evaluation_context.rs + // `Scope` construction, where non-result runtime values are set as static when computing argument `ComputeKind`. + // We skip this for result types, which must remain dynamic constant as they are the source of later variables during + // comparison of result values, and for arrays, which during evaluation do not have investigatable contents. + args_compute_kinds.push(ComputeKind::Static); + } else { + args_compute_kinds.push(*arg_compute_kind); + } } args_compute_kinds } @@ -2803,6 +2821,14 @@ fn is_any_result(t: &Ty) -> bool { } } +fn is_any_array(t: &Ty) -> bool { + match t { + Ty::Array(_) => true, + Ty::Tuple(ts) => ts.iter().any(is_any_array), + _ => false, + } +} + struct ClearComputeKinds<'a, 'b> { analyzer: &'a mut Analyzer<'b>, }