Skip to content

Commit

Permalink
Fix ICE involving calling Instance.ty during const evaluation
Browse files Browse the repository at this point in the history
Fixes #67639

`Instance.ty` assumes that we are in a fully monomorphic context (e.g.
codegen), and can therefore use an empty `ParamEnv` when performing
normalization. Howver, the MIR constant evaluator code ends up calling
`Instance.ty` as a result of us attemptign to 'speculatively'
const-evaluate generic functions during const propagation.

As a result,
we may end up with projections involving type parameters
(e.g. <T as MyTrait>::Bar>) in the type we are trying to normalize.
Normalization expects us to have proper predicates in the `ParamEnv` for
such projections, and will ICE if we don't.

This commit adds a new method `Instance.ty_env`, which takes a
`ParamEnv` for use during normalization. The MIR const-evaluator code is
changed to use this method, passing in the proper `ParamEnv` for the
context at hand.
  • Loading branch information
Aaron1011 committed Jan 5, 2020
1 parent b69f6e6 commit ee922d4
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 2 deletions.
28 changes: 28 additions & 0 deletions src/librustc/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,38 @@ pub enum InstanceDef<'tcx> {
}

impl<'tcx> Instance<'tcx> {
/// Returns the `Ty` corresponding to this `Instance`,
/// with generic substitutions applied and lifetimes erased.
///
/// This method can only be called when the 'substs' for this Instance
/// are fully monomorphic (no `ty::Param`'s are present).
/// This is usually the case (e.g. during codegen).
/// However, during constant evaluation, we may want
/// to try to resolve a `Instance` using generic parameters
/// (e.g. when we are attempting to to do const-propagation).
/// In this case, `Instace.ty_env` should be used to provide
/// the `ParamEnv` for our generic context.
pub fn ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
let ty = tcx.type_of(self.def.def_id());
// There shouldn't be any params - if there are, then
// Instance.ty_env should have been used to provide the proper
// ParamEnv
if self.substs.has_param_types() {
panic!(
"Instance.ty called for type {:?} with projections in substs: {:?}",
ty, self.substs
);
}
tcx.subst_and_normalize_erasing_regions(self.substs, ty::ParamEnv::reveal_all(), &ty)
}

/// Like `Instance.ty`, but allows a `ParamEnv` to be specified for use during
/// normalization. This method is only really useful during constant evaluation,
/// where we are dealing with potentially generic types.
pub fn ty_env(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> {
let ty = tcx.type_of(self.def.def_id());
tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty)
}
}

impl<'tcx> InstanceDef<'tcx> {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ pub fn const_eval_validated_provider<'tcx>(
// We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
// Catch such calls and evaluate them instead of trying to load a constant's MIR.
if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def {
let ty = key.value.instance.ty(tcx);
let ty = key.value.instance.ty_env(tcx, key.param_env);
let substs = match ty.kind {
ty::FnDef(_, substs) => substs,
_ => bug!("intrinsic with type {:?}", ty),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/interpret/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// ABI check
{
let callee_abi = {
let instance_ty = instance.ty(*self.tcx);
let instance_ty = instance.ty_env(*self.tcx, self.param_env);
match instance_ty.kind {
ty::FnDef(..) => instance_ty.fn_sig(*self.tcx).abi(),
ty::Closure(..) => Abi::RustCall,
Expand Down
34 changes: 34 additions & 0 deletions src/test/ui/mir/issue-67639-normalization-ice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// compile-flags: -Z mir-opt-level=3
// build-pass

// This used to ICE in const-prop due
// to an empty ParamEnv being used during normalization
// of a generic type


fn main() {
join_all::<u32>();
}

trait Foo {
type Item;
}

impl Foo for u32 {
type Item = u8;
}

trait Bar {
type Item2;
}

impl Bar for u8 {
type Item2 = u64;
}

fn join_all<I>()
where I: Foo,
I::Item: Bar
{
Vec::<<I::Item as Bar>::Item2>::new(); // ICE occurs processing this line
}

0 comments on commit ee922d4

Please sign in to comment.