Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 29 additions & 38 deletions source/compiler/qsc_partial_eval/src/evaluation_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use qsc_eval::{
val::{Result, Value},
};
use qsc_fir::fir::{LocalItemId, LocalVarId, PackageId};
use qsc_rca::{RuntimeKind, ValueKind};
use qsc_rca::{ComputeKind, RuntimeFeatureFlags, ValueKind};
use qsc_rir::rir::{BlockId, Literal, VariableId};
use rustc_hash::FxHashMap;
use std::collections::hash_map::Entry;
Expand Down Expand Up @@ -99,8 +99,8 @@ pub struct Scope {
pub package_id: PackageId,
/// The ID and functor information of the callable.
pub callable: Option<(LocalItemId, FunctorApp)>,
/// The value of the arguments passed to the callable.
pub args_value_kind: Vec<ValueKind>,
/// The compute kind of the arguments passed to the callable.
pub args_compute_kind: Vec<ComputeKind>,
/// The classical environment of the callable, which holds values corresponding to local variables.
pub env: Env,
/// Map that holds the values of local variables.
Expand All @@ -127,15 +127,18 @@ impl Scope {
let mut env = Env::default();
env.push_scope(CLASSICAL_EVALUATOR_CALL_SCOPE_ID);

// Determine the runtime kind (static or dynamic) of the arguments.
let args_value_kind: Vec<ValueKind> = args
// Determine the compute kind of the arguments, assuming empty feature flags.
let args_compute_kind: Vec<ComputeKind> = args
.iter()
.map(|arg| {
let value = match arg {
Arg::Discard(value) => value,
Arg::Var(_, var) => &var.value,
};
map_eval_value_to_value_kind(value)
ComputeKind::new_with_runtime_features(
RuntimeFeatureFlags::empty(),
map_eval_value_to_value_kind(value),
)
})
.collect();

Expand All @@ -149,7 +152,7 @@ impl Scope {

// Add the values to both the classical environment and the hybrid variables depending on whether the value is
// static or dynamic.
let arg_runtime_kind_tuple = args.into_iter().zip(args_value_kind.iter());
let arg_runtime_kind_tuple = args.into_iter().zip(args_compute_kind.iter());
for (arg, _) in arg_runtime_kind_tuple {
let Arg::Var(local_var_id, var) = arg else {
continue;
Expand All @@ -162,7 +165,7 @@ impl Scope {
Self {
package_id,
callable,
args_value_kind,
args_compute_kind,
env,
active_block_count: 1,
hybrid_vars,
Expand Down Expand Up @@ -286,39 +289,27 @@ impl EvalControlFlow {
}

fn map_eval_value_to_value_kind(value: &Value) -> ValueKind {
fn map_array_eval_value_to_value_kind(elements: &[Value]) -> ValueKind {
let mut content_runtime_kind = RuntimeKind::Static;
for element in elements {
let element_value_kind = map_eval_value_to_value_kind(element);
if element_value_kind.is_dynamic() {
content_runtime_kind = RuntimeKind::Dynamic;
break;
match value {
Value::Array(elements) => {
for element in elements.iter() {
let element_runtime_kind = map_eval_value_to_value_kind(element);
if element_runtime_kind == ValueKind::Dynamic {
return ValueKind::Dynamic;
}
}
}

// The runtime capabilities check pass disallows dynamically-sized arrays for all targets for which we generate
// QIR. Because of this, we assume that during partial evaluation all arrays are statically-sized.
ValueKind::Array(content_runtime_kind, RuntimeKind::Static)
}

fn map_tuple_eval_value_to_value_kind(elements: &[Value]) -> ValueKind {
let mut runtime_kind = RuntimeKind::Static;
for element in elements {
let element_value_kind = map_eval_value_to_value_kind(element);
if element_value_kind.is_dynamic() {
runtime_kind = RuntimeKind::Dynamic;
break;
}
ValueKind::Static
}
ValueKind::Element(runtime_kind)
}

match value {
Value::Array(elements) => map_array_eval_value_to_value_kind(elements),
Value::Tuple(elements, _) => map_tuple_eval_value_to_value_kind(elements),
Value::Result(Result::Id(_) | Result::Loss) | Value::Var(_) => {
ValueKind::Element(RuntimeKind::Dynamic)
Value::Tuple(elements, _) => {
for element in elements.iter() {
let element_runtime_kind = map_eval_value_to_value_kind(element);
if element_runtime_kind == ValueKind::Dynamic {
return ValueKind::Dynamic;
}
}
ValueKind::Static
}
Value::Result(Result::Id(_) | Result::Loss) | Value::Var(_) => ValueKind::Dynamic,
Value::BigInt(_)
| Value::Bool(_)
| Value::Closure(_)
Expand All @@ -329,6 +320,6 @@ fn map_eval_value_to_value_kind(value: &Value) -> ValueKind {
| Value::Qubit(_)
| Value::Range(_)
| Value::Result(Result::Val(_))
| Value::String(_) => ValueKind::Element(RuntimeKind::Static),
| Value::String(_) => ValueKind::Static,
}
}
17 changes: 5 additions & 12 deletions source/compiler/qsc_partial_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use qsc_fir::{
use qsc_lowerer::map_fir_package_to_hir;
use qsc_rca::{
ComputeKind, ComputePropertiesLookup, ItemComputeProperties, PackageStoreComputeProperties,
QuantumProperties, RuntimeFeatureFlags, RuntimeKind, ValueKind,
QuantumProperties, RuntimeFeatureFlags, ValueKind,
errors::{
Error as CapabilityError, generate_errors_from_runtime_features,
get_missing_runtime_features,
Expand Down Expand Up @@ -1526,7 +1526,7 @@ impl<'a> PartialEvaluator<'a> {
// 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() {
if value_kind == ValueKind::Dynamic {
return Err(Error::UnexpectedDynamicValue(
self.get_expr_package_span(call_expr_id),
));
Expand Down Expand Up @@ -2211,14 +2211,7 @@ impl<'a> PartialEvaluator<'a> {
// Verify assumptions: the condition expression must either classical (such that it can be fully evaluated) or
// quantum but statically known at runtime (such that it can be partially evaluated to a known value).
assert!(
matches!(
self.get_expr_compute_kind(condition_expr_id),
ComputeKind::Classical
| ComputeKind::Quantum(QuantumProperties {
runtime_features: _,
value_kind: ValueKind::Element(RuntimeKind::Static),
})
),
!self.get_expr_compute_kind(condition_expr_id).is_dynamic(),
"loop conditions must be purely classical"
);

Expand Down Expand Up @@ -2636,7 +2629,7 @@ impl<'a> PartialEvaluator<'a> {
let store_expr_id = StoreExprId::from((current_package_id, expr_id));
let expr_generator_set = self.compute_properties.get_expr(store_expr_id);
let callable_scope = self.eval_context.get_current_scope();
expr_generator_set.generate_application_compute_kind(&callable_scope.args_value_kind)
expr_generator_set.generate_application_compute_kind(&callable_scope.args_compute_kind)
}

fn is_unresolved_callee_expr(&self, expr_id: ExprId) -> bool {
Expand Down Expand Up @@ -2677,7 +2670,7 @@ impl<'a> PartialEvaluator<'a> {
},
None => panic!("call compute kind should have callable"),
};
callable_generator_set.generate_application_compute_kind(&callable_scope.args_value_kind)
callable_generator_set.generate_application_compute_kind(&callable_scope.args_compute_kind)
}

fn try_create_mutable_variable(
Expand Down
Loading
Loading