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_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index 3c56999fcbdc9..63b2adceb524c 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -195,28 +195,20 @@ 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); - 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 39df33187d3a9..68c8e03de8022 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -1,5 +1,3 @@ -use std::ops::RangeInclusive; - use rustc_middle::bug; use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges}; @@ -10,6 +8,13 @@ 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 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, @@ -21,19 +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>; - /// 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`. @@ -53,6 +45,18 @@ 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()) + } + + /// 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>, @@ -128,83 +132,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!(!to.precedes_in_backward_order(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, @@ -214,8 +141,6 @@ impl Direction for Backward { ) where A: Analysis<'tcx>, { - vis.visit_block_end(state); - let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); analysis.apply_early_terminator_effect(state, term, loc); @@ -230,8 +155,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); } } @@ -241,6 +164,18 @@ 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) + } + + /// 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>, @@ -310,80 +245,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!(!to.precedes_in_forward_order(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, @@ -393,8 +254,6 @@ impl Direction for Forward { ) where A: Analysis<'tcx>, { - vis.visit_block_start(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); @@ -409,7 +268,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..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,18 +660,6 @@ 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_after_early_statement_effect( &mut self, analysis: &A, diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 6445ba7ad27b6..211a1b8d05b71 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -32,13 +32,13 @@ //! //! [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}; 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; @@ -125,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. /// @@ -402,41 +436,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); } } } diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 46940c6ab62fc..b6827be3d8beb 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -46,8 +46,6 @@ pub trait ResultsVisitor<'tcx, A> where A: Analysis<'tcx>, { - fn visit_block_start(&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, @@ -89,6 +87,4 @@ where _location: Location, ) { } - - fn visit_block_end(&mut self, _state: &A::Domain) {} } 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/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/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 } 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 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 +