diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 6372c66050e7c..a3c6e4c76d9ff 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -44,6 +44,8 @@ pub mod token; pub mod tokenstream; pub mod visit; +pub use rustc_ast_ir::Limit; + pub use self::ast::*; pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasTokens}; diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index ff9d940ce9f28..a40c4dc63f8a9 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -5,6 +5,9 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end +use std::fmt; +use std::ops::{Div, Mul}; + #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; @@ -86,3 +89,51 @@ pub enum Pinnedness { Not, Pinned, } + +/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against +/// limits are consistent throughout the compiler. +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] +pub struct Limit(pub usize); + +impl Limit { + /// Create a new limit from a `usize`. + pub fn new(value: usize) -> Self { + Limit(value) + } + + /// Check that `value` is within the limit. Ensures that the same comparisons are used + /// throughout the compiler, as mismatches can cause ICEs, see #72540. + #[inline] + pub fn value_within_limit(&self, value: usize) -> bool { + value <= self.0 + } +} + +impl From for Limit { + fn from(value: usize) -> Self { + Self::new(value) + } +} + +impl fmt::Display for Limit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Div for Limit { + type Output = Limit; + + fn div(self, rhs: usize) -> Self::Output { + Limit::new(self.0 / rhs) + } +} + +impl Mul for Limit { + type Output = Limit; + + fn mul(self, rhs: usize) -> Self::Output { + Limit::new(self.0 * rhs) + } +} diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index d179396398f01..781ee95ef5e06 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf}; use std::process::ExitStatus; use rustc_abi::TargetDataLayoutErrors; +use rustc_ast::Limit; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast_pretty::pprust; use rustc_macros::Subdiagnostic; @@ -190,6 +191,12 @@ impl IntoDiagArg for PathBuf { } } +impl IntoDiagArg for Limit { + fn into_diag_arg(self) -> rustc_errors::DiagArgValue { + self.to_string().into_diag_arg() + } +} + impl IntoDiagArg for PanicStrategy { fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(self.desc().to_string())) diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index cad7b2a1e57b9..072ec63cd7a81 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -42,6 +42,7 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; +use rustc_type_ir::visit::collect_referenced_late_bound_regions; use tracing::{debug, instrument}; use crate::check::intrinsic::intrinsic_operation_unsafety; @@ -635,11 +636,10 @@ fn get_new_lifetime_name<'tcx>( poly_trait_ref: ty::PolyTraitRef<'tcx>, generics: &hir::Generics<'tcx>, ) -> String { - let existing_lifetimes = tcx - .collect_referenced_late_bound_regions(poly_trait_ref) + let existing_lifetimes = collect_referenced_late_bound_regions(tcx, poly_trait_ref) .into_iter() .filter_map(|lt| { - if let ty::BoundRegionKind::Named(_, name) = lt { + if let ty::BoundRegionKind::Named(_, name) = lt.kind { Some(name.as_str().to_string()) } else { None diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 6a9ae0de1c1ab..2a384e57b4ba6 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -3,7 +3,7 @@ use rustc_middle::bug; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitor}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; -use rustc_type_ir::fold::TypeFoldable; +use rustc_type_ir::fold::{TypeFoldable, expand_weak_alias_tys}; use tracing::debug; #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -51,7 +51,7 @@ pub(crate) fn parameters_for<'tcx>( include_nonconstraining: bool, ) -> Vec { let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining }; - let value = if !include_nonconstraining { tcx.expand_weak_alias_tys(value) } else { value }; + let value = if !include_nonconstraining { expand_weak_alias_tys(tcx, value) } else { value }; value.visit_with(&mut collector); collector.parameters } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index e949d4a11261e..65e23b978db8a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -11,7 +11,10 @@ use rustc_middle::bug; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::traits; -use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; +use rustc_type_ir::visit::{ + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + collect_constrained_late_bound_regions, collect_referenced_late_bound_regions, +}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -358,9 +361,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // for<'a> ::Item = &'a str // <-- 'a is bad // for<'a> >::Output = &'a str // <-- 'a is ok let late_bound_in_projection_ty = - tcx.collect_constrained_late_bound_regions(projection_term); + collect_constrained_late_bound_regions(tcx, projection_term); let late_bound_in_term = - tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term)); + collect_referenced_late_bound_regions(tcx, trait_ref.rebind(term)); debug!(?late_bound_in_projection_ty); debug!(?late_bound_in_term); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index e59ff02642cf1..762e20f9f866f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -13,6 +13,9 @@ use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations}; use rustc_type_ir::elaborate::ClauseWithSupertraitSpan; +use rustc_type_ir::visit::{ + collect_constrained_late_bound_regions, collect_referenced_late_bound_regions, +}; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -362,10 +365,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // // for<'a> ::Item = &'a str // <-- 'a is bad // for<'a> >::Output = &'a str // <-- 'a is ok - let late_bound_in_projection_term = - tcx.collect_constrained_late_bound_regions(pred.map_bound(|pred| pred.projection_term)); + let late_bound_in_projection_term = collect_constrained_late_bound_regions( + tcx, + pred.map_bound(|pred| pred.projection_term), + ); let late_bound_in_term = - tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term)); + collect_referenced_late_bound_regions(tcx, pred.map_bound(|pred| pred.term)); debug!(?late_bound_in_projection_term); debug!(?late_bound_in_term); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 61d5869c19f15..46c3189cc9bf9 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -49,6 +49,9 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; +use rustc_type_ir::visit::{ + collect_constrained_late_bound_regions, collect_referenced_late_bound_regions, +}; use tracing::{debug, instrument}; use crate::bounds::Bounds; @@ -2606,9 +2609,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // for<'a> fn(&'a String) -> &'a str <-- 'a is ok let inputs = bare_fn_ty.inputs(); let late_bound_in_args = - tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned())); + collect_constrained_late_bound_regions(tcx, inputs.map_bound(|i| i.to_owned())); let output = bare_fn_ty.output(); - let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output); + let late_bound_in_ret = collect_referenced_late_bound_regions(tcx, output); self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| { struct_span_code_err!( @@ -2665,12 +2668,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { #[instrument(level = "trace", skip(self, generate_err))] fn validate_late_bound_regions<'cx>( &'cx self, - constrained_regions: FxIndexSet, - referenced_regions: FxIndexSet, + constrained_regions: FxIndexSet, + referenced_regions: FxIndexSet, generate_err: impl Fn(&str) -> Diag<'cx>, ) { for br in referenced_regions.difference(&constrained_regions) { - let br_name = match *br { + let br_name = match br.kind { ty::BoundRegionKind::Named(_, kw::UnderscoreLifetime) | ty::BoundRegionKind::Anon | ty::BoundRegionKind::ClosureEnv => "an anonymous lifetime".to_string(), @@ -2680,7 +2683,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut err = generate_err(&br_name); if let ty::BoundRegionKind::Named(_, kw::UnderscoreLifetime) - | ty::BoundRegionKind::Anon = *br + | ty::BoundRegionKind::Anon = br.kind { // The only way for an anonymous lifetime to wind up // in the return type but **also** be unconstrained is diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 0c22c056dab5f..317986b3cbcd6 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -305,8 +305,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.parent(def_id) } - fn recursion_limit(self) -> usize { - self.recursion_limit().0 + fn recursion_limit(self) -> Limit { + self.recursion_limit() } type Features = &'tcx rustc_feature::Features; diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 7d5e5c2e82370..e8766342c0051 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -6,7 +6,6 @@ use rustc_abi::{ExternAbi, Float, Integer, IntegerType, Size}; use rustc_apfloat::Float as _; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; -use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -895,30 +894,6 @@ impl<'tcx> TyCtxt<'tcx> { || self.extern_crate(key).is_some_and(|e| e.is_direct()) } - /// Expand any [weak alias types][weak] contained within the given `value`. - /// - /// This should be used over other normalization routines in situations where - /// it's important not to normalize other alias types and where the predicates - /// on the corresponding type alias shouldn't be taken into consideration. - /// - /// Whenever possible **prefer not to use this function**! Instead, use standard - /// normalization routines or if feasible don't normalize at all. - /// - /// This function comes in handy if you want to mimic the behavior of eager - /// type alias expansion in a localized manner. - /// - ///
- /// This delays a bug on overflow! Therefore you need to be certain that the - /// contained types get fully normalized at a later stage. Note that even on - /// overflow all well-behaved weak alias types get expanded correctly, so the - /// result is still useful. - ///
- /// - /// [weak]: ty::Weak - pub fn expand_weak_alias_tys>>(self, value: T) -> T { - value.fold_with(&mut WeakAliasTypeExpander { tcx: self, depth: 0 }) - } - /// Peel off all [weak alias types] in this type until there are none left. /// /// This only expands weak alias types in “head” / outermost positions. It can @@ -1088,42 +1063,6 @@ impl<'tcx> TypeFolder> for OpaqueTypeExpander<'tcx> { } } -struct WeakAliasTypeExpander<'tcx> { - tcx: TyCtxt<'tcx>, - depth: usize, -} - -impl<'tcx> TypeFolder> for WeakAliasTypeExpander<'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !ty.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) { - return ty; - } - let ty::Alias(ty::Weak, alias) = ty.kind() else { - return ty.super_fold_with(self); - }; - if !self.tcx.recursion_limit().value_within_limit(self.depth) { - let guar = self.tcx.dcx().delayed_bug("overflow expanding weak alias type"); - return Ty::new_error(self.tcx, guar); - } - - self.depth += 1; - ensure_sufficient_stack(|| { - self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).fold_with(self) - }) - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - if !ct.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) { - return ct; - } - ct.super_fold_with(self) - } -} - /// Indicates the form of `AsyncDestruct::Destructor`. Used to simplify async /// drop glue for types not using async drop. #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 4efaccefcf74b..e8951ae14097a 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -1,7 +1,5 @@ use std::ops::ControlFlow; -use rustc_data_structures::fx::FxIndexSet; -use rustc_type_ir::fold::TypeFoldable; pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags}; @@ -102,114 +100,6 @@ impl<'tcx> TyCtxt<'tcx> { value.visit_with(&mut RegionVisitor { outer_index: ty::INNERMOST, callback }).is_break() } - - /// Returns a set of all late-bound regions that are constrained - /// by `value`, meaning that if we instantiate those LBR with - /// variables and equate `value` with something else, those - /// variables will also be equated. - pub fn collect_constrained_late_bound_regions( - self, - value: Binder<'tcx, T>, - ) -> FxIndexSet - where - T: TypeFoldable>, - { - self.collect_late_bound_regions(value, true) - } - - /// Returns a set of all late-bound regions that appear in `value` anywhere. - pub fn collect_referenced_late_bound_regions( - self, - value: Binder<'tcx, T>, - ) -> FxIndexSet - where - T: TypeFoldable>, - { - self.collect_late_bound_regions(value, false) - } - - fn collect_late_bound_regions( - self, - value: Binder<'tcx, T>, - just_constrained: bool, - ) -> FxIndexSet - where - T: TypeFoldable>, - { - let mut collector = LateBoundRegionsCollector::new(just_constrained); - let value = value.skip_binder(); - let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value }; - value.visit_with(&mut collector); - collector.regions - } -} - -/// Collects all the late-bound regions at the innermost binding level -/// into a hash set. -struct LateBoundRegionsCollector { - current_index: ty::DebruijnIndex, - regions: FxIndexSet, - - /// `true` if we only want regions that are known to be - /// "constrained" when you equate this type with another type. In - /// particular, if you have e.g., `&'a u32` and `&'b u32`, equating - /// them constraints `'a == 'b`. But if you have `<&'a u32 as - /// Trait>::Foo` and `<&'b u32 as Trait>::Foo`, normalizing those - /// types may mean that `'a` and `'b` don't appear in the results, - /// so they are not considered *constrained*. - just_constrained: bool, -} - -impl LateBoundRegionsCollector { - fn new(just_constrained: bool) -> Self { - Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained } - } -} - -impl<'tcx> TypeVisitor> for LateBoundRegionsCollector { - fn visit_binder>>(&mut self, t: &Binder<'tcx, T>) { - self.current_index.shift_in(1); - t.super_visit_with(self); - self.current_index.shift_out(1); - } - - fn visit_ty(&mut self, t: Ty<'tcx>) { - if self.just_constrained { - match t.kind() { - // If we are only looking for "constrained" regions, we have to ignore the - // inputs to a projection as they may not appear in the normalized form. - ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => { - return; - } - // All weak alias types should've been expanded beforehand. - ty::Alias(ty::Weak, _) => bug!("unexpected weak alias type"), - _ => {} - } - } - - t.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) { - // if we are only looking for "constrained" region, we have to - // ignore the inputs of an unevaluated const, as they may not appear - // in the normalized form - if self.just_constrained { - if let ty::ConstKind::Unevaluated(..) = c.kind() { - return; - } - } - - c.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) { - if let ty::ReBound(debruijn, br) = *r { - if debruijn == self.current_index { - self.regions.insert(br.kind); - } - } - } } /// Finds the max universe present diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index e99cd3d272700..4ec16d6ddcf3c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -168,7 +168,7 @@ where if !self.is_normalizes_to_goal { let num_non_region_vars = canonical.variables.iter().filter(|c| !c.is_region() && c.is_existential()).count(); - if num_non_region_vars > self.cx().recursion_limit() { + if !self.cx().recursion_limit().value_within_limit(num_non_region_vars) { debug!(?num_non_region_vars, "too many inference variables -> overflow"); return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { suggest_increasing_limit: true, diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 91ad24bff6743..b5fedeae0c4bc 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1,6 +1,7 @@ use std::ops::ControlFlow; use derive_where::derive_where; +use rustc_ast_ir::Limit; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack}; @@ -148,7 +149,7 @@ pub trait SolverDelegateEvalExt: SolverDelegate { /// in coherence checking. fn root_goal_may_hold_with_depth( &self, - root_depth: usize, + root_depth: Limit, goal: Goal::Predicate>, ) -> bool; @@ -182,7 +183,7 @@ where fn root_goal_may_hold_with_depth( &self, - root_depth: usize, + root_depth: Limit, goal: Goal::Predicate>, ) -> bool { self.probe(|| { @@ -227,7 +228,7 @@ where /// over using this manually (such as [`SolverDelegateEvalExt::evaluate_root_goal`]). pub(super) fn enter_root( delegate: &D, - root_depth: usize, + root_depth: Limit, generate_proof_tree: GenerateProofTree, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> R, ) -> (R, Option>) { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 1f03de3f53db8..130ba45d259ff 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,11 +1,11 @@ use std::any::Any; -use std::ops::{Div, Mul}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; use std::sync::atomic::AtomicBool; -use std::{env, fmt, io}; +use std::{env, io}; +pub use rustc_ast::Limit; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; @@ -58,59 +58,6 @@ pub enum CtfeBacktrace { Immediate, } -/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against -/// limits are consistent throughout the compiler. -#[derive(Clone, Copy, Debug, HashStable_Generic)] -pub struct Limit(pub usize); - -impl Limit { - /// Create a new limit from a `usize`. - pub fn new(value: usize) -> Self { - Limit(value) - } - - /// Check that `value` is within the limit. Ensures that the same comparisons are used - /// throughout the compiler, as mismatches can cause ICEs, see #72540. - #[inline] - pub fn value_within_limit(&self, value: usize) -> bool { - value <= self.0 - } -} - -impl From for Limit { - fn from(value: usize) -> Self { - Self::new(value) - } -} - -impl fmt::Display for Limit { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl Div for Limit { - type Output = Limit; - - fn div(self, rhs: usize) -> Self::Output { - Limit::new(self.0 / rhs) - } -} - -impl Mul for Limit { - type Output = Limit; - - fn mul(self, rhs: usize) -> Self::Output { - Limit::new(self.0 * rhs) - } -} - -impl rustc_errors::IntoDiagArg for Limit { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { - self.to_string().into_diag_arg() - } -} - #[derive(Clone, Copy, Debug, HashStable_Generic)] pub struct Limits { /// The maximum recursion limit for potentially infinitely recursive diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs index 445937ad16928..ce8e56841fa09 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs @@ -6,6 +6,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; +use rustc_type_ir::visit::collect_referenced_late_bound_regions; use tracing::instrument; use crate::error_reporting::infer::nice_region_error::NiceRegionError; @@ -142,10 +143,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ty: Binder<'tcx, impl TypeFoldable>>, region_def_id: DefId, ) -> bool { - let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty); - // We are only checking is any region meets the condition so order doesn't matter - #[allow(rustc::potential_query_instability)] - late_bound_regions.iter().any(|r| match *r { + collect_referenced_late_bound_regions(self.tcx(), ty).iter().any(|r| match r.kind { ty::BoundRegionKind::Named(def_id, _) => def_id == region_def_id, _ => false, }) diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 50d47d20e1a4e..744ebdf276602 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -6,6 +6,7 @@ use std::fmt::Debug; +use rustc_ast_ir::Limit; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir::def::DefKind; @@ -350,8 +351,10 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>( // a very low recursion depth and bail if any of them don't // hold. if !obligations.iter().all(|o| { - <&SolverDelegate<'tcx>>::from(infcx) - .root_goal_may_hold_with_depth(8, Goal::new(infcx.tcx, o.param_env, o.predicate)) + <&SolverDelegate<'tcx>>::from(infcx).root_goal_may_hold_with_depth( + Limit(8), + Goal::new(infcx.tcx, o.param_env, o.predicate), + ) }) { return IntersectionHasImpossibleObligations::Yes; } diff --git a/compiler/rustc_type_ir/src/data_structures/mod.rs b/compiler/rustc_type_ir/src/data_structures/mod.rs index d9766d91845cb..ca23f1b5d7c40 100644 --- a/compiler/rustc_type_ir/src/data_structures/mod.rs +++ b/compiler/rustc_type_ir/src/data_structures/mod.rs @@ -11,6 +11,9 @@ mod delayed_map; #[cfg(feature = "nightly")] mod impl_ { pub use rustc_data_structures::sso::{SsoHashMap, SsoHashSet}; + pub mod fx { + pub use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; + } pub use rustc_data_structures::stack::ensure_sufficient_stack; pub use rustc_data_structures::sync::Lrc; } @@ -19,6 +22,11 @@ mod impl_ { mod impl_ { pub use std::collections::{HashMap as SsoHashMap, HashSet as SsoHashSet}; pub use std::sync::Arc as Lrc; + pub mod fx { + pub use std::collections::{HashMap as FxHashMap, HashSet as FxHashSet}; + + pub use indexmap::{IndexMap as FxIndexMap, IndexSet as FxIndexSet}; + } #[inline] pub fn ensure_sufficient_stack(f: impl FnOnce() -> R) -> R { diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs index 923b74abdfd2f..86819629653a1 100644 --- a/compiler/rustc_type_ir/src/elaborate.rs +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -5,6 +5,7 @@ use smallvec::smallvec; use crate::data_structures::HashSet; use crate::inherent::*; use crate::outlives::{Component, push_outlives_components}; +use crate::visit::{collect_constrained_late_bound_regions, collect_referenced_late_bound_regions}; use crate::{self as ty, Interner, Upcast as _}; /// "Elaboration" is the process of identifying all the predicates that @@ -86,6 +87,18 @@ pub fn elaborate>( elaborator } +fn projection_has_unconstrained_vars(cx: I, predicate: I::Predicate) -> bool { + let Some(pred) = predicate.as_clause().and_then(|c| c.as_projection_clause()) else { + return false; + }; + + let constrained_regions = + collect_constrained_late_bound_regions(cx, pred.map_bound(|pred| pred.projection_term)); + let referenced_regions = + collect_referenced_late_bound_regions(cx, pred.map_bound(|pred| pred.term)); + referenced_regions.difference(&constrained_regions).next().is_some() +} + impl> Elaborator { fn extend_deduped(&mut self, obligations: impl IntoIterator) { // Only keep those bounds that we haven't already seen. @@ -93,9 +106,12 @@ impl> Elaborator { // cases. One common case is when people define // `trait Sized: Sized { }` rather than `trait Sized { }`. self.stack.extend( - obligations.into_iter().filter(|o| { - self.visited.insert(self.cx.anonymize_bound_vars(o.predicate().kind())) - }), + obligations + .into_iter() + .filter(|o| !projection_has_unconstrained_vars(self.cx, o.predicate())) + .filter(|o| { + self.visited.insert(self.cx.anonymize_bound_vars(o.predicate().kind())) + }), ); } diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index d337a1a8ad9b7..0a3aeb4b22a06 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -51,7 +51,7 @@ use rustc_index::{Idx, IndexVec}; use thin_vec::ThinVec; use tracing::{debug, instrument}; -use crate::data_structures::Lrc; +use crate::data_structures::{Lrc, ensure_sufficient_stack}; use crate::inherent::*; use crate::visit::{TypeVisitable, TypeVisitableExt as _}; use crate::{self as ty, Interner}; @@ -503,3 +503,63 @@ impl<'a, I: Interner> TypeFolder for RegionFolder<'a, I> { } } } + +/// Expand any [weak alias types][weak] contained within the given `value`. +/// +/// This should be used over other normalization routines in situations where +/// it's important not to normalize other alias types and where the predicates +/// on the corresponding type alias shouldn't be taken into consideration. +/// +/// Whenever possible **prefer not to use this function**! Instead, use standard +/// normalization routines or if feasible don't normalize at all. +/// +/// This function comes in handy if you want to mimic the behavior of eager +/// type alias expansion in a localized manner. +/// +///
+/// This delays a bug on overflow! Therefore you need to be certain that the +/// contained types get fully normalized at a later stage. Note that even on +/// overflow all well-behaved weak alias types get expanded correctly, so the +/// result is still useful. +///
+/// +/// [weak]: ty::Weak +pub fn expand_weak_alias_tys>(cx: I, value: T) -> T { + value.fold_with(&mut WeakAliasTypeExpander { cx, depth: 0 }) +} + +struct WeakAliasTypeExpander { + cx: I, + depth: usize, +} + +impl TypeFolder for WeakAliasTypeExpander { + fn cx(&self) -> I { + self.cx + } + + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + if !ty.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) { + return ty; + } + let ty::Alias(ty::Weak, alias) = ty.kind() else { + return ty.super_fold_with(self); + }; + if !self.cx.recursion_limit().value_within_limit(self.depth) { + let guar = self.cx.delay_bug("overflow expanding weak alias type"); + return Ty::new_error(self.cx, guar); + } + + self.depth += 1; + ensure_sufficient_stack(|| { + self.cx.type_of(alias.def_id).instantiate(self.cx, alias.args).fold_with(self) + }) + } + + fn fold_const(&mut self, ct: I::Const) -> I::Const { + if !ct.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) { + return ct; + } + ct.super_fold_with(self) + } +} diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 0c3b0758f0f95..4caf396ece597 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use std::hash::Hash; use std::ops::Deref; -use rustc_ast_ir::Movability; +use rustc_ast_ir::{Limit, Movability}; use rustc_index::bit_set::DenseBitSet; use smallvec::SmallVec; @@ -177,7 +177,7 @@ pub trait Interner: fn parent(self, def_id: Self::DefId) -> Self::DefId; - fn recursion_limit(self) -> usize; + fn recursion_limit(self) -> Limit; type Features: Features; fn features(self) -> Self::Features; diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 3cc2dcbaca6d3..abe6339113396 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -18,6 +18,7 @@ use std::hash::Hash; use std::marker::PhantomData; use derive_where::derive_where; +use rustc_ast_ir::Limit; use rustc_index::{Idx, IndexVec}; use tracing::debug; @@ -142,6 +143,11 @@ impl UsageKind { #[derive(Debug, Clone, Copy)] struct AvailableDepth(usize); +impl From for AvailableDepth { + fn from(Limit(value): Limit) -> AvailableDepth { + AvailableDepth(value) + } +} impl AvailableDepth { /// Returns the remaining depth allowed for nested goals. /// @@ -368,9 +374,9 @@ pub struct SearchGraph, X: Cx = ::Cx> { } impl, X: Cx> SearchGraph { - pub fn new(root_depth: usize) -> SearchGraph { + pub fn new(recursion_limit: Limit) -> SearchGraph { Self { - root_depth: AvailableDepth(root_depth), + root_depth: recursion_limit.into(), stack: Default::default(), provisional_cache: Default::default(), _marker: PhantomData, diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 3213638afb258..2b363daa2e0d1 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -51,6 +51,8 @@ use smallvec::SmallVec; use thin_vec::ThinVec; use crate::data_structures::Lrc; +use crate::data_structures::fx::FxIndexSet; +use crate::fold::TypeFoldable; use crate::inherent::*; use crate::{self as ty, Interner, TypeFlags}; @@ -581,3 +583,112 @@ impl TypeVisitor for HasErrorVisitor { ControlFlow::Break(guar) } } + +/// Returns a set of all late-bound regions that are constrained +/// by `value`, meaning that if we instantiate those LBR with +/// variables and equate `value` with something else, those +/// variables will also be equated. +pub fn collect_constrained_late_bound_regions( + cx: I, + value: ty::Binder, +) -> FxIndexSet +where + T: TypeFoldable, +{ + collect_late_bound_regions(cx, value, true) +} + +/// Returns a set of all late-bound regions that appear in `value` anywhere. +pub fn collect_referenced_late_bound_regions( + cx: I, + value: ty::Binder, +) -> FxIndexSet +where + T: TypeFoldable, +{ + collect_late_bound_regions(cx, value, false) +} + +fn collect_late_bound_regions( + cx: I, + value: ty::Binder, + just_constrained: bool, +) -> FxIndexSet +where + T: TypeFoldable, +{ + let mut collector = LateBoundRegionsCollector::new(just_constrained); + let value = value.skip_binder(); + let value = + if just_constrained { crate::fold::expand_weak_alias_tys(cx, value) } else { value }; + value.visit_with(&mut collector); + collector.regions +} + +/// Collects all the late-bound regions at the innermost binding level +/// into a hash set. +struct LateBoundRegionsCollector { + current_index: ty::DebruijnIndex, + regions: FxIndexSet, + + /// `true` if we only want regions that are known to be + /// "constrained" when you equate this type with another type. In + /// particular, if you have e.g., `&'a u32` and `&'b u32`, equating + /// them constraints `'a == 'b`. But if you have `<&'a u32 as + /// Trait>::Foo` and `<&'b u32 as Trait>::Foo`, normalizing those + /// types may mean that `'a` and `'b` don't appear in the results, + /// so they are not considered *constrained*. + just_constrained: bool, +} + +impl LateBoundRegionsCollector { + fn new(just_constrained: bool) -> Self { + Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained } + } +} + +impl TypeVisitor for LateBoundRegionsCollector { + fn visit_binder>(&mut self, t: &ty::Binder) { + self.current_index.shift_in(1); + t.super_visit_with(self); + self.current_index.shift_out(1); + } + + fn visit_ty(&mut self, t: I::Ty) { + if self.just_constrained { + match t.kind() { + // If we are only looking for "constrained" regions, we have to ignore the + // inputs to a projection as they may not appear in the normalized form. + ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => { + return; + } + // All weak alias types should've been expanded beforehand. + ty::Alias(ty::Weak, _) => unreachable!("unexpected weak alias type"), + _ => {} + } + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: I::Const) { + // if we are only looking for "constrained" region, we have to + // ignore the inputs of an unevaluated const, as they may not appear + // in the normalized form + if self.just_constrained { + if let ty::ConstKind::Unevaluated(..) = c.kind() { + return; + } + } + + c.super_visit_with(self) + } + + fn visit_region(&mut self, r: I::Region) { + if let ty::ReBound(debruijn, br) = r.kind() { + if debruijn == self.current_index { + self.regions.insert(br); + } + } + } +} diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 0fda13e061600..4c56421635444 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -81,7 +81,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let align = this.malloc_align(size); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?; + let ptr = + this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?; interp_ok(ptr.into()) } @@ -105,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; this.write_pointer(ptr, &memptr)?; interp_ok(Scalar::from_i32(0)) @@ -138,7 +139,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), new_align, MiriMemoryKind::C.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; interp_ok(new_ptr.into()) } @@ -179,7 +180,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; interp_ok(ptr.into()) } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 1ce0c209de9e2..4fa72ac360ddc 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -509,7 +509,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), memory_kind.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; ecx.write_pointer(ptr, dest) @@ -538,7 +538,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::Rust.into(), - AllocInit::Zero + AllocInit::Zero, )?; this.write_pointer(ptr, dest) }); @@ -599,7 +599,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), align, MiriMemoryKind::Rust.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; this.write_pointer(new_ptr, dest) }); diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index cafce62cfedbe..58c4cb8111ec8 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1109,7 +1109,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), dirent_layout.align.abi, MiriMemoryKind::Runtime.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; let entry: Pointer = entry.into(); diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index 6418d749d3d9d..8e5a3021b1c03 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -49,7 +49,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), align, MiriMemoryKind::Mmap.into(), - AllocInit::Zero + AllocInit::Zero, )?; interp_ok(Scalar::from_pointer(ptr, this)) diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 2d5d3a6471ab9..aefeee6f7a3a3 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -116,7 +116,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { align, MiriMemoryKind::Mmap.into(), // mmap guarantees new mappings are zero-init. - AllocInit::Zero + AllocInit::Zero, )?; interp_ok(Scalar::from_pointer(ptr, this)) diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 4462d025beada..5fd0e40d58c3d 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -266,7 +266,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::WinHeap.into(), - init + init, )?; this.write_pointer(ptr, dest)?; } @@ -299,7 +299,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::WinHeap.into(), - AllocInit::Uninit + AllocInit::Uninit, )?; this.write_pointer(new_ptr, dest)?; }