Skip to content

Commit

Permalink
Rollup merge of #104753 - compiler-errors:drop-tracking-var-ice, r=ol…
Browse files Browse the repository at this point in the history
…i-obk

Pass `InferCtxt` to `DropRangeVisitor` so we can resolve vars

The types that we encounter in the  `TypeckResults` that we pass to the `DropRangeVisitor` are not yet fully resolved, since that only happens in writeback after type checking is complete.

Instead, pass down the whole `InferCtxt` so that we can resolve any inference vars that have been constrained since they were written into the results. This is similar to how the `MemCategorizationContext` in the `ExprUseVisitor` also needs to pass down both typeck results _and_ the inference context.

Fixes an ICE mentioned in this comment: #104382 (comment)
  • Loading branch information
matthiaskrgr committed Nov 24, 2022
2 parents c08c57e + 024bb8c commit 6938717
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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::{ParamEnv, TyCtxt, TypeVisitable, TypeckResults},
};
use std::mem::swap;

Expand All @@ -21,20 +22,29 @@ 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>,
param_env: ParamEnv<'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);
let mut drop_range_visitor = DropRangeVisitor::new(
infcx,
typeck_results,
param_env,
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)
Expand Down Expand Up @@ -82,40 +92,44 @@ pub(super) fn build_control_flow_graph<'tcx>(
/// ```

struct DropRangeVisitor<'a, 'tcx> {
hir: Map<'tcx>,
typeck_results: &'a TypeckResults<'tcx>,
infcx: &'a InferCtxt<'tcx>,
param_env: ParamEnv<'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>,
param_env: ParamEnv<'tcx>,
places: ConsumedAndBorrowedPlaces,
num_exprs: usize,
) -> Self {
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,
param_env,
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);
Expand All @@ -137,7 +151,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));
}
}

Expand Down Expand Up @@ -214,10 +228,15 @@ 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);
if ty.has_non_region_infer() {
self.tcx()
.sess
.delay_span_bug(expr.span, format!("could not resolve infer vars in `{ty}`"));
}
let ty = self.tcx().erase_regions(ty);
let m = self.tcx().parent_module(expr.hir_id).to_def_id();
if !ty.is_inhabited_from(self.tcx(), m, self.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);
}
Expand All @@ -238,7 +257,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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ 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,
fcx.param_env,
consumed_borrowed_places,
body,
num_exprs,
Expand Down
106 changes: 106 additions & 0 deletions src/test/ui/async-await/drop-tracking-unresolved-typeck-results.rs
Original file line number Diff line number Diff line change
@@ -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
});
}
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 6938717

Please sign in to comment.