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
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
match key.typing_env.typing_mode() {
ty::TypingMode::PostAnalysis => {}
ty::TypingMode::Coherence
| ty::TypingMode::Reflection
| ty::TypingMode::Analysis { .. }
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::PostBorrowckAnalysis { .. } => {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/const_eval/valtrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
match typing_env.typing_mode() {
ty::TypingMode::PostAnalysis => {}
ty::TypingMode::Coherence
| ty::TypingMode::Reflection
| ty::TypingMode::Analysis { .. }
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::PostBorrowckAnalysis { .. } => {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
match typing_env.typing_mode() {
TypingMode::PostAnalysis => {}
TypingMode::Coherence
| TypingMode::Reflection
| TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. } => {
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_const_eval/src/interpret/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ pub(crate) fn type_implements_dyn_trait<'tcx, M: Machine<'tcx>>(
);
};

let (infcx, param_env) = ecx.tcx.infer_ctxt().build_with_typing_env(ecx.typing_env);
let (infcx, param_env) = ecx.tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::new(
ecx.typing_env.param_env,
ty::TypingMode::Reflection,
));

let ocx = ObligationCtxt::new(&infcx);
ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ language_item_table! {
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);

TryAsDyn, sym::try_as_dyn, try_as_dyn, Target::Trait, GenericRequirement::Exact(1);

// lang items relating to transmutability
TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(2);
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ pub(crate) fn provide(providers: &mut Providers) {
adt_def,
fn_sig,
impl_trait_header,
impl_is_fully_generic_for_reflection,
coroutine_kind,
coroutine_for_closure,
opaque_ty_origin,
Expand Down Expand Up @@ -1370,6 +1371,11 @@ pub fn suggest_impl_trait<'tcx>(
None
}

fn impl_is_fully_generic_for_reflection(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
tcx.impl_trait_header(def_id).is_fully_generic_for_reflection()
&& tcx.explicit_predicates_of(def_id).is_fully_generic_for_reflection()
Copy link
Copy Markdown
Member

@BoxyUwU BoxyUwU May 1, 2026

Choose a reason for hiding this comment

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

If we don't need to check elaborated clauses does that mean we actually don't need to check any predicates since we'll wind up proving them in Reflection mode too

View changes since the review

}

fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplTraitHeader<'_> {
let icx = ItemCtxt::new(tcx, def_id);
let item = tcx.hir_expect_item(def_id);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
defining_opaque_types_and_generators
}
ty::TypingMode::Coherence
| ty::TypingMode::Reflection
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::PostAnalysis => {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
defining_opaque_types_and_generators
}
ty::TypingMode::Coherence
| ty::TypingMode::Reflection
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::PostAnalysis => {
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 @@ -1075,6 +1075,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 @@ -1406,6 +1407,7 @@ impl<'tcx> InferCtxt<'tcx> {
}
mode @ (ty::TypingMode::Coherence
| ty::TypingMode::PostBorrowckAnalysis { .. }
| ty::TypingMode::Reflection
| ty::TypingMode::PostAnalysis) => mode,
};
ty::TypingEnv::new(param_env, typing_mode)
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 @@ -281,7 +281,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
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ provide! { tcx, def_id, other, cdata,
fn_sig => { table }
codegen_fn_attrs => { table }
impl_trait_header => { table }
impl_is_fully_generic_for_reflection => { table_direct }
const_param_default => { table }
object_lifetime_default => { table }
thir_abstract_const => { table }
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2181,6 +2181,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let header = tcx.impl_trait_header(def_id);
record!(self.tables.impl_trait_header[def_id] <- header);

let impl_is_fully_generic_for_reflection =
tcx.impl_is_fully_generic_for_reflection(def_id);
self.tables
.impl_is_fully_generic_for_reflection
.set(def_id.index, impl_is_fully_generic_for_reflection);

self.tables.defaultness.set(def_id.index, tcx.defaultness(def_id));

let trait_ref = header.trait_ref.instantiate_identity().skip_norm_wip();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ define_tables! {
constness: Table<DefIndex, hir::Constness>,
safety: Table<DefIndex, hir::Safety>,
defaultness: Table<DefIndex, hir::Defaultness>,
impl_is_fully_generic_for_reflection: Table<DefIndex, bool>,

- optional:
attributes: Table<DefIndex, LazyArray<hir::Attribute>>,
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_middle/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,15 @@ rustc_queries! {
separate_provide_extern
}

/// 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`.
query impl_is_fully_generic_for_reflection(impl_id: DefId) -> bool {
desc { "computing trait implemented by `{}`", tcx.def_path_str(impl_id) }
cache_on_disk
separate_provide_extern
}

/// Given an `impl_def_id`, return true if the self type is guaranteed to be unsized due
/// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct
/// whose tail is one of those types.
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ pub enum SelectionCandidate<'tcx> {
BuiltinUnsizeCandidate,

BikeshedGuaranteedNoDropCandidate,

TryAsDynCandidate,
}

/// The result of trait evaluation. The order is important
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,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_is_fully_generic_for_reflection(impl_def_id)
}

fn trait_is_auto(self, trait_def_id: DefId) -> bool {
self.trait_is_auto(trait_def_id)
}
Expand Down Expand Up @@ -816,6 +820,7 @@ bidirectional_lang_item_map! {
Sized,
TransmuteTrait,
TrivialClone,
TryAsDyn,
Tuple,
Unpin,
Unsize,
Expand Down
70 changes: 68 additions & 2 deletions compiler/rustc_middle/src/ty/generics.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::ops::ControlFlow;

use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_span::{Span, Symbol, kw};
use rustc_type_ir::{TypeSuperVisitable as _, TypeVisitable, TypeVisitor};
use tracing::instrument;

use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt, Unnormalized};
use crate::ty;
use crate::ty::{EarlyBinder, GenericArgsRef};
use crate::ty::{self, ClauseKind, EarlyBinder, GenericArgsRef, Region, RegionKind, TyKind};

#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub enum GenericParamDefKind {
Expand Down Expand Up @@ -436,6 +438,70 @@ impl<'tcx> GenericPredicates<'tcx> {
instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| Unnormalized::new(*p)));
instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s));
}

