Repository navigation
Closed
Labels
A-trait-systemArea: Trait systemArea: Trait systemC-cleanupCategory: PRs that clean code up or issues documenting cleanup.Category: PRs that clean code up or issues documenting cleanup.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
Description
Part of #49810:
The DebruijnIndex type -- for some reason I no longer recall -- is 1-based:
Lines 897 to 941 in 4b9b70c
| /// A [De Bruijn index][dbi] is a standard means of representing | |
| /// regions (and perhaps later types) in a higher-ranked setting. In | |
| /// particular, imagine a type like this: | |
| /// | |
| /// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char) | |
| /// ^ ^ | | | | |
| /// | | | | | | |
| /// | +------------+ 1 | | | |
| /// | | | | |
| /// +--------------------------------+ 2 | | |
| /// | | | |
| /// +------------------------------------------+ 1 | |
| /// | |
| /// In this type, there are two binders (the outer fn and the inner | |
| /// fn). We need to be able to determine, for any given region, which | |
| /// fn type it is bound by, the inner or the outer one. There are | |
| /// various ways you can do this, but a De Bruijn index is one of the | |
| /// more convenient and has some nice properties. The basic idea is to | |
| /// count the number of binders, inside out. Some examples should help | |
| /// clarify what I mean. | |
| /// | |
| /// Let's start with the reference type `&'b isize` that is the first | |
| /// argument to the inner function. This region `'b` is assigned a De | |
| /// Bruijn index of 1, meaning "the innermost binder" (in this case, a | |
| /// fn). The region `'a` that appears in the second argument type (`&'a | |
| /// isize`) would then be assigned a De Bruijn index of 2, meaning "the | |
| /// second-innermost binder". (These indices are written on the arrays | |
| /// in the diagram). | |
| /// | |
| /// What is interesting is that De Bruijn index attached to a particular | |
| /// variable will vary depending on where it appears. For example, | |
| /// the final type `&'a char` also refers to the region `'a` declared on | |
| /// the outermost fn. But this time, this reference is not nested within | |
| /// any other binders (i.e., it is not an argument to the inner fn, but | |
| /// rather the outer one). Therefore, in this case, it is assigned a | |
| /// De Bruijn index of 1, because the innermost binder in that location | |
| /// is the outer fn. | |
| /// | |
| /// [dbi]: http://en.wikipedia.org/wiki/De_Bruijn_index | |
| #[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug, Copy, PartialOrd, Ord)] | |
| pub struct DebruijnIndex { | |
| /// We maintain the invariant that this is never 0. So 1 indicates | |
| /// the innermost binder. To ensure this, create with `DebruijnIndex::new`. | |
| pub depth: u32, | |
| } |
This seems a bit confusing and unnecessary. If we are going to unify it with CanonicalVar, it'd be nicer if it were 0-based. Also, maybe we should make the internal field private and use an accessor (e.g., to_depth() -> usize or something).
I guess that the first step to doing this refactor is to remove the assertion from new:
Lines 1158 to 1160 in 4b9b70c
| pub fn new(depth: u32) -> DebruijnIndex { | |
| assert!(depth > 0); | |
| DebruijnIndex { depth: depth } |
A quick ripgrep reveals that DebruijnIndex::new is often invoked with a hard-coded 1 or 2, which can .. presumably be just adjusted to 0 and 1, respectively:
/home/nmatsakis/.cargo/bin/rg --no-heading --color never 'DebruijnIndex::new'
src/librustc_driver/test.rs:330: let r = self.re_late_bound_with_debruijn(id, ty::DebruijnIndex::new(1));
src/librustc_driver/test.rs:487: let t_ptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
src/librustc_driver/test.rs:524: let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
src/librustc_driver/test.rs:552: let t_rptr_bound1 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(1));
src/librustc_driver/test.rs:555: let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
src/librustc_driver/test.rs:571: let re_bound1 = env.re_late_bound_with_debruijn(1, ty::DebruijnIndex::new(1));
src/librustc_driver/test.rs:586: let t_rptr_bound2 = env.t_rptr_late_bound_with_debruijn(1, ty::DebruijnIndex::new(2));
src/librustc_trans/common.rs:428: let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv);
src/librustc/util/ppaux.rs:500: tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1), br))
src/librustc/ty/sty.rs:939: /// the innermost binder. To ensure this, create with `DebruijnIndex::new`.
src/librustc/ty/fold.rs:390: self.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(current_depth), br))
src/librustc/ty/fold.rs:451: self.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrAnon(counter)))
src/librustc/ty/util.rs:592: let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv);
src/librustc/middle/resolve_lifetime.rs:101: let depth = ty::DebruijnIndex::new(1);
src/librustc/middle/resolve_lifetime.rs:110: let depth = ty::DebruijnIndex::new(1);
src/librustc_typeck/check/intrinsic.rs:122: tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
src/librustc_typeck/check/intrinsic.rs:301: tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
src/librustc_typeck/check/generator_interior.rs:128: fcx.tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(current_depth),
src/librustc/infer/higher_ranked/mod.rs:420: return infcx.tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1), *a_br));
src/librustc/infer/higher_ranked/mod.rs:476: fldr(region, ty::DebruijnIndex::new(current_depth))
src/librustc/infer/higher_ranked/mod.rs:754: ty::DebruijnIndex::new(current_depth - 1), br.clone()))
Metadata
Metadata
Assignees
Labels
A-trait-systemArea: Trait systemArea: Trait systemC-cleanupCategory: PRs that clean code up or issues documenting cleanup.Category: PRs that clean code up or issues documenting cleanup.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
Activity
muscar commentedon Apr 9, 2018
Hi. I can give this a go.
muscar commentedon Apr 10, 2018
Quick update: I'm currently working on this. I made the changes locally, but I'm getting some errors. I'll open a WIP PR today.
muscar commentedon Apr 10, 2018
WIP PR: #49840
nikomatsakis commentedon Apr 10, 2018
@muscar nice!
nikomatsakis commentedon May 2, 2018
I've been thinking about this. I think perhaps the way to go is to do this in some more fine-grained steps. I think we want to get to the point where the field of
DebruijnIndexis private, so the definition sort of looks like this:Moreover, right now there are two concepts at play. There is "depth", which is 0 when there are no binders in scope and 1 if we have passed through one binder, and "index", which would start at 0. In the existing definition, we kind of unify those by using depth all the time. So e.g. there are a bunch of visitors that carry a
bindersfieldrust/src/librustc/middle/resolve_lifetime.rs
Lines 1820 to 1825 in c1168be
The field starts at 0 and increments as we pass through a binder:
rust/src/librustc/middle/resolve_lifetime.rs
Lines 1833 to 1835 in c1168be
and we then compare against the
depthfield:rust/src/librustc/middle/resolve_lifetime.rs
Line 1878 in c1168be
I want to refactor those to carry not a "number of binders" as a
u32but rather aDebruijnIndex, representing an "outer" binder that was not passed through -- let's call itouter_index. It would start as the "innermost" debruijn index value (today: 1, after this issue is closed: 0). We would then increment and decrement same as today, but instead of checking ifdebruijn.depth < self.binder_depth, as in the code above, we would checkdebruijn.depth < self.outer_indexin order to see if the late-bound region is bound by one of the binders we passed through.Also, rather than directly manipulating the field and adding one, we should use helper functions. This is the
DebruijnIndexAPI I was working towards:nikomatsakis commentedon May 2, 2018
@csmoe mentioned an interest in that; @muscar, I've not heard from you in a while, so I'm assuming you got busy (which is totally fine!), but let me know otherwise!
nikomatsakis commentedon May 2, 2018
oh btw the approach I would take here is to:
DebruijnIndexstruct yet (i.e., keep field public, and keep it starting at 1)Use
./x.py checkin between iterations to ensure everything builds at each step. Then, when an error occurs, we can bisect to see why. =)Auto merge of #50475 - csmoe:debr, r=nikomatsakis