Auto merge of #151352 - Zalathar:rollup-nUIzD3P, r=Zalathar

Rollup of 4 pull requests

Successful merges:

 - rust-lang/rust#151080 (fix(build-manifest): enable docs target fallback for `rustc-docs`)
 - rust-lang/rust#151328 (Fix capitalization of diag messages)
 - rust-lang/rust#151341 (miri subtree update)
 - rust-lang/rust#151349 (Add myself to the review rotation)

r? @ghost
This commit is contained in:
bors 2026-01-19 06:22:30 +00:00
commit 53b6f89be2
69 changed files with 1390 additions and 799 deletions

View file

@ -230,7 +230,7 @@ attr_parsing_unstable_cfg_target_compact =
compact `cfg(target(..))` is experimental and subject to change
attr_parsing_unstable_feature_bound_incompatible_stability = item annotated with `#[unstable_feature_bound]` should not be stable
.help = If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
.help = if this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
attr_parsing_unsupported_instruction_set = target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]`

View file

@ -140,9 +140,9 @@ const_eval_incompatible_return_types =
const_eval_interior_mutable_borrow_escaping =
interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
.label = this borrow of an interior mutable value refers to such a temporary
.note = Temporaries in constants and statics can have their lifetime extended until the end of the program
.note2 = To avoid accidentally creating global mutable state, such temporaries must be immutable
.help = If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
.note = temporaries in constants and statics can have their lifetime extended until the end of the program
.note2 = to avoid accidentally creating global mutable state, such temporaries must be immutable
.help = if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
const_eval_intern_kind = {$kind ->
[static] static
@ -225,9 +225,9 @@ const_eval_modified_global =
const_eval_mutable_borrow_escaping =
mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
.label = this mutable borrow refers to such a temporary
.note = Temporaries in constants and statics can have their lifetime extended until the end of the program
.note2 = To avoid accidentally creating global mutable state, such temporaries must be immutable
.help = If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
.note = temporaries in constants and statics can have their lifetime extended until the end of the program
.note2 = to avoid accidentally creating global mutable state, such temporaries must be immutable
.help = if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}

View file

@ -542,7 +542,7 @@ lint_invalid_style = {$is_used_as_inner ->
[false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]`
*[other] the `#![{$name}]` attribute can only be used at the crate root
}
.note = This attribute does not have an `!`, which means it is applied to this {$target}
.note = this attribute does not have an `!`, which means it is applied to this {$target}
lint_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
.warn = {-lint_previously_accepted}

View file

@ -62,7 +62,7 @@ monomorphize_encountered_error_while_instantiating_global_asm =
monomorphize_large_assignments =
moving {$size} bytes
.label = value moved from here
.note = The current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
.note = the current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
monomorphize_no_optimized_mir =
missing optimized MIR for `{$instance}` in the crate `{$crate_name}`

View file

@ -376,7 +376,7 @@ passes_no_main_function =
}
.consider_adding_main_to_file = consider adding a `main` function to `{$filename}`
.consider_adding_main_at_crate = consider adding a `main` function at the crate level
.teach_note = If you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/
.teach_note = if you don't know the basics of Rust, you can go look to the Rust Book to get started: https://doc.rust-lang.org/book/
.non_function_main = non-function item at `crate::main` is found
passes_non_exhaustive_with_default_field_values =

View file

