Repository: microsoft/qsharp
Pull request #1981 (2024-10-29 18:19:46)

Add support for custom resets using the `@Reset()` attribute
This PR adds support for custom resets using the `@Reset` attribute. It consists of the following changes:
 - Adding a `@Reset` attribute to the language.
 - Piping it down to FIR.
 - Using the attribute to generate the correct QIR, including the `"irreversible"` attribute.
 - Semantic validation passes in `qsc_passes` based on the `@Reset` attribute.
 - The right handling of reset operations during partial evaluation.
 - Unit tests.
 - Update previous tests to include the `#1` irreversible attribute in QIR when calling the Reset operation.

github-actions[bot]: _Change in memory usage detected by benchmark._
## Memory Report for 94bfb0f50631caa927667f927c57aff95835d967

| Test                        | This Branch | On Main  | Difference |
|-----------------------------|-------------|----------| ---------- |
| compile core + standard lib | 17964197 bytes | 17945380 bytes | 18817 bytes
github-actions[bot]: _Change in memory usage detected by benchmark._
## Memory Report for cb50809610f388e169fbd8432a2ce4202a22cf49

| Test                        | This Branch | On Main  | Difference |
|-----------------------------|-------------|----------| ---------- |
| compile core + standard lib | 17964197 bytes | 17945380 bytes | 18817 bytes

