From 44522c5b14ec33bc20d1013986c4703cbe4bf292 Mon Sep 17 00:00:00 2001 From: Ian Davis Date: Thu, 21 Aug 2025 12:07:26 -0700 Subject: [PATCH] Adding extern compilation --- source/compiler/qsc_qasm/src/ast_builder.rs | 38 +++ source/compiler/qsc_qasm/src/compiler.rs | 118 +++++++-- source/compiler/qsc_qasm/src/semantic/ast.rs | 8 +- .../compiler/qsc_qasm/src/semantic/lowerer.rs | 18 +- .../openqasm_compiler_errors_test.qasm | 3 - .../src/semantic/tests/compiler_errors.rs | 17 +- .../src/semantic/tests/decls/extern_decl.rs | 43 +++- .../compiler/qsc_qasm/src/tests/statement.rs | 1 + .../src/tests/statement/extern_def.rs | 236 ++++++++++++++++++ 9 files changed, 434 insertions(+), 48 deletions(-) create mode 100644 source/compiler/qsc_qasm/src/tests/statement/extern_def.rs diff --git a/source/compiler/qsc_qasm/src/ast_builder.rs b/source/compiler/qsc_qasm/src/ast_builder.rs index 75cc1cd0f0..a20f0a1796 100644 --- a/source/compiler/qsc_qasm/src/ast_builder.rs +++ b/source/compiler/qsc_qasm/src/ast_builder.rs @@ -944,6 +944,14 @@ pub(crate) fn build_lit_double_expr(value: f64, span: Span) -> Expr { } } +pub(crate) fn build_expr_stmt_from_expr(expr: Expr) -> Stmt { + Stmt { + id: NodeId::default(), + span: expr.span, + kind: Box::new(StmtKind::Expr(Box::new(expr))), + } +} + pub(crate) fn build_stmt_semi_from_expr(expr: Expr) -> Stmt { Stmt { id: NodeId::default(), @@ -980,6 +988,14 @@ pub(crate) fn build_expr_wrapped_block_expr(expr: Expr) -> Block { } } +pub(crate) fn build_block_wrapped_stmts(stmts: Vec, span: Span) -> Block { + Block { + id: NodeId::default(), + span, + stmts: list_from_iter(stmts), + } +} + pub(crate) fn build_qasm_import_decl() -> Vec { build_qasm_import_items() .into_iter() @@ -1468,6 +1484,28 @@ pub(crate) fn build_barrier_call(span: Span) -> Stmt { build_stmt_semi_from_expr(expr) } +pub(crate) fn build_fail_stmt_with_message>(name: S, span: Span) -> Stmt { + let message = Expr { + kind: Box::new(ExprKind::Lit(Box::new(Lit::String( + format!( + "Extern `{}` cannot be used without a linked implementation.", + name.as_ref() + ) + .into(), + )))), + span, + ..Default::default() + }; + + let fail_expr = Expr { + kind: Box::new(ExprKind::Fail(Box::new(message))), + span, + ..Default::default() + }; + + build_stmt_semi_from_expr(fail_expr) +} + pub(crate) fn build_argument_validation_stmts(name: &String, ty: &Type, span: Span) -> Vec { assert!(ty.is_array(), "Expected array type"); assert!( diff --git a/source/compiler/qsc_qasm/src/compiler.rs b/source/compiler/qsc_qasm/src/compiler.rs index f6e7fc484d..fae9b936aa 100644 --- a/source/compiler/qsc_qasm/src/compiler.rs +++ b/source/compiler/qsc_qasm/src/compiler.rs @@ -19,26 +19,27 @@ use crate::{ build_adj_plus_ctl_functor, build_angle_cast_call_by_name, build_angle_convert_call_with_two_params, build_arg_pat, build_argument_validation_stmts, build_array_reverse_expr, build_assignment_statement, build_attr, build_barrier_call, - build_binary_expr, build_call_no_params, build_call_stmt_no_params, build_call_with_param, - build_call_with_params, build_classical_decl, build_complex_from_expr, - build_convert_call_expr, build_convert_cast_call_by_name, build_end_stmt, - build_expr_array_expr, build_for_stmt, build_function_or_operation, - build_gate_call_param_expr, build_gate_call_with_params_and_callee, - build_if_expr_then_block, build_if_expr_then_block_else_block, - build_if_expr_then_block_else_expr, build_if_expr_then_expr_else_expr, - build_implicit_return_stmt, build_index_expr, build_lit_angle_expr, build_lit_bigint_expr, - build_lit_bool_expr, build_lit_complex_expr, build_lit_double_expr, build_lit_int_expr, - build_lit_result_array_expr, build_lit_result_expr, build_managed_qubit_alloc, - build_math_call_from_exprs, build_math_call_no_params, build_measure_call, - build_measureeachz_call, build_operation_with_stmts, build_path_ident_expr, - build_path_ident_ty, build_qasm_convert_call_with_one_param, build_qasm_import_decl, - build_qasm_import_items, build_qasmstd_convert_call_with_two_params, build_range_expr, - build_reset_all_call, build_reset_call, build_return_expr, build_return_unit, - build_stmt_semi_from_expr, build_stmt_semi_from_expr_with_span, - build_top_level_ns_with_items, build_tuple_expr, build_unary_op_expr, - build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, build_while_stmt, - build_wrapped_block_expr, managed_qubit_alloc_array, map_qsharp_type_to_ast_ty, - wrap_expr_in_parens, + build_binary_expr, build_block_wrapped_stmts, build_call_no_params, + build_call_stmt_no_params, build_call_with_param, build_call_with_params, + build_classical_decl, build_complex_from_expr, build_convert_call_expr, + build_convert_cast_call_by_name, build_end_stmt, build_expr_array_expr, + build_expr_stmt_from_expr, build_fail_stmt_with_message, build_for_stmt, + build_function_or_operation, build_gate_call_param_expr, + build_gate_call_with_params_and_callee, build_if_expr_then_block, + build_if_expr_then_block_else_block, build_if_expr_then_block_else_expr, + build_if_expr_then_expr_else_expr, build_implicit_return_stmt, build_index_expr, + build_lit_angle_expr, build_lit_bigint_expr, build_lit_bool_expr, build_lit_complex_expr, + build_lit_double_expr, build_lit_int_expr, build_lit_result_array_expr, + build_lit_result_expr, build_managed_qubit_alloc, build_math_call_from_exprs, + build_math_call_no_params, build_measure_call, build_measureeachz_call, + build_operation_with_stmts, build_path_ident_expr, build_path_ident_ty, + build_qasm_convert_call_with_one_param, build_qasm_import_decl, build_qasm_import_items, + build_qasmstd_convert_call_with_two_params, build_range_expr, build_reset_all_call, + build_reset_call, build_return_expr, build_return_unit, build_stmt_semi_from_expr, + build_stmt_semi_from_expr_with_span, build_top_level_ns_with_items, build_tuple_expr, + build_unary_op_expr, build_unmanaged_qubit_alloc, build_unmanaged_qubit_alloc_array, + build_while_stmt, build_wrapped_block_expr, managed_qubit_alloc_array, + map_qsharp_type_to_ast_ty, wrap_expr_in_parens, }, io::SourceResolver, parser::ast::{List, PathKind, list_from_iter}, @@ -955,8 +956,81 @@ impl QasmCompiler { } fn compile_extern_stmt(&mut self, stmt: &semast::ExternDecl) -> Option { - self.push_unimplemented_error_message("extern statements", stmt.span); - None + let symbol = self.symbols[stmt.symbol_id].clone(); + let (params, return_type) = match &symbol.ty { + Type::Function(params, return_type) => (params, return_type), + _ => { + // this can happen if the def statement shadows a non-def symbol + // Since the symbol is not a function, we assume it returns an error type. + // There is already an error reported for this case. + (&[].into(), &Arc::from(crate::semantic::types::Type::Err)) + } + }; + + let name = symbol.name.clone(); + + // iterate over parmams with the value and index of the param + + let args: Vec<_> = params + .iter() + .enumerate() + .zip(stmt.param_spans.clone()) + .map(|((i, ty), span)| { + let name = format!("param_{i}"); + let qsharp_ty = self.map_semantic_type_to_qsharp_type(ty, *span); + let ast_type = map_qsharp_type_to_ast_ty(&qsharp_ty, *span); + ( + name.clone(), + ast_type.clone(), + build_arg_pat(name, symbol.span, ast_type), + ty.clone(), + ) + }) + .collect(); + + let fail_stmt = build_fail_stmt_with_message(&symbol.name, stmt.span); + let mut stmts = vec![fail_stmt]; + if let Some(ref default_value_expr) = stmt.default_value_expr { + // If there is a default value, we compile it and add it to the stmts + let default_value_expr = self.compile_expr(default_value_expr); + let default_stmt = build_expr_stmt_from_expr(default_value_expr); + stmts.push(default_stmt); + } + + let body = Some(build_block_wrapped_stmts(stmts, stmt.span)); + + let qsharp_ty = self.map_semantic_type_to_qsharp_type(return_type, stmt.return_ty_span); + let return_type = map_qsharp_type_to_ast_ty(&qsharp_ty, stmt.return_ty_span); + + // extern functions cannot have qubit parameters + // so we would normally use a function, but intrinsic attrs + // require us to use an operation + let kind = qsast::CallableKind::Operation; + + // If there was no qir intrinsic annotation, we generate one. + // we always want the annotation on externs, but this is safety mechanism + // we use the `QSHARP_QIR_INTRINSIC_ANNOTATION` since that is what the Q# compiler expects + let attrs = vec![build_attr( + QSHARP_QIR_INTRINSIC_ANNOTATION, + None::, + stmt.span, + )]; + + // We use the same primitives used for declaring gates, because def declarations + // in QASM can take qubits as arguments and call quantum gates. + Some(build_function_or_operation( + name, + args, + vec![], + body, + symbol.span, + stmt.span, + stmt.span, + return_type, + kind, + None, + list_from_iter(attrs), + )) } fn compile_for_stmt(&mut self, stmt: &semast::ForStmt) -> Option { diff --git a/source/compiler/qsc_qasm/src/semantic/ast.rs b/source/compiler/qsc_qasm/src/semantic/ast.rs index 5d7987a80f..77d16c33a7 100644 --- a/source/compiler/qsc_qasm/src/semantic/ast.rs +++ b/source/compiler/qsc_qasm/src/semantic/ast.rs @@ -428,12 +428,18 @@ impl Display for ExprStmt { pub struct ExternDecl { pub span: Span, pub symbol_id: SymbolId, + pub default_value_expr: Option, + pub param_spans: List, + pub return_ty_span: Span, } impl Display for ExternDecl { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { writeln_header(f, "ExternDecl", self.span)?; - write_field(f, "symbol_id", &self.symbol_id) + writeln_field(f, "symbol_id", &self.symbol_id)?; + writeln_opt_field(f, "default_value_expr", self.default_value_expr.as_ref())?; + writeln_list_field(f, "param_spans", &self.param_spans)?; + write_field(f, "return_ty_span", &self.return_ty_span) } } diff --git a/source/compiler/qsc_qasm/src/semantic/lowerer.rs b/source/compiler/qsc_qasm/src/semantic/lowerer.rs index c55b0ae8ab..df17bc3921 100644 --- a/source/compiler/qsc_qasm/src/semantic/lowerer.rs +++ b/source/compiler/qsc_qasm/src/semantic/lowerer.rs @@ -1936,14 +1936,16 @@ impl Lowerer { // 2. Build the parameter's type. let mut params = Vec::with_capacity(stmt.params.len()); + let mut param_spans = Vec::with_capacity(stmt.params.len()); for param in &stmt.params { let ty = self.lower_extern_param(param); params.push(ty); + param_spans.push(param.span()); } // 2. Build the return type. - let (return_ty, ty_span) = if let Some(ty) = &stmt.return_type { + let (return_ty, return_ty_span) = if let Some(ty) = &stmt.return_type { let ty_span = ty.span; let tydef = syntax::TypeDef::Scalar(ty.clone()); let ty = self.get_semantic_type_from_tydef(&tydef, false); @@ -1954,7 +1956,7 @@ impl Lowerer { if matches!(return_ty, crate::semantic::types::Type::Stretch(..)) { // extern functions cannot return stretches, so we push an error. - let kind = SemanticErrorKind::ExternDeclarationCannotReturnStretch(ty_span); + let kind = SemanticErrorKind::ExternDeclarationCannotReturnStretch(return_ty_span); self.push_semantic_error(kind); } @@ -1964,16 +1966,26 @@ impl Lowerer { // we also don't check the return type as it is a parse error to have an invalid return type. + // we can change the two above to parse successfully if we want to control sematic errors here. + // 3. Push the extern symbol to the symbol table. + let default_value_expr = if matches!(return_ty, Type::Void) { + None + } else { + Some(self.get_default_value(&return_ty, return_ty_span)) + }; let name = stmt.ident.name.clone(); let name_span = stmt.ident.span; let ty = crate::semantic::types::Type::Function(params.into(), return_ty.into()); - let symbol = Symbol::new(&name, name_span, ty, ty_span, IOKind::Default); + let symbol = Symbol::new(&name, name_span, ty, return_ty_span, IOKind::Default); let symbol_id = self.try_insert_or_get_existing_symbol_id(name, symbol); semantic::StmtKind::ExternDecl(semantic::ExternDecl { span: stmt.span, symbol_id, + default_value_expr, + param_spans: list_from_iter(param_spans), + return_ty_span, }) } diff --git a/source/compiler/qsc_qasm/src/semantic/resources/openqasm_compiler_errors_test.qasm b/source/compiler/qsc_qasm/src/semantic/resources/openqasm_compiler_errors_test.qasm index 58341e8f8c..14fb892924 100644 --- a/source/compiler/qsc_qasm/src/semantic/resources/openqasm_compiler_errors_test.qasm +++ b/source/compiler/qsc_qasm/src/semantic/resources/openqasm_compiler_errors_test.qasm @@ -74,8 +74,5 @@ def mut_subroutine_static(mutable array[int[8], 2, 3] arr_arg) { // NotSupported stretch types values //stretch stretch_val = dur0 - dur1; -// Unimplemented -extern extern_func(int); - // NotSupported hardware qubit x $0; diff --git a/source/compiler/qsc_qasm/src/semantic/tests/compiler_errors.rs b/source/compiler/qsc_qasm/src/semantic/tests/compiler_errors.rs index 61a7f5e5ad..5764beafe0 100644 --- a/source/compiler/qsc_qasm/src/semantic/tests/compiler_errors.rs +++ b/source/compiler/qsc_qasm/src/semantic/tests/compiler_errors.rs @@ -128,23 +128,12 @@ fn check_compiler_error_spans_are_correct() { 55 | `---- - Qasm.Compiler.Unimplemented - - x this statement is not yet handled during OpenQASM 3 import: extern - | statements - ,-[Test.qasm:78:1] - 77 | // Unimplemented - 78 | extern extern_func(int); - : ^^^^^^^^^^^^^^^^^^^^^^^^ - 79 | - `---- - Qasm.Compiler.NotSupported x hardware qubit operands are not supported - ,-[Test.qasm:81:3] - 80 | // NotSupported hardware qubit - 81 | x $0; + ,-[Test.qasm:78:3] + 77 | // NotSupported hardware qubit + 78 | x $0; : ^^ `---- "#]], diff --git a/source/compiler/qsc_qasm/src/semantic/tests/decls/extern_decl.rs b/source/compiler/qsc_qasm/src/semantic/tests/decls/extern_decl.rs index 354f8ff590..c0e1bac314 100644 --- a/source/compiler/qsc_qasm/src/semantic/tests/decls/extern_decl.rs +++ b/source/compiler/qsc_qasm/src/semantic/tests/decls/extern_decl.rs @@ -10,7 +10,10 @@ fn void_no_args() { "extern f();", &expect![[r#" ExternDecl [0-11]: - symbol_id: 8"#]], + symbol_id: 8 + default_value_expr: + param_spans: + return_ty_span: [0-0]"#]], ); } @@ -20,7 +23,11 @@ fn void_one_arg() { "extern f(int);", &expect![[r#" ExternDecl [0-14]: - symbol_id: 8"#]], + symbol_id: 8 + default_value_expr: + param_spans: + [9-12] + return_ty_span: [0-0]"#]], ); } @@ -30,7 +37,15 @@ fn void_multiple_args() { "extern f(uint, int, float, bit, bool);", &expect![[r#" ExternDecl [0-38]: - symbol_id: 8"#]], + symbol_id: 8 + default_value_expr: + param_spans: + [9-13] + [15-18] + [20-25] + [27-30] + [32-36] + return_ty_span: [0-0]"#]], ); } @@ -40,7 +55,12 @@ fn return_type() { "extern f() -> int;", &expect![[r#" ExternDecl [0-18]: - symbol_id: 8"#]], + symbol_id: 8 + default_value_expr: Expr [14-17]: + ty: const int + kind: Lit: Int(0) + param_spans: + return_ty_span: [14-17]"#]], ); } @@ -50,7 +70,12 @@ fn return_type_can_be_duration() { "extern f() -> duration;", &expect![[r#" ExternDecl [0-23]: - symbol_id: 8"#]], + symbol_id: 8 + default_value_expr: Expr [14-22]: + ty: duration + kind: Lit: Duration(0.0 s) + param_spans: + return_ty_span: [14-22]"#]], ); } @@ -67,6 +92,11 @@ fn return_type_cannot_be_stretch() { annotations: kind: ExternDecl [0-22]: symbol_id: 8 + default_value_expr: Expr [14-21]: + ty: stretch + kind: Lit: Duration(0.0 s) + param_spans: + return_ty_span: [14-21] [Qasm.Lowerer.ExternDeclarationCannotReturnStretch @@ -95,6 +125,9 @@ fn not_allowed_in_non_global_scope() { annotations: kind: ExternDecl [2-13]: symbol_id: 8 + default_value_expr: + param_spans: + return_ty_span: [0-0] [Qasm.Lowerer.ExternDeclarationInNonGlobalScope diff --git a/source/compiler/qsc_qasm/src/tests/statement.rs b/source/compiler/qsc_qasm/src/tests/statement.rs index ad8a8ca456..2515b46026 100644 --- a/source/compiler/qsc_qasm/src/tests/statement.rs +++ b/source/compiler/qsc_qasm/src/tests/statement.rs @@ -4,6 +4,7 @@ mod annotation; mod const_eval; mod end; +mod extern_def; mod for_loop; mod gate_call; mod if_stmt; diff --git a/source/compiler/qsc_qasm/src/tests/statement/extern_def.rs b/source/compiler/qsc_qasm/src/tests/statement/extern_def.rs new file mode 100644 index 0000000000..7c9d0c9c22 --- /dev/null +++ b/source/compiler/qsc_qasm/src/tests/statement/extern_def.rs @@ -0,0 +1,236 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::tests::{compile_qasm_to_qir, compile_qasm_to_qsharp}; +use expect_test::expect; +use miette::Report; + +// some types aren't supported for code gen +// angle +// duration +// bigint +// complex + +#[test] +fn void_ty_is_mapped() -> miette::Result<(), Vec> { + let source = r#" + extern fn(); + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import Std.OpenQASM.Intrinsic.*; + @SimulatableIntrinsic() + operation fn() : Unit { + fail "Extern `fn` cannot be used without a linked implementation."; + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn bit_ty_is_mapped() -> miette::Result<(), Vec> { + let source = r#" + extern fn(bit) -> bit; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import Std.OpenQASM.Intrinsic.*; + @SimulatableIntrinsic() + operation fn(param_0 : Result) : Result { + fail "Extern `fn` cannot be used without a linked implementation."; + Zero + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn bool_ty_is_mapped() -> miette::Result<(), Vec> { + let source = r#" + extern fn(bool) -> bool; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import Std.OpenQASM.Intrinsic.*; + @SimulatableIntrinsic() + operation fn(param_0 : Bool) : Bool { + fail "Extern `fn` cannot be used without a linked implementation."; + false + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn complex_ty_is_mapped() -> miette::Result<(), Vec> { + let source = r#" + extern fn(complex, complex[float[32]]) -> complex; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import Std.OpenQASM.Intrinsic.*; + @SimulatableIntrinsic() + operation fn(param_0 : Std.Math.Complex, param_1 : Std.Math.Complex) : Std.Math.Complex { + fail "Extern `fn` cannot be used without a linked implementation."; + Std.Math.Complex(0., 0.) + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn float_ty_is_mapped() -> miette::Result<(), Vec> { + let source = r#" + extern fn(float, float[17]) -> float; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import Std.OpenQASM.Intrinsic.*; + @SimulatableIntrinsic() + operation fn(param_0 : Double, param_1 : Double) : Double { + fail "Extern `fn` cannot be used without a linked implementation."; + 0. + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn int_ty_is_mapped() -> miette::Result<(), Vec> { + let source = r#" + extern fn(int, int[17]) -> int; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import Std.OpenQASM.Intrinsic.*; + @SimulatableIntrinsic() + operation fn(param_0 : Int, param_1 : Int) : Int { + fail "Extern `fn` cannot be used without a linked implementation."; + 0 + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn uint_ty_is_mapped() -> miette::Result<(), Vec> { + let source = r#" + extern fn(uint, uint[17]) -> uint; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import Std.OpenQASM.Intrinsic.*; + @SimulatableIntrinsic() + operation fn(param_0 : Int, param_1 : Int) : Int { + fail "Extern `fn` cannot be used without a linked implementation."; + 0 + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn big_uint_ty_is_mapped() -> miette::Result<(), Vec> { + let source = r#" + extern fn(uint[71]) -> uint[99]; + "#; + + let qsharp = compile_qasm_to_qsharp(source)?; + expect![[r#" + import Std.OpenQASM.Intrinsic.*; + @SimulatableIntrinsic() + operation fn(param_0 : BigInt) : BigInt { + fail "Extern `fn` cannot be used without a linked implementation."; + 0L + } + "#]] + .assert_eq(&qsharp); + Ok(()) +} + +#[test] +fn extern_generates_qir() -> miette::Result<(), Vec> { + let source = r#" + include "stdgates.inc"; + #pragma qdk.qir.profile Adaptive_RIF + bit c; + qubit q; + c = measure q; + + extern void_fn(); + extern bit_fn(bit) -> bit; + extern bool_fn(bool) -> bool; + extern float_fn(float, float[17]) -> float; + extern int_fn(int, int[17]) -> int; + extern uint_fn(uint, uint[17]) -> uint; + + void_fn(); + //bit_fn(c); // use of dynamic result + bool_fn(true); + float_fn(pi, tau); + int_fn(42, -17); + uint_fn(74, 65); + "#; + + let qsharp = compile_qasm_to_qir(source)?; + expect![[r#" + %Result = type opaque + %Qubit = type opaque + + define void @ENTRYPOINT__main() #0 { + block_0: + call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @void_fn() + %var_0 = call i1 @bool_fn(i1 true) + %var_1 = call double @float_fn(double 3.141592653589793, double 6.283185307179586) + %var_2 = call i64 @int_fn(i64 42, i64 -17) + %var_3 = call i64 @uint_fn(i64 74, i64 65) + call void @__quantum__rt__tuple_record_output(i64 0, i8* null) + ret void + } + + declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1 + + declare void @void_fn() + + declare i1 @bool_fn(i1) + + declare double @float_fn(double, double) + + declare i64 @int_fn(i64, i64) + + declare i64 @uint_fn(i64, i64) + + declare void @__quantum__rt__tuple_record_output(i64, i8*) + + attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="1" "required_num_results"="1" } + attributes #1 = { "irreversible" } + + ; module flags + + !llvm.module.flags = !{!0, !1, !2, !3, !4, !5} + + !0 = !{i32 1, !"qir_major_version", i32 1} + !1 = !{i32 7, !"qir_minor_version", i32 0} + !2 = !{i32 1, !"dynamic_qubit_management", i1 false} + !3 = !{i32 1, !"dynamic_result_management", i1 false} + !4 = !{i32 1, !"int_computations", !"i64"} + !5 = !{i32 1, !"float_computations", !"f64"} + "#]] + .assert_eq(&qsharp); + Ok(()) +}