-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix an ICE encountered in clippy that will be possible to trigger in rustc in the future #61041
Changes from all commits
16911bd
d38fb10
b535090
e849b83
600d679
27320e3
b84ed30
7fac3a1
8ac4cb5
fe2013c
1d26fda
b917c43
5a8c9d3
faa342f
51e1343
034939b
685e0e5
fdf5450
3afcb1e
f0a16c4
3aa550a
82db274
70124ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ use rustc::mir; | |
use rustc::ty::layout::{ | ||
self, Size, Align, HasDataLayout, LayoutOf, TyLayout | ||
}; | ||
use rustc::ty::subst::{Subst, SubstsRef}; | ||
use rustc::ty::subst::{SubstsRef, Subst}; | ||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; | ||
use rustc::ty::query::TyCtxtAt; | ||
use rustc_data_structures::indexed_vec::IndexVec; | ||
|
@@ -291,22 +291,38 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
ty.is_freeze(*self.tcx, self.param_env, DUMMY_SP) | ||
} | ||
|
||
pub(super) fn subst_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>( | ||
/// Call this whenever you have a value that you took from the current frame's `mir::Body`. | ||
/// Not guaranteed to actually monomorphize the value. | ||
/// If we are e.g. const propagating inside a generic function, some | ||
/// things may depend on a generic parameter and thus can never be monomorphized. | ||
pub(super) fn subst_and_normalize_erasing_regions_in_frame<T: TypeFoldable<'tcx>>( | ||
&self, | ||
substs: T, | ||
value: T, | ||
) -> InterpResult<'tcx, T> { | ||
match self.stack.last() { | ||
Some(frame) => Ok(self.tcx.subst_and_normalize_erasing_regions( | ||
frame.instance.substs, | ||
self.param_env, | ||
&substs, | ||
)), | ||
None => if substs.needs_subst() { | ||
throw_inval!(TooGeneric) | ||
} else { | ||
Ok(substs) | ||
}, | ||
self.subst_and_normalize_erasing_regions(self.frame().instance.substs, value) | ||
} | ||
|
||
/// Same thing as `subst_and_normalize_erasing_regions_in_frame` but not taking its substs | ||
/// from the top stack frame, but requiring you to pass specific substs. | ||
/// | ||
/// Only call this function if you want to apply the substs of a specific frame (that is | ||
/// definitely not the frame currently being evaluated). You need to make sure to pass correct | ||
/// substs. | ||
fn subst_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function shouldn't be needed (as a separate thing). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's needed for layout_of_local There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd inline it there. |
||
&self, | ||
param_substs: SubstsRef<'tcx>, | ||
value: T, | ||
) -> InterpResult<'tcx, T> { | ||
let substituted = value.subst(self.tcx.tcx, param_substs); | ||
// we duplicate the body of `TyCtxt::subst_and_normalize_erasing_regions` here, because | ||
// we can't normalize values with generic parameters. The difference between this function | ||
// and the `TyCtxt` version is this early abort | ||
if substituted.needs_subst() { | ||
// FIXME(oli-obk): This aborts evaluating `fn foo<T>() -> i32 { 42 }` inside an | ||
// associated constant of a generic trait, even though that should be doable. | ||
throw_inval!(TooGeneric); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does anything break if you remove this? |
||
} | ||
Ok(self.tcx.normalize_erasing_regions(self.param_env, substituted)) | ||
} | ||
|
||
pub(super) fn resolve( | ||
|
@@ -315,9 +331,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
substs: SubstsRef<'tcx> | ||
) -> InterpResult<'tcx, ty::Instance<'tcx>> { | ||
trace!("resolve: {:?}, {:#?}", def_id, substs); | ||
trace!("param_env: {:#?}", self.param_env); | ||
let substs = self.subst_and_normalize_erasing_regions(substs)?; | ||
trace!("substs: {:#?}", substs); | ||
ty::Instance::resolve( | ||
*self.tcx, | ||
self.param_env, | ||
|
@@ -349,36 +362,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
} | ||
} | ||
|
||
pub(super) fn monomorphize<T: TypeFoldable<'tcx> + Subst<'tcx>>( | ||
&self, | ||
t: T, | ||
) -> InterpResult<'tcx, T> { | ||
match self.stack.last() { | ||
Some(frame) => Ok(self.monomorphize_with_substs(t, frame.instance.substs)?), | ||
None => if t.needs_subst() { | ||
throw_inval!(TooGeneric) | ||
} else { | ||
Ok(t) | ||
}, | ||
} | ||
} | ||
|
||
fn monomorphize_with_substs<T: TypeFoldable<'tcx> + Subst<'tcx>>( | ||
&self, | ||
t: T, | ||
substs: SubstsRef<'tcx> | ||
) -> InterpResult<'tcx, T> { | ||
// miri doesn't care about lifetimes, and will choke on some crazy ones | ||
// let's simply get rid of them | ||
let substituted = t.subst(*self.tcx, substs); | ||
|
||
if substituted.needs_subst() { | ||
throw_inval!(TooGeneric) | ||
} | ||
|
||
Ok(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substituted)) | ||
} | ||
|
||
pub fn layout_of_local( | ||
&self, | ||
frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, | ||
|
@@ -391,7 +374,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
None => { | ||
let layout = crate::interpret::operand::from_known_layout(layout, || { | ||
let local_ty = frame.body.local_decls[local].ty; | ||
let local_ty = self.monomorphize_with_substs(local_ty, frame.instance.substs)?; | ||
let local_ty = self.subst_and_normalize_erasing_regions( | ||
frame.instance.substs, local_ty, | ||
)?; | ||
self.layout_of(local_ty) | ||
})?; | ||
if let Some(state) = frame.locals.get(local) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
|
||
use std::convert::TryInto; | ||
|
||
use rustc::{mir, ty}; | ||
use rustc::{mir, ty::{self, Ty}}; | ||
use rustc::ty::layout::{ | ||
self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt, VariantIdx, | ||
}; | ||
|
@@ -511,7 +511,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
Move(ref place) => | ||
self.eval_place_to_op(place, layout)?, | ||
|
||
Constant(ref constant) => self.eval_const_to_op(constant.literal, layout)?, | ||
Constant(ref constant) => { | ||
let val = self.subst_and_normalize_erasing_regions_in_frame(constant.literal.val)?; | ||
let ty = self.subst_and_normalize_erasing_regions_in_frame(constant.ty)?; | ||
self.eval_const_to_op(val, ty, layout)? | ||
}, | ||
}; | ||
trace!("{:?}: {:?}", mir_op, *op); | ||
Ok(op) | ||
|
@@ -531,18 +535,17 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
// in patterns via the `const_eval` module | ||
crate fn eval_const_to_op( | ||
&self, | ||
val: &'tcx ty::Const<'tcx>, | ||
value: ConstValue<'tcx>, | ||
ty: Ty<'tcx>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a HACK/FIXME comment. Or even better, wait for #56137 to be fixed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is, rebase this on top of #63495. |
||
layout: Option<TyLayout<'tcx>>, | ||
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { | ||
let tag_scalar = |scalar| match scalar { | ||
Scalar::Ptr(ptr) => Scalar::Ptr(self.tag_static_base_pointer(ptr)), | ||
Scalar::Raw { data, size } => Scalar::Raw { data, size }, | ||
}; | ||
// Early-return cases. | ||
match val.val { | ||
ConstValue::Param(_) => | ||
// FIXME(oli-obk): try to monomorphize | ||
throw_inval!(TooGeneric), | ||
match value { | ||
ConstValue::Param(_) => throw_inval!(TooGeneric), | ||
ConstValue::Unevaluated(def_id, substs) => { | ||
let instance = self.resolve(def_id, substs)?; | ||
return Ok(OpTy::from(self.const_eval_raw(GlobalId { | ||
|
@@ -553,10 +556,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
_ => {} | ||
} | ||
// Other cases need layout. | ||
let layout = from_known_layout(layout, || { | ||
self.layout_of(self.monomorphize(val.ty)?) | ||
})?; | ||
let op = match val.val { | ||
let layout = from_known_layout(layout, || self.layout_of(ty))?; | ||
let op = match value { | ||
ConstValue::ByRef { alloc, offset } => { | ||
let id = self.tcx.alloc_map.lock().create_memory_alloc(alloc); | ||
// We rely on mutability being set correctly in that allocation to prevent writes | ||
|
@@ -583,7 +584,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
ConstValue::Infer(..) | | ||
ConstValue::Placeholder(..) | | ||
ConstValue::Unevaluated(..) => | ||
bug!("eval_const_to_op: Unexpected ConstValue {:?}", val), | ||
bug!("eval_const_to_op: Unexpected ConstValue {:?}", value), | ||
}; | ||
Ok(OpTy { op, layout }) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// revisions:cfail1 cfail2 | ||
// check-pass | ||
// compile-flags: --crate-type staticlib | ||
|
||
#![deny(unused_attributes)] | ||
|
||
#[no_mangle] | ||
pub extern "C" fn rust_no_mangle() -> i32 { | ||
42 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
pub struct Foo<A, B>(A, B); | ||
|
||
impl<A, B> Foo<A, B> { | ||
const HOST_SIZE: usize = std::mem::size_of::<B>(); | ||
|
||
pub fn crash() -> bool { | ||
[5; Self::HOST_SIZE] == [6; 0] //~ ERROR no associated item named `HOST_SIZE` | ||
//~^ the size for values of type `A` cannot be known | ||
//~| the size for values of type `B` cannot be known | ||
} | ||
} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
error[E0599]: no associated item named `HOST_SIZE` found for type `Foo<A, B>` in the current scope | ||
--> $DIR/too_generic_eval_ice.rs:7:19 | ||
| | ||
LL | pub struct Foo<A, B>(A, B); | ||
| --------------------------- associated item `HOST_SIZE` not found for this | ||
... | ||
LL | [5; Self::HOST_SIZE] == [6; 0] | ||
| ^^^^^^^^^ associated item not found in `Foo<A, B>` | ||
| | ||
= note: the method `HOST_SIZE` exists but the following trait bounds were not satisfied: | ||
`A : std::marker::Sized` | ||
`B : std::marker::Sized` | ||
|
||
error[E0277]: the size for values of type `A` cannot be known at compilation time | ||
--> $DIR/too_generic_eval_ice.rs:7:13 | ||
| | ||
LL | [5; Self::HOST_SIZE] == [6; 0] | ||
| ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | ||
| | ||
= help: the trait `std::marker::Sized` is not implemented for `A` | ||
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait> | ||
= help: consider adding a `where A: std::marker::Sized` bound | ||
note: required by `Foo` | ||
--> $DIR/too_generic_eval_ice.rs:1:1 | ||
| | ||
LL | pub struct Foo<A, B>(A, B); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error[E0277]: the size for values of type `B` cannot be known at compilation time | ||
--> $DIR/too_generic_eval_ice.rs:7:13 | ||
| | ||
LL | [5; Self::HOST_SIZE] == [6; 0] | ||
| ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | ||
| | ||
= help: the trait `std::marker::Sized` is not implemented for `B` | ||
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait> | ||
= help: consider adding a `where B: std::marker::Sized` bound | ||
note: required by `Foo` | ||
--> $DIR/too_generic_eval_ice.rs:1:1 | ||
| | ||
LL | pub struct Foo<A, B>(A, B); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: aborting due to 3 previous errors | ||
|
||
Some errors have detailed explanations: E0277, E0599. | ||
For more information about an error, try `rustc --explain E0277`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RalfJung came up with
subst_from_frame_and_normalize_erasing_regions
, I kinda like that more?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In particular this finally made it click for me in terms of: we need this when taking something from the current frame to move it into the
InterpCx
"universe".Also see the comments I wrote for my own experiments here and here; I think it would be useful to carry those over.