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
5 changes: 4 additions & 1 deletion compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
};

let (infcx, param_env) =
self.tcx.infer_ctxt().build_with_typing_env(self.typing_env);
self.tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv {
typing_mode: ty::TypingMode::Reflection,
..self.typing_env
});

let ocx = ObligationCtxt::new(&infcx);
ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| {
Expand Down
68 changes: 67 additions & 1 deletion compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ignore-tidy-filelength
use std::borrow::Cow;
use std::fmt;
use std::ops::ControlFlow;

use rustc_abi::ExternAbi;
use rustc_ast::attr::AttributeExt;
Expand All @@ -16,6 +17,7 @@ pub use rustc_ast::{
MetaItemInner, MetaItemLit, Movability, Mutability, Pinnedness, UnOp,
};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::tagged_ptr::TaggedRef;
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
Expand All @@ -35,7 +37,7 @@ use crate::attrs::AttributeKind;
use crate::def::{CtorKind, DefKind, MacroKinds, PerNS, Res};
use crate::def_id::{DefId, LocalDefIdMap};
pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
use crate::intravisit::{FnKind, VisitorExt};
use crate::intravisit::{FnKind, Visitor, VisitorExt};
use crate::lints::DelayedLints;

#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)]
Expand Down Expand Up @@ -4445,6 +4447,70 @@ pub struct Impl<'hir> {
pub constness: Constness,
}

impl Impl<'_> {
pub fn is_fully_generic_for_reflection(&self) -> bool {
#[derive(Default)]
struct LifetimeFinder {
seen: FxHashSet<LocalDefId>,
}
impl<'v> Visitor<'v> for LifetimeFinder {
type Result = ControlFlow<()>;
fn visit_lifetime(&mut self, lifetime: &'v Lifetime) -> Self::Result {
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice in this function to stop tracking for<'any>-kind of lifetimes: those are part of the monomorphised info of the type, are they not? But this might require setting up a separate set of known-for<>-quantified lifetimes that ought to be ignored rather than seen-checked.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yea, I decided to just ignore those for now (sound but pessimistic again), but should def document that

match lifetime.kind {
LifetimeKind::Param(def_id) => {
if self.seen.insert(def_id) {
ControlFlow::Continue(())
} else {
ControlFlow::Break(())
}
}
LifetimeKind::ImplicitObjectLifetimeDefault
| LifetimeKind::Error
| LifetimeKind::Infer
| LifetimeKind::Static => ControlFlow::Break(()),
}
}
}
let mut ty_lifetimes = LifetimeFinder::default();
if ty_lifetimes.visit_ty_unambig(self.self_ty).is_break() {
return false;
Comment on lines +4475 to +4476
Copy link
Contributor

@danielhenrymantilla danielhenrymantilla Dec 19, 2025

Choose a reason for hiding this comment

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

I think this function also needs to test for unicity of type params, lest impl<T> Trait for (T, T) {} be green-lit, which when intersected over the &u32 family of types, is just (&'t u32, &'t u32) impl in disguise.

  • If anything, do add that test case:

    const _: () = {
        trait Homo {}
        impl<T> Homo for (T, T) {}
    
        // Let's pick `T = &'_ i32`.
        assert!(std::any::try_as_dyn::<_, dyn Homo>(&(&42_i32, &27_i32)).is_none());
    };

}
let ty_lifetimes = ty_lifetimes.seen;

#[derive(Default)]
struct LifetimeChecker {
ty_lifetimes: FxHashSet<LocalDefId>,
}
impl<'v> Visitor<'v> for LifetimeChecker {
type Result = ControlFlow<()>;
fn visit_lifetime(&mut self, lifetime: &'v Lifetime) -> Self::Result {
match lifetime.kind {
LifetimeKind::Param(def_id) => {
if self.ty_lifetimes.contains(&def_id) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
LifetimeKind::ImplicitObjectLifetimeDefault
| LifetimeKind::Error
| LifetimeKind::Infer
| LifetimeKind::Static => ControlFlow::Continue(()),
}
}
}

let mut checker = LifetimeChecker { ty_lifetimes };

// Pessimistic: if any of the lifetimes used in the type show up in where bounds
// don't allow this impl to be used.
self.generics
.predicates
.iter()
.all(|pred| checker.visit_where_predicate(pred).is_continue())
}
}

#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct TraitImplHeader<'hir> {
pub safety: Safety,
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1345,6 +1345,7 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader
.unwrap_or_else(|| panic!("expected impl trait, found inherent impl on {def_id:?}"));
let selfty = tcx.type_of(def_id).instantiate_identity();
let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl);
let is_fully_generic_for_reflection = impl_.is_fully_generic_for_reflection();

check_impl_constness(tcx, impl_.constness, &of_trait.trait_ref);

Expand All @@ -1355,6 +1356,7 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader
safety: of_trait.safety,
polarity: polarity_of_impl(tcx, of_trait, is_rustc_reservation),
constness: impl_.constness,
is_fully_generic_for_reflection,
}
}

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,7 @@ impl<'tcx> InferCtxt<'tcx> {
// and post-borrowck analysis mode. We may need to modify its uses
// to support PostBorrowckAnalysis in the old solver as well.
TypingMode::Coherence
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => false,
}
Expand Down Expand Up @@ -1379,6 +1380,7 @@ impl<'tcx> InferCtxt<'tcx> {
}
mode @ (ty::TypingMode::Coherence
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::Reflection
| ty::TypingMode::PostAnalysis) => mode,
};
ty::TypingEnv { typing_mode, param_env }
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_infer/src/infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,9 @@ impl<'tcx> InferCtxt<'tcx> {
.map(|obligation| obligation.as_goal()),
);
}
mode @ (ty::TypingMode::PostBorrowckAnalysis { .. } | ty::TypingMode::PostAnalysis) => {
mode @ (ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::PostAnalysis
| ty::TypingMode::Reflection) => {
bug!("insert hidden type in {mode:?}")
}
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
self.impl_polarity(impl_def_id)
}

fn is_fully_generic_for_reflection(self, impl_def_id: Self::ImplId) -> bool {
self.impl_trait_header(impl_def_id).is_fully_generic_for_reflection
}

fn trait_is_auto(self, trait_def_id: DefId) -> bool {
self.trait_is_auto(trait_def_id)
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ pub struct ImplTraitHeader<'tcx> {
pub polarity: ImplPolarity,
pub safety: hir::Safety,
pub constness: hir::Constness,
/// Whether all generic parameters of the type are unique unconstrained generic parameters
/// of the impl. `Bar<'static>` or `Foo<'a, 'a>` or outlives bounds on the lifetimes cause
/// this boolean to be false and `try_as_dyn` to return `None`.
pub is_fully_generic_for_reflection: bool,
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)]
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,7 @@ where
TypingMode::Coherence => return,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => {}
}
Expand Down Expand Up @@ -991,6 +992,7 @@ where
TypingMode::Analysis { .. } => self.opaques_with_sub_unified_hidden_type(self_ty),
TypingMode::Coherence
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => vec![],
};
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ where
TypingMode::Coherence => Certainty::AMBIGUOUS,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => return Err(NoSolution),
},
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ where
fn opaque_type_is_rigid(&self, def_id: I::DefId) -> bool {
match self.typing_mode() {
// Opaques are never rigid outside of analysis mode.
TypingMode::Coherence | TypingMode::PostAnalysis => false,
TypingMode::Reflection | TypingMode::Coherence | TypingMode::PostAnalysis => false,
// During analysis, opaques are rigid unless they may be defined by
// the current body.
TypingMode::Analysis { defining_opaque_types_and_generators: non_rigid_opaques }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ where
// Outside of coherence, we treat the associated item as rigid instead.
ty::TypingMode::Analysis { .. }
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::Reflection
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::PostAnalysis => {
ecx.structurally_instantiate_normalizes_to_term(
Expand Down Expand Up @@ -336,6 +337,7 @@ where
}
ty::TypingMode::Analysis { .. }
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::Reflection
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::PostAnalysis => {
ecx.structurally_instantiate_normalizes_to_term(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ where
self.eq(goal.param_env, expected, actual)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
TypingMode::PostAnalysis => {
TypingMode::Reflection | TypingMode::PostAnalysis => {
// FIXME: Add an assertion that opaque type storage is empty.
let actual = cx.type_of(opaque_ty.def_id).instantiate(cx, opaque_ty.args);
self.eq(goal.param_env, expected, actual)?;
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_next_trait_solver/src/solve/search_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ where
}
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => Err(NoSolution),
},
Expand Down
13 changes: 12 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,22 @@ where
TypingMode::Coherence => Certainty::AMBIGUOUS,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => return Err(NoSolution),
},

// Impl matches polarity
(ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive)
| (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => Certainty::Yes,
| (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => {
if let TypingMode::Reflection = ecx.typing_mode()
&& !cx.is_fully_generic_for_reflection(impl_def_id)
{
return Err(NoSolution);
Comment on lines +89 to +92
Copy link
Contributor

@danielhenrymantilla danielhenrymantilla Dec 19, 2025

Choose a reason for hiding this comment

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

This is way over my head, lol, but just to sanity-check: could this branch be hoisted to the beginning of the function, as in, any impl that is not fully_generic_for_reflection(), when checking in Reflection mode, ought to result in Err(NoSolution), right? As in, it's not specific to this double-Negative branch?

} else {
Certainty::Yes
}
}

// Impl doesn't match polarity
(ty::ImplPolarity::Positive, ty::PredicatePolarity::Negative)
Expand Down Expand Up @@ -1330,6 +1339,7 @@ where
TypingMode::Coherence => return,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => {}
}
Expand Down Expand Up @@ -1504,6 +1514,7 @@ where
}
TypingMode::Coherence
| TypingMode::PostAnalysis
| TypingMode::Reflection
| TypingMode::Borrowck { defining_opaque_types: _ }
| TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/solve/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
TypingMode::Coherence
| TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. } => false,
TypingMode::PostAnalysis => {
let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/solve/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ where
TypingMode::Coherence
| TypingMode::Borrowck { defining_opaque_types: _ }
| TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
| TypingMode::Reflection
| TypingMode::PostAnalysis => return Default::default(),
};

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ where
TypingMode::Coherence
| TypingMode::Borrowck { defining_opaque_types: _ }
| TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
| TypingMode::Reflection
| TypingMode::PostAnalysis => return Default::default(),
};

Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_trait_selection/src/traits/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
| TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. } => flags.remove(ty::TypeFlags::HAS_TY_OPAQUE),
TypingMode::PostAnalysis => {}
TypingMode::Reflection | TypingMode::PostAnalysis => {}
}

