Skip to content

more strongly dissuade use of skip_binder #144775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
60 changes: 30 additions & 30 deletions compiler/rustc_type_ir/src/binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@ use crate::lift::Lift;
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
use crate::{self as ty, Interner};

/// Binder is a binder for higher-ranked lifetimes or types. It is part of the
/// `Binder` is a binder for higher-ranked lifetimes or types. It is part of the
/// compiler's representation for things like `for<'a> Fn(&'a isize)`
/// (which would be represented by the type `PolyTraitRef ==
/// Binder<I, TraitRef>`). Note that when we instantiate,
/// erase, or otherwise "discharge" these bound vars, we change the
/// type from `Binder<I, T>` to just `T` (see
/// e.g., `liberate_late_bound_regions`).
/// (which would be represented by the type `PolyTraitRef == Binder<I, TraitRef>`).
///
/// See <https://rustc-dev-guide.rust-lang.org/ty_module/instantiating_binders.html>
/// for more details.
///
/// `Decodable` and `Encodable` are implemented for `Binder<T>` using the `impl_binder_encode_decode!` macro.
#[derive_where(Clone; I: Interner, T: Clone)]
Expand Down Expand Up @@ -154,22 +153,19 @@ impl<I: Interner, T: TypeVisitable<I>> TypeSuperVisitable<I> for Binder<I, T> {
}

impl<I: Interner, T> Binder<I, T> {
/// Skips the binder and returns the "bound" value. This is a
/// risky thing to do because it's easy to get confused about
/// De Bruijn indices and the like. It is usually better to
/// discharge the binder using `no_bound_vars` or
/// `instantiate_bound_regions` or something like
/// that. `skip_binder` is only valid when you are either
/// extracting data that has nothing to do with bound vars, you
/// are doing some sort of test that does not involve bound
/// regions, or you are being very careful about your depth
/// accounting.
/// Returns the value contained inside of this `for<'a>`. Accessing generic args
/// in the returned value is generally incorrect.
///
/// Please read <https://rustc-dev-guide.rust-lang.org/ty_module/instantiating_binders.html>
/// before using this function. It is usually better to discharge the binder using
/// `no_bound_vars` or `instantiate_bound_regions` or something like that.
Comment on lines +159 to +161
Copy link
Member

@fmease fmease Aug 1, 2025

Choose a reason for hiding this comment

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

Consider wrapping this and maybe its following paragraphs in a big warning block by surrounding it with <div class="warning"> </div> (note: there needs to be a blank line between the HTML tags and the Markdown content or else the latter would be interpreted as HTML), similarly for the EarlyBinder case.

These get rendered like so: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.expand_free_alias_tys

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, I feel I'd need to already mark the "Accessing generic args in the returned value is generally incorrect." as a warning, but that's part of the first line.

I personally don't know how I'd restructure this to add a warning block and would merge this as is

///
/// Some examples where `skip_binder` is reasonable:
/// `skip_binder` is only valid when you are either extracting data that does not reference
/// any generic arguments, e.g. a `DefId`, or when you're making sure you only pass the
/// value to things which can handle escaping bound vars.
///
/// - extracting the `DefId` from a PolyTraitRef;
/// - comparing the self type of a PolyTraitRef to see if it is equal to
/// a type parameter `X`, since the type `X` does not reference any regions
/// See existing uses of `.skip_binder()` in `rustc_trait_selection::traits::select`
/// or `rustc_next_trait_solver` for examples.
pub fn skip_binder(self) -> T {
self.value
}
Expand Down Expand Up @@ -336,12 +332,11 @@ impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
}
}

/// Similar to [`super::Binder`] except that it tracks early bound generics, i.e. `struct Foo<T>(T)`
/// Similar to [`Binder`] except that it tracks early bound generics, i.e. `struct Foo<T>(T)`
/// needs `T` instantiated immediately. This type primarily exists to avoid forgetting to call
/// `instantiate`.
///
/// If you don't have anything to `instantiate`, you may be looking for
/// [`instantiate_identity`](EarlyBinder::instantiate_identity) or [`skip_binder`](EarlyBinder::skip_binder).
/// See <https://rustc-dev-guide.rust-lang.org/ty_module/early_binder.html> for more details.
#[derive_where(Clone; I: Interner, T: Clone)]
#[derive_where(Copy; I: Interner, T: Copy)]
#[derive_where(PartialEq; I: Interner, T: PartialEq)]
Expand Down Expand Up @@ -404,17 +399,22 @@ impl<I: Interner, T> EarlyBinder<I, T> {
EarlyBinder { value, _tcx: PhantomData }
}

/// Skips the binder and returns the "bound" value.
/// This can be used to extract data that does not depend on generic parameters
/// (e.g., getting the `DefId` of the inner value or getting the number of
/// arguments of an `FnSig`). Otherwise, consider using
/// [`instantiate_identity`](EarlyBinder::instantiate_identity).
/// Skips the binder and returns the "bound" value. Accessing generic args
/// in the returned value is generally incorrect.
///
/// Please read <https://rustc-dev-guide.rust-lang.org/ty_module/early_binder.html>
/// before using this function.
///
/// Only use this to extract data that does not depend on generic parameters, e.g.
/// to get the `DefId` of the inner value or the number of arguments ofan `FnSig`,
/// or while making sure to only pass the value to functions which are explicitly
/// set up to handle these uninstantiated generic parameters.
///
/// To skip the binder on `x: &EarlyBinder<I, T>` to obtain `&T`, leverage
/// [`EarlyBinder::as_ref`](EarlyBinder::as_ref): `x.as_ref().skip_binder()`.
///
/// See also [`Binder::skip_binder`](super::Binder::skip_binder), which is
/// the analogous operation on [`super::Binder`].
/// See also [`Binder::skip_binder`](Binder::skip_binder), which is
/// the analogous operation on [`Binder`].
pub fn skip_binder(self) -> T {
self.value
}
Expand Down
Loading