From 17b364262b725399849df55e79eb924754453561 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 14 Jul 2026 19:10:25 +1000 Subject: [PATCH 1/8] Remove `ResultsVisitor::visit_block_end` `ResultsVisitor` has `visit_block_start` which is called on entry to a a block in a forwards analysis and on exit from a block in a backwards analysis. And vice versa for `visit_block_end`. The only visitor that impls these methods is `StateDiffCollector`, which does something in `visit_block_start` for a forwards analysis and the same thing in `visit_block_end` for a backwards analysis. In other words, `StateDiffCollector` wants to always do the same thing on entry to a block and never do anything on exit from a block. This commit replaces `visit_block_{start,end}` with `visit_block_entry`, which is always called on entry to a block. This is simpler overall. --- .../rustc_mir_dataflow/src/framework/direction.rs | 8 ++------ .../rustc_mir_dataflow/src/framework/graphviz.rs | 12 ++---------- compiler/rustc_mir_dataflow/src/framework/visitor.rs | 6 +++--- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 39df33187d3a9..ad1d9766546a6 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -214,7 +214,7 @@ impl Direction for Backward { ) where A: Analysis<'tcx>, { - vis.visit_block_end(state); + vis.visit_block_entry(state); let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); @@ -230,8 +230,6 @@ impl Direction for Backward { analysis.apply_primary_statement_effect(state, stmt, loc); vis.visit_after_primary_statement_effect(analysis, state, stmt, loc); } - - vis.visit_block_start(state); } } @@ -393,7 +391,7 @@ impl Direction for Forward { ) where A: Analysis<'tcx>, { - vis.visit_block_start(state); + vis.visit_block_entry(state); for (statement_index, stmt) in block_data.statements.iter().enumerate() { let loc = Location { block, statement_index }; @@ -409,7 +407,5 @@ impl Direction for Forward { vis.visit_after_early_terminator_effect(analysis, state, term, loc); analysis.apply_primary_terminator_effect(state, term, loc); vis.visit_after_primary_terminator_effect(analysis, state, term, loc); - - vis.visit_block_end(state); } } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 6c0f2e8d73058..ed34a1f151eb9 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -660,16 +660,8 @@ where A: Analysis<'tcx>, A::Domain: DebugWithContext, { - fn visit_block_start(&mut self, state: &A::Domain) { - if A::Direction::IS_FORWARD { - self.prev_state.clone_from(state); - } - } - - fn visit_block_end(&mut self, state: &A::Domain) { - if A::Direction::IS_BACKWARD { - self.prev_state.clone_from(state); - } + fn visit_block_entry(&mut self, state: &A::Domain) { + self.prev_state.clone_from(state); } fn visit_after_early_statement_effect( diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 46940c6ab62fc..befe3c0a738d1 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -46,7 +46,9 @@ pub trait ResultsVisitor<'tcx, A> where A: Analysis<'tcx>, { - fn visit_block_start(&mut self, _state: &A::Domain) {} + /// Called on entry to a block. In a forwards analysis, `_state` is from the block's start. In + /// a backwards analysis, `_state` is from the block's end. + fn visit_block_entry(&mut self, _state: &A::Domain) {} /// Called after the "early" effect of the given statement is applied to `state`. fn visit_after_early_statement_effect( @@ -89,6 +91,4 @@ where _location: Location, ) { } - - fn visit_block_end(&mut self, _state: &A::Domain) {} } From f2063f90994e6fe1755d487c5fe627412389d76e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 14 Jul 2026 19:46:39 +1000 Subject: [PATCH 2/8] Avoid some `IS_FORWARD` tests By adding more methods to `Direction`. This makes things more concise, and these new methods will be used more in subsequent commits. --- .../src/framework/cursor.rs | 16 ++---- .../src/framework/direction.rs | 54 ++++++++++++++++++- .../rustc_mir_dataflow/src/framework/mod.rs | 38 ------------- .../rustc_mir_dataflow/src/framework/tests.rs | 12 +---- 4 files changed, 58 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index 3c56999fcbdc9..c01cee3e86b6b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -195,18 +195,10 @@ where debug_assert_eq!(target.block, self.pos.block); let block_data = &self.body[target.block]; - #[rustfmt::skip] - let next_effect = if A::Direction::IS_FORWARD { - self.pos.curr_effect_index.map_or_else( - || Effect::Early.at_index(0), - EffectIndex::next_in_forward_order, - ) - } else { - self.pos.curr_effect_index.map_or_else( - || Effect::Early.at_index(block_data.statements.len()), - EffectIndex::next_in_backward_order, - ) - }; + let next_effect = self.pos.curr_effect_index.map_or_else( + || A::Direction::first_index(block_data), + |idx| A::Direction::next_index(idx), + ); let target_effect_index = effect.at_index(target.statement_index); diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index ad1d9766546a6..5b41effed6068 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -1,3 +1,4 @@ +use std::cmp::Ordering; use std::ops::RangeInclusive; use rustc_middle::bug; @@ -10,6 +11,16 @@ pub trait Direction { const IS_FORWARD: bool; const IS_BACKWARD: bool = !Self::IS_FORWARD; + /// Returns the first statement index for this direction. (0 when going forward and + /// `statements.len()` when going backward.) + fn first_index(block_data: &mir::BasicBlockData<'_>) -> EffectIndex; + + /// Returns `true` if `a` comes before `b` for this direction. + fn index_precedes(a: EffectIndex, b: EffectIndex) -> bool; + + /// Returns the next index for this direction. + fn next_index(idx: EffectIndex) -> EffectIndex; + /// Called by `iterate_to_fixpoint` during initial analysis computation. fn apply_effects_in_block<'mir, 'tcx, A>( analysis: &A, @@ -53,6 +64,26 @@ pub struct Backward; impl Direction for Backward { const IS_FORWARD: bool = false; + fn first_index(block_data: &mir::BasicBlockData<'_>) -> EffectIndex { + Effect::Early.at_index(block_data.statements.len()) + } + + fn index_precedes(a: EffectIndex, b: EffectIndex) -> bool { + // Higher statement indices precede lower statement indices, and then `Early` effects + // precede `Primary` effects. (That's why the two comparisons use different orders for `a` + // and `b`.) + let ord = b.statement_index.cmp(&a.statement_index).then_with(|| a.effect.cmp(&b.effect)); + ord == Ordering::Less + } + + /// Returns the next index for this direction. + fn next_index(idx: EffectIndex) -> EffectIndex { + match idx.effect { + Effect::Early => Effect::Primary.at_index(idx.statement_index), + Effect::Primary => Effect::Early.at_index(idx.statement_index - 1), + } + } + fn apply_effects_in_block<'mir, 'tcx, A>( analysis: &A, body: &mir::Body<'tcx>, @@ -141,7 +172,7 @@ impl Direction for Backward { let terminator_index = block_data.statements.len(); assert!(from.statement_index <= terminator_index); - assert!(!to.precedes_in_backward_order(from)); + assert!(!Self::index_precedes(to, from)); // Handle the statement (or terminator) at `from`. @@ -239,6 +270,25 @@ pub struct Forward; impl Direction for Forward { const IS_FORWARD: bool = true; + fn first_index(_block_data: &mir::BasicBlockData<'_>) -> EffectIndex { + Effect::Early.at_index(0) + } + + fn index_precedes(a: EffectIndex, b: EffectIndex) -> bool { + // Lower statement indices precede higher statement indices, and then `Early` effects + // precede `Primary` effects. + let ord = a.statement_index.cmp(&b.statement_index).then_with(|| a.effect.cmp(&b.effect)); + ord == Ordering::Less + } + + /// Returns the next index for this direction. + fn next_index(idx: EffectIndex) -> EffectIndex { + match idx.effect { + Effect::Early => Effect::Primary.at_index(idx.statement_index), + Effect::Primary => Effect::Early.at_index(idx.statement_index + 1), + } + } + fn apply_effects_in_block<'mir, 'tcx, A>( analysis: &A, body: &mir::Body<'tcx>, @@ -321,7 +371,7 @@ impl Direction for Forward { let terminator_index = block_data.statements.len(); assert!(to.statement_index <= terminator_index); - assert!(!to.precedes_in_forward_order(from)); + assert!(!Self::index_precedes(to, from)); // If we have applied the before affect of the statement or terminator at `from` but not its // after effect, do so now and start the loop below from the next statement. diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 6445ba7ad27b6..5156749f2e6d8 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -32,8 +32,6 @@ //! //! [gen-kill]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems -use std::cmp::Ordering; - use rustc_data_structures::work_queue::WorkQueue; use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_index::{Idx, IndexVec}; @@ -402,41 +400,5 @@ pub struct EffectIndex { effect: Effect, } -impl EffectIndex { - fn next_in_forward_order(self) -> Self { - match self.effect { - Effect::Early => Effect::Primary.at_index(self.statement_index), - Effect::Primary => Effect::Early.at_index(self.statement_index + 1), - } - } - - fn next_in_backward_order(self) -> Self { - match self.effect { - Effect::Early => Effect::Primary.at_index(self.statement_index), - Effect::Primary => Effect::Early.at_index(self.statement_index - 1), - } - } - - /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other` - /// in forward order. - fn precedes_in_forward_order(self, other: Self) -> bool { - let ord = self - .statement_index - .cmp(&other.statement_index) - .then_with(|| self.effect.cmp(&other.effect)); - ord == Ordering::Less - } - - /// Returns `true` if the effect at `self` should be applied earlier than the effect at `other` - /// in backward order. - fn precedes_in_backward_order(self, other: Self) -> bool { - let ord = other - .statement_index - .cmp(&self.statement_index) - .then_with(|| self.effect.cmp(&other.effect)); - ord == Ordering::Less - } -} - #[cfg(test)] mod tests; diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index dec273d39dd1a..86ea3a34ae0ea 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -139,11 +139,7 @@ impl MockAnalysis<'_, D> { SeekTarget::After(loc) => Effect::Primary.at_index(loc.statement_index), }; - let mut pos = if D::IS_FORWARD { - Effect::Early.at_index(0) - } else { - Effect::Early.at_index(self.body[block].statements.len()) - }; + let mut pos = D::first_index(&self.body[block]); loop { ret.insert(self.effect(pos)); @@ -152,11 +148,7 @@ impl MockAnalysis<'_, D> { return ret; } - if D::IS_FORWARD { - pos = pos.next_in_forward_order(); - } else { - pos = pos.next_in_backward_order(); - } + pos = D::next_index(pos); } } } From 642e9d2b47dc44f36c4e2384b32a5c1f54fea516 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 14 Jul 2026 15:11:32 +1000 Subject: [PATCH 3/8] Simplify `apply_effects_in_range` This commit adds `Analysis::apply_effect`, which takes an `EffectIndex` and calls the appropriate `Analysis::apply_*` method. Once that is in place, it is possible to use it with `next_index` to write a simple `apply_effects_in_range` method that can be shared between `Forward` and `Backward`. The end result is much easier to understand. --- .../src/framework/direction.rs | 170 ++---------------- .../rustc_mir_dataflow/src/framework/mod.rs | 38 +++- 2 files changed, 55 insertions(+), 153 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 5b41effed6068..c5157dc728614 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -43,7 +43,24 @@ pub trait Direction { block_data: &mir::BasicBlockData<'tcx>, effects: RangeInclusive, ) where - A: Analysis<'tcx>; + A: Analysis<'tcx>, + { + let (from, to) = (*effects.start(), *effects.end()); + let terminator_index = block_data.statements.len(); + + assert!(to.statement_index <= terminator_index); + assert!(from.statement_index <= terminator_index); + assert!(!Self::index_precedes(to, from)); + + let mut idx = from; + loop { + analysis.apply_effect(state, block, block_data, idx); + if idx == to { + break; + } + idx = Self::next_index(idx); + } + } /// Called by `ResultsVisitor` to recompute the analysis domain values for /// all locations in a basic block (starting from `entry_state` and to @@ -159,83 +176,6 @@ impl Direction for Backward { } } - fn apply_effects_in_range<'tcx, A>( - analysis: &A, - state: &mut A::Domain, - block: BasicBlock, - block_data: &mir::BasicBlockData<'tcx>, - effects: RangeInclusive, - ) where - A: Analysis<'tcx>, - { - let (from, to) = (*effects.start(), *effects.end()); - let terminator_index = block_data.statements.len(); - - assert!(from.statement_index <= terminator_index); - assert!(!Self::index_precedes(to, from)); - - // Handle the statement (or terminator) at `from`. - - let next_effect = match from.effect { - // If we need to apply the terminator effect in all or in part, do so now. - _ if from.statement_index == terminator_index => { - let location = Location { block, statement_index: from.statement_index }; - let terminator = block_data.terminator(); - - if from.effect == Effect::Early { - analysis.apply_early_terminator_effect(state, terminator, location); - if to == Effect::Early.at_index(terminator_index) { - return; - } - } - - analysis.apply_primary_terminator_effect(state, terminator, location); - if to == Effect::Primary.at_index(terminator_index) { - return; - } - - // If `from.statement_index` is `0`, we will have hit one of the earlier comparisons - // with `to`. - from.statement_index - 1 - } - - Effect::Primary => { - let location = Location { block, statement_index: from.statement_index }; - let statement = &block_data.statements[from.statement_index]; - - analysis.apply_primary_statement_effect(state, statement, location); - if to == Effect::Primary.at_index(from.statement_index) { - return; - } - - from.statement_index - 1 - } - - Effect::Early => from.statement_index, - }; - - // Handle all statements between `first_unapplied_idx` and `to.statement_index`. - - for statement_index in (to.statement_index..next_effect).rev().map(|i| i + 1) { - let location = Location { block, statement_index }; - let statement = &block_data.statements[statement_index]; - analysis.apply_early_statement_effect(state, statement, location); - analysis.apply_primary_statement_effect(state, statement, location); - } - - // Handle the statement at `to`. - - let location = Location { block, statement_index: to.statement_index }; - let statement = &block_data.statements[to.statement_index]; - analysis.apply_early_statement_effect(state, statement, location); - - if to.effect == Effect::Early { - return; - } - - analysis.apply_primary_statement_effect(state, statement, location); - } - fn visit_results_in_block<'mir, 'tcx, A>( analysis: &A, state: &mut A::Domain, @@ -358,80 +298,6 @@ impl Direction for Forward { } } - fn apply_effects_in_range<'tcx, A>( - analysis: &A, - state: &mut A::Domain, - block: BasicBlock, - block_data: &mir::BasicBlockData<'tcx>, - effects: RangeInclusive, - ) where - A: Analysis<'tcx>, - { - let (from, to) = (*effects.start(), *effects.end()); - let terminator_index = block_data.statements.len(); - - assert!(to.statement_index <= terminator_index); - assert!(!Self::index_precedes(to, from)); - - // If we have applied the before affect of the statement or terminator at `from` but not its - // after effect, do so now and start the loop below from the next statement. - - let first_unapplied_index = match from.effect { - Effect::Early => from.statement_index, - - Effect::Primary if from.statement_index == terminator_index => { - debug_assert_eq!(from, to); - - let location = Location { block, statement_index: terminator_index }; - let terminator = block_data.terminator(); - analysis.apply_primary_terminator_effect(state, terminator, location); - return; - } - - Effect::Primary => { - let location = Location { block, statement_index: from.statement_index }; - let statement = &block_data.statements[from.statement_index]; - analysis.apply_primary_statement_effect(state, statement, location); - - // If we only needed to apply the after effect of the statement at `idx`, we are - // done. - if from == to { - return; - } - - from.statement_index + 1 - } - }; - - // Handle all statements between `from` and `to` whose effects must be applied in full. - - for statement_index in first_unapplied_index..to.statement_index { - let location = Location { block, statement_index }; - let statement = &block_data.statements[statement_index]; - analysis.apply_early_statement_effect(state, statement, location); - analysis.apply_primary_statement_effect(state, statement, location); - } - - // Handle the statement or terminator at `to`. - - let location = Location { block, statement_index: to.statement_index }; - if to.statement_index == terminator_index { - let terminator = block_data.terminator(); - analysis.apply_early_terminator_effect(state, terminator, location); - - if to.effect == Effect::Primary { - analysis.apply_primary_terminator_effect(state, terminator, location); - } - } else { - let statement = &block_data.statements[to.statement_index]; - analysis.apply_early_statement_effect(state, statement, location); - - if to.effect == Effect::Primary { - analysis.apply_primary_statement_effect(state, statement, location); - } - } - } - fn visit_results_in_block<'mir, 'tcx, A>( analysis: &A, state: &mut A::Domain, diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 5156749f2e6d8..211a1b8d05b71 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -36,7 +36,9 @@ use rustc_data_structures::work_queue::WorkQueue; use rustc_index::bit_set::{DenseBitSet, MixedBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::bug; -use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges, traversal}; +use rustc_middle::mir::{ + self, BasicBlock, BasicBlockData, CallReturnPlaces, Location, TerminatorEdges, traversal, +}; use rustc_middle::ty::TyCtxt; use tracing::error; @@ -123,6 +125,40 @@ pub trait Analysis<'tcx> { // `resume`). It's not obvious how to handle `yield` points in coroutines, however. fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain); + /// Given an `EffectIndex`, calls the appropriate `apply_*` method in the + /// {early,primary} x {statement,terminator} space. + /// + /// Do not override this; instead override one or more of the `apply_*` methods. + #[inline] + fn apply_effect<'mir>( + &self, + state: &mut Self::Domain, + block: BasicBlock, + block_data: &'mir BasicBlockData<'tcx>, + idx: EffectIndex, + ) { + let statement_index = idx.statement_index; + let terminator_index = block_data.statements.len(); + let loc = Location { block, statement_index }; + let is_terminator = statement_index == terminator_index; + + if !is_terminator { + let statement = &block_data.statements[statement_index]; + match idx.effect { + Effect::Early => self.apply_early_statement_effect(state, statement, loc), + Effect::Primary => self.apply_primary_statement_effect(state, statement, loc), + } + } else { + let terminator = block_data.terminator(); + match idx.effect { + Effect::Early => self.apply_early_terminator_effect(state, terminator, loc), + Effect::Primary => { + self.apply_primary_terminator_effect(state, terminator, loc); + } + } + } + } + /// Updates the current dataflow state with an "early" effect, i.e. one /// that occurs immediately before the given statement. /// From 61b5db69f7aba90eeb831545a1c25922616c0c1f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 20 Jul 2026 13:19:20 +1000 Subject: [PATCH 4/8] Eliminate `ResultsVisitor::visit_block_entry` It's only used by `StateDiffCollector`, and it's just a complicated way to get the entry state, which can instead be done directly (avoiding the creation of a `bottom_value` which was immediately overwritten). --- compiler/rustc_mir_dataflow/src/framework/direction.rs | 4 ---- compiler/rustc_mir_dataflow/src/framework/graphviz.rs | 8 ++------ compiler/rustc_mir_dataflow/src/framework/visitor.rs | 4 ---- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index c5157dc728614..8519eb92c8e02 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -185,8 +185,6 @@ impl Direction for Backward { ) where A: Analysis<'tcx>, { - vis.visit_block_entry(state); - let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); analysis.apply_early_terminator_effect(state, term, loc); @@ -307,8 +305,6 @@ impl Direction for Forward { ) where A: Analysis<'tcx>, { - vis.visit_block_entry(state); - for (statement_index, stmt) in block_data.statements.iter().enumerate() { let loc = Location { block, statement_index }; analysis.apply_early_statement_effect(state, stmt, loc); diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index ed34a1f151eb9..a95ab44c951d6 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -633,7 +633,7 @@ struct StateDiffCollector { after: Vec, } -impl StateDiffCollector { +impl StateDiffCollector { fn run<'tcx, A>( body: &Body<'tcx>, block: BasicBlock, @@ -645,7 +645,7 @@ impl StateDiffCollector { D: DebugWithContext, { let mut collector = StateDiffCollector { - prev_state: results.analysis.bottom_value(body), + prev_state: results.entry_states[block].clone(), after: vec![], before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]), }; @@ -660,10 +660,6 @@ where A: Analysis<'tcx>, A::Domain: DebugWithContext, { - fn visit_block_entry(&mut self, state: &A::Domain) { - self.prev_state.clone_from(state); - } - fn visit_after_early_statement_effect( &mut self, analysis: &A, diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index befe3c0a738d1..b6827be3d8beb 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -46,10 +46,6 @@ pub trait ResultsVisitor<'tcx, A> where A: Analysis<'tcx>, { - /// Called on entry to a block. In a forwards analysis, `_state` is from the block's start. In - /// a backwards analysis, `_state` is from the block's end. - fn visit_block_entry(&mut self, _state: &A::Domain) {} - /// Called after the "early" effect of the given statement is applied to `state`. fn visit_after_early_statement_effect( &mut self, From e736635dd39d3d346e6d818e2bd30c7feb83b94a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 20 Jul 2026 13:32:54 +1000 Subject: [PATCH 5/8] Inline and remove `Direction::apply_effects_in_range` It has a single call site. The commit removes the assertions because they necessary any more due to the assertions and checks at the call site. This then removes the need for `index_precedes`. --- .../src/framework/cursor.rs | 16 +++--- .../src/framework/direction.rs | 51 ------------------- 2 files changed, 8 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index c01cee3e86b6b..63b2adceb524c 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -199,16 +199,16 @@ where || A::Direction::first_index(block_data), |idx| A::Direction::next_index(idx), ); - let target_effect_index = effect.at_index(target.statement_index); - A::Direction::apply_effects_in_range( - &self.results.analysis, - &mut self.state, - target.block, - block_data, - next_effect..=target_effect_index, - ); + let mut idx = next_effect; + loop { + self.results.analysis.apply_effect(&mut self.state, target.block, block_data, idx); + if idx == target_effect_index { + break; + } + idx = A::Direction::next_index(idx); + } self.pos = CursorPosition { block: target.block, curr_effect_index: Some(target_effect_index) }; diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 8519eb92c8e02..68c8e03de8022 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -1,6 +1,3 @@ -use std::cmp::Ordering; -use std::ops::RangeInclusive; - use rustc_middle::bug; use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges}; @@ -15,9 +12,6 @@ pub trait Direction { /// `statements.len()` when going backward.) fn first_index(block_data: &mir::BasicBlockData<'_>) -> EffectIndex; - /// Returns `true` if `a` comes before `b` for this direction. - fn index_precedes(a: EffectIndex, b: EffectIndex) -> bool; - /// Returns the next index for this direction. fn next_index(idx: EffectIndex) -> EffectIndex; @@ -32,36 +26,6 @@ pub trait Direction { ) where A: Analysis<'tcx>; - /// Called by `ResultsCursor` to recompute the domain value for a location - /// in a basic block. Applies all effects between the given `EffectIndex`s. - /// - /// `effects.start()` must precede or equal `effects.end()` in this direction. - fn apply_effects_in_range<'tcx, A>( - analysis: &A, - state: &mut A::Domain, - block: BasicBlock, - block_data: &mir::BasicBlockData<'tcx>, - effects: RangeInclusive, - ) where - A: Analysis<'tcx>, - { - let (from, to) = (*effects.start(), *effects.end()); - let terminator_index = block_data.statements.len(); - - assert!(to.statement_index <= terminator_index); - assert!(from.statement_index <= terminator_index); - assert!(!Self::index_precedes(to, from)); - - let mut idx = from; - loop { - analysis.apply_effect(state, block, block_data, idx); - if idx == to { - break; - } - idx = Self::next_index(idx); - } - } - /// Called by `ResultsVisitor` to recompute the analysis domain values for /// all locations in a basic block (starting from `entry_state` and to /// visit them with `vis`. @@ -85,14 +49,6 @@ impl Direction for Backward { Effect::Early.at_index(block_data.statements.len()) } - fn index_precedes(a: EffectIndex, b: EffectIndex) -> bool { - // Higher statement indices precede lower statement indices, and then `Early` effects - // precede `Primary` effects. (That's why the two comparisons use different orders for `a` - // and `b`.) - let ord = b.statement_index.cmp(&a.statement_index).then_with(|| a.effect.cmp(&b.effect)); - ord == Ordering::Less - } - /// Returns the next index for this direction. fn next_index(idx: EffectIndex) -> EffectIndex { match idx.effect { @@ -212,13 +168,6 @@ impl Direction for Forward { Effect::Early.at_index(0) } - fn index_precedes(a: EffectIndex, b: EffectIndex) -> bool { - // Lower statement indices precede higher statement indices, and then `Early` effects - // precede `Primary` effects. - let ord = a.statement_index.cmp(&b.statement_index).then_with(|| a.effect.cmp(&b.effect)); - ord == Ordering::Less - } - /// Returns the next index for this direction. fn next_index(idx: EffectIndex) -> EffectIndex { match idx.effect { From 490b3445bd1ad712987ed8e2da51ea69ebe08950 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Tue, 21 Jul 2026 16:45:50 +0800 Subject: [PATCH 6/8] increase depth for float fallback hack visitor --- .../src/fn_ctxt/inspect_obligations.rs | 47 ++++++++---- compiler/rustc_infer/src/traits/engine.rs | 11 +++ .../src/solve/fulfill.rs | 74 +++++++++++-------- .../next-solver/float-fallback-hack-depth.rs | 21 ++++++ .../float-fallback-hack-depth.stderr | 12 +++ 5 files changed, 122 insertions(+), 43 deletions(-) create mode 100644 tests/ui/traits/next-solver/float-fallback-hack-depth.rs create mode 100644 tests/ui/traits/next-solver/float-fallback-hack-depth.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index cfaa60231379e..bc1dd222c56ca 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -191,7 +191,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(from_trait) = self.tcx.lang_items().from_trait() else { return UnordSet::new(); }; - let obligations = self.fulfillment_cx.borrow().pending_obligations(); + let obligations = self + .fulfillment_cx + .borrow() + .pending_obligations_potentially_referencing_float_infer(self); debug!(?obligations); let mut vids = UnordSet::new(); for obligation in obligations { @@ -209,6 +212,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } +/// Using an intentionally low depth to minimize the chance of future +/// breaking changes in case we adapt the approach later on. This also +/// avoids any hangs for exponentially growing proof trees. +const MAX_DEPTH_FOR_OBLIGATIONS_VISITORS: usize = 5; + struct NestedObligationsForSelfTy<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, self_ty: ty::TyVid, @@ -223,10 +231,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'_, 'tcx> { } fn config(&self) -> InspectConfig { - // Using an intentionally low depth to minimize the chance of future - // breaking changes in case we adapt the approach later on. This also - // avoids any hangs for exponentially growing proof trees. - InspectConfig { max_depth: 5 } + InspectConfig { max_depth: MAX_DEPTH_FOR_OBLIGATIONS_VISITORS } } fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) { @@ -282,23 +287,37 @@ impl<'tcx> ProofTreeVisitor<'tcx> for FindFromFloatForF32RootVids<'_, 'tcx> { } fn config(&self) -> InspectConfig { - // Avoid hang from exponentially growing proof trees (see `cycle-modulo-ambig-aliases.rs`). - // 3 is more than enough for all occurrences in practice (a.k.a. `Into`). - InspectConfig { max_depth: 3 } + InspectConfig { max_depth: MAX_DEPTH_FOR_OBLIGATIONS_VISITORS } } fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) { + // No need to walk into goal subtrees that certainly hold, since they + // wouldn't then be stalled on an infer var. + if inspect_goal.result() == Ok(Certainty::Yes) { + return; + } + + // We don't care about any pending goals which don't actually + // use any float infer var. + if !inspect_goal + .orig_values() + .iter() + .filter_map(|arg| arg.as_type()) + .any(|ty| matches!(self.fcx.shallow_resolve(ty).kind(), ty::Infer(ty::FloatVar(_)))) + { + debug!(goal = ?inspect_goal.goal(), "goal does not mention float infer var"); + return; + } + if let Some(vid) = self .fcx .predicate_from_float_for_f32_root_vid(self.from_trait, inspect_goal.goal().predicate) { self.vids.insert(vid); - } else if let Some(candidate) = inspect_goal.unique_applicable_candidate() { - let start_len = self.vids.len(); - let _ = candidate.goal().infcx().commit_if_ok(|_| { - candidate.visit_nested_no_probe(self); - if self.vids.len() > start_len { Ok(()) } else { Err(()) } - }); + } + + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_no_probe(self); } } } diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 38fc991fcfeb6..6adec25be32f0 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -120,6 +120,17 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { self.pending_obligations() } + /// Pending obligations potentially referencing float inference variables. + /// + /// FIXME: use a generic filter for `pending_obligations_potentially_referencing_sub_root` + /// and this after `TraitEngine` doesn't need to be dyn compatible. + fn pending_obligations_potentially_referencing_float_infer( + &self, + _infcx: &InferCtxt<'tcx>, + ) -> PredicateObligations<'tcx> { + self.pending_obligations() + } + /// Among all pending obligations, collect those are stalled on a inference variable which has /// changed since the last call to `try_evaluate_obligations`. Those obligations are marked as /// successful and returned. diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 4c2c92ebc5072..8d5d8f26f9dce 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -6,7 +6,7 @@ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::{ FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, }; -use rustc_middle::ty::{self, TyCtxt, TyVid, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_next_trait_solver::solve::fast_path::compute_goal_fast_path; use rustc_next_trait_solver::solve::{ GoalEvaluation, GoalStalledOn, HasChanged, MaybeInfo, SolverDelegateEvalExt as _, @@ -79,33 +79,12 @@ impl<'tcx> ObligationStorage<'tcx> { obligations } - fn clone_pending_potentially_referencing_sub_root( - &self, - infcx: &InferCtxt<'tcx>, - vid: TyVid, - ) -> PredicateObligations<'tcx> { - let mut obligations: PredicateObligations<'tcx> = self - .pending - .iter() - .filter(|(_, stalled_on)| { - let Some(stalled_on) = stalled_on else { return true }; - // Don't reuse the sub-unification roots cached on `stalled_on`: - // a later sub-unification merge can have changed which root - // each stalled var belongs to, so the cached info can be stale. - // Walk `stalled_vars` and recompute the current root instead. - // - // Conservative here: if a stalled var no longer resolves to an - // infer var, some unification happened, so the goal is no longer - // stalled. Include it to be re-evaluated downstream. - stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type()).any( - |ty| match *infcx.shallow_resolve(ty).kind() { - ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid, - _ => true, - }, - ) - }) - .map(|(o, _)| o.clone()) - .collect(); + fn clone_pending_filtered(&self, f: F) -> PredicateObligations<'tcx> + where + F: FnMut(&&(PredicateObligation<'tcx>, Option>>)) -> bool, + { + let mut obligations: PredicateObligations<'tcx> = + self.pending.iter().filter(f).map(|(o, _)| o.clone()).collect(); obligations.extend(self.overflowed.iter().cloned()); obligations } @@ -310,7 +289,44 @@ where if infcx.tcx.disable_trait_solver_fast_paths() { return self.obligations.clone_pending(); } - self.obligations.clone_pending_potentially_referencing_sub_root(infcx, vid) + self.obligations.clone_pending_filtered(|(_, stalled_on)| { + let Some(stalled_on) = stalled_on else { return true }; + // Don't reuse the sub-unification roots cached on `stalled_on`: + // a later sub-unification merge can have changed which root + // each stalled var belongs to, so the cached info can be stale. + // Walk `stalled_vars` and recompute the current root instead. + // + // Conservative here: if a stalled var no longer resolves to an + // infer var, some unification happened, so the goal is no longer + // stalled. Include it to be re-evaluated downstream. + stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type()).any(|ty| { + match *infcx.shallow_resolve(ty).kind() { + ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid, + _ => true, + } + }) + }) + } + + fn pending_obligations_potentially_referencing_float_infer( + &self, + infcx: &InferCtxt<'tcx>, + ) -> PredicateObligations<'tcx> { + // `-Zdisable-fast-paths`: same gate as the other new-solver fast paths. + if infcx.tcx.disable_trait_solver_fast_paths() { + return self.obligations.clone_pending(); + } + + self.obligations.clone_pending_filtered(|(_, stalled_on)| { + let Some(stalled_on) = stalled_on else { return true }; + // If the stalled vars don't have float infers, the nested goals won't + // have them either. We only create float infers for user written literals. + stalled_on + .stalled_vars + .iter() + .filter_map(|arg| arg.as_type()) + .any(|ty| matches!(infcx.shallow_resolve(ty).kind(), ty::Infer(ty::FloatVar(_)))) + }) } fn drain_stalled_obligations_for_coroutines( diff --git a/tests/ui/traits/next-solver/float-fallback-hack-depth.rs b/tests/ui/traits/next-solver/float-fallback-hack-depth.rs new file mode 100644 index 0000000000000..34d8c73229636 --- /dev/null +++ b/tests/ui/traits/next-solver/float-fallback-hack-depth.rs @@ -0,0 +1,21 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Regression test for github.com/rust-lang/trait-system-refactor-initiative#280 +// We force unresolved float infer vars to fallback to `f32` if there're stalled `f32: From` +// obligations. +// Previously the recursion limit is 3 which is not enough, causing some bevy crates to fail. + +trait Trait {} +impl> Trait for T {} + +struct W(T); +impl Trait for W {} + +fn impls_trait(_: T) {} + +fn main() { + impls_trait(W(1.0)) + //~^ WARN: falling back to `f32` as the trait bound `f32: From` is not satisfied [float_literal_f32_fallback] + //~| WARN: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} diff --git a/tests/ui/traits/next-solver/float-fallback-hack-depth.stderr b/tests/ui/traits/next-solver/float-fallback-hack-depth.stderr new file mode 100644 index 0000000000000..e7222e5cb2852 --- /dev/null +++ b/tests/ui/traits/next-solver/float-fallback-hack-depth.stderr @@ -0,0 +1,12 @@ +warning: falling back to `f32` as the trait bound `f32: From` is not satisfied + --> $DIR/float-fallback-hack-depth.rs:18:19 + | +LL | impls_trait(W(1.0)) + | ^^^ help: explicitly specify the type as `f32`: `1.0_f32` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #154024 + = note: `#[warn(float_literal_f32_fallback)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: 1 warning emitted + From 1cf8cce06c8d8294b83d4b121c7e592b59de1ad1 Mon Sep 17 00:00:00 2001 From: Nia Deckers Date: Wed, 22 Jul 2026 06:20:22 +0200 Subject: [PATCH 7/8] split set_alloc_error_hook review damn tests --- library/alloctests/tests/c_str_alloc_error.rs | 6 ++- .../alloctests/tests/vec_deque_alloc_error.rs | 6 ++- library/std/src/alloc.rs | 37 ++++++++++++++++++- .../tests/panic/alloc_error_handler_hook.rs | 4 +- tests/ui/panics/alloc_error_hook-unwind.rs | 7 +++- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/library/alloctests/tests/c_str_alloc_error.rs b/library/alloctests/tests/c_str_alloc_error.rs index 669a783645baa..e7a340f7d2bbb 100644 --- a/library/alloctests/tests/c_str_alloc_error.rs +++ b/library/alloctests/tests/c_str_alloc_error.rs @@ -14,7 +14,7 @@ #![cfg(not(all(miri, windows)))] #![feature(alloc_error_hook)] -use std::alloc::{GlobalAlloc, Layout, System, set_alloc_error_hook}; +use std::alloc::{GlobalAlloc, Layout, System, set_alloc_error_hook_unwinding}; use std::ffi::CString; use std::panic::{AssertUnwindSafe, catch_unwind}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -52,7 +52,9 @@ static ALLOC: OneShotFailingAlloc = OneShotFailingAlloc; #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn clone_into_alloc_failure_leaves_target_valid() { - set_alloc_error_hook(|_| panic!("alloc error")); + unsafe { + set_alloc_error_hook_unwinding(|_| panic!("alloc error")); + } let src = CString::new("a fairly long value").unwrap(); let mut target = CString::new("x").unwrap(); diff --git a/library/alloctests/tests/vec_deque_alloc_error.rs b/library/alloctests/tests/vec_deque_alloc_error.rs index 21a9118a05bd6..bef53a5b7fdb0 100644 --- a/library/alloctests/tests/vec_deque_alloc_error.rs +++ b/library/alloctests/tests/vec_deque_alloc_error.rs @@ -1,6 +1,6 @@ #![feature(alloc_error_hook, allocator_api)] -use std::alloc::{AllocError, Allocator, Layout, System, set_alloc_error_hook}; +use std::alloc::{AllocError, Allocator, Layout, System, set_alloc_error_hook_unwinding}; use std::collections::VecDeque; use std::panic::{AssertUnwindSafe, catch_unwind}; use std::ptr::NonNull; @@ -36,7 +36,9 @@ fn test_shrink_to_unwind() { } } - set_alloc_error_hook(|_| panic!("alloc error")); + unsafe { + set_alloc_error_hook_unwinding(|_| panic!("alloc error")); + } let mut v = VecDeque::with_capacity_in(15, BadAlloc); v.push_back(1); diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 84447c06aaecf..6c8298508e3f8 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -296,6 +296,7 @@ unsafe impl Allocator for System { unsafe impl GlobalAllocator for System {} static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); +static ABORT: AtomicBool = AtomicBool::new(true); /// Registers a custom allocation error hook, replacing any that was previously registered. /// @@ -310,8 +311,9 @@ static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// The hook function is provided with a [`Layout`] struct which contains information /// about the allocation that failed. /// -/// The hook function may choose to panic or abort; in the event that it returns normally, this -/// will cause an immediate abort. +/// Regardless of whether the hook aborts, unwinds, or returns, the process will always abort +/// immediately after the hook is executed; an unwind will never propagate out of +/// `handle_alloc_error`. /// /// Since [`take_alloc_error_hook`] is a safe function that allows retrieving the hook, the hook /// function must be _sound_ to call even if no memory allocations were attempted. @@ -338,6 +340,29 @@ static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// ``` #[unstable(feature = "alloc_error_hook", issue = "51245")] pub fn set_alloc_error_hook(hook: fn(Layout)) { + ABORT.store(true, Ordering::Release); + HOOK.store(hook as *mut (), Ordering::Release); +} + +/// Registers a custom allocation error hook, replacing any that was previously registered. +/// +/// Unlike [`set_alloc_error_hook`], unwinds from the hook may propagate. +/// +/// # Safety +/// +/// Unwinding from the allocation error hook is not unsafe per se; however, large swathes of +/// library code assume this never happens and may cause UB. Therefore, upon setting a hook +/// that may unwind, care should be taken to audit code in dependencies for possible unsoundness +/// in the presence of unwinds and ensure UB is not triggered. +/// +/// Note that this has [also affected][example-1] the [standard library][example-2] in the past; +/// this operation is *immensely unsafe* to perform in the presence of unaudited code. +/// +/// [example-1]: https://github.com/rust-lang/rust/issues/157203 +/// [example-2]: https://github.com/rust-lang/rust/issues/156490 +#[unstable(feature = "alloc_error_hook", issue = "51245")] +pub unsafe fn set_alloc_error_hook_unwinding(hook: fn(Layout)) { + ABORT.store(false, Ordering::Release); HOOK.store(hook as *mut (), Ordering::Release); } @@ -423,6 +448,14 @@ fn default_alloc_error_hook(layout: Layout) { #[unstable(feature = "alloc_internals", issue = "none")] pub fn rust_oom(layout: Layout) -> ! { crate::sys::backtrace::__rust_end_short_backtrace(|| { + use core::mem::DropGuard; + + let guard = DropGuard::new((), |_| crate::process::abort()); + let abort = ABORT.load(Ordering::Acquire); + // Unwinds permitted, don't arm the guard. + if !abort { + DropGuard::dismiss(guard); + } let hook = HOOK.load(Ordering::Acquire); let hook: fn(Layout) = if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; diff --git a/src/tools/miri/tests/panic/alloc_error_handler_hook.rs b/src/tools/miri/tests/panic/alloc_error_handler_hook.rs index a1eadb45fd13b..da40624ecfb0e 100644 --- a/src/tools/miri/tests/panic/alloc_error_handler_hook.rs +++ b/src/tools/miri/tests/panic/alloc_error_handler_hook.rs @@ -12,7 +12,9 @@ impl Drop for Bomb { #[allow(unreachable_code, unused_variables)] fn main() { // This is a particularly tricky hook, since it unwinds, which the default one does not. - set_alloc_error_hook(|_layout| panic!("alloc error hook called")); + unsafe { + set_alloc_error_hook_unwinding(|_layout| panic!("alloc error hook called")); + } let bomb = Bomb; handle_alloc_error(Layout::for_value(&0)); diff --git a/tests/ui/panics/alloc_error_hook-unwind.rs b/tests/ui/panics/alloc_error_hook-unwind.rs index 8a107bc390d4a..ebabd34f16b73 100644 --- a/tests/ui/panics/alloc_error_hook-unwind.rs +++ b/tests/ui/panics/alloc_error_hook-unwind.rs @@ -1,4 +1,5 @@ -//! Test that out-of-memory conditions trigger catchable panics with `set_alloc_error_hook`. +//! Test that out-of-memory conditions trigger catchable panics +//! with `set_alloc_error_hook_unwinding`. //@ run-pass //@ needs-unwind @@ -12,7 +13,9 @@ use std::mem::forget; use std::panic::catch_unwind; fn main() { - std::alloc::set_alloc_error_hook(|_| panic!()); + unsafe { + std::alloc::set_alloc_error_hook_unwinding(|_| panic!()); + } let panic = catch_unwind(|| { // This is guaranteed to exceed even the size of the address space From 2f5a71d441a268c87883bd6edaeb95df6d4e69a3 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Wed, 22 Jul 2026 08:08:40 +0000 Subject: [PATCH 8/8] LLVM 24 updates (in llvm/llvm-project@bff9c544bd87) the diagnostic string for the RISC-V GPR register class in MC. As a result, invalid register operand references (such as x16..=x31 on riscv32e targets) now emit "register must be a GPR" instead of "invalid operand for instruction". To keep the test passing on both LLVM <=23 and LLVM >=24, split the target revisions into versioned pairs (e.g. riscv32e_llvm23 and riscv32e_llvm24) gated by `max-llvm-major-version: 23` and `min-llvm-version: 24`, respectively, with corresponding stderr references. This is admittedly a bit aggressive - I'm also fine if we want to just set a max-llvm-major-version and ignore it or preemptively bump it for 24 and set the min version instead of this complexity. --- ...riscv32e-registers.riscv32e_llvm23.stderr} | 32 +-- .../riscv32e-registers.riscv32e_llvm24.stderr | 194 ++++++++++++++++++ ...iscv32e-registers.riscv32em_llvm23.stderr} | 32 +-- ...riscv32e-registers.riscv32em_llvm24.stderr | 194 ++++++++++++++++++ ...scv32e-registers.riscv32emc_llvm23.stderr} | 32 +-- ...iscv32e-registers.riscv32emc_llvm24.stderr | 194 ++++++++++++++++++ tests/ui/asm/riscv/riscv32e-registers.rs | 78 ++++--- 7 files changed, 684 insertions(+), 72 deletions(-) rename tests/ui/asm/riscv/{riscv32e-registers.riscv32e.stderr => riscv32e-registers.riscv32e_llvm23.stderr} (86%) create mode 100644 tests/ui/asm/riscv/riscv32e-registers.riscv32e_llvm24.stderr rename tests/ui/asm/riscv/{riscv32e-registers.riscv32em.stderr => riscv32e-registers.riscv32em_llvm23.stderr} (86%) create mode 100644 tests/ui/asm/riscv/riscv32e-registers.riscv32em_llvm24.stderr rename tests/ui/asm/riscv/{riscv32e-registers.riscv32emc.stderr => riscv32e-registers.riscv32emc_llvm23.stderr} (86%) create mode 100644 tests/ui/asm/riscv/riscv32e-registers.riscv32emc_llvm24.stderr diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32e.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32e_llvm23.stderr similarity index 86% rename from tests/ui/asm/riscv/riscv32e-registers.riscv32e.stderr rename to tests/ui/asm/riscv/riscv32e-registers.riscv32e_llvm23.stderr index 4ae29b78b54aa..7a6bf6e5176d0 100644 --- a/tests/ui/asm/riscv/riscv32e-registers.riscv32e.stderr +++ b/tests/ui/asm/riscv/riscv32e-registers.riscv32e_llvm23.stderr @@ -1,5 +1,5 @@ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:43:11 + --> $DIR/riscv32e-registers.rs:57:11 | LL | asm!("li x16, 0"); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | li x16, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:46:11 + --> $DIR/riscv32e-registers.rs:61:11 | LL | asm!("li x17, 0"); | ^^^^^^^^^ @@ -23,7 +23,7 @@ LL | li x17, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:49:11 + --> $DIR/riscv32e-registers.rs:65:11 | LL | asm!("li x18, 0"); | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | li x18, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:52:11 + --> $DIR/riscv32e-registers.rs:69:11 | LL | asm!("li x19, 0"); | ^^^^^^^^^ @@ -47,7 +47,7 @@ LL | li x19, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:55:11 + --> $DIR/riscv32e-registers.rs:73:11 | LL | asm!("li x20, 0"); | ^^^^^^^^^ @@ -59,7 +59,7 @@ LL | li x20, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:58:11 + --> $DIR/riscv32e-registers.rs:77:11 | LL | asm!("li x21, 0"); | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | li x21, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:61:11 + --> $DIR/riscv32e-registers.rs:81:11 | LL | asm!("li x22, 0"); | ^^^^^^^^^ @@ -83,7 +83,7 @@ LL | li x22, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:64:11 + --> $DIR/riscv32e-registers.rs:85:11 | LL | asm!("li x23, 0"); | ^^^^^^^^^ @@ -95,7 +95,7 @@ LL | li x23, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:67:11 + --> $DIR/riscv32e-registers.rs:89:11 | LL | asm!("li x24, 0"); | ^^^^^^^^^ @@ -107,7 +107,7 @@ LL | li x24, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:70:11 + --> $DIR/riscv32e-registers.rs:93:11 | LL | asm!("li x25, 0"); | ^^^^^^^^^ @@ -119,7 +119,7 @@ LL | li x25, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:73:11 + --> $DIR/riscv32e-registers.rs:97:11 | LL | asm!("li x26, 0"); | ^^^^^^^^^ @@ -131,7 +131,7 @@ LL | li x26, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:76:11 + --> $DIR/riscv32e-registers.rs:101:11 | LL | asm!("li x27, 0"); | ^^^^^^^^^ @@ -143,7 +143,7 @@ LL | li x27, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:79:11 + --> $DIR/riscv32e-registers.rs:105:11 | LL | asm!("li x28, 0"); | ^^^^^^^^^ @@ -155,7 +155,7 @@ LL | li x28, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:82:11 + --> $DIR/riscv32e-registers.rs:109:11 | LL | asm!("li x29, 0"); | ^^^^^^^^^ @@ -167,7 +167,7 @@ LL | li x29, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:85:11 + --> $DIR/riscv32e-registers.rs:113:11 | LL | asm!("li x30, 0"); | ^^^^^^^^^ @@ -179,7 +179,7 @@ LL | li x30, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:88:11 + --> $DIR/riscv32e-registers.rs:117:11 | LL | asm!("li x31, 0"); | ^^^^^^^^^ diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32e_llvm24.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32e_llvm24.stderr new file mode 100644 index 0000000000000..94625fabf6750 --- /dev/null +++ b/tests/ui/asm/riscv/riscv32e-registers.riscv32e_llvm24.stderr @@ -0,0 +1,194 @@ +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:57:11 + | +LL | asm!("li x16, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x16, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:61:11 + | +LL | asm!("li x17, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x17, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:65:11 + | +LL | asm!("li x18, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x18, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:69:11 + | +LL | asm!("li x19, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x19, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:73:11 + | +LL | asm!("li x20, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x20, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:77:11 + | +LL | asm!("li x21, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x21, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:81:11 + | +LL | asm!("li x22, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x22, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:85:11 + | +LL | asm!("li x23, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x23, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:89:11 + | +LL | asm!("li x24, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x24, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:93:11 + | +LL | asm!("li x25, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x25, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:97:11 + | +LL | asm!("li x26, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x26, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:101:11 + | +LL | asm!("li x27, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x27, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:105:11 + | +LL | asm!("li x28, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x28, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:109:11 + | +LL | asm!("li x29, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x29, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:113:11 + | +LL | asm!("li x30, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x30, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:117:11 + | +LL | asm!("li x31, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x31, 0 + | ^ + +error: aborting due to 16 previous errors + diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32em.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32em_llvm23.stderr similarity index 86% rename from tests/ui/asm/riscv/riscv32e-registers.riscv32em.stderr rename to tests/ui/asm/riscv/riscv32e-registers.riscv32em_llvm23.stderr index 4ae29b78b54aa..7a6bf6e5176d0 100644 --- a/tests/ui/asm/riscv/riscv32e-registers.riscv32em.stderr +++ b/tests/ui/asm/riscv/riscv32e-registers.riscv32em_llvm23.stderr @@ -1,5 +1,5 @@ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:43:11 + --> $DIR/riscv32e-registers.rs:57:11 | LL | asm!("li x16, 0"); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | li x16, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:46:11 + --> $DIR/riscv32e-registers.rs:61:11 | LL | asm!("li x17, 0"); | ^^^^^^^^^ @@ -23,7 +23,7 @@ LL | li x17, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:49:11 + --> $DIR/riscv32e-registers.rs:65:11 | LL | asm!("li x18, 0"); | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | li x18, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:52:11 + --> $DIR/riscv32e-registers.rs:69:11 | LL | asm!("li x19, 0"); | ^^^^^^^^^ @@ -47,7 +47,7 @@ LL | li x19, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:55:11 + --> $DIR/riscv32e-registers.rs:73:11 | LL | asm!("li x20, 0"); | ^^^^^^^^^ @@ -59,7 +59,7 @@ LL | li x20, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:58:11 + --> $DIR/riscv32e-registers.rs:77:11 | LL | asm!("li x21, 0"); | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | li x21, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:61:11 + --> $DIR/riscv32e-registers.rs:81:11 | LL | asm!("li x22, 0"); | ^^^^^^^^^ @@ -83,7 +83,7 @@ LL | li x22, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:64:11 + --> $DIR/riscv32e-registers.rs:85:11 | LL | asm!("li x23, 0"); | ^^^^^^^^^ @@ -95,7 +95,7 @@ LL | li x23, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:67:11 + --> $DIR/riscv32e-registers.rs:89:11 | LL | asm!("li x24, 0"); | ^^^^^^^^^ @@ -107,7 +107,7 @@ LL | li x24, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:70:11 + --> $DIR/riscv32e-registers.rs:93:11 | LL | asm!("li x25, 0"); | ^^^^^^^^^ @@ -119,7 +119,7 @@ LL | li x25, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:73:11 + --> $DIR/riscv32e-registers.rs:97:11 | LL | asm!("li x26, 0"); | ^^^^^^^^^ @@ -131,7 +131,7 @@ LL | li x26, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:76:11 + --> $DIR/riscv32e-registers.rs:101:11 | LL | asm!("li x27, 0"); | ^^^^^^^^^ @@ -143,7 +143,7 @@ LL | li x27, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:79:11 + --> $DIR/riscv32e-registers.rs:105:11 | LL | asm!("li x28, 0"); | ^^^^^^^^^ @@ -155,7 +155,7 @@ LL | li x28, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:82:11 + --> $DIR/riscv32e-registers.rs:109:11 | LL | asm!("li x29, 0"); | ^^^^^^^^^ @@ -167,7 +167,7 @@ LL | li x29, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:85:11 + --> $DIR/riscv32e-registers.rs:113:11 | LL | asm!("li x30, 0"); | ^^^^^^^^^ @@ -179,7 +179,7 @@ LL | li x30, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:88:11 + --> $DIR/riscv32e-registers.rs:117:11 | LL | asm!("li x31, 0"); | ^^^^^^^^^ diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32em_llvm24.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32em_llvm24.stderr new file mode 100644 index 0000000000000..94625fabf6750 --- /dev/null +++ b/tests/ui/asm/riscv/riscv32e-registers.riscv32em_llvm24.stderr @@ -0,0 +1,194 @@ +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:57:11 + | +LL | asm!("li x16, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x16, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:61:11 + | +LL | asm!("li x17, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x17, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:65:11 + | +LL | asm!("li x18, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x18, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:69:11 + | +LL | asm!("li x19, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x19, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:73:11 + | +LL | asm!("li x20, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x20, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:77:11 + | +LL | asm!("li x21, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x21, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:81:11 + | +LL | asm!("li x22, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x22, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:85:11 + | +LL | asm!("li x23, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x23, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:89:11 + | +LL | asm!("li x24, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x24, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:93:11 + | +LL | asm!("li x25, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x25, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:97:11 + | +LL | asm!("li x26, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x26, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:101:11 + | +LL | asm!("li x27, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x27, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:105:11 + | +LL | asm!("li x28, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x28, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:109:11 + | +LL | asm!("li x29, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x29, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:113:11 + | +LL | asm!("li x30, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x30, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:117:11 + | +LL | asm!("li x31, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x31, 0 + | ^ + +error: aborting due to 16 previous errors + diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32emc.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32emc_llvm23.stderr similarity index 86% rename from tests/ui/asm/riscv/riscv32e-registers.riscv32emc.stderr rename to tests/ui/asm/riscv/riscv32e-registers.riscv32emc_llvm23.stderr index 4ae29b78b54aa..7a6bf6e5176d0 100644 --- a/tests/ui/asm/riscv/riscv32e-registers.riscv32emc.stderr +++ b/tests/ui/asm/riscv/riscv32e-registers.riscv32emc_llvm23.stderr @@ -1,5 +1,5 @@ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:43:11 + --> $DIR/riscv32e-registers.rs:57:11 | LL | asm!("li x16, 0"); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | li x16, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:46:11 + --> $DIR/riscv32e-registers.rs:61:11 | LL | asm!("li x17, 0"); | ^^^^^^^^^ @@ -23,7 +23,7 @@ LL | li x17, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:49:11 + --> $DIR/riscv32e-registers.rs:65:11 | LL | asm!("li x18, 0"); | ^^^^^^^^^ @@ -35,7 +35,7 @@ LL | li x18, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:52:11 + --> $DIR/riscv32e-registers.rs:69:11 | LL | asm!("li x19, 0"); | ^^^^^^^^^ @@ -47,7 +47,7 @@ LL | li x19, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:55:11 + --> $DIR/riscv32e-registers.rs:73:11 | LL | asm!("li x20, 0"); | ^^^^^^^^^ @@ -59,7 +59,7 @@ LL | li x20, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:58:11 + --> $DIR/riscv32e-registers.rs:77:11 | LL | asm!("li x21, 0"); | ^^^^^^^^^ @@ -71,7 +71,7 @@ LL | li x21, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:61:11 + --> $DIR/riscv32e-registers.rs:81:11 | LL | asm!("li x22, 0"); | ^^^^^^^^^ @@ -83,7 +83,7 @@ LL | li x22, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:64:11 + --> $DIR/riscv32e-registers.rs:85:11 | LL | asm!("li x23, 0"); | ^^^^^^^^^ @@ -95,7 +95,7 @@ LL | li x23, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:67:11 + --> $DIR/riscv32e-registers.rs:89:11 | LL | asm!("li x24, 0"); | ^^^^^^^^^ @@ -107,7 +107,7 @@ LL | li x24, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:70:11 + --> $DIR/riscv32e-registers.rs:93:11 | LL | asm!("li x25, 0"); | ^^^^^^^^^ @@ -119,7 +119,7 @@ LL | li x25, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:73:11 + --> $DIR/riscv32e-registers.rs:97:11 | LL | asm!("li x26, 0"); | ^^^^^^^^^ @@ -131,7 +131,7 @@ LL | li x26, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:76:11 + --> $DIR/riscv32e-registers.rs:101:11 | LL | asm!("li x27, 0"); | ^^^^^^^^^ @@ -143,7 +143,7 @@ LL | li x27, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:79:11 + --> $DIR/riscv32e-registers.rs:105:11 | LL | asm!("li x28, 0"); | ^^^^^^^^^ @@ -155,7 +155,7 @@ LL | li x28, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:82:11 + --> $DIR/riscv32e-registers.rs:109:11 | LL | asm!("li x29, 0"); | ^^^^^^^^^ @@ -167,7 +167,7 @@ LL | li x29, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:85:11 + --> $DIR/riscv32e-registers.rs:113:11 | LL | asm!("li x30, 0"); | ^^^^^^^^^ @@ -179,7 +179,7 @@ LL | li x30, 0 | ^ error: invalid operand for instruction - --> $DIR/riscv32e-registers.rs:88:11 + --> $DIR/riscv32e-registers.rs:117:11 | LL | asm!("li x31, 0"); | ^^^^^^^^^ diff --git a/tests/ui/asm/riscv/riscv32e-registers.riscv32emc_llvm24.stderr b/tests/ui/asm/riscv/riscv32e-registers.riscv32emc_llvm24.stderr new file mode 100644 index 0000000000000..94625fabf6750 --- /dev/null +++ b/tests/ui/asm/riscv/riscv32e-registers.riscv32emc_llvm24.stderr @@ -0,0 +1,194 @@ +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:57:11 + | +LL | asm!("li x16, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x16, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:61:11 + | +LL | asm!("li x17, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x17, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:65:11 + | +LL | asm!("li x18, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x18, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:69:11 + | +LL | asm!("li x19, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x19, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:73:11 + | +LL | asm!("li x20, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x20, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:77:11 + | +LL | asm!("li x21, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x21, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:81:11 + | +LL | asm!("li x22, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x22, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:85:11 + | +LL | asm!("li x23, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x23, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:89:11 + | +LL | asm!("li x24, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x24, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:93:11 + | +LL | asm!("li x25, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x25, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:97:11 + | +LL | asm!("li x26, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x26, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:101:11 + | +LL | asm!("li x27, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x27, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:105:11 + | +LL | asm!("li x28, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x28, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:109:11 + | +LL | asm!("li x29, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x29, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:113:11 + | +LL | asm!("li x30, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x30, 0 + | ^ + +error: register must be a GPR + --> $DIR/riscv32e-registers.rs:117:11 + | +LL | asm!("li x31, 0"); + | ^^^^^^^^^ + | +note: instantiated into assembly here + --> :1:5 + | +LL | li x31, 0 + | ^ + +error: aborting due to 16 previous errors + diff --git a/tests/ui/asm/riscv/riscv32e-registers.rs b/tests/ui/asm/riscv/riscv32e-registers.rs index 70231edddbc62..a5f4151b2c80a 100644 --- a/tests/ui/asm/riscv/riscv32e-registers.rs +++ b/tests/ui/asm/riscv/riscv32e-registers.rs @@ -2,15 +2,29 @@ // //@ add-minicore //@ build-fail -//@ revisions: riscv32e riscv32em riscv32emc -// +//@ revisions: riscv32e_llvm23 riscv32em_llvm23 riscv32emc_llvm23 +//@ revisions: riscv32e_llvm24 riscv32em_llvm24 riscv32emc_llvm24 //@ compile-flags: --crate-type=rlib -//@ [riscv32e] needs-llvm-components: riscv -//@ [riscv32e] compile-flags: --target=riscv32e-unknown-none-elf -//@ [riscv32em] needs-llvm-components: riscv -//@ [riscv32em] compile-flags: --target=riscv32em-unknown-none-elf -//@ [riscv32emc] needs-llvm-components: riscv -//@ [riscv32emc] compile-flags: --target=riscv32emc-unknown-none-elf +//@ [riscv32e_llvm23] needs-llvm-components: riscv +//@ [riscv32e_llvm23] compile-flags: --target=riscv32e-unknown-none-elf +//@ [riscv32e_llvm23] max-llvm-major-version: 23 +//@ [riscv32e_llvm24] needs-llvm-components: riscv +//@ [riscv32e_llvm24] compile-flags: --target=riscv32e-unknown-none-elf +//@ [riscv32e_llvm24] min-llvm-version: 24 + +//@ [riscv32em_llvm23] needs-llvm-components: riscv +//@ [riscv32em_llvm23] compile-flags: --target=riscv32em-unknown-none-elf +//@ [riscv32em_llvm23] max-llvm-major-version: 23 +//@ [riscv32em_llvm24] needs-llvm-components: riscv +//@ [riscv32em_llvm24] compile-flags: --target=riscv32em-unknown-none-elf +//@ [riscv32em_llvm24] min-llvm-version: 24 + +//@ [riscv32emc_llvm23] needs-llvm-components: riscv +//@ [riscv32emc_llvm23] compile-flags: --target=riscv32emc-unknown-none-elf +//@ [riscv32emc_llvm23] max-llvm-major-version: 23 +//@ [riscv32emc_llvm24] needs-llvm-components: riscv +//@ [riscv32emc_llvm24] compile-flags: --target=riscv32emc-unknown-none-elf +//@ [riscv32emc_llvm24] min-llvm-version: 24 //@ ignore-backends: gcc // Unlike bad-reg.rs, this tests if the assembler can reject invalid registers @@ -41,51 +55,67 @@ pub unsafe fn registers() { asm!("li x14, 0"); asm!("li x15, 0"); asm!("li x16, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x17, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x18, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x19, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x20, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x21, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x22, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x23, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x24, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x25, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x26, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x27, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x28, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x29, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x30, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here asm!("li x31, 0"); - //~^ ERROR invalid operand for instruction + //[riscv32e_llvm23,riscv32em_llvm23,riscv32emc_llvm23]~^ ERROR invalid operand for instruction + //[riscv32e_llvm24,riscv32em_llvm24,riscv32emc_llvm24]~^^ ERROR register must be a GPR //~| NOTE instantiated into assembly here }