Auto merge of #150284 - JonathanBrouwer:rollup-yeibc0p, r=JonathanBrouwer
Rollup of 3 pull requests Successful merges: - rust-lang/rust#149928 (Detail expectation on non-`()` block tail in `if` then condition with no `else`) - rust-lang/rust#150166 (Don't lint on interior mutable `const` item coming from derefs) - rust-lang/rust#150281 (miri subtree update) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
4f14395c37
38 changed files with 473 additions and 240 deletions
|
|
@ -1838,7 +1838,20 @@ impl<'tcx> CoerceMany<'tcx> {
|
|||
hir::ExprKind::Match(.., hir::MatchSource::TryDesugar(_))
|
||||
)
|
||||
{
|
||||
err.span_label(cond_expr.span, "expected this to be `()`");
|
||||
if let ObligationCauseCode::BlockTailExpression(hir_id, hir::MatchSource::Normal) =
|
||||
cause.code()
|
||||
&& let hir::Node::Block(block) = fcx.tcx.hir_node(*hir_id)
|
||||
&& let hir::Node::Expr(expr) = fcx.tcx.parent_hir_node(block.hir_id)
|
||||
&& let hir::Node::Expr(if_expr) = fcx.tcx.parent_hir_node(expr.hir_id)
|
||||
&& let hir::ExprKind::If(_cond, _then, None) = if_expr.kind
|
||||
{
|
||||
err.span_label(
|
||||
cond_expr.span,
|
||||
"`if` expressions without `else` arms expect their inner expression to be `()`",
|
||||
);
|
||||
} else {
|
||||
err.span_label(cond_expr.span, "expected this to be `()`");
|
||||
}
|
||||
if expr.can_have_side_effects() {
|
||||
fcx.suggest_semicolon_at_end(cond_expr.span, &mut err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, ExprKind, ItemKind, Node, find_attr};
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_session::{declare_lint, declare_lint_pass};
|
||||
|
||||
use crate::lints::{ConstItemInteriorMutationsDiag, ConstItemInteriorMutationsSuggestionStatic};
|
||||
|
|
@ -77,6 +78,13 @@ impl<'tcx> LateLintPass<'tcx> for InteriorMutableConsts {
|
|||
if let ExprKind::Path(qpath) = &receiver.kind
|
||||
&& let Res::Def(DefKind::Const | DefKind::AssocConst, const_did) =
|
||||
typeck.qpath_res(qpath, receiver.hir_id)
|
||||
// Don't consider derefs as those can do arbitrary things
|
||||
// like using thread local (see rust-lang/rust#150157)
|
||||
&& !cx
|
||||
.typeck_results()
|
||||
.expr_adjustments(receiver)
|
||||
.into_iter()
|
||||
.any(|adj| matches!(adj.kind, Adjust::Deref(_)))
|
||||
// Let's do the attribute check after the other checks for perf reasons
|
||||
&& find_attr!(
|
||||
cx.tcx.get_all_attrs(method_did),
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ degree documented below):
|
|||
- We have unofficial support (not maintained by the Miri team itself) for some further operating systems.
|
||||
- `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite.
|
||||
- `freebsd`: maintained by @YohDeadfall and @LorrensP-2158466. Supports the entire test suite.
|
||||
- `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works.
|
||||
- `android`: **maintainer wanted**. Basic OS APIs and concurrency work, but file system access is not supported.
|
||||
- For targets on other operating systems, Miri might fail before even reaching the `main` function.
|
||||
|
||||
However, even for targets that we do support, the degree of support for accessing platform APIs
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
dc47a69ed94bc88b10b7d500cceacf29b87bcbbe
|
||||
cb79c42008b970269f6a06b257e5f04b93f24d03
|
||||
|
|
|
|||
|
|
@ -374,9 +374,9 @@ impl Permission {
|
|||
self.inner.strongest_idempotent_foreign_access(prot)
|
||||
}
|
||||
|
||||
/// Returns the strongest access allowed from a child to this node without
|
||||
/// Returns the strongest access allowed that is local to this node without
|
||||
/// causing UB (only considers possible transitions to this permission).
|
||||
pub fn strongest_allowed_child_access(&self, protected: bool) -> WildcardAccessLevel {
|
||||
pub fn strongest_allowed_local_access(&self, protected: bool) -> WildcardAccessLevel {
|
||||
match self.inner {
|
||||
// Everything except disabled can be accessed by read access.
|
||||
Disabled => WildcardAccessLevel::None,
|
||||
|
|
@ -794,9 +794,9 @@ mod propagation_optimization_checks {
|
|||
/// Checks that `strongest_allowed_child_access` correctly
|
||||
/// represents which transitions are possible.
|
||||
#[test]
|
||||
fn strongest_allowed_child_access() {
|
||||
fn strongest_allowed_local_access() {
|
||||
for (permission, protected) in <(Permission, bool)>::exhaustive() {
|
||||
let strongest_child_access = permission.strongest_allowed_child_access(protected);
|
||||
let strongest_local_access = permission.strongest_allowed_local_access(protected);
|
||||
|
||||
let is_read_valid = Permission::perform_access(
|
||||
AccessKind::Read,
|
||||
|
|
@ -814,8 +814,8 @@ mod propagation_optimization_checks {
|
|||
)
|
||||
.is_some();
|
||||
|
||||
assert_eq!(is_read_valid, strongest_child_access >= WildcardAccessLevel::Read);
|
||||
assert_eq!(is_write_valid, strongest_child_access >= WildcardAccessLevel::Write);
|
||||
assert_eq!(is_read_valid, strongest_local_access >= WildcardAccessLevel::Read);
|
||||
assert_eq!(is_write_valid, strongest_local_access >= WildcardAccessLevel::Write);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ impl LocationState {
|
|||
// We need to update the wildcard state, if the permission
|
||||
// of an exposed pointer changes.
|
||||
if node.is_exposed {
|
||||
let access_type = self.permission.strongest_allowed_child_access(protected);
|
||||
let access_type = self.permission.strongest_allowed_local_access(protected);
|
||||
WildcardState::update_exposure(idx, access_type, nodes, wildcard_accesses);
|
||||
}
|
||||
}
|
||||
|
|
@ -1034,6 +1034,9 @@ impl<'tcx> LocationTree {
|
|||
wildcard_state.access_relatedness(access_kind, only_foreign)
|
||||
};
|
||||
|
||||
// Whether there is an exposed node in this tree that allows this access.
|
||||
let mut has_valid_exposed = false;
|
||||
|
||||
// This does a traversal across the tree updating children before their parents. The
|
||||
// difference to `perform_normal_access` is that we take the access relatedness from
|
||||
// the wildcard tracking state of the node instead of from the visitor itself.
|
||||
|
|
@ -1082,6 +1085,17 @@ impl<'tcx> LocationTree {
|
|||
return Err(no_valid_exposed_references_error(diagnostics));
|
||||
};
|
||||
|
||||
let mut entry = args.data.perms.entry(args.idx);
|
||||
let perm = entry.or_insert(node.default_location_state());
|
||||
|
||||
// We only count exposed nodes through which an access could happen.
|
||||
if node.is_exposed
|
||||
&& perm.permission.strongest_allowed_local_access(protected).allows(access_kind)
|
||||
&& max_local_tag.is_none_or(|max_local_tag| max_local_tag >= node.tag)
|
||||
{
|
||||
has_valid_exposed = true;
|
||||
}
|
||||
|
||||
let Some(relatedness) = wildcard_relatedness.to_relatedness() else {
|
||||
// If the access type is Either, then we do not apply any transition
|
||||
// to this node, but we still update each of its children.
|
||||
|
|
@ -1090,8 +1104,6 @@ impl<'tcx> LocationTree {
|
|||
return Ok(());
|
||||
};
|
||||
|
||||
let mut entry = args.data.perms.entry(args.idx);
|
||||
let perm = entry.or_insert(node.default_location_state());
|
||||
// We know the exact relatedness, so we can actually do precise checks.
|
||||
perm.perform_transition(
|
||||
args.idx,
|
||||
|
|
@ -1115,6 +1127,21 @@ impl<'tcx> LocationTree {
|
|||
})
|
||||
},
|
||||
)?;
|
||||
// If there is no exposed node in this tree that allows this access, then the
|
||||
// access *must* be foreign. So we check if the root of this tree would allow this
|
||||
// as a foreign access, and if not, then we can error.
|
||||
// In practice, all wildcard trees accept foreign accesses, but the main tree does
|
||||
// not, so this catches UB when none of the nodes in the main tree allows this access.
|
||||
if !has_valid_exposed
|
||||
&& self
|
||||
.wildcard_accesses
|
||||
.get(root)
|
||||
.unwrap()
|
||||
.access_relatedness(access_kind, /* only_foreign */ true)
|
||||
.is_none()
|
||||
{
|
||||
return Err(no_valid_exposed_references_error(diagnostics)).into();
|
||||
}
|
||||
interp_ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,16 @@ pub enum WildcardAccessLevel {
|
|||
Read,
|
||||
Write,
|
||||
}
|
||||
impl WildcardAccessLevel {
|
||||
/// Weather this access kind is allowed at this level.
|
||||
pub fn allows(self, kind: AccessKind) -> bool {
|
||||
let required_level = match kind {
|
||||
AccessKind::Read => Self::Read,
|
||||
AccessKind::Write => Self::Write,
|
||||
};
|
||||
required_level <= self
|
||||
}
|
||||
}
|
||||
|
||||
/// Where the access happened relative to the current node.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
|
|
@ -430,7 +440,7 @@ impl Tree {
|
|||
.map(|p| p.permission())
|
||||
.unwrap_or_else(|| node.default_location_state().permission());
|
||||
|
||||
let access_type = perm.strongest_allowed_child_access(protected);
|
||||
let access_type = perm.strongest_allowed_local_access(protected);
|
||||
WildcardState::update_exposure(
|
||||
id,
|
||||
access_type,
|
||||
|
|
@ -480,7 +490,7 @@ impl Tree {
|
|||
perms.get(id).copied().unwrap_or_else(|| node.default_location_state());
|
||||
|
||||
perm.permission()
|
||||
.strongest_allowed_child_access(protected_tags.contains_key(&node.tag))
|
||||
.strongest_allowed_local_access(protected_tags.contains_key(&node.tag))
|
||||
} else {
|
||||
WildcardAccessLevel::None
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,20 +19,6 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// Prefix should have already been checked.
|
||||
let unprefixed_name = link_name.as_str().strip_prefix("llvm.aarch64.").unwrap();
|
||||
match unprefixed_name {
|
||||
"isb" => {
|
||||
let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
|
||||
let arg = this.read_scalar(arg)?.to_i32()?;
|
||||
match arg {
|
||||
// SY ("full system scope")
|
||||
15 => {
|
||||
this.yield_active_thread();
|
||||
}
|
||||
_ => {
|
||||
throw_unsup_format!("unsupported llvm.aarch64.isb argument {}", arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used to implement the vpmaxq_u8 function.
|
||||
// Computes the maximum of adjacent pairs; the first half of the output is produced from the
|
||||
// `left` input, the second half of the output from the `right` input.
|
||||
|
|
|
|||
|
|
@ -813,22 +813,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this, link_name, abi, args, dest,
|
||||
);
|
||||
}
|
||||
// FIXME: Move this to an `arm` submodule.
|
||||
"llvm.arm.hint" if this.tcx.sess.target.arch == Arch::Arm => {
|
||||
let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
|
||||
let arg = this.read_scalar(arg)?.to_i32()?;
|
||||
// Note that different arguments might have different target feature requirements.
|
||||
match arg {
|
||||
// YIELD
|
||||
1 => {
|
||||
this.expect_target_feature_for_intrinsic(link_name, "v6")?;
|
||||
this.yield_active_thread();
|
||||
}
|
||||
_ => {
|
||||
throw_unsup_format!("unsupported llvm.arm.hint argument {}", arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to shims in submodules.
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -307,6 +307,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let res = this.GetFileInformationByHandle(handle, info)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"SetFileInformationByHandle" => {
|
||||
let [handle, class, info, size] =
|
||||
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
|
||||
let res = this.SetFileInformationByHandle(handle, class, info, size)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"FlushFileBuffers" => {
|
||||
let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
|
||||
let res = this.FlushFileBuffers(handle)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"DeleteFileW" => {
|
||||
let [file_name] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
|
||||
let res = this.DeleteFileW(file_name)?;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::path::PathBuf;
|
|||
use std::time::SystemTime;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use rustc_abi::Size;
|
||||
use rustc_target::spec::Os;
|
||||
|
||||
use crate::shims::files::{FdId, FileDescription, FileHandle};
|
||||
|
|
@ -372,6 +373,123 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
interp_ok(this.eval_windows("c", "TRUE"))
|
||||
}
|
||||
|
||||
fn SetFileInformationByHandle(
|
||||
&mut self,
|
||||
file: &OpTy<'tcx>, // HANDLE
|
||||
class: &OpTy<'tcx>, // FILE_INFO_BY_HANDLE_CLASS
|
||||
file_information: &OpTy<'tcx>, // LPVOID
|
||||
buffer_size: &OpTy<'tcx>, // DWORD
|
||||
) -> InterpResult<'tcx, Scalar> {
|
||||
// ^ Returns BOOL (i32 on Windows)
|
||||
let this = self.eval_context_mut();
|
||||
this.assert_target_os(Os::Windows, "SetFileInformationByHandle");
|
||||
this.check_no_isolation("`SetFileInformationByHandle`")?;
|
||||
|
||||
let class = this.read_scalar(class)?.to_u32()?;
|
||||
let buffer_size = this.read_scalar(buffer_size)?.to_u32()?;
|
||||
let file_information = this.read_pointer(file_information)?;
|
||||
this.check_ptr_access(
|
||||
file_information,
|
||||
Size::from_bytes(buffer_size),
|
||||
CheckInAllocMsg::MemoryAccess,
|
||||
)?;
|
||||
|
||||
let file = this.read_handle(file, "SetFileInformationByHandle")?;
|
||||
let Handle::File(fd_num) = file else { this.invalid_handle("SetFileInformationByHandle")? };
|
||||
let Some(desc) = this.machine.fds.get(fd_num) else {
|
||||
this.invalid_handle("SetFileInformationByHandle")?
|
||||
};
|
||||
let file = desc.downcast::<FileHandle>().ok_or_else(|| {
|
||||
err_unsup_format!(
|
||||
"`SetFileInformationByHandle` is only supported on file-backed file descriptors"
|
||||
)
|
||||
})?;
|
||||
|
||||
if class == this.eval_windows_u32("c", "FileEndOfFileInfo") {
|
||||
let place = this
|
||||
.ptr_to_mplace(file_information, this.windows_ty_layout("FILE_END_OF_FILE_INFO"));
|
||||
let new_len =
|
||||
this.read_scalar(&this.project_field_named(&place, "EndOfFile")?)?.to_i64()?;
|
||||
match file.file.set_len(new_len.try_into().unwrap()) {
|
||||
Ok(_) => interp_ok(this.eval_windows("c", "TRUE")),
|
||||
Err(e) => {
|
||||
this.set_last_error(e)?;
|
||||
interp_ok(this.eval_windows("c", "FALSE"))
|
||||
}
|
||||
}
|
||||
} else if class == this.eval_windows_u32("c", "FileAllocationInfo") {
|
||||
// On Windows, files are somewhat similar to a `Vec` in that they have a separate
|
||||
// "length" (called "EOF position") and "capacity" (called "allocation size").
|
||||
// Growing the allocation size is largely a performance hint which we can
|
||||
// ignore -- it can also be directly queried, but we currently do not support that.
|
||||
// So we only need to do something if this operation shrinks the allocation size
|
||||
// so far that it affects the EOF position.
|
||||
let place = this
|
||||
.ptr_to_mplace(file_information, this.windows_ty_layout("FILE_ALLOCATION_INFO"));
|
||||
let new_alloc_size: u64 = this
|
||||
.read_scalar(&this.project_field_named(&place, "AllocationSize")?)?
|
||||
.to_i64()?
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let old_len = match file.file.metadata() {
|
||||
Ok(m) => m.len(),
|
||||
Err(e) => {
|
||||
this.set_last_error(e)?;
|
||||
return interp_ok(this.eval_windows("c", "FALSE"));
|
||||
}
|
||||
};
|
||||
if new_alloc_size < old_len {
|
||||
match file.file.set_len(new_alloc_size) {
|
||||
Ok(_) => interp_ok(this.eval_windows("c", "TRUE")),
|
||||
Err(e) => {
|
||||
this.set_last_error(e)?;
|
||||
interp_ok(this.eval_windows("c", "FALSE"))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
interp_ok(this.eval_windows("c", "TRUE"))
|
||||
}
|
||||
} else {
|
||||
throw_unsup_format!(
|
||||
"SetFileInformationByHandle: Unsupported `FileInformationClass` value {}",
|
||||
class
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn FlushFileBuffers(
|
||||
&mut self,
|
||||
file: &OpTy<'tcx>, // HANDLE
|
||||
) -> InterpResult<'tcx, Scalar> {
|
||||
// ^ returns BOOL (i32 on Windows)
|
||||
let this = self.eval_context_mut();
|
||||
this.assert_target_os(Os::Windows, "FlushFileBuffers");
|
||||
|
||||
let file = this.read_handle(file, "FlushFileBuffers")?;
|
||||
let Handle::File(fd_num) = file else { this.invalid_handle("FlushFileBuffers")? };
|
||||
let Some(desc) = this.machine.fds.get(fd_num) else {
|
||||
this.invalid_handle("FlushFileBuffers")?
|
||||
};
|
||||
let file = desc.downcast::<FileHandle>().ok_or_else(|| {
|
||||
err_unsup_format!(
|
||||
"`FlushFileBuffers` is only supported on file-backed file descriptors"
|
||||
)
|
||||
})?;
|
||||
|
||||
if !file.writable {
|
||||
this.set_last_error(IoError::WindowsError("ERROR_ACCESS_DENIED"))?;
|
||||
return interp_ok(this.eval_windows("c", "FALSE"));
|
||||
}
|
||||
|
||||
match file.file.sync_all() {
|
||||
Ok(_) => interp_ok(this.eval_windows("c", "TRUE")),
|
||||
Err(e) => {
|
||||
this.set_last_error(e)?;
|
||||
interp_ok(this.eval_windows("c", "FALSE"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn DeleteFileW(
|
||||
&mut self,
|
||||
file_name: &OpTy<'tcx>, // LPCWSTR
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_target::callconv::FnAbi;
|
|||
|
||||
use super::{
|
||||
ShiftOp, horizontal_bin_op, mpsadbw, packssdw, packsswb, packusdw, packuswb, permute, pmaddbw,
|
||||
pmulhrsw, psadbw, psign, shift_simd_by_scalar,
|
||||
pmulhrsw, psadbw, pshufb, psign, shift_simd_by_scalar,
|
||||
};
|
||||
use crate::*;
|
||||
|
||||
|
|
@ -189,28 +189,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!(dest_len, left_len);
|
||||
assert_eq!(dest_len, right_len);
|
||||
|
||||
for i in 0..dest_len {
|
||||
let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u8()?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
let res = if right & 0x80 == 0 {
|
||||
// Shuffle each 128-bit (16-byte) block independently.
|
||||
let j = u64::from(right % 16).strict_add(i & !15);
|
||||
this.read_scalar(&this.project_index(&left, j)?)?
|
||||
} else {
|
||||
// If the highest bit in `right` is 1, write zero.
|
||||
Scalar::from_u8(0)
|
||||
};
|
||||
|
||||
this.write_scalar(res, &dest)?;
|
||||
}
|
||||
pshufb(this, left, right, dest)?;
|
||||
}
|
||||
// Used to implement the _mm256_sign_epi{8,16,32} functions.
|
||||
// Negates elements from `left` when the corresponding element in
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use rustc_middle::ty::Ty;
|
|||
use rustc_span::Symbol;
|
||||
use rustc_target::callconv::FnAbi;
|
||||
|
||||
use super::{permute, pmaddbw, psadbw};
|
||||
use super::{permute, pmaddbw, psadbw, pshufb};
|
||||
use crate::*;
|
||||
|
||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
|
|
@ -102,6 +102,13 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
|
||||
permute(this, left, right, dest)?;
|
||||
}
|
||||
// Used to implement the _mm512_shuffle_epi8 intrinsic.
|
||||
"pshuf.b.512" => {
|
||||
let [left, right] =
|
||||
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
|
||||
|
||||
pshufb(this, left, right, dest)?;
|
||||
}
|
||||
_ => return interp_ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
interp_ok(EmulateItemResult::NeedsReturn)
|
||||
|
|
|
|||
|
|
@ -1155,6 +1155,51 @@ fn pclmulqdq<'tcx>(
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Shuffles bytes from `left` using `right` as pattern. Each 16-byte block is shuffled independently.
|
||||
///
|
||||
/// `left` and `right` are both vectors of type `len` x i8.
|
||||
///
|
||||
/// If the highest bit of a byte in `right` is not set, the corresponding byte in `dest` is taken
|
||||
/// from the current 16-byte block of `left` at the position indicated by the lowest 4 bits of this
|
||||
/// byte in `right`. If the highest bit of a byte in `right` is set, the corresponding byte in
|
||||
/// `dest` is set to `0`.
|
||||
///
|
||||
/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8>
|
||||
/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_shuffle_epi8>
|
||||
/// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_shuffle_epi8>
|
||||
fn pshufb<'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)?;
|
||||
|
||||
assert_eq!(dest_len, left_len);
|
||||
assert_eq!(dest_len, right_len);
|
||||
|
||||
for i in 0..dest_len {
|
||||
let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_u8()?;
|
||||
let dest = ecx.project_index(&dest, i)?;
|
||||
|
||||
let res = if right & 0x80 == 0 {
|
||||
// Shuffle each 128-bit (16-byte) block independently.
|
||||
let block_offset = i & !15; // round down to previous multiple of 16
|
||||
let j = block_offset.strict_add((right % 16).into());
|
||||
ecx.read_scalar(&ecx.project_index(&left, j)?)?
|
||||
} else {
|
||||
// If the highest bit in `right` is 1, write zero.
|
||||
Scalar::from_u8(0)
|
||||
};
|
||||
|
||||
ecx.write_scalar(res, &dest)?;
|
||||
}
|
||||
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Packs two N-bit integer vectors to a single N/2-bit integers.
|
||||
///
|
||||
/// The conversion from N-bit to N/2-bit should be provided by `f`.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc_middle::ty::Ty;
|
|||
use rustc_span::Symbol;
|
||||
use rustc_target::callconv::FnAbi;
|
||||
|
||||
use super::{horizontal_bin_op, pmaddbw, pmulhrsw, psign};
|
||||
use super::{horizontal_bin_op, pmaddbw, pmulhrsw, pshufb, psign};
|
||||
use crate::*;
|
||||
|
||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
|
|
@ -29,27 +29,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!(dest_len, left_len);
|
||||
assert_eq!(dest_len, right_len);
|
||||
|
||||
for i in 0..dest_len {
|
||||
let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u8()?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
let res = if right & 0x80 == 0 {
|
||||
let j = right % 16; // index wraps around
|
||||
this.read_scalar(&this.project_index(&left, j.into())?)?
|
||||
} else {
|
||||
// If the highest bit in `right` is 1, write zero.
|
||||
Scalar::from_u8(0)
|
||||
};
|
||||
|
||||
this.write_scalar(res, &dest)?;
|
||||
}
|
||||
pshufb(this, left, right, dest)?;
|
||||
}
|
||||
// Used to implement the _mm_h{adds,subs}_epi16 functions.
|
||||
// Horizontally add / subtract with saturation adjacent 16-bit
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@ fn main() {
|
|||
// the inner reference is dangling
|
||||
let x: &&u32 = unsafe {
|
||||
let x: u32 = 42;
|
||||
&&* &raw const x
|
||||
&&*&raw const x
|
||||
};
|
||||
|
||||
let _ = || { //~ ERROR: encountered a dangling reference
|
||||
//~v ERROR: encountered a dangling reference
|
||||
let _ = || {
|
||||
match x {
|
||||
&&_y => {},
|
||||
&&_y => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ error: Undefined Behavior: constructing invalid value: encountered a dangling re
|
|||
LL | let _ = || {
|
||||
| _____________^
|
||||
LL | | match x {
|
||||
LL | | &&_y => {},
|
||||
LL | | &&_y => {}
|
||||
LL | | }
|
||||
LL | | };
|
||||
| |_____^ Undefined Behavior occurred here
|
||||
|
|
|
|||
|
|
@ -15,14 +15,15 @@ fn main() {
|
|||
let x: &(&u32, &u32) = unsafe {
|
||||
let a = 21;
|
||||
let b = 37;
|
||||
let ra = &* &raw const a;
|
||||
let rb = &* &raw const b;
|
||||
let ra = &*&raw const a;
|
||||
let rb = &*&raw const b;
|
||||
&(ra, rb)
|
||||
};
|
||||
|
||||
let _ = || { //~ ERROR: encountered a dangling reference
|
||||
//~v ERROR: encountered a dangling reference
|
||||
let _ = || {
|
||||
match x {
|
||||
(&_y, _) => {},
|
||||
(&_y, _) => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ error: Undefined Behavior: constructing invalid value: encountered a dangling re
|
|||
LL | let _ = || {
|
||||
| _____________^
|
||||
LL | | match x {
|
||||
LL | | (&_y, _) => {},
|
||||
LL | | (&_y, _) => {}
|
||||
LL | | }
|
||||
LL | | };
|
||||
| |_____^ Undefined Behavior occurred here
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
#[repr(C)]
|
||||
#[allow(dead_code)]
|
||||
enum E {
|
||||
V0, // discriminant: 0
|
||||
V1, // 1
|
||||
V2(!), // 2
|
||||
V0, // discriminant: 0
|
||||
V1, // 1
|
||||
V2(!), // 2
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
@ -20,7 +20,8 @@ fn main() {
|
|||
// After rust-lang/rust#138961, constructing the closure performs a reborrow of r.
|
||||
// Nevertheless, the discriminant is only actually inspected when the closure
|
||||
// is called.
|
||||
match r { //~ ERROR: read discriminant of an uninhabited enum variant
|
||||
match r {
|
||||
//~^ ERROR: read discriminant of an uninhabited enum variant
|
||||
E::V0 => {}
|
||||
E::V1 => {}
|
||||
E::V2(_) => {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
use std::cell::Cell;
|
||||
|
||||
/// Checks how accesses from one subtree affect other subtrees.
|
||||
/// This test checks that an access from a subtree performs a
|
||||
/// wildcard access on all earlier trees, and that local
|
||||
/// accesses are treated as access errors for tags that are
|
||||
/// larger than the root of the accessed subtree.
|
||||
/// This tests the case were we have multiple exposed nodes on
|
||||
/// the main tree that are invalid because their tag is too large.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// Activates ref1.
|
||||
*ref1 = 4;
|
||||
|
||||
let ref2 = unsafe { &mut *wild };
|
||||
|
||||
// Freezes ref1.
|
||||
let ref3 = unsafe { &mut *(ptr_base as *mut Cell<u32>) };
|
||||
let _int3 = ref3 as *mut Cell<u32> as usize;
|
||||
let ref4 = unsafe { &mut *(ptr_base as *mut Cell<u32>) };
|
||||
let _int4 = ref4 as *mut Cell<u32> as usize;
|
||||
|
||||
// ┌──────────────┐
|
||||
// │ │
|
||||
// │ptr_base(Act) ├───────────┬──────────────────┐ *
|
||||
// │ │ │ │ │
|
||||
// └──────┬───────┘ │ │ │
|
||||
// │ │ │ │
|
||||
// │ │ │ │
|
||||
// ▼ ▼ ▼ ▼
|
||||
// ┌─────────────┐ ┌────────────┐ ┌────────────┐ ┌───────────┐
|
||||
// │ │ │ │ │ │ │ │
|
||||
// │ ref1(Frz)* │ │ ref3(ReIM)*│ │ ref4(ReIM)*│ │ ref2(Res) │
|
||||
// │ │ │ │ │ │ │ │
|
||||
// └─────────────┘ └────────────┘ └────────────┘ └───────────┘
|
||||
|
||||
// Performs a wildcard access on the main root. However, as there are
|
||||
// no exposed tags with write permissions and a tag smaller than ref2
|
||||
// this access fails.
|
||||
*ref2 = 13; //~ ERROR: /write access through .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
error: Undefined Behavior: write access through <wildcard> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed2.rs:LL:CC
|
||||
|
|
||||
LL | *ref2 = 13;
|
||||
| ^^^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: there are no exposed tags which may perform this access here
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -1,114 +1,4 @@
|
|||
Running GenMC Verification...
|
||||
warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.
|
||||
--> RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
|
|
||||
LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
= note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC
|
||||
= note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC
|
||||
= note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC
|
||||
= note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC
|
||||
= note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC
|
||||
= note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC
|
||||
= note: inside `std::panicking::catch_unwind::<isize, {closure@std::rt::lang_start_internal::{closure#0}}>` at RUSTLIB/std/src/panicking.rs:LL:CC
|
||||
= note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC
|
||||
= note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC
|
||||
= note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC
|
||||
|
||||
warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access.
|
||||
--> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC
|
||||
|
|
||||
LL | || self
|
||||
| ________________^
|
||||
LL | | .state
|
||||
LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed)
|
||||
| |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC
|
||||
= note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC
|
||||
= note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
|
||||
= note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
|
||||
= note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::<std::option::Option<std::ffi::OsString>>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
|
||||
= note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::<std::option::Option<std::ffi::OsString>>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
|
||||
= note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
|
||||
= note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC
|
||||
= note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC
|
||||
= note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/genmc/fail/shims/exit.rs:LL:CC
|
||||
|
|
||||
LL | / std::thread::spawn(|| {
|
||||
LL | | unsafe { std::hint::unreachable_unchecked() };
|
||||
LL | | });
|
||||
| |______^
|
||||
|
||||
warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.
|
||||
--> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC
|
||||
|
|
||||
LL | || self
|
||||
| ________________^
|
||||
LL | | .state
|
||||
LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed)
|
||||
| |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC
|
||||
= note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC
|
||||
= note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
|
||||
= note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
|
||||
= note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::<std::option::Option<std::ffi::OsString>>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
|
||||
= note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::<std::option::Option<std::ffi::OsString>>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
|
||||
= note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
|
||||
= note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC
|
||||
= note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC
|
||||
= note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/genmc/fail/shims/exit.rs:LL:CC
|
||||
|
|
||||
LL | / std::thread::spawn(|| {
|
||||
LL | | unsafe { std::hint::unreachable_unchecked() };
|
||||
LL | | });
|
||||
| |______^
|
||||
|
||||
warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.
|
||||
--> RUSTLIB/std/src/rt.rs:LL:CC
|
||||
|
|
||||
LL | / CLEANUP.call_once(|| unsafe {
|
||||
LL | | // Flush stdout and disable buffering.
|
||||
LL | | crate::io::cleanup();
|
||||
... |
|
||||
LL | | });
|
||||
| |______^ GenMC might miss possible behaviors of this code
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC
|
||||
= note: inside `std::process::exit` at RUSTLIB/std/src/process.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/genmc/fail/shims/exit.rs:LL:CC
|
||||
|
|
||||
LL | std::process::exit(0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access.
|
||||
--> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC
|
||||
|
|
||||
LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
|
||||
|
|
||||
= note: BACKTRACE:
|
||||
= note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC
|
||||
= note: inside `std::sys::pal::PLATFORM::os::exit` at RUSTLIB/std/src/sys/pal/PLATFORM/os.rs:LL:CC
|
||||
= note: inside `std::process::exit` at RUSTLIB/std/src/process.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/genmc/fail/shims/exit.rs:LL:CC
|
||||
|
|
||||
LL | std::process::exit(0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: Undefined Behavior: entering unreachable code
|
||||
--> tests/genmc/fail/shims/exit.rs:LL:CC
|
||||
|
|
||||
|
|
@ -122,5 +12,5 @@ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a
|
|||
|
||||
note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
|
||||
|
||||
error: aborting due to 1 previous error; 5 warnings emitted
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
//@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\].*" -> ""
|
||||
//@normalize-stderr-test: "\n[ =]*note:.*" -> ""
|
||||
//@normalize-stderr-test: "DefId\([^()]*\)" -> "DefId"
|
||||
// Paths differ between bootstrap and stand-alone Miri runs, normalize them to be the same
|
||||
//@normalize-stderr-test: "/rustc-dev/[^/]*/" -> ""
|
||||
// Somehow on rustc Windows CI, the "Miri caused an ICE" message is not shown
|
||||
// and we don't even get a regular panic; rustc aborts with a different exit code instead.
|
||||
//@ignore-host: windows
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ use windows_sys::Win32::Foundation::{
|
|||
};
|
||||
use windows_sys::Win32::Storage::FileSystem::{
|
||||
BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW, CreateFileW, DeleteFileW,
|
||||
FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, FILE_BEGIN, FILE_CURRENT,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ,
|
||||
FILE_SHARE_WRITE, GetFileInformationByHandle, OPEN_ALWAYS, OPEN_EXISTING, SetFilePointerEx,
|
||||
FILE_ALLOCATION_INFO, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, FILE_BEGIN,
|
||||
FILE_CURRENT, FILE_END_OF_FILE_INFO, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, FileAllocationInfo, FileEndOfFileInfo,
|
||||
FlushFileBuffers, GetFileInformationByHandle, OPEN_ALWAYS, OPEN_EXISTING,
|
||||
SetFileInformationByHandle, SetFilePointerEx,
|
||||
};
|
||||
use windows_sys::Win32::System::IO::IO_STATUS_BLOCK;
|
||||
use windows_sys::Win32::System::Threading::GetCurrentProcess;
|
||||
|
|
@ -37,7 +39,9 @@ fn main() {
|
|||
test_ntstatus_to_dos();
|
||||
test_file_read_write();
|
||||
test_file_seek();
|
||||
test_set_file_info();
|
||||
test_dup_handle();
|
||||
test_flush_buffers();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -275,6 +279,32 @@ unsafe fn test_file_read_write() {
|
|||
assert_eq!(GetLastError(), 1234);
|
||||
}
|
||||
|
||||
unsafe fn test_set_file_info() {
|
||||
let temp = utils::tmp().join("test_set_file.txt");
|
||||
let mut file = fs::File::create(&temp).unwrap();
|
||||
let handle = file.as_raw_handle();
|
||||
|
||||
let info = FILE_END_OF_FILE_INFO { EndOfFile: 20 };
|
||||
let res = SetFileInformationByHandle(
|
||||
handle,
|
||||
FileEndOfFileInfo,
|
||||
ptr::from_ref(&info).cast(),
|
||||
size_of::<FILE_END_OF_FILE_INFO>().try_into().unwrap(),
|
||||
);
|
||||
assert!(res != 0);
|
||||
assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 20);
|
||||
|
||||
let info = FILE_ALLOCATION_INFO { AllocationSize: 0 };
|
||||
let res = SetFileInformationByHandle(
|
||||
handle,
|
||||
FileAllocationInfo,
|
||||
ptr::from_ref(&info).cast(),
|
||||
size_of::<FILE_ALLOCATION_INFO>().try_into().unwrap(),
|
||||
);
|
||||
assert!(res != 0);
|
||||
assert_eq!(file.metadata().unwrap().len(), 0);
|
||||
}
|
||||
|
||||
unsafe fn test_dup_handle() {
|
||||
let temp = utils::tmp().join("test_dup.txt");
|
||||
|
||||
|
|
@ -333,6 +363,19 @@ unsafe fn test_file_seek() {
|
|||
assert_eq!(pos, 5);
|
||||
}
|
||||
|
||||
unsafe fn test_flush_buffers() {
|
||||
let temp = utils::tmp().join("test_flush_buffers.txt");
|
||||
let file = fs::File::options().create(true).write(true).read(true).open(&temp).unwrap();
|
||||
if FlushFileBuffers(file.as_raw_handle()) == 0 {
|
||||
panic!("Failed to flush buffers");
|
||||
}
|
||||
|
||||
let file = fs::File::options().read(true).open(&temp).unwrap();
|
||||
if FlushFileBuffers(file.as_raw_handle()) != 0 {
|
||||
panic!("Successfully flushed buffers on read-only file");
|
||||
}
|
||||
}
|
||||
|
||||
fn to_wide_cstr(path: &Path) -> Vec<u16> {
|
||||
let mut raw_path = path.as_os_str().encode_wide().collect::<Vec<_>>();
|
||||
raw_path.extend([0, 0]);
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ fn main() {
|
|||
test_errors();
|
||||
test_from_raw_os_error();
|
||||
test_file_clone();
|
||||
test_file_set_len();
|
||||
test_file_sync();
|
||||
// Windows file handling is very incomplete.
|
||||
if cfg!(not(windows)) {
|
||||
test_file_set_len();
|
||||
test_file_sync();
|
||||
test_rename();
|
||||
test_directory();
|
||||
test_canonicalize();
|
||||
|
|
@ -77,6 +77,9 @@ fn test_file() {
|
|||
// However, writing 0 bytes can succeed or fail.
|
||||
let _ignore = file.write(&[]);
|
||||
|
||||
// Test calling File::create on an existing file, since that uses a different code path
|
||||
File::create(&path).unwrap();
|
||||
|
||||
// Removing file should succeed.
|
||||
remove_file(&path).unwrap();
|
||||
}
|
||||
|
|
@ -87,7 +90,6 @@ fn test_file_partial_reads_writes() {
|
|||
|
||||
// Ensure we sometimes do incomplete writes.
|
||||
check_nondet(|| {
|
||||
let _ = remove_file(&path1); // FIXME(win, issue #4483): errors if the file already exists
|
||||
let mut file = File::create(&path1).unwrap();
|
||||
file.write(&[0; 4]).unwrap() == 4
|
||||
});
|
||||
|
|
@ -210,7 +212,12 @@ fn test_file_set_len() {
|
|||
|
||||
// Can't use set_len on a file not opened for writing
|
||||
let file = OpenOptions::new().read(true).open(&path).unwrap();
|
||||
assert_eq!(ErrorKind::InvalidInput, file.set_len(14).unwrap_err().kind());
|
||||
// Due to https://github.com/rust-lang/miri/issues/4457, we have to assume the failure could
|
||||
// be either of the Windows or Unix kind, no matter which platform we're on.
|
||||
assert!(
|
||||
[ErrorKind::PermissionDenied, ErrorKind::InvalidInput]
|
||||
.contains(&file.set_len(14).unwrap_err().kind())
|
||||
);
|
||||
|
||||
remove_file(&path).unwrap();
|
||||
}
|
||||
|
|
@ -224,10 +231,16 @@ fn test_file_sync() {
|
|||
file.sync_data().unwrap();
|
||||
file.sync_all().unwrap();
|
||||
|
||||
// Test that we can call sync_data and sync_all on a file opened for reading.
|
||||
// Test that we can call sync_data and sync_all on a file opened for reading on unix, but not
|
||||
// on Windows
|
||||
let file = File::open(&path).unwrap();
|
||||
file.sync_data().unwrap();
|
||||
file.sync_all().unwrap();
|
||||
if cfg!(unix) {
|
||||
file.sync_data().unwrap();
|
||||
file.sync_all().unwrap();
|
||||
} else {
|
||||
file.sync_data().unwrap_err();
|
||||
file.sync_all().unwrap_err();
|
||||
}
|
||||
|
||||
remove_file(&path).unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,6 +143,30 @@ unsafe fn test_avx512() {
|
|||
assert_eq_m512i(r, e);
|
||||
}
|
||||
test_mm512_permutexvar_epi32();
|
||||
|
||||
#[target_feature(enable = "avx512bw")]
|
||||
unsafe fn test_mm512_shuffle_epi8() {
|
||||
#[rustfmt::skip]
|
||||
let a = _mm512_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63);
|
||||
#[rustfmt::skip]
|
||||
let b = _mm512_set_epi8(-1, 127, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
-1, 127, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
-1, 127, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
-1, 127, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
|
||||
let r = _mm512_shuffle_epi8(a, b);
|
||||
// `_mm512_set_epi8` sets the bytes in inverse order (?!?), so the indices in `b` seem to
|
||||
// index from the *back* of the corresponding 16-byte block in `a`.
|
||||
#[rustfmt::skip]
|
||||
let e = _mm512_set_epi8(0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
|
||||
0, 16, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
|
||||
0, 32, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
|
||||
0, 48, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62);
|
||||
assert_eq_m512i(r, e);
|
||||
}
|
||||
test_mm512_shuffle_epi8();
|
||||
}
|
||||
|
||||
// Some of the constants in the tests below are just bit patterns. They should not
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ LL | | false
|
|||
| | ^^^^^ expected `()`, found `bool`
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_________- expected this to be `()`
|
||||
| |_________- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:12:9
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ LL | / if true {
|
|||
LL | | Ok(S)
|
||||
| | ^^^^^ expected `()`, found `Result<S, _>`
|
||||
LL | | }
|
||||
| |_________- expected this to be `()`
|
||||
| |_________- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<S, _>`
|
||||
|
|
@ -21,7 +21,7 @@ LL | / if true {
|
|||
LL | | Ok(S)
|
||||
| | ^^^^^ expected `()`, found `Result<S, _>`
|
||||
LL | | }
|
||||
| |_________- expected this to be `()`
|
||||
| |_________- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<S, _>`
|
||||
|
|
|
|||
28
tests/ui/lint/const-item-interior-mutations-const-deref.rs
Normal file
28
tests/ui/lint/const-item-interior-mutations-const-deref.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Regression test for <https://github.com/rust-lang/rust/issues/150157>
|
||||
//
|
||||
// We shouldn't lint on user types, including through deref.
|
||||
|
||||
//@ check-pass
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::ops::Deref;
|
||||
|
||||
// Cut down version of the issue reproducer without the thread local to just a Deref
|
||||
pub struct LocalKey<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T> Deref for LocalKey<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
const LOCAL_COUNT: LocalKey<Cell<usize>> = LocalKey { inner: Cell::new(8) };
|
||||
|
||||
fn main() {
|
||||
let count = LOCAL_COUNT.get();
|
||||
LOCAL_COUNT.set(count);
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ LL | / if num == 3 {
|
|||
LL | | true
|
||||
| | ^^^^ expected `()`, found `bool`
|
||||
LL | | }
|
||||
| |_____- expected this to be `()`
|
||||
| |_____- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
help: you might have meant to return this value
|
||||
|
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ LL | | Err(1)
|
|||
| | ^^^^^^ expected `()`, found `Result<_, {integer}>`
|
||||
... |
|
||||
LL | | }
|
||||
| |_____________- expected this to be `()`
|
||||
| |_____________- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<_, {integer}>`
|
||||
|
|
@ -23,7 +23,7 @@ LL | | Err(1)
|
|||
| | ^^^^^^ expected `()`, found `Result<_, {integer}>`
|
||||
... |
|
||||
LL | | }
|
||||
| |_____________- expected this to be `()`
|
||||
| |_____________- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<_, {integer}>`
|
||||
|
|
@ -40,7 +40,7 @@ LL | | Err(1)
|
|||
| | ^^^^^^ expected `()`, found `Result<_, {integer}>`
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_____________- expected this to be `()`
|
||||
| |_____________- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<_, {integer}>`
|
||||
|
|
@ -53,7 +53,7 @@ LL | | Err(1)
|
|||
| | ^^^^^^ expected `()`, found `Result<_, {integer}>`
|
||||
LL | |
|
||||
LL | | }
|
||||
| |_____________- expected this to be `()`
|
||||
| |_____________- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<_, {integer}>`
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ LL | if x == E::V { field } {}
|
|||
| ---------------^^^^^--
|
||||
| | |
|
||||
| | expected `()`, found `bool`
|
||||
| expected this to be `()`
|
||||
| `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
help: you might have meant to return this value
|
||||
|
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ LL | | let value = unsafe { self.values.get_unchecked_mut(index) };
|
|||
LL | | value.get_or_insert_with(func)
|
||||
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `&mut V`
|
||||
LL | | }
|
||||
| |_________- expected this to be `()`
|
||||
| |_________- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found mutable reference `&mut V`
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ LL | | Err(42)
|
|||
| | ^^^^^^^ expected `()`, found `Result<_, {integer}>`
|
||||
... |
|
||||
LL | | }
|
||||
| |_____- expected this to be `()`
|
||||
| |_____- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<_, {integer}>`
|
||||
|
|
@ -23,7 +23,7 @@ LL | | 1i32
|
|||
| | ^^^^ expected `()`, found `i32`
|
||||
... |
|
||||
LL | | }
|
||||
| |_____- expected this to be `()`
|
||||
| |_____- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
help: you might have meant to return this value
|
||||
|
|
||||
|
|
@ -38,7 +38,7 @@ LL | | Err(42)
|
|||
| | ^^^^^^^ expected `()`, found `Result<_, {integer}>`
|
||||
... |
|
||||
LL | | }
|
||||
| |_____- expected this to be `()`
|
||||
| |_____- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<_, {integer}>`
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ LL | / if true {
|
|||
LL | | ""
|
||||
| | ^^ expected `()`, found `&str`
|
||||
LL | | }
|
||||
| |_____- expected this to be `()`
|
||||
| |_____- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ fn main() -> Result<(), ()> {
|
|||
// Here, we do want to suggest a semicolon:
|
||||
let x = Ok(42);
|
||||
if true {
|
||||
//~^ NOTE: expected this to be `()`
|
||||
//~^ NOTE: `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
x?
|
||||
//~^ ERROR: mismatched types [E0308]
|
||||
//~| NOTE: expected `()`, found integer
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ LL | | x?
|
|||
| | ^^ expected `()`, found integer
|
||||
... |
|
||||
LL | | }
|
||||
| |_____- expected this to be `()`
|
||||
| |_____- `if` expressions without `else` arms expect their inner expression to be `()`
|
||||
|
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue