Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler/rustc_hir_analysis/src/coherence/orphan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,8 @@ fn orphan_check<'tcx>(
}

// (1) Instantiate all generic params with fresh inference vars.
let infcx = tcx.infer_ctxt().build(TypingMode::Coherence);
let infcx =
tcx.infer_ctxt().enable_next_solver_overflow_fcw(false).build(TypingMode::Coherence);
let cause = traits::ObligationCause::dummy();
let args = infcx.fresh_args_for_item(cause.span, impl_def_id.to_def_id());
let trait_ref = trait_ref.instantiate(tcx, args).skip_norm_wip();
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_infer/src/infer/at.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ impl<'tcx> InferCtxt<'tcx> {
.placeholder_assumptions_for_next_solver
.clone(),
next_trait_solver: self.next_trait_solver,
enable_next_solver_overflow_fcw: self.enable_next_solver_overflow_fcw,
obligation_inspector: self.obligation_inspector.clone(),
}
}
Expand Down Expand Up @@ -113,6 +114,7 @@ impl<'tcx> InferCtxt<'tcx> {
.placeholder_assumptions_for_next_solver
.clone(),
next_trait_solver: self.next_trait_solver,
enable_next_solver_overflow_fcw: self.enable_next_solver_overflow_fcw,
obligation_inspector: self.obligation_inspector.clone(),
};
forked.inner.borrow_mut().projection_cache().clear();
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_infer/src/infer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
self.next_trait_solver
}

fn enable_next_solver_overflow_fcw(&self) -> bool {
self.enable_next_solver_overflow_fcw
}

fn disable_trait_solver_fast_paths(&self) -> bool {
self.disable_trait_solver_fast_paths()
}
Expand Down Expand Up @@ -326,6 +330,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
self.probe(|_| probe())
}

fn commit_if_ok<T, E>(&self, f: impl FnOnce() -> Result<T, E>) -> Result<T, E> {
self.commit_if_ok(|_| f())
}

fn sub_regions(
&self,
sub: ty::Region<'tcx>,
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,13 @@ pub struct InferCtxt<'tcx> {

next_trait_solver: bool,

/// We have a `recursion_depth_exceeding_limit` FCW to mitigate breakages
/// caused by enabling the next solver globally. But the next solver is
/// already used by default in some places so we know they won't have
/// additional breakages. We also don't want spurious result in coherence
/// checking so we disable the FCW there as well.
enable_next_solver_overflow_fcw: bool,

pub obligation_inspector: Cell<Option<ObligationInspector<'tcx>>>,
}

Expand Down Expand Up @@ -578,6 +585,7 @@ pub struct InferCtxtBuilder<'tcx> {
/// Whether we should use the new trait solver in the local inference context,
/// which affects things like which solver is used in `predicate_may_hold`.
next_trait_solver: bool,
enable_next_solver_overflow_fcw: bool,
}

#[extension(pub trait TyCtxtInferExt<'tcx>)]
Expand All @@ -589,6 +597,7 @@ impl<'tcx> TyCtxt<'tcx> {
in_hir_typeck: false,
skip_leak_check: false,
next_trait_solver: self.next_trait_solver_globally(),
enable_next_solver_overflow_fcw: true,
}
}
}
Expand All @@ -599,6 +608,14 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
self
}

pub fn enable_next_solver_overflow_fcw(
mut self,
enable_next_solver_overflow_fcw: bool,
) -> Self {
self.enable_next_solver_overflow_fcw = enable_next_solver_overflow_fcw;
self
}

pub fn ignoring_regions(mut self) -> Self {
self.considering_regions = false;
self
Expand Down Expand Up @@ -648,6 +665,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
in_hir_typeck,
skip_leak_check,
next_trait_solver,
enable_next_solver_overflow_fcw,
} = *self;
InferCtxt {
tcx,
Expand All @@ -665,6 +683,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
universe: Cell::new(ty::UniverseIndex::ROOT),
placeholder_assumptions_for_next_solver: RefCell::new(Default::default()),
next_trait_solver,
enable_next_solver_overflow_fcw,
obligation_inspector: Cell::new(None),
}
}
Expand Down
58 changes: 58 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ pub mod hardwired {
PRIVATE_INTERFACES,
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
RECURSION_DEPTH_EXCEEDING_LIMIT,
REDUNDANT_IMPORTS,
REDUNDANT_LIFETIMES,
REFINING_IMPL_TRAIT_INTERNAL,
Expand Down Expand Up @@ -5579,3 +5580,60 @@ declare_lint! {
report_in_deps: true,
};
}