@ -14,11 +14,11 @@ use crate::versions::{PkgType, Versions};
include!(concat!(env!("OUT_DIR"), "/targets.rs"));
/// This allows the manifest to contain rust-docs for hosts that don't build
/// docs.
/// This allows the manifest to contain rust-docs and rustc-docs for hosts
/// that don't build certain docs.
///
/// Tuples of `(host_partial, host_instead)`. If the host does not have the
/// rust-docs component available, then if the host name contains
/// corresponding docs component available, then if the host name contains
/// `host_partial`, it will use the docs from `host_instead` instead.
///
/// The order here matters, more specific entries should be first.
@ -392,9 +392,9 @@ impl Builder {
let t = Target::from_compressed_tar(self, &tarball_name!(fallback_target));
// Fallbacks should typically be available on 'production' builds
// but may not be available for try builds, which only build one target by
// default. Ideally we'd gate this being a hard error on whether we're in a
// production build or not, but it's not information that's readily available
// here.
// default. It is also possible that `rust-docs` and `rustc-docs` differ in
// availability per target. Thus, we take the first available fallback we can
// find.
if !t.available {
eprintln!(
"{:?} not available for fallback",

View file

@ -133,10 +133,7 @@ impl PkgType {
/// Whether to package these target-specific docs for another similar target.
pub(crate) fn use_docs_fallback(&self) -> bool {
match self {
PkgType::JsonDocs | PkgType::HtmlDocs => true,
_ => false,
}
matches!(self, PkgType::JsonDocs | PkgType::HtmlDocs | PkgType::RustcDocs)
}
}

View file

@ -228,7 +228,8 @@ and macOS targets are usually on par. Windows is supported less well.
### Running tests in parallel
Though it implements Rust threading, Miri itself is a single-threaded interpreter.
Though it implements Rust threading, Miri itself is a single-threaded interpreter
(it works like a multi-threaded OS on a single-core CPU).
This means that when running `cargo miri test`, you will probably see a dramatic
increase in the amount of time it takes to run your whole test suite due to the
inherent interpreter slowdown and a loss of parallelism.

View file

@ -1 +1 @@
f57b9e6f565a1847e83a63f3e90faa3870536c1f
b6fdaf2a15736cbccf248b532f48e33179614d40

View file

@ -710,7 +710,7 @@ fn main() {
if !miri_config.native_lib.is_empty() && miri_config.provenance_mode == ProvenanceMode::Strict {
fatal_error!("strict provenance is not compatible with calling native functions");
}
// Native calls and many-seeds are an "intersting" combination.
// Native calls and many-seeds are an "interesting" combination.
if !miri_config.native_lib.is_empty() && many_seeds.is_some() {
eprintln!(
"warning: `-Zmiri-many-seeds` runs multiple instances of the program in the same address space, \

View file

@ -2,7 +2,7 @@ use std::collections::hash_map::Entry;
use std::io::Write;
use std::path::Path;
use rustc_abi::{Align, CanonAbi, Size};
use rustc_abi::{Align, CanonAbi, ExternAbi, Size};
use rustc_ast::expand::allocator::NO_ALLOC_SHIM_IS_UNSTABLE;
use rustc_data_structures::either::Either;
use rustc_hir::attrs::Linkage;
@ -435,6 +435,40 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Return value: 0 on success, otherwise the size it would have needed.
this.write_int(if success { 0 } else { needed_size }, dest)?;
}
"miri_thread_spawn" => {
// FIXME: `check_shim_sig` does not work with function pointers.
let [start_routine, func_arg] =
this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
let start_routine = this.read_pointer(start_routine)?;
let func_arg = this.read_immediate(func_arg)?;
this.start_regular_thread(
Some(dest.clone()),
start_routine,
ExternAbi::Rust,
func_arg,
this.machine.layouts.unit,
)?;
}
"miri_thread_join" => {
let [thread_id] = this.check_shim_sig(
shim_sig!(extern "Rust" fn(usize) -> bool),
link_name,
abi,
args,
)?;
let thread = this.read_target_usize(thread_id)?;
if let Ok(thread) = this.thread_id_try_from(thread) {
this.join_thread_exclusive(
thread,
/* success_retval */ Scalar::from_bool(true),
dest,
)?;
} else {
this.write_scalar(Scalar::from_bool(false), dest)?;
}
}
// Hint that a loop is spinning indefinitely.
"miri_spin_loop" => {
let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;

View file

@ -17,11 +17,18 @@ pub struct ShimSig<'tcx, const ARGS: usize> {
/// Construct a `ShimSig` with convenient syntax:
/// ```rust,ignore
/// shim_sig!(this, extern "C" fn (*const T, i32) -> usize)
/// shim_sig!(extern "C" fn (*const T, i32) -> usize)
/// ```
///
/// The following types are supported:
/// - primitive integer types
/// - `()`
/// - (thin) raw pointers, written `*const _` and `*mut _` since the pointee type is irrelevant
/// - `$crate::$mod::...::$ty` for a type from the given crate (most commonly that is `libc`)
/// - `winapi::$ty` for a type from `std::sys::pal::windows::c`
#[macro_export]
macro_rules! shim_sig {
(extern $abi:literal fn($($arg:ty),*) -> $ret:ty) => {
(extern $abi:literal fn($($arg:ty),* $(,)?) -> $ret:ty) => {
|this| $crate::shims::sig::ShimSig {
abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"),
args: [$(shim_sig_arg!(this, $arg)),*],
@ -50,9 +57,13 @@ macro_rules! shim_sig_arg {
"u128" => $this.tcx.types.u128,
"usize" => $this.tcx.types.usize,
"()" => $this.tcx.types.unit,
"bool" => $this.tcx.types.bool,
"*const _" => $this.machine.layouts.const_raw_ptr.ty,
"*mut _" => $this.machine.layouts.mut_raw_ptr.ty,
ty if let Some(libc_ty) = ty.strip_prefix("libc::") => $this.libc_ty_layout(libc_ty).ty,
ty if let Some(win_ty) = ty.strip_prefix("winapi::") =>
$this.windows_ty_layout(win_ty).ty,
ty if ty.contains("::") =>
helpers::path_ty_layout($this, &ty.split("::").collect::<Vec<_>>()).ty,
ty => panic!("unsupported signature type {ty:?}"),
}
}};

View file

@ -28,21 +28,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
match link_name.as_str() {
// File related shims
"stat" => {
// FIXME: This does not have a direct test (#3179).
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" => {
// FIXME: This does not have a direct test (#3179).
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"readdir" => {
// FIXME: This does not have a direct test (#3179).
let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.readdir64("dirent", dirp)?;
this.write_scalar(result, dest)?;
}
"pread64" => {
// FIXME: This does not have a direct test (#3179).
let [fd, buf, count, offset] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize),
link_name,
@ -56,6 +60,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.read(fd, buf, count, Some(offset), dest)?;
}
"pwrite64" => {
// FIXME: This does not have a direct test (#3179).
let [fd, buf, n, offset] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize),
link_name,
@ -70,6 +75,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write(fd, buf, count, Some(offset), dest)?;
}
"lseek64" => {
// FIXME: This does not have a direct test (#3179).
let [fd, offset, whence] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t),
link_name,

View file

@ -143,6 +143,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"getcwd" => {
// FIXME: This does not have a direct test (#3179).
let [buf, size] = this.check_shim_sig(
shim_sig!(extern "C" fn(*mut _, usize) -> *mut _),
link_name,
@ -153,6 +154,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_pointer(result, dest)?;
}
"chdir" => {
// FIXME: This does not have a direct test (#3179).
let [path] = this.check_shim_sig(
shim_sig!(extern "C" fn(*const _) -> i32),
link_name,
@ -209,6 +211,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write(fd, buf, count, None, dest)?;
}
"pread" => {
// FIXME: This does not have a direct test (#3179).
let [fd, buf, count, offset] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off_t) -> isize),
link_name,
@ -222,6 +225,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.read(fd, buf, count, Some(offset), dest)?;
}
"pwrite" => {
// FIXME: This does not have a direct test (#3179).
let [fd, buf, n, offset] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32, *const _, usize, libc::off_t) -> isize),
link_name,
@ -299,6 +303,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"unlink" => {
// FIXME: This does not have a direct test (#3179).
let [path] = this.check_shim_sig(
shim_sig!(extern "C" fn(*const _) -> i32),
link_name,
@ -309,6 +314,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"symlink" => {
// FIXME: This does not have a direct test (#3179).
let [target, linkpath] = this.check_shim_sig(
shim_sig!(extern "C" fn(*const _, *const _) -> i32),
link_name,
@ -324,6 +330,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"rename" => {
// FIXME: This does not have a direct test (#3179).
let [oldpath, newpath] = this.check_shim_sig(
shim_sig!(extern "C" fn(*const _, *const _) -> i32),
link_name,
@ -334,6 +341,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"mkdir" => {
// FIXME: This does not have a direct test (#3179).
let [path, mode] = this.check_shim_sig(
shim_sig!(extern "C" fn(*const _, libc::mode_t) -> i32),
link_name,
@ -344,6 +352,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"rmdir" => {
// FIXME: This does not have a direct test (#3179).
let [path] = this.check_shim_sig(
shim_sig!(extern "C" fn(*const _) -> i32),
link_name,
@ -354,6 +363,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"opendir" => {
// FIXME: This does not have a direct test (#3179).
let [name] = this.check_shim_sig(
shim_sig!(extern "C" fn(*const _) -> *mut _),
link_name,
@ -364,6 +374,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"closedir" => {
// FIXME: This does not have a direct test (#3179).
let [dirp] = this.check_shim_sig(
shim_sig!(extern "C" fn(*mut _) -> i32),
link_name,
@ -374,6 +385,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"lseek" => {
// FIXME: This does not have a direct test (#3179).
let [fd, offset, whence] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32, libc::off_t, i32) -> libc::off_t),
link_name,
@ -398,6 +410,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"fsync" => {
// FIXME: This does not have a direct test (#3179).
let [fd] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32) -> i32),
link_name,
@ -408,6 +421,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"fdatasync" => {
// FIXME: This does not have a direct test (#3179).
let [fd] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32) -> i32),
link_name,
@ -659,6 +673,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_null(dest)?;
}
"pthread_key_delete" => {
// FIXME: This does not have a direct test (#3179).
let [key] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
this.machine.tls.delete_tls_key(key)?;
@ -666,6 +681,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_null(dest)?;
}
"pthread_getspecific" => {
// FIXME: This does not have a direct test (#3179).
let [key] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
let active_thread = this.active_thread();
@ -673,6 +689,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(ptr, dest)?;
}
"pthread_setspecific" => {
// FIXME: This does not have a direct test (#3179).
let [key, new_ptr] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
@ -833,6 +850,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
"sched_yield" => {
// FIXME: This does not have a direct test (#3179).
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.sched_yield()?;
this.write_null(dest)?;
@ -941,6 +959,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"pthread_atfork" => {
// FIXME: This does not have a direct test (#3179).
let [prepare, parent, child] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.read_pointer(prepare)?;

View file

@ -139,11 +139,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// For those, we both intercept `func` and `call@FBSD_1.0` symbols cases
// since freebsd 12 the former form can be expected.
"stat" | "stat@FBSD_1.0" => {
// FIXME: This does not have a direct test (#3179).
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat@FBSD_1.0" => {
// FIXME: This does not have a direct test (#3179).
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.lstat(path, buf)?;
this.write_scalar(result, dest)?;
@ -154,6 +156,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"readdir" | "readdir@FBSD_1.0" => {
// FIXME: This does not have a direct test (#3179).
let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.readdir64("dirent", dirp)?;
this.write_scalar(result, dest)?;

View file

@ -45,6 +45,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"pread64" => {
// FIXME: This does not have a direct test (#3179).
let [fd, buf, count, offset] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize),
link_name,
@ -58,6 +59,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.read(fd, buf, count, Some(offset), dest)?;
}
"pwrite64" => {
// FIXME: This does not have a direct test (#3179).
let [fd, buf, n, offset] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize),
link_name,
@ -72,6 +74,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write(fd, buf, count, Some(offset), dest)?;
}
"lseek64" => {
// FIXME: This does not have a direct test (#3179).
let [fd, offset, whence] = this.check_shim_sig(
shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t),
link_name,
@ -111,6 +114,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"readdir64" => {
// FIXME: This does not have a direct test (#3179).
let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.readdir64("dirent64", dirp)?;
this.write_scalar(result, dest)?;
@ -122,6 +126,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"statx" => {
// FIXME: This does not have a direct test (#3179).
let [dirfd, pathname, flags, mask, statxbuf] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;

View file

@ -47,11 +47,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"stat" | "stat$INODE64" => {
// FIXME: This does not have a direct test (#3179).
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat$INODE64" => {
// FIXME: This does not have a direct test (#3179).
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.lstat(path, buf)?;
this.write_scalar(result, dest)?;
@ -62,11 +64,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"opendir$INODE64" => {
// FIXME: This does not have a direct test (#3179).
let [name] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.opendir(name)?;
this.write_scalar(result, dest)?;
}
"readdir_r" | "readdir_r$INODE64" => {
// FIXME: This does not have a direct test (#3179).
let [dirp, entry, result] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.macos_readdir_r(dirp, entry, result)?;
@ -87,6 +91,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Environment related shims
"_NSGetEnviron" => {
// FIXME: This does not have a direct test (#3179).
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let environ = this.machine.env_vars.unix().environ();
this.write_pointer(environ, dest)?;
@ -111,6 +116,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"mach_timebase_info" => {
// FIXME: This does not have a direct test (#3179).
let [info] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.mach_timebase_info(info)?;
this.write_scalar(result, dest)?;
@ -118,14 +124,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Access to command-line arguments
"_NSGetArgc" => {
// FIXME: This does not have a direct test (#3179).
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?;
}
"_NSGetArgv" => {
// FIXME: This does not have a direct test (#3179).
let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
}
"_NSGetExecutablePath" => {
// FIXME: This does not have a direct test (#3179).
let [buf, bufsize] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.check_no_isolation("`_NSGetExecutablePath`")?;
@ -168,12 +177,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Querying system information
"pthread_get_stackaddr_np" => {
// FIXME: This does not have a direct test (#3179).
let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.read_target_usize(thread)?;
let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
this.write_scalar(stack_addr, dest)?;
}
"pthread_get_stacksize_np" => {
// FIXME: This does not have a direct test (#3179).
let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.read_target_usize(thread)?;
let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());

View file

@ -91,16 +91,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// File related shims
"stat" => {
// FIXME: This does not have a direct test (#3179).
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" => {
// FIXME: This does not have a direct test (#3179).
let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"readdir" => {
// FIXME: This does not have a direct test (#3179).
let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.readdir64("dirent", dirp)?;
this.write_scalar(result, dest)?;
@ -122,6 +125,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"stack_getbounds" => {
// FIXME: This does not have a direct test (#3179).
let [stack] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?;
@ -140,6 +144,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"pset_info" => {
// FIXME: This does not have a direct test (#3179).
let [pset, tpe, cpus, list] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
// We do not need to handle the current process cpu mask, available_parallelism

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@ use rustc_target::callconv::FnAbi;
use super::{
ShiftOp, horizontal_bin_op, mpsadbw, packssdw, packsswb, packusdw, packuswb, permute, pmaddbw,
pmulhrsw, psadbw, pshufb, psign, shift_simd_by_scalar,
pmaddwd, pmulhrsw, psadbw, pshufb, psign, shift_simd_by_scalar,
};
use crate::*;
@ -232,33 +232,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
let (dest, dest_len) = this.project_to_simd(dest)?;
assert_eq!(left_len, right_len);
assert_eq!(dest_len.strict_mul(2), left_len);
for i in 0..dest_len {
let j1 = i.strict_mul(2);
let left1 = this.read_scalar(&this.project_index(&left, j1)?)?.to_i16()?;
let right1 = this.read_scalar(&this.project_index(&right, j1)?)?.to_i16()?;
let j2 = j1.strict_add(1);
let left2 = this.read_scalar(&this.project_index(&left, j2)?)?.to_i16()?;
let right2 = this.read_scalar(&this.project_index(&right, j2)?)?.to_i16()?;
let dest = this.project_index(&dest, i)?;
// Multiplications are i16*i16->i32, which will not overflow.
let mul1 = i32::from(left1).strict_mul(right1.into());
let mul2 = i32::from(left2).strict_mul(right2.into());
// However, this addition can overflow in the most extreme case
// (-0x8000)*(-0x8000)+(-0x8000)*(-0x8000) = 0x80000000
let res = mul1.wrapping_add(mul2);
this.write_scalar(Scalar::from_i32(res), &dest)?;
}
pmaddwd(this, left, right, dest)?;
}
_ => return interp_ok(EmulateItemResult::NotSupported),
}

View file

@ -3,7 +3,7 @@ use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::FnAbi;
use super::{permute, pmaddbw, psadbw, pshufb};
use super::{packssdw, packsswb, packusdw, packuswb, permute, pmaddbw, pmaddwd, psadbw, pshufb};
use crate::*;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@ -88,6 +88,15 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
psadbw(this, left, right, dest)?
}
// Used to implement the _mm512_madd_epi16 function.
"pmaddw.d.512" => {
this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
pmaddwd(this, left, right, dest)?;
}
// Used to implement the _mm512_maddubs_epi16 function.
"pmaddubs.w.512" => {
let [left, right] =
@ -121,6 +130,38 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
vpdpbusd(this, src, a, b, dest)?;
}
// Used to implement the _mm512_packs_epi16 function
"packsswb.512" => {
this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packsswb(this, a, b, dest)?;
}
// Used to implement the _mm512_packus_epi16 function
"packuswb.512" => {
this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packuswb(this, a, b, dest)?;
}
// Used to implement the _mm512_packs_epi32 function
"packssdw.512" => {
this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packssdw(this, a, b, dest)?;
}
// Used to implement the _mm512_packus_epi32 function
"packusdw.512" => {
this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
packusdw(this, a, b, dest)?;
}
_ => return interp_ok(EmulateItemResult::NotSupported),
}
interp_ok(EmulateItemResult::NeedsReturn)

View file

@ -964,6 +964,52 @@ fn psadbw<'tcx>(
interp_ok(())
}
/// Multiply packed signed 16-bit integers in `left` and `right`, producing intermediate signed 32-bit integers.
/// Horizontally add adjacent pairs of intermediate 32-bit integers, and pack the results in `dest`.
///
/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_madd_epi16>
/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_madd_epi16>
/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_madd_epi16>
fn pmaddwd<'tcx>(
ecx: &mut crate::MiriInterpCx<'tcx>,
left: &OpTy<'tcx>,
right: &OpTy<'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, ()> {
let (left, left_len) = ecx.project_to_simd(left)?;
let (right, right_len) = ecx.project_to_simd(right)?;
let (dest, dest_len) = ecx.project_to_simd(dest)?;
// fn pmaddwd(a: i16x8, b: i16x8) -> i32x4;
// fn pmaddwd(a: i16x16, b: i16x16) -> i32x8;
// fn vpmaddwd(a: i16x32, b: i16x32) -> i32x16;
assert_eq!(left_len, right_len);
assert_eq!(dest_len.strict_mul(2), left_len);
for i in 0..dest_len {
let j1 = i.strict_mul(2);
let left1 = ecx.read_scalar(&ecx.project_index(&left, j1)?)?.to_i16()?;
let right1 = ecx.read_scalar(&ecx.project_index(&right, j1)?)?.to_i16()?;
let j2 = j1.strict_add(1);
let left2 = ecx.read_scalar(&ecx.project_index(&left, j2)?)?.to_i16()?;
let right2 = ecx.read_scalar(&ecx.project_index(&right, j2)?)?.to_i16()?;
let dest = ecx.project_index(&dest, i)?;
// Multiplications are i16*i16->i32, which will not overflow.
let mul1 = i32::from(left1).strict_mul(right1.into());
let mul2 = i32::from(left2).strict_mul(right2.into());
// However, this addition can overflow in the most extreme case
// (-0x8000)*(-0x8000)+(-0x8000)*(-0x8000) = 0x80000000
let res = mul1.wrapping_add(mul2);
ecx.write_scalar(Scalar::from_i32(res), &dest)?;
}
interp_ok(())
}
/// Multiplies packed 8-bit unsigned integers from `left` and packed
/// signed 8-bit integers from `right` into 16-bit signed integers. Then,
/// the saturating sum of the products with indices `2*i` and `2*i+1`

View file

@ -6,7 +6,7 @@ use rustc_target::callconv::FnAbi;
use super::{
FloatBinOp, ShiftOp, bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int,
packssdw, packsswb, packuswb, psadbw, shift_simd_by_scalar,
packssdw, packsswb, packuswb, pmaddwd, psadbw, shift_simd_by_scalar,
};
use crate::*;
@ -286,33 +286,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
let (dest, dest_len) = this.project_to_simd(dest)?;
assert_eq!(left_len, right_len);
assert_eq!(dest_len.strict_mul(2), left_len);
for i in 0..dest_len {
let j1 = i.strict_mul(2);
let left1 = this.read_scalar(&this.project_index(&left, j1)?)?.to_i16()?;
let right1 = this.read_scalar(&this.project_index(&right, j1)?)?.to_i16()?;
let j2 = j1.strict_add(1);
let left2 = this.read_scalar(&this.project_index(&left, j2)?)?.to_i16()?;
let right2 = this.read_scalar(&this.project_index(&right, j2)?)?.to_i16()?;
let dest = this.project_index(&dest, i)?;
// Multiplications are i16*i16->i32, which will not overflow.
let mul1 = i32::from(left1).strict_mul(right1.into());
let mul2 = i32::from(left2).strict_mul(right2.into());
// However, this addition can overflow in the most extreme case
// (-0x8000)*(-0x8000)+(-0x8000)*(-0x8000) = 0x80000000
let res = mul1.wrapping_add(mul2);
this.write_scalar(Scalar::from_i32(res), &dest)?;
}
pmaddwd(this, left, right, dest)?;
}
_ => return interp_ok(EmulateItemResult::NotSupported),
}

View file

@ -12,7 +12,7 @@ use windows_sys::Win32::System::Threading::{INFINITE, WaitForSingleObject};
// XXX HACK: This is how miri represents the handle for thread 0.
// This value can be "legitimately" obtained by using `GetCurrentThread` with `DuplicateHandle`
// but miri does not implement `DuplicateHandle` yet.
// but miri does not implement `DuplicateHandle` yet. (FIXME: it does now.)
const MAIN_THREAD: HANDLE = (2i32 << 29) as HANDLE;
fn main() {

View file

@ -12,6 +12,8 @@ fn main() {
#[cfg(not(target_os = "solaris"))]
getrandom_01::getrandom(&mut data).unwrap();
// On Windows, getrandom 0.2 uses the wrong return type for BCryptGenRandom
#[cfg(not(target_os = "windows"))]
getrandom_02::getrandom(&mut data).unwrap();
getrandom_03::fill(&mut data).unwrap();

View file

@ -0,0 +1,13 @@
//@ignore-target: windows # no libc
//@ revisions: default null
//@[null] compile-flags: -Zmiri-mute-stdout-stderr
fn main() {
// This is std library UB, but that's not relevant since we're
// only interacting with libc here.
unsafe {
libc::close(0);
libc::close(1);
libc::close(2);
}
}

View file

@ -7,6 +7,10 @@ use std::mem::{size_of, size_of_val};
use libc::{cpu_set_t, sched_getaffinity, sched_setaffinity};
#[path = "../../utils/libc.rs"]
mod libc_utils;
use libc_utils::errno_check;
// If pid is zero, then the calling thread is used.
const PID: i32 = 0;
@ -41,8 +45,7 @@ fn configure_unavailable_cpu() {
// Safety: valid value for this type
let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) });
// by default, only available CPUs are configured
for i in 0..cpu_count {
@ -53,11 +56,9 @@ fn configure_unavailable_cpu() {
// configure CPU that we don't have
unsafe { libc::CPU_SET(cpu_count, &mut cpuset) };
let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) });
let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) });
// the CPU is not set because it is not available
assert!(!unsafe { libc::CPU_ISSET(cpu_count, &cpuset) });
@ -70,11 +71,11 @@ fn large_set() {
// i.e. this has 2048 bits, twice the standard number
let mut cpuset = [u64::MAX; 32];
let err = unsafe { sched_setaffinity(PID, size_of_val(&cpuset), cpuset.as_ptr().cast()) };
assert_eq!(err, 0);
errno_check(unsafe { sched_setaffinity(PID, size_of_val(&cpuset), cpuset.as_ptr().cast()) });
let err = unsafe { sched_getaffinity(PID, size_of_val(&cpuset), cpuset.as_mut_ptr().cast()) };
assert_eq!(err, 0);
errno_check(unsafe {
sched_getaffinity(PID, size_of_val(&cpuset), cpuset.as_mut_ptr().cast())
});
}
fn get_small_cpu_mask() {
@ -91,8 +92,7 @@ fn get_small_cpu_mask() {
assert_eq!(std::io::Error::last_os_error().kind(), std::io::ErrorKind::InvalidInput);
} else {
// other whole multiples of the size of c_ulong works
let err = unsafe { sched_getaffinity(PID, i, &mut cpuset) };
assert_eq!(err, 0, "fail for {i}");
errno_check(unsafe { sched_getaffinity(PID, i, &mut cpuset) });
}
// anything else returns an error
@ -107,8 +107,7 @@ fn get_small_cpu_mask() {
fn set_small_cpu_mask() {
let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) });
// setting a mask of size 0 is invalid
let err = unsafe { sched_setaffinity(PID, 0, &cpuset) };
@ -122,8 +121,7 @@ fn set_small_cpu_mask() {
if cfg!(target_endian = "little") { 1 } else { core::mem::size_of::<std::ffi::c_ulong>() };
for i in cpu_zero_included_length..24 {
let err = unsafe { sched_setaffinity(PID, i, &cpuset) };
assert_eq!(err, 0, "fail for {i}");
errno_check(unsafe { sched_setaffinity(PID, i, &cpuset) });
}
}
@ -135,8 +133,7 @@ fn set_custom_cpu_mask() {
let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
// at the start, thread 1 should be set
let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) });
assert!(unsafe { libc::CPU_ISSET(1, &cpuset) });
// make a valid mask
@ -144,12 +141,10 @@ fn set_custom_cpu_mask() {
unsafe { libc::CPU_SET(0, &mut cpuset) };
// giving a smaller mask is fine
let err = unsafe { sched_setaffinity(PID, 8, &cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_setaffinity(PID, 8, &cpuset) });
// and actually disables other threads
let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) });
assert!(unsafe { !libc::CPU_ISSET(1, &cpuset) });
// it is important that we reset the cpu mask now for future tests
@ -157,8 +152,7 @@ fn set_custom_cpu_mask() {
unsafe { libc::CPU_SET(i, &mut cpuset) };
}
let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) });
}
fn parent_child() {
@ -170,15 +164,13 @@ fn parent_child() {
let mut parent_cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
unsafe { libc::CPU_SET(0, &mut parent_cpuset) };
let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &parent_cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &parent_cpuset) });
std::thread::scope(|spawner| {
spawner.spawn(|| {
let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) });
// the child inherits its parent's set
assert!(unsafe { libc::CPU_ISSET(0, &cpuset) });
@ -189,8 +181,7 @@ fn parent_child() {
});
});
let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut parent_cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut parent_cpuset) });
// the parent's set should be unaffected
assert!(unsafe { !libc::CPU_ISSET(1, &parent_cpuset) });
@ -201,8 +192,7 @@ fn parent_child() {
unsafe { libc::CPU_SET(i, &mut cpuset) };
}
let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) };
assert_eq!(err, 0);
errno_check(unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) });
}
fn main() {

View file

@ -7,6 +7,8 @@ use std::thread;
#[path = "../../utils/libc.rs"]
mod libc_utils;
use libc_utils::epoll::*;
use libc_utils::*;
// This is a set of testcases for blocking epoll.
@ -19,48 +21,20 @@ fn main() {
multiple_events_wake_multiple_threads();
}
// Using `as` cast since `EPOLLET` wraps around
const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _;
#[track_caller]
fn check_epoll_wait<const N: usize>(
epfd: i32,
expected_notifications: &[(u32, u64)],
timeout: i32,
) {
let epoll_event = libc::epoll_event { events: 0, u64: 0 };
let mut array: [libc::epoll_event; N] = [epoll_event; N];
let maxsize = N;
let array_ptr = array.as_mut_ptr();
let res = unsafe { libc::epoll_wait(epfd, array_ptr, maxsize.try_into().unwrap(), timeout) };
if res < 0 {
panic!("epoll_wait failed: {}", std::io::Error::last_os_error());
}
let got_notifications =
unsafe { std::slice::from_raw_parts(array_ptr, res.try_into().unwrap()) };
let got_notifications = got_notifications.iter().map(|e| (e.events, e.u64)).collect::<Vec<_>>();
assert_eq!(got_notifications, expected_notifications, "got wrong notifications");
}
// This test allows epoll_wait to block, then unblock without notification.
fn test_epoll_block_without_notification() {
// Create an epoll instance.
let epfd = unsafe { libc::epoll_create1(0) };
assert_ne!(epfd, -1);
let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap();
// Create an eventfd instances.
let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC;
let fd = unsafe { libc::eventfd(0, flags) };
let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap();
// Register eventfd with epoll.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// epoll_wait to clear notification.
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
let expected_value = fd as u64;
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0);
check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLOUT, data: fd }], 0);
// This epoll wait blocks, and timeout without notification.
check_epoll_wait::<1>(epfd, &[], 5);
@ -69,102 +43,74 @@ fn test_epoll_block_without_notification() {
// This test triggers notification and unblocks the epoll_wait before timeout.
fn test_epoll_block_then_unblock() {
// Create an epoll instance.
let epfd = unsafe { libc::epoll_create1(0) };
assert_ne!(epfd, -1);
let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap();
// Create a socketpair instance.
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Register one side of the socketpair with epoll.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// epoll_wait to clear notification.
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
let expected_value = fds[0] as u64;
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0);
check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLOUT, data: fds[0] }], 0);
// epoll_wait before triggering notification so it will block then get unblocked before timeout.
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
let expected_value = fds[0] as u64;
let thread1 = thread::spawn(move || {
thread::yield_now();
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
write_all_from_slice(fds[1], b"abcde").unwrap();
});
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 10);
check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLIN | libc::EPOLLOUT, data: fds[0] }], 10);
thread1.join().unwrap();
}
// This test triggers a notification after epoll_wait times out.
fn test_notification_after_timeout() {
// Create an epoll instance.
let epfd = unsafe { libc::epoll_create1(0) };
assert_ne!(epfd, -1);
let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap();
// Create a socketpair instance.
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Register one side of the socketpair with epoll.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// epoll_wait to clear notification.
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
let expected_value = fds[0] as u64;
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0);
check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLOUT, data: fds[0] }], 0);
// epoll_wait timeouts without notification.
check_epoll_wait::<1>(epfd, &[], 10);
// Trigger epoll notification after timeout.
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
write_all_from_slice(fds[1], b"abcde").unwrap();
// Check the result of the notification.
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
let expected_value = fds[0] as u64;
check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 10);
check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLIN | libc::EPOLLOUT, data: fds[0] }], 10);
}
// This test shows a data_race before epoll had vector clocks added.
fn test_epoll_race() {
// Create an epoll instance.
let epfd = unsafe { libc::epoll_create1(0) };
assert_ne!(epfd, -1);
let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap();
// Create an eventfd instance.
let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC;
let fd = unsafe { libc::eventfd(0, flags) };
let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap();
// Register eventfd with the epoll instance.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
static mut VAL: u8 = 0;
let thread1 = thread::spawn(move || {
// Write to the static mut variable.
unsafe { VAL = 1 };
// Write to the eventfd instance.
let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) };
// write returns number of bytes written, which is always 8.
assert_eq!(res, 8);
write_all_from_slice(fd, &1_u64.to_ne_bytes()).unwrap();
});
thread::yield_now();
// epoll_wait for the event to happen.
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
let expected_value = u64::try_from(fd).unwrap();
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)], -1);
check_epoll_wait::<8>(epfd, &[Ev { events: (libc::EPOLLIN | libc::EPOLLOUT), data: fd }], -1);
// Read from the static mut variable.
#[allow(static_mut_refs)]
unsafe {
@ -177,35 +123,29 @@ fn test_epoll_race() {
/// epoll it is blocked on.
fn wakeup_on_new_interest() {
// Create an epoll instance.
let epfd = unsafe { libc::epoll_create1(0) };
assert_ne!(epfd, -1);
let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap();
// Create a socketpair instance.
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Write to fd[0]
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
write_all_from_slice(fds[0], b"abcde").unwrap();
// Block a thread on the epoll instance.
let t = std::thread::spawn(move || {
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
let expected_value = u64::try_from(fds[1]).unwrap();
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)], -1);
check_epoll_wait::<8>(
epfd,
&[Ev { events: libc::EPOLLIN | libc::EPOLLOUT, data: fds[1] }],
-1,
);
});
// Ensure the thread is blocked.
std::thread::yield_now();
// Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP
let mut ev = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) as _,
u64: u64::try_from(fds[1]).unwrap(),
};
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fds[1], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP)
.unwrap();
// This should wake up the thread.
t.join().unwrap();
@ -215,25 +155,21 @@ fn wakeup_on_new_interest() {
/// to consume them all.
fn multiple_events_wake_multiple_threads() {
// Create an epoll instance.
let epfd = unsafe { libc::epoll_create1(0) };
assert_ne!(epfd, -1);
let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap();
// Create an eventfd instance.
let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC;
let fd1 = unsafe { libc::eventfd(0, flags) };
let fd1 = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap();
// Make a duplicate so that we have two file descriptors for the same file description.
let fd2 = unsafe { libc::dup(fd1) };
let fd2 = errno_result(unsafe { libc::dup(fd1) }).unwrap();
// Register both with epoll.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd1 as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd1, &mut ev) };
assert_eq!(res, 0);
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd2 as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd2, &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fd1, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
epoll_ctl_add(epfd, fd2, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// Consume the initial events.
let expected = [(libc::EPOLLOUT as u32, fd1 as u64), (libc::EPOLLOUT as u32, fd2 as u64)];
let expected =
[Ev { events: libc::EPOLLOUT, data: fd1 }, Ev { events: libc::EPOLLOUT, data: fd2 }];
check_epoll_wait::<8>(epfd, &expected, -1);
// Block two threads on the epoll, both wanting to get just one event.
@ -241,19 +177,19 @@ fn multiple_events_wake_multiple_threads() {
let mut e = libc::epoll_event { events: 0, u64: 0 };
let res = unsafe { libc::epoll_wait(epfd, &raw mut e, 1, -1) };
assert!(res == 1);
(e.events, e.u64)
Ev { events: e.events.cast_signed(), data: e.u64.try_into().unwrap() }
});
let t2 = thread::spawn(move || {
let mut e = libc::epoll_event { events: 0, u64: 0 };
let res = unsafe { libc::epoll_wait(epfd, &raw mut e, 1, -1) };
assert!(res == 1);
(e.events, e.u64)
Ev { events: e.events.cast_signed(), data: e.u64.try_into().unwrap() }
});
// Yield so both threads are waiting now.
thread::yield_now();
// Trigger the eventfd. This triggers two events at once!
libc_utils::write_all_from_slice(fd1, &0_u64.to_ne_bytes()).unwrap();
write_all_from_slice(fd1, &0_u64.to_ne_bytes()).unwrap();
// Both threads should have been woken up so that both events can be consumed.
let e1 = t1.join().unwrap();

View file

@ -32,9 +32,6 @@ fn main() {
test_issue_4374_reads();
}
// Using `as` cast since `EPOLLET` wraps around
const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _;
#[track_caller]
fn check_epoll_wait<const N: usize>(epfd: i32, expected_notifications: &[(u32, u64)]) {
let epoll_event = libc::epoll_event { events: 0, u64: 0 };
@ -60,7 +57,7 @@ fn test_epoll_socketpair() {
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Write to fd[0]
write_all_from_slice(fds[0], "abcde".as_bytes()).unwrap();
write_all_from_slice(fds[0], b"abcde").unwrap();
// Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP
epoll_ctl_add(epfd, fds[1], EPOLLIN | EPOLLOUT | EPOLLET | EPOLLRDHUP).unwrap();
@ -72,7 +69,7 @@ fn test_epoll_socketpair() {
check_epoll_wait_noblock::<8>(epfd, &[]);
// Write some more to fd[0].
write_all_from_slice(fds[0], "abcde".as_bytes()).unwrap();
write_all_from_slice(fds[0], b"abcde").unwrap();
// This did not change the readiness of fd[1], so we should get no event.
// However, Linux seems to always deliver spurious events to the peer on each write,
@ -140,12 +137,15 @@ fn test_epoll_ctl_del() {
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Write to fd[0]
let data = "abcde".as_bytes().as_ptr();
let data = b"abcde".as_ptr();
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
// Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fds[1]).unwrap() };
let mut ev = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as u32,
u64: u64::try_from(fds[1]).unwrap(),
};
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
assert_eq!(res, 0);
@ -168,16 +168,13 @@ fn test_two_epoll_instance() {
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Write to the socketpair.
let data = "abcde".as_bytes().as_ptr();
let data = b"abcde".as_ptr();
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
// Register one side of the socketpair with EPOLLIN | EPOLLOUT | EPOLLET.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fds[1]).unwrap() };
let res = unsafe { libc::epoll_ctl(epfd1, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
assert_eq!(res, 0);
let res = unsafe { libc::epoll_ctl(epfd2, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd1, fds[1], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
epoll_ctl_add(epfd2, fds[1], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// Notification should be received from both instance of epoll.
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
@ -201,20 +198,23 @@ fn test_two_same_fd_in_same_epoll_instance() {
assert_ne!(newfd, -1);
// Register both fd to the same epoll instance.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: 5 as u64 };
let mut ev = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).cast_unsigned(),
u64: 5u64,
};
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
assert_eq!(res, 0);
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, newfd, &mut ev) };
assert_eq!(res, 0);
// Write to the socketpair.
let data = "abcde".as_bytes().as_ptr();
let data = b"abcde".as_ptr();
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
// Two notification should be received.
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
let expected_value = 5 as u64;
let expected_value = 5u64;
check_epoll_wait::<8>(
epfd,
&[(expected_event, expected_value), (expected_event, expected_value)],
@ -233,9 +233,7 @@ fn test_epoll_eventfd() {
let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap();
// Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fd).unwrap() };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// Check result from epoll_wait.
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
@ -278,17 +276,13 @@ fn test_epoll_socketpair_both_sides() {
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Register both fd to the same epoll instance.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_eq!(res, 0);
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[1] as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
epoll_ctl_add(epfd, fds[1], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// Write to fds[1].
// (We do the write after the register here, unlike in `test_epoll_socketpair`, to ensure
// we cover both orders in which this could be done.)
let data = "abcde".as_bytes().as_ptr();
let data = b"abcde".as_ptr();
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
@ -307,7 +301,7 @@ fn test_epoll_socketpair_both_sides() {
let res =
unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 5);
assert_eq!(buf, "abcde".as_bytes());
assert_eq!(buf, *b"abcde");
// The state of fds[1] does not change (was writable, is writable).
// However, we force a spurious wakeup as the read buffer just got emptied.
@ -326,9 +320,7 @@ fn test_closed_fd() {
let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap();
// Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fd).unwrap() };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// Write to the eventfd instance.
let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
@ -360,9 +352,7 @@ fn test_not_fully_closed_fd() {
let newfd = errno_result(unsafe { libc::dup(fd) }).unwrap();
// Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fd).unwrap() };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// Close the original fd that being used to register with epoll.
errno_check(unsafe { libc::close(fd) });
@ -402,7 +392,7 @@ fn test_event_overwrite() {
// Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET
let mut ev = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _,
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).cast_unsigned(),
u64: u64::try_from(fd).unwrap(),
};
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
@ -431,13 +421,13 @@ fn test_socketpair_read() {
// Register both fd to the same epoll instance.
let mut ev = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _,
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).cast_unsigned(),
u64: fds[0] as u64,
};
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_eq!(res, 0);
let mut ev = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _,
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).cast_unsigned(),
u64: fds[1] as u64,
};
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
@ -493,14 +483,14 @@ fn test_no_notification_for_unregister_flag() {
// Register fd[0] with EPOLLOUT|EPOLLET.
let mut ev = libc::epoll_event {
events: (libc::EPOLLOUT | libc::EPOLLET) as _,
events: (libc::EPOLLOUT | libc::EPOLLET).cast_unsigned(),
u64: u64::try_from(fds[0]).unwrap(),
};
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_eq!(res, 0);
// Write to fd[1].
let data = "abcde".as_bytes().as_ptr();
let data = b"abcde".as_ptr();
let res: i32 = unsafe {
libc_utils::write_all(fds[1], data as *const libc::c_void, 5).try_into().unwrap()
};
@ -534,7 +524,7 @@ fn test_socketpair_epollerr() {
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Write to fd[0]
let data = "abcde".as_bytes().as_ptr();
let data = b"abcde".as_ptr();
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
@ -543,19 +533,15 @@ fn test_socketpair_epollerr() {
errno_check(unsafe { libc::close(fds[1]) });
// Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP
let mut ev = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) as _,
u64: u64::try_from(fds[1]).unwrap(),
};
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_ne!(res, -1);
epoll_ctl_add(epfd, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP)
.unwrap();
// Check result from epoll_wait.
let expected_event = u32::try_from(
libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLHUP | libc::EPOLLRDHUP | libc::EPOLLERR,
)
.unwrap();
let expected_value = u64::try_from(fds[1]).unwrap();
let expected_value = u64::try_from(fds[0]).unwrap();
check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]);
}
@ -570,12 +556,8 @@ fn test_epoll_lost_events() {
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Register both fd to the same epoll instance.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_eq!(res, 0);
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[1] as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
epoll_ctl_add(epfd, fds[1], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// Two notification should be received. But we only provide buffer for one event.
let expected_event0 = u32::try_from(libc::EPOLLOUT).unwrap();
@ -601,12 +583,8 @@ fn test_ready_list_fetching_logic() {
let fd1 = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap();
// Register both fd to the same epoll instance. At this point, both of them are on the ready list.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd0 as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd0, &mut ev) };
assert_eq!(res, 0);
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd1 as u64 };
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd1, &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd, fd0, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
epoll_ctl_add(epfd, fd1, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// Close fd0 so the first entry in the ready list will be empty.
errno_check(unsafe { libc::close(fd0) });
@ -643,9 +621,7 @@ fn test_epoll_ctl_notification() {
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Register one side of the socketpair with epoll.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
let res = unsafe { libc::epoll_ctl(epfd0, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd0, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// epoll_wait to clear notification for epfd0.
let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
@ -657,9 +633,7 @@ fn test_epoll_ctl_notification() {
assert_ne!(epfd1, -1);
// Register the same file description for epfd1.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
let res = unsafe { libc::epoll_ctl(epfd1, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd1, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
check_epoll_wait::<1>(epfd1, &[(expected_event, expected_value)]);
// Previously this epoll_wait will receive a notification, but we shouldn't return notification
@ -683,7 +657,7 @@ fn test_issue_3858() {
// Register eventfd with EPOLLIN | EPOLLET.
let mut ev = libc::epoll_event {
events: (libc::EPOLLIN | libc::EPOLLET) as _,
events: (libc::EPOLLIN | libc::EPOLLET).cast_unsigned(),
u64: u64::try_from(fd).unwrap(),
};
let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) };
@ -715,9 +689,7 @@ fn test_issue_4374() {
assert_eq!(unsafe { libc::fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK) }, 0);
// Register fds[0] with epoll while it is writable (but not readable).
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
let res = unsafe { libc::epoll_ctl(epfd0, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd0, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// Fill up fds[0] so that it is not writable any more.
let zeros = [0u8; 512];
@ -747,16 +719,14 @@ fn test_issue_4374_reads() {
assert_eq!(unsafe { libc::fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK) }, 0);
// Write to fds[1] so that fds[0] becomes readable.
let data = "abcde".as_bytes().as_ptr();
let data = b"abcde".as_ptr();
let res: i32 = unsafe {
libc_utils::write_all(fds[1], data as *const libc::c_void, 5).try_into().unwrap()
};
assert_eq!(res, 5);
// Register fds[0] with epoll while it is readable.
let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
let res = unsafe { libc::epoll_ctl(epfd0, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
assert_eq!(res, 0);
epoll_ctl_add(epfd0, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap();
// Read fds[0] so it is no longer readable.
let mut buf = [0u8; 512];

View file

@ -4,11 +4,14 @@
//@compile-flags: -Zmiri-disable-isolation
use std::ffi::CString;
use std::io::{Error, ErrorKind};
use std::io::ErrorKind;
use std::os::unix::ffi::OsStrExt;
#[path = "../../utils/libc.rs"]
mod libc_utils;
#[path = "../../utils/mod.rs"]
mod utils;
use libc_utils::errno_result;
fn main() {
test_readlink();
@ -31,44 +34,48 @@ fn test_readlink() {
// Make the buf one byte larger than it needs to be,
// and check that the last byte is not overwritten.
let mut large_buf = vec![0xFF; expected_path.len() + 1];
let res =
unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) };
let res = errno_result(unsafe {
libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len())
})
.unwrap();
// Check that the resolved path was properly written into the buf.
assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
assert_eq!(large_buf.last(), Some(&0xFF));
assert_eq!(res, large_buf.len() as isize - 1);
assert_eq!(res, (large_buf.len() - 1) as isize);
// Test that the resolved path is truncated if the provided buffer
// is too small.
let mut small_buf = [0u8; 2];
let res =
unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) };
let res = errno_result(unsafe {
libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len())
})
.unwrap();
assert_eq!(small_buf, &expected_path[..small_buf.len()]);
assert_eq!(res, small_buf.len() as isize);
// Test that we report a proper error for a missing path.
let res = unsafe {
let err = errno_result(unsafe {
libc::readlink(
c"MIRI_MISSING_FILE_NAME".as_ptr(),
small_buf.as_mut_ptr().cast(),
small_buf.len(),
)
};
assert_eq!(res, -1);
assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
})
.unwrap_err();
assert_eq!(err.kind(), ErrorKind::NotFound);
}
fn test_nofollow_symlink() {
let bytes = b"Hello, World!\n";
let path = utils::prepare_with_content("test_nofollow_symlink_target.txt", bytes);
let path = utils::prepare_with_content("test_nofollow_symlink_target.txt", b"Hello, World!\n");
let symlink_path = utils::prepare("test_nofollow_symlink.txt");
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
let symlink_cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap();
let ret = unsafe { libc::open(symlink_cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) };
assert_eq!(ret, -1);
let err = Error::last_os_error().raw_os_error().unwrap();
assert_eq!(err, libc::ELOOP);
let err = errno_result(unsafe {
libc::open(symlink_cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC)
})
.unwrap_err();
assert_eq!(err.raw_os_error(), Some(libc::ELOOP));
}

View file

@ -1,4 +1,4 @@
//@ignore-target: windows # File handling is not implemented yet
//@ignore-target: windows # no libc
//@compile-flags: -Zmiri-disable-isolation
#![feature(io_error_more)]
@ -48,7 +48,6 @@ fn main() {
test_nofollow_not_symlink();
#[cfg(target_os = "macos")]
test_ioctl();
test_close_stdout();
}
fn test_file_open_unix_allow_two_args() {
@ -580,11 +579,3 @@ fn test_ioctl() {
assert_eq!(libc::ioctl(fd, libc::FIOCLEX), 0);
}
}
fn test_close_stdout() {
// This is std library UB, but that's not relevant since we're
// only interacting with libc here.
unsafe {
libc::close(1);
}
}

View file

@ -9,6 +9,7 @@ use std::thread;
#[path = "../../utils/libc.rs"]
mod libc_utils;
use libc_utils::*;
fn main() {
test_socketpair();
@ -21,139 +22,89 @@ fn main() {
fn test_socketpair() {
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Read size == data available in buffer.
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
let mut buf: [u8; 5] = [0; 5];
let res =
unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 5);
assert_eq!(buf, "abcde".as_bytes());
let data = b"abcde";
write_all_from_slice(fds[0], data).unwrap();
let buf = read_all_into_array::<5>(fds[1]).unwrap();
assert_eq!(&buf, data);
// Read size > data available in buffer.
let data = "abc".as_bytes();
let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) };
assert_eq!(res, 3);
let data = b"abc";
write_all_from_slice(fds[0], data).unwrap();
let mut buf2: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[1], buf2.as_mut_ptr().cast(), buf2.len() as libc::size_t) };
assert!(res > 0 && res <= 3);
let res = res as usize;
assert_eq!(buf2[..res], data[..res]);
if res < 3 {
// Drain the rest from the read end.
let res = unsafe { libc_utils::read_all(fds[1], buf2[res..].as_mut_ptr().cast(), 3 - res) };
assert!(res > 0);
}
let (read, rest) = read_into_slice(fds[1], &mut buf2).unwrap();
assert_eq!(read[..], data[..read.len()]);
// Write 2 more bytes so we can exactly fill the `rest`.
write_all_from_slice(fds[0], b"12").unwrap();
read_all_into_slice(fds[1], rest).unwrap();
// Test read and write from another direction.
// Read size == data available in buffer.
let data = "12345".as_bytes().as_ptr();
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
let mut buf3: [u8; 5] = [0; 5];
let res = unsafe {
libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t)
};
assert_eq!(res, 5);
assert_eq!(buf3, "12345".as_bytes());
let data = b"12345";
write_all_from_slice(fds[1], data).unwrap();
let buf3 = read_all_into_array::<5>(fds[0]).unwrap();
assert_eq!(&buf3, data);
// Read size > data available in buffer.
let data = "123".as_bytes();
let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) };
assert_eq!(res, 3);
let data = b"123";
write_all_from_slice(fds[1], data).unwrap();
let mut buf4: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) };
assert!(res > 0 && res <= 3);
let res = res as usize;
assert_eq!(buf4[..res], data[..res]);
if res < 3 {
// Drain the rest from the read end.
let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) };
assert!(res > 0);
}
let (read, rest) = read_into_slice(fds[0], &mut buf4).unwrap();
assert_eq!(read[..], data[..read.len()]);
// Write 2 more bytes so we can exactly fill the `rest`.
write_all_from_slice(fds[1], b"12").unwrap();
read_all_into_slice(fds[0], rest).unwrap();
// Test when happens when we close one end, with some data in the buffer.
let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) };
assert_eq!(res, 3);
unsafe { libc::close(fds[0]) };
write_all_from_slice(fds[0], data).unwrap();
errno_check(unsafe { libc::close(fds[0]) });
// Reading the other end should return that data, then EOF.
let mut buf: [u8; 5] = [0; 5];
let res =
unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(&buf[0..3], "123".as_bytes());
let res =
unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 0); // 0-sized read: EOF.
let (res, _) = read_until_eof_into_slice(fds[1], &mut buf).unwrap();
assert_eq!(res, data);
// Writing the other end should emit EPIPE.
let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 1) };
assert_eq!(res, -1);
assert_eq!(std::io::Error::last_os_error().raw_os_error(), Some(libc::EPIPE));
let err = write_all_from_slice(fds[1], &mut buf).unwrap_err();
assert_eq!(err.raw_os_error(), Some(libc::EPIPE));
}
fn test_socketpair_threaded() {
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
let thread1 = thread::spawn(move || {
let mut buf: [u8; 5] = [0; 5];
let res: i64 = unsafe {
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
.try_into()
.unwrap()
};
assert_eq!(res, 5);
assert_eq!(buf, "abcde".as_bytes());
let buf = read_all_into_array::<5>(fds[1]).unwrap();
assert_eq!(&buf, b"abcde");
});
thread::yield_now();
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
write_all_from_slice(fds[0], b"abcde").unwrap();
thread1.join().unwrap();
// Read and write from different direction
let thread2 = thread::spawn(move || {
thread::yield_now();
let data = "12345".as_bytes().as_ptr();
let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
write_all_from_slice(fds[1], b"12345").unwrap();
});
let mut buf: [u8; 5] = [0; 5];
let res =
unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 5);
assert_eq!(buf, "12345".as_bytes());
let buf = read_all_into_array::<5>(fds[0]).unwrap();
assert_eq!(&buf, b"12345");
thread2.join().unwrap();
}
fn test_race() {
static mut VAL: u8 = 0;
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
let thread1 = thread::spawn(move || {
let mut buf: [u8; 1] = [0; 1];
// write() from the main thread will occur before the read() here
// because preemption is disabled and the main thread yields after write().
let res: i32 = unsafe {
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
.try_into()
.unwrap()
};
assert_eq!(res, 1);
assert_eq!(buf, "a".as_bytes());
let buf = read_all_into_array::<1>(fds[1]).unwrap();
assert_eq!(&buf, b"a");
// The read above establishes a happens-before so it is now safe to access this global variable.
unsafe { assert_eq!(VAL, 1) };
});
unsafe { VAL = 1 };
let data = "a".as_bytes().as_ptr();
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 1) };
assert_eq!(res, 1);
write_all_from_slice(fds[0], b"a").unwrap();
thread::yield_now();
thread1.join().unwrap();
}
@ -161,22 +112,15 @@ fn test_race() {
// Test the behaviour of a socketpair getting blocked on read and subsequently unblocked.
fn test_blocking_read() {
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
let thread1 = thread::spawn(move || {
// Let this thread block on read.
let mut buf: [u8; 3] = [0; 3];
let res = unsafe {
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
};
assert_eq!(res, 3);
assert_eq!(&buf, "abc".as_bytes());
let buf = read_all_into_array::<3>(fds[1]).unwrap();
assert_eq!(&buf, b"abc");
});
let thread2 = thread::spawn(move || {
// Unblock thread1 by doing writing something.
let data = "abc".as_bytes().as_ptr();
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) };
assert_eq!(res, 3);
write_all_from_slice(fds[0], b"abc").unwrap();
});
thread1.join().unwrap();
thread2.join().unwrap();
@ -185,26 +129,17 @@ fn test_blocking_read() {
// Test the behaviour of a socketpair getting blocked on write and subsequently unblocked.
fn test_blocking_write() {
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
let arr1: [u8; 0x34000] = [1; 0x34000];
// Exhaust the space in the buffer so the subsequent write will block.
let res =
unsafe { libc_utils::write_all(fds[0], arr1.as_ptr() as *const libc::c_void, arr1.len()) };
assert_eq!(res, 0x34000);
write_all_from_slice(fds[0], &arr1).unwrap();
let thread1 = thread::spawn(move || {
let data = "abc".as_bytes().as_ptr();
// The write below will be blocked because the buffer is already full.
let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) };
assert_eq!(res, 3);
write_all_from_slice(fds[0], b"abc").unwrap();
});
let thread2 = thread::spawn(move || {
// Unblock thread1 by freeing up some space.
let mut buf: [u8; 3] = [0; 3];
let res = unsafe {
libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
};
assert_eq!(res, 3);
let buf = read_all_into_array::<3>(fds[1]).unwrap();
assert_eq!(buf, [1, 1, 1]);
});
thread1.join().unwrap();
@ -215,30 +150,25 @@ fn test_blocking_write() {
fn test_socketpair_setfl_getfl() {
// Initialise socketpair fds.
let mut fds = [-1, -1];
let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
assert_eq!(res, 0);
errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) });
// Test if both sides have O_RDWR.
let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
assert_eq!(res, libc::O_RDWR);
let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) };
assert_eq!(res, libc::O_RDWR);
assert_eq!(errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), libc::O_RDWR);
assert_eq!(errno_result(unsafe { libc::fcntl(fds[1], libc::F_GETFL) }).unwrap(), libc::O_RDWR);
// Add the O_NONBLOCK flag with F_SETFL.
let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) };
assert_eq!(res, 0);
errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) });
// Test if the O_NONBLOCK flag is successfully added.
let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
assert_eq!(res, libc::O_RDWR | libc::O_NONBLOCK);
assert_eq!(
errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(),
libc::O_RDWR | libc::O_NONBLOCK
);
// The other side remains unchanged.
let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) };
assert_eq!(res, libc::O_RDWR);
assert_eq!(errno_result(unsafe { libc::fcntl(fds[1], libc::F_GETFL) }).unwrap(), libc::O_RDWR);
// Test if O_NONBLOCK flag can be unset.
let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) };
assert_eq!(res, 0);
let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) };
assert_eq!(res, libc::O_RDWR);
errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) });
assert_eq!(errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), libc::O_RDWR);
}

