Skip to content

cleanup Binder fields #49814

Closed
Closed
@nikomatsakis

Description

@nikomatsakis

Part of #49810.

The Binder type is used to represent higher-ranked regions:

rust/src/librustc/ty/sty.rs

Lines 645 to 653 in 4b9b70c

/// Binder is a binder for higher-ranked lifetimes. 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<TraitRef>`). Note that when we skolemize, instantiate,
/// erase, or otherwise "discharge" these bound regions, we change the
/// type from `Binder<T>` to just `T` (see
/// e.g. `liberate_late_bound_regions`).
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct Binder<T>(pub T);

I have long wanted to make that field private and thus to force users of Binder to go through the more "high-level" methods (as an aside, it might be nice to move binder to its own module too, so that ty can't use its private fields).

Right now, when we do ty::Binder(value), that means that any things with a debruijn-index of 1 (soon to be zero) in value will be bound by this binder. Maybe we can add a ty::Binder::bind(value) for this.

On the other hand, there are times when we do not expect value to have any bound regions in it -- for those cases we often use ty::Binder(value) today, but it'd be better if used ty::Binder::dummy(value), which adds an assertion.

Similarly, any attempt to directly access the "bound" value (binder.0) is better written with binder.skip_binder(), which makes it more clear what's happening. Better yet is to add an accessor, somewhat like the def_id function defined on Binder<TraitRef<'tcx>>. This is currently written to access the field 0 directly, which is bad, but the good part is that it is "safe" to do, because DefId is a type that never contains a bound name:

rust/src/librustc/ty/sty.rs

Lines 573 to 575 in 4b9b70c

pub fn def_id(&self) -> DefId {
self.0.def_id
}

Note that accessing fields which may have bound names should ideally preserve the binding level, like the inputs function on Binder<FnSig<'tcx>>:

rust/src/librustc/ty/sty.rs

Lines 841 to 843 in 4b9b70c

pub fn inputs(&self) -> Binder<&'tcx [Ty<'tcx>]> {
Binder(self.skip_binder().inputs())
}

This is saying, given a function like for<'a> fn(&'a u32) -> &'a u32, we get back a list of input types like for<'a> [&'a u32]. Note that the binder is preserved. (Internally, though, it could be rewritten to use the helper Binder::map_bound:

rust/src/librustc/ty/sty.rs

Lines 697 to 701 in 4b9b70c

pub fn map_bound<F, U>(self, f: F) -> Binder<U>
where F: FnOnce(T) -> U
{
ty::Binder(f(self.0))
}

Metadata

Metadata

Assignees

Labels

A-trait-systemArea: Trait systemT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.WG-traitsWorking group: Traits, https://internals.rust-lang.org/t/announcing-traits-working-group/6804

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions