1
Fork 0

Pass InferCtxt to DropRangeVisitor so we can resolve vars

This commit is contained in:
Michael Goulet 2022-11-23 01:47:56 +00:00
parent 66ccf36f16
commit 1e7f6a7e0d
4 changed files with 201 additions and 23 deletions

View file

@ -9,9 +9,10 @@ use hir::{
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_index::vec::IndexVec;
use rustc_infer::infer::InferCtxt;
use rustc_middle::{
hir::map::Map,
ty::{TyCtxt, TypeckResults},
ty::{TyCtxt, TypeVisitable, TypeckResults},
};
use std::mem::swap;
@ -21,20 +22,23 @@ use std::mem::swap;
/// The resulting structure still needs to be iterated to a fixed point, which
/// can be done with propagate_to_fixpoint in cfg_propagate.
pub(super) fn build_control_flow_graph<'tcx>(
hir: Map<'tcx>,
tcx: TyCtxt<'tcx>,
infcx: &InferCtxt<'tcx>,
typeck_results: &TypeckResults<'tcx>,
consumed_borrowed_places: ConsumedAndBorrowedPlaces,
body: &'tcx Body<'tcx>,
num_exprs: usize,
) -> (DropRangesBuilder, FxHashSet<HirId>) {
let mut drop_range_visitor =
DropRangeVisitor::new(hir, tcx, typeck_results, consumed_borrowed_places, num_exprs);
DropRangeVisitor::new(infcx, typeck_results, consumed_borrowed_places, num_exprs);
intravisit::walk_body(&mut drop_range_visitor, body);
drop_range_visitor.drop_ranges.process_deferred_edges();
if let Some(filename) = &tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg {
super::cfg_visualize::write_graph_to_file(&drop_range_visitor.drop_ranges, filename, tcx);
if let Some(filename) = &infcx.tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg {
super::cfg_visualize::write_graph_to_file(
&drop_range_visitor.drop_ranges,
filename,
infcx.tcx,
);
}
(drop_range_visitor.drop_ranges, drop_range_visitor.places.borrowed_temporaries)
@ -82,19 +86,17 @@ pub(super) fn build_control_flow_graph<'tcx>(
/// ```
struct DropRangeVisitor<'a, 'tcx> {
hir: Map<'tcx>,
typeck_results: &'a TypeckResults<'tcx>,
infcx: &'a InferCtxt<'tcx>,
places: ConsumedAndBorrowedPlaces,
drop_ranges: DropRangesBuilder,
expr_index: PostOrderId,
tcx: TyCtxt<'tcx>,
typeck_results: &'a TypeckResults<'tcx>,
label_stack: Vec<(Option<rustc_ast::Label>, PostOrderId)>,
}
impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
fn new(
hir: Map<'tcx>,
tcx: TyCtxt<'tcx>,
infcx: &'a InferCtxt<'tcx>,
typeck_results: &'a TypeckResults<'tcx>,
places: ConsumedAndBorrowedPlaces,
num_exprs: usize,
@ -102,20 +104,23 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
debug!("consumed_places: {:?}", places.consumed);
let drop_ranges = DropRangesBuilder::new(
places.consumed.iter().flat_map(|(_, places)| places.iter().cloned()),
hir,
infcx.tcx.hir(),
num_exprs,
);
Self {
hir,
infcx,
typeck_results,
places,
drop_ranges,
expr_index: PostOrderId::from_u32(0),
typeck_results,
tcx,
label_stack: vec![],
}
}
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
fn record_drop(&mut self, value: TrackedValue) {
if self.places.borrowed.contains(&value) {
debug!("not marking {:?} as dropped because it is borrowed at some point", value);
@ -137,7 +142,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
.map_or(vec![], |places| places.iter().cloned().collect());
for place in places {
trace!(?place, "consuming place");
for_each_consumable(self.hir, place, |value| self.record_drop(value));
for_each_consumable(self.tcx().hir(), place, |value| self.record_drop(value));
}
}
@ -214,10 +219,16 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
/// return.
fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) {
let ty = self.typeck_results.expr_ty(expr);
let ty = self.tcx.erase_regions(ty);
let m = self.tcx.parent_module(expr.hir_id).to_def_id();
let param_env = self.tcx.param_env(m.expect_local());
if !ty.is_inhabited_from(self.tcx, m, param_env) {
let ty = self.infcx.resolve_vars_if_possible(ty);
let ty = self.tcx().erase_regions(ty);
let m = self.tcx().parent_module(expr.hir_id).to_def_id();
let param_env = self.tcx().param_env(m.expect_local());
if ty.has_non_region_infer() {
self.tcx()
.sess
.delay_span_bug(expr.span, format!("could not resolve infer vars in `{ty}`"));
}
if !ty.is_inhabited_from(self.tcx(), m, param_env) {
// This function will not return. We model this fact as an infinite loop.
self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1);
}
@ -238,7 +249,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
destination: hir::Destination,
) -> Result<HirId, LoopIdError> {
destination.target_id.map(|target| {
let node = self.hir.get(target);
let node = self.tcx().hir().get(target);
match node {
hir::Node::Expr(_) => target,
hir::Node::Block(b) => find_last_block_expression(b),

View file

@ -43,8 +43,7 @@ pub fn compute_drop_ranges<'a, 'tcx>(
let typeck_results = &fcx.typeck_results.borrow();
let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0);
let (mut drop_ranges, borrowed_temporaries) = build_control_flow_graph(
fcx.tcx.hir(),
fcx.tcx,
&fcx,
typeck_results,
consumed_borrowed_places,
body,

View file

@ -0,0 +1,106 @@
// compile-flags: -Zdrop-tracking
// incremental
// edition: 2021
use std::future::*;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::*;
fn send<T: Send>(_: T) {}
pub trait Stream {
type Item;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
}
struct Empty<T>(PhantomData<fn() -> T>);
impl<T> Stream for Empty<T> {
type Item = T;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
todo!()
}
}
pub trait FnOnce1<A> {
type Output;
fn call_once(self, arg: A) -> Self::Output;
}
impl<T, A, R> FnOnce1<A> for T
where
T: FnOnce(A) -> R,
{
type Output = R;
fn call_once(self, arg: A) -> R {
self(arg)
}
}
pub trait FnMut1<A>: FnOnce1<A> {
fn call_mut(&mut self, arg: A) -> Self::Output;
}
impl<T, A, R> FnMut1<A> for T
where
T: FnMut(A) -> R,
{
fn call_mut(&mut self, arg: A) -> R {
self(arg)
}
}
struct Map<St, F>(St, F);
impl<St, F> Stream for Map<St, F>
where
St: Stream,
F: FnMut1<St::Item>,
{
type Item = F::Output;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
todo!()
}
}
struct FuturesOrdered<T: Future>(PhantomData<fn() -> T::Output>);
pub struct Buffered<St: Stream>(St, FuturesOrdered<St::Item>, usize)
where
St::Item: Future;
impl<St> Stream for Buffered<St>
where
St: Stream,
St::Item: Future,
{
type Item = <St::Item as Future>::Output;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
todo!()
}
}
struct Next<'a, T: ?Sized>(&'a T);
impl<St: ?Sized + Stream + Unpin> Future for Next<'_, St> {
type Output = Option<St::Item>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
todo!()
}
}
fn main() {
send(async {
//~^ ERROR implementation of `FnOnce` is not general enough
//~| ERROR implementation of `FnOnce` is not general enough
//~| ERROR implementation of `FnOnce` is not general enough
//~| ERROR implementation of `FnOnce` is not general enough
Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
});
}

View file

@ -0,0 +1,62 @@
error: implementation of `FnOnce` is not general enough
--> $DIR/drop-tracking-unresolved-typeck-results.rs:99:5
|
LL | / send(async {
LL | |
LL | |
LL | |
LL | |
LL | | Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
LL | | });
| |______^ implementation of `FnOnce` is not general enough
|
= note: `fn(&'0 ()) -> std::future::Ready<&'0 ()> {std::future::ready::<&'0 ()>}` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&(),)>`
error: implementation of `FnOnce` is not general enough
--> $DIR/drop-tracking-unresolved-typeck-results.rs:99:5
|
LL | / send(async {
LL | |
LL | |
LL | |
LL | |
LL | | Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
LL | | });
| |______^ implementation of `FnOnce` is not general enough
|
= note: `fn(&'0 ()) -> std::future::Ready<&'0 ()> {std::future::ready::<&'0 ()>}` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&(),)>`
error: implementation of `FnOnce` is not general enough
--> $DIR/drop-tracking-unresolved-typeck-results.rs:99:5
|
LL | / send(async {
LL | |
LL | |
LL | |
LL | |
LL | | Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
LL | | });
| |______^ implementation of `FnOnce` is not general enough
|
= note: `fn(&'0 ()) -> std::future::Ready<&'0 ()> {std::future::ready::<&'0 ()>}` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&(),)>`
error: implementation of `FnOnce` is not general enough
--> $DIR/drop-tracking-unresolved-typeck-results.rs:99:5
|
LL | / send(async {
LL | |
LL | |
LL | |
LL | |
LL | | Next(&Buffered(Map(Empty(PhantomData), ready::<&()>), FuturesOrdered(PhantomData), 0)).await
LL | | });
| |______^ implementation of `FnOnce` is not general enough
|
= note: `fn(&'0 ()) -> std::future::Ready<&'0 ()> {std::future::ready::<&'0 ()>}` must implement `FnOnce<(&'1 (),)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&(),)>`
error: aborting due to 4 previous errors