value.has_type_flags(flags)
Expand Down Expand Up @@ -402,7 +402,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
| TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. } => ty.super_fold_with(self),
TypingMode::PostAnalysis => {
TypingMode::Reflection | TypingMode::PostAnalysis => {
let recursion_limit = self.cx().recursion_limit();
if !recursion_limit.value_within_limit(self.depth) {
self.selcx.infcx.err_ctxt().report_overflow_error(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
TypingMode::Coherence
| TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. } => {
debug!(
assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ impl<'a, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'a, 'tcx> {
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. } => ty.try_super_fold_with(self)?,

TypingMode::PostAnalysis => {
TypingMode::Reflection | TypingMode::PostAnalysis => {
let args = data.args.try_fold_with(self)?;
let recursion_limit = self.cx().recursion_limit();

Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1467,6 +1467,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
TypingMode::Coherence => {}
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::Reflection
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => return Ok(()),
}
Expand Down Expand Up @@ -1519,6 +1520,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
defining_opaque_types.is_empty()
|| (!pred.has_opaque_types() && !pred.has_coroutines())
}
// Impls that are not fully generic are completely ignored as "nonexistent"
// in this mode, so the results wildly differ from normal trait solving.
TypingMode::Reflection => false,
// The hidden types of `defined_opaque_types` is not local to the current
// inference context, so we can freely move this to the global cache.
TypingMode::PostBorrowckAnalysis { .. } => true,
Expand Down Expand Up @@ -2548,6 +2552,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
debug!("reservation impls only apply in intercrate mode");
return Err(());
}
if !impl_trait_header.is_fully_generic_for_reflection
&& matches!(self.infcx.typing_mode(), TypingMode::Reflection)
{
debug!("reflection mode only allows fully generic impls");
return Err(());
}

Ok(Normalized { value: impl_args, obligations: nested_obligations })
}
Expand Down Expand Up @@ -2884,6 +2894,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}
TypingMode::Coherence
| TypingMode::PostAnalysis
| TypingMode::Reflection
| TypingMode::Borrowck { defining_opaque_types: _ }
| TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => false,
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ty_utils/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ fn resolve_associated_item<'tcx>(
ty::TypingMode::Coherence
| ty::TypingMode::Analysis { .. }
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::Reflection
| ty::TypingMode::PostBorrowckAnalysis { .. } => false,
ty::TypingMode::PostAnalysis => !trait_ref.still_further_specializable(),
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_type_ir/src/infer_ctxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ pub enum TypingMode<I: Interner> {
/// This is currently only used by the new solver, but should be implemented in
/// the old solver as well.
PostBorrowckAnalysis { defined_opaque_types: I::LocalDefIds },
/// During the evaluation of reflection logic that ignores lifetimes, we can only
/// handle impls that are fully generic over all lifetimes without constraints on
/// those lifetimes (other than implied bounds).
Reflection,
/// After analysis, mostly during codegen and MIR optimizations, we're able to
/// reveal all opaque types. As the hidden type should *never* be observable
/// directly by the user, this should not be used by checks which may expose
Expand Down
Loading
Loading