,
}
@@ -1833,7 +1851,9 @@ impl<'tcx> TyCtxt<'tcx> {
_ => self.optimized_mir(def),
}
}
- ty::InstanceKind::Intrinsic(..) => bug!("intrinsics have no instance MIR"),
+ ty::InstanceKind::Intrinsic(..) | ty::InstanceKind::LlvmIntrinsic(..) => {
+ bug!("intrinsics have no instance MIR")
+ }
ty::InstanceKind::Virtual(..) => bug!("virtual dispatches have no instance MIR"),
ty::InstanceKind::Shim(shim) => self.mir_shims(shim),
};
@@ -2158,12 +2178,12 @@ impl<'tcx> TyCtxt<'tcx> {
mut ident: Ident,
scope: DefId,
item_id: LocalDefId,
- ) -> (Ident, DefId) {
+ ) -> (Ident, ModId) {
let scope = ident
.span
.normalize_to_macros_2_0_and_adjust(self.expn_that_defined(scope))
.and_then(|actual_expansion| actual_expansion.expn_data().parent_module)
- .unwrap_or_else(|| self.parent_module_from_def_id(item_id).to_def_id());
+ .unwrap_or_else(|| self.parent_module_from_def_id(item_id).to_mod_id());
(ident, scope)
}
diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs
index bec1ffc642769..ccdac57cc8dcd 100644
--- a/compiler/rustc_middle/src/ty/print/mod.rs
+++ b/compiler/rustc_middle/src/ty/print/mod.rs
@@ -368,6 +368,7 @@ impl<'tcx, P: Printer<'tcx> + std::fmt::Write> Print for ty::Instance<'tcx> {
match self.def {
ty::InstanceKind::Item(_) => {}
ty::InstanceKind::Intrinsic(_) => cx.write_str(" - intrinsic")?,
+ ty::InstanceKind::LlvmIntrinsic(_) => cx.write_str(" - LLVM intrinsic")?,
ty::InstanceKind::Virtual(_, num) => cx.write_str(&format!(" - virtual#{num}"))?,
ty::InstanceKind::Shim(shim) => {
cx.write_str(" - ")?;
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 7d7ca37ff3243..858a2f21c6b75 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -11,7 +11,7 @@ use rustc_data_structures::unord::UnordMap;
use rustc_hir as hir;
use rustc_hir::LangItem;
use rustc_hir::def::{self, CtorKind, DefKind, Namespace};
-use rustc_hir::def_id::{DefIdMap, DefIdSet, LOCAL_CRATE, ModDefId};
+use rustc_hir::def_id::{DefIdMap, DefIdSet, LOCAL_CRATE, ModId};
use rustc_hir::definitions::{DefKey, DefPathDataName};
use rustc_hir::limit::Limit;
use rustc_macros::{Lift, extension};
@@ -190,6 +190,10 @@ pub struct RegionHighlightMode<'tcx> {
/// instead of the ordinary behavior.
highlight_regions: [Option<(ty::Region<'tcx>, usize)>; 3],
+ /// If set to `true`, types that include regions will always be included in the output, while
+ /// other types will be free to be trimmed.
+ pub keep_regions: bool,
+
/// If enabled, when printing a "free region" that originated from
/// the given `ty::BoundRegionKind`, print it as "`'1`". Free regions that would ordinarily
/// have names print as normal.
@@ -208,6 +212,7 @@ impl<'tcx> RegionHighlightMode<'tcx> {
region: Option>,
number: Option,
) {
+ self.keep_regions = true;
if let Some(k) = region
&& let Some(n) = number
{
@@ -223,6 +228,7 @@ impl<'tcx> RegionHighlightMode<'tcx> {
bug!("can only highlight {} placeholders at a time", num_slots,)
});
*first_avail_slot = Some((region, number));
+ self.keep_regions = true;
}
/// Convenience wrapper for `highlighting_region`.
@@ -385,8 +391,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
&& Some(*visible_parent) != actual_parent
{
this.tcx()
- // FIXME(typed_def_id): Further propagate ModDefId
- .module_children(ModDefId::new_unchecked(*visible_parent))
+ .module_children(ModId::new_unchecked(*visible_parent))
.iter()
.filter(|child| child.res.opt_def_id() == Some(def_id))
.find(|child| child.vis.is_public() && child.ident.name != kw::Underscore)
@@ -612,8 +617,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
// that's public and whose identifier isn't `_`.
let reexport = self
.tcx()
- // FIXME(typed_def_id): Further propagate ModDefId
- .module_children(ModDefId::new_unchecked(visible_parent))
+ .module_children(ModId::new_unchecked(visible_parent))
.iter()
.filter(|child| child.res.opt_def_id() == Some(def_id))
.find(|child| child.vis.is_public() && child.ident.name != kw::Underscore)
@@ -2330,12 +2334,70 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
}
fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
+ let has_regions = self.region_highlight_mode.keep_regions
+ && ty.has_type_flags(ty::TypeFlags::HAS_REGIONS);
match ty.kind() {
- ty::Tuple(tys) if tys.len() == 0 && self.should_truncate() => {
+ ty::Tuple(tys) if tys.len() == 0 => {
// Don't truncate `()`.
+ self.pretty_print_type(ty)
+ }
+
+ ty::Adt(def, args)
+ if self.should_truncate()
+ && args.consts().count() < 2
+ && args.types().count() < 2
+ && {
+ // We ensure that if there's at most a single type parameter and that type
+ // *doesn't* have any parameters, to avoid printing all the names in cases
+ // like `Foo>>`, instead truncating those always to
+ // `Foo<...>`.
+ if let Some(arg) = args.types().next() {
+ if let ty::Adt(_, arg_args) = arg.kind() {
+ if arg_args.consts().next().is_none()
+ && arg_args.types().next().is_none()
+ {
+ // Single param type with no type or const parameters:
+ // `Foo>`.
+ true
+ } else {
+ // Single param type with multiple type or const parameters:
+ // `Foo>`. We don't want to recurse into those,
+ // we'll replace the whole thing with `...`.
+ false
+ }
+ } else {
+ // Single type param that *isn't* a type with parameters, like a
+ // primitive: `Foo`.
+ true
+ }
+ } else {
+ // No type param: `Foo`.
+ true
+ }
+ }
+ && self.tcx.item_name(def.did()).as_str().len() < 7 =>
+ {
+ // Don't fully truncate types that have "short names" and at most one type or const
+ // param. We do use the short path for them (only item name instead of full path).
+ with_forced_trimmed_paths!(self.pretty_print_type(ty))
+ }
+
+ ty::Alias(_, alias)
+ if self.should_truncate()
+ && let ty::AliasTyKind::Opaque { def_id } = alias.kind
+ && self.region_highlight_mode.keep_regions
+ && self
+ .tcx
+ .explicit_item_bounds(def_id)
+ .iter_instantiated_copied(self.tcx, alias.args)
+ .map(Unnormalized::skip_norm_wip)
+ .any(|(value, _)| value.has_bound_vars()) =>
+ {
+ // ` Trait as Trait>`
self.printed_type_count += 1;
self.pretty_print_type(ty)
}
+
ty::Adt(..)
| ty::Foreign(_)
| ty::Pat(..)
@@ -2345,23 +2407,23 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
| ty::FnPtr(..)
| ty::UnsafeBinder(..)
| ty::Dynamic(..)
- | ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Tuple(_)
| ty::Alias(..)
- | ty::Param(_)
| ty::Bound(..)
| ty::Placeholder(_)
| ty::Error(_)
- if self.should_truncate() =>
+ if self.should_truncate() && !has_regions =>
{
// We only truncate types that we know are likely to be much longer than 3 chars.
// There's no point in replacing `i32` or `!`.
- write!(self, "...")?;
+ write!(self, "_")?;
Ok(())
}
+ ty::Ref(..) if self.should_truncate() && has_regions => self.pretty_print_type(ty),
+ ty::Closure(..) => self.pretty_print_type(ty),
_ => {
self.printed_type_count += 1;
self.pretty_print_type(ty)
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 49bac6a130231..7e087f6e1646f 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -250,6 +250,7 @@ TrivialTypeTraversalImpls! {
rustc_span::Ident,
rustc_span::Span,
rustc_span::Symbol,
+ rustc_span::def_id::ModId,
rustc_target::asm::InlineAsmRegOrRegClass,
// tidy-alphabetical-end
}
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index fee9cd80161e5..29e9d1243f0ca 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -140,7 +140,7 @@ impl ImplRestrictionKind {
if restricted_to.krate == rustc_hir::def_id::LOCAL_CRATE {
with_crate_prefix!(with_no_trimmed_paths!(tcx.def_path_str(restricted_to)))
} else {
- tcx.def_path_str(restricted_to.krate.as_mod_def_id())
+ tcx.def_path_str(restricted_to.krate.as_mod_id())
}
}
}
diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs
index 932e4af130459..a5707e0177c63 100644
--- a/compiler/rustc_mir_build/src/builder/scope.rs
+++ b/compiler/rustc_mir_build/src/builder/scope.rs
@@ -967,7 +967,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let cx = RustcPatCtxt {
tcx: self.tcx,
typeck_results,
- module: self.tcx.parent_module(self.hir_id).to_def_id(),
+ module: self.tcx.parent_module(self.hir_id),
typing_env: ty::TypingEnv::post_typeck_until_borrowck_for_mir_build(
self.tcx,
self.def_id,
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 57a71467f6f01..41806547932cd 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -385,7 +385,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
tcx: self.tcx,
typeck_results: self.typeck_results,
typing_env: self.typing_env,
- module: self.tcx.parent_module(self.hir_source).to_def_id(),
+ module: self.tcx.parent_module(self.hir_source),
dropless_arena: self.dropless_arena,
match_lint_level: self.hir_source,
whole_match_span,
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 3249f196cc56d..0e6aaf3ccf220 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -732,7 +732,7 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>(
}
}
// These have no own callable MIR.
- InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => {
+ InstanceKind::Intrinsic(_) | InstanceKind::LlvmIntrinsic(_) | InstanceKind::Virtual(..) => {
debug!("instance without MIR (intrinsic / virtual)");
return Err("implementation limitation -- cannot inline intrinsic");
}
diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs
index 8c33b0930d438..22529d7fcd56f 100644
--- a/compiler/rustc_mir_transform/src/inline/cycle.rs
+++ b/compiler/rustc_mir_transform/src/inline/cycle.rs
@@ -21,7 +21,9 @@ fn should_recurse<'tcx>(tcx: TyCtxt<'tcx>, callee: ty::Instance<'tcx>) -> bool {
}
// These have no own callable MIR.
- InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => return false,
+ InstanceKind::Intrinsic(_) | InstanceKind::LlvmIntrinsic(_) | InstanceKind::Virtual(..) => {
+ return false;
+ }
// These have MIR and if that MIR is inlined, instantiated and then inlining is run
// again, a function item can end up getting inlined. Thus we'll be able to cause
diff --git a/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs b/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs
index b359077753fd6..7d902c0149c95 100644
--- a/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs
+++ b/compiler/rustc_mir_transform/src/lint_and_remove_uninhabited.rs
@@ -14,7 +14,7 @@ impl<'tcx> crate::MirPass<'tcx> for LintAndRemoveUninhabited {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let def_id = body.source.def_id().expect_local();
tracing::debug!(?def_id);
- let parent_module = tcx.parent_module_from_def_id(def_id).to_def_id();
+ let parent_module = tcx.parent_module_from_def_id(def_id);
let typing_env = body.typing_env(tcx);
// check if the function's return type is inhabited
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index f7c10a6ffed7c..631d28d6cef00 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -1019,7 +1019,9 @@ fn visit_instance_use<'tcx>(
}
match instance.def {
- ty::InstanceKind::Virtual(..) | ty::InstanceKind::Intrinsic(_) => {
+ ty::InstanceKind::Virtual(..)
+ | ty::InstanceKind::Intrinsic(_)
+ | ty::InstanceKind::LlvmIntrinsic(_) => {
if !is_direct_call {
bug!("{:?} being reified", instance);
}
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index bf4a2bdd15107..fa09e7b49de76 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -631,6 +631,7 @@ fn characteristic_def_id_of_mono_item<'tcx>(
let def_id = match instance.def {
ty::InstanceKind::Item(def) => def,
ty::InstanceKind::Intrinsic(..)
+ | ty::InstanceKind::LlvmIntrinsic(..)
| ty::InstanceKind::Virtual(..)
| ty::InstanceKind::Shim(ty::ShimKind::VTable(..))
| ty::InstanceKind::Shim(ty::ShimKind::Reify(..))
@@ -770,6 +771,17 @@ fn static_visibility<'tcx>(
*can_be_internalized = false;
default_visibility(tcx, def_id, false)
} else {
+ if tcx.def_kind(def_id).has_codegen_attrs() {
+ // Prevent EII and `rustc_std_internal_symbol` statics being internalized.
+ let attrs = tcx.codegen_fn_attrs(def_id);
+ if attrs.flags.intersects(
+ CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
+ | CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM,
+ ) {
+ *can_be_internalized = false;
+ }
+ }
+
Visibility::Hidden
}
}
@@ -810,6 +822,7 @@ fn mono_item_visibility<'tcx>(
| InstanceKind::Shim(ShimKind::FnPtr(..))
| InstanceKind::Virtual(..)
| InstanceKind::Intrinsic(..)
+ | InstanceKind::LlvmIntrinsic(..)
| InstanceKind::Shim(ShimKind::ClosureOnce { .. })
| InstanceKind::Shim(ShimKind::ConstructCoroutineInClosure { .. })
| InstanceKind::Shim(ShimKind::DropGlue(..))
diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs
index 2ebb0fb851a4c..7b66667486fc9 100644
--- a/compiler/rustc_next_trait_solver/src/delegate.rs
+++ b/compiler/rustc_next_trait_solver/src/delegate.rs
@@ -1,7 +1,8 @@
use std::ops::Deref;
use rustc_type_ir::solve::{
- Certainty, FetchEligibleAssocItemResponse, Goal, NoSolution, VisibleForLeakCheck,
+ Certainty, ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, NoSolution,
+ VisibleForLeakCheck,
};
use rustc_type_ir::{self as ty, InferCtxtLike, Interner, TypeFoldable};
@@ -23,7 +24,7 @@ pub trait SolverDelegate: Deref + Sized {
&self,
goal: Goal::Predicate>,
span: ::Span,
- ) -> Option;
+ ) -> ComputeGoalFastPathOutcome;
fn fresh_var_for_kind_with_span(
&self,
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/fast_path.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/fast_path.rs
new file mode 100644
index 0000000000000..f5b0b28281cf7
--- /dev/null
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/fast_path.rs
@@ -0,0 +1,162 @@
+//! This file contains a number of standalone functions useful for taking _fast paths_ in the trait
+//! solver. The exact place where we check for these fast paths changes, and matters a lot for
+//! performance. Ideally we'd only check them in `evaluate_goal`, but when evaluating root goals
+//! we can check them earlier and save some time creating an `EvalCtxt` in the first place.
+//!
+//! For debugging, fast paths can be disabled using `-Zdisable-fast-paths`.
+
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::solve::{
+ Certainty, ComputeGoalFastPathOutcome, Goal, GoalStalledOn, GoalStalledOnOpaques,
+ SucceededInErased,
+};
+use rustc_type_ir::{InferCtxtLike, Interner};
+
+use crate::delegate::SolverDelegate;
+use crate::solve::eval_ctxt::{RerunDecision, should_rerun_after_erased_canonicalization};
+use crate::solve::{GoalEvaluation, HasChanged};
+
+#[derive(Debug, Clone, Copy)]
+pub(super) enum RerunStalled {
+ WontMakeProgress(Certainty),
+ MayMakeProgress,
+}
+
+/// If we have run a goal before, and it was stalled, check that any of the goal's
+/// args have changed. This is a cheap way to determine that if we were to rerun this goal now,
+/// it will remain stalled since it'll canonicalize the same way and evaluation is pure.
+/// Therefore, we can skip this rerun
+#[inline]
+pub(super) fn rerunning_stalled_goal_may_make_progress(
+ delegate: &D,
+ stalled_on: Option<&GoalStalledOn>,
+) -> RerunStalled
+where
+ D: SolverDelegate,
+ I: Interner,
+{
+ use RerunStalled::*;
+
+ // If fast paths are turned off, then we assume all goals can always make progress
+ if delegate.disable_trait_solver_fast_paths() {
+ return MayMakeProgress;
+ }
+
+ // If the goal isn't stalled, we should definitely run it.
+ let Some(&GoalStalledOn { ref opaques, ref stalled_vars, ref sub_roots, stalled_certainty }) =
+ stalled_on
+ else {
+ return MayMakeProgress;
+ };
+
+ // If any of the stalled goal's generic arguments changed,
+ // rerunning might make progress so we should rerun.
+ if stalled_vars.iter().any(|value| delegate.is_changed_arg(*value)) {
+ return MayMakeProgress;
+ }
+
+ // If some inference took place in any of the sub roots,
+ // rerunning might make progress so we should rerun.
+ if sub_roots.iter().any(|&vid| delegate.sub_unification_table_root_var(vid) != vid) {
+ return MayMakeProgress;
+ }
+
+ match opaques {
+ GoalStalledOnOpaques::No => {}
+ &GoalStalledOnOpaques::Yes {
+ num_opaques_in_storage,
+ ref previously_succeeded_in_erased,
+ } => {
+ // If any opaques changed in the opaque type storage,
+ // rerunning might make progress so we should rerun.
+ if delegate
+ .opaque_types_storage_num_entries()
+ .needs_reevaluation(num_opaques_in_storage)
+ {
+ // Unless this goal previously succeeded in erased mode.
+ // If the stalled goal successfully evaluated while erasing opaque types,
+ // and the current state of the opaque type storage is not different in a way that is
+ // relevant, this stalled goal cannot make any progress and we set this variable to true.
+ let mut previous_erased_run_is_still_valid = false;
+
+ if let &SucceededInErased::Yes { accessed_opaques } = previously_succeeded_in_erased
+ {
+ match should_rerun_after_erased_canonicalization(
+ accessed_opaques,
+ delegate.typing_mode_raw(),
+ &delegate.clone_opaque_types_lookup_table(),
+ ) {
+ RerunDecision::Yes => {}
+ RerunDecision::EagerlyPropagateToParent => {
+ unreachable!("we never retry stalled queries if the parent was erased")
+ }
+ RerunDecision::No => {
+ previous_erased_run_is_still_valid = true;
+ }
+ }
+ }
+
+ if !previous_erased_run_is_still_valid {
+ return MayMakeProgress;
+ }
+ }
+ }
+ }
+
+ // Otherwise, we can be sure that this stalled goal cannot make any progress
+ // and we can exit early.
+ WontMakeProgress(stalled_certainty)
+}
+
+/// `compute_goal_fast_path` is complicated enough that outling helps, so it gets optimized
+/// separately from the caller. `compute_goal_fast_path` is the inlined version,
+/// and most call sites (when adding goals) use it. However, when entering the root
+/// we also want to check the fast path, and there the outlining matters.
+///
+/// FIXME(perf) cold might not be worth it here, given that we shuffled some things around since it
+/// mattered.
+#[cold]
+#[inline(never)]
+pub(super) fn compute_goal_fast_path_cold(
+ delegate: &D,
+ goal: Goal,
+ origin_span: I::Span,
+) -> Option>
+where
+ D: SolverDelegate,
+ I: Interner,
+{
+ compute_goal_fast_path(delegate, goal, origin_span)
+}
+
+/// This is a fast path optimization:
+/// See the docs on [`ComputeGoalFastPathOutcome`]
+pub fn compute_goal_fast_path(
+ delegate: &D,
+ goal: Goal,
+ origin_span: I::Span,
+) -> Option>
+where
+ D: SolverDelegate,
+ I: Interner,
+{
+ if delegate.disable_trait_solver_fast_paths() {
+ return None;
+ }
+
+ match delegate.compute_goal_fast_path(goal, origin_span) {
+ ComputeGoalFastPathOutcome::NoFastPath => None,
+ ComputeGoalFastPathOutcome::TriviallyHolds => Some(GoalEvaluation {
+ goal,
+ certainty: Certainty::Yes,
+ has_changed: HasChanged::No,
+ stalled_on: None,
+ }),
+ ComputeGoalFastPathOutcome::TriviallyStalled { stalled_on } => Some(GoalEvaluation {
+ goal,
+ certainty: Certainty::AMBIGUOUS,
+ has_changed: HasChanged::No,
+ stalled_on: Some(stalled_on),
+ }),
+ }
+}
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 d83320f5fe800..48a421bb5dd9b 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
@@ -31,15 +31,20 @@ use crate::delegate::SolverDelegate;
use crate::normalize::{NormalizationFolder, NormalizationWasAmbiguous};
use crate::placeholder::BoundVarReplacer;
use crate::resolve::eager_resolve_vars;
+use crate::solve::eval_ctxt::fast_path::{
+ RerunStalled, compute_goal_fast_path, rerunning_stalled_goal_may_make_progress,
+};
+use crate::solve::fast_path::compute_goal_fast_path_cold;
use crate::solve::search_graph::SearchGraph;
use crate::solve::ty::may_use_unstable_feature;
use crate::solve::{
CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, FIXPOINT_STEP_LIMIT,
- Goal, GoalEvaluation, GoalSource, GoalStalledOn, HasChanged, MaybeCause,
+ Goal, GoalEvaluation, GoalSource, GoalStalledOn, GoalStalledOnOpaques, HasChanged, MaybeCause,
NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, Response, SucceededInErased,
VisibleForLeakCheck, inspect,
};
+pub mod fast_path;
mod probe;
mod solver_region_constraints;
@@ -89,12 +94,6 @@ impl CurrentGoalKind {
}
}
-#[derive(Debug)]
-enum RerunDecision {
- Yes,
- No,
- EagerlyPropagateToParent,
-}
pub struct EvalCtxt<'a, D, I = ::Interner>
where
D: SolverDelegate,
@@ -226,8 +225,30 @@ where
span: I::Span,
stalled_on: Option>,
) -> Result, NoSolution> {
+ // Run fast paths *before* building an `EvalCtxt`, saving a little bit of time.
+ if let RerunStalled::WontMakeProgress(stalled_certainty) =
+ rerunning_stalled_goal_may_make_progress(self, stalled_on.as_ref())
+ {
+ return Ok(GoalEvaluation {
+ goal,
+ certainty: stalled_certainty,
+ has_changed: HasChanged::No,
+ stalled_on,
+ });
+ }
+
+ // No need to try the fast path if stalled_on is `None`, since we already try the fast path
+ // immediately when adding new goals. If we didn't check `stalled_on` here we'd be trying
+ // the fast path twice for some goals.
+ if stalled_on.is_some()
+ && let Some(res) = compute_goal_fast_path_cold(self, goal, span)
+ {
+ return Ok(res);
+ }
+
let result = EvalCtxt::enter_root(self, self.cx().recursion_limit(), span, |ecx| {
- ecx.evaluate_goal(GoalSource::Misc, goal, stalled_on)
+ // Fast paths handled above
+ ecx.evaluate_goal_no_fast_paths(GoalSource::Misc, goal)
});
match result {
@@ -285,12 +306,6 @@ where
}
}
-#[derive(Debug, Clone, Copy)]
-enum RerunStalled {
- WontMakeProgress(Certainty),
- MayMakeProgress,
-}
-
impl<'a, D, I> EvalCtxt<'a, D>
where
D: SolverDelegate,
@@ -483,85 +498,41 @@ where
goal: Goal,
stalled_on: Option>,
) -> Result, NoSolutionOrRerunNonErased> {
- let (normalization_nested_goals, goal_evaluation) =
- self.evaluate_goal_raw(source, goal, stalled_on, LowerAvailableDepth::Yes)?;
- assert!(normalization_nested_goals.is_empty());
- Ok(goal_evaluation)
- }
-
- /// This is a fast path optimization:
- /// If we have run this goal before, and it was stalled, check that any of the goal's
- /// args have changed. This is a cheap way to determine that if we were to rerun this goal now,
- /// it will remain stalled since it'll canonicalize the same way and evaluation is pure.
- /// Therefore, we can skip this rerun
- fn rerunning_stalled_goal_may_make_progress(
- &self,
- stalled_on: Option<&GoalStalledOn>,
- ) -> RerunStalled {
- use RerunStalled::*;
-
- // If fast paths are turned off, then we assume all goals can always make progress
- if self.delegate.disable_trait_solver_fast_paths() {
- return MayMakeProgress;
- }
-
- // If the goal isn't stalled, we should definitely run it.
- let Some(&GoalStalledOn {
- num_opaques,
- ref stalled_vars,
- ref sub_roots,
- stalled_certainty,
- ref previously_succeeded_in_erased,
- }) = stalled_on
- else {
- return MayMakeProgress;
- };
-
- // If any of the stalled goal's generic arguments changed,
- // rerunning might make progress so we should rerun.
- if stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) {
- return MayMakeProgress;
+ if let RerunStalled::WontMakeProgress(stalled_certainty) =
+ rerunning_stalled_goal_may_make_progress(self.delegate, stalled_on.as_ref())
+ {
+ return Ok(GoalEvaluation {
+ goal,
+ certainty: stalled_certainty,
+ has_changed: HasChanged::No,
+ stalled_on,
+ });
}
- // If some inference took place in any of the sub roots,
- // rerunning might make progress so we should rerun.
- if sub_roots.iter().any(|&vid| self.delegate.sub_unification_table_root_var(vid) != vid) {
- return MayMakeProgress;
+ // No need to try the fast path if stalled_on is `None`, since we already try the fast path
+ // immediately when adding new goals. If we didn't check `stalled_on` here we'd be trying
+ // the fast path twice for some goals.
+ if stalled_on.is_some()
+ && let Some(res) = compute_goal_fast_path_cold(self.delegate, goal, self.origin_span)
+ {
+ return Ok(res);
}
- // If any opaques changed in the opaque type storage,
- // rerunning might make progress so we should rerun.
- if self.delegate.opaque_types_storage_num_entries().needs_reevaluation(num_opaques) {
- // Unless this goal previously succeeded in erased mode.
- // If the stalled goal successfully evaluated while erasing opaque types,
- // and the current state of the opaque type storage is not different in a way that is
- // relevant, this stalled goal cannot make any progress and we set this variable to true.
- let mut previous_erased_run_is_still_valid = false;
-
- if let &SucceededInErased::Yes { accessed_opaques } = previously_succeeded_in_erased {
- match self.should_rerun_after_erased_canonicalization(
- accessed_opaques,
- self.typing_mode(),
- &self.delegate.clone_opaque_types_lookup_table(),
- ) {
- RerunDecision::Yes => {}
- RerunDecision::EagerlyPropagateToParent => {
- unreachable!("we never retry stalled queries if the parent was erased")
- }
- RerunDecision::No => {
- previous_erased_run_is_still_valid = true;
- }
- }
- }
-
- if !previous_erased_run_is_still_valid {
- return MayMakeProgress;
- }
- }
+ self.evaluate_goal_no_fast_paths(source, goal)
+ }
- // Otherwise, we can be sure that this stalled goal cannot make any progress
- // and we can exit early.
- WontMakeProgress(stalled_certainty)
+ // Outlining and `#[cold]` matter here because fast paths make it less likely to get here.
+ #[cold]
+ #[inline(never)]
+ fn evaluate_goal_no_fast_paths(
+ &mut self,
+ source: GoalSource,
+ goal: Goal,
+ ) -> Result, NoSolutionOrRerunNonErased> {
+ let (normalization_nested_goals, goal_evaluation) =
+ self.evaluate_goal_raw(source, goal, LowerAvailableDepth::Yes)?;
+ assert!(normalization_nested_goals.is_empty());
+ Ok(goal_evaluation)
}
/// Recursively evaluates `goal`, returning the nested goals in case
@@ -572,32 +543,6 @@ where
/// should use [`EvalCtxt::evaluate_goal`] which discards that empty
/// storage.
pub(super) fn evaluate_goal_raw(
- &mut self,
- source: GoalSource,
- goal: Goal,
- stalled_on: Option>,
- increase_depth_for_nested: LowerAvailableDepth,
- ) -> Result<(NestedNormalizationGoals, GoalEvaluation), NoSolutionOrRerunNonErased> {
- if let RerunStalled::WontMakeProgress(stalled_certainty) =
- self.rerunning_stalled_goal_may_make_progress(stalled_on.as_ref())
- {
- return Ok((
- NestedNormalizationGoals::empty(),
- GoalEvaluation {
- goal,
- certainty: stalled_certainty,
- has_changed: HasChanged::No,
- stalled_on,
- },
- ));
- }
-
- self.evaluate_goal_cold(source, goal, increase_depth_for_nested)
- }
-
- #[cold]
- #[inline(never)]
- pub(super) fn evaluate_goal_cold(
&mut self,
source: GoalSource,
goal: Goal,
@@ -669,7 +614,7 @@ where
&mut inspect::ProofTreeBuilder::new_noop(),
);
- let should_rerun = self.should_rerun_after_erased_canonicalization(
+ let should_rerun = should_rerun_after_erased_canonicalization(
accessed_opaques,
self.typing_mode(),
&opaque_types,
@@ -759,41 +704,12 @@ where
// that is not resolved. Only when *these* have changed is it meaningful
// to recompute this goal.
HasChanged::Yes => None,
- HasChanged::No => {
- // Remove the canonicalized universal vars, since we only care about stalled existentials.
- let mut sub_roots = Vec::new();
- let mut stalled_vars = orig_values;
- stalled_vars.retain(|arg| match arg.kind() {
- // Lifetimes can never stall goals.
- ty::GenericArgKind::Lifetime(_) => false,
- ty::GenericArgKind::Type(ty) => match ty.kind() {
- ty::Infer(ty::TyVar(vid)) => {
- sub_roots.push(self.delegate.sub_unification_table_root_var(vid));
- true
- }
- ty::Infer(_) => true,
- ty::Param(_) | ty::Placeholder(_) => false,
- _ => unreachable!("unexpected orig_value: {ty:?}"),
- },
- ty::GenericArgKind::Const(ct) => match ct.kind() {
- ty::ConstKind::Infer(_) => true,
- ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(_) => false,
- _ => unreachable!("unexpected orig_value: {ct:?}"),
- },
- });
-
- Some(GoalStalledOn {
- num_opaques: canonical_goal
- .canonical
- .value
- .predefined_opaques_in_body
- .len(),
- stalled_vars,
- sub_roots,
- stalled_certainty: certainty,
- previously_succeeded_in_erased: succeeded_in_erased,
- })
- }
+ HasChanged::No => Some(self.build_stalled_on(
+ canonical_goal,
+ certainty,
+ orig_values,
+ succeeded_in_erased,
+ )),
},
};
@@ -803,98 +719,47 @@ where
))
}
- fn should_rerun_after_erased_canonicalization(
+ fn build_stalled_on(
&self,
- AccessedOpaques { reason: _, rerun }: AccessedOpaques,
- original_typing_mode: TypingMode,
- parent_opaque_types: &[(OpaqueTypeKey, I::Ty)],
- ) -> RerunDecision {
- let parent_opaque_defids = parent_opaque_types.iter().map(|(key, _)| key.def_id.into());
- let opaque_in_storage = |opaques: I::LocalDefIds, defids: SmallCopyList<_>| {
- if defids.as_ref().is_empty() {
- RerunDecision::No
- } else if opaques
- .iter()
- .chain(parent_opaque_defids)
- .any(|opaque| defids.as_ref().contains(&opaque))
- {
- RerunDecision::Yes
- } else {
- RerunDecision::No
- }
- };
- let any_opaque_has_infer_as_hidden = || {
- if parent_opaque_types.iter().any(|(_, ty)| ty.is_ty_var()) {
- RerunDecision::Yes
- } else {
- RerunDecision::No
- }
- };
-
- let res = match (rerun, original_typing_mode) {
- // =============================
- (RerunCondition::Never, _) => RerunDecision::No,
- // =============================
- (_, TypingMode::ErasedNotCoherence(MayBeErased)) => {
- RerunDecision::EagerlyPropagateToParent
- }
- // =============================
- // In coherence, we never switch to erased mode, so we will never register anything
- // in the rerun state, so we should've taken the first branch of this match
- (_, TypingMode::Coherence) => unreachable!(),
- // =============================
- (RerunCondition::Always, _) => RerunDecision::Yes,
- // =============================
- (
- RerunCondition::OpaqueInStorage(..),
- TypingMode::PostAnalysis | TypingMode::Codegen,
- ) => RerunDecision::Yes,
- (
- RerunCondition::OpaqueInStorage(defids),
- TypingMode::PostBorrowck { defined_opaque_types: opaques }
- | TypingMode::Typeck { defining_opaque_types_and_generators: opaques }
- | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: opaques },
- ) => opaque_in_storage(opaques, defids),
- // =============================
- (RerunCondition::AnyOpaqueHasInferAsHidden, TypingMode::Typeck { .. }) => {
- any_opaque_has_infer_as_hidden()
- }
- (
- RerunCondition::AnyOpaqueHasInferAsHidden,
- TypingMode::PostBorrowck { .. }
- | TypingMode::PostAnalysis
- | TypingMode::Codegen
- | TypingMode::PostTypeckUntilBorrowck { .. },
- ) => RerunDecision::No,
- // =============================
- (
- RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(_),
- TypingMode::PostAnalysis | TypingMode::Codegen,
- ) => RerunDecision::No,
- (
- RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(defids),
- TypingMode::Typeck { defining_opaque_types_and_generators: opaques },
- ) => {
- if let RerunDecision::Yes = any_opaque_has_infer_as_hidden() {
- RerunDecision::Yes
- } else if let RerunDecision::Yes = opaque_in_storage(opaques, defids) {
- RerunDecision::Yes
- } else {
- RerunDecision::No
+ canonical_goal: CanonicalInput,
+ certainty: Certainty,
+ mut stalled_vars: Vec,
+ previously_succeeded_in_erased: SucceededInErased,
+ ) -> GoalStalledOn {
+ // Remove the canonicalized universal vars, since we only care about stalled existentials.
+ let mut sub_roots = Vec::new();
+ stalled_vars.retain(|arg| match arg.kind() {
+ // Lifetimes can never stall goals.
+ ty::GenericArgKind::Lifetime(_) => false,
+ ty::GenericArgKind::Type(ty) => match ty.kind() {
+ ty::Infer(ty::TyVar(vid)) => {
+ sub_roots.push(self.delegate.sub_unification_table_root_var(vid));
+ true
}
- }
- (
- RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(defids),
- TypingMode::PostBorrowck { defined_opaque_types: opaques }
- | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: opaques },
- ) => opaque_in_storage(opaques, defids),
- };
-
- debug!(
- "checking whether to rerun {rerun:?} in outer typing mode {original_typing_mode:?} and opaques {parent_opaque_types:?}: {res:?}"
- );
+ ty::Infer(_) => true,
+ ty::Param(_) | ty::Placeholder(_) => false,
+ _ => unreachable!("unexpected orig_value: {ty:?}"),
+ },
+ ty::GenericArgKind::Const(ct) => match ct.kind() {
+ ty::ConstKind::Infer(_) => true,
+ ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(_) => false,
+ _ => unreachable!("unexpected orig_value: {ct:?}"),
+ },
+ });
- res
+ GoalStalledOn {
+ stalled_vars,
+ sub_roots,
+ stalled_certainty: certainty,
+ opaques: GoalStalledOnOpaques::Yes {
+ num_opaques_in_storage: canonical_goal
+ .canonical
+ .value
+ .predefined_opaques_in_body
+ .len(),
+ previously_succeeded_in_erased,
+ },
+ }
}
pub(super) fn compute_goal(
@@ -983,6 +848,9 @@ where
) -> Result