Repository: microsoft/qsharp
Pull request #1519 (2024-05-14 03:23:25)

Fix for logic based on `CallToUnresolvedCallee` flag
The check for the dynamism of the return from unresolved callees incorrectly ended up applying to calls where the flag had propagated from another call site, including the entry point callable itself. This change introduces new infra to track the call expression that introduced the flag as opposed to all calls the flag had propagated to, which keeps the desired protection but avoids rejecting other call expressions.


Patch:
diff --git a/compiler/qsc_partial_eval/src/lib.rs b/compiler/qsc_partial_eval/src/lib.rs
index 686dde7f..61bb2c0f 100644
--- a/compiler/qsc_partial_eval/src/lib.rs
+++ b/compiler/qsc_partial_eval/src/lib.rs
@@ -981,13 +981,7 @@ impl<'a> PartialEvaluator<'a> {
         // Now that we are in evaluation, we have a distinct callable resolved and can perform runtime capability check
         // ahead of performing the actual call and return the appropriate capabilities error if this call is not supported
         // by the target.
-        let call_expr_compute_kind = self.get_expr_compute_kind(call_expr_id);
-        let call_was_unresolved = match call_expr_compute_kind {
-            ComputeKind::Quantum(props) => props
-                .runtime_features
-                .contains(RuntimeFeatureFlags::CallToUnresolvedCallee),
-            ComputeKind::Classical => false,
-        };
+        let call_was_unresolved = self.is_unresolved_callee_expr(callee_expr_id);
         if call_was_unresolved {
             let call_compute_kind = self.get_call_compute_kind(&call_scope);
             if let ComputeKind::Quantum(QuantumProperties {
@@ -1648,6 +1642,13 @@ impl<'a> PartialEvaluator<'a> {
         expr_generator_set.generate_application_compute_kind(&callable_scope.args_value_kind)
     }
 
+    fn is_unresolved_callee_expr(&self, expr_id: ExprId) -> bool {
+        let current_package_id = self.get_current_package_id();
+        let store_expr_id = StoreExprId::from((current_package_id, expr_id));
+        self.compute_properties
+            .is_unresolved_callee_expr(store_expr_id)
+    }
+
     fn get_call_compute_kind(&self, callable_scope: &Scope) -> ComputeKind {
         let store_item_id = StoreItemId::from((
             callable_scope.package_id,
diff --git a/compiler/qsc_rca/src/applications.rs b/compiler/qsc_rca/src/applications.rs
index 4a111f03..6a8bb8f7 100644
--- a/compiler/qsc_rca/src/applications.rs
+++ b/compiler/qsc_rca/src/applications.rs
@@ -384,6 +384,16 @@ impl GeneratorSetsBuilder {
                 .exprs
                 .insert(expr_id, application_generator_set);
         }
+
+        // Save the unresolved callee expressions.
+        for unresolved_callee_expr_id in inherent_application_compute_properties
+            .unresolved_callee_exprs
+            .drain(..)
+        {
+            package_compute_properties
+                .unresolved_callee_exprs
+                .push(unresolved_callee_expr_id);
+        }
     }
 }
 
@@ -406,6 +416,7 @@ pub struct ApplicationInstance {
     stmts: FxHashMap<StmtId, ComputeKind>,
     /// The compute kind of the expressions related to the application instance.
     exprs: FxHashMap<ExprId, ComputeKind>,
+    pub unresolved_callee_exprs: Vec<ExprId>,
 }
 
 impl ApplicationInstance {
@@ -505,6 +516,7 @@ impl ApplicationInstance {
             blocks: FxHashMap::default(),
             stmts: FxHashMap::default(),
             exprs: FxHashMap::default(),
+            unresolved_callee_exprs: Vec::new(),
         }
     }
 
@@ -562,6 +574,7 @@ impl ApplicationInstance {
             blocks: self.blocks,
             stmts: self.stmts,
             exprs: self.exprs,
+            unresolved_callee_exprs: self.unresolved_callee_exprs,
             value_kind,
         }
     }
@@ -619,6 +632,7 @@ struct ApplicationInstanceComputeProperties {
     stmts: FxHashMap<StmtId, ComputeKind>,
     exprs: FxHashMap<ExprId, ComputeKind>,
     value_kind: Option<ValueKind>,
+    unresolved_callee_exprs: Vec<ExprId>,
 }
 
 impl ApplicationInstanceComputeProperties {
diff --git a/compiler/qsc_rca/src/core.rs b/compiler/qsc_rca/src/core.rs
index ad68ad18..f5510543 100644
--- a/compiler/qsc_rca/src/core.rs
+++ b/compiler/qsc_rca/src/core.rs
@@ -511,6 +511,9 @@ impl<'a> Analyzer<'a> {
                 runtime_features: RuntimeFeatureFlags::CallToUnresolvedCallee,
                 value_kind,
             });
+            self.get_current_application_instance_mut()
+                .unresolved_callee_exprs
+                .push(callee_expr_id);
             return CallComputeKind::Regular(compute_kind);
         };
 
diff --git a/compiler/qsc_rca/src/lib.rs b/compiler/qsc_rca/src/lib.rs
index fe13f932..6fb654aa 100644
--- a/compiler/qsc_rca/src/lib.rs
+++ b/compiler/qsc_rca/src/lib.rs
@@ -30,6 +30,7 @@ use qsc_fir::{
     },
     ty::Ty,
 };
+use rustc_hash::FxHashSet;
 
 use std::{
     cmp::Ord,
@@ -140,6 +141,13 @@ impl PackageStoreComputeProperties {
     pub fn iter(&self) -> Iter<PackageId, PackageComputeProperties> {
         self.0.iter()
     }
+
+    #[must_use]
+    pub fn is_unresolved_callee_expr(&self, id: StoreExprId) -> bool {
+        self.get(id.package)
+            .unresolved_callee_exprs
+            .contains(&id.expr)
+    }
 }
 
 /// The compute properties of a package.
@@ -153,6 +161,8 @@ pub struct PackageComputeProperties {
     pub stmts: IndexMap<StmtId, ApplicationGeneratorSet>,
     /// The application generator sets of the package expressions.
     pub exprs: IndexMap<ExprId, ApplicationGeneratorSet>,
+    /// The expressions that were unresolved callees at analysis time.
+    pub unresolved_callee_exprs: FxHashSet<ExprId>,
 }
 
 impl Default for PackageComputeProperties {
@@ -162,6 +172,7 @@ impl Default for PackageComputeProperties {
             blocks: IndexMap::new(),
             stmts: IndexMap::new(),
             exprs: IndexMap::new(),
+            unresolved_callee_exprs: FxHashSet::default(),
         }
     }
 }
diff --git a/compiler/qsc_rca/src/scaffolding.rs b/compiler/qsc_rca/src/scaffolding.rs
index a814f2b0..f7e0ee15 100644
--- a/compiler/qsc_rca/src/scaffolding.rs
+++ b/compiler/qsc_rca/src/scaffolding.rs
@@ -35,6 +35,10 @@ impl From<PackageStoreComputeProperties> for InternalPackageStoreComputeProperti
                 blocks: package_compute_properties.blocks,
                 stmts: package_compute_properties.stmts,
                 exprs: package_compute_properties.exprs,
+                unresolved_callee_exprs: package_compute_properties
+                    .unresolved_callee_exprs
+                    .into_iter()
+                    .collect(),
             };
             scaffolding.insert(package_id, package_compute_properties);
         }
