Auto merge of #110239 - matthiaskrgr:rollup-o90hx4s, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #109959 (Fix transmute intrinsic mir validation ICE)
 - #110176 (Renumbering cleanups)
 - #110182 (Use `itertools::Either` instead of own impl)
 - #110188 (Remove orphaned remove_dir_all implementation from rust-installer)
 - #110190 (Custom MIR: Support `BinOp::Offset`)
 - #110209 (Add regression test for #59003)
 - #110210 (`DescriptionCtx` cleanups)
 - #110217 (doc: loongarch: Fix typos)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-04-12 17:45:33 +00:00
commit 59a05ad118
19 changed files with 240 additions and 1160 deletions

View File

@ -2769,13 +2769,11 @@ dependencies = [
"anyhow",
"clap 3.2.20",
"flate2",
"lazy_static",
"num_cpus",
"rayon",
"remove_dir_all",
"tar",
"walkdir",
"winapi",
"xz2",
]
@ -4576,6 +4574,7 @@ dependencies = [
"elsa",
"ena",
"indexmap",
"itertools",
"jobserver",
"libc",
"measureme",

View File

@ -20,31 +20,13 @@ pub fn renumber_mir<'tcx>(
) {
debug!(?body.arg_count);
let mut visitor = NllVisitor { infcx };
let mut renumberer = RegionRenumberer { infcx };
for body in promoted.iter_mut() {
visitor.visit_body(body);
renumberer.visit_body(body);
}
visitor.visit_body(body);
}
/// Replaces all regions appearing in `value` with fresh inference
/// variables.
#[instrument(skip(infcx, get_ctxt_fn), level = "debug")]
pub(crate) fn renumber_regions<'tcx, T, F>(
infcx: &BorrowckInferCtxt<'_, 'tcx>,
value: T,
get_ctxt_fn: F,
) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
F: Fn() -> RegionCtxt,
{
infcx.tcx.fold_regions(value, |_region, _depth| {
let origin = NllRegionVariableOrigin::Existential { from_forall: false };
infcx.next_nll_region_var(origin, || get_ctxt_fn())
})
renumberer.visit_body(body);
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
@ -82,21 +64,26 @@ impl RegionCtxt {
}
}
struct NllVisitor<'a, 'tcx> {
struct RegionRenumberer<'a, 'tcx> {
infcx: &'a BorrowckInferCtxt<'a, 'tcx>,
}
impl<'a, 'tcx> NllVisitor<'a, 'tcx> {
impl<'a, 'tcx> RegionRenumberer<'a, 'tcx> {
/// Replaces all regions appearing in `value` with fresh inference
/// variables.
fn renumber_regions<T, F>(&mut self, value: T, region_ctxt_fn: F) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
F: Fn() -> RegionCtxt,
{
renumber_regions(self.infcx, value, region_ctxt_fn)
let origin = NllRegionVariableOrigin::Existential { from_forall: false };
self.infcx.tcx.fold_regions(value, |_region, _depth| {
self.infcx.next_nll_region_var(origin, || region_ctxt_fn())
})
}
}
impl<'a, 'tcx> MutVisitor<'tcx> for NllVisitor<'a, 'tcx> {
impl<'a, 'tcx> MutVisitor<'tcx> for RegionRenumberer<'a, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
@ -124,9 +111,9 @@ impl<'a, 'tcx> MutVisitor<'tcx> for NllVisitor<'a, 'tcx> {
}
#[instrument(skip(self), level = "debug")]
fn visit_constant(&mut self, constant: &mut Constant<'tcx>, _location: Location) {
fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
let literal = constant.literal;
constant.literal = self.renumber_regions(literal, || RegionCtxt::Location(_location));
constant.literal = self.renumber_regions(literal, || RegionCtxt::Location(location));
debug!("constant: {:#?}", constant);
}
}

View File

