mirror of
https://codeberg.org/kiss-community/repo
synced 2024-12-21 14:50:07 -07:00
rust: add patches for 1.82/llvm19 regressions
This commit is contained in:
parent
46cc285da9
commit
e5cf8580f5
@ -2,17 +2,14 @@
|
|||||||
|
|
||||||
export DESTDIR="$1"
|
export DESTDIR="$1"
|
||||||
|
|
||||||
patch -p1 < fix-curl.patch
|
for p in *.patch; do
|
||||||
|
patch -p1 < "$p"
|
||||||
|
done
|
||||||
|
|
||||||
# Instruct the compiler to trim absolute paths in resulting binaries and instead
|
# Instruct the compiler to trim absolute paths in resulting binaries and instead
|
||||||
# change them to relative paths ($PWD/... ./...).
|
# change them to relative paths ($PWD/... ./...).
|
||||||
export RUSTFLAGS="$RUSTFLAGS --remap-path-prefix=$PWD=."
|
export RUSTFLAGS="$RUSTFLAGS --remap-path-prefix=$PWD=."
|
||||||
|
|
||||||
# Set shared linking as the default.
|
|
||||||
sed 's/\(crt_static_default = \)true/\1false/' \
|
|
||||||
compiler/rustc_target/src/spec/base/linux_musl.rs > _
|
|
||||||
mv -f _ compiler/rustc_target/src/spec/base/linux_musl.rs
|
|
||||||
|
|
||||||
sed 's/\("files":{\)[^}]*/\1/' \
|
sed 's/\("files":{\)[^}]*/\1/' \
|
||||||
vendor/curl-sys-0.4.52+curl-7.81.0/.cargo-checksum.json > _
|
vendor/curl-sys-0.4.52+curl-7.81.0/.cargo-checksum.json > _
|
||||||
mv -f _ vendor/curl-sys-0.4.52+curl-7.81.0/.cargo-checksum.json
|
mv -f _ vendor/curl-sys-0.4.52+curl-7.81.0/.cargo-checksum.json
|
||||||
|
@ -3,3 +3,5 @@ bfe3cbea8ad275258a7a9d0bd46f9dc7a6d8adbaabe6257507e47c1dd4c129a52e
|
|||||||
c5bc0fed9d73f8f5e3fddc7d0366b067e7c3ffce0df5ceb8c80d77537b929e9e86
|
c5bc0fed9d73f8f5e3fddc7d0366b067e7c3ffce0df5ceb8c80d77537b929e9e86
|
||||||
ea13ff0a3533fc726daf004852371bff500ef1464617078ef04a0d63302532bf50
|
ea13ff0a3533fc726daf004852371bff500ef1464617078ef04a0d63302532bf50
|
||||||
c03ac16dbb7aecb143e7cd701feb480862af63dbb044e0f0637eaff93758937fca
|
c03ac16dbb7aecb143e7cd701feb480862af63dbb044e0f0637eaff93758937fca
|
||||||
|
90e39b7bc45b0d389a211a857374b121f52a173ceeecb48df345c77ce0c18a4f66
|
||||||
|
891504d6dd8b3d7f463df9140850cd982a263ab485faf171496d3862c76b00bec2
|
||||||
|
57
extra/rust/patches/fix-linux_musl_base.patch
Normal file
57
extra/rust/patches/fix-linux_musl_base.patch
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
From: Jakub Jirutka <jakub@jirutka.cz>
|
||||||
|
Date: Sat, 08 Aug 2016 15:06:00 +0200
|
||||||
|
Subject: [PATCH] Fix linux_musl_base for native musl host
|
||||||
|
|
||||||
|
See https://github.com/rust-lang/rust/pull/40113
|
||||||
|
|
||||||
|
--- a/compiler/rustc_target/src/spec/base/linux_musl.rs
|
||||||
|
+++ b/compiler/rustc_target/src/spec/base/linux_musl.rs
|
||||||
|
@@ -1,15 +1,12 @@
|
||||||
|
-use crate::spec::{base, crt_objects, LinkSelfContainedDefault, TargetOptions};
|
||||||
|
+use crate::spec::{base, TargetOptions};
|
||||||
|
|
||||||
|
pub fn opts() -> TargetOptions {
|
||||||
|
let mut base = base::linux::opts();
|
||||||
|
|
||||||
|
base.env = "musl".into();
|
||||||
|
- base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained();
|
||||||
|
- base.post_link_objects_self_contained = crt_objects::post_musl_self_contained();
|
||||||
|
- base.link_self_contained = LinkSelfContainedDefault::InferredForMusl;
|
||||||
|
|
||||||
|
// These targets statically link libc by default
|
||||||
|
- base.crt_static_default = true;
|
||||||
|
+ base.crt_static_default = false;
|
||||||
|
|
||||||
|
base
|
||||||
|
}
|
||||||
|
--- a/compiler/rustc_target/src/spec/crt_objects.rs
|
||||||
|
+++ b/compiler/rustc_target/src/spec/crt_objects.rs
|
||||||
|
@@ -58,28 +61,6 @@
|
||||||
|
(LinkOutputKind::StaticPicExe, &[obj]),
|
||||||
|
(LinkOutputKind::DynamicDylib, &[obj]),
|
||||||
|
(LinkOutputKind::StaticDylib, &[obj]),
|
||||||
|
- ])
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-pub(super) fn pre_musl_self_contained() -> CrtObjects {
|
||||||
|
- new(&[
|
||||||
|
- (LinkOutputKind::DynamicNoPicExe, &["crt1.o", "crti.o", "crtbegin.o"]),
|
||||||
|
- (LinkOutputKind::DynamicPicExe, &["Scrt1.o", "crti.o", "crtbeginS.o"]),
|
||||||
|
- (LinkOutputKind::StaticNoPicExe, &["crt1.o", "crti.o", "crtbegin.o"]),
|
||||||
|
- (LinkOutputKind::StaticPicExe, &["rcrt1.o", "crti.o", "crtbeginS.o"]),
|
||||||
|
- (LinkOutputKind::DynamicDylib, &["crti.o", "crtbeginS.o"]),
|
||||||
|
- (LinkOutputKind::StaticDylib, &["crti.o", "crtbeginS.o"]),
|
||||||
|
- ])
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-pub(super) fn post_musl_self_contained() -> CrtObjects {
|
||||||
|
- new(&[
|
||||||
|
- (LinkOutputKind::DynamicNoPicExe, &["crtend.o", "crtn.o"]),
|
||||||
|
- (LinkOutputKind::DynamicPicExe, &["crtendS.o", "crtn.o"]),
|
||||||
|
- (LinkOutputKind::StaticNoPicExe, &["crtend.o", "crtn.o"]),
|
||||||
|
- (LinkOutputKind::StaticPicExe, &["crtendS.o", "crtn.o"]),
|
||||||
|
- (LinkOutputKind::DynamicDylib, &["crtendS.o", "crtn.o"]),
|
||||||
|
- (LinkOutputKind::StaticDylib, &["crtendS.o", "crtn.o"]),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
242
extra/rust/patches/fix-llvm19.patch
Normal file
242
extra/rust/patches/fix-llvm19.patch
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
From 1dc106121b62562ead6e7d612fa136dc4b35cd5d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Kyle Huey <khuey@kylehuey.com>
|
||||||
|
Date: Mon, 4 Nov 2024 11:38:14 -0800
|
||||||
|
Subject: [PATCH] Add discriminators to DILocations when multiple functions are
|
||||||
|
inlined into a single point.
|
||||||
|
|
||||||
|
LLVM does not expect to ever see multiple dbg_declares for the same variable at the same
|
||||||
|
location with different values. proc-macros make it possible for arbitrary code,
|
||||||
|
including multiple calls that get inlined, to happen at any given location in the source
|
||||||
|
code. Add discriminators when that happens so these locations are different to LLVM.
|
||||||
|
|
||||||
|
This may interfere with the AddDiscriminators pass in LLVM, which is added by the
|
||||||
|
unstable flag -Zdebug-info-for-profiling.
|
||||||
|
|
||||||
|
Fixes #131944
|
||||||
|
---
|
||||||
|
.../src/debuginfo/create_scope_map.rs | 60 ++++++++++++++++++-
|
||||||
|
compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 4 ++
|
||||||
|
.../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 8 +++
|
||||||
|
.../auxiliary/macro_def.rs | 11 ++++
|
||||||
|
.../mir_inlined_twice_var_locs.rs | 53 ++++++++++++++++
|
||||||
|
5 files changed, 133 insertions(+), 3 deletions(-)
|
||||||
|
create mode 100644 tests/codegen/debuginfo-proc-macro/auxiliary/macro_def.rs
|
||||||
|
create mode 100644 tests/codegen/debuginfo-proc-macro/mir_inlined_twice_var_locs.rs
|
||||||
|
|
||||||
|
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
|
||||||
|
index ac6c2fb1b83a6..0f1909486ec7e 100644
|
||||||
|
--- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
|
||||||
|
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
|
||||||
|
@@ -1,11 +1,15 @@
|
||||||
|
+use std::collections::hash_map::Entry;
|
||||||
|
+
|
||||||
|
use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext};
|
||||||
|
use rustc_codegen_ssa::traits::*;
|
||||||
|
+use rustc_data_structures::fx::FxHashMap;
|
||||||
|
use rustc_index::bit_set::BitSet;
|
||||||
|
use rustc_index::Idx;
|
||||||
|
use rustc_middle::mir::{Body, SourceScope};
|
||||||
|
use rustc_middle::ty::layout::FnAbiOf;
|
||||||
|
use rustc_middle::ty::{self, Instance};
|
||||||
|
use rustc_session::config::DebugInfo;
|
||||||
|
+use rustc_span::BytePos;
|
||||||
|
|
||||||
|
use super::metadata::file_metadata;
|
||||||
|
use super::utils::DIB;
|
||||||
|
@@ -37,10 +41,20 @@ pub(crate) fn compute_mir_scopes<'ll, 'tcx>(
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let mut instantiated = BitSet::new_empty(mir.source_scopes.len());
|
||||||
|
+ let mut discriminators = FxHashMap::default();
|
||||||
|
// Instantiate all scopes.
|
||||||
|
for idx in 0..mir.source_scopes.len() {
|
||||||
|
let scope = SourceScope::new(idx);
|
||||||
|
- make_mir_scope(cx, instance, mir, &variables, debug_context, &mut instantiated, scope);
|
||||||
|
+ make_mir_scope(
|
||||||
|
+ cx,
|
||||||
|
+ instance,
|
||||||
|
+ mir,
|
||||||
|
+ &variables,
|
||||||
|
+ debug_context,
|
||||||
|
+ &mut instantiated,
|
||||||
|
+ &mut discriminators,
|
||||||
|
+ scope,
|
||||||
|
+ );
|
||||||
|
}
|
||||||
|
assert!(instantiated.count() == mir.source_scopes.len());
|
||||||
|
}
|
||||||
|
@@ -52,6 +66,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||||
|
variables: &Option<BitSet<SourceScope>>,
|
||||||
|
debug_context: &mut FunctionDebugContext<'tcx, &'ll DIScope, &'ll DILocation>,
|
||||||
|
instantiated: &mut BitSet<SourceScope>,
|
||||||
|
+ discriminators: &mut FxHashMap<BytePos, u32>,
|
||||||
|
scope: SourceScope,
|
||||||
|
) {
|
||||||
|
if instantiated.contains(scope) {
|
||||||
|
@@ -60,7 +75,16 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||||
|
|
||||||
|
let scope_data = &mir.source_scopes[scope];
|
||||||
|
let parent_scope = if let Some(parent) = scope_data.parent_scope {
|
||||||
|
- make_mir_scope(cx, instance, mir, variables, debug_context, instantiated, parent);
|
||||||
|
+ make_mir_scope(
|
||||||
|
+ cx,
|
||||||
|
+ instance,
|
||||||
|
+ mir,
|
||||||
|
+ variables,
|
||||||
|
+ debug_context,
|
||||||
|
+ instantiated,
|
||||||
|
+ discriminators,
|
||||||
|
+ parent,
|
||||||
|
+ );
|
||||||
|
debug_context.scopes[parent]
|
||||||
|
} else {
|
||||||
|
// The root is the function itself.
|
||||||
|
@@ -117,7 +141,37 @@ fn make_mir_scope<'ll, 'tcx>(
|
||||||
|
// FIXME(eddyb) this doesn't account for the macro-related
|
||||||
|
// `Span` fixups that `rustc_codegen_ssa::mir::debuginfo` does.
|
||||||
|
let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span);
|
||||||
|
- cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span)
|
||||||
|
+ let loc = cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span);
|
||||||
|
+
|
||||||
|
+ // NB: In order to produce proper debug info for variables (particularly
|
||||||
|
+ // arguments) in multiply-inline functions, LLVM expects to see a single
|
||||||
|
+ // DILocalVariable with multiple different DILocations in the IR. While
|
||||||
|
+ // the source information for each DILocation would be identical, their
|
||||||
|
+ // inlinedAt attributes will be unique to the particular callsite.
|
||||||
|
+ //
|
||||||
|
+ // We generate DILocations here based on the callsite's location in the
|
||||||
|
+ // source code. A single location in the source code usually can't
|
||||||
|
+ // produce multiple distinct calls so this mostly works, until
|
||||||
|
+ // proc-macros get involved. A proc-macro can generate multiple calls
|
||||||
|
+ // at the same span, which breaks the assumption that we're going to
|
||||||
|
+ // produce a unique DILocation for every scope we process here. We
|
||||||
|
+ // have to explicitly add discriminators if we see inlines into the
|
||||||
|
+ // same source code location.
|
||||||
|
+ //
|
||||||
|
+ // Note further that we can't key this hashtable on the span itself,
|
||||||
|
+ // because these spans could have distinct SyntaxContexts. We have
|
||||||
|
+ // to key on exactly what we're giving to LLVM.
|
||||||
|
+ match discriminators.entry(callsite_span.lo()) {
|
||||||
|
+ Entry::Occupied(mut o) => {
|
||||||
|
+ *o.get_mut() += 1;
|
||||||
|
+ unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) }
|
||||||
|
+ .expect("Failed to encode discriminator in DILocation")
|
||||||
|
+ }
|
||||||
|
+ Entry::Vacant(v) => {
|
||||||
|
+ v.insert(0);
|
||||||
|
+ loc
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
});
|
||||||
|
|
||||||
|
debug_context.scopes[scope] = DebugScope {
|
||||||
|
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
|
||||||
|
index 3d2e270a3868e..75a5ec44c2285 100644
|
||||||
|
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
|
||||||
|
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
|
||||||
|
@@ -2174,6 +2174,10 @@ unsafe extern "C" {
|
||||||
|
Scope: &'a DIScope,
|
||||||
|
InlinedAt: Option<&'a DILocation>,
|
||||||
|
) -> &'a DILocation;
|
||||||
|
+ pub fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>(
|
||||||
|
+ Location: &'a DILocation,
|
||||||
|
+ BD: c_uint,
|
||||||
|
+ ) -> Option<&'a DILocation>;
|
||||||
|
pub fn LLVMRustDIBuilderCreateOpDeref() -> u64;
|
||||||
|
pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> u64;
|
||||||
|
pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> u64;
|
||||||
|
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
|
||||||
|
index 9330c83b7f230..cd70c3f266920 100644
|
||||||
|
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
|
||||||
|
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
|
||||||
|
@@ -1305,6 +1305,14 @@ LLVMRustDIBuilderCreateDebugLocation(unsigned Line, unsigned Column,
|
||||||
|
return wrap(Loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
+extern "C" LLVMMetadataRef
|
||||||
|
+LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location,
|
||||||
|
+ unsigned BD) {
|
||||||
|
+ DILocation *Loc = unwrapDIPtr<DILocation>(Location);
|
||||||
|
+ auto NewLoc = Loc->cloneWithBaseDiscriminator(BD);
|
||||||
|
+ return wrap(NewLoc.has_value() ? NewLoc.value() : nullptr);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
extern "C" uint64_t LLVMRustDIBuilderCreateOpDeref() {
|
||||||
|
return dwarf::DW_OP_deref;
|
||||||
|
}
|
||||||
|
diff --git a/tests/codegen/debuginfo-proc-macro/auxiliary/macro_def.rs b/tests/codegen/debuginfo-proc-macro/auxiliary/macro_def.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000..159ecfd09743d
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/codegen/debuginfo-proc-macro/auxiliary/macro_def.rs
|
||||||
|
@@ -0,0 +1,11 @@
|
||||||
|
+//@ force-host
|
||||||
|
+//@ no-prefer-dynamic
|
||||||
|
+#![crate_type = "proc-macro"]
|
||||||
|
+
|
||||||
|
+extern crate proc_macro;
|
||||||
|
+use proc_macro::*;
|
||||||
|
+
|
||||||
|
+#[proc_macro]
|
||||||
|
+pub fn square_twice(_item: TokenStream) -> TokenStream {
|
||||||
|
+ "(square(env::vars().count() as i32), square(env::vars().count() as i32))".parse().unwrap()
|
||||||
|
+}
|
||||||
|
diff --git a/tests/codegen/debuginfo-proc-macro/mir_inlined_twice_var_locs.rs b/tests/codegen/debuginfo-proc-macro/mir_inlined_twice_var_locs.rs
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000..c3858044c0c9f
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/tests/codegen/debuginfo-proc-macro/mir_inlined_twice_var_locs.rs
|
||||||
|
@@ -0,0 +1,53 @@
|
||||||
|
+//@ min-llvm-version: 19
|
||||||
|
+//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 -Zmir-enable-passes=+Inline
|
||||||
|
+// MSVC is different because of the individual allocas.
|
||||||
|
+//@ ignore-msvc
|
||||||
|
+
|
||||||
|
+//@ aux-build:macro_def.rs
|
||||||
|
+
|
||||||
|
+// Find the variable.
|
||||||
|
+// CHECK-DAG: ![[#var_dbg:]] = !DILocalVariable(name: "n",{{( arg: 1,)?}} scope: ![[#var_scope:]]
|
||||||
|
+
|
||||||
|
+// Find both dbg_declares. These will proceed the variable metadata, of course, so we're looking
|
||||||
|
+// backwards.
|
||||||
|
+// CHECK-DAG: dbg_declare(ptr %n.dbg.spill{{[0-9]}}, ![[#var_dbg]], !DIExpression(), ![[#var_loc2:]])
|
||||||
|
+// CHECK-DAG: dbg_declare(ptr %n.dbg.spill, ![[#var_dbg]], !DIExpression(), ![[#var_loc1:]])
|
||||||
|
+
|
||||||
|
+// Find the first location definition, looking forwards again.
|
||||||
|
+// CHECK: ![[#var_loc1]] = !DILocation
|
||||||
|
+// CHECK-SAME: scope: ![[#var_scope:]], inlinedAt: ![[#var_inlinedAt1:]]
|
||||||
|
+
|
||||||
|
+// Find the first location's inlinedAt
|
||||||
|
+// NB: If we fail here it's *probably* because we failed to produce two
|
||||||
|
+// different locations and ended up reusing an earlier one.
|
||||||
|
+// CHECK: ![[#var_inlinedAt1]] = !DILocation
|
||||||
|
+// CHECK-SAME: scope: ![[var_inlinedAt1_scope:]]
|
||||||
|
+
|
||||||
|
+// Find the second location definition, still looking forwards.
|
||||||
|
+// NB: If we failed to produce two different locations, the test will
|
||||||
|
+// definitely fail by this point (if it hasn't already) because we won't
|
||||||
|
+// be able to find the same line again.
|
||||||
|
+// CHECK: ![[#var_loc2]] = !DILocation
|
||||||
|
+// CHECK-SAME: scope: ![[#var_scope]], inlinedAt: ![[#var_inlinedAt2:]]
|
||||||
|
+
|
||||||
|
+// Find the second location's inlinedAt.
|
||||||
|
+// CHECK: ![[#var_inlinedAt2]] = !DILocation
|
||||||
|
+// CHECK-SAME: scope: ![[#var_inlinedAt2_scope:]]
|
||||||
|
+
|
||||||
|
+// Finally, check that a discriminator was emitted for the second scope.
|
||||||
|
+// FIXMEkhuey ideally we would check that *either* scope has a discriminator
|
||||||
|
+// but I don't know that it's possible to check that with FileCheck.
|
||||||
|
+// CHECK: ![[#var_inlinedAt2_scope]] = !DILexicalBlockFile
|
||||||
|
+// CHECK-SAME: discriminator: [[#]]
|
||||||
|
+extern crate macro_def;
|
||||||
|
+
|
||||||
|
+use std::env;
|
||||||
|
+
|
||||||
|
+fn square(n: i32) -> i32 {
|
||||||
|
+ n * n
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+fn main() {
|
||||||
|
+ let (z1, z2) = macro_def::square_twice!();
|
||||||
|
+ println!("{z1} == {z2}");
|
||||||
|
+}
|
@ -3,3 +3,5 @@ https://static.rust-lang.org/dist/2024-09-05/rust-std-1.81.0-x86_64-unknown-linu
|
|||||||
https://static.rust-lang.org/dist/2024-09-05/rustc-1.81.0-x86_64-unknown-linux-musl.tar.xz?no-extract
|
https://static.rust-lang.org/dist/2024-09-05/rustc-1.81.0-x86_64-unknown-linux-musl.tar.xz?no-extract
|
||||||
https://static.rust-lang.org/dist/2024-09-05/cargo-1.81.0-x86_64-unknown-linux-musl.tar.xz?no-extract
|
https://static.rust-lang.org/dist/2024-09-05/cargo-1.81.0-x86_64-unknown-linux-musl.tar.xz?no-extract
|
||||||
patches/fix-curl.patch
|
patches/fix-curl.patch
|
||||||
|
patches/fix-linux_musl_base.patch
|
||||||
|
patches/fix-llvm19.patch
|
||||||
|
Loading…
Reference in New Issue
Block a user