Skip to content

Commit 988137c

Browse files
authored
Rollup merge of #135377 - compiler-errors:impossible-step, r=oli-obk
Make MIR cleanup for functions with impossible predicates into a real MIR pass It's a bit jarring to see the body of a function with an impossible-to-satisfy where clause suddenly go to a single `unreachable` terminator when looking at the MIR dump output in order, and I discovered it's because we manually replace the body outside of a MIR pass. Let's make it into a fully flegded MIR pass so it's more clear what it's doing and when it's being applied.
2 parents 55503a1 + f1d6226 commit 988137c

File tree

4 files changed

+99
-46
lines changed

4 files changed

+99
-46
lines changed
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//! Check if it's even possible to satisfy the 'where' clauses
2+
//! for this item.
3+
//!
4+
//! It's possible to `#!feature(trivial_bounds)]` to write
5+
//! a function with impossible to satisfy clauses, e.g.:
6+
//! `fn foo() where String: Copy {}`.
7+
//!
8+
//! We don't usually need to worry about this kind of case,
9+
//! since we would get a compilation error if the user tried
10+
//! to call it. However, since we optimize even without any
11+
//! calls to the function, we need to make sure that it even
12+
//! makes sense to try to evaluate the body.
13+
//!
14+
//! If there are unsatisfiable where clauses, then all bets are
15+
//! off, and we just give up.
16+
//!
17+
//! We manually filter the predicates, skipping anything that's not
18+
//! "global". We are in a potentially generic context
19+
//! (e.g. we are evaluating a function without instantiating generic
20+
//! parameters, so this filtering serves two purposes:
21+
//!
22+
//! 1. We skip evaluating any predicates that we would
23+
//! never be able prove are unsatisfiable (e.g. `<T as Foo>`
24+
//! 2. We avoid trying to normalize predicates involving generic
25+
//! parameters (e.g. `<T as Foo>::MyItem`). This can confuse
26+
//! the normalization code (leading to cycle errors), since
27+
//! it's usually never invoked in this way.
28+
29+
use rustc_middle::mir::{Body, START_BLOCK, TerminatorKind};
30+
use rustc_middle::ty::{TyCtxt, TypeVisitableExt};
31+
use rustc_trait_selection::traits;
32+
use tracing::trace;
33+
34+
use crate::pass_manager::MirPass;
35+
36+
pub(crate) struct ImpossiblePredicates;
37+
38+
impl<'tcx> MirPass<'tcx> for ImpossiblePredicates {
39+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
40+
let predicates = tcx
41+
.predicates_of(body.source.def_id())
42+
.predicates
43+
.iter()
44+
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
45+
if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
46+
trace!("found unsatisfiable predicates for {:?}", body.source);
47+
// Clear the body to only contain a single `unreachable` statement.
48+
let bbs = body.basic_blocks.as_mut();
49+
bbs.raw.truncate(1);
50+
bbs[START_BLOCK].statements.clear();
51+
bbs[START_BLOCK].terminator_mut().kind = TerminatorKind::Unreachable;
52+
body.var_debug_info.clear();
53+
body.local_decls.raw.truncate(body.arg_count + 1);
54+
}
55+
}
56+
}

Diff for: compiler/rustc_mir_transform/src/lib.rs

+3-46
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ use rustc_middle::util::Providers;
3434
use rustc_middle::{bug, query, span_bug};
3535
use rustc_span::source_map::Spanned;
3636
use rustc_span::{DUMMY_SP, sym};
37-
use rustc_trait_selection::traits;
38-
use tracing::{debug, trace};
37+
use tracing::debug;
3938

4039
#[macro_use]
4140
mod pass_manager;
@@ -142,6 +141,7 @@ declare_passes! {
142141
// Made public so that `mir_drops_elaborated_and_const_checked` can be overridden
143142
// by custom rustc drivers, running all the steps by themselves. See #114628.
144143
pub mod inline : Inline, ForceInline;
144+
mod impossible_predicates : ImpossiblePredicates;
145145
mod instsimplify : InstSimplify { BeforeInline, AfterSimplifyCfg };
146146
mod jump_threading : JumpThreading;
147147
mod known_panics_lint : KnownPanicsLint;
@@ -502,50 +502,6 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
502502
body.tainted_by_errors = Some(error_reported);
503503
}
504504