@ -679,13 +679,21 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// Unlike `mem::transmute`, a MIR `Transmute` is well-formed
// for any two `Sized` types, just potentially UB to run.
if !op_ty.is_sized(self.tcx, self.param_env) {
if !self
.tcx
.normalize_erasing_regions(self.param_env, op_ty)
.is_sized(self.tcx, self.param_env)
{
self.fail(
location,
format!("Cannot transmute from non-`Sized` type {op_ty:?}"),
);
}
if !target_type.is_sized(self.tcx, self.param_env) {
if !self
.tcx
.normalize_erasing_regions(self.param_env, *target_type)
.is_sized(self.tcx, self.param_env)
{
self.fail(
location,
format!("Cannot transmute to non-`Sized` type {target_type:?}"),

View File

@ -33,6 +33,7 @@ tempfile = "3.2"
thin-vec = "0.2.12"
tracing = "0.1"
elsa = "=1.7.1"
itertools = "0.10.1"
[dependencies.parking_lot]
version = "0.11"

View File

@ -1,73 +0,0 @@
use std::fmt;
use std::iter::FusedIterator;
/// Iterator which may contain instance of
/// one of two specific implementations.
///
/// Note: For most methods providing custom
/// implementation may marginally
/// improve performance by avoiding
/// doing Left/Right match on every step
/// and doing it only once instead.
#[derive(Clone)]
pub enum EitherIter<L, R> {
Left(L),
Right(R),
}
impl<L, R> Iterator for EitherIter<L, R>
where
L: Iterator,
R: Iterator<Item = L::Item>,
{
type Item = L::Item;
fn next(&mut self) -> Option<Self::Item> {
match self {
EitherIter::Left(l) => l.next(),
EitherIter::Right(r) => r.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
EitherIter::Left(l) => l.size_hint(),
EitherIter::Right(r) => r.size_hint(),
}
}
}
impl<L, R> ExactSizeIterator for EitherIter<L, R>
where
L: ExactSizeIterator,
R: ExactSizeIterator,
EitherIter<L, R>: Iterator,
{
fn len(&self) -> usize {
match self {
EitherIter::Left(l) => l.len(),
EitherIter::Right(r) => r.len(),
}
}
}
impl<L, R> FusedIterator for EitherIter<L, R>
where
L: FusedIterator,
R: FusedIterator,
EitherIter<L, R>: Iterator,
{
}
impl<L, R> fmt::Debug for EitherIter<L, R>
where
L: fmt::Debug,
R: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EitherIter::Left(l) => l.fmt(f),
EitherIter::Right(r) => r.fmt(f),
}
}
}

View File

@ -1,24 +1,24 @@
use super::either_iter::EitherIter;
use crate::fx::FxHashMap;
use arrayvec::ArrayVec;
use itertools::Either;
use std::fmt;
use std::hash::Hash;
use std::ops::Index;
// For pointer-sized arguments arrays
// are faster than set/map for up to 64
// arguments.
//
// On the other hand such a big array
// hurts cache performance, makes passing
// sso structures around very expensive.
//
// Biggest performance benefit is gained
// for reasonably small arrays that stay
// small in vast majority of cases.
//
// '8' is chosen as a sane default, to be
// reevaluated later.
/// For pointer-sized arguments arrays
/// are faster than set/map for up to 64
/// arguments.
///
/// On the other hand such a big array
/// hurts cache performance, makes passing
/// sso structures around very expensive.
///
/// Biggest performance benefit is gained
/// for reasonably small arrays that stay
/// small in vast majority of cases.
///
/// '8' is chosen as a sane default, to be
/// reevaluated later.
const SSO_ARRAY_SIZE: usize = 8;
/// Small-storage-optimized implementation of a map.
@ -138,8 +138,8 @@ impl<K, V> SsoHashMap<K, V> {
/// The iterator element type is `&'a K`.
pub fn keys(&self) -> impl Iterator<Item = &'_ K> {
match self {
SsoHashMap::Array(array) => EitherIter::Left(array.iter().map(|(k, _v)| k)),
SsoHashMap::Map(map) => EitherIter::Right(map.keys()),
SsoHashMap::Array(array) => Either::Left(array.iter().map(|(k, _v)| k)),
SsoHashMap::Map(map) => Either::Right(map.keys()),
}
}
@ -147,8 +147,8 @@ impl<K, V> SsoHashMap<K, V> {
/// The iterator element type is `&'a V`.
pub fn values(&self) -> impl Iterator<Item = &'_ V> {
match self {
SsoHashMap::Array(array) => EitherIter::Left(array.iter().map(|(_k, v)| v)),
SsoHashMap::Map(map) => EitherIter::Right(map.values()),
SsoHashMap::Array(array) => Either::Left(array.iter().map(|(_k, v)| v)),
SsoHashMap::Map(map) => Either::Right(map.values()),
}
}
@ -156,8 +156,8 @@ impl<K, V> SsoHashMap<K, V> {
/// The iterator element type is `&'a mut V`.
pub fn values_mut(&mut self) -> impl Iterator<Item = &'_ mut V> {
match self {
SsoHashMap::Array(array) => EitherIter::Left(array.iter_mut().map(|(_k, v)| v)),
SsoHashMap::Map(map) => EitherIter::Right(map.values_mut()),
SsoHashMap::Array(array) => Either::Left(array.iter_mut().map(|(_k, v)| v)),
SsoHashMap::Map(map) => Either::Right(map.values_mut()),
}
}
@ -165,8 +165,8 @@ impl<K, V> SsoHashMap<K, V> {
/// allocated memory for reuse.
pub fn drain(&mut self) -> impl Iterator<Item = (K, V)> + '_ {
match self {
SsoHashMap::Array(array) => EitherIter::Left(array.drain(..)),
SsoHashMap::Map(map) => EitherIter::Right(map.drain()),
SsoHashMap::Array(array) => Either::Left(array.drain(..)),
SsoHashMap::Map(map) => Either::Right(map.drain()),
}
}
}
@ -406,16 +406,16 @@ where
}
impl<K, V> IntoIterator for SsoHashMap<K, V> {
type IntoIter = EitherIter<
<ArrayVec<(K, V), 8> as IntoIterator>::IntoIter,
type IntoIter = Either<
<ArrayVec<(K, V), SSO_ARRAY_SIZE> as IntoIterator>::IntoIter,
<FxHashMap<K, V> as IntoIterator>::IntoIter,
>;
type Item = <Self::IntoIter as Iterator>::Item;
fn into_iter(self) -> Self::IntoIter {
match self {
SsoHashMap::Array(array) => EitherIter::Left(array.into_iter()),
SsoHashMap::Map(map) => EitherIter::Right(map.into_iter()),
SsoHashMap::Array(array) => Either::Left(array.into_iter()),
SsoHashMap::Map(map) => Either::Right(map.into_iter()),
}
}
}
@ -435,9 +435,9 @@ fn adapt_array_mut_it<K, V>(pair: &mut (K, V)) -> (&K, &mut V) {
}
impl<'a, K, V> IntoIterator for &'a SsoHashMap<K, V> {
type IntoIter = EitherIter<
type IntoIter = Either<
std::iter::Map<
<&'a ArrayVec<(K, V), 8> as IntoIterator>::IntoIter,
<&'a ArrayVec<(K, V), SSO_ARRAY_SIZE> as IntoIterator>::IntoIter,
fn(&'a (K, V)) -> (&'a K, &'a V),
>,
<&'a FxHashMap<K, V> as IntoIterator>::IntoIter,
@ -446,16 +446,16 @@ impl<'a, K, V> IntoIterator for &'a SsoHashMap<K, V> {
fn into_iter(self) -> Self::IntoIter {
match self {
SsoHashMap::Array(array) => EitherIter::Left(array.into_iter().map(adapt_array_ref_it)),
SsoHashMap::Map(map) => EitherIter::Right(map.iter()),
SsoHashMap::Array(array) => Either::Left(array.into_iter().map(adapt_array_ref_it)),
SsoHashMap::Map(map) => Either::Right(map.iter()),
}
}
}
impl<'a, K, V> IntoIterator for &'a mut SsoHashMap<K, V> {
type IntoIter = EitherIter<
type IntoIter = Either<
std::iter::Map<
<&'a mut ArrayVec<(K, V), 8> as IntoIterator>::IntoIter,
<&'a mut ArrayVec<(K, V), SSO_ARRAY_SIZE> as IntoIterator>::IntoIter,
fn(&'a mut (K, V)) -> (&'a K, &'a mut V),
>,
<&'a mut FxHashMap<K, V> as IntoIterator>::IntoIter,
@ -464,8 +464,8 @@ impl<'a, K, V> IntoIterator for &'a mut SsoHashMap<K, V> {
fn into_iter(self) -> Self::IntoIter {
match self {
SsoHashMap::Array(array) => EitherIter::Left(array.into_iter().map(adapt_array_mut_it)),
SsoHashMap::Map(map) => EitherIter::Right(map.iter_mut()),
SsoHashMap::Array(array) => Either::Left(array.into_iter().map(adapt_array_mut_it)),
SsoHashMap::Map(map) => Either::Right(map.iter_mut()),
}
}
}

View File

@ -1,4 +1,3 @@
mod either_iter;
mod map;
mod set;

View File

@ -163,7 +163,6 @@ infer_region_explanation = {$pref_kind ->
[as_defined] the lifetime `{$desc_arg}` as defined here
[as_defined_anon] the anonymous lifetime as defined here
[defined_here] the anonymous lifetime defined here
[anon_num_here] the anonymous lifetime #{$desc_num_arg} defined here
[defined_here_reg] the lifetime `{$desc_arg}` as defined here
}{$suff_kind ->
*[should_not_happen] [{$suff_kind}]

View File

@ -4,12 +4,10 @@ use rustc_errors::{self, AddToDiagnostic, Diagnostic, IntoDiagnosticArg, Subdiag
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{symbol::kw, Span};
#[derive(Default)]
struct DescriptionCtx<'a> {
span: Option<Span>,
kind: &'a str,
arg: String,
num_arg: u32,
}
impl<'a> DescriptionCtx<'a> {
@ -18,19 +16,63 @@ impl<'a> DescriptionCtx<'a> {
region: ty::Region<'tcx>,
alt_span: Option<Span>,
) -> Option<Self> {
let mut me = DescriptionCtx::default();
me.span = alt_span;
match *region {
ty::ReEarlyBound(_) | ty::ReFree(_) => {
return Self::from_early_bound_and_free_regions(tcx, region);
let (span, kind, arg) = match *region {
ty::ReEarlyBound(ref br) => {
let scope = region.free_region_binding_scope(tcx).expect_local();
let span = if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
{
param.span
} else {
tcx.def_span(scope)
};
if br.has_name() {
(Some(span), "as_defined", br.name.to_string())
} else {
(Some(span), "as_defined_anon", String::new())
}
}
ty::ReStatic => {
me.kind = "restatic";
ty::ReFree(ref fr) => {
if !fr.bound_region.is_named()
&& let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region)
{
(Some(ty.span), "defined_here", String::new())
} else {
let scope = region.free_region_binding_scope(tcx).expect_local();
match fr.bound_region {
ty::BoundRegionKind::BrNamed(_, name) => {
let span = if let Some(param) = tcx
.hir()
.get_generics(scope)
.and_then(|generics| generics.get_named(name))
{
param.span
} else {
tcx.def_span(scope)
};
if name == kw::UnderscoreLifetime {
(Some(span), "as_defined_anon", String::new())
} else {
(Some(span), "as_defined", name.to_string())
}
}
ty::BrAnon(span) => {
let span = match span {
Some(_) => span,
None => Some(tcx.def_span(scope)),
};
(span, "defined_here", String::new())
}
_ => {
(Some(tcx.def_span(scope)), "defined_here_reg", region.to_string())
}
}
}
}
ty::RePlaceholder(_) => return None,
ty::ReStatic => (alt_span, "restatic", String::new()),
ty::ReError(_) => return None,
ty::RePlaceholder(_) | ty::ReError(_) => return None,
// FIXME(#13998) RePlaceholder should probably print like
// ReFree rather than dumping Debug output on the user.
@ -38,82 +80,10 @@ impl<'a> DescriptionCtx<'a> {
// We shouldn't really be having unification failures with ReVar
// and ReLateBound though.
ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
me.kind = "revar";
me.arg = format!("{:?}", region);
(alt_span, "revar", format!("{:?}", region))
}
};
Some(me)
}
fn from_early_bound_and_free_regions<'tcx>(
tcx: TyCtxt<'tcx>,
region: ty::Region<'tcx>,
) -> Option<Self> {
let mut me = DescriptionCtx::default();
let scope = region.free_region_binding_scope(tcx).expect_local();
match *region {
ty::ReEarlyBound(ref br) => {
let mut sp = tcx.def_span(scope);
if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
{
sp = param.span;
}
if br.has_name() {
me.kind = "as_defined";
me.arg = br.name.to_string();
} else {
me.kind = "as_defined_anon";
};
me.span = Some(sp)
}
ty::ReFree(ref fr) => {
if !fr.bound_region.is_named()
&& let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region)
{
me.kind = "defined_here";
me.span = Some(ty.span);
} else {
match fr.bound_region {
ty::BoundRegionKind::BrNamed(_, name) => {
let mut sp = tcx.def_span(scope);
if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
{
sp = param.span;
}
if name == kw::UnderscoreLifetime {
me.kind = "as_defined_anon";
} else {
me.kind = "as_defined";
me.arg = name.to_string();
};
me.span = Some(sp);
}
ty::BrAnon(span) => {
me.kind = "defined_here";
me.span = match span {
Some(_) => span,
None => Some(tcx.def_span(scope)),
}
},
_ => {
me.kind = "defined_here_reg";
me.arg = region.to_string();
me.span = Some(tcx.def_span(scope));
},
}
}
}
_ => bug!(),
}
Some(me)
}
fn add_to(self, diag: &mut rustc_errors::Diagnostic) {
diag.set_arg("desc_kind", self.kind);
diag.set_arg("desc_arg", self.arg);
diag.set_arg("desc_num_arg", self.num_arg);
Some(DescriptionCtx { span, kind, arg })
}
}
@ -198,10 +168,11 @@ impl AddToDiagnostic for RegionExplanation<'_> {
{
diag.set_arg("pref_kind", self.prefix);
diag.set_arg("suff_kind", self.suffix);
let desc_span = self.desc.span;
self.desc.add_to(diag);
diag.set_arg("desc_kind", self.desc.kind);
diag.set_arg("desc_arg", self.desc.arg);
let msg = f(diag, fluent::infer_region_explanation.into());
if let Some(span) = desc_span {
if let Some(span) = self.desc.span {
diag.span_note(span, msg);
} else {
diag.note(msg);

View File

@ -185,9 +185,58 @@ fn msg_span_from_named_region<'tcx>(
alt_span: Option<Span>,
) -> (String, Option<Span>) {
match *region {
ty::ReEarlyBound(_) | ty::ReFree(_) => {
let (msg, span) = msg_span_from_early_bound_and_free_regions(tcx, region);
(msg, Some(span))
ty::ReEarlyBound(ref br) => {
let scope = region.free_region_binding_scope(tcx).expect_local();
let span = if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
{
param.span
} else {
tcx.def_span(scope)
};
let text = if br.has_name() {
format!("the lifetime `{}` as defined here", br.name)
} else {
"the anonymous lifetime as defined here".to_string()
};
(text, Some(span))
}
ty::ReFree(ref fr) => {
if !fr.bound_region.is_named()
&& let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region)
{
("the anonymous lifetime defined here".to_string(), Some(ty.span))
} else {
let scope = region.free_region_binding_scope(tcx).expect_local();
match fr.bound_region {
ty::BoundRegionKind::BrNamed(_, name) => {
let span = if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
{
param.span
} else {
tcx.def_span(scope)
};
let text = if name == kw::UnderscoreLifetime {
"the anonymous lifetime as defined here".to_string()
} else {
format!("the lifetime `{}` as defined here", name)
};
(text, Some(span))
}
ty::BrAnon(span) => (
"the anonymous lifetime as defined here".to_string(),
Some(match span {
Some(span) => span,
None => tcx.def_span(scope)
})
),
_ => (
format!("the lifetime `{}` as defined here", region),
Some(tcx.def_span(scope)),
),
}
}
}
ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
ty::RePlaceholder(ty::PlaceholderRegion {
@ -206,65 +255,6 @@ fn msg_span_from_named_region<'tcx>(
}
}
fn msg_span_from_early_bound_and_free_regions<'tcx>(
tcx: TyCtxt<'tcx>,
region: ty::Region<'tcx>,
) -> (String, Span) {
let scope = region.free_region_binding_scope(tcx).expect_local();
match *region {
ty::ReEarlyBound(ref br) => {
let mut sp = tcx.def_span(scope);
if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
{
sp = param.span;
}
let text = if br.has_name() {
format!("the lifetime `{}` as defined here", br.name)
} else {
"the anonymous lifetime as defined here".to_string()
};
(text, sp)
}
ty::ReFree(ref fr) => {
if !fr.bound_region.is_named()
&& let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region)
{
("the anonymous lifetime defined here".to_string(), ty.span)
} else {
match fr.bound_region {
ty::BoundRegionKind::BrNamed(_, name) => {
let mut sp = tcx.def_span(scope);
if let Some(param) =
tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name))
{
sp = param.span;
}
let text = if name == kw::UnderscoreLifetime {
"the anonymous lifetime as defined here".to_string()
} else {
format!("the lifetime `{}` as defined here", name)
};
(text, sp)
}
ty::BrAnon(span) => (
"the anonymous lifetime as defined here".to_string(),
match span {
Some(span) => span,
None => tcx.def_span(scope)
}
),
_ => (
format!("the lifetime `{}` as defined here", region),
tcx.def_span(scope),
),
}
}
}
_ => bug!(),
}
}
fn emit_msg_span(
err: &mut Diagnostic,
prefix: &str,

View File

@ -148,6 +148,11 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
)),
)
},
@call("mir_offset", args) => {
let ptr = self.parse_operand(args[0])?;
let offset = self.parse_operand(args[1])?;
Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset))))
},
@call("mir_len", args) => Ok(Rvalue::Len(self.parse_place(args[0])?)),
ExprKind::Borrow { borrow_kind, arg } => Ok(
Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?)

