Skip to content

Commit

Permalink
Erase trivial caller bounds when typechecking MIR after optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Feb 24, 2022
1 parent 3d127e2 commit 244a73c
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 3 deletions.
7 changes: 4 additions & 3 deletions compiler/rustc_const_eval/src/transform/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
return true;
}
// Normalize projections and things like that.
// FIXME: We need to reveal_all, as some optimizations change types in ways
// that require unfolding opaque types.
let param_env = self.param_env.with_reveal_all_normalized(self.tcx);
// FIXME: We need to reveal_all and erase any trivial caller bounds (see query description)
// due to the fact that certain optimizations (e.g. inlining) change the way that types are
// normalized.
let param_env = self.tcx.reveal_all_and_erase_trivial_caller_bounds(self.param_env);
let src = self.tcx.normalize_erasing_regions(param_env, src);
let dest = self.tcx.normalize_erasing_regions(param_env, dest);

Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,16 @@ rustc_queries! {
desc { |tcx| "computing revealed normalized predicates of `{}`", tcx.def_path_str(def_id) }
}

/// Like `ty::ParamEnv::reveal_all_normalized`, but returns the `ParamEnv` with any
/// where clause bounds removed if they hold "trivially" -- that is, if they are
/// satisfied by the input param-env with that bound removed, such as if they match a
/// global `impl` or are a duplicated bound in the where clause.
///
/// This is a query because it is computationally expensive, and we want to cache it.
query reveal_all_and_erase_trivial_caller_bounds(key: ty::ParamEnv<'tcx>) -> ty::ParamEnv<'tcx> {
desc { |tcx| "normalizing and erasing trivial predicates of `{:?}`", key }
}

/// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`,
/// `ty.is_copy()`, etc, since that will prune the environment where possible.
query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,12 @@ impl<'tcx> ParamEnv<'tcx> {
Self::new(List::empty(), self.reveal(), self.constness())
}

/// Returns this same environment but with new caller bounds.
#[inline]
pub fn with_caller_bounds(self, caller_bounds: &'tcx List<Predicate<'tcx>>) -> Self {
Self::new(caller_bounds, self.reveal(), self.constness())
}

/// Creates a suitable environment in which to perform trait
/// queries on the given value. When type-checking, this is simply
/// the pair of the environment plus value. But when reveal is set to
Expand Down
47 changes: 47 additions & 0 deletions compiler/rustc_ty_utils/src/ty.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::TraitEngine;
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt};
use rustc_span::{sym, Span};
Expand Down Expand Up @@ -356,6 +358,50 @@ fn param_env_reveal_all_normalized(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamE
tcx.param_env(def_id).with_reveal_all_normalized(tcx)
}

fn reveal_all_and_erase_trivial_caller_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> ty::ParamEnv<'tcx> {
let mut old_caller_bounds = param_env.caller_bounds();
loop {
// The caller bounds that we must continue to include in our ParamEnv,
// because they cause errors without them...
let mut needed_caller_bounds = vec![];
for (idx, bound) in old_caller_bounds.iter().enumerate() {
let new_param_env = param_env.with_caller_bounds(tcx.mk_predicates(
needed_caller_bounds.iter().chain(old_caller_bounds[idx + 1..].iter()).copied(),
));
// a bound is trivial if it does not have const projections
// (which might induce cycles), and if we can prove that bound
// given a copy of our param-env that has the bound removed.
let is_bound_trivial = tcx.infer_ctxt().enter(|infcx| {
let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
fulfillcx.register_predicate_obligation(
&infcx,
traits::Obligation::new(traits::ObligationCause::dummy(), new_param_env, bound),
);
let errors = fulfillcx.select_all_or_error(&infcx);
if !errors.is_empty() {
info!("{:?} is NOT trivial: {:?}", bound, errors);
} else {
info!("{:?} is trivial", bound);
}
// is trivial iff there are no errors when fulfilling this bound
errors.is_empty()
});
if !is_bound_trivial {
needed_caller_bounds.push(bound);
}
}
let new_caller_bounds = tcx.mk_predicates(needed_caller_bounds.into_iter());
if new_caller_bounds == old_caller_bounds {
return param_env.with_caller_bounds(new_caller_bounds).with_reveal_all_normalized(tcx);
} else {
old_caller_bounds = new_caller_bounds;
}
}
}

fn instance_def_size_estimate<'tcx>(
tcx: TyCtxt<'tcx>,
instance_def: ty::InstanceDef<'tcx>,
Expand Down Expand Up @@ -495,6 +541,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
def_ident_span,
param_env,
param_env_reveal_all_normalized,
reveal_all_and_erase_trivial_caller_bounds,
instance_def_size_estimate,
issue33140_self_ty,
impl_defaultness,
Expand Down
21 changes: 21 additions & 0 deletions src/test/ui/mir/mir-inlining/caller-with-trivial-bound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// check-pass
// compile-flags: -Zmir-opt-level=3 --crate-type=lib

pub trait Factory<T> {
type Item;
}

pub struct IntFactory;

impl<T> Factory<T> for IntFactory {
type Item = usize;
}

pub fn foo<T>() where IntFactory: Factory<T> {
let mut x: <IntFactory as Factory<T>>::Item = bar::<T>();
}

#[inline]
pub fn bar<T>() -> <IntFactory as Factory<T>>::Item {
0usize
}

0 comments on commit 244a73c

Please sign in to comment.