View file

@ -1,8 +1,13 @@
//@ignore-target: windows # no libc time APIs on Windows
//@compile-flags: -Zmiri-disable-isolation
#[path = "../../utils/libc.rs"]
mod libc_utils;
use std::time::{Duration, Instant};
use std::{env, mem, ptr};
use libc_utils::errno_check;
fn main() {
test_clocks();
test_posix_gettimeofday();
@ -39,30 +44,23 @@ fn main() {
/// Tests whether clock support exists at all
fn test_clocks() {
let mut tp = mem::MaybeUninit::<libc::timespec>::uninit();
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
errno_check(unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) });
errno_check(unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) });
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "android"))]
{
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
let is_error =
unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
errno_check(unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) });
errno_check(unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) });
}
#[cfg(target_os = "macos")]
{
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
errno_check(unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) });
}
}
fn test_posix_gettimeofday() {
let mut tp = mem::MaybeUninit::<libc::timeval>::uninit();
let tz = ptr::null_mut::<libc::timezone>();
let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz.cast()) };
assert_eq!(is_error, 0);
errno_check(unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz.cast()) });
let tv = unsafe { tp.assume_init() };
assert!(tv.tv_sec > 0);
assert!(tv.tv_usec >= 0); // Theoretically this could be 0.
@ -334,15 +332,13 @@ fn test_nanosleep() {
let start_test_sleep = Instant::now();
let duration_zero = libc::timespec { tv_sec: 0, tv_nsec: 0 };
let remainder = ptr::null_mut::<libc::timespec>();
let is_error = unsafe { libc::nanosleep(&duration_zero, remainder) };
assert_eq!(is_error, 0);
errno_check(unsafe { libc::nanosleep(&duration_zero, remainder) });
assert!(start_test_sleep.elapsed() < Duration::from_millis(100));
let start_test_sleep = Instant::now();
let duration_100_millis = libc::timespec { tv_sec: 0, tv_nsec: 1_000_000_000 / 10 };
let remainder = ptr::null_mut::<libc::timespec>();
let is_error = unsafe { libc::nanosleep(&duration_100_millis, remainder) };
assert_eq!(is_error, 0);
errno_check(unsafe { libc::nanosleep(&duration_100_millis, remainder) });
assert!(start_test_sleep.elapsed() > Duration::from_millis(100));
}
@ -371,8 +367,7 @@ mod test_clock_nanosleep {
/// Helper function to get the current time for testing relative sleeps
fn timespec_now(clock: libc::clockid_t) -> libc::timespec {
let mut timespec = mem::MaybeUninit::<libc::timespec>::uninit();
let is_error = unsafe { libc::clock_gettime(clock, timespec.as_mut_ptr()) };
assert_eq!(is_error, 0);
errno_check(unsafe { libc::clock_gettime(clock, timespec.as_mut_ptr()) });
unsafe { timespec.assume_init() }
}
@ -380,7 +375,7 @@ mod test_clock_nanosleep {
let start_test_sleep = Instant::now();
let before_start = libc::timespec { tv_sec: 0, tv_nsec: 0 };
let remainder = ptr::null_mut::<libc::timespec>();
let error = unsafe {
errno_check(unsafe {
// this will not sleep since unix time zero is in the past
libc::clock_nanosleep(
libc::CLOCK_MONOTONIC,
@ -388,22 +383,20 @@ mod test_clock_nanosleep {
&before_start,
remainder,
)
};
assert_eq!(error, 0);
});
assert!(start_test_sleep.elapsed() < Duration::from_millis(100));
let start_test_sleep = Instant::now();
let hunderd_millis_after_start = add_100_millis(timespec_now(libc::CLOCK_MONOTONIC));
let remainder = ptr::null_mut::<libc::timespec>();
let error = unsafe {
errno_check(unsafe {
libc::clock_nanosleep(
libc::CLOCK_MONOTONIC,
libc::TIMER_ABSTIME,
&hunderd_millis_after_start,
remainder,
)
};
assert_eq!(error, 0);
});
assert!(start_test_sleep.elapsed() > Duration::from_millis(100));
}
@ -413,19 +406,17 @@ mod test_clock_nanosleep {
let start_test_sleep = Instant::now();
let duration_zero = libc::timespec { tv_sec: 0, tv_nsec: 0 };
let remainder = ptr::null_mut::<libc::timespec>();
let error = unsafe {
errno_check(unsafe {
libc::clock_nanosleep(libc::CLOCK_MONOTONIC, NO_FLAGS, &duration_zero, remainder)
};
assert_eq!(error, 0);
});
assert!(start_test_sleep.elapsed() < Duration::from_millis(100));
let start_test_sleep = Instant::now();
let duration_100_millis = libc::timespec { tv_sec: 0, tv_nsec: 1_000_000_000 / 10 };
let remainder = ptr::null_mut::<libc::timespec>();
let error = unsafe {
errno_check(unsafe {
libc::clock_nanosleep(libc::CLOCK_MONOTONIC, NO_FLAGS, &duration_100_millis, remainder)
};
assert_eq!(error, 0);
});
assert!(start_test_sleep.elapsed() > Duration::from_millis(100));
}
}

View file

@ -0,0 +1,28 @@
#![no_std]
#![no_main]
//@compile-flags: -Cpanic=abort
//@ignore-target: windows # no-std not supported on Windows
#[path = "../../utils/mod.no_std.rs"]
mod utils;
extern "Rust" fn thread_start(_null: *mut ()) {
unsafe {
utils::miri_spin_loop();
utils::miri_spin_loop();
}
}
#[no_mangle]
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
unsafe {
let thread_id = utils::miri_thread_spawn(thread_start, core::ptr::null_mut());
assert_eq!(utils::miri_thread_join(thread_id), true);
}
0
}
#[panic_handler]
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
loop {}
}

