Repository navigation
ICE error performing operation: fully_perform #140123
Description
Activity
- addedC-bugCategory: This is a bug.Category: This is a bug.I-ICEIssue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
on Apr 21, 2025 - addedneeds-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
on Apr 21, 2025 // MIR for `ICE` after built const ICE: [&mut [()]; 2] = { let mut _0: [&mut [()]; 2]; let mut _1: &mut [()]; let mut _2: &mut [()]; bb0: { StorageLive(_1); StorageLive(_2); _2 = const ICE::{constant#1}; _1 = &mut (*_2); _0 = [move _1; 2]; StorageDead(_2); StorageDead(_1); return; } }We're reborrowing the
&mut [()]argument at_1 = &mut (*_2), b/c we always reborrow unsized mut refs in coercion I think.This could be fixed, but we still have other operations that still cause ICEs like real unsize coercions:
trait Trait {} impl Trait for [(); 0] {} const ICE: [&mut dyn Trait; 2] = [const { empty_mut() }; 2]; const fn empty_mut() -> &'static mut [(); 0] { &mut [] }
Which also leads to an ICE:
// MIR for `ICE` after built const ICE: [&mut dyn Trait; 2] = { let mut _0: [&mut dyn Trait; 2]; let mut _1: &mut dyn Trait; let mut _2: &mut dyn Trait; let mut _3: &mut dyn Trait; bb0: { StorageLive(_1); StorageLive(_2); StorageLive(_3); _3 = const ICE::{constant#1}; _2 = &mut (*_3); _1 = move _2 as &mut dyn Trait (PointerCoercion(Unsize, Implicit)); StorageDead(_2); _0 = [move _1; 2]; StorageDead(_3); StorageDead(_1); return; } }This also ICEs when using
std::slice::from_raw_parts_mutinstead.const ICE: [&mut [()]; 2] = [const { empty_mut() }; 2]; const fn empty_mut() -> &'static mut [()] { unsafe { std::slice::from_raw_parts_mut(std::ptr::dangling_mut(), 0) } }
A variant that doesn't involve unsized coercions:
const ICE: [&mut [(); 0]; 2] = [const { empty_mut() }; 2]; const fn empty_mut() -> &'static mut [(); 0] { &mut [] }
A variant without a function call:
const ICE: [&mut [(); 0]; 2] = [const { let x = &mut []; x }; 2];
@rustbot label: +A-const-eval +S-has-mcve -needs-triage
- addedA-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)Area: Constant evaluation, covers all const contexts (static, const fn, ...)S-has-mcveStatus: A Minimal Complete and Verifiable Example has been found for this issueStatus: A Minimal Complete and Verifiable Example has been found for this issueand removedneeds-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
on Apr 28, 2025 - addedS-bug-has-testStatus: This bug is tracked inside the repo by a `known-bug` test.Status: This bug is tracked inside the repo by a `known-bug` test.
on May 20, 2025 A variant without empty arrays:
const ICE: [&mut (); 2] = [const { unsafe { &mut *(1 as *mut ()) } }; 2];
A variant with an explicit static:
static mut X: () = (); const ICE: [&mut (); 2] = [const { unsafe { &mut *(&raw mut X) } }; 2];
Variant without const promotion or const blocks:
const DANGLING: &mut () = unsafe { &mut *(1 as *mut ()) }; const ICE: [&mut (); 2] = [DANGLING; 2];
The compiler is crashing here:
rust/compiler/rustc_borrowck/src/type_check/mod.rs
Lines 997 to 1008 in 255aa22
// If the length cannot be evaluated we must assume that the length can be larger // than 1. // If the length is larger than 1, the repeat expression will need to copy the // element, so we require the `Copy` trait. if len.try_to_target_usize(tcx).is_none_or(|len| len > 1) { match operand { Operand::Copy(..) | Operand::Constant(..) => { // These are always okay: direct use of a const, or a value that can // evidently be copied. } Operand::Move(place) => { // Make sure that repeated elements implement `Copy`. The initial MIR (for the variant in the above comment) is:
// MIR for `ICE` after built const ICE: [&mut (); 2] = { let mut _0: [&mut (); 2]; let mut _1: &mut (); let mut _2: &mut (); bb0: { StorageLive(_1); StorageLive(_2); _2 = const DANGLING; _1 = &mut (*_2); _0 = [move _1; 2]; StorageDead(_2); StorageDead(_1); return; } }For some reason, the MIR has
move _1, which implies that the array should be created by copying. The compiler then gets confused because&mut Tdoesn't implementCopy.The fix is presumably to change the MIR frommove _1toconst DANGLING.A variant that has the array not in a const (requires the variable to be annotated with a type):
const X: &mut () = unsafe { &mut *(1 as *mut ()) }; fn main() { let _a: [&mut (); 2] = [X; 2]; }
fn main() { let _a: [&mut (); 2] = [const { unsafe { &mut *(1 as *mut ()) } }; 2]; }
I suspect that the reborrowing (caused by the fact that the variable is annotated with a type) is causing the issue. Maybe the correct behavior is to reborrow into a newly promoted const?
A new variant that doesn't involve
&mutat all, but requires dyn_star:#![feature(dyn_star)] trait Trait {} impl Trait for usize {} const X: usize = 1; fn foo() { let _x: [dyn* Trait; 2] = [X; 2]; }
In this case, the issue is caused by the dyn coercion, instead of a reborrow.
Another variant that doesn't have any mutability. (Instead it relies on the fact that
Foodoesn't implementCopy.) Still requires an unstable feature though.#![feature(derive_coerce_pointee)] use std::marker::CoercePointee; trait Trait {} impl Trait for i32 {} #[derive(CoercePointee)] #[repr(transparent)] struct Foo<T: ?Sized + 'static> { value: &'static T } const X: Foo<i32> = Foo { value: &1 }; fn main() { let _x: [Foo<dyn Trait>; 2] = [X; 2]; }
Edit: Here's without the feature
use std::rc::Weak; trait Trait {} impl Trait for i32 {} const X: Weak<i32> = Weak::new(); fn main() { let _x: [Weak<dyn Trait>; 2] = [X; 2]; }
Removing the check in #140123 (comment) causes a single test failure, in https://github.com/rust-lang/rust/blob/5b74275f89b6041bf2e9dc2abcf332e206d4cfca/tests/ui/lifetimes/copy_modulo_regions.rs
That is, the check is required to ensure that lifetime-dependend Copy impls are checked correctly. Therefore, I think removing the check isn't viable.
Variant where the coercion occurs inside the const, not in the array expression (!?)
use std::rc::Weak; trait Trait {} impl Trait for i32 {} const X: Weak<dyn Trait + 'static> = Weak::<i32>::new(); fn main() { let _x: [Weak<dyn Trait + 'static>; 2]= [X; 2]; }
Edit: Seems like the compiler is implicitly adding a no-op unsize coercion in the MIR for some reason?
@rustbot claim
I will research on how to proceed with fixing this, too.
My PR #163494 appears to partially (though not fully) fix this ICE. I have no idea why though!
ICE when
const {}initializing mutable references in[expr; N]array initializers whereNis >= 2. Does not happen when setting every element explicitly[expr, expr]. Interestingly you cannot useconst { &mut[] }directly and must use a function call to get around it. I will make another issue for that.Related issue: "Compile error when using &mut in explicity const context directly" #140126
Code
Meta
Occurs in version >= 1.83.0. Older versions fail to compile normally due to mutable references in const.
rustc --version --verbose:Error output
Backtrace