Rollup merge of #103780 - compiler-errors:bound-closure-lifetimes, r=jackh726

Fix late-bound lifetime closure ICEs in HIR typeck and MIR borrowck

During HIR typeck, we need to teach astconv to treat late-bound regions within a closure body as free, fixing escaping bound vars ICEs in both of the issues below.

However, this then gets us to MIR borrowck, which itself needs to be taught how to instantiate free region vids for late-bound regions that come from items that _aren't_ the typeck root (for now, just closures).

Fixes #103771
Fixes #103736
This commit is contained in:
Matthias Krüger 2022-11-04 12:18:01 +01:00 committed by GitHub
commit d10187f040
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 161 additions and 38 deletions

View File

@ -2314,7 +2314,7 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx
tcx,
closure_substs,
self.num_external_vids,
tcx.typeck_root_def_id(closure_def_id),
closure_def_id.expect_local(),
);
debug!("apply_requirements: closure_mapping={:?}", closure_mapping);

View File

@ -22,7 +22,9 @@ use rustc_hir::{BodyOwnerKind, HirId};
use rustc_index::vec::{Idx, IndexVec};
use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt};
use rustc_middle::ty::{
self, DefIdTree, InlineConstSubsts, InlineConstSubstsParts, RegionVid, Ty, TyCtxt,
};
use rustc_middle::ty::{InternalSubsts, SubstsRef};
use std::iter;
@ -241,7 +243,7 @@ impl<'tcx> UniversalRegions<'tcx> {
tcx: TyCtxt<'tcx>,
closure_substs: SubstsRef<'tcx>,
expected_num_vars: usize,
typeck_root_def_id: DefId,
closure_def_id: LocalDefId,
) -> IndexVec<RegionVid, ty::Region<'tcx>> {
let mut region_mapping = IndexVec::with_capacity(expected_num_vars);
region_mapping.push(tcx.lifetimes.re_static);
@ -249,7 +251,7 @@ impl<'tcx> UniversalRegions<'tcx> {
region_mapping.push(fr);
});
for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| {
for_each_late_bound_region_in_recursive_scope(tcx, tcx.local_parent(closure_def_id), |r| {
region_mapping.push(r);
});
@ -339,9 +341,8 @@ impl<'tcx> UniversalRegions<'tcx> {
// tests, and the resulting print-outs include def-ids
// and other things that are not stable across tests!
// So we just include the region-vid. Annoying.
let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| {
err.note(&format!("late-bound region is {:?}", self.to_region_vid(r),));
for_each_late_bound_region_in_recursive_scope(tcx, def_id.expect_local(), |r| {
err.note(&format!("late-bound region is {:?}", self.to_region_vid(r)));
});
}
DefiningTy::Generator(def_id, substs, _) => {
@ -354,9 +355,8 @@ impl<'tcx> UniversalRegions<'tcx> {
// FIXME: As above, we'd like to print out the region
// `r` but doing so is not stable across architectures
// and so forth.
let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
for_each_late_bound_region_defined_on(tcx, typeck_root_def_id, |r| {
err.note(&format!("late-bound region is {:?}", self.to_region_vid(r),));
for_each_late_bound_region_in_recursive_scope(tcx, def_id.expect_local(), |r| {
err.note(&format!("late-bound region is {:?}", self.to_region_vid(r)));
});
}
DefiningTy::FnDef(def_id, substs) => {
@ -421,13 +421,24 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
first_extern_index
} else {
// If this is a closure, generator, or inline-const, then the late-bound regions from the enclosing
// function are actually external regions to us. For example, here, 'a is not local
// function/closures are actually external regions to us. For example, here, 'a is not local
// to the closure c (although it is local to the fn foo):
// fn foo<'a>() {
// let c = || { let x: &'a u32 = ...; }
// }
self.infcx
.replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices);
for_each_late_bound_region_in_recursive_scope(
self.infcx.tcx,
self.infcx.tcx.local_parent(self.mir_def.did),
|r| {
debug!(?r);
if !indices.indices.contains_key(&r) {
let region_vid = self.infcx.next_nll_region_var(FR);
debug!(?region_vid);
indices.insert_late_bound_region(r, region_vid.to_region_vid());
}
},
);
// Any regions created during the execution of `defining_ty` or during the above
// late-bound region replacement are all considered 'extern' regions
self.infcx.num_region_vars()
@ -444,12 +455,16 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
bound_inputs_and_output,
&mut indices,
);
// Converse of above, if this is a function then the late-bound regions declared on its
// signature are local to the fn.
if self.mir_def.did.to_def_id() == typeck_root_def_id {
self.infcx
.replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices);
}
// Converse of above, if this is a function/closure then the late-bound regions declared on its
// signature are local.
for_each_late_bound_region_in_item(self.infcx.tcx, self.mir_def.did, |r| {
debug!(?r);
if !indices.indices.contains_key(&r) {
let region_vid = self.infcx.next_nll_region_var(FR);
debug!(?region_vid);
indices.insert_late_bound_region(r, region_vid.to_region_vid());
}
});
let (unnormalized_output_ty, mut unnormalized_input_tys) =
inputs_and_output.split_last().unwrap();
@ -692,7 +707,13 @@ trait InferCtxtExt<'tcx> {
where
T: TypeFoldable<'tcx>;
fn replace_late_bound_regions_with_nll_infer_vars(
fn replace_late_bound_regions_with_nll_infer_vars_in_recursive_scope(
&self,
mir_def_id: LocalDefId,
indices: &mut UniversalRegionIndices<'tcx>,
);
fn replace_late_bound_regions_with_nll_infer_vars_in_item(
&self,
mir_def_id: LocalDefId,
indices: &mut UniversalRegionIndices<'tcx>,
@ -746,13 +767,28 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
/// set of late-bound regions and checks for any that we have not yet seen, adding them to the
/// inputs vector.
#[instrument(skip(self, indices))]
fn replace_late_bound_regions_with_nll_infer_vars(
fn replace_late_bound_regions_with_nll_infer_vars_in_recursive_scope(
&self,
mir_def_id: LocalDefId,
indices: &mut UniversalRegionIndices<'tcx>,
) {
let typeck_root_def_id = self.tcx.typeck_root_def_id(mir_def_id.to_def_id());
for_each_late_bound_region_defined_on(self.tcx, typeck_root_def_id, |r| {
for_each_late_bound_region_in_recursive_scope(self.tcx, mir_def_id, |r| {
debug!(?r);
if !indices.indices.contains_key(&r) {
let region_vid = self.next_nll_region_var(FR);
debug!(?region_vid);
indices.insert_late_bound_region(r, region_vid.to_region_vid());
}
});
}
#[instrument(skip(self, indices))]
fn replace_late_bound_regions_with_nll_infer_vars_in_item(
&self,
mir_def_id: LocalDefId,
indices: &mut UniversalRegionIndices<'tcx>,
) {
for_each_late_bound_region_in_item(self.tcx, mir_def_id, |r| {
debug!(?r);
if !indices.indices.contains_key(&r) {
let region_vid = self.next_nll_region_var(FR);
@ -803,21 +839,44 @@ impl<'tcx> UniversalRegionIndices<'tcx> {
}
}
/// Iterates over the late-bound regions defined on fn_def_id and
/// invokes `f` with the liberated form of each one.
fn for_each_late_bound_region_defined_on<'tcx>(
/// Iterates over the late-bound regions defined on `mir_def_id` and all of its
/// parents, up to the typeck root, and invokes `f` with the liberated form
/// of each one.
fn for_each_late_bound_region_in_recursive_scope<'tcx>(
tcx: TyCtxt<'tcx>,
fn_def_id: DefId,
mut mir_def_id: LocalDefId,
mut f: impl FnMut(ty::Region<'tcx>),
) {
if let Some(late_bounds) = tcx.is_late_bound_map(fn_def_id.expect_local()) {
for &region_def_id in late_bounds.iter() {
let name = tcx.item_name(region_def_id.to_def_id());
let liberated_region = tcx.mk_region(ty::ReFree(ty::FreeRegion {
scope: fn_def_id,
bound_region: ty::BoundRegionKind::BrNamed(region_def_id.to_def_id(), name),
}));
f(liberated_region);
let typeck_root_def_id = tcx.typeck_root_def_id(mir_def_id.to_def_id());
// Walk up the tree, collecting late-bound regions until we hit the typeck root
loop {
for_each_late_bound_region_in_item(tcx, mir_def_id, &mut f);
if mir_def_id.to_def_id() == typeck_root_def_id {
break;
} else {
mir_def_id = tcx.local_parent(mir_def_id);
}
}
}
/// Iterates over the late-bound regions defined on `mir_def_id` and all of its
/// parents, up to the typeck root, and invokes `f` with the liberated form
/// of each one.
fn for_each_late_bound_region_in_item<'tcx>(
tcx: TyCtxt<'tcx>,
mir_def_id: LocalDefId,
mut f: impl FnMut(ty::Region<'tcx>),
) {
if !tcx.def_kind(mir_def_id).is_fn_like() {
return;
}
for bound_var in tcx.late_bound_vars(tcx.hir().local_def_id_to_hir_id(mir_def_id)) {
let ty::BoundVariableKind::Region(bound_region) = bound_var else { continue; };
let liberated_region = tcx
.mk_region(ty::ReFree(ty::FreeRegion { scope: mir_def_id.to_def_id(), bound_region }));
f(liberated_region);
}
}

View File

@ -1377,11 +1377,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
} else if let Some(body_id) = outermost_body {
let fn_id = self.tcx.hir().body_owner(body_id);
match self.tcx.hir().get(fn_id) {
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. })
| Node::TraitItem(&hir::TraitItem {
Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })
| Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Fn(..), ..
})
| Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => {
| Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. })
| Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => {
let scope = self.tcx.hir().local_def_id(fn_id);
def = Region::Free(scope.to_def_id(), def.id().unwrap());
}

View File

@ -0,0 +1,9 @@
// check-pass
#![feature(closure_lifetime_binder)]
fn main() {
let _ = for<'a> || -> () {
let _: &'a bool = &true;
};
}

View File

@ -0,0 +1,9 @@
// check-pass
#![feature(closure_lifetime_binder)]
#![feature(rustc_attrs)]
#[rustc_regions]
fn main() {
for<'a> || -> () { for<'c> |_: &'a ()| -> () {}; };
}

View File

@ -0,0 +1,38 @@
note: external requirements
--> $DIR/nested-closures-regions.rs:8:24
|
LL | for<'a> || -> () { for<'c> |_: &'a ()| -> () {}; };
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: defining type: main::{closure#0}::{closure#0} with closure substs [
i8,
extern "rust-call" fn((&(),)),
(),
]
= note: late-bound region is '_#4r
= note: late-bound region is '_#2r
= note: number of external vids: 3
= note: where '_#1r: '_#2r
= note: where '_#2r: '_#1r
note: no external requirements
--> $DIR/nested-closures-regions.rs:8:5
|
LL | for<'a> || -> () { for<'c> |_: &'a ()| -> () {}; };
| ^^^^^^^^^^^^^^^^
|
= note: defining type: main::{closure#0} with closure substs [
i8,
extern "rust-call" fn(()),
(),
]
= note: late-bound region is '_#2r
note: no external requirements
--> $DIR/nested-closures-regions.rs:7:1
|
LL | fn main() {
| ^^^^^^^^^
|
= note: defining type: main

View File

@ -0,0 +1,7 @@
// check-pass
#![feature(closure_lifetime_binder)]
fn main() {
for<'a> || -> () { for<'c> |_: &'a ()| -> () {}; };
}