View file

@ -6,17 +6,15 @@
// windows linker section, we can run this test on windows again.
//@ignore-target: windows # no-std not supported on Windows
extern "Rust" {
fn miri_alloc(size: usize, align: usize) -> *mut u8;
fn miri_dealloc(ptr: *mut u8, size: usize, align: usize);
}
#[path = "../utils/mod.no_std.rs"]
mod utils;
#[no_mangle]
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
unsafe {
let ptr = miri_alloc(123, 1);
let ptr = utils::miri_alloc(123, 1);
core::ptr::write_bytes(ptr, 0u8, 123);
miri_dealloc(ptr, 123, 1);
utils::miri_dealloc(ptr, 123, 1);
}
0
}

View file

@ -1,6 +1,6 @@
// We're testing x86 target specific features
//@only-target: x86_64 i686
//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq,+avx512vnni
//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512bitalg,+avx512vpopcntdq,+avx512vnni
#[cfg(target_arch = "x86")]
use std::arch::x86::*;
@ -11,12 +11,14 @@ use std::mem::transmute;
fn main() {
assert!(is_x86_feature_detected!("avx512f"));
assert!(is_x86_feature_detected!("avx512vl"));
assert!(is_x86_feature_detected!("avx512bw"));
assert!(is_x86_feature_detected!("avx512bitalg"));
assert!(is_x86_feature_detected!("avx512vpopcntdq"));
assert!(is_x86_feature_detected!("avx512vnni"));
unsafe {
test_avx512();
test_avx512bw();
test_avx512bitalg();
test_avx512vpopcntdq();
test_avx512ternarylogic();
@ -100,6 +102,77 @@ unsafe fn test_avx512() {
}
test_mm512_maddubs_epi16();
#[target_feature(enable = "avx512bw")]
unsafe fn test_mm512_madd_epi16() {
// Input pairs
//
// - `i16::MIN * i16::MIN + i16::MIN * i16::MIN`: the 32-bit addition overflows
// - `i16::MAX * i16::MAX + i16::MAX * i16::MAX`: check that widening happens before
// arithmetic
// - `i16::MIN * i16::MAX + i16::MAX * i16::MIN`: check that large negative values are
// handled correctly
// - `3 * 1 + 4 * 2`: A sanity check, the result should be 14.
#[rustfmt::skip]
let a = _mm512_set_epi16(
i16::MIN, i16::MIN,
i16::MAX, i16::MAX,
i16::MIN, i16::MAX,
3, 1,
i16::MIN, i16::MIN,
i16::MAX, i16::MAX,
i16::MIN, i16::MAX,
3, 1,
i16::MIN, i16::MIN,
i16::MAX, i16::MAX,
i16::MIN, i16::MAX,
3, 1,
i16::MIN, i16::MIN,
i16::MAX, i16::MAX,
i16::MIN, i16::MAX,
3, 1,
);
#[rustfmt::skip]
let b = _mm512_set_epi16(
i16::MIN, i16::MIN,
i16::MAX, i16::MAX,
i16::MAX, i16::MIN,
4, 2,
i16::MIN, i16::MIN,
i16::MAX, i16::MAX,
i16::MAX, i16::MIN,
4, 2,
i16::MIN, i16::MIN,
i16::MAX, i16::MAX,
i16::MAX, i16::MIN,
4, 2,
i16::MIN, i16::MIN,
i16::MAX, i16::MAX,
i16::MAX, i16::MIN,
4, 2,
);
let r = _mm512_madd_epi16(a, b);
#[rustfmt::skip]
let e = _mm512_set_epi32(
i32::MIN, 2_147_352_578, -2_147_418_112, 14,
i32::MIN, 2_147_352_578, -2_147_418_112, 14,
i32::MIN, 2_147_352_578, -2_147_418_112, 14,
i32::MIN, 2_147_352_578, -2_147_418_112, 14,
);
assert_eq_m512i(r, e);
}
test_mm512_madd_epi16();
#[target_feature(enable = "avx512f")]
unsafe fn test_mm512_permutexvar_epi32() {
let a = _mm512_set_epi32(
@ -508,9 +581,133 @@ unsafe fn test_avx512vnni() {
test_mm512_dpbusd_epi32();
}
#[target_feature(enable = "avx512bw")]
unsafe fn test_avx512bw() {
#[target_feature(enable = "avx512bw")]
unsafe fn test_mm512_packs_epi16() {
let a = _mm512_set1_epi16(120);
// Because `packs` instructions do signed saturation, we expect
// that any value over `i8::MAX` will be saturated to `i8::MAX`, and any value
// less than `i8::MIN` will also be saturated to `i8::MIN`.
let b = _mm512_set_epi16(
200, 200, 200, 200, 200, 200, 200, 200, -200, -200, -200, -200, -200, -200, -200, -200,
200, 200, 200, 200, 200, 200, 200, 200, -200, -200, -200, -200, -200, -200, -200, -200,
);
// The pack* family of instructions in x86 operate in blocks
// of 128-bit lanes, meaning the first 128-bit lane in `a` is converted and written
// then the first 128-bit lane of `b`, followed by the second 128-bit lane in `a`, etc...
// Because we are going from 16-bits to 8-bits our 128-bit block becomes 64-bits in
// the output register.
// This leaves us with 8x 8-bit values interleaved in the final register.
#[rustfmt::skip]
const DST: [i8; 64] = [
120, 120, 120, 120, 120, 120, 120, 120,
i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN,
120, 120, 120, 120, 120, 120, 120, 120,
i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX,
120, 120, 120, 120, 120, 120, 120, 120,
i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN,
120, 120, 120, 120, 120, 120, 120, 120,
i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX,
];
let dst = _mm512_loadu_si512(DST.as_ptr().cast::<__m512i>());
assert_eq_m512i(_mm512_packs_epi16(a, b), dst);
}
test_mm512_packs_epi16();
#[target_feature(enable = "avx512bw")]
unsafe fn test_mm512_packus_epi16() {
let a = _mm512_set1_epi16(120);
// Because `packus` instructions do unsigned saturation, we expect
// that any value over `u8::MAX` will be saturated to `u8::MAX`, and any value
// less than `u8::MIN` will also be saturated to `u8::MIN`.
let b = _mm512_set_epi16(
300, 300, 300, 300, 300, 300, 300, 300, -200, -200, -200, -200, -200, -200, -200, -200,
300, 300, 300, 300, 300, 300, 300, 300, -200, -200, -200, -200, -200, -200, -200, -200,
);
// See `test_mm512_packs_epi16` for an explanation of the output structure.
#[rustfmt::skip]
const DST: [u8; 64] = [
120, 120, 120, 120, 120, 120, 120, 120,
u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN,
120, 120, 120, 120, 120, 120, 120, 120,
u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX,
120, 120, 120, 120, 120, 120, 120, 120,
u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN,
120, 120, 120, 120, 120, 120, 120, 120,
u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX,
];
let dst = _mm512_loadu_si512(DST.as_ptr().cast::<__m512i>());
assert_eq_m512i(_mm512_packus_epi16(a, b), dst);
}
test_mm512_packus_epi16();
#[target_feature(enable = "avx512bw")]
unsafe fn test_mm512_packs_epi32() {
let a = _mm512_set1_epi32(8_000);
// Because `packs` instructions do signed saturation, we expect
// that any value over `i16::MAX` will be saturated to `i16::MAX`, and any value
// less than `i16::MIN` will also be saturated to `i16::MIN`.
let b = _mm512_set_epi32(
50_000, 50_000, 50_000, 50_000, -50_000, -50_000, -50_000, -50_000, 50_000, 50_000,
50_000, 50_000, -50_000, -50_000, -50_000, -50_000,
);
// See `test_mm512_packs_epi16` for an explanation of the output structure.
#[rustfmt::skip]
const DST: [i16; 32] = [
8_000, 8_000, 8_000, 8_000,
i16::MIN, i16::MIN, i16::MIN, i16::MIN,
8_000, 8_000, 8_000, 8_000,
i16::MAX, i16::MAX, i16::MAX, i16::MAX,
8_000, 8_000, 8_000, 8_000,
i16::MIN, i16::MIN, i16::MIN, i16::MIN,
8_000, 8_000, 8_000, 8_000,
i16::MAX, i16::MAX, i16::MAX, i16::MAX,
];
let dst = _mm512_loadu_si512(DST.as_ptr().cast::<__m512i>());
assert_eq_m512i(_mm512_packs_epi32(a, b), dst);
}
test_mm512_packs_epi32();
#[target_feature(enable = "avx512bw")]
unsafe fn test_mm512_packus_epi32() {
let a = _mm512_set1_epi32(8_000);
// Because `packus` instructions do unsigned saturation, we expect
// that any value over `u16::MAX` will be saturated to `u16::MAX`, and any value
// less than `u16::MIN` will also be saturated to `u16::MIN`.
let b = _mm512_set_epi32(
80_000, 80_000, 80_000, 80_000, -50_000, -50_000, -50_000, -50_000, 80_000, 80_000,
80_000, 80_000, -50_000, -50_000, -50_000, -50_000,
);
// See `test_mm512_packs_epi16` for an explanation of the output structure.
#[rustfmt::skip]
const DST: [u16; 32] = [
8_000, 8_000, 8_000, 8_000,
u16::MIN, u16::MIN, u16::MIN, u16::MIN,
8_000, 8_000, 8_000, 8_000,
u16::MAX, u16::MAX, u16::MAX, u16::MAX,
8_000, 8_000, 8_000, 8_000,
u16::MIN, u16::MIN, u16::MIN, u16::MIN,
8_000, 8_000, 8_000, 8_000,
u16::MAX, u16::MAX, u16::MAX, u16::MAX,
];
let dst = _mm512_loadu_si512(DST.as_ptr().cast::<__m512i>());
assert_eq_m512i(_mm512_packus_epi32(a, b), dst);
}
test_mm512_packus_epi32();
}
#[track_caller]
unsafe fn assert_eq_m512i(a: __m512i, b: __m512i) {
assert_eq!(transmute::<_, [i32; 16]>(a), transmute::<_, [i32; 16]>(b))
assert_eq!(transmute::<_, [u16; 32]>(a), transmute::<_, [u16; 32]>(b))
}
#[track_caller]

View file

@ -5,14 +5,14 @@ use std::ptr;
extern "system" {
fn TlsAlloc() -> u32;
fn TlsSetValue(key: u32, val: *mut c_void) -> bool;
fn TlsSetValue(key: u32, val: *mut c_void) -> i32;
fn TlsGetValue(key: u32) -> *mut c_void;
fn TlsFree(key: u32) -> bool;
fn TlsFree(key: u32) -> i32;
}
fn main() {
let key = unsafe { TlsAlloc() };
assert!(unsafe { TlsSetValue(key, ptr::without_provenance_mut(1)) });
assert!(unsafe { TlsSetValue(key, ptr::without_provenance_mut(1)) != 0 });
assert_eq!(unsafe { TlsGetValue(key).addr() }, 1);
assert!(unsafe { TlsFree(key) });
assert!(unsafe { TlsFree(key) != 0 });
}

View file

@ -40,21 +40,17 @@ pub unsafe fn read_all(
return read_so_far as libc::ssize_t;
}
/// Try to fill the given slice by reading from `fd`. Error if that many bytes could not be read.
/// Try to fill the given slice by reading from `fd`. Panic if that many bytes could not be read.
#[track_caller]
pub fn read_all_into_slice(fd: libc::c_int, buf: &mut [u8]) -> Result<(), libc::ssize_t> {
let res = unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) };
if res >= 0 {
assert_eq!(res as usize, buf.len());
Ok(())
} else {
Err(res)
}
pub fn read_all_into_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<()> {
let res = errno_result(unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) })?;
assert_eq!(res as usize, buf.len());
Ok(())
}
/// Read exactly `N` bytes from `fd`. Error if that many bytes could not be read.
#[track_caller]
pub fn read_all_into_array<const N: usize>(fd: libc::c_int) -> Result<[u8; N], libc::ssize_t> {
pub fn read_all_into_array<const N: usize>(fd: libc::c_int) -> io::Result<[u8; N]> {
let mut buf = [0; N];
read_all_into_slice(fd, &mut buf)?;
Ok(buf)
@ -63,12 +59,20 @@ pub fn read_all_into_array<const N: usize>(fd: libc::c_int) -> Result<[u8; N], l
/// Do a single read from `fd` and return the part of the buffer that was written into,
/// and the rest.
#[track_caller]
pub fn read_into_slice(
pub fn read_into_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> {
let res = errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) })?;
Ok(buf.split_at_mut(res as usize))
}
/// Read from `fd` until we get EOF and return the part of the buffer that was written into,
/// and the rest.
#[track_caller]
pub fn read_until_eof_into_slice(
fd: libc::c_int,
buf: &mut [u8],
) -> Result<(&mut [u8], &mut [u8]), libc::ssize_t> {
let res = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) };
if res >= 0 { Ok(buf.split_at_mut(res as usize)) } else { Err(res) }
) -> io::Result<(&mut [u8], &mut [u8])> {
let res = errno_result(unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) })?;
Ok(buf.split_at_mut(res as usize))
}
pub unsafe fn write_all(
@ -89,16 +93,12 @@ pub unsafe fn write_all(
return written_so_far as libc::ssize_t;
}
/// Write the entire `buf` to `fd`. Error if not all bytes could be written.
/// Write the entire `buf` to `fd`. Panic if not all bytes could be written.
#[track_caller]
pub fn write_all_from_slice(fd: libc::c_int, buf: &[u8]) -> Result<(), libc::ssize_t> {
let res = unsafe { write_all(fd, buf.as_ptr().cast(), buf.len()) };
if res >= 0 {
assert_eq!(res as usize, buf.len());
Ok(())
} else {
Err(res)
}
pub fn write_all_from_slice(fd: libc::c_int, buf: &[u8]) -> io::Result<()> {
let res = errno_result(unsafe { write_all(fd, buf.as_ptr().cast(), buf.len()) })?;
assert_eq!(res as usize, buf.len());
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "android", target_os = "illumos"))]
@ -113,7 +113,7 @@ pub mod epoll {
/// The libc epoll_event type doesn't fit to the EPOLLIN etc constants, so we have our
/// own type. We also make the data field an int since we typically want to store FDs there.
#[derive(PartialEq, Debug)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct Ev {
pub events: c_int,
pub data: c_int,
@ -138,10 +138,10 @@ pub mod epoll {
}
#[track_caller]
pub fn check_epoll_wait_noblock<const N: usize>(epfd: i32, expected: &[Ev]) {
pub fn check_epoll_wait<const N: usize>(epfd: i32, expected: &[Ev], timeout: i32) {
let mut array: [libc::epoll_event; N] = [libc::epoll_event { events: 0, u64: 0 }; N];
let num = errno_result(unsafe {
libc::epoll_wait(epfd, array.as_mut_ptr(), N.try_into().unwrap(), 0)
libc::epoll_wait(epfd, array.as_mut_ptr(), N.try_into().unwrap(), timeout)
})
.expect("epoll_wait returned an error");
let got = &mut array[..num.try_into().unwrap()];
@ -151,4 +151,9 @@ pub mod epoll {
.collect::<Vec<_>>();
assert_eq!(got, expected, "got wrong notifications");
}
#[track_caller]
pub fn check_epoll_wait_noblock<const N: usize>(epfd: i32, expected: &[Ev]) {
check_epoll_wait::<N>(epfd, expected, 0);
}
}

View file

@ -156,6 +156,19 @@ extern "Rust" {
/// Blocks the current execution if the argument is false
pub fn miri_genmc_assume(condition: bool);
/// Miri-provided extern function to spawn a new thread in the interpreter.
///
/// Returns the thread id.
///
/// This is useful when no fundamental way of spawning threads is available, e.g. when using
/// `no_std`.
pub fn miri_thread_spawn(t: extern "Rust" fn(*mut ()), data: *mut ()) -> usize;
/// Miri-provided extern function to join a thread that was spawned by Miri.
pub fn miri_thread_join(thread_id: usize) -> bool;
/// Indicate to Miri that this thread is busy-waiting in a spin loop.
///
/// As far as Miri is concerned, this is equivalent to `yield_now`.
pub fn miri_spin_loop();
}

View file

@ -4,7 +4,7 @@ error: crate-level attribute should be an inner attribute: add an exclamation ma
LL | #[crate_name = "owo"]
| ^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this function
note: this attribute does not have an `!`, which means it is applied to this function
--> $DIR/crate-only-as-outer.rs:5:1
|
LL | fn main() {}

View file

@ -771,7 +771,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[crate_name]
| ^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this function
note: this attribute does not have an `!`, which means it is applied to this function
--> $DIR/malformed-attrs.rs:116:1
|
LL | / fn test() {

View file

@ -58,7 +58,7 @@ error: crate-level attribute should be an inner attribute: add an exclamation ma
LL | #[no_std]
| ^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this extern crate
note: this attribute does not have an `!`, which means it is applied to this extern crate
--> $DIR/malformed-no-std.rs:26:1
|
LL | extern crate core;
@ -75,7 +75,7 @@ error: crate-level attribute should be an inner attribute: add an exclamation ma
LL | #[no_core]
| ^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this extern crate
note: this attribute does not have an `!`, which means it is applied to this extern crate
--> $DIR/malformed-no-std.rs:26:1
|
LL | extern crate core;

View file

@ -4,9 +4,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | const S: &'static mut str = &mut " hello ";
| ^^^^^^^^^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error: aborting due to 1 previous error

View file

@ -4,9 +4,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | const B: *mut i32 = &mut 4;
| ^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/mut_ref_in_final.rs:21:35
@ -14,9 +14,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | const B3: Option<&mut i32> = Some(&mut 42);
| ^^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0716]: temporary value dropped while borrowed
--> $DIR/mut_ref_in_final.rs:24:42
@ -86,9 +86,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | static RAW_MUT_CAST_S: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/mut_ref_in_final.rs:73:54
@ -96,9 +96,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | static RAW_MUT_COERCE_S: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/mut_ref_in_final.rs:75:52
@ -106,9 +106,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/mut_ref_in_final.rs:77:53
@ -116,9 +116,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0080]: constructing invalid value at .<enum-variant(Some)>.0: encountered a dangling reference (0x2a[noalloc] has no provenance)
--> $DIR/mut_ref_in_final.rs:86:5

View file

@ -13,9 +13,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | const BAZ: &Foo = &FOO;
| ^^^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0716]: temporary value dropped while borrowed
--> $DIR/const-promoted-opaque.rs:40:26

