Skip to content

Use trait_object_dummy_self more & heavily fix+update related docs#153497

Open
fmease wants to merge 1 commit intorust-lang:mainfrom
fmease:trait-obj-dummy-self-improvs
Open

Use trait_object_dummy_self more & heavily fix+update related docs#153497
fmease wants to merge 1 commit intorust-lang:mainfrom
fmease:trait-obj-dummy-self-improvs

Conversation

@fmease
Copy link
Member

@fmease fmease commented Mar 6, 2026

  1. Use trait_object_dummy_self (Infer(FreshTy(0))) in more places
    • Two places used to construct this dummy self type manually (Ty::new_fresh(tcx, 0))
    • One used to use usize as a dummy self type!
  2. Really flesh out the docs of trait_object_dummy_self
  3. Rewrite severely outdated & wrong (doc) comments from scratch (some date back to 2018)
  4. Uncomment a debug assertion that was accidentally commented out in a refactoring PR back in 2018!

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 6, 2026

/// Infer predicates for the items in the crate.
///
/// `global_inferred_outlives`: this is initially the empty map that
Copy link
Member Author

Choose a reason for hiding this comment

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

(this parameter no longer exists, it's a local variable now)


/// Object types don't have a self type specified. Therefore, when
/// we convert the principal trait-ref into a normal trait-ref,
/// you must give *some* self type. A common choice is `mk_err()`
Copy link
Member Author

@fmease fmease Mar 6, 2026

Choose a reason for hiding this comment

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

A common choice is mk_err()

No longer up to date.

First of all, mk_err() no longer exists, it's Ty::new_error from middle which type IR shouldn't know about.

Second of all, we no longer use {type error} for that since that one carries an ErrorGuaranteed now, so it can no longer be used as a flexible dummy type. It's superseded by, you guessed it, trait_object_dummy_self (cc #70866, #71938).

///
/// Existential trait refs don't contain a self type, it's erased.
/// Therefore, you must specify *some* self type to perform the conversion.
/// A common choice is the trait object type itself or some kind of dummy type.
Copy link
Member Author

Choose a reason for hiding this comment

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

We're not mentioning trait_object_dummy_self from middle by name because type IR shouldn't know about it.

/// For example, the trait object type `Trait<'a, T, N>` can be understood as:
/// ```ignore (illustrative)
/// exists T. T: Trait<'a, 'b, X, Y>
/// exists<X> X: Trait<'a, T, N>
Copy link
Member Author

Choose a reason for hiding this comment

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

Using a more Rust-like syntax instead of a Haskell-based one.

pub never: Ty<'tcx>,
pub self_param: Ty<'tcx>,

/// Dummy type used for the `Self` of a `TraitRef` created for converting
Copy link
Member Author

Choose a reason for hiding this comment

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

Context: "converting" is old terminology for (HIR ty) lowering before the ASTconvHIR ty lowering renaming.

/// A common choice is the trait object type itself or some kind of dummy type.
pub fn with_self_ty(self, interner: I, self_ty: I::Ty) -> TraitRef<I> {
// otherwise the escaping vars would be captured by the binder
// debug_assert!(!self_ty.has_escaping_bound_vars());
Copy link
Member Author

Choose a reason for hiding this comment

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

This debug assert was accidentally commented out in a refactoring PR from 2018! See PR #53816, path src/librustc/ty/sty.rs.

for (outlives_predicate, &span) in explicit_predicates.as_ref().skip_binder() {
debug!("outlives_predicate = {outlives_predicate:?}");

// Careful: If we are inferring the effects of a `dyn Trait<..>`
Copy link
Member Author

Choose a reason for hiding this comment

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

I've "moved" this lengthy explainer to the ty::Dynamic branch (or rather replaced it with a more concise & up to date one) because it is about trait object types specifically and therefore shouldn't belong in this general function!

Comment on lines -288 to -292
// Note that we do this check for self **before** applying `args`. In the
// case that `args` come from a `dyn Trait` type, our caller will have
// included `Self = usize` as the value for `Self`. If we were
// to apply the args, and not filter this predicate, we might then falsely
// conclude that e.g., `X: 'x` was a reasonable inferred requirement.
Copy link
Member Author

@fmease fmease Mar 6, 2026

Choose a reason for hiding this comment

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

The provided example ("we might then falsely conclude that e.g., X: 'x was a reasonable inferred requirement") doesn't make any sense whatsoever! That's because this comment wasn't properly updated over time:

Originally, it didn't say Self = usize but Self = dyn Trait<'x, X> which makes a whole lot more sense since back in ancient times, we did use the trait object type itself for the erased self type which was problematic and thus changed to usize (sic!).

So the problem this paragraph is trying to convey is:

  1. Given:
    1. struct Struct<'x, X>(Box<dyn Trait<'x, X>>);
    2. trait Trait<'a, T> where Self: 'a {}
  2. We'd convert existential trait ref Trait<'x, X> to normal trait ref <dyn Trait<'x, X> as Trait<'x, X>> (setting the self type to the trait object type itself)
  3. Extract its args, so [dyn Trait<'x, X>, 'x, X]
  4. Instantiate Self: 'a from the trait with these args to obtain dyn Trait<'x, X>: 'x
  5. We then split the trait object type in outlives-components, namely X: 'x (and 'x: 'x) which would be bad.

Therefore we filter out outlives-bounds mentioning Self before substitution/instantiation.

However, using the trait object type itself is still problematic since it can lead to escaping bound vars (issue #53419), therefore it was replaced with the dummy usize (PR #53441) but the comment wasn't updated (PR #56003 later updated it to Self = usize but didn't update the rest of the paragraph).

// predicates in `check_explicit_predicates` we
// need to ignore checking the explicit_map for
// Self type.
let args = ex_trait_ref.with_self_ty(tcx, tcx.types.usize).skip_binder().args;
Copy link
Member Author

Choose a reason for hiding this comment

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

Instead of a dummy usize we now use the "canonical" trait_object_dummy_self which is way less janky.

Comment on lines +262 to +263
if let IgnorePredicatesReferencingSelf::Yes = ignore_preds_refing_self
&& arg.walk().any(|arg| arg == tcx.types.self_param.into())
Copy link
Member Author

Choose a reason for hiding this comment

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

Note that this whole "filter out things before substitution/instantiation" topic is completely irrelevant now due to us using trait_object_dummy_self instead of usize. The former is guaranteed to never occur in the outlives-predicates (that's a guarantee by HIR ty lowering / HIR analysis's type collection). The latter, usize, can obviously occur in the predicates.

So theoretically I could just look for fn param ignored_self_ty (set to the dummy Self for branch ty::Dynamic) after instantiation if I so wanted to.

However, I feel like looking for self_param before instantiation plus just having a Boolean-like parameter is a lot cleaner.

@fmease fmease marked this pull request as ready for review March 6, 2026 15:28
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 6, 2026
@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Mar 6, 2026
@rustbot
Copy link
Collaborator

rustbot commented Mar 6, 2026

r? @dingxiangfei2009

rustbot has assigned @dingxiangfei2009.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler, types
  • compiler, types expanded to 69 candidates
  • Random selection from 15 candidates

@fmease fmease force-pushed the trait-obj-dummy-self-improvs branch 2 times, most recently from aeaf232 to 03b5e8e Compare March 6, 2026 15:40
@fmease fmease force-pushed the trait-obj-dummy-self-improvs branch from 03b5e8e to 4e7b116 Compare March 6, 2026 16:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants