From 446f78d05192ada54441f0a4ff237064d086e0e8 Mon Sep 17 00:00:00 2001
From: Gurinder Singh <frederick.the.fool@gmail.com>
Date: Mon, 22 Apr 2024 10:31:32 +0530
Subject: [PATCH 1/8] Do not ICE on `AnonConst`s in `diagnostic_hir_wf_check`

---
 .../rustc_hir_analysis/src/hir_wf_check.rs    |  14 +-
 tests/crashes/122989.rs                       |   8 -
 ...ce-hir-wf-check-anon-const-issue-122989.rs |  21 ++
 ...ir-wf-check-anon-const-issue-122989.stderr | 179 ++++++++++++++++++
 4 files changed, 208 insertions(+), 14 deletions(-)
 delete mode 100644 tests/crashes/122989.rs
 create mode 100644 tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs
 create mode 100644 tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr

diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index 7014b23ff07de..d6ba5fa9b5b04 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -163,15 +163,17 @@ fn diagnostic_hir_wf_check<'tcx>(
                 kind: hir::GenericParamKind::Type { default: Some(ty), .. },
                 ..
             }) => vec![*ty],
-            hir::Node::AnonConst(_)
-                if let Some(const_param_id) =
-                    tcx.hir().opt_const_param_default_param_def_id(hir_id)
+            hir::Node::AnonConst(_) => {
+                if let Some(const_param_id) = tcx.hir().opt_const_param_default_param_def_id(hir_id)
                     && let hir::Node::GenericParam(hir::GenericParam {
                         kind: hir::GenericParamKind::Const { ty, .. },
                         ..
-                    }) = tcx.hir_node_by_def_id(const_param_id) =>
-            {
-                vec![*ty]
+                    }) = tcx.hir_node_by_def_id(const_param_id)
+                {
+                    vec![*ty]
+                } else {
+                    vec![]
+                }
             }
             ref node => bug!("Unexpected node {:?}", node),
         },
