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, None); 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); 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_struct_field() { check_pass( r#" //- minicore: drop, add, option, cell, builtin_impls use core::cell::Cell; fn should_not_reach() { _ // FIXME: replace this function with panic when that works } struct X<'a>(&'a Cell); impl<'a> Drop for X<'a> { fn drop(&mut self) { self.0.set(self.0.get() + 1) } } struct Tuple<'a>(X<'a>, X<'a>, X<'a>); fn main() { let s = Cell::new(0); { let x0 = X(&s); let xt = Tuple(x0, X(&s), X(&s)); let x1 = xt.1; if s.get() != 0 { should_not_reach(); } drop(xt.0); if s.get() != 1 { should_not_reach(); } } // FIXME: this should be 3 if s.get() != 2 { 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); trait Tr { fn f(&self, x: F); } impl Tr for S { fn 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 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(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); impl Option> Iterator for FromFn { type Item = T; fn next(&mut self) -> Option { (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 { 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 { pub t: T::Ty, } impl S { 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 = S { t: A(2) }; s.g(); } "#, ); } #[test] fn specialization_array_clone() { check_pass( r#" //- minicore: copy, derive, slice, index, coerce_unsized impl Clone for [T; N] { #[inline] fn clone(&self) -> Self { SpecArrayClone::clone(self) } } trait SpecArrayClone: Clone { fn clone(array: &[Self; N]) -> [Self; N]; } impl SpecArrayClone for T { #[inline] default fn clone(array: &[T; N]) -> [T; N] { // FIXME: panic here when we actually implement specialization. from_slice(array) } } fn from_slice(s: &[T]) -> [T; N] { [s[0]; N] } impl SpecArrayClone for T { #[inline] fn clone(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(mut x: [X; N]) { // -> impl FnOnce() { let c = || { x; }; c(); } fn main() { let s = f([X(1)]); //s(); } "#, ); } #[test] fn self_with_capital_s() { check_pass( r#" //- minicore: fn, add, copy struct S1; impl S1 { fn f() { Self; } } struct S2 { f1: i32, } impl S2 { fn f() { Self { f1: 5 }; } } struct S3(i32); impl S3 { fn f() { Self(2); Self; let this = Self; this(2); } } fn main() { S1::f(); S2::f(); S3::f(); } "#, ); } #[test] fn syscalls() { check_pass( r#" //- minicore: option extern "C" { pub unsafe extern "C" fn syscall(num: i64, ...) -> i64; } const SYS_getrandom: i64 = 318; fn should_not_reach() { _ // FIXME: replace this function with panic when that works } fn main() { let mut x: i32 = 0; let r = syscall(SYS_getrandom, &mut x, 4usize, 0); if r != 4 { should_not_reach(); } } "#, ) } #[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, ) -> 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 { fn a(&self) {} } impl A<()> for () {} struct B; impl B { pub fn b(s: &dyn A) -> Self { B } } struct C; impl C { fn c(a: &dyn A) -> 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(&()); } "#, ); }