View File

@ -232,6 +232,7 @@
//! - `&`, `&mut`, `addr_of!`, and `addr_of_mut!` all work to create their associated rvalue.
//! - [`Discriminant`] and [`Len`] have associated functions.
//! - Unary and binary operations use their normal Rust syntax - `a * b`, `!c`, etc.
//! - The binary operation `Offset` can be created via [`Offset`].
//! - Checked binary operations are represented by wrapping the associated binop in [`Checked`].
//! - Array repetition syntax (`[foo; 10]`) creates the associated rvalue.
//!
@ -289,6 +290,7 @@ define!(
fn Discriminant<T>(place: T) -> <T as ::core::marker::DiscriminantKind>::Discriminant
);
define!("mir_set_discriminant", fn SetDiscriminant<T>(place: T, index: u32));
define!("mir_offset", fn Offset<T, U>(ptr: T, count: U) -> T);
define!(
"mir_field",
/// Access the field with the given index of some place.

View File

@ -6,12 +6,12 @@
[LoongArch]: https://loongson.github.io/LoongArch-Documentation/README-EN.html
The target name follow this format: `<machine>-<vendor>-<os><fabi_suffix>, where `<machine>` specifies the CPU family/model, `<vendor>` specifies the vendor and `<os>` the operating system name.
The target name follow this format: `<machine>-<vendor>-<os><fabi_suffix>`, where `<machine>` specifies the CPU family/model, `<vendor>` specifies the vendor and `<os>` the operating system name.
While the integer base ABI is implied by themachinefield, the floating point base ABI type is encoded into the os field of the specifier using the string suffix `<fabi-suffix>`.
| `<fabi-suffix>` | `Description` |
|------------------------|--------------------------------------------------------------------|
| f64 | The base ABI use 64-bits FPRs for parameter passing.(lp64d)|
| f64 | The base ABI use 64-bits FPRs for parameter passing. (lp64d)|
| f32 | The base ABI uses 32-bit FPRs for parameter passing. (lp64f)|
| sf | The base ABI uses no FPR for parameter passing. (lp64s) |
@ -26,9 +26,9 @@ While the integer base ABI is implied by themachinefield, the floating po
## Target maintainers
- [ZHAI xiaojuan](https://github.com/zhaixiaojuan) `zhaixiaojuan@loongson.cn`
- [WANG rui](https://github.com/heiher) `wangrui@loongson.cn`
- [ZHAI xiang](https://github.com/xiangzhai) `zhaixiang@loongson.cn`
- [ZHAI Xiaojuan](https://github.com/zhaixiaojuan) `zhaixiaojuan@loongson.cn`
- [WANG Rui](https://github.com/heiher) `wangrui@loongson.cn`
- [ZHAI Xiang](https://github.com/xiangzhai) `zhaixiang@loongson.cn`
- [WANG Xuerui](https://github.com/xen0n) `git@xen0n.name`
## Requirements

View File

@ -22,7 +22,3 @@ remove_dir_all = "0.5"
[dependencies.clap]
features = ["derive"]
version = "3.1"
[target."cfg(windows)".dependencies]
lazy_static = "1"
winapi = { version = "0.3", features = ["errhandlingapi", "handleapi", "ioapiset", "winerror", "winioctl", "winnt"] }

View File

@ -1,860 +0,0 @@
#![allow(non_snake_case)]
use std::io;
use std::path::Path;
#[cfg(not(windows))]
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
::std::fs::remove_dir_all(path)
}
#[cfg(windows)]
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
win::remove_dir_all(path)
}
#[cfg(windows)]
mod win {
use winapi::ctypes::{c_uint, c_ushort};
use winapi::shared::minwindef::{BOOL, DWORD, FALSE, FILETIME, LPVOID};
use winapi::shared::winerror::{
ERROR_CALL_NOT_IMPLEMENTED, ERROR_INSUFFICIENT_BUFFER, ERROR_NO_MORE_FILES,
};
use winapi::um::errhandlingapi::{GetLastError, SetLastError};
use winapi::um::fileapi::{
CreateFileW, FindFirstFileW, FindNextFileW, GetFileInformationByHandle,
};
use winapi::um::fileapi::{BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW};
use winapi::um::fileapi::{FILE_BASIC_INFO, FILE_RENAME_INFO, TRUNCATE_EXISTING};
use winapi::um::fileapi::{OPEN_ALWAYS, OPEN_EXISTING};
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::um::ioapiset::DeviceIoControl;
use winapi::um::libloaderapi::{GetModuleHandleW, GetProcAddress};
use winapi::um::minwinbase::{
FileBasicInfo, FileRenameInfo, FILE_INFO_BY_HANDLE_CLASS, WIN32_FIND_DATAW,
};
use winapi::um::winbase::SECURITY_SQOS_PRESENT;
use winapi::um::winbase::{
FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_DELETE_ON_CLOSE, FILE_FLAG_OPEN_REPARSE_POINT,
};
use winapi::um::winioctl::FSCTL_GET_REPARSE_POINT;
use winapi::um::winnt::{DELETE, FILE_ATTRIBUTE_DIRECTORY, HANDLE, LPCWSTR};
use winapi::um::winnt::{FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_REPARSE_POINT};
use winapi::um::winnt::{FILE_GENERIC_WRITE, FILE_WRITE_DATA, GENERIC_READ, GENERIC_WRITE};
use winapi::um::winnt::{FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES};
use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE};
use winapi::um::winnt::{IO_REPARSE_TAG_MOUNT_POINT, IO_REPARSE_TAG_SYMLINK, LARGE_INTEGER};
use std::ffi::{OsStr, OsString};
use std::io;
use std::mem;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::path::{Path, PathBuf};
use std::ptr;
use std::sync::Arc;
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
// On Windows it is not enough to just recursively remove the contents of a
// directory and then the directory itself. Deleting does not happen
// instantaneously, but is scheduled.
// To work around this, we move the file or directory to some `base_dir`
// right before deletion to avoid races.
//
// As `base_dir` we choose the parent dir of the directory we want to
// remove. We very probably have permission to create files here, as we
// already need write permission in this dir to delete the directory. And it
// should be on the same volume.
//
// To handle files with names like `CON` and `morse .. .`, and when a
// directory structure is so deep it needs long path names the path is first
// converted to a `//?/`-path with `get_path()`.
//
// To make sure we don't leave a moved file laying around if the process
// crashes before we can delete the file, we do all operations on an file
// handle. By opening a file with `FILE_FLAG_DELETE_ON_CLOSE` Windows will
// always delete the file when the handle closes.
//
// All files are renamed to be in the `base_dir`, and have their name
// changed to "rm-<counter>". After every rename the counter is increased.
// Rename should not overwrite possibly existing files in the base dir. So
// if it fails with `AlreadyExists`, we just increase the counter and try
// again.
//
// For read-only files and directories we first have to remove the read-only
// attribute before we can move or delete them. This also removes the
// attribute from possible hardlinks to the file, so just before closing we
// restore the read-only attribute.
//
// If 'path' points to a directory symlink or junction we should not
// recursively remove the target of the link, but only the link itself.
//
// Moving and deleting is guaranteed to succeed if we are able to open the
// file with `DELETE` permission. If others have the file open we only have
// `DELETE` permission if they have specified `FILE_SHARE_DELETE`. We can
// also delete the file now, but it will not disappear until all others have
// closed the file. But no-one can open the file after we have flagged it
// for deletion.
// Open the path once to get the canonical path, file type and attributes.
let (path, metadata) = {
let mut opts = OpenOptions::new();
opts.access_mode(FILE_READ_ATTRIBUTES);
opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT);
let file = File::open(path, &opts)?;
(get_path(&file)?, file.file_attr()?)
};
let mut ctx = RmdirContext {
base_dir: match path.parent() {
Some(dir) => dir,
None => {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"can't delete root directory",
))
}
},
readonly: metadata.perm().readonly(),
counter: 0,
};
let filetype = metadata.file_type();
if filetype.is_dir() {
remove_dir_all_recursive(path.as_ref(), &mut ctx)
} else if filetype.is_symlink_dir() {
remove_item(path.as_ref(), &mut ctx)
} else {
Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Not a directory",
))
}
}
fn readdir(p: &Path) -> io::Result<ReadDir> {
let root = p.to_path_buf();
let star = p.join("*");
let path = to_u16s(&star)?;
unsafe {
let mut wfd = mem::zeroed();
let find_handle = FindFirstFileW(path.as_ptr(), &mut wfd);
if find_handle != INVALID_HANDLE_VALUE {
Ok(ReadDir {
handle: FindNextFileHandle(find_handle),
root: Arc::new(root),
first: Some(wfd),
})
} else {
Err(io::Error::last_os_error())
}
}
}
struct RmdirContext<'a> {
base_dir: &'a Path,
readonly: bool,
counter: u64,
}
fn remove_dir_all_recursive(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> {
let dir_readonly = ctx.readonly;
for child in readdir(path)? {
let child = child?;
let child_type = child.file_type()?;
ctx.readonly = child.metadata()?.perm().readonly();
if child_type.is_dir() {
remove_dir_all_recursive(&child.path(), ctx)?;
} else {
remove_item(&child.path().as_ref(), ctx)?;
}
}
ctx.readonly = dir_readonly;
remove_item(path, ctx)
}
fn remove_item(path: &Path, ctx: &mut RmdirContext) -> io::Result<()> {
if !ctx.readonly {
let mut opts = OpenOptions::new();
opts.access_mode(DELETE);
opts.custom_flags(
FILE_FLAG_BACKUP_SEMANTICS | // delete directory
FILE_FLAG_OPEN_REPARSE_POINT | // delete symlink
FILE_FLAG_DELETE_ON_CLOSE,
);
let file = File::open(path, &opts)?;
move_item(&file, ctx)
} else {
// remove read-only permision
set_perm(&path, FilePermissions::new())?;
// move and delete file, similar to !readonly.
// only the access mode is different.
let mut opts = OpenOptions::new();
opts.access_mode(DELETE | FILE_WRITE_ATTRIBUTES);
opts.custom_flags(
FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OPEN_REPARSE_POINT
| FILE_FLAG_DELETE_ON_CLOSE,
);
let file = File::open(path, &opts)?;
move_item(&file, ctx)?;
// restore read-only flag just in case there are other hard links
let mut perm = FilePermissions::new();
perm.set_readonly(true);
let _ = file.set_perm(perm); // ignore if this fails
Ok(())
}
}
macro_rules! compat_fn {
($module:ident: $(
fn $symbol:ident($($argname:ident: $argtype:ty),*)
-> $rettype:ty {
$($body:expr);*
}
)*) => ($(
#[allow(unused_variables)]
unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
use std::sync::atomic::{AtomicUsize, Ordering};
use std::mem;
use std::ffi::CString;
type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
lazy_static! { static ref PTR: AtomicUsize = AtomicUsize::new(0);}
fn lookup(module: &str, symbol: &str) -> Option<usize> {
let mut module: Vec<u16> = module.encode_utf16().collect();
module.push(0);
let symbol = CString::new(symbol).unwrap();
unsafe {
let handle = GetModuleHandleW(module.as_ptr());
match GetProcAddress(handle, symbol.as_ptr()) as usize {
0 => None,
n => Some(n),
}
}
}
fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str,
fallback: usize) -> usize {
let value = lookup(module, symbol).unwrap_or(fallback);
ptr.store(value, Ordering::SeqCst);
value
}
fn load() -> usize {
store_func(&PTR, stringify!($module), stringify!($symbol), fallback as usize)
}
unsafe extern "system" fn fallback($($argname: $argtype),*)
-> $rettype {
$($body);*
}
let addr = match PTR.load(Ordering::SeqCst) {
0 => load(),
n => n,
};
mem::transmute::<usize, F>(addr)($($argname),*)
}
)*)
}
compat_fn! {
kernel32:
fn GetFinalPathNameByHandleW(_hFile: HANDLE,
_lpszFilePath: LPCWSTR,
_cchFilePath: DWORD,
_dwFlags: DWORD) -> DWORD {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
}
fn SetFileInformationByHandle(_hFile: HANDLE,
_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
_lpFileInformation: LPVOID,
_dwBufferSize: DWORD) -> BOOL {
SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); 0
}
}
fn cvt(i: i32) -> io::Result<i32> {
if i == 0 {
Err(io::Error::last_os_error())
} else {
Ok(i)
}
}
fn to_u16s<S: AsRef<OsStr>>(s: S) -> io::Result<Vec<u16>> {
fn inner(s: &OsStr) -> io::Result<Vec<u16>> {
let mut maybe_result: Vec<u16> = s.encode_wide().collect();
if maybe_result.iter().any(|&u| u == 0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"strings passed to WinAPI cannot contain NULs",
));
}
maybe_result.push(0);
Ok(maybe_result)
}
inner(s.as_ref())
}
fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] {
match v.iter().position(|c| *c == 0) {
// don't include the 0
Some(i) => &v[..i],
None => v,
}
}
fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T>
where
F1: FnMut(*mut u16, DWORD) -> DWORD,
F2: FnOnce(&[u16]) -> T,
{
// Start off with a stack buf but then spill over to the heap if we end up
// needing more space.
let mut stack_buf = [0u16; 512];
let mut heap_buf = Vec::new();
unsafe {
let mut n = stack_buf.len();
loop {
let buf = if n <= stack_buf.len() {
&mut stack_buf[..]
} else {
let extra = n - heap_buf.len();
heap_buf.reserve(extra);
heap_buf.set_len(n);
&mut heap_buf[..]
};
// This function is typically called on windows API functions which
// will return the correct length of the string, but these functions
// also return the `0` on error. In some cases, however, the
// returned "correct length" may actually be 0!
//
// To handle this case we call `SetLastError` to reset it to 0 and
// then check it again if we get the "0 error value". If the "last
// error" is still 0 then we interpret it as a 0 length buffer and
// not an actual error.
SetLastError(0);
let k = match f1(buf.as_mut_ptr(), n as DWORD) {
0 if GetLastError() == 0 => 0,
0 => return Err(io::Error::last_os_error()),
n => n,
} as usize;
if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER {
n *= 2;
} else if k >= n {
n = k;
} else {
return Ok(f2(&buf[..k]));
}
}
}
}
#[derive(Clone, PartialEq, Eq, Debug, Default)]
struct FilePermissions {
readonly: bool,
}
impl FilePermissions {
fn new() -> FilePermissions {
Default::default()
}
fn readonly(&self) -> bool {
self.readonly
}
fn set_readonly(&mut self, readonly: bool) {
self.readonly = readonly
}
}
#[derive(Clone)]
struct OpenOptions {
// generic
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
create_new: bool,
// system-specific
custom_flags: u32,
access_mode: Option<DWORD>,
attributes: DWORD,
share_mode: DWORD,
security_qos_flags: DWORD,
security_attributes: usize, // FIXME: should be a reference
}
impl OpenOptions {
fn new() -> OpenOptions {
OpenOptions {
// generic
read: false,
write: false,
append: false,
truncate: false,
create: false,
create_new: false,
// system-specific
custom_flags: 0,
access_mode: None,
share_mode: FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
attributes: 0,
security_qos_flags: 0,
security_attributes: 0,
}
}
fn custom_flags(&mut self, flags: u32) {
self.custom_flags = flags;
}
fn access_mode(&mut self, access_mode: u32) {
self.access_mode = Some(access_mode);
}
fn get_access_mode(&self) -> io::Result<DWORD> {
const ERROR_INVALID_PARAMETER: i32 = 87;
match (self.read, self.write, self.append, self.access_mode) {
(_, _, _, Some(mode)) => Ok(mode),
(true, false, false, None) => Ok(GENERIC_READ),
(false, true, false, None) => Ok(GENERIC_WRITE),
(true, true, false, None) => Ok(GENERIC_READ | GENERIC_WRITE),
(false, _, true, None) => Ok(FILE_GENERIC_WRITE & !FILE_WRITE_DATA),
(true, _, true, None) => Ok(GENERIC_READ | (FILE_GENERIC_WRITE & !FILE_WRITE_DATA)),
(false, false, false, None) => {
Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER))
}
}
}
fn get_creation_mode(&self) -> io::Result<DWORD> {
const ERROR_INVALID_PARAMETER: i32 = 87;
match (self.write, self.append) {
(true, false) => {}
(false, false) => {
if self.truncate || self.create || self.create_new {
return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
}
}
(_, true) => {
if self.truncate && !self.create_new {
return Err(io::Error::from_raw_os_error(ERROR_INVALID_PARAMETER));
}
}
}
Ok(match (self.create, self.truncate, self.create_new) {
(false, false, false) => OPEN_EXISTING,
(true, false, false) => OPEN_ALWAYS,
(false, true, false) => TRUNCATE_EXISTING,
(true, true, false) => CREATE_ALWAYS,
(_, _, true) => CREATE_NEW,
})
}
fn get_flags_and_attributes(&self) -> DWORD {
self.custom_flags
| self.attributes
| self.security_qos_flags
| if self.security_qos_flags != 0 {
SECURITY_SQOS_PRESENT
} else {
0
}
| if self.create_new {
FILE_FLAG_OPEN_REPARSE_POINT
} else {
0
}
}
}
struct File {
handle: Handle,
}
impl File {
fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
let path = to_u16s(path)?;
let handle = unsafe {
CreateFileW(
path.as_ptr(),
opts.get_access_mode()?,
opts.share_mode,
opts.security_attributes as *mut _,
opts.get_creation_mode()?,
opts.get_flags_and_attributes(),
ptr::null_mut(),
)
};
if handle == INVALID_HANDLE_VALUE {
Err(io::Error::last_os_error())
} else {
Ok(File {
handle: Handle::new(handle),
})
}
}
fn file_attr(&self) -> io::Result<FileAttr> {
unsafe {
let mut info: BY_HANDLE_FILE_INFORMATION = mem::zeroed();
cvt(GetFileInformationByHandle(self.handle.raw(), &mut info))?;
let mut attr = FileAttr {
attributes: info.dwFileAttributes,
creation_time: info.ftCreationTime,
last_access_time: info.ftLastAccessTime,
last_write_time: info.ftLastWriteTime,
file_size: ((info.nFileSizeHigh as u64) << 32) | (info.nFileSizeLow as u64),
reparse_tag: 0,
};
if attr.is_reparse_point() {
let mut b = [0; MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
if let Ok((_, buf)) = self.reparse_point(&mut b) {
attr.reparse_tag = buf.ReparseTag;
}
}
Ok(attr)
}
}
fn set_attributes(&self, attr: DWORD) -> io::Result<()> {
let zero: LARGE_INTEGER = unsafe { mem::zeroed() };
let mut info = FILE_BASIC_INFO {
CreationTime: zero, // do not change
LastAccessTime: zero, // do not change
LastWriteTime: zero, // do not change
ChangeTime: zero, // do not change
FileAttributes: attr,
};
let size = mem::size_of_val(&info);
cvt(unsafe {
SetFileInformationByHandle(
self.handle.raw(),
FileBasicInfo,
&mut info as *mut _ as *mut _,
size as DWORD,
)
})?;
Ok(())
}
fn rename(&self, new: &Path, replace: bool) -> io::Result<()> {
// &self must be opened with DELETE permission
use std::iter;
#[cfg(target_arch = "x86")]
const STRUCT_SIZE: usize = 12;
#[cfg(target_arch = "x86_64")]
const STRUCT_SIZE: usize = 20;
// FIXME: check for internal NULs in 'new'
let mut data: Vec<u16> = iter::repeat(0u16)
.take(STRUCT_SIZE / 2)
.chain(new.as_os_str().encode_wide())
.collect();
data.push(0);
let size = data.len() * 2;
unsafe {
// Thanks to alignment guarantees on Windows this works
// (8 for 32-bit and 16 for 64-bit)
let info = data.as_mut_ptr() as *mut FILE_RENAME_INFO;
// The type of ReplaceIfExists is BOOL, but it actually expects a
// BOOLEAN. This means true is -1, not c::TRUE.
(*info).ReplaceIfExists = if replace { -1 } else { FALSE };
(*info).RootDirectory = ptr::null_mut();
(*info).FileNameLength = (size - STRUCT_SIZE) as DWORD;
cvt(SetFileInformationByHandle(
self.handle().raw(),
FileRenameInfo,
data.as_mut_ptr() as *mut _ as *mut _,
size as DWORD,
))?;
Ok(())
}
}
fn set_perm(&self, perm: FilePermissions) -> io::Result<()> {
let attr = self.file_attr()?.attributes;
if perm.readonly == (attr & FILE_ATTRIBUTE_READONLY != 0) {
Ok(())
} else if perm.readonly {
self.set_attributes(attr | FILE_ATTRIBUTE_READONLY)
} else {
self.set_attributes(attr & !FILE_ATTRIBUTE_READONLY)
}
}
fn handle(&self) -> &Handle {
&self.handle
}
fn reparse_point<'a>(
&self,
space: &'a mut [u8; MAXIMUM_REPARSE_DATA_BUFFER_SIZE],
) -> io::Result<(DWORD, &'a REPARSE_DATA_BUFFER)> {
unsafe {
let mut bytes = 0;
cvt({
DeviceIoControl(
self.handle.raw(),
FSCTL_GET_REPARSE_POINT,
ptr::null_mut(),
0,
space.as_mut_ptr() as *mut _,
space.len() as DWORD,
&mut bytes,
ptr::null_mut(),
)
})?;
Ok((bytes, &*(space.as_ptr() as *const REPARSE_DATA_BUFFER)))
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
enum FileType {
Dir,
File,
SymlinkFile,
SymlinkDir,
ReparsePoint,
MountPoint,
}
impl FileType {
fn new(attrs: DWORD, reparse_tag: DWORD) -> FileType {
match (
attrs & FILE_ATTRIBUTE_DIRECTORY != 0,
attrs & FILE_ATTRIBUTE_REPARSE_POINT != 0,
reparse_tag,
) {
(false, false, _) => FileType::File,
(true, false, _) => FileType::Dir,
(false, true, IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkFile,
(true, true, IO_REPARSE_TAG_SYMLINK) => FileType::SymlinkDir,
(true, true, IO_REPARSE_TAG_MOUNT_POINT) => FileType::MountPoint,
(_, true, _) => FileType::ReparsePoint,
// Note: if a _file_ has a reparse tag of the type IO_REPARSE_TAG_MOUNT_POINT it is
// invalid, as junctions always have to be dirs. We set the filetype to ReparsePoint
// to indicate it is something symlink-like, but not something you can follow.
}
}
fn is_dir(&self) -> bool {
*self == FileType::Dir
}
fn is_symlink_dir(&self) -> bool {
*self == FileType::SymlinkDir || *self == FileType::MountPoint
}
}
impl DirEntry {
fn new(root: &Arc<PathBuf>, wfd: &WIN32_FIND_DATAW) -> Option<DirEntry> {
let first_bytes = &wfd.cFileName[0..3];
if first_bytes.starts_with(&[46, 0]) || first_bytes.starts_with(&[46, 46, 0]) {
None
} else {
Some(DirEntry {
root: root.clone(),
data: *wfd,
})
}
}
fn path(&self) -> PathBuf {
self.root.join(&self.file_name())
}
fn file_name(&self) -> OsString {
let filename = truncate_utf16_at_nul(&self.data.cFileName);
OsString::from_wide(filename)
}
fn file_type(&self) -> io::Result<FileType> {
Ok(FileType::new(
self.data.dwFileAttributes,
/* reparse_tag = */ self.data.dwReserved0,
))
}
fn metadata(&self) -> io::Result<FileAttr> {
Ok(FileAttr {
attributes: self.data.dwFileAttributes,
creation_time: self.data.ftCreationTime,
last_access_time: self.data.ftLastAccessTime,
last_write_time: self.data.ftLastWriteTime,
file_size: ((self.data.nFileSizeHigh as u64) << 32)
| (self.data.nFileSizeLow as u64),
reparse_tag: if self.data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
// reserved unless this is a reparse point
self.data.dwReserved0
} else {
0
},
})
}
}
struct DirEntry {
root: Arc<PathBuf>,
data: WIN32_FIND_DATAW,
}
struct ReadDir {
handle: FindNextFileHandle,
root: Arc<PathBuf>,
first: Option<WIN32_FIND_DATAW>,
}
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
if let Some(first) = self.first.take() {
if let Some(e) = DirEntry::new(&self.root, &first) {
return Some(Ok(e));
}
}
unsafe {
let mut wfd = mem::zeroed();
loop {
if FindNextFileW(self.handle.0, &mut wfd) == 0 {
if GetLastError() == ERROR_NO_MORE_FILES {
return None;
} else {
return Some(Err(io::Error::last_os_error()));
}
}
if let Some(e) = DirEntry::new(&self.root, &wfd) {
return Some(Ok(e));
}
}
}
}
}
#[derive(Clone)]
struct FileAttr {
attributes: DWORD,
creation_time: FILETIME,
last_access_time: FILETIME,
last_write_time: FILETIME,
file_size: u64,
reparse_tag: DWORD,
}
impl FileAttr {
fn perm(&self) -> FilePermissions {
FilePermissions {
readonly: self.attributes & FILE_ATTRIBUTE_READONLY != 0,
}
}
fn file_type(&self) -> FileType {
FileType::new(self.attributes, self.reparse_tag)
}
fn is_reparse_point(&self) -> bool {
self.attributes & FILE_ATTRIBUTE_REPARSE_POINT != 0
}
}
#[repr(C)]
struct REPARSE_DATA_BUFFER {
ReparseTag: c_uint,
ReparseDataLength: c_ushort,
Reserved: c_ushort,
rest: (),
}
const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: usize = 16 * 1024;
/// An owned container for `HANDLE` object, closing them on Drop.
///
/// All methods are inherited through a `Deref` impl to `RawHandle`
struct Handle(RawHandle);
use std::ops::Deref;
/// A wrapper type for `HANDLE` objects to give them proper Send/Sync inference
/// as well as Rust-y methods.
///
/// This does **not** drop the handle when it goes out of scope, use `Handle`
/// instead for that.
#[derive(Copy, Clone)]
struct RawHandle(HANDLE);
unsafe impl Send for RawHandle {}
unsafe impl Sync for RawHandle {}
impl Handle {
fn new(handle: HANDLE) -> Handle {
Handle(RawHandle::new(handle))
}
}
impl Deref for Handle {
type Target = RawHandle;
fn deref(&self) -> &RawHandle {
&self.0
}
}
impl Drop for Handle {
fn drop(&mut self) {
unsafe {
let _ = CloseHandle(self.raw());
}
}
}
impl RawHandle {
fn new(handle: HANDLE) -> RawHandle {
RawHandle(handle)
}
fn raw(&self) -> HANDLE {
self.0
}
}
struct FindNextFileHandle(HANDLE);
fn get_path(f: &File) -> io::Result<PathBuf> {
fill_utf16_buf(
|buf, sz| unsafe {
GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, VOLUME_NAME_DOS)
},
|buf| PathBuf::from(OsString::from_wide(buf)),
)
}
fn move_item(file: &File, ctx: &mut RmdirContext) -> io::Result<()> {
let mut tmpname = ctx.base_dir.join(format! {"rm-{}", ctx.counter});
ctx.counter += 1;
// Try to rename the file. If it already exists, just retry with an other
// filename.
while let Err(err) = file.rename(tmpname.as_ref(), false) {
if err.kind() != io::ErrorKind::AlreadyExists {
return Err(err);
};
tmpname = ctx.base_dir.join(format!("rm-{}", ctx.counter));
ctx.counter += 1;
}
Ok(())
}
fn set_perm(path: &Path, perm: FilePermissions) -> io::Result<()> {
let mut opts = OpenOptions::new();
opts.access_mode(FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
opts.custom_flags(FILE_FLAG_BACKUP_SEMANTICS);
let file = File::open(path, &opts)?;
file.set_perm(perm)
}
const VOLUME_NAME_DOS: DWORD = 0x0;
}

