borrowck nested items in dead code
This commit is contained in:
@@ -127,6 +127,14 @@ fn mir_borrowck(
|
|||||||
Ok(tcx.arena.alloc(opaque_types))
|
Ok(tcx.arena.alloc(opaque_types))
|
||||||
} else {
|
} else {
|
||||||
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def);
|
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def);
|
||||||
|
// We need to manually borrowck all nested bodies from the HIR as
|
||||||
|
// we do not generate MIR for dead code. Not doing so causes us to
|
||||||
|
// never check closures in dead code.
|
||||||
|
let nested_bodies = tcx.nested_bodies_within(def);
|
||||||
|
for def_id in nested_bodies {
|
||||||
|
root_cx.get_or_insert_nested(def_id);
|
||||||
|
}
|
||||||
|
|
||||||
let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
|
let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
|
||||||
do_mir_borrowck(&mut root_cx, def, None).0;
|
do_mir_borrowck(&mut root_cx, def, None).0;
|
||||||
debug_assert!(closure_requirements.is_none());
|
debug_assert!(closure_requirements.is_none());
|
||||||
|
|||||||
@@ -62,7 +62,10 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
|
|||||||
self.tainted_by_errors = Some(guar);
|
self.tainted_by_errors = Some(guar);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_or_insert_nested(&mut self, def_id: LocalDefId) -> &PropagatedBorrowCheckResults<'tcx> {
|
pub(super) fn get_or_insert_nested(
|
||||||
|
&mut self,
|
||||||
|
def_id: LocalDefId,
|
||||||
|
) -> &PropagatedBorrowCheckResults<'tcx> {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
self.tcx.typeck_root_def_id(def_id.to_def_id()),
|
self.tcx.typeck_root_def_id(def_id.to_def_id()),
|
||||||
self.root_def_id.to_def_id()
|
self.root_def_id.to_def_id()
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ rustc_queries! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query stalled_generators_within(
|
query nested_bodies_within(
|
||||||
key: LocalDefId
|
key: LocalDefId
|
||||||
) -> &'tcx ty::List<LocalDefId> {
|
) -> &'tcx ty::List<LocalDefId> {
|
||||||
desc {
|
desc {
|
||||||
|
|||||||
@@ -684,15 +684,17 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||||||
self.opaque_types_defined_by(defining_anchor)
|
self.opaque_types_defined_by(defining_anchor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opaque_types_and_generators_defined_by(
|
fn opaque_types_and_coroutines_defined_by(
|
||||||
self,
|
self,
|
||||||
defining_anchor: Self::LocalDefId,
|
defining_anchor: Self::LocalDefId,
|
||||||
) -> Self::LocalDefIds {
|
) -> Self::LocalDefIds {
|
||||||
if self.next_trait_solver_globally() {
|
if self.next_trait_solver_globally() {
|
||||||
self.mk_local_def_ids_from_iter(
|
let coroutines_defined_by = self
|
||||||
self.opaque_types_defined_by(defining_anchor)
|
.nested_bodies_within(defining_anchor)
|
||||||
.iter()
|
.iter()
|
||||||
.chain(self.stalled_generators_within(defining_anchor)),
|
.filter(|def_id| self.is_coroutine(def_id.to_def_id()));
|
||||||
|
self.mk_local_def_ids_from_iter(
|
||||||
|
self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
self.opaque_types_defined_by(defining_anchor)
|
self.opaque_types_defined_by(defining_anchor)
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ mod implied_bounds;
|
|||||||
mod instance;
|
mod instance;
|
||||||
mod layout;
|
mod layout;
|
||||||
mod needs_drop;
|
mod needs_drop;
|
||||||
|
mod nested_bodies;
|
||||||
mod opaque_types;
|
mod opaque_types;
|
||||||
mod representability;
|
mod representability;
|
||||||
pub mod sig_types;
|
pub mod sig_types;
|
||||||
mod stalled_generators;
|
|
||||||
mod structural_match;
|
mod structural_match;
|
||||||
mod ty;
|
mod ty;
|
||||||
|
|
||||||
@@ -51,5 +51,5 @@ pub fn provide(providers: &mut Providers) {
|
|||||||
ty::provide(providers);
|
ty::provide(providers);
|
||||||
instance::provide(providers);
|
instance::provide(providers);
|
||||||
structural_match::provide(providers);
|
structural_match::provide(providers);
|
||||||
stalled_generators::provide(providers);
|
nested_bodies::provide(providers);
|
||||||
}
|
}
|
||||||
|
|||||||
34
compiler/rustc_ty_utils/src/nested_bodies.rs
Normal file
34
compiler/rustc_ty_utils/src/nested_bodies.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
|
use rustc_hir::intravisit::Visitor;
|
||||||
|
use rustc_middle::query::Providers;
|
||||||
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
|
|
||||||
|
fn nested_bodies_within<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx ty::List<LocalDefId> {
|
||||||
|
let body = tcx.hir_body_owned_by(item);
|
||||||
|
let mut collector =
|
||||||
|
NestedBodiesVisitor { tcx, root_def_id: item.to_def_id(), nested_bodies: vec![] };
|
||||||
|
collector.visit_body(body);
|
||||||
|
tcx.mk_local_def_ids(&collector.nested_bodies)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NestedBodiesVisitor<'tcx> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
root_def_id: DefId,
|
||||||
|
nested_bodies: Vec<LocalDefId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Visitor<'tcx> for NestedBodiesVisitor<'tcx> {
|
||||||
|
fn visit_nested_body(&mut self, id: hir::BodyId) {
|
||||||
|
let body_def_id = self.tcx.hir_body_owner_def_id(id);
|
||||||
|
if self.tcx.typeck_root_def_id(body_def_id.to_def_id()) == self.root_def_id {
|
||||||
|
self.nested_bodies.push(body_def_id);
|
||||||
|
let body = self.tcx.hir_body(id);
|
||||||
|
self.visit_body(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn provide(providers: &mut Providers) {
|
||||||
|
*providers = Providers { nested_bodies_within, ..*providers };
|
||||||
|
}
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
use rustc_hir as hir;
|
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
|
||||||
use rustc_hir::intravisit;
|
|
||||||
use rustc_hir::intravisit::Visitor;
|
|
||||||
use rustc_middle::query::Providers;
|
|
||||||
use rustc_middle::ty::{self, TyCtxt};
|
|
||||||
|
|
||||||
fn stalled_generators_within<'tcx>(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
item: LocalDefId,
|
|
||||||
) -> &'tcx ty::List<LocalDefId> {
|
|
||||||
if !tcx.next_trait_solver_globally() {
|
|
||||||
return ty::List::empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
let body = tcx.hir_body_owned_by(item);
|
|
||||||
let mut collector =
|
|
||||||
StalledGeneratorVisitor { tcx, root_def_id: item.to_def_id(), stalled_coroutines: vec![] };
|
|
||||||
collector.visit_body(body);
|
|
||||||
tcx.mk_local_def_ids(&collector.stalled_coroutines)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StalledGeneratorVisitor<'tcx> {
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
root_def_id: DefId,
|
|
||||||
stalled_coroutines: Vec<LocalDefId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for StalledGeneratorVisitor<'tcx> {
|
|
||||||
fn visit_nested_body(&mut self, id: hir::BodyId) {
|
|
||||||
if self.tcx.typeck_root_def_id(self.tcx.hir_body_owner_def_id(id).to_def_id())
|
|
||||||
== self.root_def_id
|
|
||||||
{
|
|
||||||
let body = self.tcx.hir_body(id);
|
|
||||||
self.visit_body(body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
|
|
||||||
if let hir::ExprKind::Closure(&hir::Closure {
|
|
||||||
def_id,
|
|
||||||
kind: hir::ClosureKind::Coroutine(_),
|
|
||||||
..
|
|
||||||
}) = ex.kind
|
|
||||||
{
|
|
||||||
self.stalled_coroutines.push(def_id);
|
|
||||||
}
|
|
||||||
intravisit::walk_expr(self, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn provide(providers: &mut Providers) {
|
|
||||||
*providers = Providers { stalled_generators_within, ..*providers };
|
|
||||||
}
|
|
||||||
@@ -100,7 +100,7 @@ impl<I: Interner> TypingMode<I> {
|
|||||||
pub fn typeck_for_body(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> {
|
pub fn typeck_for_body(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> {
|
||||||
TypingMode::Analysis {
|
TypingMode::Analysis {
|
||||||
defining_opaque_types_and_generators: cx
|
defining_opaque_types_and_generators: cx
|
||||||
.opaque_types_and_generators_defined_by(body_def_id),
|
.opaque_types_and_coroutines_defined_by(body_def_id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ pub trait Interner:
|
|||||||
|
|
||||||
fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds;
|
fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds;
|
||||||
|
|
||||||
fn opaque_types_and_generators_defined_by(
|
fn opaque_types_and_coroutines_defined_by(
|
||||||
self,
|
self,
|
||||||
defining_anchor: Self::LocalDefId,
|
defining_anchor: Self::LocalDefId,
|
||||||
) -> Self::LocalDefIds;
|
) -> Self::LocalDefIds;
|
||||||
|
|||||||
28
tests/ui/nll/nested-bodies-in-dead-code.rs
Normal file
28
tests/ui/nll/nested-bodies-in-dead-code.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//@ edition: 2024
|
||||||
|
|
||||||
|
// Regression test for #140583. We want to borrowck nested
|
||||||
|
// bodies even if they are in dead code. While not necessary for
|
||||||
|
// soundness, it is desirable to error in such cases.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
return;
|
||||||
|
|x: &str| -> &'static str { x };
|
||||||
|
//~^ ERROR lifetime may not live long enough
|
||||||
|
|| {
|
||||||
|
|| {
|
||||||
|
let temp = 1;
|
||||||
|
let p: &'static u32 = &temp;
|
||||||
|
//~^ ERROR `temp` does not live long enough
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const {
|
||||||
|
let temp = 1;
|
||||||
|
let p: &'static u32 = &temp;
|
||||||
|
//~^ ERROR `temp` does not live long enough
|
||||||
|
};
|
||||||
|
async {
|
||||||
|
let temp = 1;
|
||||||
|
let p: &'static u32 = &temp;
|
||||||
|
//~^ ERROR `temp` does not live long enough
|
||||||
|
};
|
||||||
|
}
|
||||||
50
tests/ui/nll/nested-bodies-in-dead-code.stderr
Normal file
50
tests/ui/nll/nested-bodies-in-dead-code.stderr
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
error: lifetime may not live long enough
|
||||||
|
--> $DIR/nested-bodies-in-dead-code.rs:9:33
|
||||||
|
|
|
||||||
|
LL | |x: &str| -> &'static str { x };
|
||||||
|
| - ^ returning this value requires that `'1` must outlive `'static`
|
||||||
|
| |
|
||||||
|
| let's call the lifetime of this reference `'1`
|
||||||
|
|
||||||
|
error[E0597]: `temp` does not live long enough
|
||||||
|
--> $DIR/nested-bodies-in-dead-code.rs:14:35
|
||||||
|
|
|
||||||
|
LL | let temp = 1;
|
||||||
|
| ---- binding `temp` declared here
|
||||||
|
LL | let p: &'static u32 = &temp;
|
||||||
|
| ------------ ^^^^^ borrowed value does not live long enough
|
||||||
|
| |
|
||||||
|
| type annotation requires that `temp` is borrowed for `'static`
|
||||||
|
LL |
|
||||||
|
LL | };
|
||||||
|
| - `temp` dropped here while still borrowed
|
||||||
|
|
||||||
|
error[E0597]: `temp` does not live long enough
|
||||||
|
--> $DIR/nested-bodies-in-dead-code.rs:20:31
|
||||||
|
|
|
||||||
|
LL | let temp = 1;
|
||||||
|
| ---- binding `temp` declared here
|
||||||
|
LL | let p: &'static u32 = &temp;
|
||||||
|
| ------------ ^^^^^ borrowed value does not live long enough
|
||||||
|
| |
|
||||||
|
| type annotation requires that `temp` is borrowed for `'static`
|
||||||
|
LL |
|
||||||
|
LL | };
|
||||||
|
| - `temp` dropped here while still borrowed
|
||||||
|
|
||||||
|
error[E0597]: `temp` does not live long enough
|
||||||
|
--> $DIR/nested-bodies-in-dead-code.rs:25:31
|
||||||
|
|
|
||||||
|
LL | let temp = 1;
|
||||||
|
| ---- binding `temp` declared here
|
||||||
|
LL | let p: &'static u32 = &temp;
|
||||||
|
| ------------ ^^^^^ borrowed value does not live long enough
|
||||||
|
| |
|
||||||
|
| type annotation requires that `temp` is borrowed for `'static`
|
||||||
|
LL |
|
||||||
|
LL | };
|
||||||
|
| - `temp` dropped here while still borrowed
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0597`.
|
||||||
Reference in New Issue
Block a user