Skip to content

Commit

Permalink
Don't evaluate predicates twice to check for impossible predicates
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Feb 8, 2024
1 parent 9e69204 commit f271893
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 41 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/query/erase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ trivial! {
Option<rustc_target::spec::PanicStrategy>,
Option<usize>,
Result<(), rustc_errors::ErrorGuaranteed>,
Result<(), traits::util::HasImpossiblePredicates>,
Result<(), rustc_middle::traits::query::NoSolution>,
Result<rustc_middle::traits::EvaluationResult, rustc_middle::traits::OverflowError>,
rustc_ast::expand::allocator::AllocatorKind,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use crate::traits::query::{
OutlivesBound,
};
use crate::traits::specialization_graph;
use crate::traits::util::HasImpossiblePredicates;
use crate::traits::{
CodegenObligationError, EvaluationResult, ImplSource, ObjectSafetyViolation, ObligationCause,
OverflowError, WellFormedLoc,
Expand Down Expand Up @@ -1019,7 +1020,7 @@ rustc_queries! {
}

/// Run the const prop lints on the `mir_promoted` of an item.
query const_prop_lint(key: LocalDefId) {
query const_prop_lint(key: LocalDefId) -> Result<(), HasImpossiblePredicates> {
desc { |tcx| "checking const prop lints for `{}`", tcx.def_path_str(key) }
cache_on_disk_if { true }
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ impl<'tcx> Iterator for Elaborator<'tcx> {
}
}
}

/// Used as an error type to signal that an item may have an invalid body, because its
/// where bounds are trivially not satisfyable.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[derive(HashStable, Encodable, Decodable)]
pub struct HasImpossiblePredicates;
76 changes: 36 additions & 40 deletions compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use rustc_middle::mir::{
SourceInfo, Statement, StatementKind, TerminatorKind, START_BLOCK,
};
use rustc_middle::query::Providers;
use rustc_middle::traits::util::HasImpossiblePredicates;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_span::{source_map::Spanned, sym, DUMMY_SP};
use rustc_trait_selection::traits;
Expand Down Expand Up @@ -399,51 +400,12 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
body
}

fn const_prop_lint(tcx: TyCtxt<'_>, def: LocalDefId) {
fn const_prop_lint(tcx: TyCtxt<'_>, def: LocalDefId) -> Result<(), HasImpossiblePredicates> {
let (body, _) = tcx.mir_promoted(def);
let body = body.borrow();

let mir_borrowck = tcx.mir_borrowck(def);

// If there are impossible bounds on the body being const prop linted,
// the const eval logic used in const prop may ICE unexpectedly.
let predicates = tcx
.predicates_of(body.source.def_id())
.predicates
.iter()
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
if !traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect())
&& mir_borrowck.tainted_by_errors.is_none()
&& body.tainted_by_errors.is_none()
{
const_prop_lint::ConstPropLint.run_lint(tcx, &body);
}
}

/// Obtain just the main MIR (no promoteds) and run some cleanups on it. This also runs
/// mir borrowck *before* doing so in order to ensure that borrowck can be run and doesn't
/// end up missing the source MIR due to stealing happening.
fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
if tcx.is_coroutine(def.to_def_id()) {
tcx.ensure_with_value().mir_coroutine_witnesses(def);
}
let mir_borrowck = tcx.mir_borrowck(def);
tcx.ensure().const_prop_lint(def);

let is_fn_like = tcx.def_kind(def).is_fn_like();
if is_fn_like {
// Do not compute the mir call graph without said call graph actually being used.
if pm::should_run_pass(tcx, &inline::Inline) {
tcx.ensure_with_value().mir_inliner_callees(ty::InstanceDef::Item(def.to_def_id()));
}
}

let (body, _) = tcx.mir_promoted(def);
let mut body = body.steal();
if let Some(error_reported) = mir_borrowck.tainted_by_errors {
body.tainted_by_errors = Some(error_reported);
}

// Check if it's even possible to satisfy the 'where' clauses
// for this item.
//
Expand Down Expand Up @@ -478,6 +440,40 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
.iter()
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
Err(HasImpossiblePredicates)
} else {
if mir_borrowck.tainted_by_errors.is_none() && body.tainted_by_errors.is_none() {
const_prop_lint::ConstPropLint.run_lint(tcx, &body);
}
Ok(())
}
}

/// Obtain just the main MIR (no promoteds) and run some cleanups on it. This also runs
/// mir borrowck *before* doing so in order to ensure that borrowck can be run and doesn't
/// end up missing the source MIR due to stealing happening.
fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
if tcx.is_coroutine(def.to_def_id()) {
tcx.ensure_with_value().mir_coroutine_witnesses(def);
}
let mir_borrowck = tcx.mir_borrowck(def);
let has_impossible_predicates = tcx.const_prop_lint(def);

let is_fn_like = tcx.def_kind(def).is_fn_like();
if is_fn_like {
// Do not compute the mir call graph without said call graph actually being used.
if pm::should_run_pass(tcx, &inline::Inline) {
tcx.ensure_with_value().mir_inliner_callees(ty::InstanceDef::Item(def.to_def_id()));
}
}

let (body, _) = tcx.mir_promoted(def);
let mut body = body.steal();
if let Some(error_reported) = mir_borrowck.tainted_by_errors {
body.tainted_by_errors = Some(error_reported);
}

if let Err(HasImpossiblePredicates) = has_impossible_predicates {
trace!("found unsatisfiable predicates for {:?}", body.source);
// Clear the body to only contain a single `unreachable` statement.
let bbs = body.basic_blocks.as_mut();
Expand Down

0 comments on commit f271893

Please sign in to comment.