Skip to content

Commit ee922d4

Browse files
committed
Fix ICE involving calling Instance.ty during const evaluation
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.
1 parent b69f6e6 commit ee922d4

File tree

4 files changed

+64
-2
lines changed

4 files changed

+64
-2
lines changed

src/librustc/ty/instance.rs

+28
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,38 @@ pub enum InstanceDef<'tcx> {
6262
}
6363

6464
impl<'tcx> Instance<'tcx> {
65+
/// Returns the `Ty` corresponding to this `Instance`,
66+
/// with generic substitutions applied and lifetimes erased.
67+
///
68+
/// This method can only be called when the 'substs' for this Instance
69+
/// are fully monomorphic (no `ty::Param`'s are present).
70+
/// This is usually the case (e.g. during codegen).
71+
/// However, during constant evaluation, we may want
72+
/// to try to resolve a `Instance` using generic parameters
73+
/// (e.g. when we are attempting to to do const-propagation).
74+
/// In this case, `Instace.ty_env` should be used to provide
75+
/// the `ParamEnv` for our generic context.
6576
pub fn ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
6677
let ty = tcx.type_of(self.def.def_id());
78+
// There shouldn't be any params - if there are, then
79+
// Instance.ty_env should have been used to provide the proper
80+
// ParamEnv
81+
if self.substs.has_param_types() {
82+
panic!(
83+
"Instance.ty called for type {:?} with projections in substs: {:?}",
84+
ty, self.substs
85+
);
86+
}
6787
tcx.subst_and_normalize_erasing_regions(self.substs, ty::ParamEnv::reveal_all(), &ty)
6888
}
89+
90+
/// Like `Instance.ty`, but allows a `ParamEnv` to be specified for use during
91+
/// normalization. This method is only really useful during constant evaluation,
92+
/// where we are dealing with potentially generic types.
93+
pub fn ty_env(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> {
94+
let ty = tcx.type_of(self.def.def_id());
95+
tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty)
96+
}
6997
}
7098

7199
impl<'tcx> InstanceDef<'tcx> {

src/librustc_mir/const_eval/eval_queries.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ pub fn const_eval_validated_provider<'tcx>(
221221
// We call `const_eval` for zero arg intrinsics, too, in order to cache their value.
222222
// Catch such calls and evaluate them instead of trying to load a constant's MIR.
223223
if let ty::InstanceDef::Intrinsic(def_id) = key.value.instance.def {
224-
let ty = key.value.instance.ty(tcx);
224+
let ty = key.value.instance.ty_env(tcx, key.param_env);
225225
let substs = match ty.kind {
226226
ty::FnDef(_, substs) => substs,
227227
_ => bug!("intrinsic with type {:?}", ty),

src/librustc_mir/interpret/terminator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
204204
// ABI check
205205
{
206206
let callee_abi = {
207-
let instance_ty = instance.ty(*self.tcx);
207+
let instance_ty = instance.ty_env(*self.tcx, self.param_env);
208208
match instance_ty.kind {
209209
ty::FnDef(..) => instance_ty.fn_sig(*self.tcx).abi(),
210210
ty::Closure(..) => Abi::RustCall,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// compile-flags: -Z mir-opt-level=3
2+
// build-pass
3+
4+
// This used to ICE in const-prop due
5+
// to an empty ParamEnv being used during normalization
6+
// of a generic type
7+
8+
9+
fn main() {
10+
join_all::<u32>();
11+
}
12+
13+
trait Foo {
14+
type Item;
15+
}
16+
17+
impl Foo for u32 {
18+
type Item = u8;
19+
}
20+
21+
trait Bar {
22+
type Item2;
23+
}
24+
25+
impl Bar for u8 {
26+
type Item2 = u64;
27+
}
28+
29+
fn join_all<I>()
30+
where I: Foo,
31+
I::Item: Bar
32+
{
33+
Vec::<<I::Item as Bar>::Item2>::new(); // ICE occurs processing this line
34+
}

0 commit comments

Comments
 (0)