View file

@ -4,9 +4,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | const C1: &'static mut [usize] = &mut [];
| ^^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error: aborting due to 1 previous error

View file

@ -4,9 +4,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | const B: &'static UnsafeCell<usize> = &A;
| ^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/issue-17718-const-borrow.rs:9:39
@ -14,9 +14,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | const E: &'static UnsafeCell<usize> = &D.a;
| ^^^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/issue-17718-const-borrow.rs:11:23
@ -24,9 +24,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | const F: &'static C = &D;
| ^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error: aborting due to 3 previous errors

View file

@ -4,9 +4,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | &{a}
| ^^^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error: aborting due to 1 previous error

View file

@ -4,9 +4,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | &{a}
| ^^^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error: aborting due to 1 previous error

View file

@ -4,9 +4,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | &{a.0}
| ^^^^^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error: aborting due to 1 previous error

View file

@ -4,9 +4,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | static RAW_SYNC_S: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
| ^^^^^^^^^^^^^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/refs-to-cell-in-final.rs:15:53
@ -14,9 +14,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | const RAW_SYNC_C: SyncPtr<Cell<i32>> = SyncPtr { x: &Cell::new(42) };
| ^^^^^^^^^^^^^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/refs-to-cell-in-final.rs:41:57
@ -31,9 +31,9 @@ LL | | x
LL | | };
| |_^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error: aborting due to 3 previous errors

