Auto merge of #65661 - JohnTitor:rollup-68la1fq, r=JohnTitor
Rollup of 5 pull requests Successful merges: - #65544 (Added doc on keyword break) - #65620 (Correctly note code as Ok not error for E0573) - #65624 ([mir-opt] Improve SimplifyLocals pass so it can remove unused consts) - #65650 (use unwrap_or in lint code) - #65652 (Fix `canonicalize_const_var` leaking inference variables) Failed merges: r? @ghost
This commit is contained in:
commit
10f12fe3e7
@ -701,7 +701,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
|
||||
self.tcx().mk_const(
|
||||
ty::Const {
|
||||
val: ConstValue::Infer(InferConst::Canonical(self.binder_index, var.into())),
|
||||
ty: const_var.ty,
|
||||
ty: self.fold_ty(const_var.ty),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -202,11 +202,7 @@ impl<'a> LintLevelsBuilder<'a> {
|
||||
let meta = unwrap_or!(attr.meta(), continue);
|
||||
attr::mark_used(attr);
|
||||
|
||||
let mut metas = if let Some(metas) = meta.meta_item_list() {
|
||||
metas
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let mut metas = unwrap_or!(meta.meta_item_list(), continue);
|
||||
|
||||
if metas.is_empty() {
|
||||
// FIXME (#55112): issue unused-attributes lint for `#[level()]`
|
||||
|
@ -431,7 +431,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
place_layout: TyLayout<'tcx>,
|
||||
source_info: SourceInfo,
|
||||
place: &Place<'tcx>,
|
||||
) -> Option<Const<'tcx>> {
|
||||
) -> Option<()> {
|
||||
let span = source_info.span;
|
||||
|
||||
let overflow_check = self.tcx.sess.overflow_checks();
|
||||
@ -540,20 +540,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// Work around: avoid extra unnecessary locals. FIXME(wesleywiser)
|
||||
// Const eval will turn this into a `const Scalar(<ZST>)` that
|
||||
// `SimplifyLocals` doesn't know it can remove.
|
||||
Rvalue::Aggregate(_, operands) if operands.len() == 0 => {
|
||||
return None;
|
||||
}
|
||||
|
||||
_ => { }
|
||||
}
|
||||
|
||||
self.use_ecx(source_info, |this| {
|
||||
trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place);
|
||||
this.ecx.eval_rvalue_into_place(rvalue, place)?;
|
||||
this.ecx.eval_place_to_op(place, Some(place_layout))
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
@ -717,16 +710,15 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
|
||||
base: PlaceBase::Local(local),
|
||||
projection: box [],
|
||||
} = *place {
|
||||
if let Some(value) = self.const_prop(rval,
|
||||
place_layout,
|
||||
statement.source_info,
|
||||
place) {
|
||||
trace!("checking whether {:?} can be stored to {:?}", value, local);
|
||||
let source = statement.source_info;
|
||||
if let Some(()) = self.const_prop(rval, place_layout, source, place) {
|
||||
if self.can_const_prop[local] {
|
||||
trace!("stored {:?} to {:?}", value, local);
|
||||
assert_eq!(self.get_const(local), Some(value));
|
||||
trace!("propagated into {:?}", local);
|
||||
|
||||
if self.should_const_prop() {
|
||||
let value =
|
||||
self.get_const(local).expect("local was dead/uninitialized");
|
||||
trace!("replacing {:?} with {:?}", rval, value);
|
||||
self.replace_with_const(
|
||||
rval,
|
||||
value,
|
||||
@ -734,7 +726,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
|
||||
);
|
||||
}
|
||||
} else {
|
||||
trace!("can't propagate {:?} to {:?}", value, local);
|
||||
trace!("can't propagate into {:?}", local);
|
||||
self.remove_const(local);
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::vec::{Idx, IndexVec};
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc::mir::*;
|
||||
use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext};
|
||||
use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext, MutatingUseContext};
|
||||
use rustc::session::config::DebugInfo;
|
||||
use std::borrow::Cow;
|
||||
use crate::transform::{MirPass, MirSource};
|
||||
@ -293,23 +293,31 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
|
||||
pub struct SimplifyLocals;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for SimplifyLocals {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) {
|
||||
let mut marker = DeclMarker { locals: BitSet::new_empty(body.local_decls.len()) };
|
||||
marker.visit_body(body);
|
||||
// Return pointer and arguments are always live
|
||||
marker.locals.insert(RETURN_PLACE);
|
||||
for arg in body.args_iter() {
|
||||
marker.locals.insert(arg);
|
||||
}
|
||||
|
||||
// We may need to keep dead user variables live for debuginfo.
|
||||
if tcx.sess.opts.debuginfo == DebugInfo::Full {
|
||||
for local in body.vars_iter() {
|
||||
marker.locals.insert(local);
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
|
||||
trace!("running SimplifyLocals on {:?}", source);
|
||||
let locals = {
|
||||
let mut marker = DeclMarker {
|
||||
locals: BitSet::new_empty(body.local_decls.len()),
|
||||
body,
|
||||
};
|
||||
marker.visit_body(body);
|
||||
// Return pointer and arguments are always live
|
||||
marker.locals.insert(RETURN_PLACE);
|
||||
for arg in body.args_iter() {
|
||||
marker.locals.insert(arg);
|
||||
}
|
||||
}
|
||||
|
||||
let map = make_local_map(&mut body.local_decls, marker.locals);
|
||||
// We may need to keep dead user variables live for debuginfo.
|
||||
if tcx.sess.opts.debuginfo == DebugInfo::Full {
|
||||
for local in body.vars_iter() {
|
||||
marker.locals.insert(local);
|
||||
}
|
||||
}
|
||||
|
||||
marker.locals
|
||||
};
|
||||
|
||||
let map = make_local_map(&mut body.local_decls, locals);
|
||||
// Update references to all vars and tmps now
|
||||
LocalUpdater { map }.visit_body(body);
|
||||
body.local_decls.shrink_to_fit();
|
||||
@ -334,18 +342,35 @@ fn make_local_map<V>(
|
||||
map
|
||||
}
|
||||
|
||||
struct DeclMarker {
|
||||
struct DeclMarker<'a, 'tcx> {
|
||||
pub locals: BitSet<Local>,
|
||||
pub body: &'a Body<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for DeclMarker {
|
||||
fn visit_local(&mut self, local: &Local, ctx: PlaceContext, _: Location) {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> {
|
||||
fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) {
|
||||
// Ignore storage markers altogether, they get removed along with their otherwise unused
|
||||
// decls.
|
||||
// FIXME: Extend this to all non-uses.
|
||||
if !ctx.is_storage_marker() {
|
||||
self.locals.insert(*local);
|
||||
if ctx.is_storage_marker() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore stores of constants because `ConstProp` and `CopyProp` can remove uses of many
|
||||
// of these locals. However, if the local is still needed, then it will be referenced in
|
||||
// another place and we'll mark it as being used there.
|
||||
if ctx == PlaceContext::MutatingUse(MutatingUseContext::Store) {
|
||||
let stmt =
|
||||
&self.body.basic_blocks()[location.block].statements[location.statement_index];
|
||||
if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(c)))) = &stmt.kind {
|
||||
if p.as_local().is_some() {
|
||||
trace!("skipping store of const value {:?} to {:?}", c, local);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.locals.insert(*local);
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,9 +382,16 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater {
|
||||
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
|
||||
// Remove unnecessary StorageLive and StorageDead annotations.
|
||||
data.statements.retain(|stmt| {
|
||||
match stmt.kind {
|
||||
match &stmt.kind {
|
||||
StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
|
||||
self.map[l].is_some()
|
||||
self.map[*l].is_some()
|
||||
}
|
||||
StatementKind::Assign(box (place, _)) => {
|
||||
if let Some(local) = place.as_local() {
|
||||
self.map[local].is_some()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
_ => true
|
||||
}
|
||||
|
@ -1682,7 +1682,7 @@ enum Wizard {
|
||||
}
|
||||
|
||||
trait Isengard {
|
||||
fn wizard(w: Wizard) { // error!
|
||||
fn wizard(w: Wizard) { // ok!
|
||||
match w {
|
||||
Wizard::Saruman => {
|
||||
// do something
|
||||
|
@ -33,9 +33,72 @@ mod as_keyword { }
|
||||
//
|
||||
/// Exit early from a loop.
|
||||
///
|
||||
/// The documentation for this keyword is [not yet complete]. Pull requests welcome!
|
||||
/// When `break` is encountered, execution of the associated loop body is
|
||||
/// immediately terminated.
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut last = 0;
|
||||
///
|
||||
/// for x in 1..100 {
|
||||
/// if x > 12 {
|
||||
/// break;
|
||||
/// }
|
||||
/// last = x;
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(last, 12);
|
||||
/// println!("{}", last);
|
||||
/// ```
|
||||
///
|
||||
/// A break expression is normally associated with the innermost loop enclosing the
|
||||
/// `break` but a label can be used to specify which enclosing loop is affected.
|
||||
///
|
||||
///```rust
|
||||
/// 'outer: for i in 1..=5 {
|
||||
/// println!("outer iteration (i): {}", i);
|
||||
///
|
||||
/// 'inner: for j in 1..=200 {
|
||||
/// println!(" inner iteration (j): {}", j);
|
||||
/// if j >= 3 {
|
||||
/// // breaks from inner loop, let's outer loop continue.
|
||||
/// break;
|
||||
/// }
|
||||
/// if i >= 2 {
|
||||
/// // breaks from outer loop, and directly to "Bye".
|
||||
/// break 'outer;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// println!("Bye.");
|
||||
///```
|
||||
///
|
||||
/// When associated with `loop`, a break expression may be used to return a value from that loop.
|
||||
/// This is only valid with `loop` and not with any other type of loop.
|
||||
/// If no value is specified, `break;` returns `()`.
|
||||
/// Every `break` within a loop must return the same type.
|
||||
///
|
||||
/// ```rust
|
||||
/// let (mut a, mut b) = (1, 1);
|
||||
/// let result = loop {
|
||||
/// if b > 10 {
|
||||
/// break b;
|
||||
/// }
|
||||
/// let c = a + b;
|
||||
/// a = b;
|
||||
/// b = c;
|
||||
/// };
|
||||
/// // first number in Fibonacci sequence over 10:
|
||||
/// assert_eq!(result, 13);
|
||||
/// println!("{}", result);
|
||||
/// ```
|
||||
///
|
||||
/// For more details consult the [Reference on "break expression"] and the [Reference on "break and
|
||||
/// loop values"].
|
||||
///
|
||||
/// [Reference on "break expression"]: ../reference/expressions/loop-expr.html#break-expressions
|
||||
/// [Reference on "break and loop values"]:
|
||||
/// ../reference/expressions/loop-expr.html#break-and-loop-values
|
||||
///
|
||||
/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601
|
||||
mod break_keyword { }
|
||||
|
||||
#[doc(keyword = "const")]
|
||||
|
14
src/test/incremental/const-generics/issue-61338.rs
Normal file
14
src/test/incremental/const-generics/issue-61338.rs
Normal file
@ -0,0 +1,14 @@
|
||||
// revisions:rpass1
|
||||
|
||||
#![feature(const_generics)]
|
||||
|
||||
struct Struct<T>(T);
|
||||
|
||||
impl<T, const N: usize> Struct<[T; N]> {
|
||||
fn f() {}
|
||||
fn g() { Self::f(); }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Struct::<[u32; 3]>::g();
|
||||
}
|
16
src/test/incremental/const-generics/issue-61516.rs
Normal file
16
src/test/incremental/const-generics/issue-61516.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// revisions:rpass1
|
||||
|
||||
#![feature(const_generics)]
|
||||
|
||||
struct FakeArray<T, const N: usize>(T);
|
||||
|
||||
impl<T, const N: usize> FakeArray<T, { N }> {
|
||||
fn len(&self) -> usize {
|
||||
N
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let fa = FakeArray::<u32, { 32 }>(1);
|
||||
assert_eq!(fa.len(), 32);
|
||||
}
|
12
src/test/incremental/const-generics/issue-62536.rs
Normal file
12
src/test/incremental/const-generics/issue-62536.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// revisions:cfail1
|
||||
#![feature(const_generics)]
|
||||
//[cfail1]~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
|
||||
|
||||
struct S<T, const N: usize>([T; N]);
|
||||
|
||||
fn f<T, const N: usize>(x: T) -> S<T, {N}> { panic!() }
|
||||
|
||||
fn main() {
|
||||
f(0u8);
|
||||
//[cfail1]~^ ERROR type annotations needed
|
||||
}
|
11
src/test/incremental/const-generics/issue-64087.rs
Normal file
11
src/test/incremental/const-generics/issue-64087.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// revisions:cfail1
|
||||
#![feature(const_generics)]
|
||||
//[cfail1]~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
|
||||
|
||||
fn combinator<T, const S: usize>() -> [T; S] {}
|
||||
//[cfail1]~^ ERROR mismatched types
|
||||
|
||||
fn main() {
|
||||
combinator().into_iter();
|
||||
//[cfail1]~^ ERROR type annotations needed
|
||||
}
|
14
src/test/incremental/const-generics/issue-65623.rs
Normal file
14
src/test/incremental/const-generics/issue-65623.rs
Normal file
@ -0,0 +1,14 @@
|
||||
// revisions:rpass1
|
||||
#![feature(const_generics)]
|
||||
|
||||
pub struct Foo<T, const N: usize>([T; 0]);
|
||||
|
||||
impl<T, const N: usize> Foo<T, {N}> {
|
||||
pub fn new() -> Self {
|
||||
Foo([])
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _: Foo<u32, 0> = Foo::new();
|
||||
}
|
@ -25,7 +25,7 @@ pub fn change_loop_body() {
|
||||
}
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
|
||||
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
|
||||
#[rustc_clean(cfg="cfail3")]
|
||||
pub fn change_loop_body() {
|
||||
let mut _x = 0;
|
||||
|
@ -22,7 +22,7 @@ pub fn change_name() {
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_clean(cfg="cfail2",
|
||||
except="HirBody,mir_built,optimized_mir")]
|
||||
except="HirBody,mir_built")]
|
||||
#[rustc_clean(cfg="cfail3")]
|
||||
pub fn change_name() {
|
||||
let _y = 2u64;
|
||||
@ -86,7 +86,7 @@ pub fn change_mutability_of_slot() {
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_clean(cfg="cfail2",
|
||||
except="HirBody,typeck_tables_of,mir_built,optimized_mir")]
|
||||
except="HirBody,typeck_tables_of,mir_built")]
|
||||
#[rustc_clean(cfg="cfail3")]
|
||||
pub fn change_mutability_of_slot() {
|
||||
let _x: u64 = 0;
|
||||
@ -182,7 +182,7 @@ pub fn add_initializer() {
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_clean(cfg="cfail2",
|
||||
except="HirBody,typeck_tables_of,mir_built,optimized_mir")]
|
||||
except="HirBody,typeck_tables_of,mir_built")]
|
||||
#[rustc_clean(cfg="cfail3")]
|
||||
pub fn add_initializer() {
|
||||
let _x: i16 = 3i16;
|
||||
@ -198,7 +198,7 @@ pub fn change_initializer() {
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_clean(cfg="cfail2",
|
||||
except="HirBody,mir_built,optimized_mir")]
|
||||
except="HirBody,mir_built")]
|
||||
#[rustc_clean(cfg="cfail3")]
|
||||
pub fn change_initializer() {
|
||||
let _x = 5u16;
|
||||
|
@ -25,7 +25,7 @@ pub fn change_loop_body() {
|
||||
}
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
|
||||
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
|
||||
#[rustc_clean(cfg="cfail3")]
|
||||
pub fn change_loop_body() {
|
||||
let mut _x = 0;
|
||||
|
@ -25,7 +25,7 @@ pub fn change_loop_body() {
|
||||
}
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
|
||||
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
|
||||
#[rustc_clean(cfg="cfail3")]
|
||||
pub fn change_loop_body() {
|
||||
let mut _x = 0;
|
||||
|
@ -25,7 +25,7 @@ pub fn change_loop_body() {
|
||||
}
|
||||
|
||||
#[cfg(not(cfail1))]
|
||||
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")]
|
||||
#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")]
|
||||
#[rustc_clean(cfg="cfail3")]
|
||||
pub fn change_loop_body() {
|
||||
let mut _x = 0;
|
||||
|
89
src/test/mir-opt/simplify-locals-removes-unused-consts.rs
Normal file
89
src/test/mir-opt/simplify-locals-removes-unused-consts.rs
Normal file
@ -0,0 +1,89 @@
|
||||
// compile-flags: -C overflow-checks=no
|
||||
|
||||
fn use_zst(_: ((), ())) { }
|
||||
|
||||
struct Temp {
|
||||
x: u8
|
||||
}
|
||||
|
||||
fn use_u8(_: u8) { }
|
||||
|
||||
fn main() {
|
||||
let ((), ()) = ((), ());
|
||||
use_zst(((), ()));
|
||||
|
||||
use_u8((Temp { x : 40 }).x + 2);
|
||||
}
|
||||
|
||||
// END RUST SOURCE
|
||||
|
||||
// START rustc.main.SimplifyLocals.before.mir
|
||||
// let mut _0: ();
|
||||
// let mut _1: ((), ());
|
||||
// let mut _2: ();
|
||||
// let mut _3: ();
|
||||
// let _4: ();
|
||||
// let mut _5: ((), ());
|
||||
// let mut _6: ();
|
||||
// let mut _7: ();
|
||||
// let _8: ();
|
||||
// let mut _9: u8;
|
||||
// let mut _10: u8;
|
||||
// let mut _11: Temp;
|
||||
// scope 1 {
|
||||
// }
|
||||
// bb0: {
|
||||
// StorageLive(_1);
|
||||
// StorageLive(_2);
|
||||
// _2 = const Scalar(<ZST>) : ();
|
||||
// StorageLive(_3);
|
||||
// _3 = const Scalar(<ZST>) : ();
|
||||
// _1 = const Scalar(<ZST>) : ((), ());
|
||||
// StorageDead(_3);
|
||||
// StorageDead(_2);
|
||||
// StorageDead(_1);
|
||||
// StorageLive(_4);
|
||||
// StorageLive(_6);
|
||||
// _6 = const Scalar(<ZST>) : ();
|
||||
// StorageLive(_7);
|
||||
// _7 = const Scalar(<ZST>) : ();
|
||||
// StorageDead(_7);
|
||||
// StorageDead(_6);
|
||||
// _4 = const use_zst(const Scalar(<ZST>) : ((), ())) -> bb1;
|
||||
// }
|
||||
// bb1: {
|
||||
// StorageDead(_4);
|
||||
// StorageLive(_8);
|
||||
// StorageLive(_10);
|
||||
// StorageLive(_11);
|
||||
// _11 = const Scalar(0x28) : Temp;
|
||||
// _10 = const 40u8;
|
||||
// StorageDead(_10);
|
||||
// _8 = const use_u8(const 42u8) -> bb2;
|
||||
// }
|
||||
// bb2: {
|
||||
// StorageDead(_11);
|
||||
// StorageDead(_8);
|
||||
// return;
|
||||
// }
|
||||
// END rustc.main.SimplifyLocals.before.mir
|
||||
// START rustc.main.SimplifyLocals.after.mir
|
||||
// let mut _0: ();
|
||||
// let _1: ();
|
||||
// let _2: ();
|
||||
// scope 1 {
|
||||
// }
|
||||
// bb0: {
|
||||
// StorageLive(_1);
|
||||
// _1 = const use_zst(const Scalar(<ZST>) : ((), ())) -> bb1;
|
||||
// }
|
||||
// bb1: {
|
||||
// StorageDead(_1);
|
||||
// StorageLive(_2);
|
||||
// _2 = const use_u8(const 42u8) -> bb2;
|
||||
// }
|
||||
// bb2: {
|
||||
// StorageDead(_2);
|
||||
// return;
|
||||
// }
|
||||
// END rustc.main.SimplifyLocals.after.mir
|
@ -1,5 +1,7 @@
|
||||
// compile-flags: -Zmir-opt-level=0
|
||||
|
||||
fn main() {
|
||||
std::ptr::drop_in_place::<[String]> as unsafe fn(_);
|
||||
let _fn = std::ptr::drop_in_place::<[String]> as unsafe fn(_);
|
||||
}
|
||||
|
||||
// END RUST SOURCE
|
||||
|
Loading…
x
Reference in New Issue
Block a user