@@ -59,6 +63,10 @@ impl From<InternalPackageStoreComputeProperties> for PackageStoreComputeProperti
                 blocks: package_scaffolding.blocks,
                 stmts: package_scaffolding.stmts,
                 exprs: package_scaffolding.exprs,
+                unresolved_callee_exprs: package_scaffolding
+                    .unresolved_callee_exprs
+                    .into_iter()
+                    .collect(),
             };
             package_store_compute_properties.insert(package_id, package_compute_properties);
         }
@@ -194,6 +202,8 @@ pub struct InternalPackageComputeProperties {
     pub stmts: IndexMap<StmtId, ApplicationGeneratorSet>,
     /// The application generator sets of the package expressions.
     pub exprs: IndexMap<ExprId, ApplicationGeneratorSet>,
+    /// The expressions that were unresolved callees at analysis time.
+    pub unresolved_callee_exprs: Vec<ExprId>,
 }
 
 /// Scaffolding used to build the compute properties of an item.
diff --git a/compiler/qsc_rir/src/builder.rs b/compiler/qsc_rir/src/builder.rs
index aa67cc07..50ae4d09 100644
--- a/compiler/qsc_rir/src/builder.rs
+++ b/compiler/qsc_rir/src/builder.rs
@@ -121,7 +121,7 @@ pub fn result_record_decl() -> Callable {
 #[must_use]
 pub fn int_record_decl() -> Callable {
     Callable {
-        name: "__quantum__rt__integer_record_output".to_string(),
+        name: "__quantum__rt__int_record_output".to_string(),
         input_type: vec![Ty::Integer, Ty::Pointer],
         output_type: None,
         body: None,


Test patch:
diff --git a/compiler/qsc/src/codegen/tests.rs b/compiler/qsc/src/codegen/tests.rs
index f1d021a9..40160e82 100644
--- a/compiler/qsc/src/codegen/tests.rs
+++ b/compiler/qsc/src/codegen/tests.rs
@@ -626,7 +626,7 @@ mod adaptive_ri_profile {
               br label %block_3
             block_3:
               %var_3 = phi i64 [0, %block_1], [1, %block_2]
-              call void @__quantum__rt__integer_record_output(i64 %var_3, i8* null)
+              call void @__quantum__rt__int_record_output(i64 %var_3, i8* null)
               ret void
             }
 
@@ -636,7 +636,7 @@ mod adaptive_ri_profile {
 
             declare i1 @__quantum__qis__read_result__body(%Result*)
 
-            declare void @__quantum__rt__integer_record_output(i64, i8*)
+            declare void @__quantum__rt__int_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" }
diff --git a/compiler/qsc_partial_eval/tests/assigns.rs b/compiler/qsc_partial_eval/tests/assigns.rs
index 6821eb8d..85b03c9b 100644
--- a/compiler/qsc_partial_eval/tests/assigns.rs
+++ b/compiler/qsc_partial_eval/tests/assigns.rs
@@ -217,7 +217,7 @@ fn assigning_classical_int_updates_value_and_adds_store_instructions() {
         output_recording_callable_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -283,7 +283,7 @@ fn assigning_dynamic_int_updates_value_and_adds_store_instructions() {
         output_recording_callable_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -439,7 +439,7 @@ fn assigning_classical_int_within_dynamic_if_else_expression_adds_store_instruct
         output_recording_callable_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1170,7 +1170,7 @@ fn integer_assign_add_with_lhs_classical_integer_and_rhs_dynamic_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1250,7 +1250,7 @@ fn integer_assign_sub_with_lhs_dynamic_integer_and_rhs_classical_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1330,7 +1330,7 @@ fn integer_assign_mul_with_lhs_dynamic_integer_and_rhs_dynamic_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1421,7 +1421,7 @@ fn integer_assign_div_with_lhs_classical_integer_and_rhs_dynamic_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1501,7 +1501,7 @@ fn integer_assign_mod_with_lhs_dynamic_integer_and_rhs_classical_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1653,7 +1653,7 @@ fn integer_assign_bitwise_and_with_lhs_dynamic_integer_and_rhs_dynamic_integer()
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1744,7 +1744,7 @@ fn integer_assign_bitwise_or_with_lhs_classical_integer_and_rhs_dynamic_integer(
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1824,7 +1824,7 @@ fn integer_bitwise_xor_with_lhs_dynamic_integer_and_rhs_classical_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1904,7 +1904,7 @@ fn integer_assign_bitwise_left_shift_with_lhs_dynamic_integer_and_rhs_dynamic_in
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1995,7 +1995,7 @@ fn integer_assign_bitwise_right_shift_with_lhs_classical_integer_and_rhs_dynamic
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
diff --git a/compiler/qsc_partial_eval/tests/bindings.rs b/compiler/qsc_partial_eval/tests/bindings.rs
index ccfa7a76..568dd045 100644
--- a/compiler/qsc_partial_eval/tests/bindings.rs
+++ b/compiler/qsc_partial_eval/tests/bindings.rs
@@ -294,7 +294,7 @@ fn immutable_int_binding_does_not_generate_store_instruction() {
         output_recording_callable_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -368,7 +368,7 @@ fn mutable_int_binding_does_generate_store_instruction() {
         output_recording_callable_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
diff --git a/compiler/qsc_partial_eval/tests/calls.rs b/compiler/qsc_partial_eval/tests/calls.rs
index c14c9598..087161be 100644
--- a/compiler/qsc_partial_eval/tests/calls.rs
+++ b/compiler/qsc_partial_eval/tests/calls.rs
@@ -1314,3 +1314,35 @@ fn call_to_unresolved_callee_via_closure_with_dynamic_arg_fails() {
         &expect!["CapabilityError(UseOfDynamicDouble(Span { lo: 302, hi: 309 }))"],
     );
 }
+
+#[test]
+fn call_to_unresolved_callee_with_static_arg_and_entry_return_value_succeeds() {
+    let program = get_rir_program_with_capabilities(
+        indoc! {"
+        namespace Test {
+            open Microsoft.Quantum.Convert;
+            operation Op(i : Int, q : Qubit) : Unit {
+                Rx(IntAsDouble(i), q);
+            }
+            @EntryPoint()
+            operation Main() : Result {
+                use q = Qubit();
+                let f = [Op][0];
+                f(1, q);
+                MResetZ(q)
+            }
+        }"},
+        TargetCapabilityFlags::Adaptive | TargetCapabilityFlags::IntegerComputations,
+    );
+
+    assert_block_instructions(
+        &program,
+        BlockId(0),
+        &expect![[r#"
+            Block:
+                Call id(1), args( Double(1), Qubit(0), )
+                Call id(2), args( Qubit(0), Result(0), )
+                Call id(3), args( Result(0), Pointer, )
+                Return"#]],
+    );
+}
diff --git a/compiler/qsc_partial_eval/tests/intrinsics.rs b/compiler/qsc_partial_eval/tests/intrinsics.rs
index b1d2bcb7..b60cbcdb 100644
--- a/compiler/qsc_partial_eval/tests/intrinsics.rs
+++ b/compiler/qsc_partial_eval/tests/intrinsics.rs
@@ -1013,7 +1013,7 @@ fn call_to_length_in_inner_function_succeeds() {
         output_recording_callable_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
diff --git a/compiler/qsc_partial_eval/tests/misc.rs b/compiler/qsc_partial_eval/tests/misc.rs
index 6288af3d..4cdbe88c 100644
--- a/compiler/qsc_partial_eval/tests/misc.rs
+++ b/compiler/qsc_partial_eval/tests/misc.rs
@@ -301,7 +301,7 @@ fn integer_assign_with_hybrid_value_within_an_if_with_dynamic_condition() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
diff --git a/compiler/qsc_partial_eval/tests/operators.rs b/compiler/qsc_partial_eval/tests/operators.rs
index 86907b61..a36b2c37 100644
--- a/compiler/qsc_partial_eval/tests/operators.rs
+++ b/compiler/qsc_partial_eval/tests/operators.rs
@@ -58,7 +58,7 @@ fn leading_positive_unary_operator_does_not_generate_rir_instruction() {
         output_recording_callable_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -132,7 +132,7 @@ fn leading_negative_unary_operator_generates_rir_instruction() {
         output_recording_callable_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -207,7 +207,7 @@ fn bitwise_not_unary_operator_generates_rir_instruction() {
         output_recording_callable_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1137,7 +1137,7 @@ fn integer_add_with_lhs_classical_integer_and_rhs_dynamic_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1214,7 +1214,7 @@ fn integer_sub_with_lhs_dynamic_integer_and_rhs_classical_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1292,7 +1292,7 @@ fn integer_mul_with_lhs_dynamic_integer_and_rhs_dynamic_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1380,7 +1380,7 @@ fn integer_div_with_lhs_classical_integer_and_rhs_dynamic_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1477,7 +1477,7 @@ fn integer_mod_with_lhs_dynamic_integer_and_rhs_classical_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1625,7 +1625,7 @@ fn integer_bitwise_and_with_lhs_dynamic_integer_and_rhs_dynamic_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1713,7 +1713,7 @@ fn integer_bitwise_or_with_lhs_classical_integer_and_rhs_dynamic_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1790,7 +1790,7 @@ fn integer_bitwise_xor_with_lhs_dynamic_integer_and_rhs_classical_integer() {
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1868,7 +1868,7 @@ fn integer_bitwise_left_shif_with_lhs_dynamic_integer_and_rhs_dynamic_integer()
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
@@ -1956,7 +1956,7 @@ fn integer_bitwise_right_shift_with_lhs_classical_integer_and_rhs_dynamic_intege
         output_record_id,
         &expect![[r#"
             Callable:
-                name: __quantum__rt__integer_record_output
+                name: __quantum__rt__int_record_output
                 call_type: OutputRecording
                 input_type:
                     [0]: Integer
diff --git a/compiler/qsc_partial_eval/tests/output_recording.rs b/compiler/qsc_partial_eval/tests/output_recording.rs
index a83fcc41..88ecc575 100644
--- a/compiler/qsc_partial_eval/tests/output_recording.rs
+++ b/compiler/qsc_partial_eval/tests/output_recording.rs
@@ -429,7 +429,7 @@ fn output_recording_for_literal_int() {
                     output_type: <VOID>
                     body: 0
                 Callable 1: Callable:
-                    name: __quantum__rt__integer_record_output
+                    name: __quantum__rt__int_record_output
                     call_type: OutputRecording
                     input_type:
                         [0]: Integer