View file

@ -4,9 +4,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | static OH_NO: &mut i32 = &mut 42;
| ^^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0594]: cannot assign to `*OH_NO`, as `OH_NO` is an immutable static item
--> $DIR/write_to_static_via_mut_ref.rs:4:5

View file

@ -19,9 +19,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | const CR: &'static mut i32 = &mut C;
| ^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0596]: cannot borrow immutable static item `X` as mutable
--> $DIR/E0017.rs:11:39
@ -52,9 +52,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | static CONST_REF: &'static mut i32 = &mut C;
| ^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error: aborting due to 3 previous errors; 2 warnings emitted

View file

@ -4,9 +4,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | const B: &'static AtomicUsize = &A;
| ^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/E0492.rs:5:34
@ -14,9 +14,9 @@ error[E0492]: interior mutable shared borrows of temporaries that have their lif
LL | static C: &'static AtomicUsize = &A;
| ^^ this borrow of an interior mutable value refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error: aborting due to 2 previous errors

View file

@ -525,25 +525,25 @@ mod macro_escape {
#[no_std]
//~^ WARN crate-level attribute should be an inner attribute
mod no_std {
//~^ NOTE This attribute does not have an `!`, which means it is applied to this module
//~^ NOTE this attribute does not have an `!`, which means it is applied to this module
mod inner { #![no_std] }
//~^ WARN the `#![no_std]` attribute can only be used at the crate root
#[no_std] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this function
//~| NOTE this attribute does not have an `!`, which means it is applied to this function
#[no_std] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this struct
//~| NOTE this attribute does not have an `!`, which means it is applied to this struct
#[no_std] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this type alias
//~| NOTE this attribute does not have an `!`, which means it is applied to this type alias
#[no_std] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block
//~| NOTE this attribute does not have an `!`, which means it is applied to this implementation block
}
// At time of authorship, #[proc_macro_derive = "2500"] signals error
@ -786,25 +786,25 @@ mod must_use {
#[windows_subsystem = "windows"]
//~^ WARN crate-level attribute should be an inner attribute
mod windows_subsystem {
//~^ NOTE This attribute does not have an `!`, which means it is applied to this module
//~^ NOTE this attribute does not have an `!`, which means it is applied to this module
mod inner { #![windows_subsystem="windows"] }
//~^ WARN the `#![windows_subsystem]` attribute can only be used at the crate root
#[windows_subsystem = "windows"] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this function
//~| NOTE this attribute does not have an `!`, which means it is applied to this function
#[windows_subsystem = "windows"] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this struct
//~| NOTE this attribute does not have an `!`, which means it is applied to this struct
#[windows_subsystem = "windows"] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this type alias
//~| NOTE this attribute does not have an `!`, which means it is applied to this type alias
#[windows_subsystem = "windows"] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block
//~| NOTE this attribute does not have an `!`, which means it is applied to this implementation block
}
// BROKEN USES OF CRATE-LEVEL BUILT-IN ATTRIBUTES
@ -812,25 +812,25 @@ mod windows_subsystem {
#[crate_name = "0900"]
//~^ WARN crate-level attribute should be an inner attribute
mod crate_name {
//~^ NOTE This attribute does not have an `!`, which means it is applied to this module
//~^ NOTE this attribute does not have an `!`, which means it is applied to this module
mod inner { #![crate_name="0900"] }
//~^ WARN the `#![crate_name]` attribute can only be used at the crate root
#[crate_name = "0900"] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this function
//~| NOTE this attribute does not have an `!`, which means it is applied to this function
#[crate_name = "0900"] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this struct
//~| NOTE this attribute does not have an `!`, which means it is applied to this struct
#[crate_name = "0900"] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this type alias
//~| NOTE this attribute does not have an `!`, which means it is applied to this type alias
#[crate_name = "0900"] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block
//~| NOTE this attribute does not have an `!`, which means it is applied to this implementation block
}
#[crate_type = "0800"]
@ -885,25 +885,25 @@ mod feature {
#[no_main]
//~^ WARN crate-level attribute should be an inner attribute
mod no_main_1 {
//~^ NOTE: This attribute does not have an `!`, which means it is applied to this module
//~^ NOTE: this attribute does not have an `!`, which means it is applied to this module
mod inner { #![no_main] }
//~^ WARN the `#![no_main]` attribute can only be used at the crate root
#[no_main] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this function
//~| NOTE this attribute does not have an `!`, which means it is applied to this function
#[no_main] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this struct
//~| NOTE this attribute does not have an `!`, which means it is applied to this struct
#[no_main] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this type alias
//~| NOTE this attribute does not have an `!`, which means it is applied to this type alias
#[no_main] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this implementation
//~| NOTE this attribute does not have an `!`, which means it is applied to this implementation
}
#[no_builtins]
@ -933,49 +933,49 @@ mod no_builtins {
#[recursion_limit="0200"]
//~^ WARN crate-level attribute should be an inner attribute
mod recursion_limit {
//~^ NOTE This attribute does not have an `!`, which means it is applied to this module
//~^ NOTE this attribute does not have an `!`, which means it is applied to this module
mod inner { #![recursion_limit="0200"] }
//~^ WARN the `#![recursion_limit]` attribute can only be used at the crate root
#[recursion_limit="0200"] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this function
//~| NOTE this attribute does not have an `!`, which means it is applied to this function
#[recursion_limit="0200"] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this struct
//~| NOTE this attribute does not have an `!`, which means it is applied to this struct
#[recursion_limit="0200"] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this type alias
//~| NOTE this attribute does not have an `!`, which means it is applied to this type alias
#[recursion_limit="0200"] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block
//~| NOTE this attribute does not have an `!`, which means it is applied to this implementation block
}
#[type_length_limit="0100"]
//~^ WARN crate-level attribute should be an inner attribute
mod type_length_limit {
//~^ NOTE This attribute does not have an `!`, which means it is applied to this module
//~^ NOTE this attribute does not have an `!`, which means it is applied to this module
mod inner { #![type_length_limit="0100"] }
//~^ WARN the `#![type_length_limit]` attribute can only be used at the crate root
#[type_length_limit="0100"] fn f() { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this function
//~| NOTE this attribute does not have an `!`, which means it is applied to this function
#[type_length_limit="0100"] struct S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this struct
//~| NOTE this attribute does not have an `!`, which means it is applied to this struct
#[type_length_limit="0100"] type T = S;
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this type alias
//~| NOTE this attribute does not have an `!`, which means it is applied to this type alias
#[type_length_limit="0100"] impl S { }
//~^ WARN crate-level attribute should be an inner attribute
//~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block
//~| NOTE this attribute does not have an `!`, which means it is applied to this implementation block
}
fn main() {}

View file

@ -935,7 +935,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[no_std]
| ^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this module
note: this attribute does not have an `!`, which means it is applied to this module
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:527:1
|
LL | / mod no_std {
@ -957,7 +957,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[no_std] fn f() { }
| ^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this function
note: this attribute does not have an `!`, which means it is applied to this function
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:532:15
|
LL | #[no_std] fn f() { }
@ -969,7 +969,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[no_std] struct S;
| ^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this struct
note: this attribute does not have an `!`, which means it is applied to this struct
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:536:15
|
LL | #[no_std] struct S;
@ -981,7 +981,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[no_std] type T = S;
| ^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this type alias
note: this attribute does not have an `!`, which means it is applied to this type alias
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:540:15
|
LL | #[no_std] type T = S;
@ -993,7 +993,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[no_std] impl S { }
| ^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this implementation block
note: this attribute does not have an `!`, which means it is applied to this implementation block
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:544:15
|
LL | #[no_std] impl S { }
@ -1212,7 +1212,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[windows_subsystem = "windows"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this module
note: this attribute does not have an `!`, which means it is applied to this module
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:788:1
|
LL | / mod windows_subsystem {
@ -1234,7 +1234,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[windows_subsystem = "windows"] fn f() { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this function
note: this attribute does not have an `!`, which means it is applied to this function
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:38
|
LL | #[windows_subsystem = "windows"] fn f() { }
@ -1246,7 +1246,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[windows_subsystem = "windows"] struct S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this struct
note: this attribute does not have an `!`, which means it is applied to this struct
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:797:38
|
LL | #[windows_subsystem = "windows"] struct S;
@ -1258,7 +1258,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[windows_subsystem = "windows"] type T = S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this type alias
note: this attribute does not have an `!`, which means it is applied to this type alias
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:38
|
LL | #[windows_subsystem = "windows"] type T = S;
@ -1270,7 +1270,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[windows_subsystem = "windows"] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this implementation block
note: this attribute does not have an `!`, which means it is applied to this implementation block
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:805:38
|
LL | #[windows_subsystem = "windows"] impl S { }
@ -1282,7 +1282,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[crate_name = "0900"]
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this module
note: this attribute does not have an `!`, which means it is applied to this module
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:814:1
|
LL | / mod crate_name {
@ -1304,7 +1304,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[crate_name = "0900"] fn f() { }
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this function
note: this attribute does not have an `!`, which means it is applied to this function
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:28
|
LL | #[crate_name = "0900"] fn f() { }
@ -1316,7 +1316,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[crate_name = "0900"] struct S;
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this struct
note: this attribute does not have an `!`, which means it is applied to this struct
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:823:28
|
LL | #[crate_name = "0900"] struct S;
@ -1328,7 +1328,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[crate_name = "0900"] type T = S;
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this type alias
note: this attribute does not have an `!`, which means it is applied to this type alias
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:827:28
|
LL | #[crate_name = "0900"] type T = S;
@ -1340,7 +1340,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[crate_name = "0900"] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this implementation block
note: this attribute does not have an `!`, which means it is applied to this implementation block
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:831:28
|
LL | #[crate_name = "0900"] impl S { }
@ -1352,7 +1352,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[no_main]
| ^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this module
note: this attribute does not have an `!`, which means it is applied to this module
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:887:1
|
LL | / mod no_main_1 {
@ -1374,7 +1374,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[no_main] fn f() { }
| ^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this function
note: this attribute does not have an `!`, which means it is applied to this function
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:892:16
|
LL | #[no_main] fn f() { }
@ -1386,7 +1386,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[no_main] struct S;
| ^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this struct
note: this attribute does not have an `!`, which means it is applied to this struct
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:896:16
|
LL | #[no_main] struct S;
@ -1398,7 +1398,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[no_main] type T = S;
| ^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this type alias
note: this attribute does not have an `!`, which means it is applied to this type alias
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:900:16
|
LL | #[no_main] type T = S;
@ -1410,7 +1410,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[no_main] impl S { }
| ^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this implementation block
note: this attribute does not have an `!`, which means it is applied to this implementation block
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:904:16
|
LL | #[no_main] impl S { }
@ -1422,7 +1422,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[recursion_limit="0200"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this module
note: this attribute does not have an `!`, which means it is applied to this module
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:935:1
|
LL | / mod recursion_limit {
@ -1444,7 +1444,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[recursion_limit="0200"] fn f() { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this function
note: this attribute does not have an `!`, which means it is applied to this function
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:940:31
|
LL | #[recursion_limit="0200"] fn f() { }
@ -1456,7 +1456,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[recursion_limit="0200"] struct S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this struct
note: this attribute does not have an `!`, which means it is applied to this struct
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:944:31
|
LL | #[recursion_limit="0200"] struct S;
@ -1468,7 +1468,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[recursion_limit="0200"] type T = S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this type alias
note: this attribute does not have an `!`, which means it is applied to this type alias
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:948:31
|
LL | #[recursion_limit="0200"] type T = S;
@ -1480,7 +1480,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[recursion_limit="0200"] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this implementation block
note: this attribute does not have an `!`, which means it is applied to this implementation block
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:952:31
|
LL | #[recursion_limit="0200"] impl S { }
@ -1492,7 +1492,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[type_length_limit="0100"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this module
note: this attribute does not have an `!`, which means it is applied to this module
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:959:1
|
LL | / mod type_length_limit {
@ -1514,7 +1514,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[type_length_limit="0100"] fn f() { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this function
note: this attribute does not have an `!`, which means it is applied to this function
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:33
|
LL | #[type_length_limit="0100"] fn f() { }
@ -1526,7 +1526,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[type_length_limit="0100"] struct S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this struct
note: this attribute does not have an `!`, which means it is applied to this struct
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:968:33
|
LL | #[type_length_limit="0100"] struct S;
@ -1538,7 +1538,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[type_length_limit="0100"] type T = S;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this type alias
note: this attribute does not have an `!`, which means it is applied to this type alias
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:972:33
|
LL | #[type_length_limit="0100"] type T = S;
@ -1550,7 +1550,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation
LL | #[type_length_limit="0100"] impl S { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this implementation block
note: this attribute does not have an `!`, which means it is applied to this implementation block
--> $DIR/issue-43106-gating-of-builtin-attrs.rs:976:33
|
LL | #[type_length_limit="0100"] impl S { }

View file

@ -4,9 +4,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | static buf: &mut [u8] = &mut [1u8,2,3,4,5,7];
| ^^^^^^^^^^^^^^^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error[E0594]: cannot assign to `buf[_]`, as `buf` is an immutable static item
--> $DIR/issue-46604.rs:6:5

View file

@ -4,7 +4,7 @@ error: moving 9999 bytes
LL | let _ = NotBox::new(data);
| ^^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
note: the lint level is defined here
--> $DIR/copy_into_box_rc_arc.rs:1:9
|
@ -19,7 +19,7 @@ LL | | data,
LL | | }
| |_________^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
error: aborting due to 2 previous errors

View file

@ -4,7 +4,7 @@ error: moving 9999 bytes
LL | one_arg(Data([0; 9999]));
| ^^^^^^^^^^^^^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
note: the lint level is defined here
--> $DIR/copy_into_fn.rs:5:9
|
@ -17,7 +17,7 @@ error: moving 9999 bytes
LL | many_args(Data([0; 9999]), true, Data([0; 9999]));
| ^^^^^^^^^^^^^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
error: moving 9999 bytes
--> $DIR/copy_into_fn.rs:17:38
@ -25,7 +25,7 @@ error: moving 9999 bytes
LL | many_args(Data([0; 9999]), true, Data([0; 9999]));
| ^^^^^^^^^^^^^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
error: aborting due to 3 previous errors

View file

@ -4,7 +4,7 @@ error: moving 9999 bytes
LL | let cell = std::cell::UnsafeCell::new(data);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
note: the lint level is defined here
--> $DIR/inline_mir.rs:2:9
|
@ -17,7 +17,7 @@ error: moving 9999 bytes
LL | std::hint::black_box(cell);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
error: aborting due to 2 previous errors

View file

@ -4,7 +4,7 @@ error: moving 10024 bytes
LL | let z = (x, 42);
| ^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
note: the lint level is defined here
--> $DIR/large_future.rs:1:9
|
@ -17,7 +17,7 @@ error: moving 10024 bytes
LL | let a = z.0;
| ^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
error: aborting due to 2 previous errors

View file

@ -4,7 +4,7 @@ error: moving 10024 bytes
LL | let z = (x, 42);
| ^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
note: the lint level is defined here
--> $DIR/large_future.rs:1:9
|
@ -17,7 +17,7 @@ error: moving 10024 bytes
LL | let a = z.0;
| ^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
error: aborting due to 2 previous errors

View file

@ -4,7 +4,7 @@ error: moving 9999 bytes
LL | data,
| ^^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
note: the lint level is defined here
--> $DIR/move_into_box_rc_arc.rs:1:9
|

View file

@ -4,7 +4,7 @@ error: moving 9999 bytes
LL | let data = Data([100; 9999]);
| ^^^^^^^^^^^^^^^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
note: the lint level is defined here
--> $DIR/move_into_fn.rs:5:9
|
@ -17,7 +17,7 @@ error: moving 9999 bytes
LL | take_data(data);
| ^^^^ value moved from here
|
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
= note: the current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
error: aborting due to 2 previous errors

View file

@ -17,7 +17,7 @@ error: crate-level attribute should be an inner attribute: add an exclamation ma
LL | #[crate_name = concat !()]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this macro def
note: this attribute does not have an `!`, which means it is applied to this macro def
--> $DIR/concat-in-crate-name-issue-137687.rs:5:1
|
LL | / macro_rules! a {

View file

@ -27,7 +27,7 @@ error: crate-level attribute should be an inner attribute: add an exclamation ma
LL | #[recursion_limit="1"]
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: This attribute does not have an `!`, which means it is applied to this macro def
note: this attribute does not have an `!`, which means it is applied to this macro def
--> $DIR/unused-attr-macro-rules.rs:12:1
|
LL | / macro_rules! foo {

View file

@ -4,9 +4,9 @@ error[E0764]: mutable borrows of temporaries that have their lifetime extended u
LL | static TEST: &'static mut [isize] = &mut [];
| ^^^^^^^ this mutable borrow refers to such a temporary
|
= note: Temporaries in constants and statics can have their lifetime extended until the end of the program
= note: To avoid accidentally creating global mutable state, such temporaries must be immutable
= help: If you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
error: aborting due to 1 previous error

View file

@ -4,7 +4,7 @@ error: item annotated with `#[unstable_feature_bound]` should not be stable
LL | fn bar() {}
| ^^^^^^^^^^^
|
= help: If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
= help: if this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
error: aborting due to 1 previous error

View file

@ -1465,6 +1465,7 @@ compiler = [
"@oli-obk",
"@petrochenkov",
"@SparrowLii",
"@tiif",
"@WaffleLapkin",
"@wesleywiser",
]