rust-lang/rust · critical

expected subslice projection on fixed-size array

Error message

expected subslice projection on fixed-size array

What it means

In `PlaceTy::projection_ty`, handling a `Subslice { from, to, from_end: true }` projection on a `ty::Array(inner, size)` calls `size.try_to_target_usize(tcx)` and `.expect`s `Some`. rustc_middle throws this when an from-end subslice is applied to an array whose length constant is not a monomorphic `Const` evaluating to a concrete usize (i.e. a generic/abstract array size).

Source

Thrown at compiler/rustc_middle/src/mir/statement.rs:235

        }
        let answer = match *elem {
            ProjectionElem::Deref => {
                let ty = structurally_normalize(self.ty).builtin_deref(true).unwrap_or_else(|| {
                    bug!("deref projection of non-dereferenceable ty {:?}", self)
                });
                PlaceTy::from_ty(ty)
            }
            ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => {
                PlaceTy::from_ty(structurally_normalize(self.ty).builtin_index().unwrap())
            }
            ProjectionElem::Subslice { from, to, from_end } => {
                PlaceTy::from_ty(match structurally_normalize(self.ty).kind() {
                    ty::Slice(..) => self.ty,
                    ty::Array(inner, _) if !from_end => Ty::new_array(tcx, *inner, to - from),
                    ty::Array(inner, size) if from_end => {
                        let size = size
                            .try_to_target_usize(tcx)
                            .expect("expected subslice projection on fixed-size array");
                        let len = size - from - to;
                        Ty::new_array(tcx, *inner, len)
                    }
                    _ => bug!("cannot subslice non-array type: `{:?}`", self),
                })
            }
            ProjectionElem::Downcast(_name, index) => {
                PlaceTy { ty: self.ty, variant_index: Some(index) }
            }
            ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(
                structurally_normalize(self.ty),
                self.variant_index,
                f,
                fty,
            )),
            ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast_and_subtype(ty)),

            // FIXME(unsafe_binders): Rename `handle_opaque_cast_and_subtype` to be more general.

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Move the subslicing operation out of the generic context into a monomorphic call site so `N` is concrete when the projection is evaluated.
  2. Use a `&[T]` slice (or `array::AsSlice`) instead of subslicing `[T; N]` directly so the projection goes through the `ty::Slice` arm which doesn't need a concrete length.
  3. If writing/testing mir-opt, ensure subslice projections are only emitted after monomorphization, or guard `from_end` array subslices behind a concrete-length check.
  4. Report a rustc bug with the generic-array subslice repro if it occurs in stock code (this is supposed to be handled by monomorphization before projection).

Example fix

// before: generic from-end array subslice
fn tail<T, const N: usize>(a: [T; N]) -> &[T] { &a[1..] }
// panic: expected subslice projection on fixed-size array (N is generic)

// after
fn tail<T, const N: usize>(a: [T; N]) -> &[T] { a[1..].as_ref() }
Defensive patterns

Strategy: type-guard

Validate before calling

// ProjectionElem::Subslice { from_end: true } on an Array requires the
// array length to be a concrete target-usize. Pre-check before projecting.
use rustc_middle::mir::PlaceTy;
use rustc_middle::ty::{self, Ty, TyCtxt};
fn array_supports_from_end_subslice<'tcx>(pty: &PlaceTy<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
    if let ty::Array(_, len) = pty.ty.kind() {
        len.try_to_target_usize(tcx).is_some() // concrete length resolvable
    } else {
        false // not an array at all
    }
}
// Usage:
//   if array_supports_from_end_subslice(&pty, tcx) {
//       pty.projection_ty(tcx, ProjectionElem::Subslice { from, to, from_end: true })
//   } else {
//       // array length is generic/unknown: lower differently or reject
//   }

Type guard

// Narrow a PlaceTy to 'Array with a statically-known length'.
use rustc_middle::mir::PlaceTy;
use rustc_middle::ty::{self, TyCtxt};
fn as_fixed_array<'tcx>(pty: &PlaceTy<'tcx>, tcx: TyCtxt<'tcx>) -> Option<(ty::Ty<'tcx>, u64)> {
    match pty.ty.kind() {
        ty::Array(inner, len) => len.try_to_target_usize(tcx).map(|n| (*inner, n)),
        _ => None,
    }
}

Prevention

When it happens

Trigger: Triggered by MIR containing `&arr[from .. arr.len() - to]` (from_end subslice) where `arr` has type `[T; N]` with `N` still generic or otherwise not a concrete `usize` at the point of projection (e.g. evaluating MIR before monomorphization filled in `N`).

Common situations: Const generics code subslicing `[T; N]` inside a generic fn before monomorphization; mir-opt pass that re-orders subslice evaluation; nightly mir-level evaluation of generic array code; user code that depends on slice patterns of generic arrays in const context.

Related errors


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/5c7644a72f61a750.json. Report an issue: GitHub.