diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs')
-rw-r--r-- | src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs new file mode 100644 index 000000000..ca4268b8f --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -0,0 +1,676 @@ +use base_db::{fixture::WithFixture, FileId}; +use hir_def::db::DefDatabase; +use syntax::{TextRange, TextSize}; + +use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution}; + +use super::{interpret_mir, MirEvalError}; + +fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalError> { + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(db); + let scope = &def_map[module_id.local_id].scope; + let func_id = scope + .declarations() + .find_map(|x| match x { + hir_def::ModuleDefId::FunctionId(x) => { + if db.function_data(x).name.display(db).to_string() == "main" { + Some(x) + } else { + None + } + } + _ => None, + }) + .expect("no main function found"); + let body = db + .monomorphized_mir_body( + func_id.into(), + Substitution::empty(Interner), + db.trait_environment(func_id.into()), + ) + .map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?; + let (result, stdout, stderr) = interpret_mir(db, &body, false); + result?; + Ok((stdout, stderr)) +} + +fn check_pass(ra_fixture: &str) { + check_pass_and_stdio(ra_fixture, "", ""); +} + +fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr: &str) { + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + let file_id = *file_ids.last().unwrap(); + let x = eval_main(&db, file_id); + match x { + Err(e) => { + let mut err = String::new(); + let line_index = |size: TextSize| { + let mut size = u32::from(size) as usize; + let mut lines = ra_fixture.lines().enumerate(); + while let Some((i, l)) = lines.next() { + if let Some(x) = size.checked_sub(l.len()) { + size = x; + } else { + return (i, size); + } + } + (usize::MAX, size) + }; + let span_formatter = |file, range: TextRange| { + format!("{:?} {:?}..{:?}", file, line_index(range.start()), line_index(range.end())) + }; + e.pretty_print(&mut err, &db, span_formatter).unwrap(); + panic!("Error in interpreting: {err}"); + } + Ok((stdout, stderr)) => { + assert_eq!(stdout, expected_stdout); + assert_eq!(stderr, expected_stderr); + } + } +} + +#[test] +fn function_with_extern_c_abi() { + check_pass( + r#" +extern "C" fn foo(a: i32, b: i32) -> i32 { + a + b +} + +fn main() { + let x = foo(2, 3); +} + "#, + ); +} + +#[test] +fn drop_basic() { + check_pass( + r#" +//- minicore: drop, add + +struct X<'a>(&'a mut i32); +impl<'a> Drop for X<'a> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +struct NestedX<'a> { f1: X<'a>, f2: X<'a> } + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn my_drop2(x: X<'_>) { + return; +} + +fn my_drop(x: X<'_>) { + drop(x); +} + +fn main() { + let mut s = 10; + let mut x = X(&mut s); + my_drop(x); + x = X(&mut s); + my_drop2(x); + X(&mut s); // dropped immediately + let x = X(&mut s); + NestedX { f1: x, f2: X(&mut s) }; + if s != 15 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn drop_if_let() { + check_pass( + r#" +//- minicore: drop, add, option, cell, builtin_impls + +use core::cell::Cell; + +struct X<'a>(&'a Cell<i32>); +impl<'a> Drop for X<'a> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1) + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +#[test] +fn main() { + let s = Cell::new(0); + let x = Some(X(&s)); + if let Some(y) = x { + if s.get() != 0 { + should_not_reach(); + } + if s.get() != 0 { + should_not_reach(); + } + } else { + should_not_reach(); + } + if s.get() != 1 { + should_not_reach(); + } + let x = Some(X(&s)); + if let None = x { + should_not_reach(); + } else { + if s.get() != 1 { + should_not_reach(); + } + } + if s.get() != 1 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn drop_in_place() { + check_pass( + r#" +//- minicore: drop, add, coerce_unsized +use core::ptr::drop_in_place; + +struct X<'a>(&'a mut i32); +impl<'a> Drop for X<'a> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let mut s = 2; + let x = X(&mut s); + drop_in_place(&mut x); + drop(x); + if s != 4 { + should_not_reach(); + } + let p: &mut [X] = &mut [X(&mut 2)]; + drop_in_place(p); +} + "#, + ); +} + +#[test] +fn manually_drop() { + check_pass( + r#" +//- minicore: manually_drop +use core::mem::ManuallyDrop; + +struct X; +impl Drop for X { + fn drop(&mut self) { + should_not_reach(); + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let x = ManuallyDrop::new(X); +} + "#, + ); +} + +#[test] +fn generic_impl_for_trait_with_generic_method() { + check_pass( + r#" +//- minicore: drop +struct S<T>(T); + +trait Tr { + fn f<F>(&self, x: F); +} + +impl<T> Tr for S<T> { + fn f<F>(&self, x: F) { + } +} + +fn main() { + let s = S(1u8); + s.f(5i64); +} + "#, + ); +} + +#[test] +fn index_of_slice_should_preserve_len() { + check_pass( + r#" +//- minicore: index, slice, coerce_unsized + +struct X; + +impl core::ops::Index<X> for [i32] { + type Output = i32; + + fn index(&self, _: X) -> &i32 { + if self.len() != 3 { + should_not_reach(); + } + &self[0] + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let x: &[i32] = &[1, 2, 3]; + &x[X]; +} + "#, + ); +} + +#[test] +fn memcmp() { + check_pass( + r#" +//- minicore: slice, coerce_unsized, index + +fn should_not_reach() -> bool { + _ // FIXME: replace this function with panic when that works +} + +extern "C" { + fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; +} + +fn my_cmp(x: &[u8], y: &[u8]) -> i32 { + memcmp(x as *const u8, y as *const u8, x.len()) +} + +fn main() { + if my_cmp(&[1, 2, 3], &[1, 2, 3]) != 0 { + should_not_reach(); + } + if my_cmp(&[1, 20, 3], &[1, 2, 3]) <= 0 { + should_not_reach(); + } + if my_cmp(&[1, 2, 3], &[1, 20, 3]) >= 0 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn unix_write_stdout() { + check_pass_and_stdio( + r#" +//- minicore: slice, index, coerce_unsized + +type pthread_key_t = u32; +type c_void = u8; +type c_int = i32; + +extern "C" { + pub fn write(fd: i32, buf: *const u8, count: usize) -> usize; +} + +fn main() { + let stdout = b"stdout"; + let stderr = b"stderr"; + write(1, &stdout[0], 6); + write(2, &stderr[0], 6); +} + "#, + "stdout", + "stderr", + ); +} + +#[test] +fn closure_layout_in_rpit() { + check_pass( + r#" +//- minicore: fn + +fn f<F: Fn()>(x: F) { + fn g(x: impl Fn()) -> impl FnOnce() { + move || { + x(); + } + } + g(x)(); +} + +fn main() { + f(|| {}); +} + "#, + ); +} + +#[test] +fn from_fn() { + check_pass( + r#" +//- minicore: fn, iterator +struct FromFn<F>(F); + +impl<T, F: FnMut() -> Option<T>> Iterator for FromFn<F> { + type Item = T; + + fn next(&mut self) -> Option<Self::Item> { + (self.0)() + } +} + +fn main() { + let mut tokenize = { + FromFn(move || Some(2)) + }; + let s = tokenize.next(); +} + "#, + ); +} + +#[test] +fn for_loop() { + check_pass( + r#" +//- minicore: iterator, add +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +struct X; +struct XIter(i32); + +impl IntoIterator for X { + type Item = i32; + + type IntoIter = XIter; + + fn into_iter(self) -> Self::IntoIter { + XIter(0) + } +} + +impl Iterator for XIter { + type Item = i32; + + fn next(&mut self) -> Option<Self::Item> { + if self.0 == 5 { + None + } else { + self.0 += 1; + Some(self.0) + } + } +} + +fn main() { + let mut s = 0; + for x in X { + s += x; + } + if s != 15 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn field_with_associated_type() { + check_pass( + r#" +//- /b/mod.rs crate:b +pub trait Tr { + fn f(self); +} + +pub trait Tr2 { + type Ty: Tr; +} + +pub struct S<T: Tr2> { + pub t: T::Ty, +} + +impl<T: Tr2> S<T> { + pub fn g(&self) { + let k = (self.t, self.t); + self.t.f(); + } +} + +//- /a/mod.rs crate:a deps:b +use b::{Tr, Tr2, S}; + +struct A(i32); +struct B(u8); + +impl Tr for A { + fn f(&self) { + } +} + +impl Tr2 for B { + type Ty = A; +} + +#[test] +fn main() { + let s: S<B> = S { t: A(2) }; + s.g(); +} + "#, + ); +} + +#[test] +fn specialization_array_clone() { + check_pass( + r#" +//- minicore: copy, derive, slice, index, coerce_unsized +impl<T: Clone, const N: usize> Clone for [T; N] { + #[inline] + fn clone(&self) -> Self { + SpecArrayClone::clone(self) + } +} + +trait SpecArrayClone: Clone { + fn clone<const N: usize>(array: &[Self; N]) -> [Self; N]; +} + +impl<T: Clone> SpecArrayClone for T { + #[inline] + default fn clone<const N: usize>(array: &[T; N]) -> [T; N] { + // FIXME: panic here when we actually implement specialization. + from_slice(array) + } +} + +fn from_slice<T, const N: usize>(s: &[T]) -> [T; N] { + [s[0]; N] +} + +impl<T: Copy> SpecArrayClone for T { + #[inline] + fn clone<const N: usize>(array: &[T; N]) -> [T; N] { + *array + } +} + +#[derive(Clone, Copy)] +struct X(i32); + +fn main() { + let ar = [X(1), X(2)]; + ar.clone(); +} + "#, + ); +} + +#[test] +fn short_circuit_operator() { + check_pass( + r#" +fn should_not_reach() -> bool { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + if false && should_not_reach() { + should_not_reach(); + } + true || should_not_reach(); + +} + "#, + ); +} + +#[test] +fn closure_state() { + check_pass( + r#" +//- minicore: fn, add, copy +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let mut x = 2; + let mut c = move || { + x += 1; + x + }; + c(); + c(); + c(); + if x != 2 { + should_not_reach(); + } + if c() != 6 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn closure_capture_array_const_generic() { + check_pass( + r#" +//- minicore: fn, add, copy +struct X(i32); + +fn f<const N: usize>(mut x: [X; N]) { // -> impl FnOnce() { + let c = || { + x; + }; + c(); +} + +fn main() { + let s = f([X(1)]); + //s(); +} + "#, + ); +} + +#[test] +fn posix_tls() { + check_pass( + r#" +//- minicore: option + +type pthread_key_t = u32; +type c_void = u8; +type c_int = i32; + +extern "C" { + pub fn pthread_key_create( + key: *mut pthread_key_t, + dtor: Option<unsafe extern "C" fn(*mut c_void)>, + ) -> c_int; + pub fn pthread_key_delete(key: pthread_key_t) -> c_int; + pub fn pthread_getspecific(key: pthread_key_t) -> *mut c_void; + pub fn pthread_setspecific(key: pthread_key_t, value: *const c_void) -> c_int; +} + +fn main() { + let mut key = 2; + pthread_key_create(&mut key, None); +} + "#, + ); +} + +#[test] +fn regression_14966() { + check_pass( + r#" +//- minicore: fn, copy, coerce_unsized +trait A<T> { + fn a(&self) {} +} +impl A<()> for () {} + +struct B; +impl B { + pub fn b<T>(s: &dyn A<T>) -> Self { + B + } +} +struct C; +impl C { + fn c<T>(a: &dyn A<T>) -> Self { + let mut c = C; + let b = B::b(a); + c.d(|| a.a()); + c + } + fn d(&mut self, f: impl FnOnce()) {} +} + +fn main() { + C::c(&()); +} +"#, + ); +} |