mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-09 14:25:24 +00:00
Auto merge of #47998 - kennytm:rollup, r=kennytm
Rollup of 10 pull requests - Successful merges: #47862, #47877, #47896, #47912, #47947, #47958, #47978, #47996, #47999, #47892 - Failed merges:
This commit is contained in:
commit
e7e982ac03
@ -610,7 +610,6 @@ static HOST_COMPILETESTS: &[Test] = &[
|
||||
mode: "incremental",
|
||||
suite: "incremental-fulldeps",
|
||||
},
|
||||
Test { path: "src/test/run-make", mode: "run-make", suite: "run-make" },
|
||||
Test { path: "src/test/rustdoc", mode: "rustdoc", suite: "rustdoc" },
|
||||
|
||||
Test { path: "src/test/pretty", mode: "pretty", suite: "pretty" },
|
||||
@ -619,6 +618,7 @@ static HOST_COMPILETESTS: &[Test] = &[
|
||||
Test { path: "src/test/run-pass-valgrind/pretty", mode: "pretty", suite: "run-pass-valgrind" },
|
||||
Test { path: "src/test/run-pass-fulldeps/pretty", mode: "pretty", suite: "run-pass-fulldeps" },
|
||||
Test { path: "src/test/run-fail-fulldeps/pretty", mode: "pretty", suite: "run-fail-fulldeps" },
|
||||
Test { path: "src/test/run-make", mode: "run-make", suite: "run-make" },
|
||||
];
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -1,23 +0,0 @@
|
||||
# `match_beginning_vert`
|
||||
|
||||
The tracking issue for this feature is [#44101].
|
||||
|
||||
With this feature enabled, you are allowed to add a '|' to the beginning of a
|
||||
match arm:
|
||||
|
||||
```rust
|
||||
#![feature(match_beginning_vert)]
|
||||
|
||||
enum Foo { A, B, C }
|
||||
|
||||
fn main() {
|
||||
let x = Foo::A;
|
||||
match x {
|
||||
| Foo::A
|
||||
| Foo::B => println!("AB"),
|
||||
| Foo::C => println!("C"),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[#44101]: https://github.com/rust-lang/rust/issues/44101
|
@ -367,9 +367,36 @@ impl TypeId {
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg(stage0)]
|
||||
pub fn of<T: ?Sized + 'static>() -> TypeId {
|
||||
TypeId {
|
||||
t: unsafe { intrinsics::type_id::<T>() },
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `TypeId` of the type this generic function has been
|
||||
/// instantiated with.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::any::{Any, TypeId};
|
||||
///
|
||||
/// fn is_string<T: ?Sized + Any>(_s: &T) -> bool {
|
||||
/// TypeId::of::<String>() == TypeId::of::<T>()
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert_eq!(is_string(&0), false);
|
||||
/// assert_eq!(is_string(&"cookie monster".to_string()), true);
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature="const_type_id")]
|
||||
#[cfg(not(stage0))]
|
||||
pub const fn of<T: ?Sized + 'static>() -> TypeId {
|
||||
TypeId {
|
||||
t: unsafe { intrinsics::type_id::<T>() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,7 @@
|
||||
#![feature(untagged_unions)]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(doc_spotlight)]
|
||||
#![feature(rustc_const_unstable)]
|
||||
|
||||
#[prelude_import]
|
||||
#[allow(unused)]
|
||||
|
@ -1825,7 +1825,7 @@ pub struct Location {
|
||||
/// the location is within this block
|
||||
pub block: BasicBlock,
|
||||
|
||||
/// the location is the start of the this statement; or, if `statement_index`
|
||||
/// the location is the start of the statement; or, if `statement_index`
|
||||
/// == num-statements, then the start of the terminator.
|
||||
pub statement_index: usize,
|
||||
}
|
||||
|
@ -327,6 +327,10 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
|
||||
return Ok(mk_const(Integral(Usize(ConstUsize::new(align,
|
||||
tcx.sess.target.usize_ty).unwrap()))));
|
||||
}
|
||||
"type_id" => {
|
||||
let type_id = tcx.type_id_hash(substs.type_at(0));
|
||||
return Ok(mk_const(Integral(U64(type_id))));
|
||||
}
|
||||
_ => signal!(e, TypeckError)
|
||||
}
|
||||
}
|
||||
|
@ -302,19 +302,38 @@ impl EarlyLintPass for UnusedParens {
|
||||
Assign(_, ref value) => (value, "assigned value", false),
|
||||
AssignOp(.., ref value) => (value, "assigned value", false),
|
||||
InPlace(_, ref value) => (value, "emplacement value", false),
|
||||
Call(_, ref args) => {
|
||||
for arg in args {
|
||||
self.check_unused_parens_core(cx, arg, "function argument", false)
|
||||
// either function/method call, or something this lint doesn't care about
|
||||
ref call_or_other => {
|
||||
let args_to_check;
|
||||
let call_kind;
|
||||
match *call_or_other {
|
||||
Call(_, ref args) => {
|
||||
call_kind = "function";
|
||||
args_to_check = &args[..];
|
||||
},
|
||||
MethodCall(_, ref args) => {
|
||||
call_kind = "method";
|
||||
// first "argument" is self (which sometimes needs parens)
|
||||
args_to_check = &args[1..];
|
||||
}
|
||||
// actual catch-all arm
|
||||
_ => { return; }
|
||||
}
|
||||
return;
|
||||
},
|
||||
MethodCall(_, ref args) => {
|
||||
for arg in &args[1..] { // first "argument" is self (which sometimes needs parens)
|
||||
self.check_unused_parens_core(cx, arg, "method argument", false)
|
||||
// Don't lint if this is a nested macro expansion: otherwise, the lint could
|
||||
// trigger in situations that macro authors shouldn't have to care about, e.g.,
|
||||
// when a parenthesized token tree matched in one macro expansion is matched as
|
||||
// an expression in another and used as a fn/method argument (Issue #47775)
|
||||
if e.span.ctxt().outer().expn_info()
|
||||
.map_or(false, |info| info.call_site.ctxt().outer()
|
||||
.expn_info().is_some()) {
|
||||
return;
|
||||
}
|
||||
let msg = format!("{} argument", call_kind);
|
||||
for arg in args_to_check {
|
||||
self.check_unused_parens_core(cx, arg, &msg, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens);
|
||||
}
|
||||
|
@ -374,13 +374,20 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
||||
}
|
||||
};
|
||||
if let PlaceContext::Copy = context {
|
||||
let ty = place_ty.to_ty(self.tcx());
|
||||
if self.cx
|
||||
.infcx
|
||||
.type_moves_by_default(self.cx.param_env, ty, DUMMY_SP)
|
||||
{
|
||||
span_mirbug!(self, place, "attempted copy of non-Copy type ({:?})", ty);
|
||||
}
|
||||
let tcx = self.tcx();
|
||||
let trait_ref = ty::TraitRef {
|
||||
def_id: tcx.lang_items().copy_trait().unwrap(),
|
||||
substs: tcx.mk_substs_trait(place_ty.to_ty(tcx), &[]),
|
||||
};
|
||||
|
||||
// In order to have a Copy operand, the type T of the value must be Copy. Note that we
|
||||
// prove that T: Copy, rather than using the type_moves_by_default test. This is
|
||||
// important because type_moves_by_default ignores the resulting region obligations and
|
||||
// assumes they pass. This can result in bounds from Copy impls being unsoundly ignored
|
||||
// (e.g., #29149). Note that we decide to use Copy before knowing whether the bounds
|
||||
// fully apply: in effect, the rule is that if a value of some type could implement
|
||||
// Copy, then it must.
|
||||
self.cx.prove_trait_ref(trait_ref, location);
|
||||
}
|
||||
place_ty
|
||||
}
|
||||
|
@ -243,6 +243,12 @@ impl<'tcx> super::Machine<'tcx> for CompileTimeEvaluator {
|
||||
ecx.write_primval(dest, PrimVal::from_u128(size), dest_layout.ty)?;
|
||||
}
|
||||
|
||||
"type_id" => {
|
||||
let ty = substs.type_at(0);
|
||||
let type_id = ecx.tcx.type_id_hash(ty) as u128;
|
||||
ecx.write_primval(dest, PrimVal::from_u128(type_id), dest_layout.ty)?;
|
||||
}
|
||||
|
||||
name => return Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()),
|
||||
}
|
||||
|
||||
|
@ -737,7 +737,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
||||
Abi::PlatformIntrinsic => {
|
||||
assert!(!self.tcx.is_const_fn(def_id));
|
||||
match &self.tcx.item_name(def_id)[..] {
|
||||
"size_of" | "min_align_of" => is_const_fn = Some(def_id),
|
||||
"size_of" | "min_align_of" | "type_id" => is_const_fn = Some(def_id),
|
||||
|
||||
name if name.starts_with("simd_shuffle") => {
|
||||
is_shuffle = true;
|
||||
|
@ -411,6 +411,11 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
||||
self.cx.align_of(substs.type_at(0)).abi());
|
||||
Ok(Const::new(llval, tcx.types.usize))
|
||||
}
|
||||
"type_id" => {
|
||||
let llval = C_u64(self.cx,
|
||||
self.cx.tcx.type_id_hash(substs.type_at(0)));
|
||||
Ok(Const::new(llval, tcx.types.u64))
|
||||
}
|
||||
_ => span_bug!(span, "{:?} in constant", terminator.kind)
|
||||
}
|
||||
} else if let Some((op, is_checked)) = self.is_binop_lang_item(def_id) {
|
||||
|
@ -2447,7 +2447,12 @@ impl Clean<Type> for hir::Ty {
|
||||
let def_id = cx.tcx.hir.body_owner_def_id(n);
|
||||
let param_env = cx.tcx.param_env(def_id);
|
||||
let substs = Substs::identity_for_item(cx.tcx, def_id);
|
||||
let n = cx.tcx.const_eval(param_env.and((def_id, substs))).unwrap();
|
||||
let n = cx.tcx.const_eval(param_env.and((def_id, substs))).unwrap_or_else(|_| {
|
||||
cx.tcx.mk_const(ty::Const {
|
||||
val: ConstVal::Unevaluated(def_id, substs),
|
||||
ty: cx.tcx.types.usize
|
||||
})
|
||||
});
|
||||
let n = if let ConstVal::Integral(ConstInt::Usize(n)) = n.val {
|
||||
n.to_string()
|
||||
} else if let ConstVal::Unevaluated(def_id, _) = n.val {
|
||||
@ -2577,7 +2582,9 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
|
||||
let mut n = cx.tcx.lift(&n).unwrap();
|
||||
if let ConstVal::Unevaluated(def_id, substs) = n.val {
|
||||
let param_env = cx.tcx.param_env(def_id);
|
||||
n = cx.tcx.const_eval(param_env.and((def_id, substs))).unwrap()
|
||||
if let Ok(new_n) = cx.tcx.const_eval(param_env.and((def_id, substs))) {
|
||||
n = new_n;
|
||||
}
|
||||
};
|
||||
let n = if let ConstVal::Integral(ConstInt::Usize(n)) = n.val {
|
||||
n.to_string()
|
||||
|
@ -482,20 +482,42 @@ impl File {
|
||||
self.inner.file_attr().map(Metadata)
|
||||
}
|
||||
|
||||
/// Creates a new independently owned handle to the underlying file.
|
||||
///
|
||||
/// The returned `File` is a reference to the same state that this object
|
||||
/// references. Both handles will read and write with the same cursor
|
||||
/// position.
|
||||
/// Create a new `File` instance that shares the same underlying file handle
|
||||
/// as the existing `File` instance. Reads, writes, and seeks will affect
|
||||
/// both `File` instances simultaneously.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create two handles for a file named `foo.txt`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// # fn foo() -> std::io::Result<()> {
|
||||
/// let mut f = File::open("foo.txt")?;
|
||||
/// let file_copy = f.try_clone()?;
|
||||
/// let mut file = File::open("foo.txt")?;
|
||||
/// let file_copy = file.try_clone()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Assuming there’s a file named `foo.txt` with contents `abcdef\n`, create
|
||||
/// two handles, seek one of them, and read the remaining bytes from the
|
||||
/// other handle:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fs::File;
|
||||
/// use std::io::SeekFrom;
|
||||
/// use std::io::prelude::*;
|
||||
///
|
||||
/// # fn foo() -> std::io::Result<()> {
|
||||
/// let mut file = File::open("foo.txt")?;
|
||||
/// let mut file_copy = file.try_clone()?;
|
||||
///
|
||||
/// file.seek(SeekFrom::Start(3))?;
|
||||
///
|
||||
/// let mut contents = vec![];
|
||||
/// file_copy.read_to_end(&mut contents)?;
|
||||
/// assert_eq!(contents, b"def\n");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
@ -1001,7 +1023,7 @@ impl Metadata {
|
||||
self.0.accessed().map(FromInner::from_inner)
|
||||
}
|
||||
|
||||
/// Returns the creation time listed in the this metadata.
|
||||
/// Returns the creation time listed in this metadata.
|
||||
///
|
||||
/// The returned value corresponds to the `birthtime` field of `stat` on
|
||||
/// Unix platforms and the `ftCreationTime` field on Windows platforms.
|
||||
|
@ -111,10 +111,11 @@ impl Drop for Thread {
|
||||
|
||||
#[cfg_attr(test, allow(dead_code))]
|
||||
pub mod guard {
|
||||
pub unsafe fn current() -> Option<usize> {
|
||||
pub type Guard = !;
|
||||
pub unsafe fn current() -> Option<Guard> {
|
||||
None
|
||||
}
|
||||
pub unsafe fn init() -> Option<usize> {
|
||||
pub unsafe fn init() -> Option<Guard> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ impl Thread {
|
||||
}
|
||||
|
||||
pub mod guard {
|
||||
pub unsafe fn current() -> Option<usize> { None }
|
||||
pub unsafe fn init() -> Option<usize> { None }
|
||||
pub type Guard = !;
|
||||
pub unsafe fn current() -> Option<Guard> { None }
|
||||
pub unsafe fn init() -> Option<Guard> { None }
|
||||
}
|
||||
|
@ -57,9 +57,6 @@ mod imp {
|
||||
use sys_common::thread_info;
|
||||
|
||||
|
||||
// This is initialized in init() and only read from after
|
||||
static mut PAGE_SIZE: usize = 0;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
|
||||
#[repr(C)]
|
||||
@ -102,12 +99,12 @@ mod imp {
|
||||
_data: *mut libc::c_void) {
|
||||
use sys_common::util::report_overflow;
|
||||
|
||||
let guard = thread_info::stack_guard().unwrap_or(0);
|
||||
let guard = thread_info::stack_guard().unwrap_or(0..0);
|
||||
let addr = siginfo_si_addr(info);
|
||||
|
||||
// If the faulting address is within the guard page, then we print a
|
||||
// message saying so and abort.
|
||||
if guard != 0 && guard - PAGE_SIZE <= addr && addr < guard {
|
||||
if guard.start <= addr && addr < guard.end {
|
||||
report_overflow();
|
||||
rtabort!("stack overflow");
|
||||
} else {
|
||||
@ -123,8 +120,6 @@ mod imp {
|
||||
static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut();
|
||||
|
||||
pub unsafe fn init() {
|
||||
PAGE_SIZE = ::sys::os::page_size();
|
||||
|
||||
let mut action: sigaction = mem::zeroed();
|
||||
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
action.sa_sigaction = signal_handler as sighandler_t;
|
||||
|
@ -205,8 +205,10 @@ impl Drop for Thread {
|
||||
not(target_os = "solaris")))]
|
||||
#[cfg_attr(test, allow(dead_code))]
|
||||
pub mod guard {
|
||||
pub unsafe fn current() -> Option<usize> { None }
|
||||
pub unsafe fn init() -> Option<usize> { None }
|
||||
use ops::Range;
|
||||
pub type Guard = Range<usize>;
|
||||
pub unsafe fn current() -> Option<Guard> { None }
|
||||
pub unsafe fn init() -> Option<Guard> { None }
|
||||
}
|
||||
|
||||
|
||||
@ -222,14 +224,43 @@ pub mod guard {
|
||||
use libc;
|
||||
use libc::mmap;
|
||||
use libc::{PROT_NONE, MAP_PRIVATE, MAP_ANON, MAP_FAILED, MAP_FIXED};
|
||||
use ops::Range;
|
||||
use sys::os;
|
||||
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "bitrig",
|
||||
target_os = "openbsd",
|
||||
target_os = "solaris"))]
|
||||
// This is initialized in init() and only read from after
|
||||
static mut PAGE_SIZE: usize = 0;
|
||||
|
||||
pub type Guard = Range<usize>;
|
||||
|
||||
#[cfg(target_os = "solaris")]
|
||||
unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
|
||||
current().map(|s| s as *mut libc::c_void)
|
||||
let mut current_stack: libc::stack_t = ::mem::zeroed();
|
||||
assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
|
||||
Some(current_stack.ss_sp)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
|
||||
let stackaddr = libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize -
|
||||
libc::pthread_get_stacksize_np(libc::pthread_self());
|
||||
Some(stackaddr as *mut libc::c_void)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "openbsd", target_os = "bitrig"))]
|
||||
unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
|
||||
let mut current_stack: libc::stack_t = ::mem::zeroed();
|
||||
assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(),
|
||||
&mut current_stack), 0);
|
||||
|
||||
let extra = if cfg!(target_os = "bitrig") {3} else {1} * PAGE_SIZE;
|
||||
let stackaddr = if libc::pthread_main_np() == 1 {
|
||||
// main thread
|
||||
current_stack.ss_sp as usize - current_stack.ss_size + extra
|
||||
} else {
|
||||
// new thread
|
||||
current_stack.ss_sp as usize - current_stack.ss_size
|
||||
};
|
||||
Some(stackaddr as *mut libc::c_void)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "freebsd",
|
||||
@ -253,8 +284,9 @@ pub mod guard {
|
||||
ret
|
||||
}
|
||||
|
||||
pub unsafe fn init() -> Option<usize> {
|
||||
let psize = os::page_size();
|
||||
pub unsafe fn init() -> Option<Guard> {
|
||||
PAGE_SIZE = os::page_size();
|
||||
|
||||
let mut stackaddr = get_stack_start()?;
|
||||
|
||||
// Ensure stackaddr is page aligned! A parent process might
|
||||
@ -263,9 +295,9 @@ pub mod guard {
|
||||
// stackaddr < stackaddr + stacksize, so if stackaddr is not
|
||||
// page-aligned, calculate the fix such that stackaddr <
|
||||
// new_page_aligned_stackaddr < stackaddr + stacksize
|
||||
let remainder = (stackaddr as usize) % psize;
|
||||
let remainder = (stackaddr as usize) % PAGE_SIZE;
|
||||
if remainder != 0 {
|
||||
stackaddr = ((stackaddr as usize) + psize - remainder)
|
||||
stackaddr = ((stackaddr as usize) + PAGE_SIZE - remainder)
|
||||
as *mut libc::c_void;
|
||||
}
|
||||
|
||||
@ -280,60 +312,42 @@ pub mod guard {
|
||||
// Instead, we'll just note where we expect rlimit to start
|
||||
// faulting, so our handler can report "stack overflow", and
|
||||
// trust that the kernel's own stack guard will work.
|
||||
Some(stackaddr as usize)
|
||||
let stackaddr = stackaddr as usize;
|
||||
Some(stackaddr - PAGE_SIZE..stackaddr)
|
||||
} else {
|
||||
// Reallocate the last page of the stack.
|
||||
// This ensures SIGBUS will be raised on
|
||||
// stack overflow.
|
||||
let result = mmap(stackaddr, psize, PROT_NONE,
|
||||
let result = mmap(stackaddr, PAGE_SIZE, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
|
||||
|
||||
if result != stackaddr || result == MAP_FAILED {
|
||||
panic!("failed to allocate a guard page");
|
||||
}
|
||||
|
||||
let guardaddr = stackaddr as usize;
|
||||
let offset = if cfg!(target_os = "freebsd") {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
Some(stackaddr as usize + offset * psize)
|
||||
Some(guardaddr..guardaddr + offset * PAGE_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "solaris")]
|
||||
pub unsafe fn current() -> Option<usize> {
|
||||
let mut current_stack: libc::stack_t = ::mem::zeroed();
|
||||
assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
|
||||
Some(current_stack.ss_sp as usize)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub unsafe fn current() -> Option<usize> {
|
||||
Some(libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize -
|
||||
libc::pthread_get_stacksize_np(libc::pthread_self()))
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "openbsd", target_os = "bitrig"))]
|
||||
pub unsafe fn current() -> Option<usize> {
|
||||
let mut current_stack: libc::stack_t = ::mem::zeroed();
|
||||
assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(),
|
||||
&mut current_stack), 0);
|
||||
|
||||
let extra = if cfg!(target_os = "bitrig") {3} else {1} * os::page_size();
|
||||
Some(if libc::pthread_main_np() == 1 {
|
||||
// main thread
|
||||
current_stack.ss_sp as usize - current_stack.ss_size + extra
|
||||
} else {
|
||||
// new thread
|
||||
current_stack.ss_sp as usize - current_stack.ss_size
|
||||
})
|
||||
#[cfg(any(target_os = "macos",
|
||||
target_os = "bitrig",
|
||||
target_os = "openbsd",
|
||||
target_os = "solaris"))]
|
||||
pub unsafe fn current() -> Option<Guard> {
|
||||
let stackaddr = get_stack_start()? as usize;
|
||||
Some(stackaddr - PAGE_SIZE..stackaddr)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "freebsd",
|
||||
target_os = "linux", target_os = "netbsd", target_os = "l4re"))]
|
||||
pub unsafe fn current() -> Option<usize> {
|
||||
pub unsafe fn current() -> Option<Guard> {
|
||||
let mut ret = None;
|
||||
let mut attr: libc::pthread_attr_t = ::mem::zeroed();
|
||||
assert_eq!(libc::pthread_attr_init(&mut attr), 0);
|
||||
@ -352,12 +366,23 @@ pub mod guard {
|
||||
assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr,
|
||||
&mut size), 0);
|
||||
|
||||
let stackaddr = stackaddr as usize;
|
||||
ret = if cfg!(target_os = "freebsd") {
|
||||
Some(stackaddr as usize - guardsize)
|
||||
// FIXME does freebsd really fault *below* the guard addr?
|
||||
let guardaddr = stackaddr - guardsize;
|
||||
Some(guardaddr - PAGE_SIZE..guardaddr)
|
||||
} else if cfg!(target_os = "netbsd") {
|
||||
Some(stackaddr as usize)
|
||||
Some(stackaddr - guardsize..stackaddr)
|
||||
} else if cfg!(all(target_os = "linux", target_env = "gnu")) {
|
||||
// glibc used to include the guard area within the stack, as noted in the BUGS
|
||||
// section of `man pthread_attr_getguardsize`. This has been corrected starting
|
||||
// with glibc 2.27, and in some distro backports, so the guard is now placed at the
|
||||
// end (below) the stack. There's no easy way for us to know which we have at
|
||||
// runtime, so we'll just match any fault in the range right above or below the
|
||||
// stack base to call that fault a stack overflow.
|
||||
Some(stackaddr - guardsize..stackaddr + guardsize)
|
||||
} else {
|
||||
Some(stackaddr as usize + guardsize)
|
||||
Some(stackaddr..stackaddr + guardsize)
|
||||
};
|
||||
}
|
||||
assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
|
||||
|
@ -43,6 +43,7 @@ impl Thread {
|
||||
}
|
||||
|
||||
pub mod guard {
|
||||
pub unsafe fn current() -> Option<usize> { None }
|
||||
pub unsafe fn init() -> Option<usize> { None }
|
||||
pub type Guard = !;
|
||||
pub unsafe fn current() -> Option<Guard> { None }
|
||||
pub unsafe fn init() -> Option<Guard> { None }
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ impl Thread {
|
||||
|
||||
#[cfg_attr(test, allow(dead_code))]
|
||||
pub mod guard {
|
||||
pub unsafe fn current() -> Option<usize> { None }
|
||||
pub unsafe fn init() -> Option<usize> { None }
|
||||
pub type Guard = !;
|
||||
pub unsafe fn current() -> Option<Guard> { None }
|
||||
pub unsafe fn init() -> Option<Guard> { None }
|
||||
}
|
||||
|
@ -11,10 +11,11 @@
|
||||
#![allow(dead_code)] // stack_guard isn't used right now on all platforms
|
||||
|
||||
use cell::RefCell;
|
||||
use sys::thread::guard::Guard;
|
||||
use thread::Thread;
|
||||
|
||||
struct ThreadInfo {
|
||||
stack_guard: Option<usize>,
|
||||
stack_guard: Option<Guard>,
|
||||
thread: Thread,
|
||||
}
|
||||
|
||||
@ -38,11 +39,11 @@ pub fn current_thread() -> Option<Thread> {
|
||||
ThreadInfo::with(|info| info.thread.clone())
|
||||
}
|
||||
|
||||
pub fn stack_guard() -> Option<usize> {
|
||||
ThreadInfo::with(|info| info.stack_guard).and_then(|o| o)
|
||||
pub fn stack_guard() -> Option<Guard> {
|
||||
ThreadInfo::with(|info| info.stack_guard.clone()).and_then(|o| o)
|
||||
}
|
||||
|
||||
pub fn set(stack_guard: Option<usize>, thread: Thread) {
|
||||
pub fn set(stack_guard: Option<Guard>, thread: Thread) {
|
||||
THREAD_INFO.with(|c| assert!(c.borrow().is_none()));
|
||||
THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo{
|
||||
stack_guard,
|
||||
|
@ -883,7 +883,6 @@ pub struct Arm {
|
||||
pub pats: Vec<P<Pat>>,
|
||||
pub guard: Option<P<Expr>>,
|
||||
pub body: P<Expr>,
|
||||
pub beginning_vert: Option<Span>, // For RFC 1925 feature gate
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||
|
@ -883,7 +883,6 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
||||
pats,
|
||||
guard: None,
|
||||
body: expr,
|
||||
beginning_vert: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,9 +386,6 @@ declare_features! (
|
||||
// allow `#[must_use]` on functions and comparison operators (RFC 1940)
|
||||
(active, fn_must_use, "1.21.0", Some(43302)),
|
||||
|
||||
// allow '|' at beginning of match arms (RFC 1925)
|
||||
(active, match_beginning_vert, "1.21.0", Some(44101)),
|
||||
|
||||
// Future-proofing enums/structs with #[non_exhaustive] attribute (RFC 2008)
|
||||
(active, non_exhaustive, "1.22.0", Some(44109)),
|
||||
|
||||
@ -545,6 +542,8 @@ declare_features! (
|
||||
(accepted, abi_sysv64, "1.24.0", Some(36167)),
|
||||
// Allows `repr(align(16))` struct attribute (RFC 1358)
|
||||
(accepted, repr_align, "1.24.0", Some(33626)),
|
||||
// allow '|' at beginning of match arms (RFC 1925)
|
||||
(accepted, match_beginning_vert, "1.25.0", Some(44101)),
|
||||
);
|
||||
|
||||
// If you change this, please modify src/doc/unstable-book as well. You must
|
||||
@ -1683,11 +1682,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
}
|
||||
|
||||
fn visit_arm(&mut self, arm: &'a ast::Arm) {
|
||||
if let Some(span) = arm.beginning_vert {
|
||||
gate_feature_post!(&self, match_beginning_vert,
|
||||
span,
|
||||
"Use of a '|' at the beginning of a match arm is experimental")
|
||||
}
|
||||
visit::walk_arm(self, arm)
|
||||
}
|
||||
|
||||
|
@ -340,14 +340,13 @@ pub fn fold_thin_attrs<T: Folder>(attrs: ThinVec<Attribute>, fld: &mut T) -> Thi
|
||||
fold_attrs(attrs.into(), fld).into()
|
||||
}
|
||||
|
||||
pub fn noop_fold_arm<T: Folder>(Arm {attrs, pats, guard, body, beginning_vert}: Arm,
|
||||
pub fn noop_fold_arm<T: Folder>(Arm {attrs, pats, guard, body}: Arm,
|
||||
fld: &mut T) -> Arm {
|
||||
Arm {
|
||||
attrs: fold_attrs(attrs, fld),
|
||||
pats: pats.move_map(|x| fld.fold_pat(x)),
|
||||
guard: guard.map(|x| fld.fold_expr(x)),
|
||||
body: fld.fold_expr(body),
|
||||
beginning_vert,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3398,11 +3398,7 @@ impl<'a> Parser<'a> {
|
||||
|
||||
let attrs = self.parse_outer_attributes()?;
|
||||
// Allow a '|' before the pats (RFC 1925)
|
||||
let beginning_vert = if self.eat(&token::BinOp(token::Or)) {
|
||||
Some(self.prev_span)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.eat(&token::BinOp(token::Or));
|
||||
let pats = self.parse_pats()?;
|
||||
let guard = if self.eat_keyword(keywords::If) {
|
||||
Some(self.parse_expr()?)
|
||||
@ -3426,7 +3422,6 @@ impl<'a> Parser<'a> {
|
||||
pats,
|
||||
guard,
|
||||
body: expr,
|
||||
beginning_vert,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
// ignore-wasm
|
||||
// ignore-emscripten
|
||||
// ignore-windows
|
||||
// no-system-llvm
|
||||
// min-system-llvm-version 5.0
|
||||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
18
src/test/compile-fail/const-typeid-of.rs
Normal file
18
src/test/compile-fail/const-typeid-of.rs
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
struct A;
|
||||
|
||||
fn main() {
|
||||
const A_ID: TypeId = TypeId::of::<A>();
|
||||
//~^ ERROR `std::any::TypeId::of` is not yet stable as a const fn
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test that the 'static bound from the Copy impl is respected. Regression test for #29149.
|
||||
|
||||
#![feature(nll)]
|
||||
|
||||
#[derive(Clone)] struct Foo<'a>(&'a u32);
|
||||
impl Copy for Foo<'static> {}
|
||||
|
||||
fn main() {
|
||||
let s = 2;
|
||||
let a = Foo(&s); //~ ERROR `s` does not live long enough [E0597]
|
||||
drop(a);
|
||||
drop(a);
|
||||
}
|
43
src/test/run-pass/const-typeid-of.rs
Normal file
43
src/test/run-pass/const-typeid-of.rs
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(const_type_id)]
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
struct A;
|
||||
|
||||
static ID_ISIZE: TypeId = TypeId::of::<isize>();
|
||||
|
||||
pub fn main() {
|
||||
assert_eq!(ID_ISIZE, TypeId::of::<isize>());
|
||||
|
||||
// sanity test of TypeId
|
||||
const T: (TypeId, TypeId, TypeId) = (TypeId::of::<usize>(),
|
||||
TypeId::of::<&'static str>(),
|
||||
TypeId::of::<A>());
|
||||
let (d, e, f) = (TypeId::of::<usize>(), TypeId::of::<&'static str>(),
|
||||
TypeId::of::<A>());
|
||||
|
||||
assert!(T.0 != T.1);
|
||||
assert!(T.0 != T.2);
|
||||
assert!(T.1 != T.2);
|
||||
|
||||
assert_eq!(T.0, d);
|
||||
assert_eq!(T.1, e);
|
||||
assert_eq!(T.2, f);
|
||||
|
||||
// Check fn pointer against collisions
|
||||
const F: (TypeId, TypeId) = (TypeId::of::<fn(fn(A) -> A) -> A>(),
|
||||
TypeId::of::<fn(fn() -> A, A) -> A>());
|
||||
|
||||
assert!(F.0 != F.1);
|
||||
}
|
28
src/test/run-pass/match-beginning-vert.rs
Normal file
28
src/test/run-pass/match-beginning-vert.rs
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
enum Foo {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
}
|
||||
use Foo::*;
|
||||
|
||||
fn main() {
|
||||
for foo in &[A, B, C, D, E] {
|
||||
match *foo {
|
||||
| A => println!("A"),
|
||||
| B | C if 1 < 2 => println!("BC!"),
|
||||
| _ => {},
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
// ignore-emscripten no processes
|
||||
// ignore-musl FIXME #31506
|
||||
// ignore-pretty
|
||||
// no-system-llvm
|
||||
// min-system-llvm-version 5.0
|
||||
// compile-flags: -C lto
|
||||
// no-prefer-dynamic
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
// ignore-cloudabi no processes
|
||||
// ignore-emscripten no processes
|
||||
// ignore-musl FIXME #31506
|
||||
// no-system-llvm
|
||||
// min-system-llvm-version 5.0
|
||||
|
||||
use std::mem;
|
||||
use std::process::Command;
|
||||
|
22
src/test/rustdoc/const-evalutation-ice.rs
Normal file
22
src/test/rustdoc/const-evalutation-ice.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Just check if we don't get an ICE for the _S type.
|
||||
|
||||
#![feature(const_size_of)]
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::mem;
|
||||
|
||||
pub struct S {
|
||||
s: Cell<usize>
|
||||
}
|
||||
|
||||
pub type _S = [usize; 0 - (mem::size_of::<S>() != 4) as usize];
|
@ -1,36 +0,0 @@
|
||||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum Foo {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
}
|
||||
use Foo::*;
|
||||
|
||||
fn main() {
|
||||
let x = Foo::A;
|
||||
match x {
|
||||
| A => println!("A"),
|
||||
//~^ ERROR: Use of a '|' at the beginning of a match arm is experimental (see issue #44101)
|
||||
| B | C => println!("BC!"),
|
||||
//~^ ERROR: Use of a '|' at the beginning of a match arm is experimental (see issue #44101)
|
||||
| _ => {},
|
||||
//~^ ERROR: Use of a '|' at the beginning of a match arm is experimental (see issue #44101)
|
||||
};
|
||||
match x {
|
||||
A | B | C => println!("ABC!"),
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
error[E0658]: Use of a '|' at the beginning of a match arm is experimental (see issue #44101)
|
||||
--> $DIR/feature-gate-match_beginning_vert.rs:24:9
|
||||
|
|
||||
24 | | A => println!("A"),
|
||||
| ^
|
||||
|
|
||||
= help: add #![feature(match_beginning_vert)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: Use of a '|' at the beginning of a match arm is experimental (see issue #44101)
|
||||
--> $DIR/feature-gate-match_beginning_vert.rs:26:9
|
||||
|
|
||||
26 | | B | C => println!("BC!"),
|
||||
| ^
|
||||
|
|
||||
= help: add #![feature(match_beginning_vert)] to the crate attributes to enable
|
||||
|
||||
error[E0658]: Use of a '|' at the beginning of a match arm is experimental (see issue #44101)
|
||||
--> $DIR/feature-gate-match_beginning_vert.rs:28:9
|
||||
|
|
||||
28 | | _ => {},
|
||||
| ^
|
||||
|
|
||||
= help: add #![feature(match_beginning_vert)] to the crate attributes to enable
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
@ -0,0 +1,40 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// must-compile-successfully
|
||||
|
||||
#![warn(unused_parens)]
|
||||
|
||||
macro_rules! the_worship_the_heart_lifts_above {
|
||||
( @as_expr, $e:expr) => { $e };
|
||||
( @generate_fn, $name:tt) => {
|
||||
#[allow(dead_code)] fn the_moth_for_the_star<'a>() -> Option<&'a str> {
|
||||
Some(the_worship_the_heart_lifts_above!( @as_expr, $name ))
|
||||
}
|
||||
};
|
||||
( $name:ident ) => { the_worship_the_heart_lifts_above!( @generate_fn, (stringify!($name))); }
|
||||
// ↑ Notably, this does 𝘯𝘰𝘵 warn: we're declining to lint unused parens in
|
||||
// function/method arguments inside of nested macros because of situations
|
||||
// like those reported in Issue #47775
|
||||
}
|
||||
|
||||
macro_rules! and_the_heavens_reject_not {
|
||||
() => {
|
||||
// ↓ But let's test that we still lint for unused parens around
|
||||
// function args inside of simple, one-deep macros.
|
||||
#[allow(dead_code)] fn the_night_for_the_morrow() -> Option<isize> { Some((2)) }
|
||||
//~^ WARN unnecessary parentheses around function argument
|
||||
}
|
||||
}
|
||||
|
||||
the_worship_the_heart_lifts_above!(rah);
|
||||
and_the_heavens_reject_not!();
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,15 @@
|
||||
warning: unnecessary parentheses around function argument
|
||||
--> $DIR/issue-47775-nested-macro-unnecessary-parens-arg.rs:32:83
|
||||
|
|
||||
32 | #[allow(dead_code)] fn the_night_for_the_morrow() -> Option<isize> { Some((2)) }
|
||||
| ^^^ help: remove these parentheses
|
||||
...
|
||||
38 | and_the_heavens_reject_not!();
|
||||
| ------------------------------ in this macro invocation
|
||||
|
|
||||
note: lint level defined here
|
||||
--> $DIR/issue-47775-nested-macro-unnecessary-parens-arg.rs:13:9
|
||||
|
|
||||
13 | #![warn(unused_parens)]
|
||||
| ^^^^^^^^^^^^^
|
||||
|
@ -167,7 +167,7 @@ impl EarlyProps {
|
||||
.expect("Malformed llvm version directive");
|
||||
// Ignore if using system LLVM and actual version
|
||||
// is smaller the minimum required version
|
||||
!(config.system_llvm && &actual_version[..] < min_version)
|
||||
config.system_llvm && &actual_version[..] < min_version
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma
|
||||
let mut results = Vec::new();
|
||||
let mut mismatch = Mismatch::new(0);
|
||||
|
||||
for result in diff::lines(actual, expected) {
|
||||
for result in diff::lines(expected, actual) {
|
||||
match result {
|
||||
diff::Result::Left(str) => {
|
||||
if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
|
||||
@ -91,7 +91,8 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma
|
||||
mismatch.lines.push(DiffLine::Context(line.to_owned()));
|
||||
}
|
||||
|
||||
mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
|
||||
mismatch.lines.push(DiffLine::Expected(str.to_owned()));
|
||||
line_number += 1;
|
||||
lines_since_mismatch = 0;
|
||||
}
|
||||
diff::Result::Right(str) => {
|
||||
@ -104,8 +105,7 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma
|
||||
mismatch.lines.push(DiffLine::Context(line.to_owned()));
|
||||
}
|
||||
|
||||
mismatch.lines.push(DiffLine::Expected(str.to_owned()));
|
||||
line_number += 1;
|
||||
mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
|
||||
lines_since_mismatch = 0;
|
||||
}
|
||||
diff::Result::Both(str, _) => {
|
||||
|
Loading…
Reference in New Issue
Block a user