diff --git a/compiler/qsc_partial_eval/src/lib.rs b/compiler/qsc_partial_eval/src/lib.rs index 608e50c24e..9b0e8dc72d 100644 --- a/compiler/qsc_partial_eval/src/lib.rs +++ b/compiler/qsc_partial_eval/src/lib.rs @@ -287,8 +287,8 @@ impl<'a> PartialEvaluator<'a> { &mut self, bin_op: BinOp, lhs_value: Value, - lhs_span: Span, rhs_expr_id: ExprId, + lhs_span: Span, // For diagnostic purposes only. bin_op_expr_span: Span, // For diagnostic purposes only. ) -> Result { // Evaluate the binary operation differently depending on the LHS value variant. @@ -299,26 +299,31 @@ impl<'a> PartialEvaluator<'a> { rhs_expr_id, bin_op_expr_span, ), - Value::Result(_lhs_result) => Err(Error::Unimplemented( - "result binary operation".to_string(), - lhs_span, - )), - Value::Bool(_lhs_bool) => Err(Error::Unimplemented( - "bool binary operation".to_string(), - lhs_span, - )), - Value::Int(_lhs_int) => Err(Error::Unimplemented( - "int binary operation".to_string(), - lhs_span, - )), + Value::Result(lhs_result) => self.eval_bin_op_with_lhs_result_operand( + bin_op, + lhs_result, + rhs_expr_id, + bin_op_expr_span, + ), + Value::Bool(lhs_bool) => { + self.eval_bin_op_with_lhs_classical_bool_operand(bin_op, lhs_bool, rhs_expr_id) + } + Value::Int(lhs_int) => { + let lhs_operand = Operand::Literal(Literal::Integer(lhs_int)); + self.eval_bin_op_with_lhs_integer_operand( + bin_op, + lhs_operand, + rhs_expr_id, + bin_op_expr_span, + ) + } Value::Double(_lhs_double) => Err(Error::Unimplemented( "double binary operation".to_string(), lhs_span, )), - Value::Var(_lhs_eval_var) => Err(Error::Unimplemented( - "binary operation with dynamic LHS".to_string(), - lhs_span, - )), + Value::Var(lhs_eval_var) => { + self.eval_bin_op_with_lhs_var(bin_op, lhs_eval_var, rhs_expr_id, bin_op_expr_span) + } _ => Err(Error::Unexpected( format!("unsupported LHS value: {lhs_value}"), lhs_span, @@ -367,6 +372,191 @@ impl<'a> PartialEvaluator<'a> { Ok(EvalControlFlow::Continue(array_value)) } + fn eval_bin_op_with_lhs_result_operand( + &mut self, + bin_op: BinOp, + lhs_result: val::Result, + rhs_expr_id: ExprId, + bin_op_expr_span: Span, // For diagnostic purposes only. + ) -> Result { + let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?; + let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else { + let rhs_expr = self.get_expr(rhs_expr_id); + return Err(Error::Unexpected( + "embedded return in RHS expression".to_string(), + rhs_expr.span, + )); + }; + let Value::Result(rhs_result) = rhs_value else { + panic!("expected result value from RHS expression"); + }; + + // Get the operands to use when generating the binary operation instruction. + let lhs_operand = self.eval_result_as_bool_operand(lhs_result); + let rhs_operand = self.eval_result_as_bool_operand(rhs_result); + + // Create a variable to store the result of the expression. + let variable_id = self.resource_manager.next_var(); + let rir_variable = rir::Variable { + variable_id, + ty: rir::Ty::Boolean, // Binary operations between results are always Boolean. + }; + + // Create the binary operation instruction and add it to the current block. + let condition_code = match bin_op { + BinOp::Eq => ConditionCode::Eq, + BinOp::Neq => ConditionCode::Ne, + _ => { + return Err(Error::Unexpected( + format!("invalid binary operator for Result operands: {bin_op:?})"), + bin_op_expr_span, + )) + } + }; + let instruction = Instruction::Icmp(condition_code, lhs_operand, rhs_operand, rir_variable); + self.get_current_rir_block_mut().0.push(instruction); + + // Return the variable as a value. + let value = Value::Var(map_rir_var_to_eval_var(rir_variable)); + Ok(EvalControlFlow::Continue(value)) + } + + fn eval_bin_op_with_lhs_classical_bool_operand( + &mut self, + bin_op: BinOp, + lhs_bool: bool, + rhs_expr_id: ExprId, + ) -> Result { + let value = match (bin_op, lhs_bool) { + // Handle short-circuiting for logical AND and logical OR. + (BinOp::AndL, false) => Value::Bool(false), + (BinOp::OrL, true) => Value::Bool(true), + // The other possible cases. + (BinOp::AndL | BinOp::OrL | BinOp::Eq | BinOp::Neq, _) => { + // Try to evaluate the RHS expression to get its value. + let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?; + let EvalControlFlow::Continue(Value::Var(rhs_eval_var)) = rhs_control_flow else { + let rhs_expr = self.get_expr(rhs_expr_id); + return Err(Error::Unexpected( + "expected var from RHS expression".to_string(), + rhs_expr.span, + )); + }; + assert!( + matches!(rhs_eval_var.ty, VarTy::Boolean), + "expected var to be boolean" + ); + + // Generate the specific instruction depending on the operand. + let bin_op_variable_id = self.resource_manager.next_var(); + let bin_op_rir_variable = rir::Variable { + variable_id: bin_op_variable_id, + ty: rir::Ty::Boolean, + }; + let lhs_operand = Operand::Literal(Literal::Bool(lhs_bool)); + let rhs_operand = Operand::Variable(map_eval_var_to_rir_var(rhs_eval_var)); + let bin_op_ins = match bin_op { + BinOp::AndL => { + Instruction::LogicalAnd(lhs_operand, rhs_operand, bin_op_rir_variable) + } + BinOp::OrL => { + Instruction::LogicalOr(lhs_operand, rhs_operand, bin_op_rir_variable) + } + BinOp::Eq => Instruction::Icmp( + ConditionCode::Eq, + lhs_operand, + rhs_operand, + bin_op_rir_variable, + ), + BinOp::Neq => Instruction::Icmp( + ConditionCode::Ne, + lhs_operand, + rhs_operand, + bin_op_rir_variable, + ), + _ => panic!("unsupported binary operation for bools: {bin_op:?}"), + }; + self.get_current_rir_block_mut().0.push(bin_op_ins); + Value::Var(map_rir_var_to_eval_var(bin_op_rir_variable)) + } + _ => panic!("unsupported binary operation for bools: {bin_op:?}"), + }; + Ok(EvalControlFlow::Continue(value)) + } + + fn eval_bin_op_with_lhs_var( + &mut self, + bin_op: BinOp, + lhs_eval_var: Var, + rhs_expr_id: ExprId, + bin_op_expr_span: Span, // For diagnostic purposes only. + ) -> Result { + match lhs_eval_var.ty { + VarTy::Boolean => Err(Error::Unimplemented( + "bool binary operation with dynamic LHS".to_string(), + bin_op_expr_span, + )), + VarTy::Integer => { + let lhs_rir_var = map_eval_var_to_rir_var(lhs_eval_var); + let lhs_operand = Operand::Variable(lhs_rir_var); + self.eval_bin_op_with_lhs_integer_operand( + bin_op, + lhs_operand, + rhs_expr_id, + bin_op_expr_span, + ) + } + VarTy::Double => Err(Error::Unimplemented( + "double binary operation with dynamic LHS".to_string(), + bin_op_expr_span, + )), + } + } + + fn eval_bin_op_with_lhs_integer_operand( + &mut self, + bin_op: BinOp, + lhs_operand: Operand, + rhs_expr_id: ExprId, + bin_op_expr_span: Span, // For diagnostic purposes only. + ) -> Result { + assert!( + matches!(lhs_operand.get_type(), rir::Ty::Integer), + "LHS is expected to be of integer type" + ); + + // Try to evaluate the RHS expression to get its value and construct its operand. + let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?; + let EvalControlFlow::Continue(rhs_value) = rhs_control_flow else { + let rhs_expr = self.get_expr(rhs_expr_id); + return Err(Error::Unexpected( + "embedded return in RHS expression".to_string(), + rhs_expr.span, + )); + }; + let rhs_operand = map_eval_value_to_rir_operand(&rhs_value); + assert!( + matches!(rhs_operand.get_type(), rir::Ty::Integer), + "LHS value is expected to be of integer type" + ); + + // Create the variable ID, create the instruction and insert it. + let bin_op_variable_id = self.resource_manager.next_var(); + let (bin_op_rir_variable, bin_op_rir_ins) = + create_instruction_for_binary_operation_with_integer_operands( + bin_op, + lhs_operand, + rhs_operand, + bin_op_variable_id, + bin_op_expr_span, + )?; + + // Insert the instruction. + self.get_current_rir_block_mut().0.push(bin_op_rir_ins); + let value = Value::Var(map_rir_var_to_eval_var(bin_op_rir_variable)); + Ok(EvalControlFlow::Continue(value)) + } + fn eval_classical_expr(&mut self, expr_id: ExprId) -> Result { let current_package_id = self.get_current_package_id(); let store_expr_id = StoreExprId::from((current_package_id, expr_id)); @@ -445,7 +635,7 @@ impl<'a> PartialEvaluator<'a> { self.eval_expr_assign_op(*bin_op, *lhs_expr_id, *rhs_expr_id, expr.span) } ExprKind::BinOp(bin_op, lhs_expr_id, rhs_expr_id) => { - self.eval_expr_bin_op(expr_id, *bin_op, *lhs_expr_id, *rhs_expr_id) + self.eval_expr_bin_op(*bin_op, *lhs_expr_id, *rhs_expr_id, expr.span) } ExprKind::Block(block_id) => self.try_eval_block(*block_id), ExprKind::Call(callee_expr_id, args_expr_id) => { @@ -628,12 +818,12 @@ impl<'a> PartialEvaluator<'a> { let bin_op_control_flow = self.eval_bin_op( bin_op, lhs_value, - lhs_expr.span, rhs_expr_id, + lhs_expr.span, bin_op_expr_span, )?; let EvalControlFlow::Continue(bin_op_value) = bin_op_control_flow else { - panic!("evaluating a binary operation is expected to return an error or a continue, never a return"); + panic!("evaluating a binary operation is expected to result in an error or a continue, but never in a return"); }; self.update_bindings(lhs_expr_id, bin_op_value)?; Ok(EvalControlFlow::Continue(Value::unit())) @@ -642,71 +832,31 @@ impl<'a> PartialEvaluator<'a> { #[allow(clippy::similar_names)] fn eval_expr_bin_op( &mut self, - bin_op_expr_id: ExprId, bin_op: BinOp, lhs_expr_id: ExprId, rhs_expr_id: ExprId, + bin_op_expr_span: Span, // For diagnostic purposes only. ) -> Result { - // Try to evaluate both the LHS and RHS expressions to get their value, short-circuiting execution if any of the - // expressions is a return. + // Try to evaluate the LHS expression and get its value, short-circuiting execution if it is a return. let lhs_control_flow = self.try_eval_expr(lhs_expr_id)?; - if lhs_control_flow.is_return() { + let EvalControlFlow::Continue(lhs_value) = lhs_control_flow else { let lhs_expr = self.get_expr(lhs_expr_id); return Err(Error::Unexpected( "embedded return in binary operation".to_string(), lhs_expr.span, )); - } - let rhs_control_flow = self.try_eval_expr(rhs_expr_id)?; - if rhs_control_flow.is_return() { - let rhs_expr = self.get_expr(rhs_expr_id); - return Err(Error::Unexpected( - "embedded return in binary operation".to_string(), - rhs_expr.span, - )); - } - - // Get the operands to use when generating the binary operation instruction depending on the type of the - // expression's value. - let lhs_value = lhs_control_flow.into_value(); - let lhs_operand = if let Value::Result(result) = lhs_value { - self.eval_result_as_bool_operand(result) - } else { - map_eval_value_to_rir_operand(&lhs_value) - }; - let rhs_value = rhs_control_flow.into_value(); - let rhs_operand = if let Value::Result(result) = rhs_value { - self.eval_result_as_bool_operand(result) - } else { - map_eval_value_to_rir_operand(&rhs_value) - }; - - // Create a variable to store the result of the expression. - let bin_op_expr = self.get_expr(bin_op_expr_id); - let variable_id = self.resource_manager.next_var(); - let variable_ty = map_fir_type_to_rir_type(&bin_op_expr.ty); - let variable = rir::Variable { - variable_id, - ty: variable_ty, - }; - - // Create the binary operation instruction and add it to the current block. - let instruction = match bin_op { - BinOp::Eq => Instruction::Icmp(ConditionCode::Eq, lhs_operand, rhs_operand, variable), - BinOp::Neq => Instruction::Icmp(ConditionCode::Ne, lhs_operand, rhs_operand, variable), - _ => { - return Err(Error::Unimplemented( - format!("BinOp Expr ({bin_op:?})"), - bin_op_expr.span, - )) - } }; - let current_block = self.get_current_rir_block_mut(); - current_block.0.push(instruction); - // Return the variable as a value. - let value = Value::Var(map_rir_var_to_eval_var(variable)); - Ok(EvalControlFlow::Continue(value)) + // Now that we have a LHS value, evaluate the binary operation, which will properly consider short-circuiting + // logic in the case of Boolean operations. + let lhs_expr = self.get_expr(lhs_expr_id); + self.eval_bin_op( + bin_op, + lhs_value, + rhs_expr_id, + lhs_expr.span, + bin_op_expr_span, + ) } fn eval_expr_call( @@ -1912,6 +2062,140 @@ impl<'a> PartialEvaluator<'a> { } } +#[allow(clippy::too_many_lines)] +fn create_instruction_for_binary_operation_with_integer_operands( + bin_op: BinOp, + lhs_operand: Operand, + rhs_operand: Operand, + bin_op_variable_id: rir::VariableId, + bin_op_expr_span: Span, // For diagnostic purposes only. +) -> Result<(rir::Variable, Instruction), Error> { + let (rir_variable, rir_ins) = match bin_op { + BinOp::Add => { + let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Add(lhs_operand, rhs_operand, bin_op_rir_variable); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Sub => { + let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Sub(lhs_operand, rhs_operand, bin_op_rir_variable); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Mul => { + let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Mul(lhs_operand, rhs_operand, bin_op_rir_variable); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Div => { + let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Sdiv(lhs_operand, rhs_operand, bin_op_rir_variable); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Mod => { + let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Srem(lhs_operand, rhs_operand, bin_op_rir_variable); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Exp => { + let error = Error::Unimplemented( + "exponentiation for integer operands".to_string(), + bin_op_expr_span, + ); + return Err(error); + } + BinOp::AndB => { + let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id); + let bin_op_rir_ins = + Instruction::BitwiseAnd(lhs_operand, rhs_operand, bin_op_rir_variable); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::OrB => { + let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id); + let bin_op_rir_ins = + Instruction::BitwiseOr(lhs_operand, rhs_operand, bin_op_rir_variable); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::XorB => { + let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id); + let bin_op_rir_ins = + Instruction::BitwiseXor(lhs_operand, rhs_operand, bin_op_rir_variable); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Shl => { + let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Shl(lhs_operand, rhs_operand, bin_op_rir_variable); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Shr => { + let bin_op_rir_variable = rir::Variable::new_integer(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Ashr(lhs_operand, rhs_operand, bin_op_rir_variable); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Eq => { + let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Icmp( + ConditionCode::Eq, + lhs_operand, + rhs_operand, + bin_op_rir_variable, + ); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Neq => { + let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Icmp( + ConditionCode::Ne, + lhs_operand, + rhs_operand, + bin_op_rir_variable, + ); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Gt => { + let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Icmp( + ConditionCode::Sgt, + lhs_operand, + rhs_operand, + bin_op_rir_variable, + ); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Gte => { + let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Icmp( + ConditionCode::Sge, + lhs_operand, + rhs_operand, + bin_op_rir_variable, + ); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Lt => { + let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Icmp( + ConditionCode::Slt, + lhs_operand, + rhs_operand, + bin_op_rir_variable, + ); + (bin_op_rir_variable, bin_op_rir_ins) + } + BinOp::Lte => { + let bin_op_rir_variable = rir::Variable::new_boolean(bin_op_variable_id); + let bin_op_rir_ins = Instruction::Icmp( + ConditionCode::Sle, + lhs_operand, + rhs_operand, + bin_op_rir_variable, + ); + (bin_op_rir_variable, bin_op_rir_ins) + } + _ => panic!("unsupported binary operation for integers: {bin_op:?}"), + }; + Ok((rir_variable, rir_ins)) +} + fn get_spec_decl(spec_impl: &SpecImpl, functor_app: FunctorApp) -> &SpecDecl { if !functor_app.adjoint && functor_app.controlled == 0 { &spec_impl.body diff --git a/compiler/qsc_partial_eval/tests/arrays.rs b/compiler/qsc_partial_eval/tests/arrays.rs index f34bd59cd3..eb1220fe9b 100644 --- a/compiler/qsc_partial_eval/tests/arrays.rs +++ b/compiler/qsc_partial_eval/tests/arrays.rs @@ -215,231 +215,6 @@ fn array_repeat_with_dynamic_content() { ); } -#[test] -fn array_of_results_replace_element_at_index_with_dynamic_content() { - let program = get_rir_program(indoc! {r#" - namespace Test { - @EntryPoint() - operation Main() : Result[] { - use (q0, q1) = (Qubit(), Qubit()); - mutable arr = [MResetZ(q0), Zero]; - set arr w/= 1 <- MResetZ(q1); - arr - } - } - "#}); - let measurement_callable_id = CallableId(1); - assert_callable( - &program, - measurement_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__mresetz__body - call_type: Measurement - input_type: - [0]: Qubit - [1]: Result - output_type: - body: "#]], - ); - let array_output_recording_callable_id = CallableId(2); - assert_callable( - &program, - array_output_recording_callable_id, - &expect![[r#" - Callable: - name: __quantum__rt__array_record_output - call_type: OutputRecording - input_type: - [0]: Integer - [1]: Pointer - output_type: - body: "#]], - ); - let result_output_recording_callable_id = CallableId(3); - assert_callable( - &program, - result_output_recording_callable_id, - &expect![[r#" - Callable: - name: __quantum__rt__result_record_output - call_type: OutputRecording - input_type: - [0]: Result - [1]: Pointer - output_type: - body: "#]], - ); - assert_block_instructions( - &program, - BlockId(0), - &expect![[r#" - Block: - Call id(1), args( Qubit(0), Result(0), ) - Call id(1), args( Qubit(1), Result(1), ) - Call id(2), args( Integer(2), Pointer, ) - Call id(3), args( Result(0), Pointer, ) - Call id(3), args( Result(1), Pointer, ) - Return"#]], - ); -} - -#[test] -fn array_of_bools_replace_element_at_index_with_dynamic_content() { - let program = get_rir_program(indoc! {r#" - namespace Test { - @EntryPoint() - operation Main() : Bool[] { - use (q0, q1) = (Qubit(), Qubit()); - mutable arr = [MResetZ(q0) == Zero, true]; - set arr w/= 1 <- MResetZ(q1) == One; - arr - } - } - "#}); - let measurement_callable_id = CallableId(1); - assert_callable( - &program, - measurement_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__mresetz__body - call_type: Measurement - input_type: - [0]: Qubit - [1]: Result - output_type: - body: "#]], - ); - let readout_callable_id = CallableId(2); - assert_callable( - &program, - readout_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__read_result__body - call_type: Readout - input_type: - [0]: Result - output_type: Boolean - body: "#]], - ); - let array_output_recording_callable_id = CallableId(3); - assert_callable( - &program, - array_output_recording_callable_id, - &expect![[r#" - Callable: - name: __quantum__rt__array_record_output - call_type: OutputRecording - input_type: - [0]: Integer - [1]: Pointer - output_type: - body: "#]], - ); - let bool_output_recording_callable_id = CallableId(4); - assert_callable( - &program, - bool_output_recording_callable_id, - &expect![[r#" - Callable: - name: __quantum__rt__bool_record_output - call_type: OutputRecording - input_type: - [0]: Boolean - [1]: Pointer - output_type: - body: "#]], - ); - assert_block_instructions( - &program, - BlockId(0), - &expect![[r#" - Block: - Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) - Call id(1), args( Qubit(1), Result(1), ) - Variable(2, Boolean) = Call id(2), args( Result(1), ) - Variable(3, Boolean) = Icmp Eq, Variable(2, Boolean), Bool(true) - Call id(3), args( Integer(2), Pointer, ) - Call id(4), args( Variable(1, Boolean), Pointer, ) - Call id(4), args( Variable(3, Boolean), Pointer, ) - Return"#]], - ); -} - -#[test] -fn empty_array_of_results_in_place_concatenation() { - let program = get_rir_program(indoc! {r#" - namespace Test { - @EntryPoint() - operation Main() : Result[] { - use (q0, q1) = (Qubit(), Qubit()); - mutable results = []; - set results += [MResetZ(q0)]; - set results += [MResetZ(q1)]; - results - } - } - "#}); - let measurement_callable_id = CallableId(1); - assert_callable( - &program, - measurement_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__mresetz__body - call_type: Measurement - input_type: - [0]: Qubit - [1]: Result - output_type: - body: "#]], - ); - let array_output_recording_callable_id = CallableId(2); - assert_callable( - &program, - array_output_recording_callable_id, - &expect![[r#" - Callable: - name: __quantum__rt__array_record_output - call_type: OutputRecording - input_type: - [0]: Integer - [1]: Pointer - output_type: - body: "#]], - ); - let result_output_recording_callable_id = CallableId(3); - assert_callable( - &program, - result_output_recording_callable_id, - &expect![[r#" - Callable: - name: __quantum__rt__result_record_output - call_type: OutputRecording - input_type: - [0]: Result - [1]: Pointer - output_type: - body: "#]], - ); - assert_block_instructions( - &program, - BlockId(0), - &expect![[r#" - Block: - Call id(1), args( Qubit(0), Result(0), ) - Call id(1), args( Qubit(1), Result(1), ) - Call id(2), args( Integer(2), Pointer, ) - Call id(3), args( Result(0), Pointer, ) - Call id(3), args( Result(1), Pointer, ) - Return"#]], - ); -} - #[test] fn result_at_index_in_array() { let program = get_rir_program(indoc! {r#" @@ -491,72 +266,3 @@ fn result_at_index_in_array() { Return"#]], ); } - -#[test] -fn non_empty_array_of_results_in_place_concatenation() { - let program = get_rir_program(indoc! {r#" - namespace Test { - @EntryPoint() - operation Main() : Result[] { - use (q0, q1) = (Qubit(), Qubit()); - mutable results = [MResetZ(q0)]; - set results += [MResetZ(q1)]; - results - } - } - "#}); - let measurement_callable_id = CallableId(1); - assert_callable( - &program, - measurement_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__mresetz__body - call_type: Measurement - input_type: - [0]: Qubit - [1]: Result - output_type: - body: "#]], - ); - let array_output_recording_callable_id = CallableId(2); - assert_callable( - &program, - array_output_recording_callable_id, - &expect![[r#" - Callable: - name: __quantum__rt__array_record_output - call_type: OutputRecording - input_type: - [0]: Integer - [1]: Pointer - output_type: - body: "#]], - ); - let result_output_recording_callable_id = CallableId(3); - assert_callable( - &program, - result_output_recording_callable_id, - &expect![[r#" - Callable: - name: __quantum__rt__result_record_output - call_type: OutputRecording - input_type: - [0]: Result - [1]: Pointer - output_type: - body: "#]], - ); - assert_block_instructions( - &program, - BlockId(0), - &expect![[r#" - Block: - Call id(1), args( Qubit(0), Result(0), ) - Call id(1), args( Qubit(1), Result(1), ) - Call id(2), args( Integer(2), Pointer, ) - Call id(3), args( Result(0), Pointer, ) - Call id(3), args( Result(1), Pointer, ) - Return"#]], - ); -} diff --git a/compiler/qsc_partial_eval/tests/assigns.rs b/compiler/qsc_partial_eval/tests/assigns.rs index 05c46381e3..05376694a6 100644 --- a/compiler/qsc_partial_eval/tests/assigns.rs +++ b/compiler/qsc_partial_eval/tests/assigns.rs @@ -491,3 +491,1492 @@ fn assigning_result_literal_within_dynamic_if_expression_produces_error() { ]], ); } + +#[test] +fn array_of_results_replace_element_at_index_with_dynamic_content() { + let program = get_rir_program(indoc! {r#" + namespace Test { + @EntryPoint() + operation Main() : Result[] { + use (q0, q1) = (Qubit(), Qubit()); + mutable arr = [MResetZ(q0), Zero]; + set arr w/= 1 <- MResetZ(q1); + arr + } + } + "#}); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let array_output_recording_callable_id = CallableId(2); + assert_callable( + &program, + array_output_recording_callable_id, + &expect![[r#" + Callable: + name: __quantum__rt__array_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + let result_output_recording_callable_id = CallableId(3); + assert_callable( + &program, + result_output_recording_callable_id, + &expect![[r#" + Callable: + name: __quantum__rt__result_record_output + call_type: OutputRecording + input_type: + [0]: Result + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Call id(1), args( Qubit(1), Result(1), ) + Call id(2), args( Integer(2), Pointer, ) + Call id(3), args( Result(0), Pointer, ) + Call id(3), args( Result(1), Pointer, ) + Return"#]], + ); +} + +#[test] +fn array_of_bools_replace_element_at_index_with_dynamic_content() { + let program = get_rir_program(indoc! {r#" + namespace Test { + @EntryPoint() + operation Main() : Bool[] { + use (q0, q1) = (Qubit(), Qubit()); + mutable arr = [MResetZ(q0) == Zero, true]; + set arr w/= 1 <- MResetZ(q1) == One; + arr + } + } + "#}); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let array_output_recording_callable_id = CallableId(3); + assert_callable( + &program, + array_output_recording_callable_id, + &expect![[r#" + Callable: + name: __quantum__rt__array_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + let bool_output_recording_callable_id = CallableId(4); + assert_callable( + &program, + bool_output_recording_callable_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Call id(1), args( Qubit(1), Result(1), ) + Variable(2, Boolean) = Call id(2), args( Result(1), ) + Variable(3, Boolean) = Icmp Eq, Variable(2, Boolean), Bool(true) + Call id(3), args( Integer(2), Pointer, ) + Call id(4), args( Variable(1, Boolean), Pointer, ) + Call id(4), args( Variable(3, Boolean), Pointer, ) + Return"#]], + ); +} + +#[test] +fn empty_array_of_results_in_place_concatenation() { + let program = get_rir_program(indoc! {r#" + namespace Test { + @EntryPoint() + operation Main() : Result[] { + use (q0, q1) = (Qubit(), Qubit()); + mutable results = []; + set results += [MResetZ(q0)]; + set results += [MResetZ(q1)]; + results + } + } + "#}); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let array_output_recording_callable_id = CallableId(2); + assert_callable( + &program, + array_output_recording_callable_id, + &expect![[r#" + Callable: + name: __quantum__rt__array_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + let result_output_recording_callable_id = CallableId(3); + assert_callable( + &program, + result_output_recording_callable_id, + &expect![[r#" + Callable: + name: __quantum__rt__result_record_output + call_type: OutputRecording + input_type: + [0]: Result + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Call id(1), args( Qubit(1), Result(1), ) + Call id(2), args( Integer(2), Pointer, ) + Call id(3), args( Result(0), Pointer, ) + Call id(3), args( Result(1), Pointer, ) + Return"#]], + ); +} + +#[test] +fn non_empty_array_of_results_in_place_concatenation() { + let program = get_rir_program(indoc! {r#" + namespace Test { + @EntryPoint() + operation Main() : Result[] { + use (q0, q1) = (Qubit(), Qubit()); + mutable results = [MResetZ(q0)]; + set results += [MResetZ(q1)]; + results + } + } + "#}); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let array_output_recording_callable_id = CallableId(2); + assert_callable( + &program, + array_output_recording_callable_id, + &expect![[r#" + Callable: + name: __quantum__rt__array_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + let result_output_recording_callable_id = CallableId(3); + assert_callable( + &program, + result_output_recording_callable_id, + &expect![[r#" + Callable: + name: __quantum__rt__result_record_output + call_type: OutputRecording + input_type: + [0]: Result + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Call id(1), args( Qubit(1), Result(1), ) + Call id(2), args( Integer(2), Pointer, ) + Call id(3), args( Result(0), Pointer, ) + Call id(3), args( Result(1), Pointer, ) + Return"#]], + ); +} + +#[test] +fn logical_and_assign_with_lhs_classical_true_generates_boolean_instruction() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let a = MResetZ(q) == One; + mutable b = true; + set b and= a; + b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Variable(2, Boolean) = Store Bool(true) + Variable(3, Boolean) = LogicalAnd Bool(true), Variable(1, Boolean) + Variable(2, Boolean) = Store Variable(3, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) + Return"#]], + ); +} + +#[test] +fn logical_and_assign_with_lhs_classical_false_short_circuits_evaluation() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let a = MResetZ(q) == One; + mutable b = false; + set b and= a; + b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Variable(2, Boolean) = Store Bool(false) + Variable(2, Boolean) = Store Bool(false) + Call id(3), args( Variable(2, Boolean), Pointer, ) + Return"#]], + ); +} + +#[test] +fn logical_or_assign_with_lhs_classical_true_short_circuits_evaluation() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let a = MResetZ(q) == One; + mutable b = true; + set b or= a; + b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Variable(2, Boolean) = Store Bool(true) + Variable(2, Boolean) = Store Bool(true) + Call id(3), args( Variable(2, Boolean), Pointer, ) + Return"#]], + ); +} + +#[test] +fn logical_or_assign_with_lhs_classical_false_generates_boolean_instruction() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let a = MResetZ(q) == One; + mutable b = false; + set b or= a; + b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Variable(2, Boolean) = Store Bool(false) + Variable(3, Boolean) = LogicalOr Bool(false), Variable(1, Boolean) + Variable(2, Boolean) = Store Variable(3, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) + Return"#]], + ); +} + +#[test] +fn integer_assign_add_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = 0; + set i += MResetZ(q) == Zero ? 0 | 1; + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) + Branch Variable(2, Boolean), 2, 3 + Block 1:Block: + Variable(4, Integer) = Add Integer(0), Variable(3, Integer) + Variable(0, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(0, Integer), Pointer, ) + Return + Block 2:Block: + Variable(3, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(3, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_assign_sub_with_lhs_dynamic_integer_and_rhs_classical_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = MResetZ(q) == Zero ? 0 | 1; + set i -= 1; + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Sub Variable(3, Integer), Integer(1) + Variable(3, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_assign_mul_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = MResetZ(q) == Zero ? 0 | 1; + set i *= MResetZ(q) == Zero ? 1 | 0; + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Call id(1), args( Qubit(0), Result(1), ) + Variable(4, Boolean) = Call id(2), args( Result(1), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(7, Integer) = Mul Variable(3, Integer), Variable(6, Integer) + Variable(3, Integer) = Store Variable(7, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 5:Block: + Variable(6, Integer) = Store Integer(1) + Jump(4) + Block 6:Block: + Variable(6, Integer) = Store Integer(0) + Jump(4)"#]], + ); +} + +#[test] +fn integer_assign_div_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = 0; + set i /= MResetZ(q) == Zero ? 0 | 1; + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) + Branch Variable(2, Boolean), 2, 3 + Block 1:Block: + Variable(4, Integer) = Sdiv Integer(0), Variable(3, Integer) + Variable(0, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(0, Integer), Pointer, ) + Return + Block 2:Block: + Variable(3, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(3, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_assign_mod_with_lhs_dynamic_integer_and_rhs_classical_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = MResetZ(q) == Zero ? 0 | 1; + set i %= 1; + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = Srem Variable(3, Integer), Integer(1) + Variable(3, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_assign_exp_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let error = get_partial_evaluation_error(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = 0; + set i ^= MResetZ(q) == Zero ? 0 | 1; + i + } + } + "#, + }); + // When this binary operation is supported, this error should be different. + assert_error( + &error, + &expect![[ + r#"Unimplemented("exponentiation for integer operands", Span { lo: 121, hi: 156 })"# + ]], + ); +} + +#[test] +fn integer_assign_exp_with_lhs_dynamic_integer_and_rhs_classical_integer() { + let error = get_partial_evaluation_error(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = MResetZ(q) == Zero ? 0 | 1; + set i ^= 3; + i + } + } + "#, + }); + // When this binary operation is supported, this program should not yield an error. + assert_error( + &error, + &expect![[ + r#"Unimplemented("exponentiation for integer operands", Span { lo: 146, hi: 156 })"# + ]], + ); +} + +#[test] +fn integer_assign_exp_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { + let error = get_partial_evaluation_error(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = MResetZ(q) == Zero ? 0 | 1; + set i ^= MResetZ(q) == Zero ? 1 | 0; + i + } + } + "#, + }); + // When this binary operation is supported, this error should be different. + assert_error( + &error, + &expect![[ + r#"Unimplemented("exponentiation for integer operands", Span { lo: 146, hi: 181 })"# + ]], + ); +} + +#[test] +fn integer_assign_bitwise_and_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = MResetZ(q) == Zero ? 0 | 1; + set i &&&= MResetZ(q) == Zero ? 1 | 0; + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Call id(1), args( Qubit(0), Result(1), ) + Variable(4, Boolean) = Call id(2), args( Result(1), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(7, Integer) = BitwiseAnd Variable(3, Integer), Variable(6, Integer) + Variable(3, Integer) = Store Variable(7, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 5:Block: + Variable(6, Integer) = Store Integer(1) + Jump(4) + Block 6:Block: + Variable(6, Integer) = Store Integer(0) + Jump(4)"#]], + ); +} + +#[test] +fn integer_assign_bitwise_or_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = 0; + set i |||= MResetZ(q) == Zero ? 0 | 1; + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) + Branch Variable(2, Boolean), 2, 3 + Block 1:Block: + Variable(4, Integer) = BitwiseOr Integer(0), Variable(3, Integer) + Variable(0, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(0, Integer), Pointer, ) + Return + Block 2:Block: + Variable(3, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(3, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_bitwise_xor_with_lhs_dynamic_integer_and_rhs_classical_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = MResetZ(q) == Zero ? 0 | 1; + set i ^^^= 1; + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Variable(4, Integer) = BitwiseXor Variable(3, Integer), Integer(1) + Variable(3, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_assign_bitwise_left_shift_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = MResetZ(q) == Zero ? 0 | 1; + set i <<<= MResetZ(q) == Zero ? 1 | 0; + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Store Variable(2, Integer) + Call id(1), args( Qubit(0), Result(1), ) + Variable(4, Boolean) = Call id(2), args( Result(1), ) + Variable(5, Boolean) = Icmp Eq, Variable(4, Boolean), Bool(false) + Branch Variable(5, Boolean), 5, 6 + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(7, Integer) = Shl Variable(3, Integer), Variable(6, Integer) + Variable(3, Integer) = Store Variable(7, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 5:Block: + Variable(6, Integer) = Store Integer(1) + Jump(4) + Block 6:Block: + Variable(6, Integer) = Store Integer(0) + Jump(4)"#]], + ); +} + +#[test] +fn integer_assign_bitwise_right_shift_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + mutable i = 0; + set i >>>= MResetZ(q) == Zero ? 0 | 1; + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Variable(0, Integer) = Store Integer(0) + Call id(1), args( Qubit(0), Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(0), ) + Variable(2, Boolean) = Icmp Eq, Variable(1, Boolean), Bool(false) + Branch Variable(2, Boolean), 2, 3 + Block 1:Block: + Variable(4, Integer) = Ashr Integer(0), Variable(3, Integer) + Variable(0, Integer) = Store Variable(4, Integer) + Call id(3), args( Variable(0, Integer), Pointer, ) + Return + Block 2:Block: + Variable(3, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(3, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} diff --git a/compiler/qsc_partial_eval/tests/operators.rs b/compiler/qsc_partial_eval/tests/operators.rs index b8781eff1e..42cb7bec91 100644 --- a/compiler/qsc_partial_eval/tests/operators.rs +++ b/compiler/qsc_partial_eval/tests/operators.rs @@ -8,7 +8,10 @@ pub mod test_utils; use expect_test::expect; use indoc::indoc; 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, get_rir_program, +}; #[test] fn leading_positive_unary_operator_does_not_generate_rir_instruction() { @@ -300,3 +303,2065 @@ fn logical_not_unary_operator_generates_logical_not_rir_instruction() { Return"#]], ); } + +#[test] +fn comparing_measurement_results_for_equality_adds_read_result_and_comparison_instructions() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use (q0, q1) = (Qubit(), Qubit()); + let r0 = MResetZ(q0); + let r1 = MResetZ(q1); + r0 == r1 + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let bool_record_id = CallableId(3); + assert_callable( + &program, + bool_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Call id(1), args( Qubit(1), Result(1), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(1), ) + Variable(2, Boolean) = Icmp Eq, Variable(0, Boolean), Variable(1, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) + Return"#]], + ); + assert_eq!(program.num_qubits, 2); + assert_eq!(program.num_results, 2); +} + +#[test] +fn comparing_measurement_results_for_inequality_adds_read_result_and_comparison_instructions() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use (q0, q1) = (Qubit(), Qubit()); + let r0 = MResetZ(q0); + let r1 = MResetZ(q1); + r0 != r1 + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let bool_record_id = CallableId(3); + assert_callable( + &program, + bool_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Call id(1), args( Qubit(1), Result(1), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Call id(2), args( Result(1), ) + Variable(2, Boolean) = Icmp Ne, Variable(0, Boolean), Variable(1, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) + Return"#]], + ); + assert_eq!(program.num_qubits, 2); + assert_eq!(program.num_results, 2); +} + +#[test] +fn comparing_measurement_result_against_result_literal_for_equality_adds_read_result_and_comparison_instructions( +) { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let r = MResetZ(q); + r == One + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let bool_record_id = CallableId(3); + assert_callable( + &program, + bool_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Call id(3), args( Variable(1, Boolean), Pointer, ) + Return"#]], + ); + assert_eq!(program.num_qubits, 1); + assert_eq!(program.num_results, 1); +} + +#[test] +fn comparing_measurement_result_against_result_literal_for_inequality_adds_read_result_and_comparison_instructions( +) { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let r = MResetZ(q); + r != Zero + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let bool_record_id = CallableId(3); + assert_callable( + &program, + bool_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Ne, Variable(0, Boolean), Bool(false) + Call id(3), args( Variable(1, Boolean), Pointer, ) + Return"#]], + ); + assert_eq!(program.num_qubits, 1); + assert_eq!(program.num_results, 1); +} + +#[test] +fn comparing_lhs_classical_boolean_against_rhs_dynamic_boolean_for_equality() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let b = MResetZ(q) == One; + true == b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Variable(2, Boolean) = Icmp Eq, Bool(true), Variable(1, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) + Return"#]], + ); +} + +#[test] +fn comparing_lhs_classical_boolean_against_rhs_dynamic_boolean_for_inequality() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let b = MResetZ(q) == One; + true != b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Variable(2, Boolean) = Icmp Ne, Bool(true), Variable(1, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) + Return"#]], + ); +} + +#[test] +fn logical_and_with_lhs_classical_true_generates_boolean_instruction() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let b = MResetZ(q) == One; + true and b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Variable(2, Boolean) = LogicalAnd Bool(true), Variable(1, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) + Return"#]], + ); +} + +#[test] +fn logical_and_with_lhs_classical_false_short_circuits_evaluation() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let b = MResetZ(q) == One; + false and (true == b) + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Call id(3), args( Bool(false), Pointer, ) + Return"#]], + ); +} + +#[test] +fn logical_or_with_lhs_classical_true_short_circuits_evaluation() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let b = MResetZ(q) == One; + true or (false != b) + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Call id(3), args( Bool(true), Pointer, ) + Return"#]], + ); +} + +#[test] +fn logical_or_with_lhs_classical_false_generates_boolean_instruction() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let b = MResetZ(q) == One; + false or b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_block_instructions( + &program, + BlockId(0), + &expect![[r#" + Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) + Variable(2, Boolean) = LogicalOr Bool(false), Variable(1, Boolean) + Call id(3), args( Variable(2, Boolean), Pointer, ) + Return"#]], + ); +} + +#[test] +fn integer_add_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + 1 + i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Add Integer(1), Variable(2, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_sub_with_lhs_dynamic_integer_and_rhs_classical_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + i - 1 + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Sub Variable(2, Integer), Integer(1) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_mul_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let a = MResetZ(q) == Zero ? 0 | 1; + let b = MResetZ(q) == Zero ? 1 | 0; + a * b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Call id(1), args( Qubit(0), Result(1), ) + Variable(3, Boolean) = Call id(2), args( Result(1), ) + Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) + Branch Variable(4, Boolean), 5, 6 + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(6, Integer) = Mul Variable(2, Integer), Variable(5, Integer) + Call id(3), args( Variable(6, Integer), Pointer, ) + Return + Block 5:Block: + Variable(5, Integer) = Store Integer(1) + Jump(4) + Block 6:Block: + Variable(5, Integer) = Store Integer(0) + Jump(4)"#]], + ); +} + +#[test] +fn integer_div_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + 1 / i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Sdiv Integer(1), Variable(2, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_mod_with_lhs_dynamic_integer_and_rhs_classical_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + i % 1 + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Srem Variable(2, Integer), Integer(1) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_exponentiation_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let error = get_partial_evaluation_error(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + 2 ^ i + } + } + "#, + }); + // When this binary operation is supported, this error should be different. + assert_error( + &error, + &expect![[ + r#"Unimplemented("exponentiation for integer operands", Span { lo: 142, hi: 147 })"# + ]], + ); +} + +#[test] +fn integer_exponentiation_with_lhs_dynamic_integer_and_rhs_classical_integer() { + let error = get_partial_evaluation_error(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + i ^ 3 + } + } + "#, + }); + // When this binary operation is supported, this program should not yield an error. + assert_error( + &error, + &expect![[ + r#"Unimplemented("exponentiation for integer operands", Span { lo: 142, hi: 147 })"# + ]], + ); +} + +#[test] +fn integer_exponentiation_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { + let error = get_partial_evaluation_error(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let a = MResetZ(q) == Zero ? 0 | 1; + let b = MResetZ(q) == Zero ? 1 | 0; + a ^ b + } + } + "#, + }); + // When this binary operation is supported, this error should be different. + assert_error( + &error, + &expect![[ + r#"Unimplemented("exponentiation for integer operands", Span { lo: 186, hi: 191 })"# + ]], + ); +} + +#[test] +fn integer_bitwise_and_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let a = MResetZ(q) == Zero ? 0 | 1; + let b = MResetZ(q) == Zero ? 1 | 0; + a &&& b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Call id(1), args( Qubit(0), Result(1), ) + Variable(3, Boolean) = Call id(2), args( Result(1), ) + Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) + Branch Variable(4, Boolean), 5, 6 + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(6, Integer) = BitwiseAnd Variable(2, Integer), Variable(5, Integer) + Call id(3), args( Variable(6, Integer), Pointer, ) + Return + Block 5:Block: + Variable(5, Integer) = Store Integer(1) + Jump(4) + Block 6:Block: + Variable(5, Integer) = Store Integer(0) + Jump(4)"#]], + ); +} + +#[test] +fn integer_bitwise_or_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + 1 ||| i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = BitwiseOr Integer(1), Variable(2, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_bitwise_xor_with_lhs_dynamic_integer_and_rhs_classical_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + i ^^^ 1 + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = BitwiseXor Variable(2, Integer), Integer(1) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_bitwise_left_shif_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let a = MResetZ(q) == Zero ? 0 | 1; + let b = MResetZ(q) == Zero ? 1 | 0; + a <<< b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Call id(1), args( Qubit(0), Result(1), ) + Variable(3, Boolean) = Call id(2), args( Result(1), ) + Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) + Branch Variable(4, Boolean), 5, 6 + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(6, Integer) = Shl Variable(2, Integer), Variable(5, Integer) + Call id(3), args( Variable(6, Integer), Pointer, ) + Return + Block 5:Block: + Variable(5, Integer) = Store Integer(1) + Jump(4) + Block 6:Block: + Variable(5, Integer) = Store Integer(0) + Jump(4)"#]], + ); +} + +#[test] +fn integer_bitwise_right_shift_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Int { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + 1 >>> i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__integer_record_output + call_type: OutputRecording + input_type: + [0]: Integer + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Integer) = Ashr Integer(1), Variable(2, Integer) + Call id(3), args( Variable(3, Integer), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_equality_comparison_with_lhs_dynamic_integer_and_rhs_classical_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + i == 1 + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Boolean) = Icmp Eq, Variable(2, Integer), Integer(1) + Call id(3), args( Variable(3, Boolean), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_inequality_comparison_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let a = MResetZ(q) == Zero ? 0 | 1; + let b = MResetZ(q) == Zero ? 1 | 0; + a != b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Call id(1), args( Qubit(0), Result(1), ) + Variable(3, Boolean) = Call id(2), args( Result(1), ) + Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) + Branch Variable(4, Boolean), 5, 6 + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(6, Boolean) = Icmp Ne, Variable(2, Integer), Variable(5, Integer) + Call id(3), args( Variable(6, Boolean), Pointer, ) + Return + Block 5:Block: + Variable(5, Integer) = Store Integer(1) + Jump(4) + Block 6:Block: + Variable(5, Integer) = Store Integer(0) + Jump(4)"#]], + ); +} + +#[test] +fn integer_greater_than_comparison_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + 1 > i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Boolean) = Icmp Sgt, Integer(1), Variable(2, Integer) + Call id(3), args( Variable(3, Boolean), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_greater_or_equal_than_comparison_with_lhs_dynamic_integer_and_rhs_classical_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + i >= 1 + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Boolean) = Icmp Sge, Variable(2, Integer), Integer(1) + Call id(3), args( Variable(3, Boolean), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} + +#[test] +fn integer_less_than_comparison_with_lhs_dynamic_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let a = MResetZ(q) == Zero ? 0 | 1; + let b = MResetZ(q) == Zero ? 1 | 0; + a < b + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Call id(1), args( Qubit(0), Result(1), ) + Variable(3, Boolean) = Call id(2), args( Result(1), ) + Variable(4, Boolean) = Icmp Eq, Variable(3, Boolean), Bool(false) + Branch Variable(4, Boolean), 5, 6 + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1) + Block 4:Block: + Variable(6, Boolean) = Icmp Slt, Variable(2, Integer), Variable(5, Integer) + Call id(3), args( Variable(6, Boolean), Pointer, ) + Return + Block 5:Block: + Variable(5, Integer) = Store Integer(1) + Jump(4) + Block 6:Block: + Variable(5, Integer) = Store Integer(0) + Jump(4)"#]], + ); +} + +#[test] +fn integer_less_or_equal_than_comparison_with_lhs_classical_integer_and_rhs_dynamic_integer() { + let program = get_rir_program(indoc! { + r#" + namespace Test { + @EntryPoint() + operation Main() : Bool { + use q = Qubit(); + let i = MResetZ(q) == Zero ? 0 | 1; + 1 <= i + } + } + "#, + }); + let measurement_callable_id = CallableId(1); + assert_callable( + &program, + measurement_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__mresetz__body + call_type: Measurement + input_type: + [0]: Qubit + [1]: Result + output_type: + body: "#]], + ); + let readout_callable_id = CallableId(2); + assert_callable( + &program, + readout_callable_id, + &expect![[r#" + Callable: + name: __quantum__qis__read_result__body + call_type: Readout + input_type: + [0]: Result + output_type: Boolean + body: "#]], + ); + let output_record_id = CallableId(3); + assert_callable( + &program, + output_record_id, + &expect![[r#" + Callable: + name: __quantum__rt__bool_record_output + call_type: OutputRecording + input_type: + [0]: Boolean + [1]: Pointer + output_type: + body: "#]], + ); + assert_blocks( + &program, + &expect![[r#" + Blocks: + Block 0:Block: + Call id(1), args( Qubit(0), Result(0), ) + Variable(0, Boolean) = Call id(2), args( Result(0), ) + Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(false) + Branch Variable(1, Boolean), 2, 3 + Block 1:Block: + Variable(3, Boolean) = Icmp Sle, Integer(1), Variable(2, Integer) + Call id(3), args( Variable(3, Boolean), Pointer, ) + Return + Block 2:Block: + Variable(2, Integer) = Store Integer(0) + Jump(1) + Block 3:Block: + Variable(2, Integer) = Store Integer(1) + Jump(1)"#]], + ); +} diff --git a/compiler/qsc_partial_eval/tests/results.rs b/compiler/qsc_partial_eval/tests/results.rs index 8c15c6e7ad..97110b2b0b 100644 --- a/compiler/qsc_partial_eval/tests/results.rs +++ b/compiler/qsc_partial_eval/tests/results.rs @@ -340,291 +340,3 @@ fn result_ids_are_correct_for_measuring_multiple_qubits() { assert_eq!(program.num_qubits, 3); assert_eq!(program.num_results, 3); } - -#[test] -fn comparing_measurement_results_for_equality_adds_read_result_and_comparison_instructions() { - let program = get_rir_program(indoc! { - r#" - namespace Test { - @EntryPoint() - operation Main() : Bool { - use (q0, q1) = (Qubit(), Qubit()); - let r0 = QIR.Intrinsic.__quantum__qis__m__body(q0); - let r1 = QIR.Intrinsic.__quantum__qis__m__body(q1); - r0 == r1 - } - } - "#, - }); - let measurement_callable_id = CallableId(1); - assert_callable( - &program, - measurement_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__mz__body - call_type: Measurement - input_type: - [0]: Qubit - [1]: Result - output_type: - body: "#]], - ); - let readout_callable_id = CallableId(2); - assert_callable( - &program, - readout_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__read_result__body - call_type: Readout - input_type: - [0]: Result - output_type: Boolean - body: "#]], - ); - let bool_record_id = CallableId(3); - assert_callable( - &program, - bool_record_id, - &expect![[r#" - Callable: - name: __quantum__rt__bool_record_output - call_type: OutputRecording - input_type: - [0]: Boolean - [1]: Pointer - output_type: - body: "#]], - ); - assert_block_instructions( - &program, - BlockId(0), - &expect![[r#" - Block: - Call id(1), args( Qubit(0), Result(0), ) - Call id(1), args( Qubit(1), Result(1), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Call id(2), args( Result(1), ) - Variable(2, Boolean) = Icmp Eq, Variable(0, Boolean), Variable(1, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) - Return"#]], - ); - assert_eq!(program.num_qubits, 2); - assert_eq!(program.num_results, 2); -} - -#[test] -fn comparing_measurement_results_for_inequality_adds_read_result_and_comparison_instructions() { - let program = get_rir_program(indoc! { - r#" - namespace Test { - @EntryPoint() - operation Main() : Bool { - use (q0, q1) = (Qubit(), Qubit()); - let r0 = QIR.Intrinsic.__quantum__qis__m__body(q0); - let r1 = QIR.Intrinsic.__quantum__qis__m__body(q1); - r0 != r1 - } - } - "#, - }); - let measurement_callable_id = CallableId(1); - assert_callable( - &program, - measurement_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__mz__body - call_type: Measurement - input_type: - [0]: Qubit - [1]: Result - output_type: - body: "#]], - ); - let readout_callable_id = CallableId(2); - assert_callable( - &program, - readout_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__read_result__body - call_type: Readout - input_type: - [0]: Result - output_type: Boolean - body: "#]], - ); - let bool_record_id = CallableId(3); - assert_callable( - &program, - bool_record_id, - &expect![[r#" - Callable: - name: __quantum__rt__bool_record_output - call_type: OutputRecording - input_type: - [0]: Boolean - [1]: Pointer - output_type: - body: "#]], - ); - assert_block_instructions( - &program, - BlockId(0), - &expect![[r#" - Block: - Call id(1), args( Qubit(0), Result(0), ) - Call id(1), args( Qubit(1), Result(1), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Call id(2), args( Result(1), ) - Variable(2, Boolean) = Icmp Ne, Variable(0, Boolean), Variable(1, Boolean) - Call id(3), args( Variable(2, Boolean), Pointer, ) - Return"#]], - ); - assert_eq!(program.num_qubits, 2); - assert_eq!(program.num_results, 2); -} - -#[test] -fn comparing_measurement_result_against_result_literal_for_equality_adds_read_result_and_comparison_instructions( -) { - let program = get_rir_program(indoc! { - r#" - namespace Test { - @EntryPoint() - operation Main() : Bool { - use q = Qubit(); - let r = QIR.Intrinsic.__quantum__qis__m__body(q); - r == One - } - } - "#, - }); - let measurement_callable_id = CallableId(1); - assert_callable( - &program, - measurement_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__mz__body - call_type: Measurement - input_type: - [0]: Qubit - [1]: Result - output_type: - body: "#]], - ); - let readout_callable_id = CallableId(2); - assert_callable( - &program, - readout_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__read_result__body - call_type: Readout - input_type: - [0]: Result - output_type: Boolean - body: "#]], - ); - let bool_record_id = CallableId(3); - assert_callable( - &program, - bool_record_id, - &expect![[r#" - Callable: - name: __quantum__rt__bool_record_output - call_type: OutputRecording - input_type: - [0]: Boolean - [1]: Pointer - output_type: - body: "#]], - ); - assert_block_instructions( - &program, - BlockId(0), - &expect![[r#" - Block: - Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Eq, Variable(0, Boolean), Bool(true) - Call id(3), args( Variable(1, Boolean), Pointer, ) - Return"#]], - ); - assert_eq!(program.num_qubits, 1); - assert_eq!(program.num_results, 1); -} - -#[test] -fn comparing_measurement_result_against_result_literal_for_inequality_adds_read_result_and_comparison_instructions( -) { - let program = get_rir_program(indoc! { - r#" - namespace Test { - @EntryPoint() - operation Main() : Bool { - use q = Qubit(); - let r = QIR.Intrinsic.__quantum__qis__m__body(q); - r != Zero - } - } - "#, - }); - let measurement_callable_id = CallableId(1); - assert_callable( - &program, - measurement_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__mz__body - call_type: Measurement - input_type: - [0]: Qubit - [1]: Result - output_type: - body: "#]], - ); - let readout_callable_id = CallableId(2); - assert_callable( - &program, - readout_callable_id, - &expect![[r#" - Callable: - name: __quantum__qis__read_result__body - call_type: Readout - input_type: - [0]: Result - output_type: Boolean - body: "#]], - ); - let bool_record_id = CallableId(3); - assert_callable( - &program, - bool_record_id, - &expect![[r#" - Callable: - name: __quantum__rt__bool_record_output - call_type: OutputRecording - input_type: - [0]: Boolean - [1]: Pointer - output_type: - body: "#]], - ); - assert_block_instructions( - &program, - BlockId(0), - &expect![[r#" - Block: - Call id(1), args( Qubit(0), Result(0), ) - Variable(0, Boolean) = Call id(2), args( Result(0), ) - Variable(1, Boolean) = Icmp Ne, Variable(0, Boolean), Bool(false) - Call id(3), args( Variable(1, Boolean), Pointer, ) - Return"#]], - ); - assert_eq!(program.num_qubits, 1); - assert_eq!(program.num_results, 1); -} diff --git a/compiler/qsc_partial_eval/tests/returns.rs b/compiler/qsc_partial_eval/tests/returns.rs index a03ddc7716..4199611864 100644 --- a/compiler/qsc_partial_eval/tests/returns.rs +++ b/compiler/qsc_partial_eval/tests/returns.rs @@ -930,10 +930,9 @@ fn explicit_return_embedded_in_assign_op_expr_yields_error() { } } "#}); - // The type of error will change once this kind of hybrid expression is supported. assert_error( &error, - &expect![[r#"Unimplemented("int binary operation", Span { lo: 166, hi: 167 })"#]], + &expect![[r#"Unexpected("embedded return in RHS expression", Span { lo: 171, hi: 183 })"#]], ); } @@ -951,9 +950,7 @@ fn explicit_return_embedded_in_bin_op_expr_yields_error() { "#}); assert_error( &error, - &expect![[ - r#"Unexpected("embedded return in binary operation", Span { lo: 151, hi: 163 })"# - ]], + &expect![[r#"Unexpected("embedded return in RHS expression", Span { lo: 151, hi: 163 })"#]], ); } diff --git a/compiler/qsc_rca/src/core.rs b/compiler/qsc_rca/src/core.rs index 77fe671081..5da592bf62 100644 --- a/compiler/qsc_rca/src/core.rs +++ b/compiler/qsc_rca/src/core.rs @@ -1404,17 +1404,22 @@ impl<'a> Analyzer<'a> { panic!("expected a local variable"); }; - // The updated compute kind is based on the compute kind of the value expression. + // The updated compute kind is the aggregation of the compute kind of the local variable and the + // assigned value. + // Start by initializing the updated compute kind with the compute kind of the local variable. let application_instance = self.get_current_application_instance(); - let value_expr_compute_kind = - *application_instance.get_expr_compute_kind(value_expr_id); - - // Since the local variable compute kind is what will be updated, the value kind must match the local - // variable's type. In some cases, there might be some loss of granularity on the value kind (e.g. - // assigning an array to a UDT variable field since we do not track individual UDT fields). let local_var_compute_kind = application_instance .locals_map .get_local_compute_kind(*local_var_id); + let mut updated_compute_kind = local_var_compute_kind.compute_kind; + + // Since the local variable compute kind is what will be updated, the value kind must match the local + // variable's type. That is why before aggregating the compute kind of the assigned value we need to get + // a default value kind of the matching type. + // In some cases, there might be some loss of granularity on the value kind (e.g. assigning an array to + // a UDT variable field since we do not track individual UDT fields). + let value_expr_compute_kind = + *application_instance.get_expr_compute_kind(value_expr_id); let mut value_kind = ValueKind::new_static_from_type(&local_var_compute_kind.local.ty); if let ComputeKind::Quantum(value_expr_quantum_properties) = value_expr_compute_kind @@ -1423,8 +1428,6 @@ impl<'a> Analyzer<'a> { .value_kind .project_onto_variant(&mut value_kind); } - - let mut updated_compute_kind = ComputeKind::Classical; updated_compute_kind = updated_compute_kind .aggregate_runtime_features(value_expr_compute_kind, value_kind); diff --git a/compiler/qsc_rca/tests/assigns.rs b/compiler/qsc_rca/tests/assigns.rs index 98f4bdf34d..cc02a2542d 100644 --- a/compiler/qsc_rca/tests/assigns.rs +++ b/compiler/qsc_rca/tests/assigns.rs @@ -276,3 +276,101 @@ fn check_rca_for_assign_dynamic_call_result_to_tuple_of_vars() { dynamic_param_applications: "#]], ); } + +#[test] +fn check_rca_for_mutable_classical_integer_assigned_updated_with_classical_integer() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Measurement; + use register = Qubit[8]; + let results = MeasureEachZ(register); + mutable i = 0; + set i += 1; + i"#, + ); + 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_mutable_classical_integer_assigned_updated_with_dynamic_integer() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Measurement; + use register = Qubit[8]; + let results = MeasureEachZ(register); + mutable i = 0; + set i += ResultArrayAsInt(results); + i"#, + ); + 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 | UseOfDynamicInt) + value_kind: Element(Dynamic) + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_mutable_dynamic_integer_assigned_updated_with_classical_integer() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Measurement; + use register = Qubit[8]; + let results = MeasureEachZ(register); + mutable i = ResultArrayAsInt(results); + set i += 1; + i"#, + ); + 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 | UseOfDynamicInt) + value_kind: Element(Dynamic) + dynamic_param_applications: "#]], + ); +} + +#[test] +fn check_rca_for_mutable_dynamic_integer_assigned_updated_with_dynamic_integer() { + let mut compilation_context = CompilationContext::default(); + compilation_context.update( + r#" + open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Measurement; + use register = Qubit[8]; + let results = MeasureEachZ(register); + mutable i = ResultArrayAsInt(results); + set i += ResultArrayAsInt(results); + i"#, + ); + 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 | UseOfDynamicInt) + value_kind: Element(Dynamic) + dynamic_param_applications: "#]], + ); +} diff --git a/compiler/qsc_rir/src/rir.rs b/compiler/qsc_rir/src/rir.rs index 6415350d2a..e283e73d6e 100644 --- a/compiler/qsc_rir/src/rir.rs +++ b/compiler/qsc_rir/src/rir.rs @@ -456,6 +456,32 @@ impl Display for Variable { } } +impl Variable { + #[must_use] + pub fn new_boolean(id: VariableId) -> Self { + Self { + variable_id: id, + ty: Ty::Boolean, + } + } + + #[must_use] + pub fn new_integer(id: VariableId) -> Self { + Self { + variable_id: id, + ty: Ty::Integer, + } + } + + #[must_use] + pub fn new_double(id: VariableId) -> Self { + Self { + variable_id: id, + ty: Ty::Double, + } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Ty { Qubit,