Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/consteval/tests.rs')
-rw-r--r--crates/hir-ty/src/consteval/tests.rs312
1 files changed, 281 insertions, 31 deletions
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 0db1fefbfe..5bb327606d 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1,10 +1,11 @@
use base_db::{fixture::WithFixture, FileId};
use chalk_ir::Substitution;
use hir_def::db::DefDatabase;
+use test_utils::skip_slow_tests;
use crate::{
consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
- Interner,
+ Interner, MemoryMap,
};
use super::{
@@ -16,7 +17,7 @@ mod intrinsics;
fn simplify(e: ConstEvalError) -> ConstEvalError {
match e {
- ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => {
+ ConstEvalError::MirEvalError(MirEvalError::InFunction(e, _)) => {
simplify(ConstEvalError::MirEvalError(*e))
}
_ => e,
@@ -36,7 +37,37 @@ fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
#[track_caller]
fn check_number(ra_fixture: &str, answer: i128) {
- let (db, file_id) = TestDB::with_single_file(ra_fixture);
+ check_answer(ra_fixture, |b, _| {
+ assert_eq!(
+ b,
+ &answer.to_le_bytes()[0..b.len()],
+ "Bytes differ. In decimal form: actual = {}, expected = {answer}",
+ i128::from_le_bytes(pad16(b, true))
+ );
+ });
+}
+
+#[track_caller]
+fn check_str(ra_fixture: &str, answer: &str) {
+ check_answer(ra_fixture, |b, mm| {
+ let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
+ let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
+ let Some(bytes) = mm.get(addr, size) else {
+ panic!("string data missed in the memory map");
+ };
+ assert_eq!(
+ bytes,
+ answer.as_bytes(),
+ "Bytes differ. In string form: actual = {}, expected = {answer}",
+ String::from_utf8_lossy(bytes)
+ );
+ });
+}
+
+#[track_caller]
+fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
+ let (db, file_ids) = TestDB::with_many_files(ra_fixture);
+ let file_id = *file_ids.last().unwrap();
let r = match eval_goal(&db, file_id) {
Ok(t) => t,
Err(e) => {
@@ -46,13 +77,8 @@ fn check_number(ra_fixture: &str, answer: i128) {
};
match &r.data(Interner).value {
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
- ConstScalar::Bytes(b, _) => {
- assert_eq!(
- b,
- &answer.to_le_bytes()[0..b.len()],
- "Bytes differ. In decimal form: actual = {}, expected = {answer}",
- i128::from_le_bytes(pad16(b, true))
- );
+ ConstScalar::Bytes(b, mm) => {
+ check(b, mm);
}
x => panic!("Expected number but found {:?}", x),
},
@@ -87,7 +113,7 @@ fn eval_goal(db: &TestDB, file_id: FileId) -> Result<Const, ConstEvalError> {
}
_ => None,
})
- .unwrap();
+ .expect("No const named GOAL found in the test");
db.const_eval(const_id.into(), Substitution::empty(Interner))
}
@@ -108,6 +134,7 @@ fn bit_op() {
check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| {
e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_string()))
});
+ check_number(r#"const GOAL: i32 = 100000000i32 << 11"#, (100000000i32 << 11) as i128);
}
#[test]
@@ -166,14 +193,21 @@ fn casts() {
check_number(
r#"
//- minicore: coerce_unsized, index, slice
+ struct X {
+ unsize_field: [u8],
+ }
+
const GOAL: usize = {
let a = [10, 20, 3, 15];
let x: &[i32] = &a;
- let y: *const [i32] = x;
- let z = y as *const [u8]; // slice fat pointer cast don't touch metadata
- let q = z as *const str;
- let p = q as *const [u8];
- let w = unsafe { &*z };
+ let x: *const [i32] = x;
+ let x = x as *const [u8]; // slice fat pointer cast don't touch metadata
+ let x = x as *const str;
+ let x = x as *const X;
+ let x = x as *const [i16];
+ let x = x as *const X;
+ let x = x as *const [u8];
+ let w = unsafe { &*x };
w.len()
};
"#,
@@ -199,6 +233,30 @@ fn raw_pointer_equality() {
}
#[test]
+fn alignment() {
+ check_answer(
+ r#"
+//- minicore: transmute
+use core::mem::transmute;
+const GOAL: usize = {
+ let x: i64 = 2;
+ transmute(&x)
+}
+ "#,
+ |b, _| assert_eq!(b[0] % 8, 0),
+ );
+ check_answer(
+ r#"
+//- minicore: transmute
+use core::mem::transmute;
+static X: i64 = 12;
+const GOAL: usize = transmute(&X);
+ "#,
+ |b, _| assert_eq!(b[0] % 8, 0),
+ );
+}
+
+#[test]
fn locals() {
check_number(
r#"
@@ -1551,6 +1609,30 @@ fn closures() {
}
#[test]
+fn manual_fn_trait_impl() {
+ check_number(
+ r#"
+//- minicore: fn, copy
+struct S(i32);
+
+impl FnOnce<(i32, i32)> for S {
+ type Output = i32;
+
+ extern "rust-call" fn call_once(self, arg: (i32, i32)) -> i32 {
+ arg.0 + arg.1 + self.0
+ }
+}
+
+const GOAL: i32 = {
+ let s = S(1);
+ s(2, 3)
+};
+"#,
+ 6,
+ );
+}
+
+#[test]
fn closure_and_impl_fn() {
check_number(
r#"
@@ -1663,6 +1745,18 @@ fn function_pointer() {
);
check_number(
r#"
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ const GOAL: u8 = {
+ let plus2 = add2 as fn(u8) -> u8;
+ plus2(3)
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
//- minicore: coerce_unsized, index, slice
fn add2(x: u8) -> u8 {
x + 2
@@ -1850,6 +1944,38 @@ fn dyn_trait() {
}
#[test]
+fn coerce_unsized() {
+ check_number(
+ r#"
+//- minicore: coerce_unsized, deref_mut, slice, index, transmute, non_null
+use core::ops::{Deref, DerefMut, CoerceUnsized};
+use core::{marker::Unsize, mem::transmute, ptr::NonNull};
+
+struct ArcInner<T: ?Sized> {
+ strong: usize,
+ weak: usize,
+ data: T,
+}
+
+pub struct Arc<T: ?Sized> {
+ inner: NonNull<ArcInner<T>>,
+}
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
+
+const GOAL: usize = {
+ let x = transmute::<usize, Arc<[i32; 3]>>(12);
+ let y: Arc<[i32]> = x;
+ let z = transmute::<Arc<[i32]>, (usize, usize)>(y);
+ z.1
+};
+
+ "#,
+ 3,
+ );
+}
+
+#[test]
fn boxes() {
check_number(
r#"
@@ -1961,6 +2087,17 @@ fn array_and_index() {
}
#[test]
+fn string() {
+ check_str(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: &str = "hello";
+ "#,
+ "hello",
+ );
+}
+
+#[test]
fn byte_string() {
check_number(
r#"
@@ -2018,6 +2155,57 @@ fn consts() {
"#,
6,
);
+
+ check_number(
+ r#"
+ const F1: i32 = 2147483647;
+ const F2: i32 = F1 - 25;
+ const GOAL: i32 = F2;
+ "#,
+ 2147483622,
+ );
+
+ check_number(
+ r#"
+ const F1: i32 = -2147483648;
+ const F2: i32 = F1 + 18;
+ const GOAL: i32 = F2;
+ "#,
+ -2147483630,
+ );
+
+ check_number(
+ r#"
+ const F1: i32 = 10;
+ const F2: i32 = F1 - 20;
+ const GOAL: i32 = F2;
+ "#,
+ -10,
+ );
+
+ check_number(
+ r#"
+ const F1: i32 = 25;
+ const F2: i32 = F1 - 25;
+ const GOAL: i32 = F2;
+ "#,
+ 0,
+ );
+
+ check_number(
+ r#"
+ const A: i32 = -2147483648;
+ const GOAL: bool = A > 0;
+ "#,
+ 0,
+ );
+
+ check_number(
+ r#"
+ const GOAL: i64 = (-2147483648_i32) as i64;
+ "#,
+ -2147483648,
+ );
}
#[test]
@@ -2287,6 +2475,25 @@ fn const_trait_assoc() {
);
check_number(
r#"
+ //- /a/lib.rs crate:a
+ pub trait ToConst {
+ const VAL: usize;
+ }
+ pub const fn to_const<T: ToConst>() -> usize {
+ T::VAL
+ }
+ //- /main.rs crate:main deps:a
+ use a::{ToConst, to_const};
+ struct U0;
+ impl ToConst for U0 {
+ const VAL: usize = 5;
+ }
+ const GOAL: usize = to_const::<U0>();
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
struct S<T>(*mut T);
trait MySized: Sized {
@@ -2311,21 +2518,11 @@ fn const_trait_assoc() {
}
#[test]
-fn panic_messages() {
- check_fail(
- r#"
- //- minicore: panic
- const GOAL: u8 = {
- let x: u16 = 2;
- panic!("hello");
- };
- "#,
- |e| e == ConstEvalError::MirEvalError(MirEvalError::Panic("hello".to_string())),
- );
-}
-
-#[test]
fn exec_limits() {
+ if skip_slow_tests() {
+ return;
+ }
+
check_fail(
r#"
const GOAL: usize = loop {};
@@ -2339,7 +2536,7 @@ fn exec_limits() {
}
const GOAL: i32 = f(0);
"#,
- |e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
+ |e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
);
// Reasonable code should still work
check_number(
@@ -2363,6 +2560,28 @@ fn exec_limits() {
}
#[test]
+fn memory_limit() {
+ check_fail(
+ r#"
+ extern "Rust" {
+ #[rustc_allocator]
+ fn __rust_alloc(size: usize, align: usize) -> *mut u8;
+ }
+
+ const GOAL: u8 = unsafe {
+ __rust_alloc(30_000_000_000, 1); // 30GB
+ 2
+ };
+ "#,
+ |e| {
+ e == ConstEvalError::MirEvalError(MirEvalError::Panic(
+ "Memory allocation of 30000000000 bytes failed".to_string(),
+ ))
+ },
+ );
+}
+
+#[test]
fn type_error() {
check_fail(
r#"
@@ -2377,6 +2596,37 @@ fn type_error() {
}
#[test]
+fn unsized_field() {
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice, transmute
+ use core::mem::transmute;
+
+ struct Slice([u8]);
+ struct Slice2(Slice);
+
+ impl Slice2 {
+ fn as_inner(&self) -> &Slice {
+ &self.0
+ }
+
+ fn as_bytes(&self) -> &[u8] {
+ &self.as_inner().0
+ }
+ }
+
+ const GOAL: u8 = unsafe {
+ let x: &[u8] = &[1, 2, 3];
+ let x: &Slice2 = transmute(x);
+ let x = x.as_bytes();
+ x[0] + x[1] + x[2]
+ };
+ "#,
+ 6,
+ );
+}
+
+#[test]
fn unsized_local() {
check_fail(
r#"