mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-24 01:02:12 +00:00
And make more queries non-interned. Also flip the default for queries, now the default is to not intern and to intern a query you need to say `invoke_interned`.
986 lines
18 KiB
Rust
986 lines
18 KiB
Rust
use hir_def::db::DefDatabase;
|
|
use hir_expand::EditionedFileId;
|
|
use span::Edition;
|
|
use syntax::{TextRange, TextSize};
|
|
use test_fixture::WithFixture;
|
|
|
|
use crate::display::DisplayTarget;
|
|
use crate::{Interner, Substitution, db::HirDatabase, mir::MirLowerError, test_db::TestDB};
|
|
|
|
use super::{MirEvalError, interpret_mir};
|
|
|
|
fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), MirEvalError> {
|
|
let module_id = db.module_for_file(file_id.file_id(db));
|
|
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_signature(x).name.display(db, Edition::CURRENT).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, e))?;
|
|
|
|
let (result, output) = interpret_mir(db, body, false, None)?;
|
|
result?;
|
|
Ok((output.stdout().into_owned(), output.stderr().into_owned()))
|
|
}
|
|
|
|
fn check_pass(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
|
check_pass_and_stdio(ra_fixture, "", "");
|
|
}
|
|
|
|
fn check_pass_and_stdio(
|
|
#[rust_analyzer::rust_fixture] 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 lines = ra_fixture.lines().enumerate();
|
|
for (i, l) in lines {
|
|
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()))
|
|
};
|
|
let krate = db.module_for_file(file_id.file_id(&db)).krate();
|
|
e.pretty_print(&mut err, &db, span_formatter, DisplayTarget::from_crate(&db, krate))
|
|
.unwrap();
|
|
panic!("Error in interpreting: {err}");
|
|
}
|
|
Ok((stdout, stderr)) => {
|
|
assert_eq!(stdout, expected_stdout);
|
|
assert_eq!(stderr, expected_stderr);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn check_panic(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_panic: &str) {
|
|
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
|
|
let file_id = *file_ids.last().unwrap();
|
|
let e = eval_main(&db, file_id).unwrap_err();
|
|
assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {e:?}")), expected_panic);
|
|
}
|
|
|
|
fn check_error_with(
|
|
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
|
expect_err: impl FnOnce(MirEvalError) -> bool,
|
|
) {
|
|
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
|
|
let file_id = *file_ids.last().unwrap();
|
|
let e = eval_main(&db, file_id).unwrap_err();
|
|
assert!(expect_err(e));
|
|
}
|
|
|
|
#[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 panic_fmt() {
|
|
// panic!
|
|
// -> panic_2021 (builtin macro redirection)
|
|
// -> #[lang = "panic_fmt"] core::panicking::panic_fmt (hooked by CTFE for redirection)
|
|
// -> core::panicking::const_panic_fmt
|
|
// -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic)
|
|
// -> Err(ConstEvalError::Panic)
|
|
check_panic(
|
|
r#"
|
|
//- minicore: fmt, panic
|
|
fn main() {
|
|
panic!("hello, world!");
|
|
}
|
|
"#,
|
|
"hello, world!",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn panic_display() {
|
|
// panic!
|
|
// -> panic_2021 (builtin macro redirection)
|
|
// -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic)
|
|
// -> Err(ConstEvalError::Panic)
|
|
check_panic(
|
|
r#"
|
|
//- minicore: fmt, panic
|
|
|
|
fn main() {
|
|
panic!("{}", "hello, world!");
|
|
}
|
|
"#,
|
|
"hello, world!",
|
|
);
|
|
}
|
|
|
|
#[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_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<i32>);
|
|
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>(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; 3], y: &[u8; 3]) -> 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 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_getenv() {
|
|
check_pass(
|
|
r#"
|
|
//- minicore: sized
|
|
//- /main.rs env:foo=bar
|
|
|
|
type c_char = u8;
|
|
|
|
extern "C" {
|
|
pub fn getenv(s: *const c_char) -> *mut c_char;
|
|
}
|
|
|
|
fn should_not_reach() {
|
|
_ // FIXME: replace this function with panic when that works
|
|
}
|
|
|
|
fn main() {
|
|
let result = getenv(b"foo\0" as *const _);
|
|
if *result != b'b' {
|
|
should_not_reach();
|
|
}
|
|
let result = (result as usize + 1) as *const c_char;
|
|
if *result != b'a' {
|
|
should_not_reach();
|
|
}
|
|
let result = (result as usize + 1) as *const c_char;
|
|
if *result != b'r' {
|
|
should_not_reach();
|
|
}
|
|
let result = (result as usize + 1) as *const c_char;
|
|
if *result != 0 {
|
|
should_not_reach();
|
|
}
|
|
let result = getenv(b"not found\0" as *const _);
|
|
if result as usize != 0 {
|
|
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<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, dispatch_from_dyn
|
|
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(&());
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn long_str_eq_same_prefix() {
|
|
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() {
|
|
// More than 16 bytes, the size of `i128`.
|
|
let long_str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab";
|
|
let output = match long_str {
|
|
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => b"true" as &[u8],
|
|
_ => b"false",
|
|
};
|
|
write(1, &output[0], output.len());
|
|
}
|
|
"#,
|
|
"false",
|
|
"",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn regression_19021() {
|
|
check_pass(
|
|
r#"
|
|
//- minicore: deref
|
|
use core::ops::Deref;
|
|
|
|
#[lang = "owned_box"]
|
|
struct Box<T>(T);
|
|
|
|
impl<T> Deref for Box<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
struct Foo;
|
|
|
|
fn main() {
|
|
let x = Box(Foo);
|
|
let y = &Foo;
|
|
|
|
|| match x {
|
|
ref x => x,
|
|
_ => y,
|
|
};
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn regression_19177() {
|
|
check_error_with(
|
|
r#"
|
|
//- minicore: copy
|
|
trait Foo {}
|
|
trait Bar {}
|
|
trait Baz {}
|
|
trait Qux {
|
|
type Assoc;
|
|
}
|
|
|
|
fn main<'a, T: Foo + Bar + Baz>(
|
|
x: &T,
|
|
y: (),
|
|
z: &'a dyn Qux<Assoc = T>,
|
|
w: impl Foo + Bar,
|
|
) {
|
|
}
|
|
"#,
|
|
|e| matches!(e, MirEvalError::MirLowerError(_, MirLowerError::GenericArgNotProvided(..))),
|
|
);
|
|
}
|