-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Fix an ICE encountered in clippy that will be possible to trigger in rustc in the future #61041
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
Changes from 21 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,37 @@ 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 `needs_subst`. 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>>( | ||||||
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. @RalfJung came up with 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. |
||||||
&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 compute the substs of a specific frame (that is | ||||||
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.
Suggested change
|
||||||
/// 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 +330,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 +361,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 +373,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 |
---|---|---|
|
@@ -538,11 +538,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | |
Scalar::Ptr(ptr) => Scalar::Ptr(self.tag_static_base_pointer(ptr)), | ||
Scalar::Raw { data, size } => Scalar::Raw { data, size }, | ||
}; | ||
let value = self.subst_and_normalize_erasing_regions_in_frame(val.val)?; | ||
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. After talking with @eddyb I cam to the conclusion that this is actually wrong. There is no reason to assume here that Instead, whoever gets a constant out of the MIR of this frame should do this -- other callers won't need this. 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. Yupp, only 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.
Right -- and 721df8f shows that we indeed got this wrong so far. I wonder what happens if we fix just that, if that's enough duck tape to make it still mostly work. ;) 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.
|
||
// 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 { | ||
|
@@ -554,9 +553,13 @@ 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)?) | ||
// Substituting is not a cached or O(1) operation. Substituting the type happens here, | ||
// which may not happen if we already have a layout. Or if we use the early abort above. | ||
// Thus we do not substitute and normalize `val` above, but only `val.val` and then | ||
// substitute `val.ty` here. | ||
self.layout_of(self.subst_and_normalize_erasing_regions_in_frame(val.ty)?) | ||
})?; | ||
let op = match val.val { | ||
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 | ||
|
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`. |
Uh oh!
There was an error while loading. Please reload this page.
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.
This advice seems wrong. It should be called whenever (and only when) you took something from the MIR of the current frame.
Maybe something like