Patch:
diff --git a/compiler/qsc_codegen/src/qir.rs b/compiler/qsc_codegen/src/qir.rs
index 45f7c04f..e80fcc06 100644
--- a/compiler/qsc_codegen/src/qir.rs
+++ b/compiler/qsc_codegen/src/qir.rs
@@ -504,8 +504,11 @@ impl ToQir<String> for rir::Callable {
             return format!(
                 "declare {output_type} @{}({input_type}){}",
                 self.name,
-                if self.call_type == rir::CallableType::Measurement {
-                    // Measurement callables are a special case that needs the irreversable attribute.
+                if matches!(
+                    self.call_type,
+                    rir::CallableType::Measurement | rir::CallableType::Reset
+                ) {
+                    // These callables are a special case that need the irreversable attribute.
                     " #1"
                 } else {
                     ""
diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs
index f3f6188a..5fc87365 100644
--- a/compiler/qsc_doc_gen/src/generate_docs.rs
+++ b/compiler/qsc_doc_gen/src/generate_docs.rs
@@ -350,6 +350,18 @@ enum PackageKind {
     Core,
 }
 
+impl Display for PackageKind {
+    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+        let s = match &self {
+            PackageKind::UserCode => "__Main__",
+            PackageKind::AliasedPackage(alias) => alias,
+            PackageKind::StandardLibrary => "__Std__",
+            PackageKind::Core => "__Core__",
+        };
+        write!(f, "{s}")
+    }
+}
+
 impl Display for Metadata {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
         let kind = match &self.kind {
@@ -366,11 +378,19 @@ title: {}
 ms.date: {{TIMESTAMP}}
 ms.topic: {}
 qsharp.kind: {}
+qsharp.package: {}
 qsharp.namespace: {}
 qsharp.name: {}
 qsharp.summary: \"{}\"
 ---",
-            self.uid, self.title, self.topic, kind, self.namespace, self.name, self.summary
+            self.uid,
+            self.title,
+            self.topic,
+            kind,
+            self.package,
+            self.namespace,
+            self.name,
+            self.summary
         )
     }
 }
diff --git a/compiler/qsc_fir/src/fir.rs b/compiler/qsc_fir/src/fir.rs
index 844976dd..40a24330 100644
--- a/compiler/qsc_fir/src/fir.rs
+++ b/compiler/qsc_fir/src/fir.rs
@@ -756,6 +756,8 @@ pub struct CallableDecl {
     pub functors: FunctorSetValue,
     /// The callable implementation.
     pub implementation: CallableImpl,
+    /// The attributes of the callable, (e.g.: Measurement or Reset).
+    pub attrs: Vec<Attr>,
 }
 
 impl CallableDecl {
@@ -1521,6 +1523,8 @@ pub enum Attr {
     EntryPoint,
     /// Indicates that a callable is a measurement.
     Measurement,
+    /// Indicates that a callable is a reset.
+    Reset,
 }
 
 /// A field.
diff --git a/compiler/qsc_frontend/src/closure.rs b/compiler/qsc_frontend/src/closure.rs
index b24d31a0..62bd69b1 100644
--- a/compiler/qsc_frontend/src/closure.rs
+++ b/compiler/qsc_frontend/src/closure.rs
@@ -151,6 +151,7 @@ pub(super) fn lift(
         adj: None,
         ctl: None,
         ctl_adj: None,
+        attrs: Vec::default(),
     };
 
     (free_vars, callable)
diff --git a/compiler/qsc_frontend/src/lower.rs b/compiler/qsc_frontend/src/lower.rs
index 2b881027..f3a7a89f 100644
--- a/compiler/qsc_frontend/src/lower.rs
+++ b/compiler/qsc_frontend/src/lower.rs
@@ -30,10 +30,10 @@ pub(super) enum Error {
     #[error("invalid attribute arguments: expected {0}")]
     #[diagnostic(code("Qsc.LowerAst.InvalidAttrArgs"))]
     InvalidAttrArgs(String, #[label] Span),
-    #[error("invalid use of the Measurement attribute on a function")]
+    #[error("invalid use of the {0} attribute on a function")]
     #[diagnostic(help("try declaring the callable as an operation"))]
-    #[diagnostic(code("Qsc.LowerAst.InvalidMeasurementAttrOnFunction"))]
-    InvalidMeasurementAttrOnFunction(#[label] Span),
+    #[diagnostic(code("Qsc.LowerAst.InvalidAttrOnFunction"))]
+    InvalidAttrOnFunction(String, #[label] Span),
     #[error("missing callable body")]
     #[diagnostic(code("Qsc.LowerAst.MissingBody"))]
     MissingBody(#[label] Span),
@@ -368,6 +368,15 @@ impl With<'_> {
                     None
                 }
             },
+            Ok(hir::Attr::Reset) => match &*attr.arg.kind {
+                ast::ExprKind::Tuple(args) if args.is_empty() => Some(hir::Attr::Reset),
+                _ => {
+                    self.lowerer
+                        .errors
+                        .push(Error::InvalidAttrArgs("()".to_string(), attr.arg.span));
+                    None
+                }
+            },
             Err(()) => {
                 self.lowerer.errors.push(Error::UnknownAttr(
                     attr.name.name.to_string(),
@@ -429,29 +438,40 @@ impl With<'_> {
             adj,
             ctl,
             ctl_adj,
+            attrs: attrs.to_vec(),
+        }
+    }
+
+    fn check_invalid_attrs_on_function(&mut self, attrs: &[hir::Attr], span: Span) {
+        const INVALID_ATTRS: [hir::Attr; 2] = [hir::Attr::Measurement, hir::Attr::Reset];
+
+        for invalid_attr in &INVALID_ATTRS {
+            if attrs.contains(invalid_attr) {
+                self.lowerer.errors.push(Error::InvalidAttrOnFunction(
+                    format!("{invalid_attr:?}"),
+                    span,
+                ));
+            }
         }
     }
 
     fn lower_callable_kind(
         &mut self,
         kind: ast::CallableKind,
-        attrs: &[qsc_hir::hir::Attr],
+        attrs: &[hir::Attr],
         span: Span,
     ) -> hir::CallableKind {
-        if attrs.contains(&qsc_hir::hir::Attr::Measurement) {
-            match kind {
-                ast::CallableKind::Operation => hir::CallableKind::Measurement,
-                ast::CallableKind::Function => {
-                    self.lowerer
-                        .errors
-                        .push(Error::InvalidMeasurementAttrOnFunction(span));
-                    hir::CallableKind::Function
-                }
+        match kind {
+            ast::CallableKind::Function => {
+                self.check_invalid_attrs_on_function(attrs, span);
+                hir::CallableKind::Function
             }
-        } else {
-            match kind {
-                ast::CallableKind::Operation => hir::CallableKind::Operation,
-                ast::CallableKind::Function => hir::CallableKind::Function,
+            ast::CallableKind::Operation => {
+                if attrs.contains(&hir::Attr::Measurement) {
+                    hir::CallableKind::Measurement
+                } else {
+                    hir::CallableKind::Operation
+                }
             }
         }
     }
diff --git a/compiler/qsc_hir/src/hir.rs b/compiler/qsc_hir/src/hir.rs
index a39caa7e..3ea39201 100644
--- a/compiler/qsc_hir/src/hir.rs
+++ b/compiler/qsc_hir/src/hir.rs
@@ -391,6 +391,8 @@ pub struct CallableDecl {
     pub ctl: Option<SpecDecl>,
     /// The controlled adjoint specialization.
     pub ctl_adj: Option<SpecDecl>,
+    /// The attributes of the callable, (e.g.: Measurement or Reset).
+    pub attrs: Vec<Attr>,
 }
 
 impl CallableDecl {
@@ -1352,8 +1354,11 @@ pub enum Attr {
     /// and any implementation should be ignored.
     SimulatableIntrinsic,
     /// Indicates that a callable is a measurement. This means that the operation will be marked as
-    /// "irreversible" in the generated QIR.
+    /// "irreversible" in the generated QIR, and output Result types will be moved to the arguments.
     Measurement,
+    /// Indicates that a callable is a reset. This means that the operation will be marked as
+    /// "irreversible" in the generated QIR.
+    Reset,
 }
 
 impl FromStr for Attr {
@@ -1366,6 +1371,7 @@ impl FromStr for Attr {
             "Unimplemented" => Ok(Self::Unimplemented),
             "SimulatableIntrinsic" => Ok(Self::SimulatableIntrinsic),
             "Measurement" => Ok(Self::Measurement),
+            "Reset" => Ok(Self::Reset),
             _ => Err(()),
         }
     }
diff --git a/compiler/qsc_lowerer/src/lib.rs b/compiler/qsc_lowerer/src/lib.rs
index 44a46aac..5b7d8fe6 100644
--- a/compiler/qsc_lowerer/src/lib.rs
+++ b/compiler/qsc_lowerer/src/lib.rs
@@ -282,6 +282,7 @@ impl Lowerer {
             };
             CallableImpl::Spec(specialized_implementation)
         };
+        let attrs = lower_attrs(&decl.attrs);
 
         self.assigner.reset_local();
         self.locals.clear();
@@ -299,6 +300,7 @@ impl Lowerer {
             output,
             functors,
             implementation,
+            attrs,
         }
     }
 
@@ -888,6 +890,7 @@ fn lower_attrs(attrs: &[hir::Attr]) -> Vec<fir::Attr> {
         .filter_map(|attr| match attr {
             hir::Attr::EntryPoint => Some(fir::Attr::EntryPoint),
             hir::Attr::Measurement => Some(fir::Attr::Measurement),
+            hir::Attr::Reset => Some(fir::Attr::Reset),
             hir::Attr::SimulatableIntrinsic | hir::Attr::Unimplemented | hir::Attr::Config => None,
         })
         .collect()
diff --git a/compiler/qsc_partial_eval/src/lib.rs b/compiler/qsc_partial_eval/src/lib.rs
index e362968a..b43b5075 100644
--- a/compiler/qsc_partial_eval/src/lib.rs
+++ b/compiler/qsc_partial_eval/src/lib.rs
@@ -1313,6 +1313,9 @@ impl<'a> PartialEvaluator<'a> {
         if matches!(callable_decl.kind, qsc_fir::fir::CallableKind::Measurement) {
             return self.measure_qubits(callable_decl, args_value, args_span);
         }
+        if callable_decl.attrs.contains(&fir::Attr::Reset) {
+            return self.reset_qubits(store_item_id, callable_decl, args_value);
+        }
 
         // There are a few special cases regarding intrinsic callables. Identify them and handle them properly.
         match callable_decl.name.name.as_ref() {
@@ -2372,6 +2375,60 @@ impl<'a> PartialEvaluator<'a> {
         result_value
     }
 
+    fn reset_qubits(
+        &mut self,
+        store_item_id: StoreItemId,
+        callable_decl: &CallableDecl,
+        args_value: Value,
+    ) -> Result<Value, Error> {
+        let callable_package = self.package_store.get(store_item_id.package);
+        let input_type: Vec<rir::Ty> = callable_package
+            .derive_callable_input_params(callable_decl)
+            .iter()
+            .map(|input_param| map_fir_type_to_rir_type(&input_param.ty))
+            .collect();
+        let output_type = if callable_decl.output == Ty::UNIT {
+            None
+        } else {
+            panic!("the expressions that make it to this point should return Unit");
+        };
+
+        let measurement_callable = Callable {
+            name: callable_decl.name.name.to_string(),
+            input_type,
+            output_type,
+            body: None,
+            call_type: CallableType::Reset,
+        };
+
+        // Resolve the call arguments, create the call instruction and insert it to the current block.
+        let (args, ctls_arg) = self
+            .resolve_args(
+                (store_item_id.package, callable_decl.input).into(),
+                args_value,
+                None,
+                None,
+                None,
+            )
+            .expect("no controls to verify");
+        assert!(
+            ctls_arg.is_none(),
+            "intrinsic operations cannot have controls"
+        );
+        let operands = args
+            .into_iter()
+            .map(|arg| self.map_eval_value_to_rir_operand(&arg.into_value()))
+            .collect();
+
+        // Check if the callable has already been added to the program and if not do so now.
+        let measure_callable_id = self.get_or_insert_callable(measurement_callable);
+        let instruction = Instruction::Call(measure_callable_id, operands, None);
+        let current_block = self.get_current_rir_block_mut();
+        current_block.0.push(instruction);
+
+        Ok(Value::unit())
+    }
+
     fn release_qubit(&mut self, args_value: Value) -> Value {
         let qubit = args_value.unwrap_qubit();
         self.resource_manager.release_qubit(qubit);
diff --git a/compiler/qsc_passes/src/lib.rs b/compiler/qsc_passes/src/lib.rs
index 76cc40e9..c20a3816 100644
--- a/compiler/qsc_passes/src/lib.rs
+++ b/compiler/qsc_passes/src/lib.rs
@@ -13,6 +13,7 @@ mod logic_sep;
 mod loop_unification;
 mod measurement;
 mod replace_qubit_allocation;
+mod reset;
 mod spec_gen;
 
 use callable_limits::CallableLimits;
@@ -49,6 +50,7 @@ pub enum Error {
     ConjInvert(conjugate_invert::Error),
     EntryPoint(entry_point::Error),
     Measurement(measurement::Error),
+    Reset(reset::Error),
     SpecGen(spec_gen::Error),
 }
 
@@ -108,6 +110,7 @@ impl PassContext {
         Validator::default().visit_package(package);
 
         let measurement_decl_errors = measurement::validate_measurement_declarations(package);
+        let reset_decl_errors = reset::validate_reset_declarations(package);
 
         let entry_point_errors = generate_entry_expr(package, assigner, package_type);
         Validator::default().visit_package(package);
@@ -126,6 +129,7 @@ impl PassContext {
             .chain(conjugate_errors.into_iter().map(Error::ConjInvert))
             .chain(entry_point_errors)
             .chain(measurement_decl_errors.into_iter().map(Error::Measurement))
+            .chain(reset_decl_errors.into_iter().map(Error::Reset))
             .collect()
     }
 
diff --git a/compiler/qsc_passes/src/measurement.rs b/compiler/qsc_passes/src/measurement.rs
index f9a29349..d1a87928 100644
--- a/compiler/qsc_passes/src/measurement.rs
+++ b/compiler/qsc_passes/src/measurement.rs
@@ -15,7 +15,9 @@ use thiserror::Error;
 
 #[derive(Clone, Debug, Diagnostic, Error)]
 pub enum Error {
-    #[error("a callable with the measurement attribute should take at least one argument of type")]
+    #[error(
+        "a callable with the measurement attribute should take at least one argument of type Qubit"
+    )]
     #[diagnostic(code("Qsc.Measurement.NoArguments"))]
     NoArguments(#[label] Span),
 
diff --git a/compiler/qsc_passes/src/reset.rs b/compiler/qsc_passes/src/reset.rs
new file mode 100644
index 00000000..9c2c0e5d
--- /dev/null
+++ b/compiler/qsc_passes/src/reset.rs
@@ -0,0 +1,101 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#[cfg(test)]
+mod tests;
+
+use miette::Diagnostic;
+use qsc_data_structures::span::Span;
+use qsc_hir::{
+    hir::{Attr, CallableDecl, Item, ItemKind, Package, SpecBody, SpecGen},
+    ty::{Prim, Ty},
+    visit::Visitor,
+};
+use thiserror::Error;
+
+#[derive(Clone, Debug, Diagnostic, Error)]
+pub enum Error {
+    #[error("a callable with the reset attribute should take at least one argument of type Qubit")]
+    #[diagnostic(code("Qsc.Reset.NoArguments"))]
+    NoArguments(#[label] Span),
+
+    #[error("a callable with the reset attribute should only take arguments of type Qubit")]
+    #[diagnostic(code("Qsc.Reset.NonQubitArgument"))]
+    NonQubitArgument(#[label] Span),
+
+    #[error("a callable with the reset attribute should have output of type Unit")]
+    #[diagnostic(code("Qsc.Reset.NonResultOutput"))]
+    NonUnitOutput(#[label] Span),
+
+    #[error("a callable with the reset attribute should be an intrinsic")]
+    #[diagnostic(code("Qsc.Reset.NotIntrinsic"))]
+    NotIntrinsic(#[label] Span),
+}
+
+/// For each reset declaration check that:
+///  1. It takes at least one argument.
+///  2. It only takes Qubits as arguments.
+///  3. It only outputs Results.
+///  4. It is an intrinsic.
+pub(super) fn validate_reset_declarations(package: &Package) -> Vec<Error> {
+    let mut validator = ResetValidator { errors: Vec::new() };
+    validator.visit_package(package);
+    validator.errors
+}
+
+fn validate_reset_declaration(decl: &CallableDecl, attrs: &[Attr], errors: &mut Vec<Error>) {
+    // 1. Check that the declaration takes at least one argument.
+    if decl.input.ty == Ty::UNIT {
+        errors.push(Error::NoArguments(decl.input.span));
+    }
+
+    // 2. Check that the declaration only takes Qubits as arguments.
+    match &decl.input.ty {
+        Ty::Prim(Prim::Qubit) => (),
+        Ty::Tuple(types) => {
+            for ty in types {
+                if !matches!(ty, Ty::Prim(Prim::Qubit)) {
+                    errors.push(Error::NonQubitArgument(decl.input.span));
+                    // break so that we don't repeat the same error multiple times
+                    break;
+                }
+            }
+        }
+        _ => errors.push(Error::NonQubitArgument(decl.input.span)),
+    }
+
+    // 3. Check that the declaration only outputs Unit.
+    if decl.output != Ty::UNIT {
+        errors.push(Error::NonUnitOutput(decl.span));
+    }
+
+    // 4. Check that the declaration is an intrinsic.
+    if !decl_is_intrinsic(decl, attrs) {
+        errors.push(Error::NotIntrinsic(decl.name.span));
+    }
+}
+
+/// Returns `true` if a declaration is an intrinsic. A declaration is
+/// an intrinsic if it has `body intrinsic;` in its body or if it has
+/// the `@SimulatableIntrinsic()` attribute.
+fn decl_is_intrinsic(decl: &CallableDecl, attrs: &[Attr]) -> bool {
+    matches!(decl.body.body, SpecBody::Gen(SpecGen::Intrinsic))
+        || attrs
+            .iter()
+            .any(|attr| matches!(attr, Attr::SimulatableIntrinsic))
+}
+
+/// A helper structure to find and validate reset callables in a Package.
+struct ResetValidator {
+    errors: Vec<Error>,
+}
+
+impl<'a> Visitor<'a> for ResetValidator {
+    fn visit_item(&mut self, item: &'a Item) {
+        if let ItemKind::Callable(callable) = &item.kind {
+            if item.attrs.contains(&Attr::Reset) {
+                validate_reset_declaration(callable, &item.attrs, &mut self.errors);
+            }
+        }
+    }
+}
diff --git a/compiler/qsc_rca/src/core.rs b/compiler/qsc_rca/src/core.rs
index ce945a79..be71a55c 100644
--- a/compiler/qsc_rca/src/core.rs
+++ b/compiler/qsc_rca/src/core.rs
@@ -15,10 +15,11 @@ use qsc_data_structures::{functors::FunctorApp, index_map::IndexMap};
 use qsc_fir::{
     extensions::InputParam,
     fir::{
-        BinOp, Block, BlockId, CallableDecl, CallableImpl, CallableKind, Expr, ExprId, ExprKind,
-        FieldAssign, Global, Ident, Item, ItemKind, LocalVarId, Mutability, Package, PackageId,
-        PackageLookup, PackageStore, PackageStoreLookup, Pat, PatId, PatKind, Res, SpecDecl,
-        SpecImpl, Stmt, StmtId, StmtKind, StoreExprId, StoreItemId, StorePatId, StringComponent,
+        Attr, BinOp, Block, BlockId, CallableDecl, CallableImpl, CallableKind, Expr, ExprId,
+        ExprKind, FieldAssign, Global, Ident, Item, ItemKind, LocalVarId, Mutability, Package,
+        PackageId, PackageLookup, PackageStore, PackageStoreLookup, Pat, PatId, PatKind, Res,
+        SpecDecl, SpecImpl, Stmt, StmtId, StmtKind, StoreExprId, StoreItemId, StorePatId,
+        StringComponent,
     },
     ty::{Arrow, FunctorSetValue, Prim, Ty},
     visit::{walk_stmt, Visitor},
@@ -1239,6 +1240,7 @@ impl<'a> Analyzer<'a> {
             callable_decl.kind,
             input_params,
             callable_decl.output.clone(),
+            &callable_decl.attrs,
         );
 
         // Continue with the analysis differently depending on whether the callable is an intrinsic or not.
@@ -1788,7 +1790,12 @@ impl<'a> Visitor<'a> for Analyzer<'a> {
         let package = self.package_store.get(package_id);
         let input_params = package.derive_callable_input_params(decl);
         let current_callable_context = self.get_current_item_context_mut();
-        current_callable_context.set_callable_context(decl.kind, input_params, decl.output.clone());
+        current_callable_context.set_callable_context(
+            decl.kind,
+            input_params,
+            decl.output.clone(),
+            &decl.attrs,
+        );
         self.visit_callable_impl(&decl.implementation);
     }
 
@@ -2129,12 +2136,14 @@ impl ItemContext {
         kind: CallableKind,
         input_params: Vec<InputParam>,
         output_type: Ty,
+        attrs: &[Attr],
     ) {
         assert!(self.callable_context.is_none());
         self.callable_context = Some(CallableContext {
             kind,
             input_params,
             output_type,
+            attrs: attrs.to_vec(),
         });
     }
 
@@ -2148,6 +2157,7 @@ struct CallableContext {
     pub kind: CallableKind,
     pub input_params: Vec<InputParam>,
     pub output_type: Ty,
+    pub attrs: Vec<Attr>,
 }
 
 struct SpecContext {
@@ -2252,13 +2262,17 @@ fn derive_instrinsic_operation_application_generator_set(
         ValueKind::new_dynamic_from_type(&callable_context.output_type)
     };
 
+    let mut inherent_runtime_features = RuntimeFeatureFlags::empty();
+    if callable_context.attrs.contains(&Attr::Measurement) {
+        inherent_runtime_features |= RuntimeFeatureFlags::CallToCustomMeasurement;
+    }
+    if callable_context.attrs.contains(&Attr::Reset) {
+        inherent_runtime_features |= RuntimeFeatureFlags::CallToCustomReset;
+    }
+
     // The compute kind of intrinsic operations is always quantum.
     let inherent_compute_kind = ComputeKind::Quantum(QuantumProperties {
-        runtime_features: if matches!(callable_context.kind, CallableKind::Operation) {
-            RuntimeFeatureFlags::empty()
-        } else {
-            RuntimeFeatureFlags::CustomMeasurement
-        },
+        runtime_features: inherent_runtime_features,
         value_kind,
     });
 
diff --git a/compiler/qsc_rca/src/errors.rs b/compiler/qsc_rca/src/errors.rs
index 120810e9..dc6ddb65 100644
--- a/compiler/qsc_rca/src/errors.rs
+++ b/compiler/qsc_rca/src/errors.rs
@@ -167,6 +167,12 @@ pub enum Error {
     #[diagnostic(code("Qsc.CapabilitiesCk.CallToCustomMeasurement"))]
     CallToCustomMeasurement(#[label] Span),
 
+    #[error("cannot call a custom reset")]
+    #[diagnostic(help("cannot call a custom reset in the configured target profile"))]
+    #[diagnostic(url("https://aka.ms/qdk.qir#call-to-custom-reset"))]
+    #[diagnostic(code("Qsc.CapabilitiesCk.CallToCustomReset"))]
+    CallToCustomReset(#[label] Span),
+
     #[error("cannot access an array using a dynamic index")]
     #[diagnostic(help("accessing an array using an index that depends on a measurement result is not supported by the configured target profile"))]
     #[diagnostic(url("https://aka.ms/qdk.qir#use-of-dynamic-array-index"))]
@@ -308,9 +314,12 @@ pub fn generate_errors_from_runtime_features(
     if runtime_features.contains(RuntimeFeatureFlags::UseOfAdvancedOutput) {
         errors.push(Error::UseOfAdvancedOutput(span));
     }
-    if runtime_features.contains(RuntimeFeatureFlags::CustomMeasurement) {
+    if runtime_features.contains(RuntimeFeatureFlags::CallToCustomMeasurement) {
         errors.push(Error::CallToCustomMeasurement(span));
     }
+    if runtime_features.contains(RuntimeFeatureFlags::CallToCustomReset) {
+        errors.push(Error::CallToCustomReset(span));
+    }
     errors
 }
 
diff --git a/compiler/qsc_rca/src/lib.rs b/compiler/qsc_rca/src/lib.rs
index a62fff64..f331aaec 100644
--- a/compiler/qsc_rca/src/lib.rs
+++ b/compiler/qsc_rca/src/lib.rs
@@ -810,8 +810,10 @@ bitflags! {
         const UseOfDynamicResult = 1 << 26;
         /// Use of a dynamic tuple variable.
         const UseOfDynamicTuple = 1 << 27;
-        /// A custom measurement was declared using the @Measurement attribute.
-        const CustomMeasurement = 1 << 28;
+        /// A callee expression to a measurement.
+        const CallToCustomMeasurement = 1 << 28;
+        /// A callee expression to a reset.
+        const CallToCustomReset = 1 << 29;
     }
 }
 
@@ -914,9 +916,13 @@ impl RuntimeFeatureFlags {
         if self.contains(RuntimeFeatureFlags::UseOfDynamicTuple) {
             capabilities |= TargetCapabilityFlags::HigherLevelConstructs;
         }
-        if self.contains(RuntimeFeatureFlags::CustomMeasurement) {
+        if self.contains(RuntimeFeatureFlags::CallToCustomMeasurement) {
             capabilities |= TargetCapabilityFlags::Adaptive;
         }
+        if self.contains(RuntimeFeatureFlags::CallToCustomReset) {
+            capabilities |= TargetCapabilityFlags::Adaptive;
+            capabilities |= TargetCapabilityFlags::QubitReset;
+        }
         capabilities
     }
 
diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs
index 2f4fa282..bf6b2ffa 100644
--- a/language_service/src/completion.rs
+++ b/language_service/src/completion.rs
@@ -132,6 +132,7 @@ fn collect_hardcoded_words(expected: WordKinds) -> Vec<Completion> {
                     Completion::new("EntryPoint".to_string(), CompletionItemKind::Interface),
                     Completion::new("Config".to_string(), CompletionItemKind::Interface),
                     Completion::new("Measurement".to_string(), CompletionItemKind::Interface),
+                    Completion::new("Reset".to_string(), CompletionItemKind::Interface),
                 ]);
             }
             HardcodedIdentKind::Size => {
diff --git a/samples/notebooks/noise.ipynb b/samples/notebooks/noise.ipynb
new file mode 100644
index 00000000..c4fde081
--- /dev/null
+++ b/samples/notebooks/noise.ipynb
@@ -0,0 +1,208 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Simulating Pauli noise\n",
+    "This notebook shows how to run simulations with Pauli noise, such as bit-flip or depolarizing noise.\n",
+    "\n",
+    "First, make sure prerequisites are available. Packages `qsharp` and `qsharp_widgets` must be already installed."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import qsharp\n",
+    "import qsharp_widgets"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Simulation with noise\n",
+    "\n",
+    "Define a simple program that creates a Bell state on two qubits and measures both qubits."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {
+    "vscode": {
+     "languageId": "qsharp"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "%%qsharp\n",
+    "\n",
+    "operation BellPair() : Result[] {\n",
+    "    use q = Qubit[2];\n",
+    "    H(q[0]);\n",
+    "    CNOT(q[0], q[1]);\n",
+    "    MResetEachZ(q)\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Run 20 shots without noise and display results."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "results = qsharp.run(\"BellPair()\", 20)\n",
+    "results"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Note that measurements always agree within a shot as expected. Now run 20 shots of the same program with 10% [depolarizing noise](https://en.wikipedia.org/wiki/Quantum_depolarizing_channel). Depolarizing noise is applied to each gate and each measurement."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "results = qsharp.run(\"BellPair()\", 20, noise=qsharp.DepolarizingNoise(0.1))\n",
+    "results"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Note that measurements do not always agree within the shot."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Histograms\n",
+    "\n",
+    "Define a program to prepare a cat state on five qubits and measure each qubit."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {
+    "vscode": {
+     "languageId": "qsharp"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "%%qsharp\n",
+    "\n",
+    "operation Cat5() : Result[] {\n",
+    "    use q = Qubit[5];\n",
+    "    H(q[0]);\n",
+    "    ApplyCNOTChain(q);\n",
+    "    MResetEachZ(q)\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "First, run this program without noise. Roughly half of the outcomes should be $\\ket{00000}$ and another half should be $\\ket{11111}$."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "result = qsharp.run(\"Cat5()\", 1000)\n",
+    "qsharp_widgets.Histogram(result)\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Now, run the same program with bit-flip noise of 1%, 5%, 10%, 25%."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for p in [0.01, 0.05, 0.1, 0.25]:\n",
+    "    result = qsharp.run(\"Cat5()\", 1000, noise=qsharp.BitFlipNoise(p))\n",
+    "    display(f\"Noise probability = {p}\")\n",
+    "    display(qsharp_widgets.Histogram(result))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can see that with 1% noise, cat state can still be clearly seen, but when noise approaches 25%, the cat state is indistinguishable from noise."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Arbitrary Pauli noise\n",
+    "\n",
+    "Standard bit-flip, phase-flip, and [depolarizing](https://en.wikipedia.org/wiki/Quantum_depolarizing_channel) noise are available, but arbitrary Pauli noise is also possible. The following example runs the same Cat5 program. First it applies noise with 20% probability (bit-flip half the time and phase-flip half the time). In a second experiment it applies Pauli-Y noise with 10% probability."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "result = qsharp.run(\"Cat5()\", 1000, noise=(0.1, 0.0, 0.1))\n",
+    "display(qsharp_widgets.Histogram(result))\n",
+    "result = qsharp.run(\"Cat5()\", 1000, noise=(0.0, 0.1, 0.0))\n",
+    "display(qsharp_widgets.Histogram(result))"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts
index f9d068d1..d58da7c8 100644
--- a/vscode/src/documentation.ts
+++ b/vscode/src/documentation.ts
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
-import { getCompilerWorker } from "qsharp-lang";
+import { getCompilerWorker, IDocFile } from "qsharp-lang";
 import { Uri } from "vscode";
 import { sendMessageToPanel } from "./webviewPanel";
 import { getActiveProgram } from "./programConfig";
@@ -27,20 +27,24 @@ export async function showDocumentationCommand(extensionUri: Uri) {
   const worker = getCompilerWorker(compilerWorkerScriptPath);
   const docFiles = await worker.getDocumentation(program.programConfig);
 
-  const documentation: string[] = [];
+  const documentation: IDocFile[] = [];
   for (const file of docFiles) {
     // Some files may contain information other than documentation
     // For example, table of content is a separate file in a special format
     // We check presence of qsharp.name in metadata to make sure we take
     // only files that contain documentation from some qsharp object.
     if (file.metadata.indexOf("qsharp.name:") >= 0) {
-      documentation.push(file.contents);
+      documentation.push(file);
     }
   }
 
+  let projectName = program.programConfig.projectName;
+  if (projectName.endsWith(".qs")) projectName = projectName.slice(0, -3);
+
   const message = {
     command: "showDocumentationCommand", // This is handled in webview.tsx onMessage
     fragmentsToRender: documentation,
+    projectName,
   };
 
   sendMessageToPanel(
diff --git a/vscode/src/webview/docview.tsx b/vscode/src/webview/docview.tsx
index 94627a16..1d6b8ec8 100644
--- a/vscode/src/webview/docview.tsx
+++ b/vscode/src/webview/docview.tsx
@@ -2,13 +2,435 @@
 // Licensed under the MIT License.
 
 import { Markdown } from "qsharp-lang/ux";
+import { useEffect, useState } from "preact/hooks";
 
-export function DocumentationView(props: { fragmentsToRender: string[] }) {
-  // Concatenate all documentation.
-  // The following adds an empty line and a horizontal line
-  // between documentation for different functions.
-  // We may consider filtering of fragments later.
-  const contentToRender = props.fragmentsToRender.join("<br/>\n\n---\n\n");
+export interface IDocFile {
+  filename: string;
+  metadata: string;
+  contents: string;
+}
+
+interface ItemDocs {
+  pkg: string;
+  namespace: string;
+  member: string;
+  content: string;
+}
+
+interface PageContents {
+  [name: string]: {
+    onclick: (evt: Event) => void;
+    content: string;
+    anchor: string;
+  };
+}
+
+function GetPageContents(
+  currPath: string,
+  docs: ItemDocs[],
+  setPath: any,
+): PageContents {
+  const contents: PageContents = {};
+
+  if (currPath === "") {
+    // Collect the set of all packages
+    docs.forEach((doc) => {
+      if (!(doc.pkg in contents)) {
+        contents[doc.pkg] = {
+          onclick: () => setPath(doc.pkg),
+          content: "",
+          anchor: "",
+        };
+      }
+    });
+  } else if (currPath.indexOf("/") === -1) {
+    // Render the list of namespaces in the current package
+    docs.forEach((doc) => {
+      if (doc.pkg === currPath) {
+        if (!(doc.namespace in contents)) {
+          contents[doc.namespace] = {
+            onclick: () => setPath(`${currPath}/${doc.namespace}`),
+            content: "",
+            anchor: "",
+          };
+        }
+      }
+    });
+  } else {
+    // Render the list of members in the current namespace
+    docs.forEach((doc) => {
+      if (
+        doc.pkg === currPath.split("/")[0] &&
+        doc.namespace === currPath.split("/")[1]
+      ) {
+        contents[doc.member] = {
+          onclick: (evt: Event) => {
+            evt.preventDefault();
+            scrollToElement(doc.member);
+          },
+          content: doc.content,
+          anchor: doc.member,
+        };
+      }
+    });
+  }
+  return contents;
+}
+
+function scrollToElement(id: string) {
+  const elem = id ? document.getElementById(id) : null;
+  if (!elem) {
+    window.scrollTo({
+      top: 0,
+      behavior: "instant",
+    });
+  } else {
+    const yOffset = -64; // Negative value to offset from the top
+    const yPosition =
+      elem.getBoundingClientRect().top + window.scrollY + yOffset;
+
+    window.scrollTo({
+      top: yPosition,
+      behavior: "instant",
+    });
+  }
+}
+
+interface SearchResult {
+  rank: number;
+  anchor: string;
+  header: string;
+  summary: string;
+}
+
+function getSearchResults(
+  searchText: string,
+  docs: ItemDocs[],
+): SearchResult[] {
+  const results: SearchResult[] = [];
+
+  // Search on member name first, then on namespace name, then on package name, then on content
+  // Prefer earlier matches (e.g. "X" should match "X" before "CX")
+
+  // RegExp groups
+  // 1. is the first header (e.g. 'Foo operation'), group
+  // 2. Is any text after the first header that precedes the next header
+  // 3. Ignore
+  // 4. Is the summary (if present)
+  const summaryRe =
+    /^(?:# )(.*?\n)\s*(.*?)(?=(##+)|$)(## Summary\r?\n.*?(?=(##+)|$))?/s;
+
+  docs.forEach((doc) => {
+    const reMatch = doc.content.match(summaryRe);
+    const summary = (reMatch?.[2] ?? "") + (reMatch?.[4] ?? "");
+    const header = doc.content.match(summaryRe)?.[1] ?? doc.member;
+
+    const lowerText = searchText.toLowerCase();
+    let rank = doc.member.toLowerCase().indexOf(lowerText) + 1;
+    if (rank) {
+      // Matches on larger parts of the name are more important
+      rank += doc.member.length / 100;
+    }
+
+    if (!rank) {
+      rank = (doc.namespace.toLowerCase().indexOf(lowerText) + 1) * 100;
+      if (rank) {
+        rank += doc.namespace.length;
+      }
+    }
+    if (!rank) {
+      rank = (doc.pkg.toLowerCase().indexOf(lowerText) + 1) * 1000;
+    }
+    if (!rank) {
+      rank = (doc.content.toLowerCase().indexOf(lowerText) + 1) * 10000;
+    }
+
+    if (rank) {
+      results.push({
+        rank,
+        anchor: `${doc.pkg}/${doc.namespace}/${doc.member}`,
+        header,
+        summary,
+      });
+      return;
+    }
+  });
+
+  return results.sort((a, b) => a.rank - b.rank);
+}
+
+function DocsPage(props: { fragmentsToRender: ItemDocs[] }) {
+  // currPath is of the format: "<pkg>/<namespace>/<member>", e.g.
+  // "Std/Canon/CCNOT" or "Std/Microsoft.Quantum.Diagnostics/AssertMeasurementEqual" or
+  // "Unsigned/Main/GetInt" or "Main/Particle/Particle". When at the top level, currPath is "".
+
+  const [currPath, setPath] = useState("");
+  const [searchText, setSearchText] = useState("");
+
+  useEffect(() => {
+    // If the member is navigated to, scroll to it after rendering
+    const member = currPath.split("/")[2];
+    scrollToElement(member);
+  }, [currPath]);
+
+  // Skip processing contents if searching
+  const contents =
+    searchText === ""
+      ? GetPageContents(currPath, props.fragmentsToRender, setPath)
+      : {};
+
+  const searchResults =
+    searchText === ""
+      ? []
+      : getSearchResults(searchText, props.fragmentsToRender);
+
+  // Used to bold the text links when overing
+  function overLi(e: MouseEvent) {
+    (e.target as HTMLElement).style.fontWeight = "600";
+    (e.target as HTMLElement).style.textDecoration = "underline";
+    (e.target as HTMLElement).style.cursor = "pointer";
+  }
+
+  function outLi(e: MouseEvent) {
+    (e.target as HTMLElement).style.fontWeight = "400";
+    (e.target as HTMLElement).style.textDecoration = "none";
+    (e.target as HTMLElement).style.cursor = "default";
+  }
+
+  // Whenever the breadcrumbs are clicked, go up one level
+  function onPathClick() {
+    setSearchText("");
+    if (currPath) {
+      const parts = currPath.split("/");
+      parts.pop();
+      setPath(parts.join("/"));
+    }
+  }
+
+  // Handle the user focusing or updating the search box
+  function onSearchFocus(e: FocusEvent) {
+    e.preventDefault();
+    if (e.target) {
+      const currText = (e.target as HTMLInputElement).value;
+      if (currText) setPath("");
+      setSearchText(currText);
+    }
+  }
+
+  function onSearchInput(e: InputEvent) {
+    e.preventDefault();
+    if (e.target) {
+      setPath("");
+      setSearchText((e.target as HTMLInputElement).value);
+    }
+  }
+
+  function searchResultClicked(anchor: string) {
+    setSearchText("");
+    setPath(anchor);
+  }
+
+  return (
+    <div
+      class="qs-docsPage"
+      style="width: 100%; padding-bottom: 1em; position: relative; background-color: var(--main-background); color: var(--main-color)"
+    >
+      <div
+        class="qs-docsHeader"
+        style="height: 3em; display: flex; justify-content: space-between; align-items: center; margin-top: 0px; padding-top: 1.5em; padding-bottom: 1em; position: fixed; top: 0; width: 95%; background-color: var(--main-background); z-index: 1;"
+      >
+        <div
+          onClick={() => {
+            setSearchText("");
+            setPath("");
+          }}
+          onMouseOver={overLi}
+          onMouseOut={outLi}
+        >
+          <svg
+            style="height: 2.25em; width: 2.25em; margin: 0.25em"
+            viewBox="0 0 100 100"
+          >
+            <g transform="translate(10,10)" fill="#888">
+              <path d="M10,50 l40,-40 l40,40 Z"></path>
+              <path d="M20,49 l0,30 l20,0 l0,-30Z"></path>
+              <path d="M60,49 l0,30 l20,0 l0,-30Z"></path>
+              <path d="M39,49 l0,8 l24,0 l0,-30Z"></path>
+              <path d="M77,40 l0,-20 l-10,0 l0,20"></path>
+            </g>
+          </svg>
+        </div>
+        <div
+          style="flex-grow: 1; font-size: 1.4em; margin-left: 0.5em;"
+          onClick={onPathClick}
+        >
+          {currPath
+            ? currPath.replaceAll("/", " > ")
+            : searchText
+              ? "Search results"
+              : "Q# API documentation"}
+        </div>
+        <div>
+          <input
+            type="text"
+            placeholder="Search..."
+            onFocus={onSearchFocus}
+            onInput={onSearchInput}
+          />
+        </div>
+        <div>
+          <svg
+            style="height: 2.25em; width: 2.25em; margin: 0.25em"
+            viewBox="0 0 100 100"
+          >
+            <g stroke="#888" fill="none" transform="translate(0,10)">
+              <circle stroke-width="6" cx="40" cy="35" r="25"></circle>
+              <path stroke-width="8" d="M52,57 l20,30"></path>
+            </g>
+          </svg>
+        </div>
+      </div>
+      {searchText ? (
+        <div
+          class="qs-searchResults"
+          style="margin: 2em; position: relative; top: 2em;"
+        >
+          {searchResults.map((result) => (
+            <div>
+              <hr />
+              <h1
+                onMouseOver={overLi}
+                onMouseOut={outLi}
+                onClick={() => searchResultClicked(result.anchor)}
+              >
+                {result.header}
+              </h1>
+              <Markdown markdown={result.summary} />
+            </div>
+          ))}
+        </div>
+      ) : (
+        <div
+          class="qs-docsContent"
+          style="margin: 2em; position: relative; top: 2em;"
+        >
+          <div
+            class="qs-index"
+            style="background: var(--vscode-textCodeBlock-background); padding: 0.1em"
+          >
+            <p style="font-size: 1.1em; font-weight: 600; margin: 0.8em;">
+              {currPath === ""
+                ? "Packages"
+                : currPath.indexOf("/") === -1
+                  ? "Namespaces"
+                  : "Members"}
+            </p>
+            <ul>
+              {Object.keys(contents).map((key) => (
+                <li
+                  onClick={contents[key].onclick}
+                  onMouseOver={overLi}
+                  onMouseOut={outLi}
+                >
+                  {key}
+                </li>
+              ))}
+            </ul>
+          </div>
+
+          {Object.keys(contents).map((key) =>
+            !contents[key].content ? null : (
+              <div id={contents[key].anchor} style="margin-top: 12px;">
+                <Markdown markdown={contents[key].content} />
+                <hr />
+              </div>
+            ),
+          )}
+        </div>
+      )}
+    </div>
+  );
+}
+
+export function DocumentationView(props: {
+  fragmentsToRender: IDocFile[];
+  projectName: string;
+}) {
+  const docs: ItemDocs[] = [];
+
+  props.fragmentsToRender.forEach((doc) => {
+    if (!doc.metadata) {
+      return;
+    }
+    const pkg = doc.metadata.match(/^(?:qsharp\.package: )(.*)$/m)?.[1];
+    const namespace = doc.metadata.match(/^(?:qsharp\.namespace: )(.*)$/m)?.[1];
+    const member = doc.metadata.match(/^(?:qsharp\.name: )(.*)$/m)?.[1];
+
+    if (pkg && namespace && member) {
+      docs.push({
+        pkg:
+          pkg === "__Core__" || pkg === "__Std__"
+            ? "Std"
+            : pkg === "__Main__"
+              ? props.projectName
+              : pkg,
+        // For some reason Std namespaces include the package name. Remove it.
+        namespace: namespace.startsWith("Std.")
+          ? namespace.slice(4)
+          : namespace,
+        member,
+        content: doc.contents,
+      });
+    }
+  });
+
+  docs.sort((a, b) => {
+    if (a.pkg != b.pkg) {
+      // Sorted by __Main__, then reference packages, then __Std__ (which includes __Core__)
+      if (a.pkg === props.projectName || b.pkg === "Std") {
+        return -1;
+      } else if (b.pkg === props.projectName || a.pkg === "Std") {
+        return 1;
+      } else {
+        return a.pkg.localeCompare(b.pkg);
+      }
+    } else if (a.namespace != b.namespace) {
+      // Main namespace comes first and "Microsoft.Quantum.*" comes last
+      if (
+        a.namespace === props.projectName ||
+        b.namespace.startsWith("Microsoft.Quantum")
+      ) {
+        return -1;
+      } else if (
+        b.namespace === props.projectName ||
+        a.namespace.startsWith("Microsoft.Quantum")
+      ) {
+        return 1;
+      } else {
+        return a.namespace.localeCompare(b.namespace);
+      }
+    } else {
+      return a.member.localeCompare(b.member);
+    }
+  });
+
+  const style = document.createElement("style");
+  style.textContent = `
+body {
+  background-color: var(--main-background);
+  margin: 0;
+  padding: 0;
+}
+
+.markdown-body {
+  background-color: var(--main-background);
+}
+
+.markdown-body code {
+  color: var(--main-color);
+}
+`;
+  document.head.appendChild(style);
 
-  return <Markdown markdown={contentToRender} />;
+  return <DocsPage fragmentsToRender={docs} />;
 }
diff --git a/vscode/src/webview/tsconfig.json b/vscode/src/webview/tsconfig.json
index 0fe35e1e..5e5f6917 100644
--- a/vscode/src/webview/tsconfig.json
+++ b/vscode/src/webview/tsconfig.json
@@ -3,7 +3,7 @@
     "module": "CommonJS",
     "target": "ES2020",
     "noEmit": true,
-    "lib": ["DOM", "ES2020"],
+    "lib": ["DOM", "ES2021"],
     "rootDir": ".",
     "strict": true /* enable all strict type-checking options */,
     "jsx": "react-jsx",
diff --git a/vscode/src/webview/webview.tsx b/vscode/src/webview/webview.tsx
index 92b686c8..3a4c3a3d 100644
--- a/vscode/src/webview/webview.tsx
+++ b/vscode/src/webview/webview.tsx
@@ -15,7 +15,7 @@ import {
   type ReData,
 } from "qsharp-lang/ux";
 import { HelpPage } from "./help";
-import { DocumentationView } from "./docview";
+import { DocumentationView, IDocFile } from "./docview";
 
 // eslint-disable-next-line @typescript-eslint/ban-ts-comment
 // @ts-ignore - there are no types for this
@@ -52,7 +52,8 @@ type CircuitState = {
 
 type DocumentationState = {
   viewType: "documentation";
-  fragmentsToRender: string[];
+  fragmentsToRender: IDocFile[];
+  projectName: string;
 };
 
 type State =
@@ -186,6 +187,7 @@ function onMessage(event: any) {
         state = {
           viewType: "documentation",
           fragmentsToRender: message.fragmentsToRender,
+          projectName: message.projectName,
         };
       }
       break;
@@ -260,7 +262,12 @@ function App({ state }: { state: State }) {
       // too large in the others right now. Something to unify later.
       document.body.classList.add("markdown-body");
       document.body.style.fontSize = "0.8em";
-      return <DocumentationView fragmentsToRender={state.fragmentsToRender} />;
+      return (
+        <DocumentationView
+          fragmentsToRender={state.fragmentsToRender}
+          projectName={state.projectName}
+        />
+      );
     default:
       console.error("Unknown view type in state", state);
       return <div>Loading error</div>;


Test patch:
diff --git a/compiler/qsc/src/codegen/tests.rs b/compiler/qsc/src/codegen/tests.rs
index 02c2a436..6c82359c 100644
--- a/compiler/qsc/src/codegen/tests.rs
+++ b/compiler/qsc/src/codegen/tests.rs
@@ -1080,7 +1080,7 @@ mod adaptive_ri_profile {
 
             declare void @__quantum__qis__x__body(%Qubit*)
 
-            declare void @__quantum__qis__reset__body(%Qubit*)
+            declare void @__quantum__qis__reset__body(%Qubit*) #1
 
             declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
 
@@ -1263,4 +1263,73 @@ mod adaptive_ri_profile {
             !10 = !{i32 1, !"multiple_target_branching", i1 false}
         "#]].assert_eq(&qir);
     }
+
+    #[test]
+    fn custom_reset_generates_correct_qir() {
+        let source = "namespace Test {
+            operation Main() : Result {
+                use q = Qubit();
+                __quantum__qis__custom_reset__body(q);
+                M(q)
+            }
+
+            @Reset()
+            operation __quantum__qis__custom_reset__body(target: Qubit) : Unit {
+                body intrinsic;
+            }
+        }";
+        let sources = SourceMap::new([("test.qs".into(), source.into())], None);
+        let language_features = LanguageFeatures::default();
+        let capabilities = TargetCapabilityFlags::Adaptive
+            | TargetCapabilityFlags::QubitReset
+            | TargetCapabilityFlags::IntegerComputations;
+
+        let (std_id, store) = crate::compile::package_store_with_stdlib(capabilities);
+        let qir = get_qir(
+            sources,
+            language_features,
+            capabilities,
+            store,
+            &[(std_id, None)],
+        )
+        .expect("the input program set in the `source` variable should be valid Q#");
+        expect![[r#"
+            %Result = type opaque
+            %Qubit = type opaque
+
+            define void @ENTRYPOINT__main() #0 {
+            block_0:
+              call void @__quantum__qis__custom_reset__body(%Qubit* inttoptr (i64 0 to %Qubit*))
+              call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
+              call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
+              ret void
+            }
+
+            declare void @__quantum__qis__custom_reset__body(%Qubit*) #1
+
+            declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
+
+            declare void @__quantum__rt__result_record_output(%Result*, 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, !6, !7, !8, !9, !10}
+
+            !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, !"classical_ints", i1 true}
+            !5 = !{i32 1, !"qubit_resetting", i1 true}
+            !6 = !{i32 1, !"classical_floats", i1 false}
+            !7 = !{i32 1, !"backwards_branching", i1 false}
+            !8 = !{i32 1, !"classical_fixed_points", i1 false}
+            !9 = !{i32 1, !"user_functions", i1 false}
+            !10 = !{i32 1, !"multiple_target_branching", i1 false}
+        "#]]
+        .assert_eq(&qir);
+    }
 }
diff --git a/compiler/qsc_doc_gen/src/generate_docs/tests.rs b/compiler/qsc_doc_gen/src/generate_docs/tests.rs
index 9ae10590..89d4d396 100644
--- a/compiler/qsc_doc_gen/src/generate_docs/tests.rs
+++ b/compiler/qsc_doc_gen/src/generate_docs/tests.rs
@@ -20,6 +20,7 @@ fn docs_generation() {
         ms.date: {TIMESTAMP}
         ms.topic: managed-reference
         qsharp.kind: function
+        qsharp.package: __Core__
         qsharp.namespace: Std.Core
         qsharp.name: Length
         qsharp.summary: "Returns the number of elements in the input array `a`."
diff --git a/compiler/qsc_frontend/src/lower/tests.rs b/compiler/qsc_frontend/src/lower/tests.rs
index f35ba17a..0e8bbd26 100644
--- a/compiler/qsc_frontend/src/lower/tests.rs
+++ b/compiler/qsc_frontend/src/lower/tests.rs
@@ -2221,7 +2221,8 @@ fn test_measurement_attr_on_function_issues_error() {
         "#},
         &expect![[r#"
             [
-                InvalidMeasurementAttrOnFunction(
+                InvalidAttrOnFunction(
+                    "Measurement",
                     Span {
                         lo: 49,
                         hi: 52,
@@ -2232,6 +2233,31 @@ fn test_measurement_attr_on_function_issues_error() {
     );
 }
 
+#[test]
+fn test_reset_attr_on_function_issues_error() {
+    check_errors(
+        indoc! {r#"
+            namespace Test {
+                @Reset()
+                function Foo(q: Qubit) : Unit {
+                    body intrinsic;
+                }
+            }
+        "#},
+        &expect![[r#"
+            [
+                InvalidAttrOnFunction(
+                    "Reset",
+                    Span {
+                        lo: 43,
+                        hi: 46,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
 #[test]
 fn item_docs() {
     check_hir(
diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs
index 8a042fa7..e3c006a2 100644
--- a/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs
+++ b/compiler/qsc_passes/src/capabilitiesck/tests_adaptive.rs
@@ -6,7 +6,8 @@ use super::tests_common::{
     CALL_TO_CYCLIC_FUNCTION_WITH_CLASSICAL_ARGUMENT, CALL_TO_CYCLIC_FUNCTION_WITH_DYNAMIC_ARGUMENT,
     CALL_TO_CYCLIC_OPERATION_WITH_CLASSICAL_ARGUMENT,
     CALL_TO_CYCLIC_OPERATION_WITH_DYNAMIC_ARGUMENT, CALL_UNRESOLVED_FUNCTION, CUSTOM_MEASUREMENT,
-    CUSTOM_MEASUREMENT_WITH_SIMULATABLE_INTRINSIC_ATTR, LOOP_WITH_DYNAMIC_CONDITION,
+    CUSTOM_MEASUREMENT_WITH_SIMULATABLE_INTRINSIC_ATTR, CUSTOM_RESET,
+    CUSTOM_RESET_WITH_SIMULATABLE_INTRINSIC_ATTR, LOOP_WITH_DYNAMIC_CONDITION,
     MEASUREMENT_WITHIN_DYNAMIC_SCOPE, MINIMAL, RETURN_WITHIN_DYNAMIC_SCOPE, USE_CLOSURE_FUNCTION,
     USE_DYNAMICALLY_SIZED_ARRAY, USE_DYNAMIC_BIG_INT, USE_DYNAMIC_BOOLEAN, USE_DYNAMIC_DOUBLE,
     USE_DYNAMIC_FUNCTION, USE_DYNAMIC_INDEX, USE_DYNAMIC_INT, USE_DYNAMIC_LHS_EXP_BINOP,
@@ -24,6 +25,14 @@ fn check_profile(source: &str, expect: &Expect) {
     check(source, expect, TargetCapabilityFlags::Adaptive);
 }
 
+fn check_profile_extended(source: &str, capabilities: TargetCapabilityFlags, expect: &Expect) {
+    check(
+        source,
+        expect,
+        TargetCapabilityFlags::Adaptive | capabilities,
+    );
+}
+
 fn check_profile_for_exe(source: &str, expect: &Expect) {
     check_for_exe(source, expect, TargetCapabilityFlags::Adaptive);
 }
@@ -466,6 +475,28 @@ fn custom_measurement_with_simulatable_intrinsic_yields_no_errors() {
     );
 }
 
+#[test]
+fn custom_reset_yields_no_errors() {
+    check_profile_extended(
+        CUSTOM_RESET,
+        TargetCapabilityFlags::QubitReset,
+        &expect![[r#"
+            []
+        "#]],
+    );
+}
+
+#[test]
+fn custom_reset_with_simulatable_intrinsic_yields_no_errors() {
+    check_profile_extended(
+        CUSTOM_RESET_WITH_SIMULATABLE_INTRINSIC_ATTR,
+        TargetCapabilityFlags::QubitReset,
+        &expect![[r#"
+            []
+        "#]],
+    );
+}
+
 #[test]
 fn use_of_dynamic_index_yields_errors() {
     check_profile(
diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_base.rs b/compiler/qsc_passes/src/capabilitiesck/tests_base.rs
index 710fbdac..d582f843 100644
--- a/compiler/qsc_passes/src/capabilitiesck/tests_base.rs
+++ b/compiler/qsc_passes/src/capabilitiesck/tests_base.rs
@@ -6,7 +6,8 @@ use super::tests_common::{
     CALL_TO_CYCLIC_FUNCTION_WITH_CLASSICAL_ARGUMENT, CALL_TO_CYCLIC_FUNCTION_WITH_DYNAMIC_ARGUMENT,
     CALL_TO_CYCLIC_OPERATION_WITH_CLASSICAL_ARGUMENT,
     CALL_TO_CYCLIC_OPERATION_WITH_DYNAMIC_ARGUMENT, CALL_UNRESOLVED_FUNCTION, CUSTOM_MEASUREMENT,
-    CUSTOM_MEASUREMENT_WITH_SIMULATABLE_INTRINSIC_ATTR, LOOP_WITH_DYNAMIC_CONDITION,
+    CUSTOM_MEASUREMENT_WITH_SIMULATABLE_INTRINSIC_ATTR, CUSTOM_RESET,
+    CUSTOM_RESET_WITH_SIMULATABLE_INTRINSIC_ATTR, LOOP_WITH_DYNAMIC_CONDITION,
     MEASUREMENT_WITHIN_DYNAMIC_SCOPE, MINIMAL, RETURN_WITHIN_DYNAMIC_SCOPE, USE_CLOSURE_FUNCTION,
     USE_DYNAMICALLY_SIZED_ARRAY, USE_DYNAMIC_BIG_INT, USE_DYNAMIC_BOOLEAN, USE_DYNAMIC_DOUBLE,
     USE_DYNAMIC_FUNCTION, USE_DYNAMIC_INDEX, USE_DYNAMIC_INT, USE_DYNAMIC_LHS_EXP_BINOP,
@@ -581,6 +582,40 @@ fn custom_measurement_with_simulatable_intrinsic_yields_errors() {
     );
 }
 
+#[test]
+fn custom_reset_yields_errors() {
+    check_profile(
+        CUSTOM_RESET,
+        &expect![[r#"
+            [
+                CallToCustomReset(
+                    Span {
+                        lo: 145,
+                        hi: 151,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
+fn custom_reset_with_simulatable_intrinsic_yields_errors() {
+    check_profile(
+        CUSTOM_RESET_WITH_SIMULATABLE_INTRINSIC_ATTR,
+        &expect![[r#"
+            [
+                CallToCustomReset(
+                    Span {
+                        lo: 145,
+                        hi: 151,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
 #[test]
 fn use_of_dynamic_index_yields_errors() {
     check_profile(
diff --git a/compiler/qsc_passes/src/capabilitiesck/tests_common.rs b/compiler/qsc_passes/src/capabilitiesck/tests_common.rs
index d6d50caf..6b742770 100644
--- a/compiler/qsc_passes/src/capabilitiesck/tests_common.rs
+++ b/compiler/qsc_passes/src/capabilitiesck/tests_common.rs
@@ -353,6 +353,39 @@ pub const CUSTOM_MEASUREMENT_WITH_SIMULATABLE_INTRINSIC_ATTR: &str = r#"
         }
     }"#;
 
+pub const CUSTOM_RESET: &str = r#"
+    namespace Test {
+        operation Main() : Result {
+            use q = Qubit();
+            H(q);
+            let res = M(q);
+            Foo(q);
+            res
+        }
+
+        @Reset()
+        operation Foo(q: Qubit) : Unit {
+            body intrinsic;
+        }
+    }"#;
+
+pub const CUSTOM_RESET_WITH_SIMULATABLE_INTRINSIC_ATTR: &str = r#"
+    namespace Test {
+        operation Main() : Result {
+            use q = Qubit();
+            H(q);
+            let res = M(q);
+            Foo(q);
+            res
+        }
+
+        @Reset()
+        @SimulatableIntrinsic()
+        operation Foo(q: Qubit) : Unit {
+            Reset(q);
+        }
+    }"#;
+
 pub const USE_DYNAMIC_INDEX: &str = r#"
     namespace Test {
         import Std.Convert.*;
diff --git a/compiler/qsc_passes/src/reset/tests.rs b/compiler/qsc_passes/src/reset/tests.rs
new file mode 100644
index 00000000..a818d729
--- /dev/null
+++ b/compiler/qsc_passes/src/reset/tests.rs
@@ -0,0 +1,145 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#![allow(clippy::needless_raw_string_hashes)]
+
+use crate::reset::validate_reset_declarations;
+use expect_test::{expect, Expect};
+use indoc::indoc;
+use qsc_data_structures::{language_features::LanguageFeatures, target::TargetCapabilityFlags};
+use qsc_frontend::compile::{self, compile, PackageStore, SourceMap};
+
+fn check(file: &str, expr: &str, expect: &Expect) {
+    let sources = SourceMap::new([("test".into(), file.into())], Some(expr.into()));
+    let unit = compile(
+        &PackageStore::new(compile::core()),
+        &[],
+        sources,
+        TargetCapabilityFlags::all(),
+        LanguageFeatures::default(),
+    );
+
+    let errors = validate_reset_declarations(&unit.package);
+    expect.assert_debug_eq(&errors);
+}
+
+#[test]
+fn test_custom_reset_declaration_with_simulatable_intrinsic_attr() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                @SimulatableIntrinsic()
+                operation CustomReset(target: Qubit) : Unit {
+                    Reset(target);
+                }
+            }"},
+        "",
+        &expect![[r#"
+            []
+        "#]],
+    );
+}
+
+#[test]
+fn test_custom_reset_declaration() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                operation CustomReset(target: Qubit) : Unit { body intrinsic; }
+            }"},
+        "",
+        &expect![[r#"
+            []
+        "#]],
+    );
+}
+
+#[test]
+fn test_reset_with_no_arguments_error() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                operation CustomReset() : Unit { body intrinsic; }
+            }"},
+        "",
+        &expect![[r#"
+            [
+                NoArguments(
+                    Span {
+                        lo: 56,
+                        hi: 58,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
+fn test_non_intrinsic_reset_error() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                operation CustomReset(target: Qubit) : Unit { Reset(target); }
+            }"},
+        "",
+        &expect![[r#"
+            [
+                NotIntrinsic(
+                    Span {
+                        lo: 45,
+                        hi: 56,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
+fn test_non_qubit_arguments_error() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                operation CustomReset(target: Qubit, n: Int) : Unit { body intrinsic; }
+            }"},
+        "",
+        &expect![[r#"
+            [
+                NonQubitArgument(
+                    Span {
+                        lo: 56,
+                        hi: 79,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
+
+#[test]
+fn test_non_unit_output_error() {
+    check(
+        indoc! {"
+            namespace Test {
+                @Reset()
+                operation CustomReset(target: Qubit) : Reset { body intrinsic; }
+            }"},
+        "",
+        &expect![[r#"
+            [
+                NonUnitOutput(
+                    Span {
+                        lo: 35,
+                        hi: 99,
+                    },
+                ),
+            ]
+        "#]],
+    );
+}
diff --git a/compiler/qsc_qasm3/src/tests/statement/reset.rs b/compiler/qsc_qasm3/src/tests/statement/reset.rs
index f661f661..9965803d 100644
--- a/compiler/qsc_qasm3/src/tests/statement/reset.rs
+++ b/compiler/qsc_qasm3/src/tests/statement/reset.rs
@@ -139,7 +139,7 @@ fn reset_with_adaptive_ri_profile_generates_reset_qir() -> miette::Result<(), Ve
           ret void
         }
 
-        declare void @__quantum__qis__reset__body(%Qubit*)
+        declare void @__quantum__qis__reset__body(%Qubit*) #1
 
         declare void @__quantum__qis__h__body(%Qubit*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/ArithmeticOps.ll b/pip/tests-integration/resources/output/Adaptive_RI/ArithmeticOps.ll
index 33a10374..cf12385b 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/ArithmeticOps.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/ArithmeticOps.ll
@@ -93,7 +93,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/BernsteinVaziraniNISQ.ll b/pip/tests-integration/resources/output/Adaptive_RI/BernsteinVaziraniNISQ.ll
index e2c6cea4..5090b319 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/BernsteinVaziraniNISQ.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/BernsteinVaziraniNISQ.ll
@@ -41,7 +41,7 @@ declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
 declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__array_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/ConstantFolding.ll b/pip/tests-integration/resources/output/Adaptive_RI/ConstantFolding.ll
index 879ab5d4..b0f16a39 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/ConstantFolding.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/ConstantFolding.ll
@@ -47,7 +47,7 @@ declare void @__quantum__qis__rx__body(double, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__array_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/DeutschJozsaNISQ.ll b/pip/tests-integration/resources/output/Adaptive_RI/DeutschJozsaNISQ.ll
index c4274122..93f74a3f 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/DeutschJozsaNISQ.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/DeutschJozsaNISQ.ll
@@ -65,7 +65,7 @@ declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/Functors.ll b/pip/tests-integration/resources/output/Adaptive_RI/Functors.ll
index 8e3bfb43..b30fe0ae 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/Functors.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/Functors.ll
@@ -95,7 +95,7 @@ declare void @__quantum__qis__s__adj(%Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/IntegerComparison.ll b/pip/tests-integration/resources/output/Adaptive_RI/IntegerComparison.ll
index 6bf77e60..600b675c 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/IntegerComparison.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/IntegerComparison.ll
@@ -119,7 +119,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCCNOT.ll b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCCNOT.ll
index a0bc857a..54f88077 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCCNOT.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCCNOT.ll
@@ -47,7 +47,7 @@ declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__qis__x__body(%Qubit*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCNOT.ll b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCNOT.ll
index a6f23124..1d86c703 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCNOT.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicCNOT.ll
@@ -28,7 +28,7 @@ declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__qis__x__body(%Qubit*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicM.ll b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicM.ll
index 8a86cd7c..1d479f1e 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicM.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/IntrinsicM.ll
@@ -18,7 +18,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare void @__quantum__qis__x__body(%Qubit*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/MeasurementComparison.ll b/pip/tests-integration/resources/output/Adaptive_RI/MeasurementComparison.ll
index 195bdee7..725e3ae6 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/MeasurementComparison.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/MeasurementComparison.ll
@@ -38,7 +38,7 @@ declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/NestedBranching.ll b/pip/tests-integration/resources/output/Adaptive_RI/NestedBranching.ll
index 2543d69c..7c07f5a2 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/NestedBranching.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/NestedBranching.ll
@@ -305,7 +305,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__qis__y__body(%Qubit*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/RandomBit.ll b/pip/tests-integration/resources/output/Adaptive_RI/RandomBit.ll
index ac85c5fc..f1c5ecce 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/RandomBit.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/RandomBit.ll
@@ -14,7 +14,7 @@ declare void @__quantum__qis__h__body(%Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__result_record_output(%Result*, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/SampleTeleport.ll b/pip/tests-integration/resources/output/Adaptive_RI/SampleTeleport.ll
index 4d4ec739..65f847d5 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/SampleTeleport.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/SampleTeleport.ll
@@ -46,7 +46,7 @@ declare void @__quantum__qis__mresetz__body(%Qubit*, %Result*) #1
 
 declare void @__quantum__qis__z__body(%Qubit*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__result_record_output(%Result*, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/ShortcuttingMeasurement.ll b/pip/tests-integration/resources/output/Adaptive_RI/ShortcuttingMeasurement.ll
index a9c1a11e..d946deb2 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/ShortcuttingMeasurement.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/ShortcuttingMeasurement.ll
@@ -38,7 +38,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/Slicing.ll b/pip/tests-integration/resources/output/Adaptive_RI/Slicing.ll
index eaa3c824..038810da 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/Slicing.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/Slicing.ll
@@ -36,7 +36,7 @@ declare void @__quantum__qis__x__body(%Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__array_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll b/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll
index 0d5f9f3c..03607bf2 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/SuperdenseCoding.ll
@@ -59,7 +59,7 @@ declare void @__quantum__qis__x__body(%Qubit*)
 
 declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/SwitchHandling.ll b/pip/tests-integration/resources/output/Adaptive_RI/SwitchHandling.ll
index 80437b89..325d88bb 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/SwitchHandling.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/SwitchHandling.ll
@@ -58,7 +58,7 @@ declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
 declare i1 @__quantum__qis__read_result__body(%Result*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__qis__ry__body(double, %Qubit*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll b/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll
index 944aff01..2d136271 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/ThreeQubitRepetitionCode.ll
@@ -283,7 +283,7 @@ declare i1 @__quantum__qis__read_result__body(%Result*)
 
 declare void @__quantum__qis__x__body(%Qubit*)
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__tuple_record_output(i64, i8*)
 
diff --git a/pip/tests-integration/resources/output/Adaptive_RI/WithinApply.ll b/pip/tests-integration/resources/output/Adaptive_RI/WithinApply.ll
index 493b644b..544d8f4f 100644
--- a/pip/tests-integration/resources/output/Adaptive_RI/WithinApply.ll
+++ b/pip/tests-integration/resources/output/Adaptive_RI/WithinApply.ll
@@ -27,7 +27,7 @@ declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*)
 
 declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1
 
-declare void @__quantum__qis__reset__body(%Qubit*)
+declare void @__quantum__qis__reset__body(%Qubit*) #1
 
 declare void @__quantum__rt__array_record_output(i64, i8*)