Skip to content

Commit 787f4cb

Browse files
committed
Handle uninhabited return types
This changes drop range analysis to handle uninhabited return types such as `!`. Since these calls to these functions do not return, we model them as ending in an infinite loop.
1 parent f730bd0 commit 787f4cb

File tree

4 files changed

+64
-40
lines changed

4 files changed

+64
-40
lines changed

compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,14 @@ pub fn compute_drop_ranges<'a, 'tcx>(
3636
let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body);
3737

3838
let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0);
39-
let mut drop_ranges =
40-
build_control_flow_graph(fcx.tcx.hir(), consumed_borrowed_places, body, num_exprs);
39+
let mut drop_ranges = build_control_flow_graph(
40+
fcx.tcx.hir(),
41+
fcx.tcx,
42+
&fcx.typeck_results.borrow(),
43+
consumed_borrowed_places,
44+
body,
45+
num_exprs,
46+
);
4147

4248
drop_ranges.propagate_to_fixpoint();
4349

compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use hir::{
88
};
99
use rustc_hir as hir;
1010
use rustc_index::vec::IndexVec;
11-
use rustc_middle::hir::map::Map;
11+
use rustc_middle::{
12+
hir::map::Map,
13+
ty::{TyCtxt, TypeckResults},
14+
};
1215
use std::mem::swap;
1316

1417
/// Traverses the body to find the control flow graph and locations for the
@@ -18,11 +21,14 @@ use std::mem::swap;
1821
/// can be done with propagate_to_fixpoint in cfg_propagate.
1922
pub(super) fn build_control_flow_graph<'tcx>(
2023
hir: Map<'tcx>,
24+
tcx: TyCtxt<'tcx>,
25+
typeck_results: &TypeckResults<'tcx>,
2126
consumed_borrowed_places: ConsumedAndBorrowedPlaces,
2227
body: &'tcx Body<'tcx>,
2328
num_exprs: usize,
2429
) -> DropRangesBuilder {
25-
let mut drop_range_visitor = DropRangeVisitor::new(hir, consumed_borrowed_places, num_exprs);
30+
let mut drop_range_visitor =
31+
DropRangeVisitor::new(hir, tcx, typeck_results, consumed_borrowed_places, num_exprs);
2632
intravisit::walk_body(&mut drop_range_visitor, body);
2733

2834
drop_range_visitor.drop_ranges.process_deferred_edges();
@@ -36,22 +42,30 @@ pub(super) fn build_control_flow_graph<'tcx>(
3642
/// We are interested in points where a variables is dropped or initialized, and the control flow
3743
/// of the code. We identify locations in code by their post-order traversal index, so it is
3844
/// important for this traversal to match that in `RegionResolutionVisitor` and `InteriorVisitor`.
39-
struct DropRangeVisitor<'tcx> {
45+
struct DropRangeVisitor<'a, 'tcx> {
4046
hir: Map<'tcx>,
4147
places: ConsumedAndBorrowedPlaces,
4248
drop_ranges: DropRangesBuilder,
4349
expr_index: PostOrderId,
50+
tcx: TyCtxt<'tcx>,
51+
typeck_results: &'a TypeckResults<'tcx>,
4452
}
4553

46-
impl<'tcx> DropRangeVisitor<'tcx> {
47-
fn new(hir: Map<'tcx>, places: ConsumedAndBorrowedPlaces, num_exprs: usize) -> Self {
54+
impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
55+
fn new(
56+
hir: Map<'tcx>,
57+
tcx: TyCtxt<'tcx>,
58+
typeck_results: &'a TypeckResults<'tcx>,
59+
places: ConsumedAndBorrowedPlaces,
60+
num_exprs: usize,
61+
) -> Self {
4862
debug!("consumed_places: {:?}", places.consumed);
4963
let drop_ranges = DropRangesBuilder::new(
5064
places.consumed.iter().flat_map(|(_, places)| places.iter().copied()),
5165
hir,
5266
num_exprs,
5367
);
54-
Self { hir, places, drop_ranges, expr_index: PostOrderId::from_u32(0) }
68+
Self { hir, places, drop_ranges, expr_index: PostOrderId::from_u32(0), typeck_results, tcx }
5569
}
5670

5771
fn record_drop(&mut self, hir_id: HirId) {
@@ -91,9 +105,23 @@ impl<'tcx> DropRangeVisitor<'tcx> {
91105
debug!("reinitializing {:?} is not supported", expr);
92106
}
93107
}
108+
109+
/// For an expression with an uninhabited return type (e.g. a function that returns !),
110+
/// this adds a self edge to to the CFG to model the fact that the function does not
111+
/// return.
112+
fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) {
113+
let ty = self.typeck_results.expr_ty(expr);
114+
let ty = self.tcx.erase_regions(ty);
115+
let m = self.tcx.parent_module(expr.hir_id).to_def_id();
116+
let param_env = self.tcx.param_env(m.expect_local());
117+
if self.tcx.is_ty_uninhabited_from(m, ty, param_env) {
118+
// This function will not return. We model this fact as an infinite loop.
119+
self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1);
120+
}
121+
}
94122
}
95123