505-
// Check if it's even possible to satisfy the 'where' clauses
506-
// for this item.
507-
//
508-
// This branch will never be taken for any normal function.
509-
// However, it's possible to `#!feature(trivial_bounds)]` to write
510-
// a function with impossible to satisfy clauses, e.g.:
511-
// `fn foo() where String: Copy {}`
512-
//
513-
// We don't usually need to worry about this kind of case,
514-
// since we would get a compilation error if the user tried
515-
// to call it. However, since we optimize even without any
516-
// calls to the function, we need to make sure that it even
517-
// makes sense to try to evaluate the body.
518-
//
519-
// If there are unsatisfiable where clauses, then all bets are
520-
// off, and we just give up.
521-
//
522-
// We manually filter the predicates, skipping anything that's not
523-
// "global". We are in a potentially generic context
524-
// (e.g. we are evaluating a function without instantiating generic
525-
// parameters, so this filtering serves two purposes:
526-
//
527-
// 1. We skip evaluating any predicates that we would
528-
// never be able prove are unsatisfiable (e.g. `<T as Foo>`
529-
// 2. We avoid trying to normalize predicates involving generic
530-
// parameters (e.g. `<T as Foo>::MyItem`). This can confuse
531-
// the normalization code (leading to cycle errors), since
532-
// it's usually never invoked in this way.
533-
let predicates = tcx
534-
.predicates_of(body.source.def_id())
535-
.predicates
536-
.iter()
537-
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
538-
if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
539-
trace!("found unsatisfiable predicates for {:?}", body.source);
540-
// Clear the body to only contain a single `unreachable` statement.
541-
let bbs = body.basic_blocks.as_mut();
542-
bbs.raw.truncate(1);
543-
bbs[START_BLOCK].statements.clear();
544-
bbs[START_BLOCK].terminator_mut().kind = TerminatorKind::Unreachable;
545-
body.var_debug_info.clear();
546-
body.local_decls.raw.truncate(body.arg_count + 1);
547-
}
548-
549505
run_analysis_to_runtime_passes(tcx, &mut body);
550506

551507
// Now that drop elaboration has been performed, we can check for
@@ -593,6 +549,7 @@ pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'
593549
/// After this series of passes, no lifetime analysis based on borrowing can be done.
594550
fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
595551
let passes: &[&dyn MirPass<'tcx>] = &[
552+
&impossible_predicates::ImpossiblePredicates,
596553
&cleanup_post_borrowck::CleanupPostBorrowck,
597554
&remove_noop_landing_pads::RemoveNoopLandingPads,
598555
&simplify::SimplifyCfg::PostAnalysis,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
- // MIR for `impossible_predicate` before ImpossiblePredicates
2+
+ // MIR for `impossible_predicate` after ImpossiblePredicates
3+
4+
fn impossible_predicate(_1: &mut i32) -> (&mut i32, &mut i32) {
5+
- debug x => _1;
6+
let mut _0: (&mut i32, &mut i32);
7+
- let _2: &mut i32;
8+
- let mut _3: &mut i32;
9+
- let mut _4: &mut i32;
10+
scope 1 {
11+
- debug y => _2;
12+
}
13+
14+
bb0: {
15+
- StorageLive(_2);
16+
- _2 = copy _1;
17+
- FakeRead(ForLet(None), _2);
18+
- StorageLive(_3);
19+
- _3 = &mut (*_2);
20+
- StorageLive(_4);
21+
- _4 = &mut (*_1);
22+
- _0 = (move _3, move _4);
23+
- StorageDead(_4);
24+
- StorageDead(_3);
25+
- StorageDead(_2);
26+
- return;
27+
+ unreachable;
28+
}
29+
}
30+

Diff for: tests/mir-opt/impossible_predicates.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// skip-filecheck
2+
// EMIT_MIR impossible_predicates.impossible_predicate.ImpossiblePredicates.diff
3+
4+
pub fn impossible_predicate(x: &mut i32) -> (&mut i32, &mut i32)
5+
where
6+
for<'a> &'a mut i32: Copy,
7+
{
8+
let y = x;
9+
(y, x)
10+
}

0 commit comments

Comments
 (0)