From ff57a3c173a838a7e5bd3bf5ccb2e1a48e5dcc0a Mon Sep 17 00:00:00 2001 From: "Stefan J. Wernli" Date: Mon, 13 May 2024 13:38:09 -0700 Subject: [PATCH] Check unresolved callables during partial eval (#1497) This change updates the RCA check pass to not treat unresolved callables as errors and instead relies on a new check at partial eval time that checks the resolved callable with the known arguments to return RCA check errors at that point. --- Cargo.lock | 2 + compiler/qsc_partial_eval/src/lib.rs | 237 +++++++++++----- compiler/qsc_partial_eval/tests/calls.rs | 90 +++++- .../tests/output_recording.rs | 14 +- compiler/qsc_partial_eval/tests/test_utils.rs | 41 ++- compiler/qsc_passes/src/capabilitiesck.rs | 258 +---------------- .../src/capabilitiesck/tests_adaptive.rs | 17 +- .../tests_adaptive_plus_integers.rs | 17 +- .../src/capabilitiesck/tests_base.rs | 17 +- compiler/qsc_passes/src/lib.rs | 2 +- compiler/qsc_rca/Cargo.toml | 2 + compiler/qsc_rca/src/core.rs | 7 +- compiler/qsc_rca/src/errors.rs | 260 ++++++++++++++++++ compiler/qsc_rca/src/lib.rs | 3 +- 14 files changed, 579 insertions(+), 388 deletions(-) create mode 100644 compiler/qsc_rca/src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index e1b78089e6..1e19226928 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1195,6 +1195,7 @@ dependencies = [ "bitflags 2.4.2", "expect-test", "indenter", + "miette", "qsc", "qsc_data_structures", "qsc_fir", @@ -1202,6 +1203,7 @@ dependencies = [ "qsc_lowerer", "qsc_passes", "rustc-hash", + "thiserror", ] [[package]] diff --git a/compiler/qsc_partial_eval/src/lib.rs b/compiler/qsc_partial_eval/src/lib.rs index 3abf992a80..b9e39cf2af 100644 --- a/compiler/qsc_partial_eval/src/lib.rs +++ b/compiler/qsc_partial_eval/src/lib.rs @@ -8,6 +8,7 @@ mod evaluation_context; mod management; +use core::panic; use evaluation_context::{ Arg, BlockNode, BranchControlFlow, EvalControlFlow, EvaluationContext, MutableKind, MutableVar, Scope, @@ -32,7 +33,12 @@ use qsc_fir::{ }, ty::{Prim, Ty}, }; -use qsc_rca::{ComputeKind, ComputePropertiesLookup, PackageStoreComputeProperties}; +use qsc_rca::errors::{generate_errors_from_runtime_features, get_missing_runtime_features}; +use qsc_rca::{ + errors::Error as CapabilityError, ComputeKind, ComputePropertiesLookup, + PackageStoreComputeProperties, +}; +use qsc_rca::{ItemComputeProperties, QuantumProperties, RuntimeFeatureFlags}; use qsc_rir::{ builder, rir::{ @@ -59,6 +65,17 @@ pub fn partially_evaluate( /// A partial evaluation error. #[derive(Clone, Debug, Diagnostic, Error)] pub enum Error { + #[error(transparent)] + #[diagnostic(transparent)] + CapabilityError(CapabilityError), + + #[error("use of unanalyzed dynamic value")] + #[diagnostic(code("Qsc.PartialEval.UnexpectedDynamicValue"))] + #[diagnostic(help( + "analysis is limited for callables that cannot be uniquely resolved at compile time, try invoking the desired callable directly" + ))] + UnexpectedDynamicValue(#[label] Span), + #[error("partial evaluation failed with error {0}")] #[diagnostic(code("Qsc.PartialEval.EvaluationFailed"))] EvaluationFailed(String, #[label] Span), @@ -678,7 +695,7 @@ impl<'a> PartialEvaluator<'a> { } ExprKind::Block(block_id) => self.try_eval_block(*block_id), ExprKind::Call(callee_expr_id, args_expr_id) => { - self.eval_expr_call(*callee_expr_id, *args_expr_id) + self.eval_expr_call(expr_id, *callee_expr_id, *args_expr_id) } ExprKind::Closure(args, callable) => { let closure = resolve_closure( @@ -900,27 +917,12 @@ impl<'a> PartialEvaluator<'a> { fn eval_expr_call( &mut self, + call_expr_id: ExprId, callee_expr_id: ExprId, args_expr_id: ExprId, ) -> Result { - // Visit the both the callee and arguments expressions to get their values. - let callee_control_flow = self.try_eval_expr(callee_expr_id)?; - if callee_control_flow.is_return() { - let callee_expr = self.get_expr(callee_expr_id); - return Err(Error::Unexpected( - "embedded return in callee".to_string(), - callee_expr.span, - )); - } - - let args_control_flow = self.try_eval_expr(args_expr_id)?; - if args_control_flow.is_return() { - let args_expr = self.get_expr(args_expr_id); - return Err(Error::Unexpected( - "embedded return in call arguments".to_string(), - args_expr.span, - )); - } + let (callee_control_flow, args_control_flow) = + self.try_eval_callee_and_args(callee_expr_id, args_expr_id)?; // Get the callable. let (store_item_id, functor_app, fixed_args) = match callee_control_flow.into_value() { @@ -937,30 +939,132 @@ impl<'a> PartialEvaluator<'a> { panic!("global is not a callable"); }; + // Set up the scope for the call, which allows additional error checking if the callable was + // previously unresolved. + let spec_decl = if let CallableImpl::Spec(spec_impl) = &callable_decl.implementation { + Some(get_spec_decl(spec_impl, functor_app)) + } else { + None + }; + + let args_value = args_control_flow.into_value(); + let ctls = if let Some(Some(ctls_pat_id)) = spec_decl.map(|spec_decl| spec_decl.input) { + assert!( + functor_app.controlled > 0, + "control qubits count was expected to be greater than zero" + ); + Some(( + StorePatId::from((store_item_id.package, ctls_pat_id)), + functor_app.controlled, + )) + } else { + assert!( + functor_app.controlled == 0, + "control qubits count was expected to be zero" + ); + None + }; + let (args, ctls_arg) = self.resolve_args( + (store_item_id.package, callable_decl.input).into(), + args_value.clone(), + ctls, + fixed_args, + ); + let call_scope = Scope::new( + store_item_id.package, + Some((store_item_id.item, functor_app)), + args, + ctls_arg, + ); + + // If the call has the unresolved flag, it tells us that RCA could not perform static analysis on this call site. + // Now that we are in evaluation, we have a distinct callable resolved and can perform runtime capability check + // ahead of performing the actual call and return the appropriate capabilities error if this call is not supported + // by the target. + let call_expr_compute_kind = self.get_expr_compute_kind(call_expr_id); + let call_was_unresolved = match call_expr_compute_kind { + ComputeKind::Quantum(props) => props + .runtime_features + .contains(RuntimeFeatureFlags::CallToUnresolvedCallee), + ComputeKind::Classical => false, + }; + if call_was_unresolved { + let call_compute_kind = self.get_call_compute_kind(&call_scope); + if let ComputeKind::Quantum(QuantumProperties { + runtime_features, + value_kind, + }) = call_compute_kind + { + let missing_features = get_missing_runtime_features( + runtime_features, + self.program.config.capabilities, + ) & !RuntimeFeatureFlags::CallToUnresolvedCallee; + if !missing_features.is_empty() { + if let Some(error) = generate_errors_from_runtime_features( + missing_features, + self.get_expr(call_expr_id).span, + ) + .drain(..) + .next() + { + return Err(Error::CapabilityError(error)); + } + } + + // If the call produces a dynamic value, we treat it as an error because we know that later + // analysis has not taken that dynamism into account and further partial evaluation may fail + // when it encounters that value. + if value_kind.is_dynamic() { + return Err(Error::UnexpectedDynamicValue( + self.get_expr(call_expr_id).span, + )); + } + } + } + // We generate instructions differently depending on whether we are calling an intrinsic or a specialization // with an implementation. - let value = match &callable_decl.implementation { - CallableImpl::Intrinsic => { + let value = match spec_decl { + None => { let callee_expr = self.get_expr(callee_expr_id); self.eval_expr_call_to_intrinsic( store_item_id, callable_decl, - args_control_flow.into_value(), + args_value, callee_expr.span, )? } - CallableImpl::Spec(spec_impl) => self.eval_expr_call_to_spec( - store_item_id, - functor_app, - spec_impl, - callable_decl.input, - args_control_flow.into_value(), - fixed_args, - )?, + Some(spec_decl) => { + self.eval_expr_call_to_spec(call_scope, store_item_id, functor_app, spec_decl)? + } }; Ok(EvalControlFlow::Continue(value)) } + fn try_eval_callee_and_args( + &mut self, + callee_expr_id: ExprId, + args_expr_id: ExprId, + ) -> Result<(EvalControlFlow, EvalControlFlow), Error> { + let callee_control_flow = self.try_eval_expr(callee_expr_id)?; + if callee_control_flow.is_return() { + let callee_expr = self.get_expr(callee_expr_id); + return Err(Error::Unexpected( + "embedded return in callee".to_string(), + callee_expr.span, + )); + } + let args_control_flow = self.try_eval_expr(args_expr_id)?; + if args_control_flow.is_return() { + let args_expr = self.get_expr(args_expr_id); + return Err(Error::Unexpected( + "embedded return in call arguments".to_string(), + args_expr.span, + )); + } + Ok((callee_control_flow, args_control_flow)) + } + fn eval_expr_call_to_intrinsic( &mut self, store_item_id: StoreItemId, @@ -1036,44 +1140,11 @@ impl<'a> PartialEvaluator<'a> { fn eval_expr_call_to_spec( &mut self, + call_scope: Scope, global_callable_id: StoreItemId, functor_app: FunctorApp, - spec_impl: &SpecImpl, - args_pat: PatId, - args_value: Value, - fixed_args: Option>, + spec_decl: &SpecDecl, ) -> Result { - let spec_decl = get_spec_decl(spec_impl, functor_app); - - // Create new call scope. - let ctls = if let Some(ctls_pat_id) = spec_decl.input { - assert!( - functor_app.controlled > 0, - "control qubits count was expected to be greater than zero" - ); - Some(( - StorePatId::from((global_callable_id.package, ctls_pat_id)), - functor_app.controlled, - )) - } else { - assert!( - functor_app.controlled == 0, - "control qubits count was expected to be zero" - ); - None - }; - let (args, ctls_arg) = self.resolve_args( - (global_callable_id.package, args_pat).into(), - args_value, - ctls, - fixed_args, - ); - let call_scope = Scope::new( - global_callable_id.package, - Some((global_callable_id.item, functor_app)), - args, - ctls_arg, - ); self.eval_context.push_scope(call_scope); let block_value = self.try_eval_block(spec_decl.block)?.into_value(); let popped_scope = self.eval_context.pop_scope(); @@ -1563,6 +1634,40 @@ impl<'a> PartialEvaluator<'a> { expr_generator_set.generate_application_compute_kind(&callable_scope.args_value_kind) } + fn get_call_compute_kind(&self, callable_scope: &Scope) -> ComputeKind { + let store_item_id = StoreItemId::from(( + callable_scope.package_id, + callable_scope + .callable + .expect("callable should be present") + .0, + )); + let ItemComputeProperties::Callable(callable_compute_properties) = + self.compute_properties.get_item(store_item_id) + else { + panic!("item compute properties not found"); + }; + let callable_generator_set = match &callable_scope.callable { + Some((_, functor_app)) => match (functor_app.adjoint, functor_app.controlled) { + (false, 0) => &callable_compute_properties.body, + (false, _) => callable_compute_properties + .ctl + .as_ref() + .expect("controlled should be supported"), + (true, 0) => callable_compute_properties + .adj + .as_ref() + .expect("adjoint should be supported"), + (true, _) => callable_compute_properties + .ctl_adj + .as_ref() + .expect("controlled adjoint should be supported"), + }, + None => panic!("call compute kind should have callable"), + }; + callable_generator_set.generate_application_compute_kind(&callable_scope.args_value_kind) + } + fn try_create_mutable_variable( &mut self, local_var_id: LocalVarId, diff --git a/compiler/qsc_partial_eval/tests/calls.rs b/compiler/qsc_partial_eval/tests/calls.rs index 1872be619e..24e49479c7 100644 --- a/compiler/qsc_partial_eval/tests/calls.rs +++ b/compiler/qsc_partial_eval/tests/calls.rs @@ -11,8 +11,13 @@ pub mod test_utils; use expect_test::expect; use indoc::indoc; +use qsc::TargetCapabilityFlags; use qsc_rir::rir::{BlockId, CallableId}; -use test_utils::{assert_block_instructions, assert_blocks, assert_callable, get_rir_program}; +use test_utils::{ + assert_block_instructions, assert_blocks, assert_callable, assert_error, + get_partial_evaluation_error_with_capabilities, get_rir_program, + get_rir_program_with_capabilities, +}; #[test] fn call_to_single_qubit_unitary_with_two_calls_to_the_same_intrinsic() { @@ -1200,3 +1205,86 @@ fn call_to_closue_with_one_bound_local_two_unbound() { Return"#]], ); } + +#[test] +fn call_to_unresolved_callee_with_classical_arg_allowed() { + let program = get_rir_program_with_capabilities( + indoc! {" + namespace Test { + open Microsoft.Quantum.Convert; + operation Op(i : Int, q : Qubit) : Unit { + Rx(IntAsDouble(i), q); + } + @EntryPoint() + operation Main() : Unit { + use q = Qubit(); + let f = [Op][0]; + f(1, q); + } + }"}, + TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::IntegerComputations, + ); + + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Double(1), Qubit(0), ) + Call id(2), args( Integer(0), Pointer, ) + Return"#]], + ); +} + +#[test] +fn call_to_unresolved_callee_with_dynamic_arg_fails() { + let error = get_partial_evaluation_error_with_capabilities( + indoc! {" + namespace Test { + open Microsoft.Quantum.Convert; + operation Op(i : Int, q : Qubit) : Unit { + Rx(IntAsDouble(i), q); + } + @EntryPoint() + operation Main() : Unit { + use q = Qubit(); + let i = if MResetZ(q) == One { 1 } else { 0 }; + let f = [Op][0]; + f(i, q); + } + }"}, + TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::IntegerComputations, + ); + + assert_error( + &error, + &expect!["CapabilityError(UseOfDynamicDouble(Span { lo: 298, hi: 305 }))"], + ); +} + +#[test] +fn call_to_unresolved_callee_producing_dynamic_value_fails() { + let error = get_partial_evaluation_error_with_capabilities( + indoc! {" + namespace Test { + open Microsoft.Quantum.Convert; + operation Op(i : Int, q : Qubit) : Int { + X(q); + i + } + @EntryPoint() + operation Main() : Unit { + use q = Qubit(); + let i = if MResetZ(q) == One { 1 } else { 0 }; + let f = [Op][0]; + let _ = f(i, q); + } + }"}, + TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::IntegerComputations, + ); + + assert_error( + &error, + &expect!["UnexpectedDynamicValue(Span { lo: 298, hi: 305 })"], + ); +} diff --git a/compiler/qsc_partial_eval/tests/output_recording.rs b/compiler/qsc_partial_eval/tests/output_recording.rs index 4b5fd28890..a83fcc414b 100644 --- a/compiler/qsc_partial_eval/tests/output_recording.rs +++ b/compiler/qsc_partial_eval/tests/output_recording.rs @@ -83,7 +83,7 @@ fn output_recording_for_tuple_of_different_types() { Call id(5), args( Variable(1, Boolean), Pointer, ) Return config: Config: - capabilities: Base + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 1 num_results: 1"#]] .assert_eq(&program.to_string()); @@ -169,7 +169,7 @@ fn output_recording_for_nested_tuples() { Call id(5), args( Variable(3, Boolean), Pointer, ) Return config: Config: - capabilities: Base + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 1 num_results: 1"#]] .assert_eq(&program.to_string()); @@ -263,7 +263,7 @@ fn output_recording_for_tuple_of_arrays() { Call id(6), args( Variable(3, Boolean), Pointer, ) Return config: Config: - capabilities: Base + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 1 num_results: 1"#]] .assert_eq(&program.to_string()); @@ -357,7 +357,7 @@ fn output_recording_for_array_of_tuples() { Call id(6), args( Variable(3, Boolean), Pointer, ) Return config: Config: - capabilities: Base + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 1 num_results: 1"#]] .assert_eq(&program.to_string()); @@ -399,7 +399,7 @@ fn output_recording_for_literal_bool() { Call id(1), args( Bool(true), Pointer, ) Return config: Config: - capabilities: Base + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -441,7 +441,7 @@ fn output_recording_for_literal_int() { Call id(1), args( Integer(42), Pointer, ) Return config: Config: - capabilities: Base + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 0 num_results: 0"#]] .assert_eq(&program.to_string()); @@ -512,7 +512,7 @@ fn output_recording_for_mix_of_literal_and_variable() { Call id(4), args( Bool(true), Pointer, ) Return config: Config: - capabilities: Base + capabilities: TargetCapabilityFlags(Adaptive | IntegerComputations | FloatingPointComputations | BackwardsBranching | HigherLevelConstructs | QubitReset) num_qubits: 1 num_results: 1"#]] .assert_eq(&program.to_string()); diff --git a/compiler/qsc_partial_eval/tests/test_utils.rs b/compiler/qsc_partial_eval/tests/test_utils.rs index a5a358b848..0a588be4a0 100644 --- a/compiler/qsc_partial_eval/tests/test_utils.rs +++ b/compiler/qsc_partial_eval/tests/test_utils.rs @@ -37,7 +37,19 @@ pub fn assert_error(error: &Error, expected_error: &Expect) { #[must_use] pub fn get_partial_evaluation_error(source: &str) -> Error { - let maybe_program = compile_and_partially_evaluate(source); + let maybe_program = compile_and_partially_evaluate(source, TargetCapabilityFlags::all()); + match maybe_program { + Ok(_) => panic!("partial evaluation succeeded"), + Err(error) => error, + } +} + +#[must_use] +pub fn get_partial_evaluation_error_with_capabilities( + source: &str, + capabilities: TargetCapabilityFlags, +) -> Error { + let maybe_program = compile_and_partially_evaluate(source, capabilities); match maybe_program { Ok(_) => panic!("partial evaluation succeeded"), Err(error) => error, @@ -46,20 +58,35 @@ pub fn get_partial_evaluation_error(source: &str) -> Error { #[must_use] pub fn get_rir_program(source: &str) -> Program { - let maybe_program = compile_and_partially_evaluate(source); + let maybe_program = compile_and_partially_evaluate(source, TargetCapabilityFlags::all()); + match maybe_program { + Ok(program) => program, + Err(error) => panic!("partial evaluation failed: {error:?}"), + } +} + +#[must_use] +pub fn get_rir_program_with_capabilities( + source: &str, + capabilities: TargetCapabilityFlags, +) -> Program { + let maybe_program = compile_and_partially_evaluate(source, capabilities); match maybe_program { Ok(program) => program, Err(error) => panic!("partial evaluation failed: {error:?}"), } } -fn compile_and_partially_evaluate(source: &str) -> Result { - let compilation_context = CompilationContext::new(source); +fn compile_and_partially_evaluate( + source: &str, + capabilities: TargetCapabilityFlags, +) -> Result { + let compilation_context = CompilationContext::new(source, capabilities); partially_evaluate( &compilation_context.fir_store, &compilation_context.compute_properties, &compilation_context.entry, - TargetCapabilityFlags::empty(), + capabilities, ) } @@ -70,13 +97,13 @@ struct CompilationContext { } impl CompilationContext { - fn new(source: &str) -> Self { + fn new(source: &str, capabilities: TargetCapabilityFlags) -> Self { let source_map = SourceMap::new([("test".into(), source.into())], Some("".into())); let compiler = Compiler::new( true, source_map, PackageType::Exe, - TargetCapabilityFlags::all(), + capabilities, LanguageFeatures::default(), ) .expect("should be able to create a new compiler"); diff --git a/compiler/qsc_passes/src/capabilitiesck.rs b/compiler/qsc_passes/src/capabilitiesck.rs index 6fe0c9d6ba..38d6b6c181 100644 --- a/compiler/qsc_passes/src/capabilitiesck.rs +++ b/compiler/qsc_passes/src/capabilitiesck.rs @@ -13,7 +13,6 @@ mod tests_adaptive_plus_integers; #[cfg(test)] pub mod tests_common; -use miette::Diagnostic; use qsc_data_structures::{span::Span, target::TargetCapabilityFlags}; use qsc_fir::{ @@ -28,172 +27,11 @@ use qsc_fir::{ use qsc_lowerer::map_hir_package_to_fir; use qsc_rca::{ + errors::{generate_errors_from_runtime_features, get_missing_runtime_features, Error}, Analyzer, ComputeKind, ItemComputeProperties, PackageComputeProperties, PackageStoreComputeProperties, RuntimeFeatureFlags, }; use rustc_hash::FxHashMap; -use thiserror::Error; - -#[derive(Clone, Debug, Diagnostic, Error)] -pub enum Error { - #[error("cannot use a dynamic bool value")] - #[diagnostic(help( - "using a bool value that depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicBool"))] - UseOfDynamicBool(#[label] Span), - - #[error("cannot use a dynamic integer value")] - #[diagnostic(help( - "using an integer value that depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicInt"))] - UseOfDynamicInt(#[label] Span), - - #[error("cannot use a dynamic Pauli value")] - #[diagnostic(help( - "using a Pauli value that depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicPauli"))] - UseOfDynamicPauli(#[label] Span), - - #[error("cannot use a dynamic Range value")] - #[diagnostic(help( - "using a Range value that depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicRange"))] - UseOfDynamicRange(#[label] Span), - - #[error("cannot use a dynamic double value")] - #[diagnostic(help( - "using a double value that depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicDouble"))] - UseOfDynamicDouble(#[label] Span), - - #[error("cannot use a dynamic qubit")] - #[diagnostic(help( - "using a qubit whose allocation depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicQubit"))] - UseOfDynamicQubit(#[label] Span), - - #[error("cannot use a dynamic big integer value")] - #[diagnostic(help( - "using a big integer value that depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicBigInt"))] - UseOfDynamicBigInt(#[label] Span), - - #[error("cannot use a dynamic string value")] - #[diagnostic(help( - "using a string value that depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicString"))] - UseOfDynamicString(#[label] Span), - - #[error("cannot use a dynamically-sized array")] - #[diagnostic(help( - "using an array whose size depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicallySizedArray"))] - UseOfDynamicallySizedArray(#[label] Span), - - #[error("cannot use a dynamic user-defined type")] - #[diagnostic(help( - "using a user-defined type in which one or more of its members depend on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicUdt"))] - UseOfDynamicUdt(#[label] Span), - - #[error("cannot use a dynamic function")] - #[diagnostic(help( - "using a function whose resolution depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicArrowFunction"))] - UseOfDynamicArrowFunction(#[label] Span), - - #[error("cannot use a dynamic operation")] - #[diagnostic(help( - "using an operation whose resolution depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicArrowOperation"))] - UseOfDynamicArrowOperation(#[label] Span), - - #[error("cannot call a cyclic function with a dynamic value as argument")] - #[diagnostic(help( - "calling a cyclic function with an argument value that depends on a measurement result is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.CallToCyclicFunctionWithDynamicArg"))] - CallToCyclicFunctionWithDynamicArg(#[label] Span), - - #[error("cannot define a cyclic operation specialization")] - #[diagnostic(help("operation specializations that contain call cycles are not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.CyclicOperationSpec"))] - CyclicOperationSpec(#[label] Span), - - #[error("cannot call a cyclic operation")] - #[diagnostic(help("calling an operation specialization that contains call cycles is not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.CallToCyclicOperation"))] - CallToCyclicOperation(#[label] Span), - - #[error("cannot call a function or operation whose resolution is dynamic")] - #[diagnostic(help("calling a function or operation whose resolution depends on a measurement result is not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.CallToDynamicCallee"))] - CallToDynamicCallee(#[label] Span), - - #[error("cannot call a function or operation that can only be resolved at runtime")] - #[diagnostic(help("calling a function or operation that can only be resolved at runtime is not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.CallToUnresolvedCallee"))] - CallToUnresolvedCallee(#[label] Span), - - #[error("cannot perform a measurement within a dynamic scope")] - #[diagnostic(help("performing a measurement within a scope that depends on a measurement result is not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.MeasurementWithinDynamicScope"))] - MeasurementWithinDynamicScope(#[label] Span), - - #[error("cannot access an array using a dynamic index")] - #[diagnostic(help("accessing an array using an index that depends on a measurement result is not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicIndex"))] - UseOfDynamicIndex(#[label] Span), - - #[error("cannot use a return within a dynamic scope")] - #[diagnostic(help("using a return within a scope that depends on a measurement result is not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.ReturnWithinDynamicScope"))] - ReturnWithinDynamicScope(#[label] Span), - - #[error("cannot have a loop with a dynamic condition")] - #[diagnostic(help("using a loop with a condition that depends on a measurement result is not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.LoopWithDynamicCondition"))] - LoopWithDynamicCondition(#[label] Span), - - #[error("cannot use a closure")] - #[diagnostic(help("closures are not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfClosure"))] - UseOfClosure(#[label] Span), - - #[error("cannot use a bool value as an output")] - #[diagnostic(help("using a bool value as an output is not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfBoolOutput"))] - UseOfBoolOutput(#[label] Span), - - #[error("cannot use a double value as an output")] - #[diagnostic(help("using a Double as an output is not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDoubleOutput"))] - UseOfDoubleOutput(#[label] Span), - - #[error("cannot use an integer value as an output")] - #[diagnostic(help("using an integer as an output is not supported by the current target"))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfIntOutput"))] - UseOfIntOutput(#[label] Span), - - #[error("cannot use value with advanced type as an output")] - #[diagnostic(help( - "using a value of type callable, range, big integer, Pauli, Qubit or string as an output is not supported by the current target" - ))] - #[diagnostic(code("Qsc.CapabilitiesCk.UseOfAdvancedOutput"))] - UseOfAdvancedOutput(#[label] Span), -} /// Lower a package store from `qsc_frontend` HIR store to a `qsc_fir` FIR store. pub fn lower_store( @@ -529,100 +367,6 @@ impl<'a> Checker<'a> { } } -fn generate_errors_from_runtime_features( - runtime_features: RuntimeFeatureFlags, - span: Span, -) -> Vec { - let mut errors = Vec::::new(); - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicBool) { - errors.push(Error::UseOfDynamicBool(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicInt) { - errors.push(Error::UseOfDynamicInt(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicPauli) { - errors.push(Error::UseOfDynamicPauli(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicRange) { - errors.push(Error::UseOfDynamicRange(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicDouble) { - errors.push(Error::UseOfDynamicDouble(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicQubit) { - errors.push(Error::UseOfDynamicQubit(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicBigInt) { - errors.push(Error::UseOfDynamicBigInt(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicString) { - errors.push(Error::UseOfDynamicString(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicallySizedArray) { - errors.push(Error::UseOfDynamicallySizedArray(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicUdt) { - errors.push(Error::UseOfDynamicUdt(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicArrowFunction) { - errors.push(Error::UseOfDynamicArrowFunction(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicArrowOperation) { - errors.push(Error::UseOfDynamicArrowOperation(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::CallToCyclicFunctionWithDynamicArg) { - errors.push(Error::CallToCyclicFunctionWithDynamicArg(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::CyclicOperationSpec) { - errors.push(Error::CyclicOperationSpec(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::CallToCyclicOperation) { - errors.push(Error::CallToCyclicOperation(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::CallToDynamicCallee) { - errors.push(Error::CallToDynamicCallee(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::CallToUnresolvedCallee) { - errors.push(Error::CallToUnresolvedCallee(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::MeasurementWithinDynamicScope) { - errors.push(Error::MeasurementWithinDynamicScope(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicIndex) { - errors.push(Error::UseOfDynamicIndex(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::ReturnWithinDynamicScope) { - errors.push(Error::ReturnWithinDynamicScope(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::LoopWithDynamicCondition) { - errors.push(Error::LoopWithDynamicCondition(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfClosure) { - errors.push(Error::UseOfClosure(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfBoolOutput) { - errors.push(Error::UseOfBoolOutput(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfDoubleOutput) { - errors.push(Error::UseOfDoubleOutput(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfIntOutput) { - errors.push(Error::UseOfIntOutput(span)); - } - if runtime_features.contains(RuntimeFeatureFlags::UseOfAdvancedOutput) { - errors.push(Error::UseOfAdvancedOutput(span)); - } - errors -} - -fn get_missing_runtime_features( - runtime_features: RuntimeFeatureFlags, - target_capabilities: TargetCapabilityFlags, -) -> RuntimeFeatureFlags { - let missing_capabilities = !target_capabilities & runtime_features.target_capabilities(); - runtime_features.contributing_features(missing_capabilities) -} - fn get_spec_level_runtime_features(runtime_features: RuntimeFeatureFlags) -> RuntimeFeatureFlags { const SPEC_LEVEL_RUNTIME_FEATURES: RuntimeFeatureFlags = RuntimeFeatureFlags::CyclicOperationSpec; diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs index 531b2d4d5d..f4f6c97f9b 100644 --- a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs +++ b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs @@ -428,24 +428,11 @@ fn call_to_dynamic_operation_yields_errors() { } #[test] -fn call_to_unresolved_yields_errors() { +fn call_to_unresolved_allowed() { check_profile( CALL_UNRESOLVED_FUNCTION, &expect![[r#" - [ - UseOfDynamicDouble( - Span { - lo: 172, - hi: 180, - }, - ), - CallToUnresolvedCallee( - Span { - lo: 172, - hi: 180, - }, - ), - ] + [] "#]], ); } diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs index ede6132933..b36c52d036 100644 --- a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs +++ b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive_plus_integers.rs @@ -368,24 +368,11 @@ fn call_to_dynamic_operation_yields_errors() { } #[test] -fn call_to_unresolved_yields_errors() { +fn call_to_unresolved_allowed() { check_profile( CALL_UNRESOLVED_FUNCTION, &expect![[r#" - [ - UseOfDynamicDouble( - Span { - lo: 172, - hi: 180, - }, - ), - CallToUnresolvedCallee( - Span { - lo: 172, - hi: 180, - }, - ), - ] + [] "#]], ); } diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_base.rs b/compiler/qsc_passes/src/capabilitiesck/tests_base.rs index d6e30d666d..c45700d56a 100644 --- a/compiler/qsc_passes/src/capabilitiesck/tests_base.rs +++ b/compiler/qsc_passes/src/capabilitiesck/tests_base.rs @@ -516,24 +516,11 @@ fn call_to_dynamic_operation_yields_errors() { } #[test] -fn call_to_unresolved_yields_errors() { +fn call_to_unresolved_allowed() { check_profile( CALL_UNRESOLVED_FUNCTION, &expect![[r#" - [ - UseOfDynamicDouble( - Span { - lo: 172, - hi: 180, - }, - ), - CallToUnresolvedCallee( - Span { - lo: 172, - hi: 180, - }, - ), - ] + [] "#]], ); } diff --git a/compiler/qsc_passes/src/lib.rs b/compiler/qsc_passes/src/lib.rs index 45ff8988d8..aabadc7567 100644 --- a/compiler/qsc_passes/src/lib.rs +++ b/compiler/qsc_passes/src/lib.rs @@ -43,7 +43,7 @@ pub enum Error { BaseProfCk(baseprofck::Error), BorrowCk(borrowck::Error), CallableLimits(callable_limits::Error), - CapabilitiesCk(capabilitiesck::Error), + CapabilitiesCk(qsc_rca::errors::Error), ConjInvert(conjugate_invert::Error), EntryPoint(entry_point::Error), SpecGen(spec_gen::Error), diff --git a/compiler/qsc_rca/Cargo.toml b/compiler/qsc_rca/Cargo.toml index 0ff5ffcb10..f254bf46a4 100644 --- a/compiler/qsc_rca/Cargo.toml +++ b/compiler/qsc_rca/Cargo.toml @@ -17,6 +17,8 @@ qsc_fir = { path = "../qsc_fir" } qsc_frontend = { path = "../qsc_frontend" } qsc_lowerer = { path = "../qsc_lowerer" } rustc-hash = { workspace = true } +miette = { workspace = true } +thiserror = { workspace = true } [dev-dependencies] expect-test = { workspace = true } diff --git a/compiler/qsc_rca/src/core.rs b/compiler/qsc_rca/src/core.rs index 5da592bf62..0b6c43f242 100644 --- a/compiler/qsc_rca/src/core.rs +++ b/compiler/qsc_rca/src/core.rs @@ -483,9 +483,10 @@ impl<'a> Analyzer<'a> { // If the callee could not be resolved, return a compute kind with certain runtime features. let Some(callee) = maybe_callee else { - // The value kind of a call expression with an unresolved callee is dynamic but its specific variant depends - // on the expression's type. - let value_kind = ValueKind::new_dynamic_from_type(expr_type); + // The value kind of a call expression with an unresolved callee is not known, so to avoid + // spurious errors in later analysis where the value is used we assume static. + // During partial-evaluation, the callable is known the actual return kind will be checked. + let value_kind = ValueKind::new_static_from_type(expr_type); let compute_kind = ComputeKind::Quantum(QuantumProperties { runtime_features: RuntimeFeatureFlags::CallToUnresolvedCallee, value_kind, diff --git a/compiler/qsc_rca/src/errors.rs b/compiler/qsc_rca/src/errors.rs new file mode 100644 index 0000000000..45408cde6f --- /dev/null +++ b/compiler/qsc_rca/src/errors.rs @@ -0,0 +1,260 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use miette::Diagnostic; +use qsc_data_structures::{span::Span, target::TargetCapabilityFlags}; +use thiserror::Error; + +use crate::RuntimeFeatureFlags; + +#[derive(Clone, Debug, Diagnostic, Error)] +pub enum Error { + #[error("cannot use a dynamic bool value")] + #[diagnostic(help( + "using a bool value that depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicBool"))] + UseOfDynamicBool(#[label] Span), + + #[error("cannot use a dynamic integer value")] + #[diagnostic(help( + "using an integer value that depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicInt"))] + UseOfDynamicInt(#[label] Span), + + #[error("cannot use a dynamic Pauli value")] + #[diagnostic(help( + "using a Pauli value that depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicPauli"))] + UseOfDynamicPauli(#[label] Span), + + #[error("cannot use a dynamic Range value")] + #[diagnostic(help( + "using a Range value that depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicRange"))] + UseOfDynamicRange(#[label] Span), + + #[error("cannot use a dynamic double value")] + #[diagnostic(help( + "using a double value that depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicDouble"))] + UseOfDynamicDouble(#[label] Span), + + #[error("cannot use a dynamic qubit")] + #[diagnostic(help( + "using a qubit whose allocation depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicQubit"))] + UseOfDynamicQubit(#[label] Span), + + #[error("cannot use a dynamic big integer value")] + #[diagnostic(help( + "using a big integer value that depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicBigInt"))] + UseOfDynamicBigInt(#[label] Span), + + #[error("cannot use a dynamic string value")] + #[diagnostic(help( + "using a string value that depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicString"))] + UseOfDynamicString(#[label] Span), + + #[error("cannot use a dynamically-sized array")] + #[diagnostic(help( + "using an array whose size depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicallySizedArray"))] + UseOfDynamicallySizedArray(#[label] Span), + + #[error("cannot use a dynamic user-defined type")] + #[diagnostic(help( + "using a user-defined type in which one or more of its members depend on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicUdt"))] + UseOfDynamicUdt(#[label] Span), + + #[error("cannot use a dynamic function")] + #[diagnostic(help( + "using a function whose resolution depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicArrowFunction"))] + UseOfDynamicArrowFunction(#[label] Span), + + #[error("cannot use a dynamic operation")] + #[diagnostic(help( + "using an operation whose resolution depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicArrowOperation"))] + UseOfDynamicArrowOperation(#[label] Span), + + #[error("cannot call a cyclic function with a dynamic value as argument")] + #[diagnostic(help( + "calling a cyclic function with an argument value that depends on a measurement result is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.CallToCyclicFunctionWithDynamicArg"))] + CallToCyclicFunctionWithDynamicArg(#[label] Span), + + #[error("cannot define a cyclic operation specialization")] + #[diagnostic(help("operation specializations that contain call cycles are not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.CyclicOperationSpec"))] + CyclicOperationSpec(#[label] Span), + + #[error("cannot call a cyclic operation")] + #[diagnostic(help("calling an operation specialization that contains call cycles is not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.CallToCyclicOperation"))] + CallToCyclicOperation(#[label] Span), + + #[error("cannot call a function or operation whose resolution is dynamic")] + #[diagnostic(help("calling a function or operation whose resolution depends on a measurement result is not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.CallToDynamicCallee"))] + CallToDynamicCallee(#[label] Span), + + #[error("cannot perform a measurement within a dynamic scope")] + #[diagnostic(help("performing a measurement within a scope that depends on a measurement result is not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.MeasurementWithinDynamicScope"))] + MeasurementWithinDynamicScope(#[label] Span), + + #[error("cannot access an array using a dynamic index")] + #[diagnostic(help("accessing an array using an index that depends on a measurement result is not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDynamicIndex"))] + UseOfDynamicIndex(#[label] Span), + + #[error("cannot use a return within a dynamic scope")] + #[diagnostic(help("using a return within a scope that depends on a measurement result is not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.ReturnWithinDynamicScope"))] + ReturnWithinDynamicScope(#[label] Span), + + #[error("cannot have a loop with a dynamic condition")] + #[diagnostic(help("using a loop with a condition that depends on a measurement result is not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.LoopWithDynamicCondition"))] + LoopWithDynamicCondition(#[label] Span), + + #[error("cannot use a closure")] + #[diagnostic(help("closures are not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfClosure"))] + UseOfClosure(#[label] Span), + + #[error("cannot use a bool value as an output")] + #[diagnostic(help("using a bool value as an output is not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfBoolOutput"))] + UseOfBoolOutput(#[label] Span), + + #[error("cannot use a double value as an output")] + #[diagnostic(help("using a Double as an output is not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfDoubleOutput"))] + UseOfDoubleOutput(#[label] Span), + + #[error("cannot use an integer value as an output")] + #[diagnostic(help("using an integer as an output is not supported by the current target"))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfIntOutput"))] + UseOfIntOutput(#[label] Span), + + #[error("cannot use value with advanced type as an output")] + #[diagnostic(help( + "using a value of type callable, range, big integer, Pauli, Qubit or string as an output is not supported by the current target" + ))] + #[diagnostic(code("Qsc.CapabilitiesCk.UseOfAdvancedOutput"))] + UseOfAdvancedOutput(#[label] Span), +} + +#[must_use] +pub fn generate_errors_from_runtime_features( + runtime_features: RuntimeFeatureFlags, + span: Span, +) -> Vec { + let mut errors = Vec::::new(); + + // Errors are reported in order of relative importance, which makes it easier to read them + // and is helpful during partial evaluation when only the first error is reported to the user. + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicBool) { + errors.push(Error::UseOfDynamicBool(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicInt) { + errors.push(Error::UseOfDynamicInt(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicPauli) { + errors.push(Error::UseOfDynamicPauli(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicRange) { + errors.push(Error::UseOfDynamicRange(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicDouble) { + errors.push(Error::UseOfDynamicDouble(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicQubit) { + errors.push(Error::UseOfDynamicQubit(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicBigInt) { + errors.push(Error::UseOfDynamicBigInt(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicString) { + errors.push(Error::UseOfDynamicString(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicallySizedArray) { + errors.push(Error::UseOfDynamicallySizedArray(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicUdt) { + errors.push(Error::UseOfDynamicUdt(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicArrowFunction) { + errors.push(Error::UseOfDynamicArrowFunction(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicArrowOperation) { + errors.push(Error::UseOfDynamicArrowOperation(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::CallToCyclicFunctionWithDynamicArg) { + errors.push(Error::CallToCyclicFunctionWithDynamicArg(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::CyclicOperationSpec) { + errors.push(Error::CyclicOperationSpec(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::CallToCyclicOperation) { + errors.push(Error::CallToCyclicOperation(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::CallToDynamicCallee) { + errors.push(Error::CallToDynamicCallee(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::MeasurementWithinDynamicScope) { + errors.push(Error::MeasurementWithinDynamicScope(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDynamicIndex) { + errors.push(Error::UseOfDynamicIndex(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::ReturnWithinDynamicScope) { + errors.push(Error::ReturnWithinDynamicScope(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::LoopWithDynamicCondition) { + errors.push(Error::LoopWithDynamicCondition(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfClosure) { + errors.push(Error::UseOfClosure(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfBoolOutput) { + errors.push(Error::UseOfBoolOutput(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfDoubleOutput) { + errors.push(Error::UseOfDoubleOutput(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfIntOutput) { + errors.push(Error::UseOfIntOutput(span)); + } + if runtime_features.contains(RuntimeFeatureFlags::UseOfAdvancedOutput) { + errors.push(Error::UseOfAdvancedOutput(span)); + } + errors +} + +#[must_use] +pub fn get_missing_runtime_features( + runtime_features: RuntimeFeatureFlags, + target_capabilities: TargetCapabilityFlags, +) -> RuntimeFeatureFlags { + let missing_capabilities = !target_capabilities & runtime_features.target_capabilities(); + runtime_features.contributing_features(missing_capabilities) +} diff --git a/compiler/qsc_rca/src/lib.rs b/compiler/qsc_rca/src/lib.rs index 367c4369da..559ddb828b 100644 --- a/compiler/qsc_rca/src/lib.rs +++ b/compiler/qsc_rca/src/lib.rs @@ -12,6 +12,7 @@ mod common; mod core; mod cycle_detection; mod cyclic_callables; +pub mod errors; mod overrider; mod scaffolding; @@ -557,7 +558,7 @@ pub struct QuantumProperties { /// The runtime features used by the program element. pub runtime_features: RuntimeFeatureFlags, /// The kind of value of the program element. - pub(crate) value_kind: ValueKind, + pub value_kind: ValueKind, } impl Display for QuantumProperties {