declare_lint! {
/// The `recursion_depth_exceeding_limit` lint detects cases where the compiler does not
/// correctly track the recursion depth in obligation evaluation.
///
/// ### Example
/// ```text
/// rustc -Znext-solver example.rs
/// ```
///
/// ```rust,ignore (requires next solver)
/// #![recursion_limit = "8"]
/// struct Foo<T> {
/// t: T,
/// opt_t: Option<T>,
/// }
/// fn require_sync<T: Sync>() {}
/// fn main() {
/// require_sync::<Foo<Foo<Foo<Foo<Foo<Foo<()>>>>>>>();
/// }
/// ```
///
/// This will produces:
/// ```text
/// error[E0275]: overflow evaluating the requirement `Foo<Foo<Foo<Foo<Foo<Foo<()>>>>>>: Sync`
/// --> example.rs:12:20
/// |
/// | require_sync::<Foo<Foo<Foo<Foo<Foo<Foo<()>>>>>>>();
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/// |
/// = help: consider increasing the recursion limit by adding a `#![recursion_limit = "16"]` attribute to your crate
/// note: required by a bound in `require_sync`
/// --> example.rs:9:20
/// |
/// | fn require_sync<T: Sync>() {}
/// | ^^^^ required by this bound in `require_sync`
/// ```
///
/// ### Explanation
///
/// The compiler uses a recursion limit in obligation evaluation to avoid hangs.
///
/// However, the old trait solver sometimes ignores the recursion depth, whereas
/// the new solver correctly tracks it. This reveals cases where overflow should
/// have occurred previously.
///
/// This is a [future-incompatible] lint to transition this to a hard error in the future.
///
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub RECURSION_DEPTH_EXCEEDING_LIMIT,
Warn,
"detects trait solving overflow that only happens with the next solver",
@future_incompatible = FutureIncompatibleInfo {
reason: fcw!(FutureReleaseError #159228),
report_in_deps: false,
};
}
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2617,10 +2617,10 @@ rustc_queries! {

/// Used by `-Znext-solver` to compute proof trees.
query evaluate_root_goal_for_proof_tree_raw(
goal: solve::CanonicalInput<'tcx>,
key: (solve::CanonicalInput<'tcx>, usize)
) -> (solve::QueryResult<'tcx>, &'tcx solve::inspect::Probe<TyCtxt<'tcx>>) {
no_hash
desc { "computing proof tree for `{}`", goal.canonical.value.goal.predicate }
desc { "computing proof tree for `{}` with depth `{}`", key.0.canonical.value.goal.predicate, key.1 }
}

/// Returns the Rust target features for the current target. These are not always the same as LLVM target features!
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/query/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,12 @@ impl<'tcx, T: QueryKeyBounds> QueryKey for (CanonicalQueryInput<'tcx, T>, bool)
}
}

impl<'tcx, T: QueryKeyBounds> QueryKey for (CanonicalQueryInput<'tcx, T>, usize) {
fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
DUMMY_SP
}
}

impl<'tcx> QueryKey for (Ty<'tcx>, rustc_abi::VariantIdx) {
fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
DUMMY_SP
Expand Down
46 changes: 42 additions & 4 deletions compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
use std::ops::ControlFlow;
use std::{debug_assert_matches, fmt};

use rustc_data_structures::Limit;
use rustc_data_structures::intern::Interned;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::def::{CtorKind, DefKind, Namespace};
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::{CRATE_HIR_ID, LangItem};
use rustc_span::{DUMMY_SP, Span, Symbol};
use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem};
use rustc_type_ir::{
Expand All @@ -22,6 +23,7 @@ use crate::traits::cache::WithDepNode;
use crate::traits::solve::{
self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, QueryResult, inspect,
};
use crate::ty::print::{FmtPrinter, Print};
use crate::ty::{
self, BoundRegion, Clause, Const, List, ParamTy, Pattern, PolyExistentialPredicate, Predicate,
Region, RegionKind, Ty, TyCtxt,
Expand Down Expand Up @@ -787,8 +789,44 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
fn evaluate_root_goal_for_proof_tree_raw(
self,
canonical_goal: CanonicalInput<'tcx>,
root_depth: usize,
) -> (QueryResult<'tcx>, &'tcx inspect::Probe<TyCtxt<'tcx>>) {
self.evaluate_root_goal_for_proof_tree_raw(canonical_goal)
self.evaluate_root_goal_for_proof_tree_raw((canonical_goal, root_depth))
}

fn emit_next_solver_overflow_fcw(self, predicate: ty::Predicate<'tcx>, span: Span) {
self.emit_node_span_lint(
rustc_session::lint::builtin::RECURSION_DEPTH_EXCEEDING_LIMIT,
CRATE_HIR_ID,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yeah, I guess that works 👍

we should somehow adjust the error message to say that this lint is always attached to the whole crate and can't be disabled on a per function basis

span,
rustc_errors::DiagDecorator(|diag| {
// FIXME: share this with overflow error in fulfillment instead of duplicating.
let pred_str = {
let s = predicate.to_string();
if s.len() > 50 {
let mut p: FmtPrinter<'_, '_> =
FmtPrinter::new_with_limit(self, Namespace::TypeNS, Limit(6));
predicate.print(&mut p).unwrap();
p.into_buffer()
} else {
s
}
};
diag.primary_message(format!(
"overflow evaluating the requirement `{pred_str}`",
));
diag.help(format!(
"consider increasing the recursion limit by adding a \
`#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
self.recursion_limit() * 2,
self.crate_name(LOCAL_CRATE),
));
diag.help(
"or consider adding a manual `impl` of auto traits like `Send` for intermediate types, if auto traits are involved",
);
diag.note("this lint is attached to the whole crate and can't be disabled on a per-function basis");
}),
)
}

fn item_name(self, id: DefId) -> Symbol {
Expand Down
Loading
Loading