View File

@ -0,0 +1,10 @@
// MIR for `raw_pointer_offset` after built
fn raw_pointer_offset(_1: *const i32) -> *const i32 {
let mut _0: *const i32; // return place in scope 0 at $DIR/references.rs:+0:45: +0:55
bb0: {
_0 = Offset(_1, const 1_isize); // scope 0 at $DIR/references.rs:+2:9: +2:33
return; // scope 0 at $DIR/references.rs:+3:9: +3:17
}
}

View File

@ -45,11 +45,22 @@ pub fn raw_pointer(x: *const i32) -> *const i32 {
})
}
// EMIT_MIR references.raw_pointer_offset.built.after.mir
#[custom_mir(dialect = "built")]
pub fn raw_pointer_offset(x: *const i32) -> *const i32 {
mir!({
RET = Offset(x, 1_isize);
Return()
})
}
fn main() {
let mut x = 5;
let arr = [1, 2];
assert_eq!(*mut_ref(&mut x), 5);
assert_eq!(*immut_ref(&x), 5);
unsafe {
assert_eq!(*raw_pointer(addr_of!(x)), 5);
assert_eq!(*raw_pointer_offset(addr_of!(arr[0])), 2);
}
}

View File

@ -0,0 +1,18 @@
// check-pass
// Make sure we don't have any false positives about the "struct is never constructed" lint.
#![deny(dead_code)]
struct Foo {
#[allow(dead_code)]
inner: u32,
}
impl From<u32> for Foo {
fn from(inner: u32) -> Self {
Self { inner }
}
}
fn main() {}

View File

@ -0,0 +1,17 @@
// build-pass
// compile-flags: -Zvalidate-mir
// edition: 2021
#![crate_type = "lib"]
// Use `PhantomData` to get target-independent size
async fn get(_r: std::marker::PhantomData<&i32>) {
loop {}
}
pub fn check() {
let mut v = get(loop {});
let _ = || unsafe {
v = std::mem::transmute([0_u8; 1]);
};
}