Skip to content

Commit 244a73c

Browse files
Erase trivial caller bounds when typechecking MIR after optimizations
1 parent 3d127e2 commit 244a73c

File tree

5 files changed

+88
-3
lines changed

5 files changed

+88
-3
lines changed

compiler/rustc_const_eval/src/transform/validate.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
171171
return true;
172172
}
173173
// Normalize projections and things like that.
174-
// FIXME: We need to reveal_all, as some optimizations change types in ways
175-
// that require unfolding opaque types.
176-
let param_env = self.param_env.with_reveal_all_normalized(self.tcx);
174+
// FIXME: We need to reveal_all and erase any trivial caller bounds (see query description)
175+
// due to the fact that certain optimizations (e.g. inlining) change the way that types are
176+
// normalized.
177+
let param_env = self.tcx.reveal_all_and_erase_trivial_caller_bounds(self.param_env);
177178
let src = self.tcx.normalize_erasing_regions(param_env, src);
178179
let dest = self.tcx.normalize_erasing_regions(param_env, dest);
179180

compiler/rustc_middle/src/query/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,16 @@ rustc_queries! {
11521152
desc { |tcx| "computing revealed normalized predicates of `{}`", tcx.def_path_str(def_id) }
11531153
}
11541154

1155+
/// Like `ty::ParamEnv::reveal_all_normalized`, but returns the `ParamEnv` with any
1156+
/// where clause bounds removed if they hold "trivially" -- that is, if they are
1157+
/// satisfied by the input param-env with that bound removed, such as if they match a
1158+
/// global `impl` or are a duplicated bound in the where clause.
1159+
///
1160+
/// This is a query because it is computationally expensive, and we want to cache it.
1161+
query reveal_all_and_erase_trivial_caller_bounds(key: ty::ParamEnv<'tcx>) -> ty::ParamEnv<'tcx> {
1162+
desc { |tcx| "normalizing and erasing trivial predicates of `{:?}`", key }
1163+
}
1164+
11551165
/// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`,
11561166
/// `ty.is_copy()`, etc, since that will prune the environment where possible.
11571167
query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {

compiler/rustc_middle/src/ty/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1455,6 +1455,12 @@ impl<'tcx> ParamEnv<'tcx> {
14551455
Self::new(List::empty(), self.reveal(), self.constness())
14561456
}
14571457

1458+
/// Returns this same environment but with new caller bounds.
1459+
#[inline]
1460+
pub fn with_caller_bounds(self, caller_bounds: &'tcx List<Predicate<'tcx>>) -> Self {
1461+
Self::new(caller_bounds, self.reveal(), self.constness())
1462+
}
1463+
14581464
/// Creates a suitable environment in which to perform trait
14591465
/// queries on the given value. When type-checking, this is simply
14601466
/// the pair of the environment plus value. But when reveal is set to

compiler/rustc_ty_utils/src/ty.rs

+47
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use rustc_data_structures::fx::FxIndexSet;
22
use rustc_hir as hir;
33
use rustc_hir::def_id::DefId;
4+
use rustc_infer::infer::TyCtxtInferExt;
5+
use rustc_infer::traits::TraitEngine;
46
use rustc_middle::ty::subst::Subst;
57
use rustc_middle::ty::{self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt};
68
use rustc_span::{sym, Span};
@@ -356,6 +358,50 @@ fn param_env_reveal_all_normalized(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamE
356358
tcx.param_env(def_id).with_reveal_all_normalized(tcx)
357359
}
358360

361+
fn reveal_all_and_erase_trivial_caller_bounds<'tcx>(
362+
tcx: TyCtxt<'tcx>,
363+
param_env: ty::ParamEnv<'tcx>,
364+
) -> ty::ParamEnv<'tcx> {
365+
let mut old_caller_bounds = param_env.caller_bounds();
366+
loop {
367+
// The caller bounds that we must continue to include in our ParamEnv,
368+
// because they cause errors without them...
369+
let mut needed_caller_bounds = vec![];
370+
for (idx, bound) in old_caller_bounds.iter().enumerate() {
371+
let new_param_env = param_env.with_caller_bounds(tcx.mk_predicates(
372+
needed_caller_bounds.iter().chain(old_caller_bounds[idx + 1..].iter()).copied(),
373+
));
374+
// a bound is trivial if it does not have const projections
375+
// (which might induce cycles), and if we can prove that bound
376+
// given a copy of our param-env that has the bound removed.
377+
let is_bound_trivial = tcx.infer_ctxt().enter(|infcx| {
378+
let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
379+
fulfillcx.register_predicate_obligation(
380+
&infcx,
381+
traits::Obligation::new(traits::ObligationCause::dummy(), new_param_env, bound),
382+
);
383+
let errors = fulfillcx.select_all_or_error(&infcx);
384+
if !errors.is_empty() {
385+
info!("{:?} is NOT trivial: {:?}", bound, errors);
386+
} else {
387+
info!("{:?} is trivial", bound);
388+
}
389+
// is trivial iff there are no errors when fulfilling this bound
390+
errors.is_empty()
391+
});
392+
if !is_bound_trivial {
393+
needed_caller_bounds.push(bound);
394+
}
395+
}
396+
let new_caller_bounds = tcx.mk_predicates(needed_caller_bounds.into_iter());
397+
if new_caller_bounds == old_caller_bounds {
398+
return param_env.with_caller_bounds(new_caller_bounds).with_reveal_all_normalized(tcx);
399+
} else {
400+
old_caller_bounds = new_caller_bounds;
401+
}
402+
}
403+
}
404+
359405
fn instance_def_size_estimate<'tcx>(
360406
tcx: TyCtxt<'tcx>,
361407
instance_def: ty::InstanceDef<'tcx>,
@@ -495,6 +541,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
495541
def_ident_span,
496542
param_env,
497543
param_env_reveal_all_normalized,
544+
reveal_all_and_erase_trivial_caller_bounds,
498545
instance_def_size_estimate,
499546
issue33140_self_ty,
500547
impl_defaultness,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// check-pass
2+
// compile-flags: -Zmir-opt-level=3 --crate-type=lib
3+
4+
pub trait Factory<T> {
5+
type Item;
6+
}
7+
8+
pub struct IntFactory;
9+
10+
impl<T> Factory<T> for IntFactory {
11+
type Item = usize;
12+
}
13+
14+
pub fn foo<T>() where IntFactory: Factory<T> {
15+
let mut x: <IntFactory as Factory<T>>::Item = bar::<T>();
16+
}
17+
18+
#[inline]
19+
pub fn bar<T>() -> <IntFactory as Factory<T>>::Item {
20+
0usize
21+
}

0 commit comments

Comments
 (0)