microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
44658e234ef2cfa1fd31c0231ec658df5d48b48f

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/compiler/qsc/src/codegen/tests.rs

5478lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![allow(clippy::too_many_lines)]
5
6use std::sync::Arc;
7
8use std::rc::Rc;
9
10use expect_test::expect;
11use miette::Report;
12use qsc_data_structures::{
13 functors::FunctorApp, language_features::LanguageFeatures, source::SourceMap,
14 target::TargetCapabilityFlags,
15};
16use qsc_eval::val::Value;
17use qsc_frontend::compile::parse_all;
18use qsc_hir::hir::{ItemKind, PackageId};
19use rustc_hash::FxHashMap;
20
21use crate::codegen::qir::{
22 get_qir, get_qir_from_ast, get_rir, prepare_codegen_fir_from_callable_args,
23};
24
25fn format_interpret_errors(errors: Vec<crate::interpret::Error>) -> String {
26 errors
27 .into_iter()
28 .map(|error| format!("{:?}", Report::new(error)))
29 .collect::<Vec<_>>()
30 .join("\n\n")
31}
32
33fn source_map_from_source(source: &str) -> SourceMap {
34 SourceMap::new([("test.qs".into(), source.into())], None)
35}
36
37fn parse_source_to_ast(source: &str) -> (qsc_ast::ast::Package, SourceMap) {
38 let sources = source_map_from_source(source);
39 let language_features = LanguageFeatures::default();
40 let (ast_package, errors) = parse_all(&sources, language_features);
41
42 if errors.is_empty() {
43 (ast_package, sources)
44 } else {
45 let diagnostics = errors
46 .into_iter()
47 .map(|error| format!("{:?}", Report::new(error)))
48 .collect::<Vec<_>>()
49 .join("\n\n");
50
51 panic!("Failed to parse AST test source:\n{diagnostics}");
52 }
53}
54
55fn compile_source_to_qir(source: &str, capabilities: TargetCapabilityFlags) -> String {
56 match compile_source_to_qir_result(source, capabilities) {
57 Ok(qir) => qir,
58 Err(errors) => panic!(
59 "Failed to generate QIR for capabilities {capabilities:?}:\n{}",
60 format_interpret_errors(errors)
61 ),
62 }
63}
64
65fn compile_source_to_qir_result(
66 source: &str,
67 capabilities: TargetCapabilityFlags,
68) -> Result<String, Vec<crate::interpret::Error>> {
69 let sources = source_map_from_source(source);
70 let language_features = LanguageFeatures::default();
71
72 let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
73 get_qir(
74 sources,
75 language_features,
76 capabilities,
77 store,
78 &[(std_id, None)],
79 )
80}
81
82fn compile_source_to_qir_from_ast(source: &str, capabilities: TargetCapabilityFlags) -> String {
83 match compile_source_to_qir_from_ast_result(source, capabilities) {
84 Ok(qir) => qir,
85 Err(errors) => panic!(
86 "Failed to generate QIR from AST for capabilities {capabilities:?}:\n{}",
87 format_interpret_errors(errors)
88 ),
89 }
90}
91
92fn compile_source_to_qir_from_ast_result(
93 source: &str,
94 capabilities: TargetCapabilityFlags,
95) -> Result<String, Vec<crate::interpret::Error>> {
96 let (ast_package, sources) = parse_source_to_ast(source);
97 let (std_id, mut store) = crate::compile::package_store_with_stdlib(capabilities);
98 let dependencies: Vec<(PackageId, Option<Arc<str>>)> =
99 vec![(PackageId::CORE, None), (std_id, None)];
100
101 get_qir_from_ast(
102 &mut store,
103 &dependencies,
104 ast_package,
105 sources,
106 capabilities,
107 )
108}
109
110fn compile_source_to_rir(source: &str, capabilities: TargetCapabilityFlags) -> Vec<String> {
111 match compile_source_to_rir_result(source, capabilities) {
112 Ok(rir) => rir,
113 Err(errors) => panic!(
114 "Failed to generate RIR for capabilities {capabilities:?}:\n{}",
115 format_interpret_errors(errors)
116 ),
117 }
118}
119
120fn compile_source_to_rir_result(
121 source: &str,
122 capabilities: TargetCapabilityFlags,
123) -> Result<Vec<String>, Vec<crate::interpret::Error>> {
124 let sources = source_map_from_source(source);
125 let language_features = LanguageFeatures::default();
126
127 let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
128 get_rir(
129 sources,
130 language_features,
131 capabilities,
132 store,
133 &[(std_id, None)],
134 )
135}
136
137#[test]
138fn code_with_errors_returns_errors() {
139 let source = "namespace Test {
140 @EntryPoint()
141 operation Main() : Unit {
142 use q = Qubit()
143 let pi_over_two = 4.0 / 2.0;
144 }
145 }";
146 let sources = SourceMap::new([("test.qs".into(), source.into())], None);
147 let language_features = LanguageFeatures::default();
148 let capabilities = TargetCapabilityFlags::empty();
149 let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
150
151 expect![[r#"
152 Err(
153 [
154 Compile(
155 WithSource {
156 sources: [
157 Source {
158 name: "test.qs",
159 contents: "namespace Test {\n @EntryPoint()\n operation Main() : Unit {\n use q = Qubit()\n let pi_over_two = 4.0 / 2.0;\n }\n }",
160 offset: 0,
161 },
162 ],
163 error: Frontend(
164 Error(
165 Parse(
166 Error(
167 Token(
168 Semi,
169 Keyword(
170 Let,
171 ),
172 Span {
173 lo: 129,
174 hi: 132,
175 },
176 ),
177 ),
178 ),
179 ),
180 ),
181 },
182 ),
183 ],
184 )
185 "#]]
186 .assert_debug_eq(&get_qir(sources, language_features, capabilities, store, &[(std_id, None)]));
187}
188
189#[test]
190fn unsupported_profile_patterns_return_pass_errors() {
191 let res = compile_source_to_qir_result(
192 indoc::indoc! {r#"
193 namespace Test {
194 @EntryPoint()
195 operation Main() : Int {
196 use q = Qubit();
197 mutable x = 1;
198 if MResetZ(q) == One {
199 set x = 2;
200 }
201 x
202 }
203 }
204 "#},
205 TargetCapabilityFlags::Adaptive,
206 );
207
208 let errors = res.expect_err("expected capability error");
209 assert!(!errors.is_empty(), "expected at least one error");
210 assert!(
211 errors
212 .iter()
213 .all(|error| matches!(error, crate::interpret::Error::Pass(_))),
214 "expected pass-derived codegen readiness errors, got {errors:?}"
215 );
216 assert!(
217 errors.iter().any(|error| error
218 .to_string()
219 .contains("cannot use a dynamic integer value")),
220 "expected a dynamic integer capability diagnostic, got {errors:?}"
221 );
222}
223
224#[test]
225fn qir_generation_succeeds_for_struct_copy_update() {
226 let source = r#"
227 namespace Test {
228 @EntryPoint()
229 operation Main() : Unit {
230 struct Point3d { X : Double, Y : Double, Z : Double }
231
232 let point = new Point3d { X = 1.0, Y = 2.0, Z = 3.0 };
233 let point2 = new Point3d { ...point, Z = 4.0 };
234 let x : Double = point2.X;
235 }
236 }
237 "#;
238
239 let qir = compile_source_to_qir(source, TargetCapabilityFlags::empty());
240 expect![[r#"
241 %Result = type opaque
242 %Qubit = type opaque
243
244 @0 = internal constant [4 x i8] c"0_t\00"
245
246 define i64 @ENTRYPOINT__main() #0 {
247 block_0:
248 call void @__quantum__rt__initialize(i8* null)
249 call void @__quantum__rt__tuple_record_output(i64 0, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
250 ret i64 0
251 }
252
253 declare void @__quantum__rt__initialize(i8*)
254
255 declare void @__quantum__rt__tuple_record_output(i64, i8*)
256
257 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="0" }
258 attributes #1 = { "irreversible" }
259
260 ; module flags
261
262 !llvm.module.flags = !{!0, !1, !2, !3}
263
264 !0 = !{i32 1, !"qir_major_version", i32 1}
265 !1 = !{i32 7, !"qir_minor_version", i32 0}
266 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
267 !3 = !{i32 1, !"dynamic_result_management", i1 false}
268 "#]]
269 .assert_eq(&qir);
270}
271
272#[test]
273fn deutsch_jozsa_sample_shape_generates_qir() {
274 let source = indoc::indoc! {r#"
275 namespace Test {
276 import Std.Diagnostics.*;
277 import Std.Math.*;
278 import Std.Measurement.*;
279
280 @EntryPoint()
281 operation Main() : Bool[] {
282 let functionsToTest = [
283 SimpleConstantBoolF,
284 SimpleBalancedBoolF,
285 ConstantBoolF,
286 BalancedBoolF
287 ];
288
289 mutable results = [];
290 for fn in functionsToTest {
291 let isConstant = DeutschJozsa(fn, 3);
292 set results += [isConstant];
293 }
294
295 return results;
296 }
297
298 operation DeutschJozsa(Uf : ((Qubit[], Qubit) => Unit), n : Int) : Bool {
299 use queryRegister = Qubit[n];
300 use target = Qubit();
301 X(target);
302 H(target);
303 within {
304 for q in queryRegister {
305 H(q);
306 }
307 } apply {
308 Uf(queryRegister, target);
309 }
310
311 mutable result = true;
312 for q in queryRegister {
313 if MResetZ(q) == One {
314 set result = false;
315 }
316 }
317
318 Reset(target);
319 return result;
320 }
321
322 operation SimpleConstantBoolF(args : Qubit[], target : Qubit) : Unit {
323 X(target);
324 }
325
326 operation SimpleBalancedBoolF(args : Qubit[], target : Qubit) : Unit {
327 CX(args[0], target);
328 }
329
330 operation ConstantBoolF(args : Qubit[], target : Qubit) : Unit {
331 for i in 0..(2^Length(args)) - 1 {
332 ApplyControlledOnInt(i, X, args, target);
333 }
334 }
335
336 operation BalancedBoolF(args : Qubit[], target : Qubit) : Unit {
337 for i in 0..2..(2^Length(args)) - 1 {
338 ApplyControlledOnInt(i, X, args, target);
339 }
340 }
341 }
342 "#};
343
344 let qir = compile_source_to_qir(
345 source,
346 TargetCapabilityFlags::Adaptive
347 | TargetCapabilityFlags::IntegerComputations
348 | TargetCapabilityFlags::FloatingPointComputations,
349 );
350
351 expect![[r#"
352 %Result = type opaque
353 %Qubit = type opaque
354
355 @0 = internal constant [4 x i8] c"0_a\00"
356 @1 = internal constant [6 x i8] c"1_a0b\00"
357 @2 = internal constant [6 x i8] c"2_a1b\00"
358 @3 = internal constant [6 x i8] c"3_a2b\00"
359 @4 = internal constant [6 x i8] c"4_a3b\00"
360
361 define i64 @ENTRYPOINT__main() #0 {
362 block_0:
363 call void @__quantum__rt__initialize(i8* null)
364 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 3 to %Qubit*))
365 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*))
366 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
367 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
368 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
369 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 3 to %Qubit*))
370 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
371 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
372 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
373 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
374 %var_6 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
375 br i1 %var_6, label %block_1, label %block_2
376 block_1:
377 br label %block_2
378 block_2:
379 %var_139 = phi i1 [true, %block_0], [false, %block_1]
380 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
381 %var_8 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 1 to %Result*))
382 br i1 %var_8, label %block_3, label %block_4
383 block_3:
384 br label %block_4
385 block_4:
386 %var_140 = phi i1 [%var_139, %block_2], [false, %block_3]
387 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*))
388 %var_10 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 2 to %Result*))
389 br i1 %var_10, label %block_5, label %block_6
390 block_5:
391 br label %block_6
392 block_6:
393 %var_141 = phi i1 [%var_140, %block_4], [false, %block_5]
394 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 3 to %Qubit*))
395 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 3 to %Qubit*))
396 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*))
397 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
398 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
399 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
400 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
401 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
402 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
403 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
404 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 3 to %Result*))
405 %var_19 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 3 to %Result*))
406 br i1 %var_19, label %block_7, label %block_8
407 block_7:
408 br label %block_8
409 block_8:
410 %var_142 = phi i1 [true, %block_6], [false, %block_7]
411 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 4 to %Result*))
412 %var_21 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 4 to %Result*))
413 br i1 %var_21, label %block_9, label %block_10
414 block_9:
415 br label %block_10
416 block_10:
417 %var_143 = phi i1 [%var_142, %block_8], [false, %block_9]
418 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 5 to %Result*))
419 %var_23 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 5 to %Result*))
420 br i1 %var_23, label %block_11, label %block_12
421 block_11:
422 br label %block_12
423 block_12:
424 %var_144 = phi i1 [%var_143, %block_10], [false, %block_11]
425 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 3 to %Qubit*))
426 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 3 to %Qubit*))
427 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*))
428 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
429 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
430 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
431 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
432 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
433 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
434 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
435 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
436 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
437 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
438 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
439 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
440 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
441 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
442 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
443 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
444 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
445 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
446 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
447 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
448 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
449 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
450 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
451 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
452 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
453 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
454 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
455 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
456 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
457 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
458 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
459 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
460 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
461 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
462 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
463 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
464 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
465 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
466 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
467 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
468 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
469 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
470 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
471 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
472 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
473 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
474 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
475 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
476 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
477 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
478 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
479 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
480 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
481 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
482 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 6 to %Result*))
483 %var_89 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 6 to %Result*))
484 br i1 %var_89, label %block_13, label %block_14
485 block_13:
486 br label %block_14
487 block_14:
488 %var_145 = phi i1 [true, %block_12], [false, %block_13]
489 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 7 to %Result*))
490 %var_91 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 7 to %Result*))
491 br i1 %var_91, label %block_15, label %block_16
492 block_15:
493 br label %block_16
494 block_16:
495 %var_146 = phi i1 [%var_145, %block_14], [false, %block_15]
496 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 8 to %Result*))
497 %var_93 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 8 to %Result*))
498 br i1 %var_93, label %block_17, label %block_18
499 block_17:
500 br label %block_18
501 block_18:
502 %var_147 = phi i1 [%var_146, %block_16], [false, %block_17]
503 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 3 to %Qubit*))
504 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 3 to %Qubit*))
505 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*))
506 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
507 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
508 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
509 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
510 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
511 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
512 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
513 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
514 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
515 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
516 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
517 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
518 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
519 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
520 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
521 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
522 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
523 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
524 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
525 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
526 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
527 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
528 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
529 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
530 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
531 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
532 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
533 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
534 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*), %Qubit* inttoptr (i64 3 to %Qubit*))
535 call void @__quantum__qis__ccx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 4 to %Qubit*))
536 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
537 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
538 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
539 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
540 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 9 to %Result*))
541 %var_131 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 9 to %Result*))
542 br i1 %var_131, label %block_19, label %block_20
543 block_19:
544 br label %block_20
545 block_20:
546 %var_148 = phi i1 [true, %block_18], [false, %block_19]
547 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 10 to %Result*))
548 %var_133 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 10 to %Result*))
549 br i1 %var_133, label %block_21, label %block_22
550 block_21:
551 br label %block_22
552 block_22:
553 %var_149 = phi i1 [%var_148, %block_20], [false, %block_21]
554 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 11 to %Result*))
555 %var_135 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 11 to %Result*))
556 br i1 %var_135, label %block_23, label %block_24
557 block_23:
558 br label %block_24
559 block_24:
560 %var_150 = phi i1 [%var_149, %block_22], [false, %block_23]
561 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 3 to %Qubit*))
562 call void @__quantum__rt__array_record_output(i64 4, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
563 call void @__quantum__rt__bool_record_output(i1 %var_141, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
564 call void @__quantum__rt__bool_record_output(i1 %var_144, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
565 call void @__quantum__rt__bool_record_output(i1 %var_147, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @3, i64 0, i64 0))
566 call void @__quantum__rt__bool_record_output(i1 %var_150, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @4, i64 0, i64 0))
567 ret i64 0
568 }
569
570 declare void @__quantum__rt__initialize(i8*)
571
572 declare void @__quantum__qis__x__body(%Qubit*)
573
574 declare void @__quantum__qis__h__body(%Qubit*)
575
576 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
577
578 declare i1 @__quantum__rt__read_result(%Result*)
579
580 declare void @__quantum__qis__reset__body(%Qubit*) #1
581
582 declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
583
584 declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*)
585
586 declare void @__quantum__rt__array_record_output(i64, i8*)
587
588 declare void @__quantum__rt__bool_record_output(i1, i8*)
589
590 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="5" "required_num_results"="12" }
591 attributes #1 = { "irreversible" }
592
593 ; module flags
594
595 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
596
597 !0 = !{i32 1, !"qir_major_version", i32 1}
598 !1 = !{i32 7, !"qir_minor_version", i32 0}
599 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
600 !3 = !{i32 1, !"dynamic_result_management", i1 false}
601 !4 = !{i32 5, !"int_computations", !{!"i64"}}
602 !5 = !{i32 5, !"float_computations", !{!"double"}}
603 "#]]
604 .assert_eq(&qir);
605}
606
607#[test]
608fn simple_phase_estimation_sample_shape_generates_qir() {
609 let source = indoc::indoc! {r#"
610 namespace Test {
611 operation Main() : Result[] {
612 use state = Qubit();
613 use phase = Qubit[3];
614
615 X(state);
616
617 let oracle = ApplyOperationPowerCA(_, qs => U(qs[0]), _);
618 ApplyQPE(oracle, [state], phase);
619
620 let results = MeasureEachZ(phase);
621
622 Reset(state);
623 ResetAll(phase);
624
625 Std.Arrays.Reversed(results)
626 }
627
628 operation U(q : Qubit) : Unit is Ctl + Adj {
629 Rz(Std.Math.PI() / 3.0, q);
630 }
631 }
632 "#};
633
634 let qir = compile_source_to_qir(
635 source,
636 TargetCapabilityFlags::Adaptive
637 | TargetCapabilityFlags::IntegerComputations
638 | TargetCapabilityFlags::FloatingPointComputations,
639 );
640
641 expect![[r#"
642 %Result = type opaque
643 %Qubit = type opaque
644
645 @0 = internal constant [4 x i8] c"0_a\00"
646 @1 = internal constant [6 x i8] c"1_a0r\00"
647 @2 = internal constant [6 x i8] c"2_a1r\00"
648 @3 = internal constant [6 x i8] c"3_a2r\00"
649
650 define i64 @ENTRYPOINT__main() #0 {
651 block_0:
652 call void @__quantum__rt__initialize(i8* null)
653 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
654 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
655 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
656 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*))
657 call void @__quantum__qis__rz__body(double 0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
658 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
659 call void @__quantum__qis__rz__body(double -0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
660 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
661 call void @__quantum__qis__rz__body(double 0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
662 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
663 call void @__quantum__qis__rz__body(double -0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
664 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
665 call void @__quantum__qis__rz__body(double 0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
666 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
667 call void @__quantum__qis__rz__body(double -0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
668 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
669 call void @__quantum__qis__rz__body(double 0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
670 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
671 call void @__quantum__qis__rz__body(double -0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
672 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
673 call void @__quantum__qis__rz__body(double 0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
674 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
675 call void @__quantum__qis__rz__body(double -0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
676 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
677 call void @__quantum__qis__rz__body(double 0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
678 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
679 call void @__quantum__qis__rz__body(double -0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
680 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
681 call void @__quantum__qis__rz__body(double 0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
682 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
683 call void @__quantum__qis__rz__body(double -0.5235987755982988, %Qubit* inttoptr (i64 0 to %Qubit*))
684 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))
685 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
686 call void @__quantum__qis__rz__body(double -0.7853981633974483, %Qubit* inttoptr (i64 2 to %Qubit*))
687 call void @__quantum__qis__rz__body(double -0.7853981633974483, %Qubit* inttoptr (i64 1 to %Qubit*))
688 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
689 call void @__quantum__qis__rz__body(double 0.7853981633974483, %Qubit* inttoptr (i64 1 to %Qubit*))
690 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
691 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))
692 call void @__quantum__qis__rz__body(double -0.39269908169872414, %Qubit* inttoptr (i64 3 to %Qubit*))
693 call void @__quantum__qis__rz__body(double -0.39269908169872414, %Qubit* inttoptr (i64 1 to %Qubit*))
694 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
695 call void @__quantum__qis__rz__body(double 0.39269908169872414, %Qubit* inttoptr (i64 1 to %Qubit*))
696 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
697 call void @__quantum__qis__rz__body(double -0.7853981633974483, %Qubit* inttoptr (i64 3 to %Qubit*))
698 call void @__quantum__qis__rz__body(double -0.7853981633974483, %Qubit* inttoptr (i64 2 to %Qubit*))
699 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*))
700 call void @__quantum__qis__rz__body(double 0.7853981633974483, %Qubit* inttoptr (i64 2 to %Qubit*))
701 call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*))
702 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*))
703 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
704 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
705 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 2 to %Result*))
706 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
707 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 1 to %Qubit*))
708 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 2 to %Qubit*))
709 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 3 to %Qubit*))
710 call void @__quantum__rt__array_record_output(i64 3, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
711 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
712 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
713 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @3, i64 0, i64 0))
714 ret i64 0
715 }
716
717 declare void @__quantum__rt__initialize(i8*)
718
719 declare void @__quantum__qis__x__body(%Qubit*)
720
721 declare void @__quantum__qis__h__body(%Qubit*)
722
723 declare void @__quantum__qis__rz__body(double, %Qubit*)
724
725 declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
726
727 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
728
729 declare void @__quantum__qis__reset__body(%Qubit*) #1
730
731 declare void @__quantum__rt__array_record_output(i64, i8*)
732
733 declare void @__quantum__rt__result_record_output(%Result*, i8*)
734
735 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="4" "required_num_results"="3" }
736 attributes #1 = { "irreversible" }
737
738 ; module flags
739
740 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
741
742 !0 = !{i32 1, !"qir_major_version", i32 1}
743 !1 = !{i32 7, !"qir_minor_version", i32 0}
744 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
745 !3 = !{i32 1, !"dynamic_result_management", i1 false}
746 !4 = !{i32 5, !"int_computations", !{!"i64"}}
747 !5 = !{i32 5, !"float_computations", !{!"double"}}
748 "#]]
749 .assert_eq(&qir);
750}
751
752#[test]
753fn explicit_return_tuple_keeps_dynamic_integer_output() {
754 let source = indoc::indoc! {r#"
755 namespace Test {
756 import Std.Measurement.*;
757
758 @EntryPoint()
759 operation Main() : (Int, Bool) {
760 use q = Qubit();
761 mutable a = 0;
762 if MResetZ(q) == Zero {
763 set a = 1;
764 } else {
765 set a = 2;
766 }
767
768 use p = Qubit();
769 return (a, MResetZ(p) == One);
770 }
771 }
772 "#};
773
774 let qir = compile_source_to_qir(
775 source,
776 TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::IntegerComputations,
777 );
778
779 expect![[r#"
780 %Result = type opaque
781 %Qubit = type opaque
782
783 @0 = internal constant [4 x i8] c"0_t\00"
784 @1 = internal constant [6 x i8] c"1_t0i\00"
785 @2 = internal constant [6 x i8] c"2_t1b\00"
786
787 define i64 @ENTRYPOINT__main() #0 {
788 block_0:
789 call void @__quantum__rt__initialize(i8* null)
790 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
791 %var_1 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
792 %var_2 = icmp eq i1 %var_1, false
793 br i1 %var_2, label %block_1, label %block_2
794 block_1:
795 br label %block_3
796 block_2:
797 br label %block_3
798 block_3:
799 %var_5 = phi i64 [1, %block_1], [2, %block_2]
800 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
801 %var_3 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 1 to %Result*))
802 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
803 call void @__quantum__rt__int_record_output(i64 %var_5, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
804 call void @__quantum__rt__bool_record_output(i1 %var_3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
805 ret i64 0
806 }
807
808 declare void @__quantum__rt__initialize(i8*)
809
810 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
811
812 declare i1 @__quantum__rt__read_result(%Result*)
813
814 declare void @__quantum__rt__tuple_record_output(i64, i8*)
815
816 declare void @__quantum__rt__int_record_output(i64, i8*)
817
818 declare void @__quantum__rt__bool_record_output(i1, i8*)
819
820 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
821 attributes #1 = { "irreversible" }
822
823 ; module flags
824
825 !llvm.module.flags = !{!0, !1, !2, !3, !4}
826
827 !0 = !{i32 1, !"qir_major_version", i32 1}
828 !1 = !{i32 7, !"qir_minor_version", i32 0}
829 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
830 !3 = !{i32 1, !"dynamic_result_management", i1 false}
831 !4 = !{i32 5, !"int_computations", !{!"i64"}}
832 "#]]
833 .assert_eq(&qir);
834}
835
836#[test]
837fn result_array_helper_return_survives_adaptive_codegen_prep() {
838 let source = indoc::indoc! {r#"
839 namespace Test {
840 import Std.Measurement.*;
841
842 @EntryPoint()
843 operation Main() : Result[] {
844 use register = Qubit[2];
845 return MResetZ2Register(register);
846 }
847
848 operation MResetZ2Register(register : Qubit[]) : Result[] {
849 return [MResetZ(register[0]), MResetZ(register[1])];
850 }
851 }
852 "#};
853
854 let qir = compile_source_to_qir(
855 source,
856 TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::IntegerComputations,
857 );
858
859 expect![[r#"
860 %Result = type opaque
861 %Qubit = type opaque
862
863 @0 = internal constant [4 x i8] c"0_a\00"
864 @1 = internal constant [6 x i8] c"1_a0r\00"
865 @2 = internal constant [6 x i8] c"2_a1r\00"
866
867 define i64 @ENTRYPOINT__main() #0 {
868 block_0:
869 call void @__quantum__rt__initialize(i8* null)
870 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
871 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
872 call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
873 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
874 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
875 ret i64 0
876 }
877
878 declare void @__quantum__rt__initialize(i8*)
879
880 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
881
882 declare void @__quantum__rt__array_record_output(i64, i8*)
883
884 declare void @__quantum__rt__result_record_output(%Result*, i8*)
885
886 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
887 attributes #1 = { "irreversible" }
888
889 ; module flags
890
891 !llvm.module.flags = !{!0, !1, !2, !3, !4}
892
893 !0 = !{i32 1, !"qir_major_version", i32 1}
894 !1 = !{i32 7, !"qir_minor_version", i32 0}
895 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
896 !3 = !{i32 1, !"dynamic_result_management", i1 false}
897 !4 = !{i32 5, !"int_computations", !{!"i64"}}
898 "#]]
899 .assert_eq(&qir);
900}
901
902#[test]
903fn higher_order_closure_captures_are_threaded_into_specialized_calls() {
904 let source = indoc::indoc! {r#"
905 namespace Test {
906 import Std.Canon.*;
907 import Std.Measurement.*;
908
909 operation ApplyOp(op : (Qubit[] => Unit), register : Qubit[]) : Result[] {
910 op(register);
911 return MResetEachZ(register);
912 }
913
914 @EntryPoint()
915 operation Main() : Result[] {
916 use register = Qubit[2];
917 return ApplyOp(register => Shifted(1, register), register);
918 }
919
920 operation Shifted(shift : Int, register : Qubit[]) : Unit {
921 ApplyXorInPlace(shift, register);
922 }
923 }
924 "#};
925
926 let qir = compile_source_to_qir(
927 source,
928 TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::IntegerComputations,
929 );
930
931 expect![[r#"
932 %Result = type opaque
933 %Qubit = type opaque
934
935 @0 = internal constant [4 x i8] c"0_a\00"
936 @1 = internal constant [6 x i8] c"1_a0r\00"
937 @2 = internal constant [6 x i8] c"2_a1r\00"
938
939 define i64 @ENTRYPOINT__main() #0 {
940 block_0:
941 call void @__quantum__rt__initialize(i8* null)
942 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
943 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
944 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
945 call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
946 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
947 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
948 ret i64 0
949 }
950
951 declare void @__quantum__rt__initialize(i8*)
952
953 declare void @__quantum__qis__x__body(%Qubit*)
954
955 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
956
957 declare void @__quantum__rt__array_record_output(i64, i8*)
958
959 declare void @__quantum__rt__result_record_output(%Result*, i8*)
960
961 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
962 attributes #1 = { "irreversible" }
963
964 ; module flags
965
966 !llvm.module.flags = !{!0, !1, !2, !3, !4}
967
968 !0 = !{i32 1, !"qir_major_version", i32 1}
969 !1 = !{i32 7, !"qir_minor_version", i32 0}
970 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
971 !3 = !{i32 1, !"dynamic_result_management", i1 false}
972 !4 = !{i32 5, !"int_computations", !{!"i64"}}
973 "#]]
974 .assert_eq(&qir);
975}
976
977#[test]
978fn two_callable_hof_closure_preserves_array_arg_threading() {
979 let source = indoc::indoc! {r#"
980 namespace Test {
981 import Std.Arrays.*;
982 import Std.Canon.*;
983 import Std.Convert.*;
984 import Std.Measurement.*;
985
986 operation Outer(Ufstar : (Qubit[] => Unit), Ug : (Qubit[] => Unit), n : Int) : Result[] {
987 use qubits = Qubit[n];
988 Ug(qubits);
989 return MResetEachZ(qubits);
990 }
991
992 operation Empty(register : Qubit[]) : Unit {
993 }
994
995 operation ShiftedSimple(shift : Int, register : Qubit[]) : Unit {
996 ApplyXorInPlace(shift, register);
997 }
998
999 @EntryPoint()
1000 operation Main() : Result[] {
1001 let bits = [true, false];
1002 let shift = BoolArrayAsInt(bits);
1003 let n = Length(bits);
1004 return Outer(Empty, register => ShiftedSimple(shift, register), n);
1005 }
1006 }
1007 "#};
1008
1009 let qir = compile_source_to_qir(
1010 source,
1011 TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::IntegerComputations,
1012 );
1013
1014 expect![[r#"
1015 %Result = type opaque
1016 %Qubit = type opaque
1017
1018 @0 = internal constant [4 x i8] c"0_a\00"
1019 @1 = internal constant [6 x i8] c"1_a0r\00"
1020 @2 = internal constant [6 x i8] c"2_a1r\00"
1021
1022 define i64 @ENTRYPOINT__main() #0 {
1023 block_0:
1024 call void @__quantum__rt__initialize(i8* null)
1025 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1026 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1027 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
1028 call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1029 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
1030 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
1031 ret i64 0
1032 }
1033
1034 declare void @__quantum__rt__initialize(i8*)
1035
1036 declare void @__quantum__qis__x__body(%Qubit*)
1037
1038 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1039
1040 declare void @__quantum__rt__array_record_output(i64, i8*)
1041
1042 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1043
1044 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
1045 attributes #1 = { "irreversible" }
1046
1047 ; module flags
1048
1049 !llvm.module.flags = !{!0, !1, !2, !3, !4}
1050
1051 !0 = !{i32 1, !"qir_major_version", i32 1}
1052 !1 = !{i32 7, !"qir_minor_version", i32 0}
1053 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1054 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1055 !4 = !{i32 5, !"int_computations", !{!"i64"}}
1056 "#]]
1057 .assert_eq(&qir);
1058}
1059
1060#[test]
1061fn callable_args_with_arrow_input_survives_dce() {
1062 let source = indoc::indoc! {r#"
1063 namespace Test {
1064 operation ApplyOp(op : Qubit => Unit) : Result {
1065 use q = Qubit();
1066 op(q);
1067 MResetZ(q)
1068 }
1069 operation MyOp(q : Qubit) : Unit { H(q); }
1070 }
1071 "#};
1072
1073 let capabilities = TargetCapabilityFlags::Adaptive
1074 | TargetCapabilityFlags::IntegerComputations
1075 | TargetCapabilityFlags::FloatingPointComputations;
1076
1077 let sources = source_map_from_source(source);
1078 let language_features = LanguageFeatures::default();
1079 let (std_id, mut store) = crate::compile::package_store_with_stdlib(capabilities);
1080 let dependencies: Vec<(PackageId, Option<Arc<str>>)> = vec![(std_id, None)];
1081
1082 let (unit, errors) = crate::compile::compile(
1083 &store,
1084 &dependencies,
1085 sources,
1086 qsc_passes::PackageType::Lib,
1087 capabilities,
1088 language_features,
1089 );
1090 assert!(errors.is_empty(), "compilation failed: {errors:?}");
1091 let package_id = store.insert(unit);
1092
1093 // Find ApplyOp and MyOp by name in the HIR package.
1094 let hir_package = &store.get(package_id).expect("package should exist").package;
1095 let mut apply_op_local = None;
1096 let mut my_op_local = None;
1097 for (local_id, item) in hir_package.items.iter() {
1098 if let ItemKind::Callable(decl) = &item.kind {
1099 if decl.name.name.as_ref() == "ApplyOp" {
1100 apply_op_local = Some(local_id);
1101 } else if decl.name.name.as_ref() == "MyOp" {
1102 my_op_local = Some(local_id);
1103 }
1104 }
1105 }
1106 let apply_op_local = apply_op_local.expect("ApplyOp should exist in HIR");
1107 let my_op_local = my_op_local.expect("MyOp should exist in HIR");
1108
1109 let apply_op_hir_id = qsc_hir::hir::ItemId {
1110 package: package_id,
1111 item: apply_op_local,
1112 };
1113
1114 // Construct Value::Global for MyOp using FIR StoreItemId.
1115 let my_op_fir_id = qsc_fir::fir::StoreItemId {
1116 package: qsc_lowerer::map_hir_package_to_fir(package_id),
1117 item: qsc_lowerer::map_hir_local_item_to_fir(my_op_local),
1118 };
1119 let my_op_value = Value::Global(my_op_fir_id, FunctorApp::default());
1120
1121 // The synthetic Call path makes ApplyOp entry-reachable. Defunc specializes
1122 // it to ApplyOp{MyOp}, and the pipeline transforms it fully. The original
1123 // ApplyOp is pinned for DCE survival so fir_to_qir_from_callable can use
1124 // the original ID with the original-shaped args.
1125 let codegen_fir =
1126 prepare_codegen_fir_from_callable_args(&store, apply_op_hir_id, &my_op_value, capabilities)
1127 .unwrap_or_else(|errors| {
1128 panic!(
1129 "callable-args with arrow-input should survive DCE, got: {}",
1130 format_interpret_errors(errors)
1131 )
1132 });
1133
1134 let backend_callable = qsc_fir::fir::StoreItemId {
1135 package: qsc_lowerer::map_hir_package_to_fir(apply_op_hir_id.package),
1136 item: qsc_lowerer::map_hir_local_item_to_fir(apply_op_hir_id.item),
1137 };
1138
1139 let qir = qsc_codegen::qir::fir_to_qir_from_callable(
1140 &codegen_fir.fir_store,
1141 capabilities,
1142 &codegen_fir.compute_properties,
1143 backend_callable,
1144 my_op_value,
1145 )
1146 .unwrap_or_else(|e| panic!("QIR generation from arrow-input callable should succeed: {e:?}"));
1147
1148 expect![[r#"
1149 %Result = type opaque
1150 %Qubit = type opaque
1151
1152 @0 = internal constant [4 x i8] c"0_r\00"
1153
1154 define i64 @ENTRYPOINT__main() #0 {
1155 block_0:
1156 call void @__quantum__rt__initialize(i8* null)
1157 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1158 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1159 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1160 ret i64 0
1161 }
1162
1163 declare void @__quantum__rt__initialize(i8*)
1164
1165 declare void @__quantum__qis__h__body(%Qubit*)
1166
1167 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1168
1169 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1170
1171 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
1172 attributes #1 = { "irreversible" }
1173
1174 ; module flags
1175
1176 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1177
1178 !0 = !{i32 1, !"qir_major_version", i32 1}
1179 !1 = !{i32 7, !"qir_minor_version", i32 0}
1180 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1181 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1182 !4 = !{i32 5, !"int_computations", !{!"i64"}}
1183 !5 = !{i32 5, !"float_computations", !{!"double"}}
1184 "#]]
1185 .assert_eq(&qir);
1186}
1187
1188#[test]
1189fn callable_args_with_udt_wrapped_arrow_survives_dce() {
1190 let source = indoc::indoc! {r#"
1191 namespace Test {
1192 newtype Config = (Op: Qubit => Unit, Data: Int);
1193 operation Apply(cfg: Config) : Unit {
1194 use q = Qubit();
1195 cfg::Op(q);
1196 }
1197 operation MyOp(q: Qubit) : Unit { H(q); }
1198 }
1199 "#};
1200
1201 let capabilities = TargetCapabilityFlags::Adaptive
1202 | TargetCapabilityFlags::IntegerComputations
1203 | TargetCapabilityFlags::FloatingPointComputations;
1204
1205 let sources = source_map_from_source(source);
1206 let language_features = LanguageFeatures::default();
1207 let (std_id, mut store) = crate::compile::package_store_with_stdlib(capabilities);
1208 let dependencies: Vec<(PackageId, Option<Arc<str>>)> = vec![(std_id, None)];
1209
1210 let (unit, errors) = crate::compile::compile(
1211 &store,
1212 &dependencies,
1213 sources,
1214 qsc_passes::PackageType::Lib,
1215 capabilities,
1216 language_features,
1217 );
1218 assert!(errors.is_empty(), "compilation failed: {errors:?}");
1219 let package_id = store.insert(unit);
1220
1221 let hir_package = &store.get(package_id).expect("package should exist").package;
1222 let mut apply_local = None;
1223 let mut my_op_local = None;
1224 let mut config_udt_local = None;
1225 for (local_id, item) in hir_package.items.iter() {
1226 match &item.kind {
1227 ItemKind::Callable(decl) if decl.name.name.as_ref() == "Apply" => {
1228 apply_local = Some(local_id);
1229 }
1230 ItemKind::Callable(decl) if decl.name.name.as_ref() == "MyOp" => {
1231 my_op_local = Some(local_id);
1232 }
1233 ItemKind::Ty(name, _) if name.name.as_ref() == "Config" => {
1234 config_udt_local = Some(local_id);
1235 }
1236 _ => {}
1237 }
1238 }
1239 let apply_local = apply_local.expect("Apply should exist in HIR");
1240 let my_op_local = my_op_local.expect("MyOp should exist in HIR");
1241
1242 let apply_hir_id = qsc_hir::hir::ItemId {
1243 package: package_id,
1244 item: apply_local,
1245 };
1246
1247 let my_op_fir_id = qsc_fir::fir::StoreItemId {
1248 package: qsc_lowerer::map_hir_package_to_fir(package_id),
1249 item: qsc_lowerer::map_hir_local_item_to_fir(my_op_local),
1250 };
1251 let my_op_value = Value::Global(my_op_fir_id, FunctorApp::default());
1252
1253 // Build a Config UDT value: Config(MyOp, 42)
1254 // UDT values are Value::Tuple(Rc<[Value]>, Option<Rc<StoreItemId>>)
1255 let config_fir_id = qsc_fir::fir::StoreItemId {
1256 package: qsc_lowerer::map_hir_package_to_fir(package_id),
1257 item: qsc_lowerer::map_hir_local_item_to_fir(
1258 config_udt_local.expect("Config UDT should exist"),
1259 ),
1260 };
1261 let config_value = Value::Tuple(
1262 vec![my_op_value, Value::Int(42)].into(),
1263 Some(Rc::new(config_fir_id)),
1264 );
1265
1266 let result =
1267 prepare_codegen_fir_from_callable_args(&store, apply_hir_id, &config_value, capabilities);
1268 match result {
1269 Ok(_) => {}
1270 Err(errors) => panic!(
1271 "callable-args with UDT-wrapped arrow should survive DCE, got: {}",
1272 format_interpret_errors(errors)
1273 ),
1274 }
1275}
1276
1277#[test]
1278fn callable_with_udt_wrapped_arrow_generates_qir_via_callable_args() {
1279 let source = indoc::indoc! {r#"
1280 namespace Test {
1281 newtype Config = (Op: Qubit => Unit, Data: Int);
1282 operation Apply(cfg: Config) : Result {
1283 use q = Qubit();
1284 cfg::Op(q);
1285 MResetZ(q)
1286 }
1287 operation MyOp(q: Qubit) : Unit { H(q); }
1288 }
1289 "#};
1290
1291 let capabilities = TargetCapabilityFlags::Adaptive
1292 | TargetCapabilityFlags::IntegerComputations
1293 | TargetCapabilityFlags::FloatingPointComputations;
1294
1295 let sources = source_map_from_source(source);
1296 let language_features = LanguageFeatures::default();
1297 let (std_id, mut store) = crate::compile::package_store_with_stdlib(capabilities);
1298 let dependencies: Vec<(PackageId, Option<Arc<str>>)> = vec![(std_id, None)];
1299
1300 let (unit, errors) = crate::compile::compile(
1301 &store,
1302 &dependencies,
1303 sources,
1304 qsc_passes::PackageType::Lib,
1305 capabilities,
1306 language_features,
1307 );
1308 assert!(errors.is_empty(), "compilation failed: {errors:?}");
1309 let package_id = store.insert(unit);
1310
1311 let hir_package = &store.get(package_id).expect("package should exist").package;
1312 let mut apply_local = None;
1313 let mut my_op_local = None;
1314 let mut config_udt_local = None;
1315 for (local_id, item) in hir_package.items.iter() {
1316 match &item.kind {
1317 ItemKind::Callable(decl) if decl.name.name.as_ref() == "Apply" => {
1318 apply_local = Some(local_id);
1319 }
1320 ItemKind::Callable(decl) if decl.name.name.as_ref() == "MyOp" => {
1321 my_op_local = Some(local_id);
1322 }
1323 ItemKind::Ty(name, _) if name.name.as_ref() == "Config" => {
1324 config_udt_local = Some(local_id);
1325 }
1326 _ => {}
1327 }
1328 }
1329 let apply_local = apply_local.expect("Apply should exist in HIR");
1330 let my_op_local = my_op_local.expect("MyOp should exist in HIR");
1331
1332 let apply_hir_id = qsc_hir::hir::ItemId {
1333 package: package_id,
1334 item: apply_local,
1335 };
1336
1337 let my_op_fir_id = qsc_fir::fir::StoreItemId {
1338 package: qsc_lowerer::map_hir_package_to_fir(package_id),
1339 item: qsc_lowerer::map_hir_local_item_to_fir(my_op_local),
1340 };
1341 let my_op_value = Value::Global(my_op_fir_id, FunctorApp::default());
1342
1343 let config_fir_id = qsc_fir::fir::StoreItemId {
1344 package: qsc_lowerer::map_hir_package_to_fir(package_id),
1345 item: qsc_lowerer::map_hir_local_item_to_fir(
1346 config_udt_local.expect("Config UDT should exist"),
1347 ),
1348 };
1349 let config_value = Value::Tuple(
1350 vec![my_op_value, Value::Int(42)].into(),
1351 Some(Rc::new(config_fir_id)),
1352 );
1353
1354 let codegen_fir =
1355 prepare_codegen_fir_from_callable_args(&store, apply_hir_id, &config_value, capabilities)
1356 .unwrap_or_else(|errors| {
1357 panic!(
1358 "callable-args with UDT-wrapped arrow should produce CodegenFir, got: {}",
1359 format_interpret_errors(errors)
1360 )
1361 });
1362
1363 let backend_callable = qsc_fir::fir::StoreItemId {
1364 package: qsc_lowerer::map_hir_package_to_fir(apply_hir_id.package),
1365 item: qsc_lowerer::map_hir_local_item_to_fir(apply_hir_id.item),
1366 };
1367
1368 let qir = qsc_codegen::qir::fir_to_qir_from_callable(
1369 &codegen_fir.fir_store,
1370 capabilities,
1371 &codegen_fir.compute_properties,
1372 backend_callable,
1373 config_value,
1374 )
1375 .unwrap_or_else(|e| panic!("QIR generation from UDT-wrapped arrow should succeed: {e:?}"));
1376
1377 expect![[r#"
1378 %Result = type opaque
1379 %Qubit = type opaque
1380
1381 @0 = internal constant [4 x i8] c"0_r\00"
1382
1383 define i64 @ENTRYPOINT__main() #0 {
1384 block_0:
1385 call void @__quantum__rt__initialize(i8* null)
1386 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1387 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1388 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1389 ret i64 0
1390 }
1391
1392 declare void @__quantum__rt__initialize(i8*)
1393
1394 declare void @__quantum__qis__h__body(%Qubit*)
1395
1396 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1397
1398 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1399
1400 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
1401 attributes #1 = { "irreversible" }
1402
1403 ; module flags
1404
1405 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1406
1407 !0 = !{i32 1, !"qir_major_version", i32 1}
1408 !1 = !{i32 7, !"qir_minor_version", i32 0}
1409 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1410 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1411 !4 = !{i32 5, !"int_computations", !{!"i64"}}
1412 !5 = !{i32 5, !"float_computations", !{!"double"}}
1413 "#]]
1414 .assert_eq(&qir);
1415}
1416
1417#[test]
1418fn callable_with_nested_udt_wrapped_arrow_generates_qir_via_callable_args() {
1419 let source = indoc::indoc! {r#"
1420 namespace Test {
1421 newtype OpWrapper = (Op: Qubit => Unit);
1422 newtype Config = (Inner: OpWrapper, Count: Int);
1423 operation Apply(cfg: Config) : Result {
1424 use q = Qubit();
1425 cfg::Inner::Op(q);
1426 MResetZ(q)
1427 }
1428 operation MyOp(q: Qubit) : Unit { X(q); }
1429 }
1430 "#};
1431
1432 let capabilities = TargetCapabilityFlags::Adaptive
1433 | TargetCapabilityFlags::IntegerComputations
1434 | TargetCapabilityFlags::FloatingPointComputations;
1435
1436 let sources = source_map_from_source(source);
1437 let language_features = LanguageFeatures::default();
1438 let (std_id, mut store) = crate::compile::package_store_with_stdlib(capabilities);
1439 let dependencies: Vec<(PackageId, Option<Arc<str>>)> = vec![(std_id, None)];
1440
1441 let (unit, errors) = crate::compile::compile(
1442 &store,
1443 &dependencies,
1444 sources,
1445 qsc_passes::PackageType::Lib,
1446 capabilities,
1447 language_features,
1448 );
1449 assert!(errors.is_empty(), "compilation failed: {errors:?}");
1450 let package_id = store.insert(unit);
1451
1452 let hir_package = &store.get(package_id).expect("package should exist").package;
1453 let mut apply_local = None;
1454 let mut my_op_local = None;
1455 let mut config_udt_local = None;
1456 for (local_id, item) in hir_package.items.iter() {
1457 match &item.kind {
1458 ItemKind::Callable(decl) if decl.name.name.as_ref() == "Apply" => {
1459 apply_local = Some(local_id);
1460 }
1461 ItemKind::Callable(decl) if decl.name.name.as_ref() == "MyOp" => {
1462 my_op_local = Some(local_id);
1463 }
1464 ItemKind::Ty(name, _) if name.name.as_ref() == "Config" => {
1465 config_udt_local = Some(local_id);
1466 }
1467 _ => {}
1468 }
1469 }
1470 let apply_local = apply_local.expect("Apply should exist in HIR");
1471 let my_op_local = my_op_local.expect("MyOp should exist in HIR");
1472
1473 let apply_hir_id = qsc_hir::hir::ItemId {
1474 package: package_id,
1475 item: apply_local,
1476 };
1477
1478 let my_op_fir_id = qsc_fir::fir::StoreItemId {
1479 package: qsc_lowerer::map_hir_package_to_fir(package_id),
1480 item: qsc_lowerer::map_hir_local_item_to_fir(my_op_local),
1481 };
1482 let my_op_value = Value::Global(my_op_fir_id, FunctorApp::default());
1483
1484 let config_fir_id = qsc_fir::fir::StoreItemId {
1485 package: qsc_lowerer::map_hir_package_to_fir(package_id),
1486 item: qsc_lowerer::map_hir_local_item_to_fir(
1487 config_udt_local.expect("Config UDT should exist"),
1488 ),
1489 };
1490 let config_value = Value::Tuple(
1491 vec![my_op_value, Value::Int(5)].into(),
1492 Some(Rc::new(config_fir_id)),
1493 );
1494
1495 let codegen_fir = prepare_codegen_fir_from_callable_args(
1496 &store,
1497 apply_hir_id,
1498 &config_value,
1499 capabilities,
1500 )
1501 .unwrap_or_else(|errors| {
1502 panic!(
1503 "callable-args with nested UDT-wrapped arrow should produce CodegenFir, got: {}",
1504 format_interpret_errors(errors)
1505 )
1506 });
1507
1508 let backend_callable = qsc_fir::fir::StoreItemId {
1509 package: qsc_lowerer::map_hir_package_to_fir(apply_hir_id.package),
1510 item: qsc_lowerer::map_hir_local_item_to_fir(apply_hir_id.item),
1511 };
1512
1513 let qir = qsc_codegen::qir::fir_to_qir_from_callable(
1514 &codegen_fir.fir_store,
1515 capabilities,
1516 &codegen_fir.compute_properties,
1517 backend_callable,
1518 config_value,
1519 )
1520 .unwrap_or_else(|e| {
1521 panic!("QIR generation from nested UDT-wrapped arrow should succeed: {e:?}")
1522 });
1523
1524 expect![[r#"
1525 %Result = type opaque
1526 %Qubit = type opaque
1527
1528 @0 = internal constant [4 x i8] c"0_r\00"
1529
1530 define i64 @ENTRYPOINT__main() #0 {
1531 block_0:
1532 call void @__quantum__rt__initialize(i8* null)
1533 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
1534 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
1535 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
1536 ret i64 0
1537 }
1538
1539 declare void @__quantum__rt__initialize(i8*)
1540
1541 declare void @__quantum__qis__x__body(%Qubit*)
1542
1543 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
1544
1545 declare void @__quantum__rt__result_record_output(%Result*, i8*)
1546
1547 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
1548 attributes #1 = { "irreversible" }
1549
1550 ; module flags
1551
1552 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
1553
1554 !0 = !{i32 1, !"qir_major_version", i32 1}
1555 !1 = !{i32 7, !"qir_minor_version", i32 0}
1556 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
1557 !3 = !{i32 1, !"dynamic_result_management", i1 false}
1558 !4 = !{i32 5, !"int_computations", !{!"i64"}}
1559 !5 = !{i32 5, !"float_computations", !{!"double"}}
1560 "#]].assert_eq(&qir);
1561}
1562
1563// ---------------------------------------------------------------------------
1564// Synthetic-path and fallback-path coverage for callable-args codegen
1565// ---------------------------------------------------------------------------
1566
1567/// Helper: compile a lib package, locate named items, and return (`store`, `package_id`, `items_map`).
1568/// `item_names` maps display names to a bool: true = Callable, false = Ty (UDT).
1569fn compile_and_locate_items(
1570 source: &str,
1571 item_names: &[(&str, bool)],
1572 capabilities: TargetCapabilityFlags,
1573) -> (
1574 crate::PackageStore,
1575 PackageId,
1576 FxHashMap<String, qsc_hir::hir::LocalItemId>,
1577) {
1578 let sources = source_map_from_source(source);
1579 let language_features = LanguageFeatures::default();
1580 let (std_id, mut store) = crate::compile::package_store_with_stdlib(capabilities);
1581 let dependencies: Vec<(PackageId, Option<Arc<str>>)> = vec![(std_id, None)];
1582 let (unit, errors) = crate::compile::compile(
1583 &store,
1584 &dependencies,
1585 sources,
1586 qsc_passes::PackageType::Lib,
1587 capabilities,
1588 language_features,
1589 );
1590 assert!(errors.is_empty(), "compilation failed: {errors:?}");
1591 let package_id = store.insert(unit);
1592
1593 let hir_package = &store.get(package_id).expect("package should exist").package;
1594 let mut found = FxHashMap::default();
1595 for (local_id, item) in hir_package.items.iter() {
1596 match &item.kind {
1597 ItemKind::Callable(decl) => {
1598 for &(name, is_callable) in item_names {
1599 if is_callable && decl.name.name.as_ref() == name {
1600 found.insert(name.to_string(), local_id);
1601 }
1602 }
1603 }
1604 ItemKind::Ty(name, _) => {
1605 for &(item_name, is_callable) in item_names {
1606 if !is_callable && name.name.as_ref() == item_name {
1607 found.insert(item_name.to_string(), local_id);
1608 }
1609 }
1610 }
1611 _ => {}
1612 }
1613 }
1614 for &(name, _) in item_names {
1615 assert!(
1616 found.contains_key(name),
1617 "{name} should exist in HIR package"
1618 );
1619 }
1620 (store, package_id, found)
1621}
1622
1623/// Returns the target capabilities used by callable-args synthetic path tests.
1624fn adaptive_capabilities() -> TargetCapabilityFlags {
1625 TargetCapabilityFlags::Adaptive
1626 | TargetCapabilityFlags::IntegerComputations
1627 | TargetCapabilityFlags::FloatingPointComputations
1628}
1629
1630/// Maps a HIR local item ID in the test package to its corresponding FIR item ID.
1631fn fir_id_for(
1632 package_id: PackageId,
1633 local_id: qsc_hir::hir::LocalItemId,
1634) -> qsc_fir::fir::StoreItemId {
1635 qsc_fir::fir::StoreItemId {
1636 package: qsc_lowerer::map_hir_package_to_fir(package_id),
1637 item: qsc_lowerer::map_hir_local_item_to_fir(local_id),
1638 }
1639}
1640
1641/// Builds a HIR item ID from a test package ID and local item ID.
1642fn hir_id_for(package_id: PackageId, local_id: qsc_hir::hir::LocalItemId) -> qsc_hir::hir::ItemId {
1643 qsc_hir::hir::ItemId {
1644 package: package_id,
1645 item: local_id,
1646 }
1647}
1648
1649/// Runs `prepare_codegen_fir_from_callable_args` and then `fir_to_qir_from_callable`,
1650/// returning the QIR string.
1651fn callable_args_to_qir(
1652 store: &crate::PackageStore,
1653 package_id: PackageId,
1654 target_local: qsc_hir::hir::LocalItemId,
1655 args: &Value,
1656 capabilities: TargetCapabilityFlags,
1657) -> String {
1658 let target_hir = hir_id_for(package_id, target_local);
1659 let codegen_fir = prepare_codegen_fir_from_callable_args(store, target_hir, args, capabilities)
1660 .unwrap_or_else(|errors| {
1661 panic!(
1662 "prepare_codegen_fir_from_callable_args failed: {}",
1663 format_interpret_errors(errors)
1664 )
1665 });
1666 let backend_callable = fir_id_for(package_id, target_local);
1667 qsc_codegen::qir::fir_to_qir_from_callable(
1668 &codegen_fir.fir_store,
1669 capabilities,
1670 &codegen_fir.compute_properties,
1671 backend_callable,
1672 args.clone(),
1673 )
1674 .unwrap_or_else(|e| panic!("fir_to_qir_from_callable failed: {e:?}"))
1675}
1676
1677// ---- Synthetic path: arrow + non-callable params (tuple input) ----
1678
1679#[test]
1680fn synthetic_path_arrow_and_int_tuple_generates_qir() {
1681 // Target takes (op: Qubit => Unit, count: Int). Only the callable flows
1682 // through `args`; count is provided as a plain Int value.
1683 let source = indoc::indoc! {r#"
1684 namespace Test {
1685 operation RunOp(op : Qubit => Unit, count : Int) : Result {
1686 use q = Qubit();
1687 for _ in 0..count - 1 {
1688 op(q);
1689 }
1690 MResetZ(q)
1691 }
1692 operation MyH(q : Qubit) : Unit { H(q); }
1693 }
1694 "#};
1695 let caps = adaptive_capabilities();
1696 let (store, pkg, items) =
1697 compile_and_locate_items(source, &[("RunOp", true), ("MyH", true)], caps);
1698
1699 let my_h = Value::Global(fir_id_for(pkg, items["MyH"]), FunctorApp::default());
1700 let args = Value::Tuple(vec![my_h, Value::Int(3)].into(), None);
1701
1702 let qir = callable_args_to_qir(&store, pkg, items["RunOp"], &args, caps);
1703 // The QIR must contain an h__body call from the loop body.
1704 assert!(
1705 qir.contains("__quantum__qis__h__body"),
1706 "expected h gate in QIR:\n{qir}"
1707 );
1708 assert!(
1709 qir.contains("__quantum__qis__mresetz__body"),
1710 "expected mresetz in QIR:\n{qir}"
1711 );
1712}
1713
1714// ---- Synthetic path: two callable args in a tuple ----
1715
1716#[test]
1717fn synthetic_path_two_arrow_args_generates_qir() {
1718 // Target takes (op1: Qubit => Unit, op2: Qubit => Unit). Both are
1719 // Global values — the synthetic Call must place both at their respective
1720 // tuple positions.
1721 let source = indoc::indoc! {r#"
1722 namespace Test {
1723 operation ApplyBoth(op1 : Qubit => Unit, op2 : Qubit => Unit) : Result {
1724 use q = Qubit();
1725 op1(q);
1726 op2(q);
1727 MResetZ(q)
1728 }
1729 operation DoH(q : Qubit) : Unit { H(q); }
1730 operation DoX(q : Qubit) : Unit { X(q); }
1731 }
1732 "#};
1733 let caps = adaptive_capabilities();
1734 let (store, pkg, items) = compile_and_locate_items(
1735 source,
1736 &[("ApplyBoth", true), ("DoH", true), ("DoX", true)],
1737 caps,
1738 );
1739
1740 let do_h = Value::Global(fir_id_for(pkg, items["DoH"]), FunctorApp::default());
1741 let do_x = Value::Global(fir_id_for(pkg, items["DoX"]), FunctorApp::default());
1742 let args = Value::Tuple(vec![do_h, do_x].into(), None);
1743
1744 let qir = callable_args_to_qir(&store, pkg, items["ApplyBoth"], &args, caps);
1745 assert!(
1746 qir.contains("__quantum__qis__h__body"),
1747 "expected H gate in QIR:\n{qir}"
1748 );
1749 assert!(
1750 qir.contains("__quantum__qis__x__body"),
1751 "expected X gate in QIR:\n{qir}"
1752 );
1753}
1754
1755// ---- Synthetic path: arrow sandwiched between non-callable params ----
1756
1757#[test]
1758fn synthetic_path_int_arrow_bool_tuple_generates_qir() {
1759 // Target takes (n: Int, op: Qubit => Unit, flag: Bool). The callable is
1760 // in the middle of the tuple — exercises the element-wise matching logic
1761 // in `build_synthetic_args`.
1762 let source = indoc::indoc! {r#"
1763 namespace Test {
1764 operation Middle(n : Int, op : Qubit => Unit, flag : Bool) : Result {
1765 use q = Qubit();
1766 if flag {
1767 for _ in 0..n - 1 { op(q); }
1768 }
1769 MResetZ(q)
1770 }
1771 operation DoX(q : Qubit) : Unit { X(q); }
1772 }
1773 "#};
1774 let caps = adaptive_capabilities();
1775 let (store, pkg, items) =
1776 compile_and_locate_items(source, &[("Middle", true), ("DoX", true)], caps);
1777
1778 let do_x = Value::Global(fir_id_for(pkg, items["DoX"]), FunctorApp::default());
1779 let args = Value::Tuple(vec![Value::Int(2), do_x, Value::Bool(true)].into(), None);
1780
1781 let qir = callable_args_to_qir(&store, pkg, items["Middle"], &args, caps);
1782 assert!(
1783 qir.contains("__quantum__qis__x__body"),
1784 "expected X gate in QIR:\n{qir}"
1785 );
1786}
1787
1788// ---- Synthetic path: no callable args (pure values) ----
1789
1790#[test]
1791fn no_callable_args_takes_early_return_path() {
1792 // When args contain no callable values, `prepare_codegen_fir_from_callable_args`
1793 // takes the `concrete_callables.is_empty()` early return to `prepare_codegen_fir_from_callable`.
1794 // This exercises that branch.
1795 let source = indoc::indoc! {r#"
1796 namespace Test {
1797 operation Simple(n : Int) : Result {
1798 use q = Qubit();
1799 for _ in 0..n - 1 { H(q); }
1800 MResetZ(q)
1801 }
1802 }
1803 "#};
1804 let caps = adaptive_capabilities();
1805 let (store, pkg, items) = compile_and_locate_items(source, &[("Simple", true)], caps);
1806
1807 let args = Value::Int(3);
1808 let target_hir = hir_id_for(pkg, items["Simple"]);
1809
1810 // Should succeed without error — takes the no-callable early path.
1811 let result = prepare_codegen_fir_from_callable_args(&store, target_hir, &args, caps);
1812 assert!(
1813 result.is_ok(),
1814 "no-callable args should succeed: {:?}",
1815 result.err().map(format_interpret_errors)
1816 );
1817}
1818
1819// ---- Synthetic path: struct with callable and non-callable fields ----
1820
1821#[test]
1822fn synthetic_path_struct_with_callable_field_generates_qir() {
1823 // `Config` is a newtype wrapping (Op: Qubit => Unit, Data: Int).
1824 // The synthetic Call builder resolves the UDT's pure tuple shape so defunc
1825 // can discover and specialize the callable field.
1826 let source = indoc::indoc! {r#"
1827 namespace Test {
1828 newtype Config = (Op: Qubit => Unit, Data: Int);
1829 operation Apply(cfg: Config) : Result {
1830 use q = Qubit();
1831 cfg::Op(q);
1832 MResetZ(q)
1833 }
1834 operation DoH(q: Qubit) : Unit { H(q); }
1835 }
1836 "#};
1837 let caps = adaptive_capabilities();
1838 let (store, pkg, items) = compile_and_locate_items(
1839 source,
1840 &[("Apply", true), ("DoH", true), ("Config", false)],
1841 caps,
1842 );
1843
1844 let do_h = Value::Global(fir_id_for(pkg, items["DoH"]), FunctorApp::default());
1845 let config = Value::Tuple(
1846 vec![do_h, Value::Int(42)].into(),
1847 Some(Rc::new(fir_id_for(pkg, items["Config"]))),
1848 );
1849
1850 let qir = callable_args_to_qir(&store, pkg, items["Apply"], &config, caps);
1851 assert!(
1852 qir.contains("__quantum__qis__h__body"),
1853 "expected H gate in QIR:\n{qir}"
1854 );
1855}
1856
1857// ---- No-callable path: struct with only non-callable fields ----
1858
1859#[test]
1860fn struct_with_no_callable_fields_takes_early_return_path() {
1861 // A UDT that contains no callable fields takes the `concrete_callables.is_empty()`
1862 // early return.
1863 let source = indoc::indoc! {r#"
1864 namespace Test {
1865 newtype Pair = (First: Int, Second: Int);
1866 operation Sum(p: Pair) : Result {
1867 use q = Qubit();
1868 let total = p::First + p::Second;
1869 if total > 0 { H(q); }
1870 MResetZ(q)
1871 }
1872 }
1873 "#};
1874 let caps = adaptive_capabilities();
1875 let (store, pkg, items) =
1876 compile_and_locate_items(source, &[("Sum", true), ("Pair", false)], caps);
1877
1878 let pair = Value::Tuple(
1879 vec![Value::Int(3), Value::Int(5)].into(),
1880 Some(Rc::new(fir_id_for(pkg, items["Pair"]))),
1881 );
1882 let target_hir = hir_id_for(pkg, items["Sum"]);
1883
1884 let result = prepare_codegen_fir_from_callable_args(&store, target_hir, &pair, caps);
1885 assert!(
1886 result.is_ok(),
1887 "struct with no callable fields should succeed: {:?}",
1888 result.err().map(format_interpret_errors)
1889 );
1890}
1891
1892// ---- Synthetic path: single Global arg (not in a tuple) ----
1893
1894#[test]
1895fn synthetic_path_single_global_arg_generates_qir() {
1896 // The simplest synthetic path: a single callable arg, not wrapped in a tuple.
1897 // `build_synthetic_args` hits the `Ty::Arrow` branch directly.
1898 let source = indoc::indoc! {r#"
1899 namespace Test {
1900 operation Invoke(op : Qubit => Unit) : Result {
1901 use q = Qubit();
1902 op(q);
1903 MResetZ(q)
1904 }
1905 operation DoX(q : Qubit) : Unit { X(q); }
1906 }
1907 "#};
1908 let caps = adaptive_capabilities();
1909 let (store, pkg, items) =
1910 compile_and_locate_items(source, &[("Invoke", true), ("DoX", true)], caps);
1911
1912 let do_x = Value::Global(fir_id_for(pkg, items["DoX"]), FunctorApp::default());
1913
1914 let qir = callable_args_to_qir(&store, pkg, items["Invoke"], &do_x, caps);
1915 assert!(
1916 qir.contains("__quantum__qis__x__body"),
1917 "expected X gate in QIR:\n{qir}"
1918 );
1919}
1920
1921#[test]
1922fn synthetic_path_captureless_closure_adjoint_preserves_functor() {
1923 let source = indoc::indoc! {r#"
1924 namespace Test {
1925 operation Invoke(op : Qubit => Unit is Adj) : Result {
1926 use q = Qubit();
1927 op(q);
1928 MResetZ(q)
1929 }
1930 operation DoS(q : Qubit) : Unit is Adj { S(q); }
1931 }
1932 "#};
1933 let caps = adaptive_capabilities();
1934 let (store, pkg, items) =
1935 compile_and_locate_items(source, &[("Invoke", true), ("DoS", true)], caps);
1936
1937 let adjoint_do_s = Value::Closure(Box::new(qsc_eval::val::Closure {
1938 fixed_args: Vec::<Value>::new().into(),
1939 id: fir_id_for(pkg, items["DoS"]),
1940 functor: FunctorApp {
1941 adjoint: true,
1942 controlled: 0,
1943 },
1944 }));
1945
1946 let target_hir = hir_id_for(pkg, items["Invoke"]);
1947 let codegen_fir =
1948 prepare_codegen_fir_from_callable_args(&store, target_hir, &adjoint_do_s, caps)
1949 .unwrap_or_else(|errors| {
1950 panic!(
1951 "adjoint captureless closure should produce CodegenFir, got: {}",
1952 format_interpret_errors(errors)
1953 )
1954 });
1955 let entry = crate::codegen::qir::entry_from_codegen_fir(&codegen_fir);
1956 let qir = qsc_codegen::qir::fir_to_qir(
1957 &codegen_fir.fir_store,
1958 caps,
1959 &codegen_fir.compute_properties,
1960 &entry,
1961 )
1962 .unwrap_or_else(|e| panic!("synthetic entry QIR generation should succeed: {e:?}"));
1963 assert!(
1964 qir.contains("__quantum__qis__s__adj"),
1965 "expected adjoint S gate in QIR:\n{qir}"
1966 );
1967}
1968
1969#[test]
1970fn synthetic_path_udt_wrapped_controlled_callable_preserves_functor() {
1971 let source = indoc::indoc! {r#"
1972 namespace Test {
1973 newtype CtlBox = (Op: ((Qubit[], Qubit) => Unit), Tag: Int);
1974 operation Invoke(b : CtlBox) : Result {
1975 use (control, target) = (Qubit(), Qubit());
1976 b::Op([control], target);
1977 Reset(control);
1978 MResetZ(target)
1979 }
1980 operation DoX(q : Qubit) : Unit is Ctl { X(q); }
1981 }
1982 "#};
1983 let caps = adaptive_capabilities();
1984 let (store, pkg, items) = compile_and_locate_items(
1985 source,
1986 &[("Invoke", true), ("DoX", true), ("CtlBox", false)],
1987 caps,
1988 );
1989
1990 let controlled_do_x = Value::Global(
1991 fir_id_for(pkg, items["DoX"]),
1992 FunctorApp {
1993 adjoint: false,
1994 controlled: 1,
1995 },
1996 );
1997 let boxed = Value::Tuple(
1998 vec![controlled_do_x, Value::Int(0)].into(),
1999 Some(Rc::new(fir_id_for(pkg, items["CtlBox"]))),
2000 );
2001
2002 let target_hir = hir_id_for(pkg, items["Invoke"]);
2003 let codegen_fir = prepare_codegen_fir_from_callable_args(&store, target_hir, &boxed, caps)
2004 .unwrap_or_else(|errors| {
2005 panic!(
2006 "controlled UDT-wrapped callable should produce CodegenFir, got: {}",
2007 format_interpret_errors(errors)
2008 )
2009 });
2010 let entry = crate::codegen::qir::entry_from_codegen_fir(&codegen_fir);
2011 let qir = qsc_codegen::qir::fir_to_qir(
2012 &codegen_fir.fir_store,
2013 caps,
2014 &codegen_fir.compute_properties,
2015 &entry,
2016 )
2017 .unwrap_or_else(|e| panic!("synthetic entry QIR generation should succeed: {e:?}"));
2018 assert!(
2019 qir.contains("__quantum__qis__cx__body"),
2020 "expected controlled X gate in QIR:\n{qir}"
2021 );
2022}
2023
2024// ---- Synthetic path: struct wrapping a callable field ----
2025
2026#[test]
2027fn synthetic_path_single_field_struct_wrapping_callable_generates_qir() {
2028 // Single-field UDT constructors are transparent in Value form: OpBox(DoH)
2029 // is represented as the bare Global callable value.
2030 let source = indoc::indoc! {r#"
2031 namespace Test {
2032 newtype OpBox = (Op: Qubit => Unit);
2033 operation RunBoxed(b: OpBox) : Result {
2034 use q = Qubit();
2035 b::Op(q);
2036 MResetZ(q)
2037 }
2038 operation DoH(q: Qubit) : Unit { H(q); }
2039 }
2040 "#};
2041 let caps = adaptive_capabilities();
2042 let (store, pkg, items) =
2043 compile_and_locate_items(source, &[("RunBoxed", true), ("DoH", true)], caps);
2044
2045 let do_h = Value::Global(fir_id_for(pkg, items["DoH"]), FunctorApp::default());
2046
2047 let qir = callable_args_to_qir(&store, pkg, items["RunBoxed"], &do_h, caps);
2048 assert!(
2049 qir.contains("__quantum__qis__h__body"),
2050 "expected H gate in QIR:\n{qir}"
2051 );
2052}
2053
2054#[test]
2055fn synthetic_path_struct_wrapping_callable_and_tag_generates_qir() {
2056 // A newtype that wraps a callable and a non-callable field.
2057 // This keeps tuple structure in the runtime Value while still exercising
2058 // UDT pure-type discovery.
2059 let source = indoc::indoc! {r#"
2060 namespace Test {
2061 newtype OpBox = (Op: Qubit => Unit, Tag: Int);
2062 operation RunBoxed(b: OpBox) : Result {
2063 use q = Qubit();
2064 b::Op(q);
2065 MResetZ(q)
2066 }
2067 operation DoH(q: Qubit) : Unit { H(q); }
2068 }
2069 "#};
2070 let caps = adaptive_capabilities();
2071 let (store, pkg, items) = compile_and_locate_items(
2072 source,
2073 &[("RunBoxed", true), ("DoH", true), ("OpBox", false)],
2074 caps,
2075 );
2076
2077 let do_h = Value::Global(fir_id_for(pkg, items["DoH"]), FunctorApp::default());
2078 let boxed = Value::Tuple(
2079 vec![do_h, Value::Int(0)].into(),
2080 Some(Rc::new(fir_id_for(pkg, items["OpBox"]))),
2081 );
2082
2083 let qir = callable_args_to_qir(&store, pkg, items["RunBoxed"], &boxed, caps);
2084 assert!(
2085 qir.contains("__quantum__qis__h__body"),
2086 "expected H gate in QIR:\n{qir}"
2087 );
2088}
2089
2090#[test]
2091fn synthetic_path_udt_wrapped_adjoint_callable_preserves_functor() {
2092 let source = indoc::indoc! {r#"
2093 namespace Test {
2094 newtype OpBox = (Op: Qubit => Unit is Adj, Tag: Int);
2095 operation RunBoxed(b: OpBox) : Result {
2096 use q = Qubit();
2097 b::Op(q);
2098 MResetZ(q)
2099 }
2100 operation DoS(q: Qubit) : Unit is Adj { S(q); }
2101 }
2102 "#};
2103 let caps = adaptive_capabilities();
2104 let (store, pkg, items) = compile_and_locate_items(
2105 source,
2106 &[("RunBoxed", true), ("DoS", true), ("OpBox", false)],
2107 caps,
2108 );
2109
2110 let adjoint_do_s = Value::Global(
2111 fir_id_for(pkg, items["DoS"]),
2112 FunctorApp {
2113 adjoint: true,
2114 controlled: 0,
2115 },
2116 );
2117 let boxed = Value::Tuple(
2118 vec![adjoint_do_s, Value::Int(0)].into(),
2119 Some(Rc::new(fir_id_for(pkg, items["OpBox"]))),
2120 );
2121
2122 let target_hir = hir_id_for(pkg, items["RunBoxed"]);
2123 let codegen_fir = prepare_codegen_fir_from_callable_args(&store, target_hir, &boxed, caps)
2124 .unwrap_or_else(|errors| {
2125 panic!(
2126 "adjoint UDT-wrapped callable should produce CodegenFir, got: {}",
2127 format_interpret_errors(errors)
2128 )
2129 });
2130 let entry = crate::codegen::qir::entry_from_codegen_fir(&codegen_fir);
2131 let qir = qsc_codegen::qir::fir_to_qir(
2132 &codegen_fir.fir_store,
2133 caps,
2134 &codegen_fir.compute_properties,
2135 &entry,
2136 )
2137 .unwrap_or_else(|e| panic!("synthetic entry QIR generation should succeed: {e:?}"));
2138 assert!(
2139 qir.contains("__quantum__qis__s__adj"),
2140 "expected adjoint S gate in QIR:\n{qir}"
2141 );
2142}
2143
2144// ---- Synthetic path: callable arg with additional non-callable tuple values ----
2145
2146#[test]
2147fn synthetic_path_callable_with_double_and_string_generates_qir() {
2148 // Target takes (factor: Double, op: Qubit => Unit, label: String).
2149 // All three value types exercise different branches in `lower_value_to_expr`.
2150 let source = indoc::indoc! {r#"
2151 namespace Test {
2152 operation Tagged(factor : Double, op : Qubit => Unit, label : String) : Result {
2153 use q = Qubit();
2154 op(q);
2155 MResetZ(q)
2156 }
2157 operation DoH(q : Qubit) : Unit { H(q); }
2158 }
2159 "#};
2160 let caps = adaptive_capabilities();
2161 let (store, pkg, items) =
2162 compile_and_locate_items(source, &[("Tagged", true), ("DoH", true)], caps);
2163
2164 let do_h = Value::Global(fir_id_for(pkg, items["DoH"]), FunctorApp::default());
2165 let args = Value::Tuple(
2166 vec![Value::Double(1.5), do_h, Value::String("test".into())].into(),
2167 None,
2168 );
2169
2170 let qir = callable_args_to_qir(&store, pkg, items["Tagged"], &args, caps);
2171 assert!(
2172 qir.contains("__quantum__qis__h__body"),
2173 "expected H gate in QIR:\n{qir}"
2174 );
2175}
2176
2177// ---- Synthetic path: nested struct (UDT inside UDT) with callable ----
2178
2179#[test]
2180fn synthetic_path_nested_struct_with_callable_generates_qir() {
2181 // Two levels of UDT wrapping: Config(Inner: OpBox, N: Int) where
2182 // OpBox(Op: Qubit => Unit, Id: Int). This exercises UDT pure-type descent
2183 // and nested field-chain replacement in defunctionalization.
2184 // Inner UDTs need 2+ fields to avoid the single-field-UDT unwrap issue
2185 // where the Value::Tuple shape misaligns with the erased type.
2186 let source = indoc::indoc! {r#"
2187 namespace Test {
2188 newtype OpBox = (Op: Qubit => Unit, Id: Int);
2189 newtype Config = (Inner: OpBox, N: Int);
2190 operation RunConfig(cfg: Config) : Result {
2191 use q = Qubit();
2192 cfg::Inner::Op(q);
2193 MResetZ(q)
2194 }
2195 operation DoX(q: Qubit) : Unit { X(q); }
2196 }
2197 "#};
2198 let caps = adaptive_capabilities();
2199 let (store, pkg, items) = compile_and_locate_items(
2200 source,
2201 &[
2202 ("RunConfig", true),
2203 ("DoX", true),
2204 ("Config", false),
2205 ("OpBox", false),
2206 ],
2207 caps,
2208 );
2209
2210 let do_x = Value::Global(fir_id_for(pkg, items["DoX"]), FunctorApp::default());
2211 let inner = Value::Tuple(
2212 vec![do_x, Value::Int(1)].into(),
2213 Some(Rc::new(fir_id_for(pkg, items["OpBox"]))),
2214 );
2215 let config = Value::Tuple(
2216 vec![inner, Value::Int(5)].into(),
2217 Some(Rc::new(fir_id_for(pkg, items["Config"]))),
2218 );
2219
2220 let qir = callable_args_to_qir(&store, pkg, items["RunConfig"], &config, caps);
2221 assert!(
2222 qir.contains("__quantum__qis__x__body"),
2223 "expected X gate in QIR:\n{qir}"
2224 );
2225}
2226
2227#[test]
2228fn synthetic_path_callable_field_taking_udt_with_callable_generates_qir() {
2229 // Outer wraps a callable whose input is Inner, and Inner itself wraps a
2230 // callable. This exercises UDT expansion through arrow input types, not
2231 // just nested UDT fields that directly contain callable values.
2232 let source = indoc::indoc! {r#"
2233 namespace Test {
2234 newtype Inner = (NestedOp: Qubit => Unit, Id: Int);
2235 newtype Outer = (ApplyInner: Inner => Result, Id: Int);
2236
2237 operation Invoke(outer: Outer) : Result {
2238 let inner = Inner(DoH, 2);
2239 outer::ApplyInner(inner)
2240 }
2241
2242 operation UseInner(inner: Inner) : Result {
2243 use q = Qubit();
2244 inner::NestedOp(q);
2245 MResetZ(q)
2246 }
2247
2248 operation DoH(q: Qubit) : Unit { H(q); }
2249 }
2250 "#};
2251 let caps = adaptive_capabilities();
2252 let (store, pkg, items) = compile_and_locate_items(
2253 source,
2254 &[
2255 ("Invoke", true),
2256 ("UseInner", true),
2257 ("DoH", true),
2258 ("Inner", false),
2259 ("Outer", false),
2260 ],
2261 caps,
2262 );
2263
2264 let use_inner = Value::Global(fir_id_for(pkg, items["UseInner"]), FunctorApp::default());
2265 let outer = Value::Tuple(
2266 vec![use_inner, Value::Int(1)].into(),
2267 Some(Rc::new(fir_id_for(pkg, items["Outer"]))),
2268 );
2269
2270 let target_hir = hir_id_for(pkg, items["Invoke"]);
2271 let codegen_fir = prepare_codegen_fir_from_callable_args(&store, target_hir, &outer, caps)
2272 .unwrap_or_else(|errors| {
2273 panic!(
2274 "callable field taking a UDT with a callable should produce CodegenFir, got: {}",
2275 format_interpret_errors(errors)
2276 )
2277 });
2278 let entry = crate::codegen::qir::entry_from_codegen_fir(&codegen_fir);
2279 let qir = qsc_codegen::qir::fir_to_qir(
2280 &codegen_fir.fir_store,
2281 caps,
2282 &codegen_fir.compute_properties,
2283 &entry,
2284 )
2285 .unwrap_or_else(|e| panic!("synthetic entry QIR generation should succeed: {e:?}"));
2286 assert!(
2287 qir.contains("__quantum__qis__h__body"),
2288 "expected H gate in QIR:\n{qir}"
2289 );
2290}
2291
2292// ---- Synthetic path: tuple arg where only one element is callable ----
2293
2294#[test]
2295fn synthetic_path_tuple_with_one_callable_among_many_scalars() {
2296 // (Int, Int, Qubit => Unit, Bool, Int) — callable buried deep in a wide tuple.
2297 let source = indoc::indoc! {r#"
2298 namespace Test {
2299 operation Wide(a : Int, b : Int, op : Qubit => Unit, flag : Bool, c : Int) : Result {
2300 use q = Qubit();
2301 if flag { op(q); }
2302 MResetZ(q)
2303 }
2304 operation DoH(q : Qubit) : Unit { H(q); }
2305 }
2306 "#};
2307 let caps = adaptive_capabilities();
2308 let (store, pkg, items) =
2309 compile_and_locate_items(source, &[("Wide", true), ("DoH", true)], caps);
2310
2311 let do_h = Value::Global(fir_id_for(pkg, items["DoH"]), FunctorApp::default());
2312 let args = Value::Tuple(
2313 vec![
2314 Value::Int(1),
2315 Value::Int(2),
2316 do_h,
2317 Value::Bool(true),
2318 Value::Int(4),
2319 ]
2320 .into(),
2321 None,
2322 );
2323
2324 let qir = callable_args_to_qir(&store, pkg, items["Wide"], &args, caps);
2325 assert!(
2326 qir.contains("__quantum__qis__h__body"),
2327 "expected H gate in QIR:\n{qir}"
2328 );
2329}
2330
2331// ---- Synthetic path: plain tuple with callable ----
2332
2333#[test]
2334fn plain_tuple_with_callable_takes_synthetic_path() {
2335 // A plain `Value::Tuple(_, None)` (no UDT tag) containing a callable takes
2336 // the same synthetic path as UDT values.
2337 let source = indoc::indoc! {r#"
2338 namespace Test {
2339 operation RunPair(op : Qubit => Unit, n : Int) : Result {
2340 use q = Qubit();
2341 for _ in 0..n - 1 { op(q); }
2342 MResetZ(q)
2343 }
2344 operation DoH(q : Qubit) : Unit { H(q); }
2345 }
2346 "#};
2347 let caps = adaptive_capabilities();
2348 let (store, pkg, items) =
2349 compile_and_locate_items(source, &[("RunPair", true), ("DoH", true)], caps);
2350
2351 let do_h = Value::Global(fir_id_for(pkg, items["DoH"]), FunctorApp::default());
2352 // Plain tuple — no UDT tag.
2353 let args = Value::Tuple(vec![do_h, Value::Int(2)].into(), None);
2354
2355 let qir = callable_args_to_qir(&store, pkg, items["RunPair"], &args, caps);
2356 assert!(
2357 qir.contains("__quantum__qis__h__body"),
2358 "expected H gate in QIR:\n{qir}"
2359 );
2360}
2361
2362// ---- Synthetic path: struct with two callable fields ----
2363
2364#[test]
2365fn synthetic_path_struct_with_two_callable_fields_generates_qir() {
2366 // A newtype with two arrow fields. Both are wrapped in the UDT.
2367 let source = indoc::indoc! {r#"
2368 namespace Test {
2369 newtype Ops = (First: Qubit => Unit, Second: Qubit => Unit);
2370 operation RunOps(ops: Ops) : Result {
2371 use q = Qubit();
2372 ops::First(q);
2373 ops::Second(q);
2374 MResetZ(q)
2375 }
2376 operation DoH(q: Qubit) : Unit { H(q); }
2377 operation DoX(q: Qubit) : Unit { X(q); }
2378 }
2379 "#};
2380 let caps = adaptive_capabilities();
2381 let (store, pkg, items) = compile_and_locate_items(
2382 source,
2383 &[
2384 ("RunOps", true),
2385 ("DoH", true),
2386 ("DoX", true),
2387 ("Ops", false),
2388 ],
2389 caps,
2390 );
2391
2392 let do_h = Value::Global(fir_id_for(pkg, items["DoH"]), FunctorApp::default());
2393 let do_x = Value::Global(fir_id_for(pkg, items["DoX"]), FunctorApp::default());
2394 let ops = Value::Tuple(
2395 vec![do_h, do_x].into(),
2396 Some(Rc::new(fir_id_for(pkg, items["Ops"]))),
2397 );
2398
2399 let qir = callable_args_to_qir(&store, pkg, items["RunOps"], &ops, caps);
2400 assert!(
2401 qir.contains("__quantum__qis__h__body"),
2402 "expected H gate in QIR:\n{qir}"
2403 );
2404 assert!(
2405 qir.contains("__quantum__qis__x__body"),
2406 "expected X gate in QIR:\n{qir}"
2407 );
2408}
2409
2410// ---- Synthetic path: callable with Pauli and Result args ----
2411
2412#[test]
2413fn synthetic_path_callable_with_pauli_and_result_values() {
2414 // Exercises the Pauli and Result branches of `lower_value_to_expr`.
2415 let source = indoc::indoc! {r#"
2416 namespace Test {
2417 operation Measure(op : Qubit => Unit, basis : Pauli) : Result {
2418 use q = Qubit();
2419 op(q);
2420 MResetZ(q)
2421 }
2422 operation DoH(q : Qubit) : Unit { H(q); }
2423 }
2424 "#};
2425 let caps = adaptive_capabilities();
2426 let (store, pkg, items) =
2427 compile_and_locate_items(source, &[("Measure", true), ("DoH", true)], caps);
2428
2429 let do_h = Value::Global(fir_id_for(pkg, items["DoH"]), FunctorApp::default());
2430 let args = Value::Tuple(
2431 vec![do_h, Value::Pauli(qsc_fir::fir::Pauli::Z)].into(),
2432 None,
2433 );
2434
2435 let qir = callable_args_to_qir(&store, pkg, items["Measure"], &args, caps);
2436 assert!(
2437 qir.contains("__quantum__qis__h__body"),
2438 "expected H gate in QIR:\n{qir}"
2439 );
2440}
2441
2442mod base_profile {
2443 use expect_test::expect;
2444 use qsc_data_structures::target::TargetCapabilityFlags;
2445
2446 use super::compile_source_to_qir;
2447 static CAPABILITIES: std::sync::LazyLock<TargetCapabilityFlags> =
2448 std::sync::LazyLock::new(TargetCapabilityFlags::empty);
2449
2450 #[test]
2451 fn simple() {
2452 let source = "namespace Test {
2453 import Std.Math.*;
2454 open QIR.Intrinsic;
2455 @EntryPoint()
2456 operation Main() : Result {
2457 use q = Qubit();
2458 let pi_over_two = 4.0 / 2.0;
2459 __quantum__qis__rz__body(pi_over_two, q);
2460 mutable some_angle = ArcSin(0.0);
2461 __quantum__qis__rz__body(some_angle, q);
2462 set some_angle = ArcCos(-1.0) / PI();
2463 __quantum__qis__rz__body(some_angle, q);
2464 __quantum__qis__mresetz__body(q)
2465 }
2466 }";
2467
2468 let qir = compile_source_to_qir(source, *CAPABILITIES);
2469 expect![[r#"
2470 %Result = type opaque
2471 %Qubit = type opaque
2472
2473 @0 = internal constant [4 x i8] c"0_r\00"
2474
2475 define i64 @ENTRYPOINT__main() #0 {
2476 block_0:
2477 call void @__quantum__rt__initialize(i8* null)
2478 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
2479 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
2480 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
2481 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
2482 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
2483 ret i64 0
2484 }
2485
2486 declare void @__quantum__rt__initialize(i8*)
2487
2488 declare void @__quantum__qis__rz__body(double, %Qubit*)
2489
2490 declare void @__quantum__rt__result_record_output(%Result*, i8*)
2491
2492 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
2493
2494 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" }
2495 attributes #1 = { "irreversible" }
2496
2497 ; module flags
2498
2499 !llvm.module.flags = !{!0, !1, !2, !3}
2500
2501 !0 = !{i32 1, !"qir_major_version", i32 1}
2502 !1 = !{i32 7, !"qir_minor_version", i32 0}
2503 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
2504 !3 = !{i32 1, !"dynamic_result_management", i1 false}
2505 "#]]
2506 .assert_eq(&qir);
2507 }
2508
2509 #[test]
2510 fn qubit_reuse_triggers_reindexing() {
2511 let source = "namespace Test {
2512 @EntryPoint()
2513 operation Main() : (Result, Result) {
2514 use q = Qubit();
2515 (MResetZ(q), MResetZ(q))
2516 }
2517 }";
2518 let qir = compile_source_to_qir(source, *CAPABILITIES);
2519 expect![[r#"
2520 %Result = type opaque
2521 %Qubit = type opaque
2522
2523 @0 = internal constant [4 x i8] c"0_t\00"
2524 @1 = internal constant [6 x i8] c"1_t0r\00"
2525 @2 = internal constant [6 x i8] c"2_t1r\00"
2526
2527 define i64 @ENTRYPOINT__main() #0 {
2528 block_0:
2529 call void @__quantum__rt__initialize(i8* null)
2530 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
2531 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
2532 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
2533 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
2534 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
2535 ret i64 0
2536 }
2537
2538 declare void @__quantum__rt__initialize(i8*)
2539
2540 declare void @__quantum__rt__tuple_record_output(i64, i8*)
2541
2542 declare void @__quantum__rt__result_record_output(%Result*, i8*)
2543
2544 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
2545
2546 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" }
2547 attributes #1 = { "irreversible" }
2548
2549 ; module flags
2550
2551 !llvm.module.flags = !{!0, !1, !2, !3}
2552
2553 !0 = !{i32 1, !"qir_major_version", i32 1}
2554 !1 = !{i32 7, !"qir_minor_version", i32 0}
2555 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
2556 !3 = !{i32 1, !"dynamic_result_management", i1 false}
2557 "#]].assert_eq(&qir);
2558 }
2559
2560 #[test]
2561 fn qubit_measurements_get_deferred() {
2562 let source = "namespace Test {
2563 @EntryPoint()
2564 operation Main() : Result[] {
2565 use (q0, q1) = (Qubit(), Qubit());
2566 X(q0);
2567 let r0 = MResetZ(q0);
2568 X(q1);
2569 let r1 = MResetZ(q1);
2570 [r0, r1]
2571 }
2572 }";
2573 let qir = compile_source_to_qir(source, *CAPABILITIES);
2574 expect![[r#"
2575 %Result = type opaque
2576 %Qubit = type opaque
2577
2578 @0 = internal constant [4 x i8] c"0_a\00"
2579 @1 = internal constant [6 x i8] c"1_a0r\00"
2580 @2 = internal constant [6 x i8] c"2_a1r\00"
2581
2582 define i64 @ENTRYPOINT__main() #0 {
2583 block_0:
2584 call void @__quantum__rt__initialize(i8* null)
2585 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
2586 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
2587 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
2588 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
2589 call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
2590 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
2591 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
2592 ret i64 0
2593 }
2594
2595 declare void @__quantum__rt__initialize(i8*)
2596
2597 declare void @__quantum__qis__x__body(%Qubit*)
2598
2599 declare void @__quantum__rt__array_record_output(i64, i8*)
2600
2601 declare void @__quantum__rt__result_record_output(%Result*, i8*)
2602
2603 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
2604
2605 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" }
2606 attributes #1 = { "irreversible" }
2607
2608 ; module flags
2609
2610 !llvm.module.flags = !{!0, !1, !2, !3}
2611
2612 !0 = !{i32 1, !"qir_major_version", i32 1}
2613 !1 = !{i32 7, !"qir_minor_version", i32 0}
2614 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
2615 !3 = !{i32 1, !"dynamic_result_management", i1 false}
2616 "#]].assert_eq(&qir);
2617 }
2618
2619 #[test]
2620 fn qubit_id_swap_results_in_different_id_usage() {
2621 let source = "namespace Test {
2622 @EntryPoint()
2623 operation Main() : (Result, Result) {
2624 use (q0, q1) = (Qubit(), Qubit());
2625 X(q0);
2626 Relabel([q0, q1], [q1, q0]);
2627 X(q1);
2628 (MResetZ(q0), MResetZ(q1))
2629 }
2630 }";
2631 let qir = compile_source_to_qir(source, *CAPABILITIES);
2632 expect![[r#"
2633 %Result = type opaque
2634 %Qubit = type opaque
2635
2636 @0 = internal constant [4 x i8] c"0_t\00"
2637 @1 = internal constant [6 x i8] c"1_t0r\00"
2638 @2 = internal constant [6 x i8] c"2_t1r\00"
2639
2640 define i64 @ENTRYPOINT__main() #0 {
2641 block_0:
2642 call void @__quantum__rt__initialize(i8* null)
2643 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
2644 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
2645 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
2646 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
2647 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
2648 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
2649 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
2650 ret i64 0
2651 }
2652
2653 declare void @__quantum__rt__initialize(i8*)
2654
2655 declare void @__quantum__qis__x__body(%Qubit*)
2656
2657 declare void @__quantum__rt__tuple_record_output(i64, i8*)
2658
2659 declare void @__quantum__rt__result_record_output(%Result*, i8*)
2660
2661 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
2662
2663 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="2" "required_num_results"="2" }
2664 attributes #1 = { "irreversible" }
2665
2666 ; module flags
2667
2668 !llvm.module.flags = !{!0, !1, !2, !3}
2669
2670 !0 = !{i32 1, !"qir_major_version", i32 1}
2671 !1 = !{i32 7, !"qir_minor_version", i32 0}
2672 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
2673 !3 = !{i32 1, !"dynamic_result_management", i1 false}
2674 "#]].assert_eq(&qir);
2675 }
2676
2677 #[test]
2678 fn qubit_id_swap_across_reset_uses_updated_ids() {
2679 let source = "namespace Test {
2680 @EntryPoint()
2681 operation Main() : (Result, Result) {
2682 {
2683 use (q0, q1) = (Qubit(), Qubit());
2684 X(q0);
2685 Relabel([q0, q1], [q1, q0]);
2686 X(q1);
2687 Reset(q0);
2688 Reset(q1);
2689 }
2690 use (q0, q1) = (Qubit(), Qubit());
2691 (MResetZ(q0), MResetZ(q1))
2692 }
2693 }";
2694 let qir = compile_source_to_qir(source, *CAPABILITIES);
2695 expect![[r#"
2696 %Result = type opaque
2697 %Qubit = type opaque
2698
2699 @0 = internal constant [4 x i8] c"0_t\00"
2700 @1 = internal constant [6 x i8] c"1_t0r\00"
2701 @2 = internal constant [6 x i8] c"2_t1r\00"
2702
2703 define i64 @ENTRYPOINT__main() #0 {
2704 block_0:
2705 call void @__quantum__rt__initialize(i8* null)
2706 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
2707 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
2708 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
2709 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
2710 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
2711 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
2712 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
2713 ret i64 0
2714 }
2715
2716 declare void @__quantum__rt__initialize(i8*)
2717
2718 declare void @__quantum__qis__x__body(%Qubit*)
2719
2720 declare void @__quantum__rt__tuple_record_output(i64, i8*)
2721
2722 declare void @__quantum__rt__result_record_output(%Result*, i8*)
2723
2724 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
2725
2726 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="3" "required_num_results"="2" }
2727 attributes #1 = { "irreversible" }
2728
2729 ; module flags
2730
2731 !llvm.module.flags = !{!0, !1, !2, !3}
2732
2733 !0 = !{i32 1, !"qir_major_version", i32 1}
2734 !1 = !{i32 7, !"qir_minor_version", i32 0}
2735 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
2736 !3 = !{i32 1, !"dynamic_result_management", i1 false}
2737 "#]].assert_eq(&qir);
2738 }
2739
2740 #[test]
2741 fn noise_intrinsic_generates_correct_qir() {
2742 let source = "namespace Test {
2743 operation Main() : Result {
2744 use q = Qubit();
2745 test_noise_intrinsic(q);
2746 MResetZ(q)
2747 }
2748
2749 @NoiseIntrinsic()
2750 operation test_noise_intrinsic(target: Qubit) : Unit {
2751 body intrinsic;
2752 }
2753 }";
2754
2755 let qir = compile_source_to_qir(source, *CAPABILITIES);
2756 expect![[r#"
2757 %Result = type opaque
2758 %Qubit = type opaque
2759
2760 @0 = internal constant [4 x i8] c"0_r\00"
2761
2762 define i64 @ENTRYPOINT__main() #0 {
2763 block_0:
2764 call void @__quantum__rt__initialize(i8* null)
2765 call void @test_noise_intrinsic(%Qubit* inttoptr (i64 0 to %Qubit*))
2766 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
2767 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
2768 ret i64 0
2769 }
2770
2771 declare void @__quantum__rt__initialize(i8*)
2772
2773 declare void @test_noise_intrinsic(%Qubit*) #2
2774
2775 declare void @__quantum__rt__result_record_output(%Result*, i8*)
2776
2777 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
2778
2779 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" }
2780 attributes #1 = { "irreversible" }
2781 attributes #2 = { "qdk_noise" }
2782
2783 ; module flags
2784
2785 !llvm.module.flags = !{!0, !1, !2, !3}
2786
2787 !0 = !{i32 1, !"qir_major_version", i32 1}
2788 !1 = !{i32 7, !"qir_minor_version", i32 0}
2789 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
2790 !3 = !{i32 1, !"dynamic_result_management", i1 false}
2791 "#]].assert_eq(&qir);
2792 }
2793}
2794
2795mod adaptive_profile {
2796 use super::compile_source_to_qir;
2797 use expect_test::expect;
2798 use qsc_data_structures::target::TargetCapabilityFlags;
2799 static CAPABILITIES: std::sync::LazyLock<TargetCapabilityFlags> =
2800 std::sync::LazyLock::new(|| TargetCapabilityFlags::Adaptive);
2801
2802 #[test]
2803 fn simple() {
2804 let source = "namespace Test {
2805 import Std.Math.*;
2806 open QIR.Intrinsic;
2807 @EntryPoint()
2808 operation Main() : Result {
2809 use q = Qubit();
2810 let pi_over_two = 4.0 / 2.0;
2811 __quantum__qis__rz__body(pi_over_two, q);
2812 mutable some_angle = ArcSin(0.0);
2813 __quantum__qis__rz__body(some_angle, q);
2814 set some_angle = ArcCos(-1.0) / PI();
2815 __quantum__qis__rz__body(some_angle, q);
2816 __quantum__qis__mresetz__body(q)
2817 }
2818 }";
2819 let qir = compile_source_to_qir(source, *CAPABILITIES);
2820 expect![[r#"
2821 %Result = type opaque
2822 %Qubit = type opaque
2823
2824 @0 = internal constant [4 x i8] c"0_r\00"
2825
2826 define i64 @ENTRYPOINT__main() #0 {
2827 block_0:
2828 call void @__quantum__rt__initialize(i8* null)
2829 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
2830 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
2831 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
2832 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
2833 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
2834 ret i64 0
2835 }
2836
2837 declare void @__quantum__rt__initialize(i8*)
2838
2839 declare void @__quantum__qis__rz__body(double, %Qubit*)
2840
2841 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
2842
2843 declare void @__quantum__rt__result_record_output(%Result*, i8*)
2844
2845 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
2846 attributes #1 = { "irreversible" }
2847
2848 ; module flags
2849
2850 !llvm.module.flags = !{!0, !1, !2, !3}
2851
2852 !0 = !{i32 1, !"qir_major_version", i32 1}
2853 !1 = !{i32 7, !"qir_minor_version", i32 0}
2854 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
2855 !3 = !{i32 1, !"dynamic_result_management", i1 false}
2856 "#]]
2857 .assert_eq(&qir);
2858 }
2859
2860 #[test]
2861 fn noise_intrinsic_generates_correct_qir() {
2862 let source = "namespace Test {
2863 operation Main() : Result {
2864 use q = Qubit();
2865 test_noise_intrinsic(q);
2866 MResetZ(q)
2867 }
2868
2869 @NoiseIntrinsic()
2870 operation test_noise_intrinsic(target: Qubit) : Unit {
2871 body intrinsic;
2872 }
2873 }";
2874
2875 let qir = compile_source_to_qir(source, *CAPABILITIES);
2876 expect![[r#"
2877 %Result = type opaque
2878 %Qubit = type opaque
2879
2880 @0 = internal constant [4 x i8] c"0_r\00"
2881
2882 define i64 @ENTRYPOINT__main() #0 {
2883 block_0:
2884 call void @__quantum__rt__initialize(i8* null)
2885 call void @test_noise_intrinsic(%Qubit* inttoptr (i64 0 to %Qubit*))
2886 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
2887 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
2888 ret i64 0
2889 }
2890
2891 declare void @__quantum__rt__initialize(i8*)
2892
2893 declare void @test_noise_intrinsic(%Qubit*) #2
2894
2895 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
2896
2897 declare void @__quantum__rt__result_record_output(%Result*, i8*)
2898
2899 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
2900 attributes #1 = { "irreversible" }
2901 attributes #2 = { "qdk_noise" }
2902
2903 ; module flags
2904
2905 !llvm.module.flags = !{!0, !1, !2, !3}
2906
2907 !0 = !{i32 1, !"qir_major_version", i32 1}
2908 !1 = !{i32 7, !"qir_minor_version", i32 0}
2909 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
2910 !3 = !{i32 1, !"dynamic_result_management", i1 false}
2911 "#]].assert_eq(&qir);
2912 }
2913
2914 #[test]
2915 fn custom_measurement_generates_correct_qir() {
2916 let source = "namespace Test {
2917 operation Main() : Result {
2918 use q = Qubit();
2919 H(q);
2920 __quantum__qis__mx__body(q)
2921 }
2922
2923 @Measurement()
2924 operation __quantum__qis__mx__body(target: Qubit) : Result {
2925 body intrinsic;
2926 }
2927 }";
2928 let qir = compile_source_to_qir(source, *CAPABILITIES);
2929 expect![[r#"
2930 %Result = type opaque
2931 %Qubit = type opaque
2932
2933 @0 = internal constant [4 x i8] c"0_r\00"
2934
2935 define i64 @ENTRYPOINT__main() #0 {
2936 block_0:
2937 call void @__quantum__rt__initialize(i8* null)
2938 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
2939 call void @__quantum__qis__mx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
2940 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
2941 ret i64 0
2942 }
2943
2944 declare void @__quantum__rt__initialize(i8*)
2945
2946 declare void @__quantum__qis__h__body(%Qubit*)
2947
2948 declare void @__quantum__qis__mx__body(%Qubit*, %Result*) #1
2949
2950 declare void @__quantum__rt__result_record_output(%Result*, i8*)
2951
2952 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
2953 attributes #1 = { "irreversible" }
2954
2955 ; module flags
2956
2957 !llvm.module.flags = !{!0, !1, !2, !3}
2958
2959 !0 = !{i32 1, !"qir_major_version", i32 1}
2960 !1 = !{i32 7, !"qir_minor_version", i32 0}
2961 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
2962 !3 = !{i32 1, !"dynamic_result_management", i1 false}
2963 "#]].assert_eq(&qir);
2964 }
2965
2966 #[test]
2967 fn custom_joint_measurement_generates_correct_qir() {
2968 let source = "namespace Test {
2969 operation Main() : (Result, Result) {
2970 use q1 = Qubit();
2971 use q2 = Qubit();
2972 H(q1);
2973 H(q2);
2974 __quantum__qis__mzz__body(q1, q2)
2975 }
2976
2977 @Measurement()
2978 operation __quantum__qis__mzz__body(q1: Qubit, q2: Qubit) : (Result, Result) {
2979 body intrinsic;
2980 }
2981 }";
2982 let qir = compile_source_to_qir(source, *CAPABILITIES);
2983 expect![[r#"
2984 %Result = type opaque
2985 %Qubit = type opaque
2986
2987 @0 = internal constant [4 x i8] c"0_t\00"
2988 @1 = internal constant [6 x i8] c"1_t0r\00"
2989 @2 = internal constant [6 x i8] c"2_t1r\00"
2990
2991 define i64 @ENTRYPOINT__main() #0 {
2992 block_0:
2993 call void @__quantum__rt__initialize(i8* null)
2994 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
2995 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))
2996 call void @__quantum__qis__mzz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*), %Result* inttoptr (i64 1 to %Result*))
2997 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
2998 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
2999 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
3000 ret i64 0
3001 }
3002
3003 declare void @__quantum__rt__initialize(i8*)
3004
3005 declare void @__quantum__qis__h__body(%Qubit*)
3006
3007 declare void @__quantum__qis__mzz__body(%Qubit*, %Qubit*, %Result*, %Result*) #1
3008
3009 declare void @__quantum__rt__tuple_record_output(i64, i8*)
3010
3011 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3012
3013 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
3014 attributes #1 = { "irreversible" }
3015
3016 ; module flags
3017
3018 !llvm.module.flags = !{!0, !1, !2, !3}
3019
3020 !0 = !{i32 1, !"qir_major_version", i32 1}
3021 !1 = !{i32 7, !"qir_minor_version", i32 0}
3022 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3023 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3024 "#]].assert_eq(&qir);
3025 }
3026
3027 #[test]
3028 fn qubit_measurements_not_deferred() {
3029 let source = "namespace Test {
3030 @EntryPoint()
3031 operation Main() : Result[] {
3032 use (q0, q1) = (Qubit(), Qubit());
3033 X(q0);
3034 let r0 = MResetZ(q0);
3035 X(q1);
3036 let r1 = MResetZ(q1);
3037 [r0, r1]
3038 }
3039 }";
3040 let qir = compile_source_to_qir(source, *CAPABILITIES);
3041 expect![[r#"
3042 %Result = type opaque
3043 %Qubit = type opaque
3044
3045 @0 = internal constant [4 x i8] c"0_a\00"
3046 @1 = internal constant [6 x i8] c"1_a0r\00"
3047 @2 = internal constant [6 x i8] c"2_a1r\00"
3048
3049 define i64 @ENTRYPOINT__main() #0 {
3050 block_0:
3051 call void @__quantum__rt__initialize(i8* null)
3052 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3053 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3054 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
3055 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
3056 call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3057 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
3058 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
3059 ret i64 0
3060 }
3061
3062 declare void @__quantum__rt__initialize(i8*)
3063
3064 declare void @__quantum__qis__x__body(%Qubit*)
3065
3066 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3067
3068 declare void @__quantum__rt__array_record_output(i64, i8*)
3069
3070 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3071
3072 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
3073 attributes #1 = { "irreversible" }
3074
3075 ; module flags
3076
3077 !llvm.module.flags = !{!0, !1, !2, !3}
3078
3079 !0 = !{i32 1, !"qir_major_version", i32 1}
3080 !1 = !{i32 7, !"qir_minor_version", i32 0}
3081 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3082 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3083 "#]].assert_eq(&qir);
3084 }
3085}
3086
3087mod adaptive_ri_profile {
3088
3089 use expect_test::expect;
3090 use qsc_data_structures::target::TargetCapabilityFlags;
3091
3092 use super::{compile_source_to_qir, compile_source_to_qir_from_ast, compile_source_to_rir};
3093 static CAPABILITIES: std::sync::LazyLock<TargetCapabilityFlags> =
3094 std::sync::LazyLock::new(|| {
3095 TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::IntegerComputations
3096 });
3097
3098 fn terminal_result_return_with_qubit_cleanup_source() -> &'static str {
3099 indoc::indoc! {r#"
3100 namespace Test {
3101 @EntryPoint()
3102 operation Main() : Result {
3103 use q = Qubit();
3104 let r = M(q);
3105 Reset(q);
3106 return r;
3107 }
3108 }
3109 "#}
3110 }
3111
3112 fn assert_terminal_result_return_with_qubit_cleanup_qir(qir: &str) {
3113 expect![[r#"
3114 %Result = type opaque
3115 %Qubit = type opaque
3116
3117 @0 = internal constant [4 x i8] c"0_r\00"
3118
3119 define i64 @ENTRYPOINT__main() #0 {
3120 block_0:
3121 call void @__quantum__rt__initialize(i8* null)
3122 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3123 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3124 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3125 ret i64 0
3126 }
3127
3128 declare void @__quantum__rt__initialize(i8*)
3129
3130 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
3131
3132 declare void @__quantum__qis__reset__body(%Qubit*) #1
3133
3134 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3135
3136 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
3137 attributes #1 = { "irreversible" }
3138
3139 ; module flags
3140
3141 !llvm.module.flags = !{!0, !1, !2, !3, !4}
3142
3143 !0 = !{i32 1, !"qir_major_version", i32 1}
3144 !1 = !{i32 7, !"qir_minor_version", i32 0}
3145 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3146 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3147 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3148 "#]]
3149 .assert_eq(qir);
3150 }
3151
3152 fn assert_terminal_result_return_with_qubit_cleanup_rir(program: &str, form: &str) {
3153 assert!(
3154 program.contains("name: __quantum__qis__m__body"),
3155 "{form} RIR should include the measurement callable"
3156 );
3157 assert!(
3158 program.contains("name: __quantum__qis__reset__body"),
3159 "{form} RIR should include the cleanup reset callable"
3160 );
3161 assert!(
3162 program.contains("name: __quantum__rt__result_record_output"),
3163 "{form} RIR should include result output recording"
3164 );
3165 assert!(
3166 program.contains("num_qubits: 1"),
3167 "{form} RIR should keep a single allocated qubit"
3168 );
3169 assert!(
3170 program.contains("num_results: 1"),
3171 "{form} RIR should keep a single returned result"
3172 );
3173
3174 let measurement_call = program
3175 .find("args( Qubit(0), Result(0), )")
3176 .unwrap_or_else(|| panic!("{form} RIR should contain the measurement call"));
3177 let reset_call = program
3178 .find("args( Qubit(0), )")
3179 .unwrap_or_else(|| panic!("{form} RIR should contain the cleanup reset call"));
3180 let output_call = program
3181 .find("args( Result(0), Tag(")
3182 .unwrap_or_else(|| panic!("{form} RIR should record the returned result"));
3183
3184 assert!(
3185 measurement_call < reset_call && reset_call < output_call,
3186 "{form} RIR should measure, reset, and then record the returned result"
3187 );
3188 }
3189
3190 #[test]
3191 fn simple() {
3192 let source = "namespace Test {
3193 import Std.Math.*;
3194 open QIR.Intrinsic;
3195 @EntryPoint()
3196 operation Main() : Result {
3197 use q = Qubit();
3198 let pi_over_two = 4.0 / 2.0;
3199 __quantum__qis__rz__body(pi_over_two, q);
3200 mutable some_angle = ArcSin(0.0);
3201 __quantum__qis__rz__body(some_angle, q);
3202 set some_angle = ArcCos(-1.0) / PI();
3203 __quantum__qis__rz__body(some_angle, q);
3204 __quantum__qis__mresetz__body(q)
3205 }
3206 }";
3207 let qir = compile_source_to_qir(source, *CAPABILITIES);
3208 expect![[r#"
3209 %Result = type opaque
3210 %Qubit = type opaque
3211
3212 @0 = internal constant [4 x i8] c"0_r\00"
3213
3214 define i64 @ENTRYPOINT__main() #0 {
3215 block_0:
3216 call void @__quantum__rt__initialize(i8* null)
3217 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
3218 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
3219 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
3220 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3221 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3222 ret i64 0
3223 }
3224
3225 declare void @__quantum__rt__initialize(i8*)
3226
3227 declare void @__quantum__qis__rz__body(double, %Qubit*)
3228
3229 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3230
3231 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3232
3233 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
3234 attributes #1 = { "irreversible" }
3235
3236 ; module flags
3237
3238 !llvm.module.flags = !{!0, !1, !2, !3, !4}
3239
3240 !0 = !{i32 1, !"qir_major_version", i32 1}
3241 !1 = !{i32 7, !"qir_minor_version", i32 0}
3242 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3243 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3244 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3245 "#]]
3246 .assert_eq(&qir);
3247 }
3248
3249 #[test]
3250 fn qubit_reuse_allowed() {
3251 let source = "namespace Test {
3252 @EntryPoint()
3253 operation Main() : (Result, Result) {
3254 use q = Qubit();
3255 (MResetZ(q), MResetZ(q))
3256 }
3257 }";
3258 let qir = compile_source_to_qir(source, *CAPABILITIES);
3259 expect![[r#"
3260 %Result = type opaque
3261 %Qubit = type opaque
3262
3263 @0 = internal constant [4 x i8] c"0_t\00"
3264 @1 = internal constant [6 x i8] c"1_t0r\00"
3265 @2 = internal constant [6 x i8] c"2_t1r\00"
3266
3267 define i64 @ENTRYPOINT__main() #0 {
3268 block_0:
3269 call void @__quantum__rt__initialize(i8* null)
3270 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3271 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
3272 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3273 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
3274 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
3275 ret i64 0
3276 }
3277
3278 declare void @__quantum__rt__initialize(i8*)
3279
3280 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3281
3282 declare void @__quantum__rt__tuple_record_output(i64, i8*)
3283
3284 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3285
3286 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="2" }
3287 attributes #1 = { "irreversible" }
3288
3289 ; module flags
3290
3291 !llvm.module.flags = !{!0, !1, !2, !3, !4}
3292
3293 !0 = !{i32 1, !"qir_major_version", i32 1}
3294 !1 = !{i32 7, !"qir_minor_version", i32 0}
3295 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3296 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3297 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3298 "#]].assert_eq(&qir);
3299 }
3300
3301 #[test]
3302 fn qubit_measurements_not_deferred() {
3303 let source = "namespace Test {
3304 @EntryPoint()
3305 operation Main() : Result[] {
3306 use (q0, q1) = (Qubit(), Qubit());
3307 X(q0);
3308 let r0 = MResetZ(q0);
3309 X(q1);
3310 let r1 = MResetZ(q1);
3311 [r0, r1]
3312 }
3313 }";
3314 let qir = compile_source_to_qir(source, *CAPABILITIES);
3315 expect![[r#"
3316 %Result = type opaque
3317 %Qubit = type opaque
3318
3319 @0 = internal constant [4 x i8] c"0_a\00"
3320 @1 = internal constant [6 x i8] c"1_a0r\00"
3321 @2 = internal constant [6 x i8] c"2_a1r\00"
3322
3323 define i64 @ENTRYPOINT__main() #0 {
3324 block_0:
3325 call void @__quantum__rt__initialize(i8* null)
3326 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3327 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3328 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
3329 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
3330 call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3331 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
3332 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
3333 ret i64 0
3334 }
3335
3336 declare void @__quantum__rt__initialize(i8*)
3337
3338 declare void @__quantum__qis__x__body(%Qubit*)
3339
3340 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3341
3342 declare void @__quantum__rt__array_record_output(i64, i8*)
3343
3344 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3345
3346 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
3347 attributes #1 = { "irreversible" }
3348
3349 ; module flags
3350
3351 !llvm.module.flags = !{!0, !1, !2, !3, !4}
3352
3353 !0 = !{i32 1, !"qir_major_version", i32 1}
3354 !1 = !{i32 7, !"qir_minor_version", i32 0}
3355 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3356 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3357 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3358 "#]].assert_eq(&qir);
3359 }
3360
3361 #[test]
3362 fn qubit_id_swap_results_in_different_id_usage() {
3363 let source = "namespace Test {
3364 @EntryPoint()
3365 operation Main() : (Result, Result) {
3366 use (q0, q1) = (Qubit(), Qubit());
3367 X(q0);
3368 Relabel([q0, q1], [q1, q0]);
3369 X(q1);
3370 (MResetZ(q0), MResetZ(q1))
3371 }
3372 }";
3373 let qir = compile_source_to_qir(source, *CAPABILITIES);
3374 expect![[r#"
3375 %Result = type opaque
3376 %Qubit = type opaque
3377
3378 @0 = internal constant [4 x i8] c"0_t\00"
3379 @1 = internal constant [6 x i8] c"1_t0r\00"
3380 @2 = internal constant [6 x i8] c"2_t1r\00"
3381
3382 define i64 @ENTRYPOINT__main() #0 {
3383 block_0:
3384 call void @__quantum__rt__initialize(i8* null)
3385 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3386 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3387 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3388 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
3389 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3390 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
3391 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
3392 ret i64 0
3393 }
3394
3395 declare void @__quantum__rt__initialize(i8*)
3396
3397 declare void @__quantum__qis__x__body(%Qubit*)
3398
3399 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3400
3401 declare void @__quantum__rt__tuple_record_output(i64, i8*)
3402
3403 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3404
3405 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
3406 attributes #1 = { "irreversible" }
3407
3408 ; module flags
3409
3410 !llvm.module.flags = !{!0, !1, !2, !3, !4}
3411
3412 !0 = !{i32 1, !"qir_major_version", i32 1}
3413 !1 = !{i32 7, !"qir_minor_version", i32 0}
3414 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3415 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3416 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3417 "#]].assert_eq(&qir);
3418 }
3419
3420 #[test]
3421 fn qubit_id_swap_across_reset_uses_updated_ids() {
3422 let source = "namespace Test {
3423 @EntryPoint()
3424 operation Main() : (Result, Result) {
3425 {
3426 use (q0, q1) = (Qubit(), Qubit());
3427 X(q0);
3428 Relabel([q0, q1], [q1, q0]);
3429 X(q1);
3430 Reset(q0);
3431 Reset(q1);
3432 }
3433 use (q0, q1) = (Qubit(), Qubit());
3434 (MResetZ(q0), MResetZ(q1))
3435 }
3436 }";
3437 let qir = compile_source_to_qir(source, *CAPABILITIES);
3438 expect![[r#"
3439 %Result = type opaque
3440 %Qubit = type opaque
3441
3442 @0 = internal constant [4 x i8] c"0_t\00"
3443 @1 = internal constant [6 x i8] c"1_t0r\00"
3444 @2 = internal constant [6 x i8] c"2_t1r\00"
3445
3446 define i64 @ENTRYPOINT__main() #0 {
3447 block_0:
3448 call void @__quantum__rt__initialize(i8* null)
3449 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3450 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3451 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 1 to %Qubit*))
3452 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3453 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3454 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
3455 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3456 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
3457 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
3458 ret i64 0
3459 }
3460
3461 declare void @__quantum__rt__initialize(i8*)
3462
3463 declare void @__quantum__qis__x__body(%Qubit*)
3464
3465 declare void @__quantum__qis__reset__body(%Qubit*) #1
3466
3467 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3468
3469 declare void @__quantum__rt__tuple_record_output(i64, i8*)
3470
3471 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3472
3473 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
3474 attributes #1 = { "irreversible" }
3475
3476 ; module flags
3477
3478 !llvm.module.flags = !{!0, !1, !2, !3, !4}
3479
3480 !0 = !{i32 1, !"qir_major_version", i32 1}
3481 !1 = !{i32 7, !"qir_minor_version", i32 0}
3482 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3483 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3484 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3485 "#]].assert_eq(&qir);
3486 }
3487
3488 #[test]
3489 fn qubit_id_swap_with_out_of_order_release_uses_correct_ids() {
3490 let source = "namespace Test {
3491 @EntryPoint()
3492 operation Main() : (Result, Result) {
3493 let q0 = QIR.Runtime.__quantum__rt__qubit_allocate();
3494 let q1 = QIR.Runtime.__quantum__rt__qubit_allocate();
3495 let q2 = QIR.Runtime.__quantum__rt__qubit_allocate();
3496 X(q0);
3497 X(q1);
3498 X(q2);
3499 Relabel([q0, q1], [q1, q0]);
3500 QIR.Runtime.__quantum__rt__qubit_release(q0);
3501 let q3 = QIR.Runtime.__quantum__rt__qubit_allocate();
3502 X(q3);
3503 (MResetZ(q3), MResetZ(q1))
3504 }
3505 }";
3506 let qir = compile_source_to_qir(source, *CAPABILITIES);
3507 expect![[r#"
3508 %Result = type opaque
3509 %Qubit = type opaque
3510
3511 @0 = internal constant [4 x i8] c"0_t\00"
3512 @1 = internal constant [6 x i8] c"1_t0r\00"
3513 @2 = internal constant [6 x i8] c"2_t1r\00"
3514
3515 define i64 @ENTRYPOINT__main() #0 {
3516 block_0:
3517 call void @__quantum__rt__initialize(i8* null)
3518 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3519 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
3520 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
3521 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
3522 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3523 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
3524 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3525 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
3526 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
3527 ret i64 0
3528 }
3529
3530 declare void @__quantum__rt__initialize(i8*)
3531
3532 declare void @__quantum__qis__x__body(%Qubit*)
3533
3534 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3535
3536 declare void @__quantum__rt__tuple_record_output(i64, i8*)
3537
3538 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3539
3540 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="2" }
3541 attributes #1 = { "irreversible" }
3542
3543 ; module flags
3544
3545 !llvm.module.flags = !{!0, !1, !2, !3, !4}
3546
3547 !0 = !{i32 1, !"qir_major_version", i32 1}
3548 !1 = !{i32 7, !"qir_minor_version", i32 0}
3549 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3550 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3551 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3552 "#]].assert_eq(&qir);
3553 }
3554
3555 #[test]
3556 fn dynamic_integer_with_branch_and_phi_supported() {
3557 let source = "namespace Test {
3558 @EntryPoint()
3559 operation Main() : Int {
3560 use q = Qubit();
3561 H(q);
3562 MResetZ(q) == Zero ? 0 | 1
3563 }
3564 }";
3565 let qir = compile_source_to_qir(source, *CAPABILITIES);
3566 expect![[r#"
3567 %Result = type opaque
3568 %Qubit = type opaque
3569
3570 @0 = internal constant [4 x i8] c"0_i\00"
3571
3572 define i64 @ENTRYPOINT__main() #0 {
3573 block_0:
3574 call void @__quantum__rt__initialize(i8* null)
3575 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3576 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3577 %var_0 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
3578 %var_1 = icmp eq i1 %var_0, false
3579 br i1 %var_1, label %block_1, label %block_2
3580 block_1:
3581 br label %block_3
3582 block_2:
3583 br label %block_3
3584 block_3:
3585 %var_4 = phi i64 [0, %block_1], [1, %block_2]
3586 call void @__quantum__rt__int_record_output(i64 %var_4, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3587 ret i64 0
3588 }
3589
3590 declare void @__quantum__rt__initialize(i8*)
3591
3592 declare void @__quantum__qis__h__body(%Qubit*)
3593
3594 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3595
3596 declare i1 @__quantum__rt__read_result(%Result*)
3597
3598 declare void @__quantum__rt__int_record_output(i64, i8*)
3599
3600 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
3601 attributes #1 = { "irreversible" }
3602
3603 ; module flags
3604
3605 !llvm.module.flags = !{!0, !1, !2, !3, !4}
3606
3607 !0 = !{i32 1, !"qir_major_version", i32 1}
3608 !1 = !{i32 7, !"qir_minor_version", i32 0}
3609 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3610 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3611 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3612 "#]].assert_eq(&qir);
3613 }
3614
3615 #[test]
3616 fn custom_reset_generates_correct_qir() {
3617 let source = "namespace Test {
3618 operation Main() : Result {
3619 use q = Qubit();
3620 __quantum__qis__custom_reset__body(q);
3621 M(q)
3622 }
3623
3624 @Reset()
3625 operation __quantum__qis__custom_reset__body(target: Qubit) : Unit {
3626 body intrinsic;
3627 }
3628 }";
3629 let qir = compile_source_to_qir(source, *CAPABILITIES);
3630 expect![[r#"
3631 %Result = type opaque
3632 %Qubit = type opaque
3633
3634 @0 = internal constant [4 x i8] c"0_r\00"
3635
3636 define i64 @ENTRYPOINT__main() #0 {
3637 block_0:
3638 call void @__quantum__rt__initialize(i8* null)
3639 call void @__quantum__qis__custom_reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3640 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3641 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3642 ret i64 0
3643 }
3644
3645 declare void @__quantum__rt__initialize(i8*)
3646
3647 declare void @__quantum__qis__custom_reset__body(%Qubit*) #1
3648
3649 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
3650
3651 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3652
3653 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
3654 attributes #1 = { "irreversible" }
3655
3656 ; module flags
3657
3658 !llvm.module.flags = !{!0, !1, !2, !3, !4}
3659
3660 !0 = !{i32 1, !"qir_major_version", i32 1}
3661 !1 = !{i32 7, !"qir_minor_version", i32 0}
3662 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3663 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3664 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3665 "#]]
3666 .assert_eq(&qir);
3667 }
3668
3669 #[test]
3670 fn terminal_result_return_with_qubit_cleanup_generates_correct_qir() {
3671 let qir = compile_source_to_qir(
3672 terminal_result_return_with_qubit_cleanup_source(),
3673 *CAPABILITIES,
3674 );
3675 assert_terminal_result_return_with_qubit_cleanup_qir(&qir);
3676 }
3677
3678 #[test]
3679 fn terminal_result_return_with_qubit_cleanup_generates_correct_qir_from_ast() {
3680 let qir = compile_source_to_qir_from_ast(
3681 terminal_result_return_with_qubit_cleanup_source(),
3682 *CAPABILITIES,
3683 );
3684 assert_terminal_result_return_with_qubit_cleanup_qir(&qir);
3685 }
3686
3687 #[test]
3688 fn terminal_result_return_with_qubit_cleanup_generates_rir() {
3689 let rir = compile_source_to_rir(
3690 terminal_result_return_with_qubit_cleanup_source(),
3691 *CAPABILITIES,
3692 );
3693 let [raw, ssa] = rir.as_slice() else {
3694 panic!("expected raw and SSA RIR programs");
3695 };
3696
3697 assert_terminal_result_return_with_qubit_cleanup_rir(raw, "raw");
3698 assert_terminal_result_return_with_qubit_cleanup_rir(ssa, "ssa");
3699 }
3700}
3701
3702mod adaptive_rif_profile {
3703 use super::compile_source_to_qir;
3704 use expect_test::expect;
3705 use qsc_data_structures::target::TargetCapabilityFlags;
3706 static CAPABILITIES: std::sync::LazyLock<TargetCapabilityFlags> =
3707 std::sync::LazyLock::new(|| {
3708 TargetCapabilityFlags::Adaptive
3709 | TargetCapabilityFlags::IntegerComputations
3710 | TargetCapabilityFlags::FloatingPointComputations
3711 });
3712
3713 #[test]
3714 fn simple() {
3715 let source = "namespace Test {
3716 import Std.Math.*;
3717 open QIR.Intrinsic;
3718 @EntryPoint()
3719 operation Main() : Result {
3720 use q = Qubit();
3721 let pi_over_two = 4.0 / 2.0;
3722 __quantum__qis__rz__body(pi_over_two, q);
3723 mutable some_angle = ArcSin(0.0);
3724 __quantum__qis__rz__body(some_angle, q);
3725 set some_angle = ArcCos(-1.0) / PI();
3726 __quantum__qis__rz__body(some_angle, q);
3727 __quantum__qis__mresetz__body(q)
3728 }
3729 }";
3730 let qir = compile_source_to_qir(source, *CAPABILITIES);
3731 expect![[r#"
3732 %Result = type opaque
3733 %Qubit = type opaque
3734
3735 @0 = internal constant [4 x i8] c"0_r\00"
3736
3737 define i64 @ENTRYPOINT__main() #0 {
3738 block_0:
3739 call void @__quantum__rt__initialize(i8* null)
3740 call void @__quantum__qis__rz__body(double 2.0, %Qubit* inttoptr (i64 0 to %Qubit*))
3741 call void @__quantum__qis__rz__body(double 0.0, %Qubit* inttoptr (i64 0 to %Qubit*))
3742 call void @__quantum__qis__rz__body(double 1.0, %Qubit* inttoptr (i64 0 to %Qubit*))
3743 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3744 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3745 ret i64 0
3746 }
3747
3748 declare void @__quantum__rt__initialize(i8*)
3749
3750 declare void @__quantum__qis__rz__body(double, %Qubit*)
3751
3752 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3753
3754 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3755
3756 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
3757 attributes #1 = { "irreversible" }
3758
3759 ; module flags
3760
3761 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
3762
3763 !0 = !{i32 1, !"qir_major_version", i32 1}
3764 !1 = !{i32 7, !"qir_minor_version", i32 0}
3765 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3766 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3767 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3768 !5 = !{i32 5, !"float_computations", !{!"double"}}
3769 "#]]
3770 .assert_eq(&qir);
3771 }
3772
3773 #[test]
3774 fn tuple_comparison_generates_qir_after_pipeline() {
3775 let qir = compile_source_to_qir(
3776 indoc::indoc! {r#"
3777 namespace Test {
3778 @EntryPoint()
3779 operation Main() : Bool {
3780 use (q0, q1) = (Qubit(), Qubit());
3781 let lhs = (MResetZ(q0), MResetZ(q1));
3782 lhs == (Zero, Zero)
3783 }
3784 }
3785 "#},
3786 *CAPABILITIES,
3787 );
3788
3789 expect![[r#"
3790 %Result = type opaque
3791 %Qubit = type opaque
3792
3793 @0 = internal constant [4 x i8] c"0_b\00"
3794
3795 define i64 @ENTRYPOINT__main() #0 {
3796 block_0:
3797 call void @__quantum__rt__initialize(i8* null)
3798 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3799 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
3800 %var_0 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
3801 %var_1 = icmp eq i1 %var_0, false
3802 br i1 %var_1, label %block_1, label %block_2
3803 block_1:
3804 %var_3 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 1 to %Result*))
3805 %var_4 = icmp eq i1 %var_3, false
3806 br label %block_2
3807 block_2:
3808 %var_6 = phi i1 [false, %block_0], [%var_4, %block_1]
3809 call void @__quantum__rt__bool_record_output(i1 %var_6, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3810 ret i64 0
3811 }
3812
3813 declare void @__quantum__rt__initialize(i8*)
3814
3815 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3816
3817 declare i1 @__quantum__rt__read_result(%Result*)
3818
3819 declare void @__quantum__rt__bool_record_output(i1, i8*)
3820
3821 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
3822 attributes #1 = { "irreversible" }
3823
3824 ; module flags
3825
3826 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
3827
3828 !0 = !{i32 1, !"qir_major_version", i32 1}
3829 !1 = !{i32 7, !"qir_minor_version", i32 0}
3830 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3831 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3832 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3833 !5 = !{i32 5, !"float_computations", !{!"double"}}
3834 "#]]
3835 .assert_eq(&qir);
3836 }
3837
3838 #[test]
3839 fn qubit_reuse_allowed() {
3840 let source = "namespace Test {
3841 @EntryPoint()
3842 operation Main() : (Result, Result) {
3843 use q = Qubit();
3844 (MResetZ(q), MResetZ(q))
3845 }
3846 }";
3847 let qir = compile_source_to_qir(source, *CAPABILITIES);
3848 expect![[r#"
3849 %Result = type opaque
3850 %Qubit = type opaque
3851
3852 @0 = internal constant [4 x i8] c"0_t\00"
3853 @1 = internal constant [6 x i8] c"1_t0r\00"
3854 @2 = internal constant [6 x i8] c"2_t1r\00"
3855
3856 define i64 @ENTRYPOINT__main() #0 {
3857 block_0:
3858 call void @__quantum__rt__initialize(i8* null)
3859 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3860 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
3861 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3862 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
3863 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
3864 ret i64 0
3865 }
3866
3867 declare void @__quantum__rt__initialize(i8*)
3868
3869 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3870
3871 declare void @__quantum__rt__tuple_record_output(i64, i8*)
3872
3873 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3874
3875 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="2" }
3876 attributes #1 = { "irreversible" }
3877
3878 ; module flags
3879
3880 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
3881
3882 !0 = !{i32 1, !"qir_major_version", i32 1}
3883 !1 = !{i32 7, !"qir_minor_version", i32 0}
3884 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3885 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3886 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3887 !5 = !{i32 5, !"float_computations", !{!"double"}}
3888 "#]].assert_eq(&qir);
3889 }
3890
3891 #[test]
3892 fn qubit_measurements_not_deferred() {
3893 let source = "namespace Test {
3894 @EntryPoint()
3895 operation Main() : Result[] {
3896 use (q0, q1) = (Qubit(), Qubit());
3897 X(q0);
3898 let r0 = MResetZ(q0);
3899 X(q1);
3900 let r1 = MResetZ(q1);
3901 [r0, r1]
3902 }
3903 }";
3904 let qir = compile_source_to_qir(source, *CAPABILITIES);
3905 expect![[r#"
3906 %Result = type opaque
3907 %Qubit = type opaque
3908
3909 @0 = internal constant [4 x i8] c"0_a\00"
3910 @1 = internal constant [6 x i8] c"1_a0r\00"
3911 @2 = internal constant [6 x i8] c"2_a1r\00"
3912
3913 define i64 @ENTRYPOINT__main() #0 {
3914 block_0:
3915 call void @__quantum__rt__initialize(i8* null)
3916 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3917 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3918 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
3919 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
3920 call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3921 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
3922 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
3923 ret i64 0
3924 }
3925
3926 declare void @__quantum__rt__initialize(i8*)
3927
3928 declare void @__quantum__qis__x__body(%Qubit*)
3929
3930 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3931
3932 declare void @__quantum__rt__array_record_output(i64, i8*)
3933
3934 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3935
3936 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
3937 attributes #1 = { "irreversible" }
3938
3939 ; module flags
3940
3941 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
3942
3943 !0 = !{i32 1, !"qir_major_version", i32 1}
3944 !1 = !{i32 7, !"qir_minor_version", i32 0}
3945 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
3946 !3 = !{i32 1, !"dynamic_result_management", i1 false}
3947 !4 = !{i32 5, !"int_computations", !{!"i64"}}
3948 !5 = !{i32 5, !"float_computations", !{!"double"}}
3949 "#]].assert_eq(&qir);
3950 }
3951
3952 #[test]
3953 fn qubit_id_swap_results_in_different_id_usage() {
3954 let source = "namespace Test {
3955 @EntryPoint()
3956 operation Main() : (Result, Result) {
3957 use (q0, q1) = (Qubit(), Qubit());
3958 X(q0);
3959 Relabel([q0, q1], [q1, q0]);
3960 X(q1);
3961 (MResetZ(q0), MResetZ(q1))
3962 }
3963 }";
3964 let qir = compile_source_to_qir(source, *CAPABILITIES);
3965 expect![[r#"
3966 %Result = type opaque
3967 %Qubit = type opaque
3968
3969 @0 = internal constant [4 x i8] c"0_t\00"
3970 @1 = internal constant [6 x i8] c"1_t0r\00"
3971 @2 = internal constant [6 x i8] c"2_t1r\00"
3972
3973 define i64 @ENTRYPOINT__main() #0 {
3974 block_0:
3975 call void @__quantum__rt__initialize(i8* null)
3976 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3977 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
3978 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
3979 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
3980 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
3981 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
3982 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
3983 ret i64 0
3984 }
3985
3986 declare void @__quantum__rt__initialize(i8*)
3987
3988 declare void @__quantum__qis__x__body(%Qubit*)
3989
3990 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
3991
3992 declare void @__quantum__rt__tuple_record_output(i64, i8*)
3993
3994 declare void @__quantum__rt__result_record_output(%Result*, i8*)
3995
3996 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
3997 attributes #1 = { "irreversible" }
3998
3999 ; module flags
4000
4001 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
4002
4003 !0 = !{i32 1, !"qir_major_version", i32 1}
4004 !1 = !{i32 7, !"qir_minor_version", i32 0}
4005 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4006 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4007 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4008 !5 = !{i32 5, !"float_computations", !{!"double"}}
4009 "#]].assert_eq(&qir);
4010 }
4011
4012 #[test]
4013 fn qubit_id_swap_across_reset_uses_updated_ids() {
4014 let source = "namespace Test {
4015 @EntryPoint()
4016 operation Main() : (Result, Result) {
4017 {
4018 use (q0, q1) = (Qubit(), Qubit());
4019 X(q0);
4020 Relabel([q0, q1], [q1, q0]);
4021 X(q1);
4022 Reset(q0);
4023 Reset(q1);
4024 }
4025 use (q0, q1) = (Qubit(), Qubit());
4026 (MResetZ(q0), MResetZ(q1))
4027 }
4028 }";
4029 let qir = compile_source_to_qir(source, *CAPABILITIES);
4030 expect![[r#"
4031 %Result = type opaque
4032 %Qubit = type opaque
4033
4034 @0 = internal constant [4 x i8] c"0_t\00"
4035 @1 = internal constant [6 x i8] c"1_t0r\00"
4036 @2 = internal constant [6 x i8] c"2_t1r\00"
4037
4038 define i64 @ENTRYPOINT__main() #0 {
4039 block_0:
4040 call void @__quantum__rt__initialize(i8* null)
4041 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
4042 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
4043 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 1 to %Qubit*))
4044 call void @__quantum__qis__reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
4045 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
4046 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
4047 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
4048 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
4049 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
4050 ret i64 0
4051 }
4052
4053 declare void @__quantum__rt__initialize(i8*)
4054
4055 declare void @__quantum__qis__x__body(%Qubit*)
4056
4057 declare void @__quantum__qis__reset__body(%Qubit*) #1
4058
4059 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
4060
4061 declare void @__quantum__rt__tuple_record_output(i64, i8*)
4062
4063 declare void @__quantum__rt__result_record_output(%Result*, i8*)
4064
4065 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
4066 attributes #1 = { "irreversible" }
4067
4068 ; module flags
4069
4070 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
4071
4072 !0 = !{i32 1, !"qir_major_version", i32 1}
4073 !1 = !{i32 7, !"qir_minor_version", i32 0}
4074 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4075 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4076 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4077 !5 = !{i32 5, !"float_computations", !{!"double"}}
4078 "#]].assert_eq(&qir);
4079 }
4080
4081 #[test]
4082 fn qubit_id_swap_with_out_of_order_release_uses_correct_ids() {
4083 let source = "namespace Test {
4084 @EntryPoint()
4085 operation Main() : (Result, Result) {
4086 let q0 = QIR.Runtime.__quantum__rt__qubit_allocate();
4087 let q1 = QIR.Runtime.__quantum__rt__qubit_allocate();
4088 let q2 = QIR.Runtime.__quantum__rt__qubit_allocate();
4089 X(q0);
4090 X(q1);
4091 X(q2);
4092 Relabel([q0, q1], [q1, q0]);
4093 QIR.Runtime.__quantum__rt__qubit_release(q0);
4094 let q3 = QIR.Runtime.__quantum__rt__qubit_allocate();
4095 X(q3);
4096 (MResetZ(q3), MResetZ(q1))
4097 }
4098 }";
4099 let qir = compile_source_to_qir(source, *CAPABILITIES);
4100 expect![[r#"
4101 %Result = type opaque
4102 %Qubit = type opaque
4103
4104 @0 = internal constant [4 x i8] c"0_t\00"
4105 @1 = internal constant [6 x i8] c"1_t0r\00"
4106 @2 = internal constant [6 x i8] c"2_t1r\00"
4107
4108 define i64 @ENTRYPOINT__main() #0 {
4109 block_0:
4110 call void @__quantum__rt__initialize(i8* null)
4111 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 0 to %Qubit*))
4112 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
4113 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 2 to %Qubit*))
4114 call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*))
4115 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
4116 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
4117 call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
4118 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i64 0, i64 0))
4119 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i64 0, i64 0))
4120 ret i64 0
4121 }
4122
4123 declare void @__quantum__rt__initialize(i8*)
4124
4125 declare void @__quantum__qis__x__body(%Qubit*)
4126
4127 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
4128
4129 declare void @__quantum__rt__tuple_record_output(i64, i8*)
4130
4131 declare void @__quantum__rt__result_record_output(%Result*, i8*)
4132
4133 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="2" }
4134 attributes #1 = { "irreversible" }
4135
4136 ; module flags
4137
4138 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
4139
4140 !0 = !{i32 1, !"qir_major_version", i32 1}
4141 !1 = !{i32 7, !"qir_minor_version", i32 0}
4142 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4143 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4144 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4145 !5 = !{i32 5, !"float_computations", !{!"double"}}
4146 "#]].assert_eq(&qir);
4147 }
4148
4149 #[test]
4150 fn dynamic_integer_with_branch_and_phi_supported() {
4151 let source = "namespace Test {
4152 @EntryPoint()
4153 operation Main() : Int {
4154 use q = Qubit();
4155 H(q);
4156 MResetZ(q) == Zero ? 0 | 1
4157 }
4158 }";
4159 let qir = compile_source_to_qir(source, *CAPABILITIES);
4160 expect![[r#"
4161 %Result = type opaque
4162 %Qubit = type opaque
4163
4164 @0 = internal constant [4 x i8] c"0_i\00"
4165
4166 define i64 @ENTRYPOINT__main() #0 {
4167 block_0:
4168 call void @__quantum__rt__initialize(i8* null)
4169 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
4170 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
4171 %var_0 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
4172 %var_1 = icmp eq i1 %var_0, false
4173 br i1 %var_1, label %block_1, label %block_2
4174 block_1:
4175 br label %block_3
4176 block_2:
4177 br label %block_3
4178 block_3:
4179 %var_4 = phi i64 [0, %block_1], [1, %block_2]
4180 call void @__quantum__rt__int_record_output(i64 %var_4, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
4181 ret i64 0
4182 }
4183
4184 declare void @__quantum__rt__initialize(i8*)
4185
4186 declare void @__quantum__qis__h__body(%Qubit*)
4187
4188 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
4189
4190 declare i1 @__quantum__rt__read_result(%Result*)
4191
4192 declare void @__quantum__rt__int_record_output(i64, i8*)
4193
4194 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
4195 attributes #1 = { "irreversible" }
4196
4197 ; module flags
4198
4199 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
4200
4201 !0 = !{i32 1, !"qir_major_version", i32 1}
4202 !1 = !{i32 7, !"qir_minor_version", i32 0}
4203 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4204 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4205 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4206 !5 = !{i32 5, !"float_computations", !{!"double"}}
4207 "#]].assert_eq(&qir);
4208 }
4209
4210 #[test]
4211 fn dynamic_double_with_branch_and_phi_supported() {
4212 let source = "namespace Test {
4213 @EntryPoint()
4214 operation Main() : Double {
4215 use q = Qubit();
4216 H(q);
4217 MResetZ(q) == Zero ? 0.0 | 1.0
4218 }
4219 }";
4220 let qir = compile_source_to_qir(source, *CAPABILITIES);
4221 expect![[r#"
4222 %Result = type opaque
4223 %Qubit = type opaque
4224
4225 @0 = internal constant [4 x i8] c"0_d\00"
4226
4227 define i64 @ENTRYPOINT__main() #0 {
4228 block_0:
4229 call void @__quantum__rt__initialize(i8* null)
4230 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
4231 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
4232 %var_0 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
4233 %var_1 = icmp eq i1 %var_0, false
4234 br i1 %var_1, label %block_1, label %block_2
4235 block_1:
4236 br label %block_3
4237 block_2:
4238 br label %block_3
4239 block_3:
4240 %var_4 = phi double [0.0, %block_1], [1.0, %block_2]
4241 call void @__quantum__rt__double_record_output(double %var_4, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
4242 ret i64 0
4243 }
4244
4245 declare void @__quantum__rt__initialize(i8*)
4246
4247 declare void @__quantum__qis__h__body(%Qubit*)
4248
4249 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
4250
4251 declare i1 @__quantum__rt__read_result(%Result*)
4252
4253 declare void @__quantum__rt__double_record_output(double, i8*)
4254
4255 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
4256 attributes #1 = { "irreversible" }
4257
4258 ; module flags
4259
4260 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
4261
4262 !0 = !{i32 1, !"qir_major_version", i32 1}
4263 !1 = !{i32 7, !"qir_minor_version", i32 0}
4264 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4265 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4266 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4267 !5 = !{i32 5, !"float_computations", !{!"double"}}
4268 "#]].assert_eq(&qir);
4269 }
4270
4271 #[test]
4272 fn custom_reset_generates_correct_qir() {
4273 let source = "namespace Test {
4274 operation Main() : Result {
4275 use q = Qubit();
4276 __quantum__qis__custom_reset__body(q);
4277 M(q)
4278 }
4279
4280 @Reset()
4281 operation __quantum__qis__custom_reset__body(target: Qubit) : Unit {
4282 body intrinsic;
4283 }
4284 }";
4285 let qir = compile_source_to_qir(source, *CAPABILITIES);
4286 expect![[r#"
4287 %Result = type opaque
4288 %Qubit = type opaque
4289
4290 @0 = internal constant [4 x i8] c"0_r\00"
4291
4292 define i64 @ENTRYPOINT__main() #0 {
4293 block_0:
4294 call void @__quantum__rt__initialize(i8* null)
4295 call void @__quantum__qis__custom_reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
4296 call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
4297 call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
4298 ret i64 0
4299 }
4300
4301 declare void @__quantum__rt__initialize(i8*)
4302
4303 declare void @__quantum__qis__custom_reset__body(%Qubit*) #1
4304
4305 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
4306
4307 declare void @__quantum__rt__result_record_output(%Result*, i8*)
4308
4309 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
4310 attributes #1 = { "irreversible" }
4311
4312 ; module flags
4313
4314 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
4315
4316 !0 = !{i32 1, !"qir_major_version", i32 1}
4317 !1 = !{i32 7, !"qir_minor_version", i32 0}
4318 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4319 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4320 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4321 !5 = !{i32 5, !"float_computations", !{!"double"}}
4322 "#]]
4323 .assert_eq(&qir);
4324 }
4325
4326 #[test]
4327 fn dynamic_double_intrinsic() {
4328 let source = "namespace Test {
4329 operation OpA(theta: Double, q : Qubit) : Unit { body intrinsic; }
4330 @EntryPoint()
4331 operation Main() : Double {
4332 use q = Qubit();
4333 H(q);
4334 let theta = MResetZ(q) == Zero ? 0.0 | 1.0;
4335 OpA(1.0 + theta, q);
4336 Rx(2.0 * theta, q);
4337 Ry(theta / 3.0, q);
4338 Rz(theta - 4.0, q);
4339 OpA(theta, q);
4340 Rx(theta, q);
4341 theta
4342 }
4343 }";
4344 let qir = compile_source_to_qir(source, *CAPABILITIES);
4345 expect![[r#"
4346 %Result = type opaque
4347 %Qubit = type opaque
4348
4349 @0 = internal constant [4 x i8] c"0_d\00"
4350
4351 define i64 @ENTRYPOINT__main() #0 {
4352 block_0:
4353 call void @__quantum__rt__initialize(i8* null)
4354 call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))
4355 call void @__quantum__qis__mresetz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
4356 %var_0 = call i1 @__quantum__rt__read_result(%Result* inttoptr (i64 0 to %Result*))
4357 %var_1 = icmp eq i1 %var_0, false
4358 br i1 %var_1, label %block_1, label %block_2
4359 block_1:
4360 br label %block_3
4361 block_2:
4362 br label %block_3
4363 block_3:
4364 %var_9 = phi double [0.0, %block_1], [1.0, %block_2]
4365 %var_4 = fadd double 1.0, %var_9
4366 call void @OpA(double %var_4, %Qubit* inttoptr (i64 0 to %Qubit*))
4367 %var_5 = fmul double 2.0, %var_9
4368 call void @__quantum__qis__rx__body(double %var_5, %Qubit* inttoptr (i64 0 to %Qubit*))
4369 %var_6 = fdiv double %var_9, 3.0
4370 call void @__quantum__qis__ry__body(double %var_6, %Qubit* inttoptr (i64 0 to %Qubit*))
4371 %var_7 = fsub double %var_9, 4.0
4372 call void @__quantum__qis__rz__body(double %var_7, %Qubit* inttoptr (i64 0 to %Qubit*))
4373 call void @OpA(double %var_9, %Qubit* inttoptr (i64 0 to %Qubit*))
4374 call void @__quantum__qis__rx__body(double %var_9, %Qubit* inttoptr (i64 0 to %Qubit*))
4375 call void @__quantum__rt__double_record_output(double %var_9, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i64 0, i64 0))
4376 ret i64 0
4377 }
4378
4379 declare void @__quantum__rt__initialize(i8*)
4380
4381 declare void @__quantum__qis__h__body(%Qubit*)
4382
4383 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
4384
4385 declare i1 @__quantum__rt__read_result(%Result*)
4386
4387 declare void @OpA(double, %Qubit*)
4388
4389 declare void @__quantum__qis__rx__body(double, %Qubit*)
4390
4391 declare void @__quantum__qis__ry__body(double, %Qubit*)
4392
4393 declare void @__quantum__qis__rz__body(double, %Qubit*)
4394
4395 declare void @__quantum__rt__double_record_output(double, i8*)
4396
4397 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
4398 attributes #1 = { "irreversible" }
4399
4400 ; module flags
4401
4402 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5}
4403
4404 !0 = !{i32 1, !"qir_major_version", i32 1}
4405 !1 = !{i32 7, !"qir_minor_version", i32 0}
4406 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4407 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4408 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4409 !5 = !{i32 5, !"float_computations", !{!"double"}}
4410 "#]].assert_eq(&qir);
4411 }
4412}
4413
4414mod adaptive_rifla_profile {
4415 use super::compile_source_to_qir;
4416 use super::compile_source_to_qir_result;
4417 use expect_test::expect;
4418 use qsc_data_structures::target::TargetCapabilityFlags;
4419
4420 static CAPABILITIES: std::sync::LazyLock<TargetCapabilityFlags> =
4421 std::sync::LazyLock::new(|| {
4422 TargetCapabilityFlags::Adaptive
4423 | TargetCapabilityFlags::IntegerComputations
4424 | TargetCapabilityFlags::FloatingPointComputations
4425 | TargetCapabilityFlags::BackwardsBranching
4426 | TargetCapabilityFlags::StaticSizedArrays
4427 });
4428
4429 #[test]
4430 fn nested_for_over_qubit_slice_succeeds() {
4431 let source = "namespace Test {
4432 import Std.Intrinsic.*;
4433 @EntryPoint()
4434 operation Main() : Unit {
4435 use qs = Qubit[3];
4436 X(qs[0]);
4437 for _ in 1..2 {
4438 for q in qs[1...] {
4439 CNOT(qs[0], q);
4440 }
4441 }
4442 }
4443 }";
4444 let qir = compile_source_to_qir(source, *CAPABILITIES);
4445 expect![[r#"
4446 @0 = internal constant [4 x i8] c"0_t\00"
4447 @array0 = internal constant [2 x ptr] [ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)]
4448
4449 define i64 @ENTRYPOINT__main() #0 {
4450 block_0:
4451 %var_1 = alloca i64
4452 %var_3 = alloca i1
4453 %var_4 = alloca i64
4454 call void @__quantum__rt__initialize(ptr null)
4455 call void @__quantum__qis__x__body(ptr inttoptr (i64 0 to ptr))
4456 store i64 1, ptr %var_1
4457 br label %block_1
4458 block_1:
4459 %var_11 = load i64, ptr %var_1
4460 %var_2 = icmp sle i64 %var_11, 2
4461 store i1 true, ptr %var_3
4462 br i1 %var_2, label %block_2, label %block_3
4463 block_2:
4464 %var_14 = load i1, ptr %var_3
4465 br i1 %var_14, label %block_4, label %block_5
4466 block_3:
4467 store i1 false, ptr %var_3
4468 br label %block_2
4469 block_4:
4470 store i64 0, ptr %var_4
4471 br label %block_6
4472 block_5:
4473 call void @__quantum__rt__tuple_record_output(i64 0, ptr @0)
4474 ret i64 0
4475 block_6:
4476 %var_16 = load i64, ptr %var_4
4477 %var_5 = icmp slt i64 %var_16, 2
4478 br i1 %var_5, label %block_7, label %block_8
4479 block_7:
4480 %var_19 = load i64, ptr %var_4
4481 %var_6 = getelementptr ptr, ptr @array0, i64 %var_19
4482 %var_20 = load ptr, ptr %var_6
4483 call void @__quantum__qis__cx__body(ptr inttoptr (i64 0 to ptr), ptr %var_20)
4484 %var_8 = add i64 %var_19, 1
4485 store i64 %var_8, ptr %var_4
4486 br label %block_6
4487 block_8:
4488 %var_17 = load i64, ptr %var_1
4489 %var_9 = add i64 %var_17, 1
4490 store i64 %var_9, ptr %var_1
4491 br label %block_1
4492 }
4493
4494 declare void @__quantum__rt__initialize(ptr)
4495
4496 declare void @__quantum__qis__x__body(ptr)
4497
4498 declare void @__quantum__qis__cx__body(ptr, ptr)
4499
4500 declare void @__quantum__rt__tuple_record_output(i64, ptr)
4501
4502 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="0" }
4503 attributes #1 = { "irreversible" }
4504
4505 ; module flags
4506
4507 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
4508
4509 !0 = !{i32 1, !"qir_major_version", i32 2}
4510 !1 = !{i32 7, !"qir_minor_version", i32 1}
4511 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4512 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4513 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4514 !5 = !{i32 5, !"float_computations", !{!"double"}}
4515 !6 = !{i32 7, !"backwards_branching", i2 3}
4516 !7 = !{i32 1, !"arrays", i1 true}
4517 "#]]
4518 .assert_eq(&qir);
4519 }
4520
4521 #[test]
4522 fn constant_folding_pattern_succeeds() {
4523 let source = "namespace Test {
4524 import Std.Intrinsic.*;
4525 @EntryPoint()
4526 operation Main() : Result[] {
4527 use qs = Qubit[3];
4528 let iterations = 2;
4529 X(qs[0]);
4530 for _ in 1..iterations {
4531 for q in qs[1...] {
4532 CNOT(qs[0], q);
4533 }
4534 }
4535 MResetEachZ(qs)
4536 }
4537 }";
4538 let qir = compile_source_to_qir(source, *CAPABILITIES);
4539 expect![[r#"
4540 @0 = internal constant [4 x i8] c"0_a\00"
4541 @1 = internal constant [6 x i8] c"1_a0r\00"
4542 @2 = internal constant [6 x i8] c"2_a1r\00"
4543 @3 = internal constant [6 x i8] c"3_a2r\00"
4544 @array0 = internal constant [2 x ptr] [ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)]
4545
4546 define i64 @ENTRYPOINT__main() #0 {
4547 block_0:
4548 %var_1 = alloca i64
4549 %var_3 = alloca i1
4550 %var_4 = alloca i64
4551 call void @__quantum__rt__initialize(ptr null)
4552 call void @__quantum__qis__x__body(ptr inttoptr (i64 0 to ptr))
4553 store i64 1, ptr %var_1
4554 br label %block_1
4555 block_1:
4556 %var_11 = load i64, ptr %var_1
4557 %var_2 = icmp sle i64 %var_11, 2
4558 store i1 true, ptr %var_3
4559 br i1 %var_2, label %block_2, label %block_3
4560 block_2:
4561 %var_14 = load i1, ptr %var_3
4562 br i1 %var_14, label %block_4, label %block_5
4563 block_3:
4564 store i1 false, ptr %var_3
4565 br label %block_2
4566 block_4:
4567 store i64 0, ptr %var_4
4568 br label %block_6
4569 block_5:
4570 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 0 to ptr))
4571 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 1 to ptr))
4572 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 2 to ptr))
4573 call void @__quantum__rt__array_record_output(i64 3, ptr @0)
4574 call void @__quantum__rt__result_record_output(ptr inttoptr (i64 0 to ptr), ptr @1)
4575 call void @__quantum__rt__result_record_output(ptr inttoptr (i64 1 to ptr), ptr @2)
4576 call void @__quantum__rt__result_record_output(ptr inttoptr (i64 2 to ptr), ptr @3)
4577 ret i64 0
4578 block_6:
4579 %var_16 = load i64, ptr %var_4
4580 %var_5 = icmp slt i64 %var_16, 2
4581 br i1 %var_5, label %block_7, label %block_8
4582 block_7:
4583 %var_19 = load i64, ptr %var_4
4584 %var_6 = getelementptr ptr, ptr @array0, i64 %var_19
4585 %var_20 = load ptr, ptr %var_6
4586 call void @__quantum__qis__cx__body(ptr inttoptr (i64 0 to ptr), ptr %var_20)
4587 %var_8 = add i64 %var_19, 1
4588 store i64 %var_8, ptr %var_4
4589 br label %block_6
4590 block_8:
4591 %var_17 = load i64, ptr %var_1
4592 %var_9 = add i64 %var_17, 1
4593 store i64 %var_9, ptr %var_1
4594 br label %block_1
4595 }
4596
4597 declare void @__quantum__rt__initialize(ptr)
4598
4599 declare void @__quantum__qis__x__body(ptr)
4600
4601 declare void @__quantum__qis__cx__body(ptr, ptr)
4602
4603 declare void @__quantum__qis__mresetz__body(ptr, ptr) #1
4604
4605 declare void @__quantum__rt__array_record_output(i64, ptr)
4606
4607 declare void @__quantum__rt__result_record_output(ptr, ptr)
4608
4609 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="3" }
4610 attributes #1 = { "irreversible" }
4611
4612 ; module flags
4613
4614 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
4615
4616 !0 = !{i32 1, !"qir_major_version", i32 2}
4617 !1 = !{i32 7, !"qir_minor_version", i32 1}
4618 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4619 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4620 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4621 !5 = !{i32 5, !"float_computations", !{!"double"}}
4622 !6 = !{i32 7, !"backwards_branching", i2 3}
4623 !7 = !{i32 1, !"arrays", i1 true}
4624 "#]]
4625 .assert_eq(&qir);
4626 }
4627
4628 #[test]
4629 fn three_qubit_repetition_code_pattern_succeeds() {
4630 let source = "namespace Test {
4631 import Std.Intrinsic.*;
4632 operation ApplyRotationalIdentity(register : Qubit[]) : Unit {
4633 let theta = 2.0 * 3.14159265;
4634 for qubit in register {
4635 Rx(theta, qubit);
4636 }
4637 }
4638 @EntryPoint()
4639 operation Main() : Result[] {
4640 use qs = Qubit[3];
4641 X(qs[0]);
4642 let iterations = 2;
4643 for _ in 1..iterations {
4644 for q in qs[1...] {
4645 CNOT(qs[0], q);
4646 }
4647 ApplyRotationalIdentity(qs);
4648 }
4649 MResetEachZ(qs)
4650 }
4651 }";
4652 let qir = compile_source_to_qir(source, *CAPABILITIES);
4653 expect![[r#"
4654 @0 = internal constant [4 x i8] c"0_a\00"
4655 @1 = internal constant [6 x i8] c"1_a0r\00"
4656 @2 = internal constant [6 x i8] c"2_a1r\00"
4657 @3 = internal constant [6 x i8] c"3_a2r\00"
4658 @array0 = internal constant [2 x ptr] [ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)]
4659 @array1 = internal constant [3 x ptr] [ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)]
4660
4661 define i64 @ENTRYPOINT__main() #0 {
4662 block_0:
4663 %var_1 = alloca i64
4664 %var_3 = alloca i1
4665 %var_4 = alloca i64
4666 %var_9 = alloca i64
4667 call void @__quantum__rt__initialize(ptr null)
4668 call void @__quantum__qis__x__body(ptr inttoptr (i64 0 to ptr))
4669 store i64 1, ptr %var_1
4670 br label %block_1
4671 block_1:
4672 %var_16 = load i64, ptr %var_1
4673 %var_2 = icmp sle i64 %var_16, 2
4674 store i1 true, ptr %var_3
4675 br i1 %var_2, label %block_2, label %block_3
4676 block_2:
4677 %var_19 = load i1, ptr %var_3
4678 br i1 %var_19, label %block_4, label %block_5
4679 block_3:
4680 store i1 false, ptr %var_3
4681 br label %block_2
4682 block_4:
4683 store i64 0, ptr %var_4
4684 br label %block_6
4685 block_5:
4686 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 0 to ptr))
4687 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 1 to ptr))
4688 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 2 to ptr))
4689 call void @__quantum__rt__array_record_output(i64 3, ptr @0)
4690 call void @__quantum__rt__result_record_output(ptr inttoptr (i64 0 to ptr), ptr @1)
4691 call void @__quantum__rt__result_record_output(ptr inttoptr (i64 1 to ptr), ptr @2)
4692 call void @__quantum__rt__result_record_output(ptr inttoptr (i64 2 to ptr), ptr @3)
4693 ret i64 0
4694 block_6:
4695 %var_21 = load i64, ptr %var_4
4696 %var_5 = icmp slt i64 %var_21, 2
4697 br i1 %var_5, label %block_7, label %block_8
4698 block_7:
4699 %var_29 = load i64, ptr %var_4
4700 %var_6 = getelementptr ptr, ptr @array0, i64 %var_29
4701 %var_30 = load ptr, ptr %var_6
4702 call void @__quantum__qis__cx__body(ptr inttoptr (i64 0 to ptr), ptr %var_30)
4703 %var_8 = add i64 %var_29, 1
4704 store i64 %var_8, ptr %var_4
4705 br label %block_6
4706 block_8:
4707 store i64 0, ptr %var_9
4708 br label %block_9
4709 block_9:
4710 %var_23 = load i64, ptr %var_9
4711 %var_10 = icmp slt i64 %var_23, 3
4712 br i1 %var_10, label %block_10, label %block_11
4713 block_10:
4714 %var_26 = load i64, ptr %var_9
4715 %var_11 = getelementptr ptr, ptr @array1, i64 %var_26
4716 %var_27 = load ptr, ptr %var_11
4717 call void @__quantum__qis__rx__body(double 6.2831853, ptr %var_27)
4718 %var_13 = add i64 %var_26, 1
4719 store i64 %var_13, ptr %var_9
4720 br label %block_9
4721 block_11:
4722 %var_24 = load i64, ptr %var_1
4723 %var_14 = add i64 %var_24, 1
4724 store i64 %var_14, ptr %var_1
4725 br label %block_1
4726 }
4727
4728 declare void @__quantum__rt__initialize(ptr)
4729
4730 declare void @__quantum__qis__x__body(ptr)
4731
4732 declare void @__quantum__qis__cx__body(ptr, ptr)
4733
4734 declare void @__quantum__qis__rx__body(double, ptr)
4735
4736 declare void @__quantum__qis__mresetz__body(ptr, ptr) #1
4737
4738 declare void @__quantum__rt__array_record_output(i64, ptr)
4739
4740 declare void @__quantum__rt__result_record_output(ptr, ptr)
4741
4742 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="3" }
4743 attributes #1 = { "irreversible" }
4744
4745 ; module flags
4746
4747 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
4748
4749 !0 = !{i32 1, !"qir_major_version", i32 2}
4750 !1 = !{i32 7, !"qir_minor_version", i32 1}
4751 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4752 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4753 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4754 !5 = !{i32 5, !"float_computations", !{!"double"}}
4755 !6 = !{i32 7, !"backwards_branching", i2 3}
4756 !7 = !{i32 1, !"arrays", i1 true}
4757 "#]]
4758 .assert_eq(&qir);
4759 }
4760
4761 #[test]
4762 fn for_over_qubit_slice_inside_dynamic_while_succeeds() {
4763 let source = "namespace Test {
4764 import Std.Intrinsic.*;
4765 @EntryPoint()
4766 operation Main() : Unit {
4767 use qs = Qubit[3];
4768 mutable done = false;
4769 while not done {
4770 for q in qs[1...] {
4771 CNOT(qs[0], q);
4772 }
4773 set done = MResetZ(qs[0]) == One;
4774 }
4775 }
4776 }";
4777 let qir = compile_source_to_qir(source, *CAPABILITIES);
4778 expect![[r#"
4779 @0 = internal constant [4 x i8] c"0_t\00"
4780 @array0 = internal constant [2 x ptr] [ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)]
4781
4782 define i64 @ENTRYPOINT__main() #0 {
4783 block_0:
4784 %var_1 = alloca i1
4785 %var_3 = alloca i64
4786 call void @__quantum__rt__initialize(ptr null)
4787 store i1 false, ptr %var_1
4788 br label %block_1
4789 block_1:
4790 %var_10 = load i1, ptr %var_1
4791 %var_2 = xor i1 %var_10, true
4792 br i1 %var_2, label %block_2, label %block_3
4793 block_2:
4794 store i64 0, ptr %var_3
4795 br label %block_4
4796 block_3:
4797 call void @__quantum__rt__tuple_record_output(i64 0, ptr @0)
4798 ret i64 0
4799 block_4:
4800 %var_12 = load i64, ptr %var_3
4801 %var_4 = icmp slt i64 %var_12, 2
4802 br i1 %var_4, label %block_5, label %block_6
4803 block_5:
4804 %var_14 = load i64, ptr %var_3
4805 %var_5 = getelementptr ptr, ptr @array0, i64 %var_14
4806 %var_15 = load ptr, ptr %var_5
4807 call void @__quantum__qis__cx__body(ptr inttoptr (i64 0 to ptr), ptr %var_15)
4808 %var_7 = add i64 %var_14, 1
4809 store i64 %var_7, ptr %var_3
4810 br label %block_4
4811 block_6:
4812 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 0 to ptr))
4813 %var_8 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 0 to ptr))
4814 store i1 %var_8, ptr %var_1
4815 br label %block_1
4816 }
4817
4818 declare void @__quantum__rt__initialize(ptr)
4819
4820 declare void @__quantum__qis__cx__body(ptr, ptr)
4821
4822 declare void @__quantum__qis__mresetz__body(ptr, ptr) #1
4823
4824 declare i1 @__quantum__rt__read_result(ptr)
4825
4826 declare void @__quantum__rt__tuple_record_output(i64, ptr)
4827
4828 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="1" }
4829 attributes #1 = { "irreversible" }
4830
4831 ; module flags
4832
4833 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
4834
4835 !0 = !{i32 1, !"qir_major_version", i32 2}
4836 !1 = !{i32 7, !"qir_minor_version", i32 1}
4837 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4838 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4839 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4840 !5 = !{i32 5, !"float_computations", !{!"double"}}
4841 !6 = !{i32 7, !"backwards_branching", i2 3}
4842 !7 = !{i32 1, !"arrays", i1 true}
4843 "#]]
4844 .assert_eq(&qir);
4845 }
4846
4847 #[test]
4848 fn result_array_dynamic_index_succeeds() {
4849 let source = "namespace Test {
4850 import Std.Intrinsic.*;
4851 @EntryPoint()
4852 operation Main() : Int {
4853 use qs = Qubit[4];
4854 let results = MResetEachZ(qs);
4855 mutable count = 0;
4856 for i in 0..3 {
4857 if results[i] == One {
4858 set count += 1;
4859 }
4860 }
4861 count
4862 }
4863 }";
4864 let qir = compile_source_to_qir(source, *CAPABILITIES);
4865 expect![[r#"
4866 @0 = internal constant [4 x i8] c"0_i\00"
4867
4868 define i64 @ENTRYPOINT__main() #0 {
4869 block_0:
4870 %var_2 = alloca i64
4871 call void @__quantum__rt__initialize(ptr null)
4872 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 0 to ptr))
4873 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 1 to ptr))
4874 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 2 to ptr))
4875 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 3 to ptr), ptr inttoptr (i64 3 to ptr))
4876 store i64 0, ptr %var_2
4877 %var_4 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 0 to ptr))
4878 br i1 %var_4, label %block_1, label %block_2
4879 block_1:
4880 %var_24 = load i64, ptr %var_2
4881 %var_6 = add i64 %var_24, 1
4882 store i64 %var_6, ptr %var_2
4883 br label %block_2
4884 block_2:
4885 %var_7 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 1 to ptr))
4886 br i1 %var_7, label %block_3, label %block_4
4887 block_3:
4888 %var_22 = load i64, ptr %var_2
4889 %var_9 = add i64 %var_22, 1
4890 store i64 %var_9, ptr %var_2
4891 br label %block_4
4892 block_4:
4893 %var_10 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 2 to ptr))
4894 br i1 %var_10, label %block_5, label %block_6
4895 block_5:
4896 %var_20 = load i64, ptr %var_2
4897 %var_12 = add i64 %var_20, 1
4898 store i64 %var_12, ptr %var_2
4899 br label %block_6
4900 block_6:
4901 %var_13 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 3 to ptr))
4902 br i1 %var_13, label %block_7, label %block_8
4903 block_7:
4904 %var_18 = load i64, ptr %var_2
4905 %var_15 = add i64 %var_18, 1
4906 store i64 %var_15, ptr %var_2
4907 br label %block_8
4908 block_8:
4909 %var_17 = load i64, ptr %var_2
4910 call void @__quantum__rt__int_record_output(i64 %var_17, ptr @0)
4911 ret i64 0
4912 }
4913
4914 declare void @__quantum__rt__initialize(ptr)
4915
4916 declare void @__quantum__qis__mresetz__body(ptr, ptr) #1
4917
4918 declare i1 @__quantum__rt__read_result(ptr)
4919
4920 declare void @__quantum__rt__int_record_output(i64, ptr)
4921
4922 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="4" "required_num_results"="4" }
4923 attributes #1 = { "irreversible" }
4924
4925 ; module flags
4926
4927 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
4928
4929 !0 = !{i32 1, !"qir_major_version", i32 2}
4930 !1 = !{i32 7, !"qir_minor_version", i32 1}
4931 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
4932 !3 = !{i32 1, !"dynamic_result_management", i1 false}
4933 !4 = !{i32 5, !"int_computations", !{!"i64"}}
4934 !5 = !{i32 5, !"float_computations", !{!"double"}}
4935 !6 = !{i32 7, !"backwards_branching", i2 3}
4936 !7 = !{i32 1, !"arrays", i1 true}
4937 "#]]
4938 .assert_eq(&qir);
4939 }
4940
4941 #[test]
4942 fn result_array_while_loop_dynamic_index_succeeds() {
4943 let source = "namespace Test {
4944 import Std.Intrinsic.*;
4945 @EntryPoint()
4946 operation Main() : Int {
4947 use qs = Qubit[4];
4948 H(qs[0]);
4949 H(qs[1]);
4950 H(qs[2]);
4951 H(qs[3]);
4952 let r0 = MResetZ(qs[0]);
4953 let r1 = MResetZ(qs[1]);
4954 let r2 = MResetZ(qs[2]);
4955 let r3 = MResetZ(qs[3]);
4956 let results = [r0, r1, r2, r3];
4957 mutable count = 0;
4958 mutable i = 0;
4959 while i < 4 {
4960 if results[i] == One { set count += 1; }
4961 set i += 1;
4962 }
4963 count
4964 }
4965 }";
4966 let qir = compile_source_to_qir(source, *CAPABILITIES);
4967 expect![[r#"
4968 @0 = internal constant [4 x i8] c"0_i\00"
4969
4970 define i64 @ENTRYPOINT__main() #0 {
4971 block_0:
4972 %var_1 = alloca i64
4973 call void @__quantum__rt__initialize(ptr null)
4974 call void @__quantum__qis__h__body(ptr inttoptr (i64 0 to ptr))
4975 call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))
4976 call void @__quantum__qis__h__body(ptr inttoptr (i64 2 to ptr))
4977 call void @__quantum__qis__h__body(ptr inttoptr (i64 3 to ptr))
4978 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 0 to ptr))
4979 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 1 to ptr))
4980 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 2 to ptr))
4981 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 3 to ptr), ptr inttoptr (i64 3 to ptr))
4982 store i64 0, ptr %var_1
4983 %var_3 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 0 to ptr))
4984 br i1 %var_3, label %block_1, label %block_2
4985 block_1:
4986 %var_23 = load i64, ptr %var_1
4987 %var_5 = add i64 %var_23, 1
4988 store i64 %var_5, ptr %var_1
4989 br label %block_2
4990 block_2:
4991 %var_6 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 1 to ptr))
4992 br i1 %var_6, label %block_3, label %block_4
4993 block_3:
4994 %var_21 = load i64, ptr %var_1
4995 %var_8 = add i64 %var_21, 1
4996 store i64 %var_8, ptr %var_1
4997 br label %block_4
4998 block_4:
4999 %var_9 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 2 to ptr))
5000 br i1 %var_9, label %block_5, label %block_6
5001 block_5:
5002 %var_19 = load i64, ptr %var_1
5003 %var_11 = add i64 %var_19, 1
5004 store i64 %var_11, ptr %var_1
5005 br label %block_6
5006 block_6:
5007 %var_12 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 3 to ptr))
5008 br i1 %var_12, label %block_7, label %block_8
5009 block_7:
5010 %var_17 = load i64, ptr %var_1
5011 %var_14 = add i64 %var_17, 1
5012 store i64 %var_14, ptr %var_1
5013 br label %block_8
5014 block_8:
5015 %var_16 = load i64, ptr %var_1
5016 call void @__quantum__rt__int_record_output(i64 %var_16, ptr @0)
5017 ret i64 0
5018 }
5019
5020 declare void @__quantum__rt__initialize(ptr)
5021
5022 declare void @__quantum__qis__h__body(ptr)
5023
5024 declare void @__quantum__qis__mresetz__body(ptr, ptr) #1
5025
5026 declare i1 @__quantum__rt__read_result(ptr)
5027
5028 declare void @__quantum__rt__int_record_output(i64, ptr)
5029
5030 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="4" "required_num_results"="4" }
5031 attributes #1 = { "irreversible" }
5032
5033 ; module flags
5034
5035 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
5036
5037 !0 = !{i32 1, !"qir_major_version", i32 2}
5038 !1 = !{i32 7, !"qir_minor_version", i32 1}
5039 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
5040 !3 = !{i32 1, !"dynamic_result_management", i1 false}
5041 !4 = !{i32 5, !"int_computations", !{!"i64"}}
5042 !5 = !{i32 5, !"float_computations", !{!"double"}}
5043 !6 = !{i32 7, !"backwards_branching", i2 3}
5044 !7 = !{i32 1, !"arrays", i1 true}
5045 "#]]
5046 .assert_eq(&qir);
5047 }
5048
5049 #[test]
5050 #[ignore = "CapabilitiesCk(UseOfDynamicResult) — mutable Result re-measurement requires UseOfDynamicResult, not in RIFLA profile"]
5051 fn mutable_result_variable_succeeds() {
5052 let source = "namespace Test {
5053 import Std.Intrinsic.*;
5054 @EntryPoint()
5055 operation Main() : Result {
5056 use q = Qubit();
5057 H(q);
5058 mutable r = M(q);
5059 if r == One {
5060 X(q);
5061 set r = M(q);
5062 }
5063 r
5064 }
5065 }";
5066 let qir = compile_source_to_qir_result(source, *CAPABILITIES)
5067 .expect("mutable Result variable should compile");
5068 assert!(qir.contains("@ENTRYPOINT__main"));
5069 }
5070
5071 #[test]
5072 fn for_loop_over_qubits_with_reset_all_succeeds() {
5073 let source = "namespace Test {
5074 import Std.Intrinsic.*;
5075 @EntryPoint()
5076 operation Main() : Result {
5077 use qs = Qubit[4];
5078 for q in qs {
5079 H(q);
5080 }
5081 let r = MResetZ(qs[0]);
5082 ResetAll(qs[1..3]);
5083 r
5084 }
5085 }";
5086 let qir = compile_source_to_qir(source, *CAPABILITIES);
5087 expect![[r#"
5088 @0 = internal constant [4 x i8] c"0_r\00"
5089 @array0 = internal constant [4 x ptr] [ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 3 to ptr)]
5090 @array1 = internal constant [3 x ptr] [ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 3 to ptr)]
5091
5092 define i64 @ENTRYPOINT__main() #0 {
5093 block_0:
5094 %var_1 = alloca i64
5095 %var_6 = alloca i64
5096 call void @__quantum__rt__initialize(ptr null)
5097 store i64 0, ptr %var_1
5098 br label %block_1
5099 block_1:
5100 %var_12 = load i64, ptr %var_1
5101 %var_2 = icmp slt i64 %var_12, 4
5102 br i1 %var_2, label %block_2, label %block_3
5103 block_2:
5104 %var_18 = load i64, ptr %var_1
5105 %var_3 = getelementptr ptr, ptr @array0, i64 %var_18
5106 %var_19 = load ptr, ptr %var_3
5107 call void @__quantum__qis__h__body(ptr %var_19)
5108 %var_5 = add i64 %var_18, 1
5109 store i64 %var_5, ptr %var_1
5110 br label %block_1
5111 block_3:
5112 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 0 to ptr))
5113 store i64 0, ptr %var_6
5114 br label %block_4
5115 block_4:
5116 %var_14 = load i64, ptr %var_6
5117 %var_7 = icmp slt i64 %var_14, 3
5118 br i1 %var_7, label %block_5, label %block_6
5119 block_5:
5120 %var_15 = load i64, ptr %var_6
5121 %var_8 = getelementptr ptr, ptr @array1, i64 %var_15
5122 %var_16 = load ptr, ptr %var_8
5123 call void @__quantum__qis__reset__body(ptr %var_16)
5124 %var_10 = add i64 %var_15, 1
5125 store i64 %var_10, ptr %var_6
5126 br label %block_4
5127 block_6:
5128 call void @__quantum__rt__result_record_output(ptr inttoptr (i64 0 to ptr), ptr @0)
5129 ret i64 0
5130 }
5131
5132 declare void @__quantum__rt__initialize(ptr)
5133
5134 declare void @__quantum__qis__h__body(ptr)
5135
5136 declare void @__quantum__qis__mresetz__body(ptr, ptr) #1
5137
5138 declare void @__quantum__qis__reset__body(ptr) #1
5139
5140 declare void @__quantum__rt__result_record_output(ptr, ptr)
5141
5142 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="4" "required_num_results"="1" }
5143 attributes #1 = { "irreversible" }
5144
5145 ; module flags
5146
5147 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
5148
5149 !0 = !{i32 1, !"qir_major_version", i32 2}
5150 !1 = !{i32 7, !"qir_minor_version", i32 1}
5151 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
5152 !3 = !{i32 1, !"dynamic_result_management", i1 false}
5153 !4 = !{i32 5, !"int_computations", !{!"i64"}}
5154 !5 = !{i32 5, !"float_computations", !{!"double"}}
5155 !6 = !{i32 7, !"backwards_branching", i2 3}
5156 !7 = !{i32 1, !"arrays", i1 true}
5157 "#]]
5158 .assert_eq(&qir);
5159 }
5160
5161 #[test]
5162 fn measure_each_z_static_qubits_succeeds() {
5163 let source = "namespace Test {
5164 import Std.Intrinsic.*;
5165 @EntryPoint()
5166 operation Main() : Result[] {
5167 use qs = Qubit[3];
5168 X(qs[0]);
5169 H(qs[1]);
5170 MResetEachZ(qs)
5171 }
5172 }";
5173 let qir = compile_source_to_qir(source, *CAPABILITIES);
5174 expect![[r#"
5175 @0 = internal constant [4 x i8] c"0_a\00"
5176 @1 = internal constant [6 x i8] c"1_a0r\00"
5177 @2 = internal constant [6 x i8] c"2_a1r\00"
5178 @3 = internal constant [6 x i8] c"3_a2r\00"
5179
5180 define i64 @ENTRYPOINT__main() #0 {
5181 block_0:
5182 call void @__quantum__rt__initialize(ptr null)
5183 call void @__quantum__qis__x__body(ptr inttoptr (i64 0 to ptr))
5184 call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))
5185 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 0 to ptr))
5186 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 1 to ptr))
5187 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 2 to ptr))
5188 call void @__quantum__rt__array_record_output(i64 3, ptr @0)
5189 call void @__quantum__rt__result_record_output(ptr inttoptr (i64 0 to ptr), ptr @1)
5190 call void @__quantum__rt__result_record_output(ptr inttoptr (i64 1 to ptr), ptr @2)
5191 call void @__quantum__rt__result_record_output(ptr inttoptr (i64 2 to ptr), ptr @3)
5192 ret i64 0
5193 }
5194
5195 declare void @__quantum__rt__initialize(ptr)
5196
5197 declare void @__quantum__qis__x__body(ptr)
5198
5199 declare void @__quantum__qis__h__body(ptr)
5200
5201 declare void @__quantum__qis__mresetz__body(ptr, ptr) #1
5202
5203 declare void @__quantum__rt__array_record_output(i64, ptr)
5204
5205 declare void @__quantum__rt__result_record_output(ptr, ptr)
5206
5207 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="3" }
5208 attributes #1 = { "irreversible" }
5209
5210 ; module flags
5211
5212 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
5213
5214 !0 = !{i32 1, !"qir_major_version", i32 2}
5215 !1 = !{i32 7, !"qir_minor_version", i32 1}
5216 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
5217 !3 = !{i32 1, !"dynamic_result_management", i1 false}
5218 !4 = !{i32 5, !"int_computations", !{!"i64"}}
5219 !5 = !{i32 5, !"float_computations", !{!"double"}}
5220 !6 = !{i32 7, !"backwards_branching", i2 3}
5221 !7 = !{i32 1, !"arrays", i1 true}
5222 "#]]
5223 .assert_eq(&qir);
5224 }
5225
5226 #[test]
5227 fn static_while_inside_emit_while_succeeds() {
5228 let source = "namespace Test {
5229 import Std.Intrinsic.*;
5230 @EntryPoint()
5231 operation Main() : Int {
5232 use q = Qubit();
5233 mutable total = 0;
5234 while MResetZ(q) == One {
5235 mutable idx = 0;
5236 while idx < 3 {
5237 set total += 1;
5238 set idx += 1;
5239 }
5240 }
5241 total
5242 }
5243 }";
5244 let qir = compile_source_to_qir(source, *CAPABILITIES);
5245 expect![[r#"
5246 @0 = internal constant [4 x i8] c"0_i\00"
5247
5248 define i64 @ENTRYPOINT__main() #0 {
5249 block_0:
5250 %var_0 = alloca i64
5251 %var_3 = alloca i64
5252 call void @__quantum__rt__initialize(ptr null)
5253 store i64 0, ptr %var_0
5254 br label %block_1
5255 block_1:
5256 call void @__quantum__qis__mresetz__body(ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 0 to ptr))
5257 %var_1 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 0 to ptr))
5258 br i1 %var_1, label %block_2, label %block_3
5259 block_2:
5260 store i64 0, ptr %var_3
5261 br label %block_4
5262 block_3:
5263 %var_8 = load i64, ptr %var_0
5264 call void @__quantum__rt__int_record_output(i64 %var_8, ptr @0)
5265 ret i64 0
5266 block_4:
5267 %var_10 = load i64, ptr %var_3
5268 %var_4 = icmp slt i64 %var_10, 3
5269 br i1 %var_4, label %block_5, label %block_6
5270 block_5:
5271 %var_11 = load i64, ptr %var_0
5272 %var_5 = add i64 %var_11, 1
5273 store i64 %var_5, ptr %var_0
5274 %var_13 = load i64, ptr %var_3
5275 %var_6 = add i64 %var_13, 1
5276 store i64 %var_6, ptr %var_3
5277 br label %block_4
5278 block_6:
5279 br label %block_1
5280 }
5281
5282 declare void @__quantum__rt__initialize(ptr)
5283
5284 declare void @__quantum__qis__mresetz__body(ptr, ptr) #1
5285
5286 declare i1 @__quantum__rt__read_result(ptr)
5287
5288 declare void @__quantum__rt__int_record_output(i64, ptr)
5289
5290 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" }
5291 attributes #1 = { "irreversible" }
5292
5293 ; module flags
5294
5295 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
5296
5297 !0 = !{i32 1, !"qir_major_version", i32 2}
5298 !1 = !{i32 7, !"qir_minor_version", i32 1}
5299 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
5300 !3 = !{i32 1, !"dynamic_result_management", i1 false}
5301 !4 = !{i32 5, !"int_computations", !{!"i64"}}
5302 !5 = !{i32 5, !"float_computations", !{!"double"}}
5303 !6 = !{i32 7, !"backwards_branching", i2 3}
5304 !7 = !{i32 1, !"arrays", i1 true}
5305 "#]]
5306 .assert_eq(&qir);
5307 }
5308
5309 #[test]
5310 fn nested_emit_while_loops_succeeds() {
5311 let source = "namespace Test {
5312 import Std.Intrinsic.*;
5313 @EntryPoint()
5314 operation Main() : Int {
5315 use qs = Qubit[2];
5316 mutable outer = 0;
5317 while outer < 3 {
5318 H(qs[0]);
5319 mutable inner = 0;
5320 while inner < 2 {
5321 H(qs[1]);
5322 set inner += 1;
5323 }
5324 set outer += 1;
5325 }
5326 outer
5327 }
5328 }";
5329 let qir = compile_source_to_qir(source, *CAPABILITIES);
5330 expect![[r#"
5331 @0 = internal constant [4 x i8] c"0_i\00"
5332
5333 define i64 @ENTRYPOINT__main() #0 {
5334 block_0:
5335 %var_1 = alloca i64
5336 %var_3 = alloca i64
5337 call void @__quantum__rt__initialize(ptr null)
5338 store i64 0, ptr %var_1
5339 br label %block_1
5340 block_1:
5341 %var_8 = load i64, ptr %var_1
5342 %var_2 = icmp slt i64 %var_8, 3
5343 br i1 %var_2, label %block_2, label %block_3
5344 block_2:
5345 call void @__quantum__qis__h__body(ptr inttoptr (i64 0 to ptr))
5346 store i64 0, ptr %var_3
5347 br label %block_4
5348 block_3:
5349 %var_9 = load i64, ptr %var_1
5350 call void @__quantum__rt__int_record_output(i64 %var_9, ptr @0)
5351 ret i64 0
5352 block_4:
5353 %var_11 = load i64, ptr %var_3
5354 %var_4 = icmp slt i64 %var_11, 2
5355 br i1 %var_4, label %block_5, label %block_6
5356 block_5:
5357 call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))
5358 %var_14 = load i64, ptr %var_3
5359 %var_5 = add i64 %var_14, 1
5360 store i64 %var_5, ptr %var_3
5361 br label %block_4
5362 block_6:
5363 %var_12 = load i64, ptr %var_1
5364 %var_6 = add i64 %var_12, 1
5365 store i64 %var_6, ptr %var_1
5366 br label %block_1
5367 }
5368
5369 declare void @__quantum__rt__initialize(ptr)
5370
5371 declare void @__quantum__qis__h__body(ptr)
5372
5373 declare void @__quantum__rt__int_record_output(i64, ptr)
5374
5375 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="0" }
5376 attributes #1 = { "irreversible" }
5377
5378 ; module flags
5379
5380 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
5381
5382 !0 = !{i32 1, !"qir_major_version", i32 2}
5383 !1 = !{i32 7, !"qir_minor_version", i32 1}
5384 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
5385 !3 = !{i32 1, !"dynamic_result_management", i1 false}
5386 !4 = !{i32 5, !"int_computations", !{!"i64"}}
5387 !5 = !{i32 5, !"float_computations", !{!"double"}}
5388 !6 = !{i32 7, !"backwards_branching", i2 3}
5389 !7 = !{i32 1, !"arrays", i1 true}
5390 "#]]
5391 .assert_eq(&qir);
5392 }
5393
5394 #[test]
5395 fn for_loop_over_qubits_with_dynamic_exit_succeeds() {
5396 let source = "namespace Test {
5397 import Std.Intrinsic.*;
5398 @EntryPoint()
5399 operation Main() : Bool {
5400 use qs = Qubit[3];
5401 mutable found = false;
5402 for q in qs {
5403 H(q);
5404 if MResetZ(q) == One {
5405 set found = true;
5406 }
5407 }
5408 found
5409 }
5410 }";
5411 let qir = compile_source_to_qir(source, *CAPABILITIES);
5412 expect![[r#"
5413 @0 = internal constant [4 x i8] c"0_b\00"
5414 @array0 = internal constant [3 x ptr] [ptr inttoptr (i64 0 to ptr), ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)]
5415
5416 define i64 @ENTRYPOINT__main() #0 {
5417 block_0:
5418 %var_1 = alloca i1
5419 %var_2 = alloca i64
5420 call void @__quantum__rt__initialize(ptr null)
5421 store i1 false, ptr %var_1
5422 store i64 0, ptr %var_2
5423 br label %block_1
5424 block_1:
5425 %var_11 = load i64, ptr %var_2
5426 %var_3 = icmp slt i64 %var_11, 3
5427 br i1 %var_3, label %block_2, label %block_3
5428 block_2:
5429 %var_13 = load i64, ptr %var_2
5430 %var_4 = getelementptr ptr, ptr @array0, i64 %var_13
5431 %var_14 = load ptr, ptr %var_4
5432 call void @__quantum__qis__h__body(ptr %var_14)
5433 call void @__quantum__qis__mresetz__body(ptr %var_14, ptr inttoptr (i64 0 to ptr))
5434 %var_6 = call i1 @__quantum__rt__read_result(ptr inttoptr (i64 0 to ptr))
5435 br i1 %var_6, label %block_4, label %block_5
5436 block_3:
5437 %var_12 = load i1, ptr %var_1
5438 call void @__quantum__rt__bool_record_output(i1 %var_12, ptr @0)
5439 ret i64 0
5440 block_4:
5441 store i1 true, ptr %var_1
5442 br label %block_5
5443 block_5:
5444 %var_15 = load i64, ptr %var_2
5445 %var_8 = add i64 %var_15, 1
5446 store i64 %var_8, ptr %var_2
5447 br label %block_1
5448 }
5449
5450 declare void @__quantum__rt__initialize(ptr)
5451
5452 declare void @__quantum__qis__h__body(ptr)
5453
5454 declare void @__quantum__qis__mresetz__body(ptr, ptr) #1
5455
5456 declare i1 @__quantum__rt__read_result(ptr)
5457
5458 declare void @__quantum__rt__bool_record_output(i1, ptr)
5459
5460 attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="3" "required_num_results"="1" }
5461 attributes #1 = { "irreversible" }
5462
5463 ; module flags
5464
5465 !llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
5466
5467 !0 = !{i32 1, !"qir_major_version", i32 2}
5468 !1 = !{i32 7, !"qir_minor_version", i32 1}
5469 !2 = !{i32 1, !"dynamic_qubit_management", i1 false}
5470 !3 = !{i32 1, !"dynamic_result_management", i1 false}
5471 !4 = !{i32 5, !"int_computations", !{!"i64"}}
5472 !5 = !{i32 5, !"float_computations", !{!"double"}}
5473 !6 = !{i32 7, !"backwards_branching", i2 3}
5474 !7 = !{i32 1, !"arrays", i1 true}
5475 "#]]
5476 .assert_eq(&qir);
5477 }
5478}
5479