diff --git a/tests/crashes/122989.rs b/tests/crashes/122989.rs
deleted file mode 100644
index 70ad7d3b65c9b..0000000000000
--- a/tests/crashes/122989.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//@ known-bug: #122989
-trait Traitor<const N: N<2> = 1, const N: N<2> = N> {
-    fn N(&N) -> N<2> {
-        M
-    }
-}
-
-trait N<const N: Traitor<2> = 12> {}
diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs
new file mode 100644
index 0000000000000..2995f26af4a61
--- /dev/null
+++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.rs
@@ -0,0 +1,21 @@
+// Regression test for ICE #122989
+trait Foo<const N: Bar<2>> {
+//~^ WARN trait objects without an explicit `dyn` are deprecated
+//~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
+//~| ERROR cycle detected when computing type of `Foo::N`
+//~| ERROR cycle detected when computing type of `Foo::N`
+//~| ERROR the trait `Foo` cannot be made into an object
+//~| ERROR the trait `Foo` cannot be made into an object
+//~| ERROR the trait `Foo` cannot be made into an object
+//~| ERROR `(dyn Bar<2> + 'static)` is forbidden as the type of a const generic parameter
+    fn func() {
+    }
+}
+
+trait Bar<const M: Foo<2>> {}
+//~^ WARN trait objects without an explicit `dyn` are deprecated
+//~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
+//~| ERROR the trait `Foo` cannot be made into an object
+//~| ERROR `(dyn Foo<2> + 'static)` is forbidden as the type of a const generic parameter
+
+fn main() {}
diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr
new file mode 100644
index 0000000000000..e6fdc44087364
--- /dev/null
+++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122989.stderr
@@ -0,0 +1,179 @@
+warning: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:20
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |                    ^^^^^^
+   |
+   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
+   = note: `#[warn(bare_trait_objects)]` on by default
+help: if this is an object-safe trait, use `dyn`
+   |
+LL | trait Foo<const N: dyn Bar<2>> {
+   |                    +++
+
+warning: trait objects without an explicit `dyn` are deprecated
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:15:20
+   |
+LL | trait Bar<const M: Foo<2>> {}
+   |                    ^^^^^^
+   |
+   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021!
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>
+help: if this is an object-safe trait, use `dyn`
+   |
+LL | trait Bar<const M: dyn Foo<2>> {}
+   |                    +++
+
+error[E0391]: cycle detected when computing type of `Foo::N`
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:11
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |           ^^^^^^^^^^^^^^^
+   |
+note: ...which requires computing type of `Bar::M`...
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:15:11
+   |
+LL | trait Bar<const M: Foo<2>> {}
+   |           ^^^^^^^^^^^^^^^
+   = note: ...which again requires computing type of `Foo::N`, completing the cycle
+note: cycle used when computing explicit predicates of trait `Foo`
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:1
+   |
+LL | trait Foo<const N: Bar<2>> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
+
+error[E0391]: cycle detected when computing type of `Foo::N`
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:11
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |           ^^^^^^^^^^^^^^^
+   |
+note: ...which requires computing type of `Bar::M`...
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:15:11
+   |
+LL | trait Bar<const M: Foo<2>> {}
+   |           ^^^^^^^^^^^^^^^
+   = note: ...which again requires computing type of `Foo::N`, completing the cycle
+note: cycle used when computing explicit predicates of trait `Foo`
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:1
+   |
+LL | trait Foo<const N: Bar<2>> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0038]: the trait `Foo` cannot be made into an object
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:24
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |                        ^ `Foo` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:8
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |       --- this trait cannot be made into an object...
+...
+LL |     fn func() {
+   |        ^^^^ ...because associated function `func` has no `self` parameter
+help: consider turning `func` into a method by giving it a `&self` argument
+   |
+LL |     fn func(&self) {
+   |             +++++
+help: alternatively, consider constraining `func` so it does not apply to trait objects
+   |
+LL |     fn func() where Self: Sized {
+   |               +++++++++++++++++
+
+error[E0038]: the trait `Foo` cannot be made into an object
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:11
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |           ^^^^^^^^^^^^^^^ `Foo` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:8
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |       --- this trait cannot be made into an object...
+...
+LL |     fn func() {
+   |        ^^^^ ...because associated function `func` has no `self` parameter
+help: consider turning `func` into a method by giving it a `&self` argument
+   |
+LL |     fn func(&self) {
+   |             +++++
+help: alternatively, consider constraining `func` so it does not apply to trait objects
+   |
+LL |     fn func() where Self: Sized {
+   |               +++++++++++++++++
+
+error: `(dyn Bar<2> + 'static)` is forbidden as the type of a const generic parameter
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:20
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |                    ^^^^^^
+   |
+   = note: the only supported types are integers, `bool` and `char`
+
+error[E0038]: the trait `Foo` cannot be made into an object
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:15:11
+   |
+LL | trait Bar<const M: Foo<2>> {}
+   |           ^^^^^^^^^^^^^^^ `Foo` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:8
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |       --- this trait cannot be made into an object...
+...
+LL |     fn func() {
+   |        ^^^^ ...because associated function `func` has no `self` parameter
+help: consider turning `func` into a method by giving it a `&self` argument
+   |
+LL |     fn func(&self) {
+   |             +++++
+help: alternatively, consider constraining `func` so it does not apply to trait objects
+   |
+LL |     fn func() where Self: Sized {
+   |               +++++++++++++++++
+
+error: `(dyn Foo<2> + 'static)` is forbidden as the type of a const generic parameter
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:15:20
+   |
+LL | trait Bar<const M: Foo<2>> {}
+   |                    ^^^^^^
+   |
+   = note: the only supported types are integers, `bool` and `char`
+
+error[E0038]: the trait `Foo` cannot be made into an object
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:2:11
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |           ^^^^^^^^^^^^^^^ `Foo` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/ice-hir-wf-check-anon-const-issue-122989.rs:11:8
+   |
+LL | trait Foo<const N: Bar<2>> {
+   |       --- this trait cannot be made into an object...
+...
+LL |     fn func() {
+   |        ^^^^ ...because associated function `func` has no `self` parameter
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider turning `func` into a method by giving it a `&self` argument
+   |
+LL |     fn func(&self) {
+   |             +++++
+help: alternatively, consider constraining `func` so it does not apply to trait objects
+   |
+LL |     fn func() where Self: Sized {
+   |               +++++++++++++++++
+
+error: aborting due to 8 previous errors; 2 warnings emitted
+
+Some errors have detailed explanations: E0038, E0391.
+For more information about an error, try `rustc --explain E0038`.

From c9dd07dd5e7eb84ac4b1cc27fc2ee620b2a2dfd2 Mon Sep 17 00:00:00 2001
From: Zalathar <Zalathar@users.noreply.github.com>
Date: Wed, 17 Apr 2024 11:41:40 +1000
Subject: [PATCH 2/8] coverage: Add branch coverage support for let-else

---
 .../rustc_mir_build/src/build/coverageinfo.rs | 26 ++++++++++++++++---
 .../rustc_mir_build/src/build/matches/mod.rs  |  4 +++
 tests/coverage/branch/let-else.cov-map        |  9 ++++---
 tests/coverage/branch/let-else.coverage       |  3 +++
 4 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs
index e2a5f97a84744..2ebbb779933e1 100644
--- a/compiler/rustc_mir_build/src/build/coverageinfo.rs
+++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs
@@ -1,17 +1,18 @@
-mod mcdc;
 use std::assert_matches::assert_matches;
 use std::collections::hash_map::Entry;
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
 use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp};
-use rustc_middle::thir::{ExprId, ExprKind, Thir};
+use rustc_middle::thir::{ExprId, ExprKind, Pat, Thir};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::LocalDefId;
 
 use crate::build::coverageinfo::mcdc::MCDCInfoBuilder;
 use crate::build::{Builder, CFG};
 
+mod mcdc;
+
 pub(crate) struct BranchInfoBuilder {
     /// Maps condition expressions to their enclosing `!`, for better instrumentation.
     nots: FxHashMap<ExprId, NotInfo>,
@@ -155,7 +156,7 @@ impl BranchInfoBuilder {
     }
 }
 
-impl Builder<'_, '_> {
+impl<'tcx> Builder<'_, 'tcx> {
     /// If branch coverage is enabled, inject marker statements into `then_block`
     /// and `else_block`, and record their IDs in the table of branch spans.
     pub(crate) fn visit_coverage_branch_condition(
@@ -195,4 +196,23 @@ impl Builder<'_, '_> {
 
         branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block);
     }
+
+    /// If branch coverage is enabled, inject marker statements into `true_block`
+    /// and `false_block`, and record their IDs in the table of branches.
+    ///
+    /// Used to instrument let-else for branch coverage.
+    pub(crate) fn visit_coverage_conditional_let(
+        &mut self,
+        pattern: &Pat<'tcx>, // Pattern that has been matched when the true path is taken
+        true_block: BasicBlock,
+        false_block: BasicBlock,
+    ) {
+        // Bail out if branch coverage is not enabled for this function.
+        let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
+
+        // FIXME(#124144) This may need special handling when MC/DC is enabled.
+
+        let source_info = SourceInfo { span: pattern.span, scope: self.source_scope };
+        branch_info.add_two_way_branch(&mut self.cfg, source_info, true_block, false_block);
+    }
 }
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index bce1526775963..54ffafb000b70 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -2452,6 +2452,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 None,
                 true,
             );
+
+            // If branch coverage is enabled, record this branch.
+            this.visit_coverage_conditional_let(pattern, matching, failure);
+
             this.break_for_else(failure, this.source_info(initializer_span));
             matching.unit()
         });
diff --git a/tests/coverage/branch/let-else.cov-map b/tests/coverage/branch/let-else.cov-map
index ad987bd6bb1e9..c7f7adddbc295 100644
--- a/tests/coverage/branch/let-else.cov-map
+++ b/tests/coverage/branch/let-else.cov-map
@@ -1,13 +1,16 @@
 Function name: let_else::let_else
-Raw bytes (38): 0x[01, 01, 02, 05, 09, 09, 02, 06, 01, 0c, 01, 01, 10, 02, 03, 0e, 00, 0f, 05, 00, 13, 00, 18, 09, 01, 09, 01, 0f, 02, 04, 05, 00, 0b, 07, 01, 01, 00, 02]
+Raw bytes (45): 0x[01, 01, 02, 05, 09, 09, 02, 07, 01, 0c, 01, 01, 10, 20, 02, 09, 03, 09, 00, 10, 02, 00, 0e, 00, 0f, 05, 00, 13, 00, 18, 09, 01, 09, 01, 0f, 02, 04, 05, 00, 0b, 07, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 2
 - expression 0 operands: lhs = Counter(1), rhs = Counter(2)
 - expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
-Number of file 0 mappings: 6
+Number of file 0 mappings: 7
 - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16)
-- Code(Expression(0, Sub)) at (prev + 3, 14) to (start + 0, 15)
+- Branch { true: Expression(0, Sub), false: Counter(2) } at (prev + 3, 9) to (start + 0, 16)
+    true  = (c1 - c2)
+    false = c2
+- Code(Expression(0, Sub)) at (prev + 0, 14) to (start + 0, 15)
     = (c1 - c2)
 - Code(Counter(1)) at (prev + 0, 19) to (start + 0, 24)
 - Code(Counter(2)) at (prev + 1, 9) to (start + 1, 15)
diff --git a/tests/coverage/branch/let-else.coverage b/tests/coverage/branch/let-else.coverage
index 83730e1dfbafe..22ad8f2b0e138 100644
--- a/tests/coverage/branch/let-else.coverage
+++ b/tests/coverage/branch/let-else.coverage
@@ -14,6 +14,9 @@
    LL|       |
    LL|      3|    let Some(x) = value else {
                            ^2
+  ------------------
+  |  Branch (LL:9): [True: 2, False: 1]
+  ------------------
    LL|      1|        say("none");
    LL|      1|        return;
    LL|       |    };

From 7c87ad043098c8d668b25fe09e9b0806d7cb41c5 Mon Sep 17 00:00:00 2001
From: Zalathar <Zalathar@users.noreply.github.com>
Date: Wed, 17 Apr 2024 11:41:41 +1000
Subject: [PATCH 3/8] coverage: Add branch coverage support for if-let and
 let-chains

---
 .../rustc_mir_build/src/build/coverageinfo.rs |  2 +-
 .../rustc_mir_build/src/build/matches/mod.rs  |  3 +++
 tests/coverage/branch/if-let.cov-map          | 23 +++++++++++++------
 tests/coverage/branch/if-let.coverage         |  9 ++++++++
 4 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs
index 2ebbb779933e1..ee9eeb62990d2 100644
--- a/compiler/rustc_mir_build/src/build/coverageinfo.rs
+++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs
@@ -200,7 +200,7 @@ impl<'tcx> Builder<'_, 'tcx> {
     /// If branch coverage is enabled, inject marker statements into `true_block`
     /// and `false_block`, and record their IDs in the table of branches.
     ///
-    /// Used to instrument let-else for branch coverage.
+    /// Used to instrument let-else and if-let (including let-chains) for branch coverage.
     pub(crate) fn visit_coverage_conditional_let(
         &mut self,
         pattern: &Pat<'tcx>, // Pattern that has been matched when the true path is taken
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 54ffafb000b70..c8c2263f2ed3e 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -1968,6 +1968,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             false,
         );
 
+        // If branch coverage is enabled, record this branch.
+        self.visit_coverage_conditional_let(pat, post_guard_block, otherwise_post_guard_block);
+
         post_guard_block.unit()
     }
 
diff --git a/tests/coverage/branch/if-let.cov-map b/tests/coverage/branch/if-let.cov-map
index c12df8d98018d..0c7d986933e7d 100644
--- a/tests/coverage/branch/if-let.cov-map
+++ b/tests/coverage/branch/if-let.cov-map
@@ -1,13 +1,16 @@
 Function name: if_let::if_let
-Raw bytes (38): 0x[01, 01, 02, 05, 09, 09, 02, 06, 01, 0c, 01, 01, 10, 02, 03, 11, 00, 12, 05, 00, 16, 00, 1b, 02, 00, 1c, 02, 06, 09, 02, 0c, 02, 06, 07, 03, 05, 01, 02]
+Raw bytes (45): 0x[01, 01, 02, 05, 09, 09, 02, 07, 01, 0c, 01, 01, 10, 20, 02, 09, 03, 0c, 00, 13, 02, 00, 11, 00, 12, 05, 00, 16, 00, 1b, 02, 00, 1c, 02, 06, 09, 02, 0c, 02, 06, 07, 03, 05, 01, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 2
 - expression 0 operands: lhs = Counter(1), rhs = Counter(2)
 - expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub)
-Number of file 0 mappings: 6
+Number of file 0 mappings: 7
 - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16)
-- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 18)
+- Branch { true: Expression(0, Sub), false: Counter(2) } at (prev + 3, 12) to (start + 0, 19)
+    true  = (c1 - c2)
+    false = c2
+- Code(Expression(0, Sub)) at (prev + 0, 17) to (start + 0, 18)
     = (c1 - c2)
 - Code(Counter(1)) at (prev + 0, 22) to (start + 0, 27)
 - Code(Expression(0, Sub)) at (prev + 0, 28) to (start + 2, 6)
@@ -17,7 +20,7 @@ Number of file 0 mappings: 6
     = (c2 + (c1 - c2))
 
 Function name: if_let::if_let_chain
-Raw bytes (52): 0x[01, 01, 04, 01, 05, 05, 09, 0f, 0d, 05, 09, 08, 01, 17, 01, 00, 33, 02, 01, 11, 00, 12, 01, 00, 16, 00, 17, 0d, 01, 15, 00, 16, 02, 00, 1a, 00, 1b, 0d, 01, 05, 03, 06, 0f, 03, 0c, 02, 06, 0b, 03, 05, 01, 02]
+Raw bytes (66): 0x[01, 01, 04, 01, 05, 05, 09, 0f, 0d, 05, 09, 0a, 01, 17, 01, 00, 33, 20, 02, 05, 01, 0c, 00, 13, 02, 00, 11, 00, 12, 01, 00, 16, 00, 17, 20, 0d, 09, 01, 10, 00, 17, 0d, 00, 15, 00, 16, 02, 00, 1a, 00, 1b, 0d, 01, 05, 03, 06, 0f, 03, 0c, 02, 06, 0b, 03, 05, 01, 02]
 Number of files: 1
 - file 0 => global file 1
 Number of expressions: 4
@@ -25,12 +28,18 @@ Number of expressions: 4
 - expression 1 operands: lhs = Counter(1), rhs = Counter(2)
 - expression 2 operands: lhs = Expression(3, Add), rhs = Counter(3)
 - expression 3 operands: lhs = Counter(1), rhs = Counter(2)
-Number of file 0 mappings: 8
+Number of file 0 mappings: 10
 - Code(Counter(0)) at (prev + 23, 1) to (start + 0, 51)
-- Code(Expression(0, Sub)) at (prev + 1, 17) to (start + 0, 18)
+- Branch { true: Expression(0, Sub), false: Counter(1) } at (prev + 1, 12) to (start + 0, 19)
+    true  = (c0 - c1)
+    false = c1
+- Code(Expression(0, Sub)) at (prev + 0, 17) to (start + 0, 18)
     = (c0 - c1)
 - Code(Counter(0)) at (prev + 0, 22) to (start + 0, 23)
-- Code(Counter(3)) at (prev + 1, 21) to (start + 0, 22)
+- Branch { true: Counter(3), false: Counter(2) } at (prev + 1, 16) to (start + 0, 23)
+    true  = c3
+    false = c2
+- Code(Counter(3)) at (prev + 0, 21) to (start + 0, 22)
 - Code(Expression(0, Sub)) at (prev + 0, 26) to (start + 0, 27)
     = (c0 - c1)
 - Code(Counter(3)) at (prev + 1, 5) to (start + 3, 6)
diff --git a/tests/coverage/branch/if-let.coverage b/tests/coverage/branch/if-let.coverage
index f30c5d34eca17..9a3f0113f7515 100644
--- a/tests/coverage/branch/if-let.coverage
+++ b/tests/coverage/branch/if-let.coverage
@@ -14,6 +14,9 @@
    LL|       |
    LL|      3|    if let Some(x) = input {
                               ^2
+  ------------------
+  |  Branch (LL:12): [True: 2, False: 1]
+  ------------------
    LL|      2|        say(x);
    LL|      2|    } else {
    LL|      1|        say("none");
@@ -24,8 +27,14 @@
    LL|     15|fn if_let_chain(a: Option<&str>, b: Option<&str>) {
    LL|     15|    if let Some(x) = a
                               ^12
+  ------------------
+  |  Branch (LL:12): [True: 12, False: 3]
+  ------------------
    LL|     12|        && let Some(y) = b
                                   ^8
+  ------------------
+  |  Branch (LL:16): [True: 8, False: 4]
+  ------------------
    LL|      8|    {
    LL|      8|        say(x);
    LL|      8|        say(y);

From a9b2f1b7abe793c0fdd1010535739b1c7d794dee Mon Sep 17 00:00:00 2001
From: Zalathar <Zalathar@users.noreply.github.com>
Date: Tue, 30 Apr 2024 23:36:08 +1000
Subject: [PATCH 4/8] coverage: `CoverageIdsInfo::mcdc_bitmap_bytes` is never
 needed

This code for recalculating `mcdc_bitmap_bytes` doesn't provide any benefit,
because its result won't have changed from the value in `FunctionCoverageInfo`
that was computed during the MIR instrumentation pass.
---
 compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs |  7 +------
 compiler/rustc_middle/src/mir/query.rs              |  4 ----
 compiler/rustc_mir_transform/src/coverage/query.rs  | 12 +-----------
 3 files changed, 2 insertions(+), 21 deletions(-)

diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index c51a7744a30a3..26ea95f0f0d50 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -207,13 +207,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
                 let cond_bitmap = coverage_context
                                     .try_get_mcdc_condition_bitmap(&instance, decision_depth)
                                     .expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
-                let bitmap_bytes = bx.tcx().coverage_ids_info(instance.def).mcdc_bitmap_bytes;
+                let bitmap_bytes = function_coverage_info.mcdc_bitmap_bytes;
                 assert!(bitmap_idx < bitmap_bytes, "bitmap index of the decision out of range");
-                assert!(
-                    bitmap_bytes <= function_coverage_info.mcdc_bitmap_bytes,
-                    "bitmap length disagreement: query says {bitmap_bytes} but function info only has {}",
-                    function_coverage_info.mcdc_bitmap_bytes
-                );
 
                 let fn_name = bx.get_pgo_func_name_var(instance);
                 let hash = bx.const_u64(function_coverage_info.function_source_hash);
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 9a7af6135e46f..9d70231be3b0c 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -362,8 +362,4 @@ pub struct CoverageIdsInfo {
     /// InstrumentCoverage MIR pass, if the highest-numbered counter increments
     /// were removed by MIR optimizations.
     pub max_counter_id: mir::coverage::CounterId,
-
-    /// Coverage codegen for mcdc needs to know the size of the global bitmap so that it can
-    /// set the `bytemap-bytes` argument of the `llvm.instrprof.mcdc.tvbitmap.update` intrinsic.
-    pub mcdc_bitmap_bytes: u32,
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs
index f77ee63d02c20..65715253647a8 100644
--- a/compiler/rustc_mir_transform/src/coverage/query.rs
+++ b/compiler/rustc_mir_transform/src/coverage/query.rs
@@ -61,17 +61,7 @@ fn coverage_ids_info<'tcx>(
         .max()
         .unwrap_or(CounterId::ZERO);
 
-    let mcdc_bitmap_bytes = mir_body
-        .coverage_branch_info
-        .as_deref()
-        .map(|info| {
-            info.mcdc_decision_spans
-                .iter()
-                .fold(0, |acc, decision| acc + (1_u32 << decision.conditions_num).div_ceil(8))
-        })
-        .unwrap_or_default();
-
-    CoverageIdsInfo { max_counter_id, mcdc_bitmap_bytes }
+    CoverageIdsInfo { max_counter_id }
 }
 
 fn all_coverage_in_mir_body<'a, 'tcx>(

From 0bd92bf5432b81a17a6f5d0af3832922bf0c988f Mon Sep 17 00:00:00 2001
From: Zalathar <Zalathar@users.noreply.github.com>
Date: Wed, 1 May 2024 11:19:52 +1000
Subject: [PATCH 5/8] coverage: Remove confusing comments from `CoverageKind`

These comments appear to be inspired by the similar comments on
`CounterIncrement` and `ExpressionUsed`. But those comments refer to specific
simplification steps performed during coverage codegen, and there is no
corresponding step for the MC/DC coverage statements.
---
 compiler/rustc_middle/src/mir/coverage.rs | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs
index 477303e2434f4..7f9a5a366d74e 100644
--- a/compiler/rustc_middle/src/mir/coverage.rs
+++ b/compiler/rustc_middle/src/mir/coverage.rs
@@ -129,17 +129,11 @@ pub enum CoverageKind {
     /// Marks the point in MIR control flow represented by a evaluated condition.
     ///
     /// This is eventually lowered to `llvm.instrprof.mcdc.condbitmap.update` in LLVM IR.
-    ///
-    /// If this statement does not survive MIR optimizations, the condition would never be
-    /// taken as evaluated.
     CondBitmapUpdate { id: ConditionId, value: bool, decision_depth: u16 },
 
     /// Marks the point in MIR control flow represented by a evaluated decision.
     ///
     /// This is eventually lowered to `llvm.instrprof.mcdc.tvbitmap.update` in LLVM IR.
-    ///
-    /// If this statement does not survive MIR optimizations, the decision would never be
-    /// taken as evaluated.
     TestVectorBitmapUpdate { bitmap_idx: u32, decision_depth: u16 },
 }
 

From eb1b9e0bfd70b721112c8d9b078e221763740a27 Mon Sep 17 00:00:00 2001
From: Zalathar <Zalathar@users.noreply.github.com>
Date: Wed, 1 May 2024 10:44:31 +1000
Subject: [PATCH 6/8] coverage: Add tests for the MC/DC condition limit

---
 tests/coverage/mcdc/condition-limit.cov-map   | 162 ++++++++++++++++++
 tests/coverage/mcdc/condition-limit.coverage  |  69 ++++++++
 tests/coverage/mcdc/condition-limit.rs        |  25 +++
 .../mcdc-condition-limit.bad.stderr           |   8 +
 .../mcdc-condition-limit.rs                   |  21 +++
 5 files changed, 285 insertions(+)
 create mode 100644 tests/coverage/mcdc/condition-limit.cov-map
 create mode 100644 tests/coverage/mcdc/condition-limit.coverage
 create mode 100644 tests/coverage/mcdc/condition-limit.rs
 create mode 100644 tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr
 create mode 100644 tests/ui/instrument-coverage/mcdc-condition-limit.rs

diff --git a/tests/coverage/mcdc/condition-limit.cov-map b/tests/coverage/mcdc/condition-limit.cov-map
new file mode 100644
index 0000000000000..0f42850df03f4
--- /dev/null
+++ b/tests/coverage/mcdc/condition-limit.cov-map
@@ -0,0 +1,162 @@
+Function name: condition_limit::bad
+Raw bytes (204): 0x[01, 01, 2c, 01, 05, 05, 1d, 05, 1d, 7a, 19, 05, 1d, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 21, 9b, 01, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 11, 01, 0e, 01, 02, 09, 20, 05, 02, 02, 08, 00, 09, 05, 00, 0d, 00, 0e, 20, 7a, 1d, 00, 0d, 00, 0e, 7a, 00, 12, 00, 13, 20, 76, 19, 00, 12, 00, 13, 76, 00, 17, 00, 18, 20, 72, 15, 00, 17, 00, 18, 72, 00, 1c, 00, 1d, 20, 6e, 11, 00, 1c, 00, 1d, 6e, 00, 21, 00, 22, 20, 6a, 0d, 00, 21, 00, 22, 6a, 00, 26, 00, 27, 20, 21, 09, 00, 26, 00, 27, 21, 00, 28, 02, 06, 9b, 01, 02, 06, 00, 07, 97, 01, 01, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 44
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(1), rhs = Counter(7)
+- expression 2 operands: lhs = Counter(1), rhs = Counter(7)
+- expression 3 operands: lhs = Expression(30, Sub), rhs = Counter(6)
+- expression 4 operands: lhs = Counter(1), rhs = Counter(7)
+- expression 5 operands: lhs = Expression(30, Sub), rhs = Counter(6)
+- expression 6 operands: lhs = Counter(1), rhs = Counter(7)
+- expression 7 operands: lhs = Expression(29, Sub), rhs = Counter(5)
+- expression 8 operands: lhs = Expression(30, Sub), rhs = Counter(6)
+- expression 9 operands: lhs = Counter(1), rhs = Counter(7)
+- expression 10 operands: lhs = Expression(29, Sub), rhs = Counter(5)
+- expression 11 operands: lhs = Expression(30, Sub), rhs = Counter(6)
+- expression 12 operands: lhs = Counter(1), rhs = Counter(7)
+- expression 13 operands: lhs = Expression(28, Sub), rhs = Counter(4)
+- expression 14 operands: lhs = Expression(29, Sub), rhs = Counter(5)
+- expression 15 operands: lhs = Expression(30, Sub), rhs = Counter(6)
+- expression 16 operands: lhs = Counter(1), rhs = Counter(7)
+- expression 17 operands: lhs = Expression(28, Sub), rhs = Counter(4)
+- expression 18 operands: lhs = Expression(29, Sub), rhs = Counter(5)
+- expression 19 operands: lhs = Expression(30, Sub), rhs = Counter(6)
+- expression 20 operands: lhs = Counter(1), rhs = Counter(7)
+- expression 21 operands: lhs = Expression(27, Sub), rhs = Counter(3)
+- expression 22 operands: lhs = Expression(28, Sub), rhs = Counter(4)
+- expression 23 operands: lhs = Expression(29, Sub), rhs = Counter(5)
+- expression 24 operands: lhs = Expression(30, Sub), rhs = Counter(6)
+- expression 25 operands: lhs = Counter(1), rhs = Counter(7)
+- expression 26 operands: lhs = Expression(27, Sub), rhs = Counter(3)
+- expression 27 operands: lhs = Expression(28, Sub), rhs = Counter(4)
+- expression 28 operands: lhs = Expression(29, Sub), rhs = Counter(5)
+- expression 29 operands: lhs = Expression(30, Sub), rhs = Counter(6)
+- expression 30 operands: lhs = Counter(1), rhs = Counter(7)
+- expression 31 operands: lhs = Expression(39, Add), rhs = Expression(0, Sub)
+- expression 32 operands: lhs = Expression(40, Add), rhs = Counter(7)
+- expression 33 operands: lhs = Expression(41, Add), rhs = Counter(6)
+- expression 34 operands: lhs = Expression(42, Add), rhs = Counter(5)
+- expression 35 operands: lhs = Expression(43, Add), rhs = Counter(4)
+- expression 36 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 37 operands: lhs = Counter(8), rhs = Expression(38, Add)
+- expression 38 operands: lhs = Expression(39, Add), rhs = Expression(0, Sub)
+- expression 39 operands: lhs = Expression(40, Add), rhs = Counter(7)
+- expression 40 operands: lhs = Expression(41, Add), rhs = Counter(6)
+- expression 41 operands: lhs = Expression(42, Add), rhs = Counter(5)
+- expression 42 operands: lhs = Expression(43, Add), rhs = Counter(4)
+- expression 43 operands: lhs = Counter(2), rhs = Counter(3)
+Number of file 0 mappings: 17
+- Code(Counter(0)) at (prev + 14, 1) to (start + 2, 9)
+- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 2, 8) to (start + 0, 9)
+    true  = c1
+    false = (c0 - c1)
+- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14)
+- Branch { true: Expression(30, Sub), false: Counter(7) } at (prev + 0, 13) to (start + 0, 14)
+    true  = (c1 - c7)
+    false = c7
+- Code(Expression(30, Sub)) at (prev + 0, 18) to (start + 0, 19)
+    = (c1 - c7)
+- Branch { true: Expression(29, Sub), false: Counter(6) } at (prev + 0, 18) to (start + 0, 19)
+    true  = ((c1 - c7) - c6)
+    false = c6
+- Code(Expression(29, Sub)) at (prev + 0, 23) to (start + 0, 24)
+    = ((c1 - c7) - c6)
+- Branch { true: Expression(28, Sub), false: Counter(5) } at (prev + 0, 23) to (start + 0, 24)
+    true  = (((c1 - c7) - c6) - c5)
+    false = c5
+- Code(Expression(28, Sub)) at (prev + 0, 28) to (start + 0, 29)
+    = (((c1 - c7) - c6) - c5)
+- Branch { true: Expression(27, Sub), false: Counter(4) } at (prev + 0, 28) to (start + 0, 29)
+    true  = ((((c1 - c7) - c6) - c5) - c4)
+    false = c4
+- Code(Expression(27, Sub)) at (prev + 0, 33) to (start + 0, 34)
+    = ((((c1 - c7) - c6) - c5) - c4)
+- Branch { true: Expression(26, Sub), false: Counter(3) } at (prev + 0, 33) to (start + 0, 34)
+    true  = (((((c1 - c7) - c6) - c5) - c4) - c3)
+    false = c3
+- Code(Expression(26, Sub)) at (prev + 0, 38) to (start + 0, 39)
+    = (((((c1 - c7) - c6) - c5) - c4) - c3)
+- Branch { true: Counter(8), false: Counter(2) } at (prev + 0, 38) to (start + 0, 39)
+    true  = c8
+    false = c2
+- Code(Counter(8)) at (prev + 0, 40) to (start + 2, 6)
+- Code(Expression(38, Add)) at (prev + 2, 6) to (start + 0, 7)
+    = ((((((c2 + c3) + c4) + c5) + c6) + c7) + (c0 - c1))
+- Code(Expression(37, Add)) at (prev + 1, 1) to (start + 0, 2)
+    = (c8 + ((((((c2 + c3) + c4) + c5) + c6) + c7) + (c0 - c1)))
+
+Function name: condition_limit::good
+Raw bytes (180): 0x[01, 01, 20, 01, 05, 05, 19, 05, 19, 52, 15, 05, 19, 52, 15, 05, 19, 4e, 11, 52, 15, 05, 19, 4e, 11, 52, 15, 05, 19, 4a, 0d, 4e, 11, 52, 15, 05, 19, 4a, 0d, 4e, 11, 52, 15, 05, 19, 73, 02, 77, 19, 7b, 15, 7f, 11, 09, 0d, 1d, 6f, 73, 02, 77, 19, 7b, 15, 7f, 11, 09, 0d, 10, 01, 07, 01, 02, 09, 28, 00, 06, 02, 08, 00, 22, 30, 05, 02, 01, 06, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 52, 19, 06, 05, 00, 00, 0d, 00, 0e, 52, 00, 12, 00, 13, 30, 4e, 15, 05, 04, 00, 00, 12, 00, 13, 4e, 00, 17, 00, 18, 30, 4a, 11, 04, 03, 00, 00, 17, 00, 18, 4a, 00, 1c, 00, 1d, 30, 46, 0d, 03, 02, 00, 00, 1c, 00, 1d, 46, 00, 21, 00, 22, 30, 1d, 09, 02, 00, 00, 00, 21, 00, 22, 1d, 00, 23, 02, 06, 6f, 02, 06, 00, 07, 6b, 01, 01, 00, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 32
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
+- expression 1 operands: lhs = Counter(1), rhs = Counter(6)
+- expression 2 operands: lhs = Counter(1), rhs = Counter(6)
+- expression 3 operands: lhs = Expression(20, Sub), rhs = Counter(5)
+- expression 4 operands: lhs = Counter(1), rhs = Counter(6)
+- expression 5 operands: lhs = Expression(20, Sub), rhs = Counter(5)
+- expression 6 operands: lhs = Counter(1), rhs = Counter(6)
+- expression 7 operands: lhs = Expression(19, Sub), rhs = Counter(4)
+- expression 8 operands: lhs = Expression(20, Sub), rhs = Counter(5)
+- expression 9 operands: lhs = Counter(1), rhs = Counter(6)
+- expression 10 operands: lhs = Expression(19, Sub), rhs = Counter(4)
+- expression 11 operands: lhs = Expression(20, Sub), rhs = Counter(5)
+- expression 12 operands: lhs = Counter(1), rhs = Counter(6)
+- expression 13 operands: lhs = Expression(18, Sub), rhs = Counter(3)
+- expression 14 operands: lhs = Expression(19, Sub), rhs = Counter(4)
+- expression 15 operands: lhs = Expression(20, Sub), rhs = Counter(5)
+- expression 16 operands: lhs = Counter(1), rhs = Counter(6)
+- expression 17 operands: lhs = Expression(18, Sub), rhs = Counter(3)
+- expression 18 operands: lhs = Expression(19, Sub), rhs = Counter(4)
+- expression 19 operands: lhs = Expression(20, Sub), rhs = Counter(5)
+- expression 20 operands: lhs = Counter(1), rhs = Counter(6)
+- expression 21 operands: lhs = Expression(28, Add), rhs = Expression(0, Sub)
+- expression 22 operands: lhs = Expression(29, Add), rhs = Counter(6)
+- expression 23 operands: lhs = Expression(30, Add), rhs = Counter(5)
+- expression 24 operands: lhs = Expression(31, Add), rhs = Counter(4)
+- expression 25 operands: lhs = Counter(2), rhs = Counter(3)
+- expression 26 operands: lhs = Counter(7), rhs = Expression(27, Add)
+- expression 27 operands: lhs = Expression(28, Add), rhs = Expression(0, Sub)
+- expression 28 operands: lhs = Expression(29, Add), rhs = Counter(6)
+- expression 29 operands: lhs = Expression(30, Add), rhs = Counter(5)
+- expression 30 operands: lhs = Expression(31, Add), rhs = Counter(4)
+- expression 31 operands: lhs = Counter(2), rhs = Counter(3)
+Number of file 0 mappings: 16
+- Code(Counter(0)) at (prev + 7, 1) to (start + 2, 9)
+- MCDCDecision { bitmap_idx: 0, conditions_num: 6 } at (prev + 2, 8) to (start + 0, 34)
+- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 6, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
+    true  = c1
+    false = (c0 - c1)
+- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14)
+- MCDCBranch { true: Expression(20, Sub), false: Counter(6), condition_id: 6, true_next_id: 5, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14)
+    true  = (c1 - c6)
+    false = c6
+- Code(Expression(20, Sub)) at (prev + 0, 18) to (start + 0, 19)
+    = (c1 - c6)
+- MCDCBranch { true: Expression(19, Sub), false: Counter(5), condition_id: 5, true_next_id: 4, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19)
+    true  = ((c1 - c6) - c5)
+    false = c5
+- Code(Expression(19, Sub)) at (prev + 0, 23) to (start + 0, 24)
+    = ((c1 - c6) - c5)
+- MCDCBranch { true: Expression(18, Sub), false: Counter(4), condition_id: 4, true_next_id: 3, false_next_id: 0 } at (prev + 0, 23) to (start + 0, 24)
+    true  = (((c1 - c6) - c5) - c4)
+    false = c4
+- Code(Expression(18, Sub)) at (prev + 0, 28) to (start + 0, 29)
+    = (((c1 - c6) - c5) - c4)
+- MCDCBranch { true: Expression(17, Sub), false: Counter(3), condition_id: 3, true_next_id: 2, false_next_id: 0 } at (prev + 0, 28) to (start + 0, 29)
+    true  = ((((c1 - c6) - c5) - c4) - c3)
+    false = c3
+- Code(Expression(17, Sub)) at (prev + 0, 33) to (start + 0, 34)
+    = ((((c1 - c6) - c5) - c4) - c3)
+- MCDCBranch { true: Counter(7), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 33) to (start + 0, 34)
+    true  = c7
+    false = c2
+- Code(Counter(7)) at (prev + 0, 35) to (start + 2, 6)
+- Code(Expression(27, Add)) at (prev + 2, 6) to (start + 0, 7)
+    = (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1))
+- Code(Expression(26, Add)) at (prev + 1, 1) to (start + 0, 2)
+    = (c7 + (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1)))
+
diff --git a/tests/coverage/mcdc/condition-limit.coverage b/tests/coverage/mcdc/condition-limit.coverage
new file mode 100644
index 0000000000000..315bacc478582
--- /dev/null
+++ b/tests/coverage/mcdc/condition-limit.coverage
@@ -0,0 +1,69 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |//@ edition: 2021
+   LL|       |//@ min-llvm-version: 18
+   LL|       |//@ compile-flags: -Zcoverage-options=mcdc
+   LL|       |//@ llvm-cov-flags: --show-branches=count --show-mcdc
+   LL|       |
+   LL|      1|fn good() {
+   LL|      1|    let [a, b, c, d, e, f] = <[bool; 6]>::default();
+   LL|      1|    if a && b && c && d && e && f {
+                          ^0   ^0   ^0   ^0   ^0
+  ------------------
+  |  Branch (LL:8): [True: 0, False: 1]
+  |  Branch (LL:13): [True: 0, False: 0]
+  |  Branch (LL:18): [True: 0, False: 0]
+  |  Branch (LL:23): [True: 0, False: 0]
+  |  Branch (LL:28): [True: 0, False: 0]
+  |  Branch (LL:33): [True: 0, False: 0]
+  ------------------
+  |---> MC/DC Decision Region (LL:8) to (LL:34)
+  |
+  |  Number of Conditions: 6
+  |     Condition C1 --> (LL:8)
+  |     Condition C2 --> (LL:13)
+  |     Condition C3 --> (LL:18)
+  |     Condition C4 --> (LL:23)
+  |     Condition C5 --> (LL:28)
+  |     Condition C6 --> (LL:33)
+  |
+  |  Executed MC/DC Test Vectors:
+  |
+  |     C1, C2, C3, C4, C5, C6    Result
+  |  1 { F,  -,  -,  -,  -,  -  = F      }
+  |
+  |  C1-Pair: not covered
+  |  C2-Pair: not covered
+  |  C3-Pair: not covered
+  |  C4-Pair: not covered
+  |  C5-Pair: not covered
+  |  C6-Pair: not covered
+  |  MC/DC Coverage for Decision: 0.00%
+  |
+  ------------------
+   LL|      0|        core::hint::black_box("hello");
+   LL|      1|    }
+   LL|      1|}
+   LL|       |
+   LL|      1|fn bad() {
+   LL|      1|    let [a, b, c, d, e, f, g] = <[bool; 7]>::default();
+   LL|      1|    if a && b && c && d && e && f && g {
+                          ^0   ^0   ^0   ^0   ^0   ^0
+  ------------------
+  |  Branch (LL:8): [True: 0, False: 1]
+  |  Branch (LL:13): [True: 0, False: 0]
+  |  Branch (LL:18): [True: 0, False: 0]
+  |  Branch (LL:23): [True: 0, False: 0]
+  |  Branch (LL:28): [True: 0, False: 0]
+  |  Branch (LL:33): [True: 0, False: 0]
+  |  Branch (LL:38): [True: 0, False: 0]
+  ------------------
+   LL|      0|        core::hint::black_box("hello");
+   LL|      1|    }
+   LL|      1|}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    good();
+   LL|       |    bad();
+   LL|       |}
+
diff --git a/tests/coverage/mcdc/condition-limit.rs b/tests/coverage/mcdc/condition-limit.rs
new file mode 100644
index 0000000000000..946e7e09686f9
--- /dev/null
+++ b/tests/coverage/mcdc/condition-limit.rs
@@ -0,0 +1,25 @@
+#![feature(coverage_attribute)]
+//@ edition: 2021
+//@ min-llvm-version: 18
+//@ compile-flags: -Zcoverage-options=mcdc
+//@ llvm-cov-flags: --show-branches=count --show-mcdc
+
+fn good() {
+    let [a, b, c, d, e, f] = <[bool; 6]>::default();
+    if a && b && c && d && e && f {
+        core::hint::black_box("hello");
+    }
+}
+
+fn bad() {
+    let [a, b, c, d, e, f, g] = <[bool; 7]>::default();
+    if a && b && c && d && e && f && g {
+        core::hint::black_box("hello");
+    }
+}
+
+#[coverage(off)]
+fn main() {
+    good();
+    bad();
+}
diff --git a/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr b/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr
new file mode 100644
index 0000000000000..4268208f88ad2
--- /dev/null
+++ b/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr
@@ -0,0 +1,8 @@
+warning: Conditions number of the decision (7) exceeds limit (6). MCDC analysis will not count this expression.
+  --> $DIR/mcdc-condition-limit.rs:18:8
+   |
+LL |     if a && b && c && d && e && f && g {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/instrument-coverage/mcdc-condition-limit.rs b/tests/ui/instrument-coverage/mcdc-condition-limit.rs
new file mode 100644
index 0000000000000..fc7231ac75a24
--- /dev/null
+++ b/tests/ui/instrument-coverage/mcdc-condition-limit.rs
@@ -0,0 +1,21 @@
+//@ edition: 2021
+//@ min-llvm-version: 18
+//@ revisions: good bad
+//@ check-pass
+//@ compile-flags: -Cinstrument-coverage -Zcoverage-options=mcdc -Zno-profiler-runtime
+
+#[cfg(good)]
+fn main() {
+    let [a, b, c, d, e, f] = <[bool; 6]>::default();
+    if a && b && c && d && e && f {
+        core::hint::black_box("hello");
+    }
+}
+
+#[cfg(bad)]
+fn main() {
+    let [a, b, c, d, e, f, g] = <[bool; 7]>::default();
+    if a && b && c && d && e && f && g { //[bad]~ WARNING Conditions number of the decision
+        core::hint::black_box("hello");
+    }
+}

From 1a701d4621640a7d209f2a5fedcdcdc7ba58119e Mon Sep 17 00:00:00 2001
From: Zalathar <Zalathar@users.noreply.github.com>
Date: Wed, 1 May 2024 08:58:34 +1000
Subject: [PATCH 7/8] coverage: Rename `conditions_num` to `num_conditions`

This field counts the number of conditions that contribute to a particular
decision, but the name "conditions num" sounds like an ID instead of a count,
so "num conditions" is clearer.
---
 .../src/coverageinfo/ffi.rs                   |  4 +--
 compiler/rustc_middle/src/mir/coverage.rs     |  4 +--
 compiler/rustc_middle/src/mir/pretty.rs       |  4 +--
 compiler/rustc_mir_build/messages.ftl         |  4 ++-
 .../src/build/coverageinfo/mcdc.rs            | 30 +++++++++----------
 compiler/rustc_mir_build/src/errors.rs        |  9 +++---
 .../src/coverage/mappings.rs                  | 10 +++----
 .../rustc_mir_transform/src/coverage/mod.rs   |  6 ++--
 .../mcdc-condition-limit.bad.stderr           |  4 ++-
 .../mcdc-condition-limit.rs                   |  2 +-
 10 files changed, 41 insertions(+), 36 deletions(-)

diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
index 12a846a49ec16..fed7a7190f2c0 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
@@ -118,7 +118,7 @@ pub mod mcdc {
     #[derive(Clone, Copy, Debug, Default)]
     pub struct DecisionParameters {
         bitmap_idx: u32,
-        conditions_num: u16,
+        num_conditions: u16,
     }
 
     // ConditionId in llvm is `unsigned int` at 18 while `int16_t` at [19](https://github.com/llvm/llvm-project/pull/81257)
@@ -178,7 +178,7 @@ pub mod mcdc {
 
     impl From<DecisionInfo> for DecisionParameters {
         fn from(value: DecisionInfo) -> Self {
-            Self { bitmap_idx: value.bitmap_idx, conditions_num: value.conditions_num }
+            Self { bitmap_idx: value.bitmap_idx, num_conditions: value.num_conditions }
         }
     }
 }
diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs
index 7f9a5a366d74e..adb0a8b311f9f 100644
--- a/compiler/rustc_middle/src/mir/coverage.rs
+++ b/compiler/rustc_middle/src/mir/coverage.rs
@@ -329,14 +329,14 @@ pub struct MCDCBranchSpan {
 #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub struct DecisionInfo {
     pub bitmap_idx: u32,
-    pub conditions_num: u16,
+    pub num_conditions: u16,
 }
 
 #[derive(Clone, Debug)]
 #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub struct MCDCDecisionSpan {
     pub span: Span,
-    pub conditions_num: usize,
+    pub num_conditions: usize,
     pub end_markers: Vec<BlockMarkerId>,
     pub decision_depth: u16,
 }
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 5aaa1c30cade0..42e088a459b63 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -511,12 +511,12 @@ fn write_coverage_branch_info(
         )?;
     }
 
-    for coverage::MCDCDecisionSpan { span, conditions_num, end_markers, decision_depth } in
+    for coverage::MCDCDecisionSpan { span, num_conditions, end_markers, decision_depth } in
         mcdc_decision_spans
     {
         writeln!(
             w,
-            "{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}"
+            "{INDENT}coverage mcdc decision {{ num_conditions: {num_conditions:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}"
         )?;
     }
 
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 34440c60cf378..80ae5ec42584f 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -97,7 +97,9 @@ mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
     .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
     .label = dereference of raw pointer
 
-mir_build_exceeds_mcdc_condition_num_limit =  Conditions number of the decision ({$conditions_num}) exceeds limit ({$max_conditions_num}). MCDC analysis will not count this expression.
+mir_build_exceeds_mcdc_condition_limit =
+    number of conditions in decision ({$num_conditions}) exceeds limit ({$limit})
+    .note = this decision will not be instrumented for MC/DC coverage
 
 mir_build_extern_static_requires_unsafe =
     use of extern static is unsafe and requires unsafe block
diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
index 566dba460d43b..e0cd651de003f 100644
--- a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
+++ b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
@@ -9,12 +9,12 @@ use rustc_middle::ty::TyCtxt;
 use rustc_span::Span;
 
 use crate::build::Builder;
-use crate::errors::MCDCExceedsConditionNumLimit;
+use crate::errors::MCDCExceedsConditionLimit;
 
 /// The MCDC bitmap scales exponentially (2^n) based on the number of conditions seen,
-/// So llvm sets a maximum value prevents the bitmap footprint from growing too large without the user's knowledge.
-/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
-const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6;
+/// So LLVM imposes a limit to prevent the bitmap footprint from growing too large without the user's knowledge.
+/// This limit may be relaxed if [upstream change #82448](https://github.com/llvm/llvm-project/pull/82448) is merged.
+const MAX_CONDITIONS_IN_DECISION: usize = 6;
 
 #[derive(Default)]
 struct MCDCDecisionCtx {
@@ -99,7 +99,7 @@ impl MCDCState {
             }
             None => decision_ctx.processing_decision.insert(MCDCDecisionSpan {
                 span,
-                conditions_num: 0,
+                num_conditions: 0,
                 end_markers: vec![],
                 decision_depth,
             }),
@@ -107,14 +107,14 @@ impl MCDCState {
 
         let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_default();
         let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
-            decision.conditions_num += 1;
-            ConditionId::from(decision.conditions_num)
+            decision.num_conditions += 1;
+            ConditionId::from(decision.num_conditions)
         } else {
             parent_condition.condition_id
         };
 
-        decision.conditions_num += 1;
-        let rhs_condition_id = ConditionId::from(decision.conditions_num);
+        decision.num_conditions += 1;
+        let rhs_condition_id = ConditionId::from(decision.num_conditions);
 
         let (lhs, rhs) = match op {
             LogicalOp::And => {
@@ -207,16 +207,16 @@ impl MCDCInfoBuilder {
         // is empty, i.e. when all the conditions of the decision were instrumented,
         // and the decision is "complete".
         if let Some(decision) = decision_result {
-            match decision.conditions_num {
+            match decision.num_conditions {
                 0 => {
                     unreachable!("Decision with no condition is not expected");
                 }
-                1..=MAX_CONDITIONS_NUM_IN_DECISION => {
+                1..=MAX_CONDITIONS_IN_DECISION => {
                     self.decision_spans.push(decision);
                 }
                 _ => {
                     // Do not generate mcdc mappings and statements for decisions with too many conditions.
-                    let rebase_idx = self.branch_spans.len() - decision.conditions_num + 1;
+                    let rebase_idx = self.branch_spans.len() - decision.num_conditions + 1;
                     for branch in &mut self.branch_spans[rebase_idx..] {
                         branch.condition_info = None;
                     }
@@ -224,10 +224,10 @@ impl MCDCInfoBuilder {
                     // ConditionInfo of this branch shall also be reset.
                     condition_info = None;
 
-                    tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit {
+                    tcx.dcx().emit_warn(MCDCExceedsConditionLimit {
                         span: decision.span,
-                        conditions_num: decision.conditions_num,
-                        max_conditions_num: MAX_CONDITIONS_NUM_IN_DECISION,
+                        num_conditions: decision.num_conditions,
+                        limit: MAX_CONDITIONS_IN_DECISION,
                     });
                 }
             }
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index f67113afd6d9d..36d405c48d4cc 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -819,12 +819,13 @@ pub struct NontrivialStructuralMatch<'tcx> {
 }
 
 #[derive(Diagnostic)]
-#[diag(mir_build_exceeds_mcdc_condition_num_limit)]
-pub(crate) struct MCDCExceedsConditionNumLimit {
+#[diag(mir_build_exceeds_mcdc_condition_limit)]
+#[note]
+pub(crate) struct MCDCExceedsConditionLimit {
     #[primary_span]
     pub span: Span,
-    pub conditions_num: usize,
-    pub max_conditions_num: usize,
+    pub num_conditions: usize,
+    pub limit: usize,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs
index ddbe1333c4b08..789f9d65cabee 100644
--- a/compiler/rustc_mir_transform/src/coverage/mappings.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs
@@ -48,7 +48,7 @@ pub(super) struct MCDCDecision {
     pub(super) span: Span,
     pub(super) end_bcbs: BTreeSet<BasicCoverageBlock>,
     pub(super) bitmap_idx: u32,
-    pub(super) conditions_num: u16,
+    pub(super) num_conditions: u16,
     pub(super) decision_depth: u16,
 }
 
@@ -136,8 +136,8 @@ pub(super) fn generate_coverage_spans(
     // Determine the length of the test vector bitmap.
     let test_vector_bitmap_bytes = mcdc_decisions
         .iter()
-        .map(|&MCDCDecision { bitmap_idx, conditions_num, .. }| {
-            bitmap_idx + (1_u32 << u32::from(conditions_num)).div_ceil(8)
+        .map(|&MCDCDecision { bitmap_idx, num_conditions, .. }| {
+            bitmap_idx + (1_u32 << u32::from(num_conditions)).div_ceil(8)
         })
         .max()
         .unwrap_or(0);
@@ -266,13 +266,13 @@ pub(super) fn extract_mcdc_mappings(
                 .collect::<Option<_>>()?;
 
             let bitmap_idx = next_bitmap_idx;
-            next_bitmap_idx += (1_u32 << decision.conditions_num).div_ceil(8);
+            next_bitmap_idx += (1_u32 << decision.num_conditions).div_ceil(8);
 
             Some(MCDCDecision {
                 span,
                 end_bcbs,
                 bitmap_idx,
-                conditions_num: decision.conditions_num as u16,
+                num_conditions: decision.num_conditions as u16,
                 decision_depth: decision.decision_depth,
             })
         },
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 1e2599e78e9e1..99af04f46abbf 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -182,9 +182,9 @@ fn create_mappings<'tcx>(
     ));
 
     mappings.extend(coverage_spans.mcdc_decisions.iter().filter_map(
-        |&mappings::MCDCDecision { span, bitmap_idx, conditions_num, .. }| {
+        |&mappings::MCDCDecision { span, bitmap_idx, num_conditions, .. }| {
             let code_region = region_for_span(span)?;
-            let kind = MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, conditions_num });
+            let kind = MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, num_conditions });
             Some(Mapping { kind, code_region })
         },
     ));
@@ -260,7 +260,7 @@ fn inject_mcdc_statements<'tcx>(
         span: _,
         ref end_bcbs,
         bitmap_idx,
-        conditions_num: _,
+        num_conditions: _,
         decision_depth,
     } in &coverage_spans.mcdc_decisions
     {
diff --git a/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr b/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr
index 4268208f88ad2..a2763f5b88a40 100644
--- a/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr
+++ b/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr
@@ -1,8 +1,10 @@
-warning: Conditions number of the decision (7) exceeds limit (6). MCDC analysis will not count this expression.
+warning: number of conditions in decision (7) exceeds limit (6)
   --> $DIR/mcdc-condition-limit.rs:18:8
    |
 LL |     if a && b && c && d && e && f && g {
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this decision will not be instrumented for MC/DC coverage
 
 warning: 1 warning emitted
 
diff --git a/tests/ui/instrument-coverage/mcdc-condition-limit.rs b/tests/ui/instrument-coverage/mcdc-condition-limit.rs
index fc7231ac75a24..7a55c7df4a946 100644
--- a/tests/ui/instrument-coverage/mcdc-condition-limit.rs
+++ b/tests/ui/instrument-coverage/mcdc-condition-limit.rs
@@ -15,7 +15,7 @@ fn main() {
 #[cfg(bad)]
 fn main() {
     let [a, b, c, d, e, f, g] = <[bool; 7]>::default();
-    if a && b && c && d && e && f && g { //[bad]~ WARNING Conditions number of the decision
+    if a && b && c && d && e && f && g { //[bad]~ WARNING number of conditions in decision
         core::hint::black_box("hello");
     }
 }

From 776f182241ca224c459356a3c30ea9bef6611971 Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Tue, 7 May 2024 10:48:04 +0200
Subject: [PATCH 8/8] next_power_of_two: add a doctest to show what happens on
 0

---
 library/core/src/num/uint_macros.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index 673144cb32827..48a96c5792c64 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -2764,6 +2764,7 @@ macro_rules! uint_impl {
         /// ```
         #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".next_power_of_two(), 2);")]
         #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".next_power_of_two(), 4);")]
+        #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".next_power_of_two(), 1);")]
         /// ```
         #[stable(feature = "rust1", since = "1.0.0")]
         #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")]