Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2478,13 +2478,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
) -> Const<'tcx> {
let tcx = self.tcx();

let elem_ty = match ty.kind() {
ty::Array(elem_ty, _) => elem_ty,
let (elem_ty, len) = match ty.kind() {
ty::Array(elem_ty, len) => (elem_ty, len),
ty::Error(e) => return Const::new_error(tcx, *e),
_ => {
let e = tcx
.dcx()
.span_err(array_expr.span, format!("expected `{}`, found const array", ty));
.span_err(array_expr.span, format!("expected `{ty}`, found const array"));
return Const::new_error(tcx, e);
}
};
Expand All @@ -2495,6 +2495,25 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.map(|elem| self.lower_const_arg(elem, *elem_ty))
.collect::<Vec<_>>();

let len = tcx
.try_normalize_erasing_regions(
ty::TypingEnv::new(ty::ParamEnv::empty(), TypingMode::non_body_analysis()),
Unnormalized::new_wip(*len),
)
.unwrap_or(*len);
if let Some(expected_len) = len.try_to_target_usize(tcx)

@JohnTitor JohnTitor Jul 3, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it should catch more cases if len is normalized, e.g. type const LEN (I haven't checked deeply so it may be caught by somewhere already)

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I changed it to call try_normalize_erasing_regions with an empty ParamEnv on the expected len and added a test for it, so now something like fn baz<const A: [u8; <S as Trait>::LEN]>() {} produces the correct error. Something like fn baz<T: Trait, const A: [u8; <T as Trait>::LEN]>() {} still ICEs if too long/produces UB if too short, however, since we can't resolve T to an actual type: trying to do try_evaluate_const with a non-empty ParamEnv led to cycle errors.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you open another issue and add an ICE test for that case (if not exists yet)? At least we should track it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #160553 and added a corresponding test to the crashes/ dir

&& expected_len != elems.len() as u64
{
let e = tcx.dcx().span_err(
array_expr.span,
format!(
"expected array with {expected_len} elements, found {} elements",
array_expr.elems.len()
),
);
return Const::new_error(tcx, e);
}

let valtree = ty::ValTree::from_branches(tcx, elems);

ty::Const::new_value(tcx, valtree, ty)
Expand Down
26 changes: 26 additions & 0 deletions tests/crashes/160553.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//@ known-bug: #160553
//@ compile-flags: -Copt-level=0
#![allow(incomplete_features)]
#![feature(adt_const_params, min_generic_const_args, macroless_generic_const_args)]
#![feature(generic_const_parameter_types)]

trait Trait {
type const LEN: usize;
}

struct S;
impl Trait for S {
type const LEN: usize = 2;
}

fn foo<T: Trait, const A: [u8; <T as Trait>::LEN]>() -> [u8; <T as Trait>::LEN] {
A
}

fn bar<T: Trait>() -> [u8; <T as Trait>::LEN] {
foo::<T, { [1, 2, 3] }>()
}

fn main() {
bar::<S>();
}
39 changes: 39 additions & 0 deletions tests/ui/const-generics/mgca/array-const-arg-len-mismatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//! Regression test for #155168
//!
//! Ensure that providing an array const arg with the wrong number of elements
//! doesn't ICE or silently cause UB.
#![expect(incomplete_features)]
#![feature(adt_const_params, min_generic_const_args, macroless_generic_const_args)]
#![feature(unsized_const_params, generic_const_parameter_types)]

use std::marker::ConstParamTy_;

fn foo<T: ConstParamTy_, const N: usize, const M: [T; N]>() -> [T; N] {
M
}

fn bar<const A: [u8; 2]>() {}

trait Trait {
type const LEN: usize;
}

struct S;
impl Trait for S {
type const LEN: usize = 3;
}

fn baz<const A: [u8; <S as Trait>::LEN]>() {}

fn main() {
foo::<u8, 2, { [] }>();
//~^ ERROR: expected array with 2 elements, found 0 elements
foo::<u8, 2, { [0, 0, 0] }>();
//~^ ERROR: expected array with 2 elements, found 3 elements
bar::<{ [] }>();
//~^ ERROR: expected array with 2 elements, found 0 elements
bar::<{ [1, 2, 3] }>();
//~^ ERROR: expected array with 2 elements, found 3 elements
baz::<{ [42] }>();
//~^ ERROR: expected array with 3 elements, found 1 elements
}
32 changes: 32 additions & 0 deletions tests/ui/const-generics/mgca/array-const-arg-len-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
error: expected array with 2 elements, found 0 elements
--> $DIR/array-const-arg-len-mismatch.rs:29:20
|
LL | foo::<u8, 2, { [] }>();
| ^^

error: expected array with 2 elements, found 3 elements
--> $DIR/array-const-arg-len-mismatch.rs:31:20
|
LL | foo::<u8, 2, { [0, 0, 0] }>();
| ^^^^^^^^^

error: expected array with 2 elements, found 0 elements
--> $DIR/array-const-arg-len-mismatch.rs:33:13
|
LL | bar::<{ [] }>();
| ^^

error: expected array with 2 elements, found 3 elements
--> $DIR/array-const-arg-len-mismatch.rs:35:13
|
LL | bar::<{ [1, 2, 3] }>();
| ^^^^^^^^^

error: expected array with 3 elements, found 1 elements
--> $DIR/array-const-arg-len-mismatch.rs:37:13
|
LL | baz::<{ [42] }>();
| ^^^^

error: aborting due to 5 previous errors

Loading