/// Allow simple where bounds like `T: Debug`, but prevent any kind of
/// outlives bounds or uses of generic parameters on the right hand side.
Copy link
Copy Markdown
Member

@BoxyUwU BoxyUwU May 1, 2026

Choose a reason for hiding this comment

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

this feels kind of arbitrary to me, Self parameters are not special in any way in the type system afaik, what's the reason we want this

View changes since the review

pub fn is_fully_generic_for_reflection(self) -> bool {
struct ParamChecker;
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParamChecker {
type Result = ControlFlow<()>;
fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result {
match r.kind() {
RegionKind::ReEarlyParam(_) | RegionKind::ReStatic | RegionKind::ReError(_) => {
ControlFlow::Break(())
}
RegionKind::ReVar(_)
| RegionKind::RePlaceholder(_)
| RegionKind::ReErased
| RegionKind::ReLateParam(_) => {
bug!("unexpected lifetime in impl: {r:?}")
}
RegionKind::ReBound(..) => ControlFlow::Continue(()),
}
}

fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
Copy link
Copy Markdown
Member

@BoxyUwU BoxyUwU May 1, 2026

Choose a reason for hiding this comment

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

no visit_const? I guess because equality of const generics is always applicable? comment :3

View changes since the review

match t.kind() {
TyKind::Param(_p) => {
// Reject using parameters used in the type in where bounds
return ControlFlow::Break(());
}
TyKind::Alias(..) => return ControlFlow::Break(()),
_ => (),
}
t.super_visit_with(self)
}
}

// Pessimistic: if any of the parameters have where bounds
// don't allow this impl to be used.
self.predicates.iter().all(|(clause, _)| {
match clause.kind().skip_binder() {
ClauseKind::Trait(trait_predicate) => {
// In a `T: Trait`, if the rhs bound does not contain any generic params
// or 'static lifetimes, then it cannot transitively cause such requirements,
// considering we apply the fully-generic-for-reflection rules to any impls for
// that trait, too.
if matches!(trait_predicate.self_ty().kind(), ty::Param(_))
&& trait_predicate.trait_ref.args[1..]
.iter()
.all(|arg| arg.visit_with(&mut ParamChecker).is_continue())
{
return true;
}
}
ClauseKind::RegionOutlives(_)
| ClauseKind::TypeOutlives(_)
| ClauseKind::Projection(_)
| ClauseKind::ConstArgHasType(_, _)
| ClauseKind::WellFormed(_)
| ClauseKind::ConstEvaluatable(_)
| ClauseKind::HostEffect(_)
| ClauseKind::UnstableFeature(_) => {}
}
clause.visit_with(&mut ParamChecker).is_continue()
})
}
}

/// `[const]` bounds for a given item. This is represented using a struct much like
Expand Down
55 changes: 55 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::num::NonZero;
use std::ops::ControlFlow;
use std::ptr::NonNull;
use std::{assert_matches, fmt, iter, str};

Expand Down Expand Up @@ -258,6 +259,59 @@ pub struct ImplTraitHeader<'tcx> {
pub constness: hir::Constness,
}

impl<'tcx> ImplTraitHeader<'tcx> {
/// For trait impls, checks whether the type and trait only have generic lifetime parameters in their
/// arguments and only use any generic param once.
/// This is a pessimistic analysis, so it will reject alias types
/// and other types that may be actually ok. We can allow more in the future.
pub fn is_fully_generic_for_reflection(self) -> bool {
#[derive(Default)]
struct ParamFinder {
seen: FxHashSet<u32>,
}

impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParamFinder {
type Result = ControlFlow<()>;
fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result {
match r.kind() {
RegionKind::ReEarlyParam(param) => {
if self.seen.insert(param.index) {
ControlFlow::Continue(())
} else {
ControlFlow::Break(())
}
}
RegionKind::ReBound(..) => ControlFlow::Continue(()),
RegionKind::ReStatic | RegionKind::ReError(_) => ControlFlow::Break(()),
RegionKind::ReVar(_)
| RegionKind::RePlaceholder(_)
| RegionKind::ReErased
| RegionKind::ReLateParam(_) => bug!("unexpected lifetime in impl: {r:?}"),
}
}

fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
Copy link
Copy Markdown
Member

@BoxyUwU BoxyUwU May 1, 2026

Choose a reason for hiding this comment

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

no visit_const? I guess because equality of const generics is always applicable? comment :3

View changes since the review

match t.kind() {
TyKind::Param(p) => {
// Reject using a parameter twice (e.g. in `Foo<T, T>`)
if !self.seen.insert(p.index) {
return ControlFlow::Break(());
}
}
TyKind::Alias(..) => return ControlFlow::Break(()),
_ => (),
}
t.super_visit_with(self)
}
}
self.trait_ref
.instantiate_identity()
.skip_norm_wip()
.visit_with(&mut ParamFinder::default())
.is_continue()
}
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)]
#[derive(TypeFoldable, TypeVisitable, Default)]
pub enum Asyncness {
Expand Down Expand Up @@ -1036,6 +1090,7 @@ impl<'tcx> TypingEnv<'tcx> {
let TypingEnv { typing_mode, param_env } = self;
match typing_mode.0 {
TypingMode::Coherence
| TypingMode::Reflection
| TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. } => {}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_transform/src/elaborate_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ where
match self.elaborator.typing_env().typing_mode() {
ty::TypingMode::PostAnalysis => {}
ty::TypingMode::Coherence
| ty::TypingMode::Reflection
| ty::TypingMode::Analysis { .. }
| ty::TypingMode::Borrowck { .. }
| ty::TypingMode::PostBorrowckAnalysis { .. } => {
Expand Down
Loading
Loading