96-
impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
124+
impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
97125
type Map = intravisit::ErasedMap<'tcx>;
98126

99127
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
@@ -109,6 +137,7 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
109137

110138
reinit = Some(lhs);
111139
}
140+
112141
ExprKind::If(test, if_true, if_false) => {
113142
self.visit_expr(test);
114143

@@ -155,6 +184,7 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
155184
self.drop_ranges.add_control_edge(arm_end, self.expr_index + 1)
156185
});
157186
}
187+
158188
ExprKind::Loop(body, ..) => {
159189
let loop_begin = self.expr_index + 1;
160190
if body.stmts.is_empty() && body.expr.is_none() {
@@ -172,14 +202,29 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
172202
self.drop_ranges.add_control_edge_hir_id(self.expr_index, target);
173203
}
174204

205+
ExprKind::Call(f, args) => {
206+
self.visit_expr(f);
207+
for arg in args {
208+
self.visit_expr(arg);
209+
}
210+
211+
self.handle_uninhabited_return(expr);
212+
}
213+
ExprKind::MethodCall(_, _, exprs, _) => {
214+
for expr in exprs {
215+
self.visit_expr(expr);
216+
}
217+
218+
self.handle_uninhabited_return(expr);
219+
}
220+
175221
ExprKind::AddrOf(..)
176222
| ExprKind::Array(..)
177223
| ExprKind::AssignOp(..)
178224
| ExprKind::Binary(..)
179225
| ExprKind::Block(..)
180226
| ExprKind::Box(..)
181227
| ExprKind::Break(..)
182-
| ExprKind::Call(..)
183228
| ExprKind::Cast(..)
184229
| ExprKind::Closure(..)
185230
| ExprKind::ConstBlock(..)
@@ -192,7 +237,6 @@ impl<'tcx> Visitor<'tcx> for DropRangeVisitor<'tcx> {
192237
| ExprKind::Let(..)
193238
| ExprKind::Lit(..)
194239
| ExprKind::LlvmInlineAsm(..)
195-
| ExprKind::MethodCall(..)
196240
| ExprKind::Path(..)
197241
| ExprKind::Repeat(..)
198242
| ExprKind::Ret(..)

src/test/ui/async-await/async-fn-nonsend.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ async fn non_send_temporary_in_match() {
3636
}
3737

3838
async fn non_sync_with_method_call() {
39-
// FIXME: it would be nice for this to work
4039
let f: &mut std::fmt::Formatter = panic!();
4140
if non_sync().fmt(f).unwrap() == () {
4241
fut().await;
@@ -57,6 +56,5 @@ pub fn pass_assert() {
5756
assert_send(non_send_temporary_in_match());
5857
//~^ ERROR future cannot be sent between threads safely
5958
assert_send(non_sync_with_method_call());
60-
//~^ ERROR future cannot be sent between threads safely
6159
assert_send(non_sync_with_infinite_loop());
6260
}
Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: future cannot be sent between threads safely
2-
--> $DIR/async-fn-nonsend.rs:57:17
2+
--> $DIR/async-fn-nonsend.rs:56:17
33
|
44
LL | assert_send(non_send_temporary_in_match());
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send`
@@ -16,34 +16,10 @@ LL | Some(_) => fut().await,
1616
LL | }
1717
| - `Some(non_send())` is later dropped here
1818
note: required by a bound in `assert_send`
19-
--> $DIR/async-fn-nonsend.rs:53:24
19+
--> $DIR/async-fn-nonsend.rs:52:24
2020
|
2121
LL | fn assert_send(_: impl Send) {}
2222
| ^^^^ required by this bound in `assert_send`
2323

24-
error: future cannot be sent between threads safely
25-
--> $DIR/async-fn-nonsend.rs:59:17
26-
|
27-
LL | assert_send(non_sync_with_method_call());
28-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
29-
|
30-
= help: the trait `Send` is not implemented for `dyn std::fmt::Write`
31-
note: future is not `Send` as this value is used across an await
32-
--> $DIR/async-fn-nonsend.rs:42:14
33-
|
34-
LL | let f: &mut std::fmt::Formatter = panic!();
35-
| - has type `&mut Formatter<'_>` which is not `Send`
36-
LL | if non_sync().fmt(f).unwrap() == () {
37-
LL | fut().await;
38-
| ^^^^^^ await occurs here, with `f` maybe used later
39-
LL | }
40-
LL | }
41-
| - `f` is later dropped here
42-
note: required by a bound in `assert_send`
43-
--> $DIR/async-fn-nonsend.rs:53:24
44-
|
45-
LL | fn assert_send(_: impl Send) {}
46-
| ^^^^ required by this bound in `assert_send`
47-
48-
error: aborting due to 2 previous errors
24+
error: aborting due to previous error
4925

0 commit comments

Comments
 (0)