mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 15:27:45 +03:00
Merge remote-tracking branch 'origin/trunk' into precedence-error
This commit is contained in:
commit
604dbf7215
@ -32,7 +32,9 @@ Create `~/.config/cargo` and add this to it:
|
||||
|
||||
```
|
||||
[build]
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
|
||||
# Link with lld, per https://github.com/rust-lang/rust/issues/39915#issuecomment-538049306
|
||||
# Use target-cpu=native, per https://deterministic.space/high-performance-rust.html
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=lld", "-C", "target-cpu=native"]
|
||||
```
|
||||
|
||||
Then install `lld` version 9 (e.g. with `$ sudo apt-get install lld-9`)
|
||||
|
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -568,6 +568,10 @@ dependencies = [
|
||||
"roc_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_builtins_bitcode"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "roc_can"
|
||||
version = "0.1.0"
|
||||
|
@ -10,6 +10,7 @@ members = [
|
||||
"compiler/types",
|
||||
"compiler/uniq",
|
||||
"compiler/builtins",
|
||||
"compiler/builtins/bitcode",
|
||||
"compiler/constrain",
|
||||
"compiler/unify",
|
||||
"compiler/solve",
|
||||
@ -23,3 +24,7 @@ members = [
|
||||
"cli"
|
||||
]
|
||||
|
||||
# Optimizations based on https://deterministic.space/high-performance-rust.html
|
||||
[profile.release]
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
mkdir -p $HOME/.cargo
|
||||
echo -e "[build]\nrustflags = [\"-C\", \"link-arg=-fuse-ld=lld\"]" > $HOME/.cargo/config
|
||||
echo -e "[build]\nrustflags = [\"-C\", \"link-arg=-fuse-ld=lld\", \"-C\", \"target-cpu=native\"]" > $HOME/.cargo/config
|
||||
|
||||
ln -s /usr/bin/lld-8 /usr/local/bin/ld.lld
|
||||
|
@ -8,7 +8,9 @@ use inkwell::passes::PassManager;
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::OptimizationLevel;
|
||||
use roc_collections::all::ImMap;
|
||||
use roc_gen::llvm::build::{build_proc, build_proc_header, get_call_conventions};
|
||||
use roc_gen::llvm::build::{
|
||||
build_proc, build_proc_header, get_call_conventions, module_from_builtins,
|
||||
};
|
||||
use roc_gen::llvm::convert::basic_type_from_layout;
|
||||
use roc_mono::expr::{Expr, Procs};
|
||||
use roc_mono::layout::Layout;
|
||||
@ -65,7 +67,7 @@ fn gen(src: &str, target: Triple, dest_filename: &Path) {
|
||||
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||
|
||||
let context = Context::create();
|
||||
let module = context.create_module("app");
|
||||
let module = module_from_builtins(&context, "app");
|
||||
let builder = context.create_builder();
|
||||
let fpm = PassManager::create(&module);
|
||||
|
||||
@ -74,17 +76,14 @@ fn gen(src: &str, target: Triple, dest_filename: &Path) {
|
||||
fpm.initialize();
|
||||
|
||||
// Compute main_fn_type before moving subs to Env
|
||||
let pointer_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
let layout = Layout::from_content(&arena, content, &subs, pointer_bytes)
|
||||
.unwrap_or_else(|err| panic!("Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}", err, subs));
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
let layout = Layout::from_content(&arena, content, &subs, ptr_bytes).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Code gen error in test: could not convert to layout. Err was {:?} and Subs were {:?}",
|
||||
err, subs
|
||||
)
|
||||
});
|
||||
|
||||
let execution_engine = module
|
||||
.create_jit_execution_engine(OptimizationLevel::None)
|
||||
.expect("Error creating JIT execution engine for test");
|
||||
|
||||
let ptr_bytes = execution_engine
|
||||
.get_target_data()
|
||||
.get_pointer_byte_size(None);
|
||||
let main_fn_type =
|
||||
basic_type_from_layout(&arena, &context, &layout, ptr_bytes).fn_type(&[], false);
|
||||
let main_fn_name = "$Test.main";
|
||||
@ -109,7 +108,7 @@ fn gen(src: &str, target: Triple, dest_filename: &Path) {
|
||||
&mut procs,
|
||||
home,
|
||||
&mut ident_ids,
|
||||
pointer_bytes,
|
||||
ptr_bytes,
|
||||
);
|
||||
|
||||
// Put this module's ident_ids back in the interns, so we can use them in env.
|
||||
|
13
compiler/builtins/bitcode/Cargo.toml
Normal file
13
compiler/builtins/bitcode/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "roc_builtins_bitcode"
|
||||
version = "0.1.0"
|
||||
authors = ["Richard Feldman <richard.t.feldman@gmail.com>"]
|
||||
repository = "https://github.com/rtfeldman/roc"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
description = "Generate LLVM bitcode for Roc builtins"
|
||||
license = "Apache-2.0"
|
||||
|
||||
|
||||
[build]
|
||||
target-dir="~/code/roc/copmiler/builtins/bitcode/"
|
56
compiler/builtins/bitcode/README.md
Normal file
56
compiler/builtins/bitcode/README.md
Normal file
@ -0,0 +1,56 @@
|
||||
# Bitcode for Builtins
|
||||
|
||||
Roc's builtins are implemented in the compiler using LLVM only.
|
||||
When their implementations are simple enough (e.g. addition), they
|
||||
can be implemented directly in Inkwell.
|
||||
|
||||
When their implementations are complex enough, it's nicer to
|
||||
implement them in a higher-level language like Rust, compile the
|
||||
result to LLVM bitcode, and import that bitcode into the compiler.
|
||||
|
||||
Here is the process for doing that.
|
||||
|
||||
## Building the bitcode
|
||||
|
||||
The source we'll use to generate the bitcode is in `src/lib.rs` in this directory.
|
||||
|
||||
To generate the bitcode, `cd` into `compiler/builtins/bitcode/` and run:
|
||||
|
||||
```bash
|
||||
$ cargo rustc --release --lib -- --emit=llvm-bc
|
||||
```
|
||||
|
||||
Then look in the root `roc` source directory under `target/release/deps/` for a file
|
||||
with a name like `roc_builtins_bitcode-8da0901c58a73ebf.bc` - except
|
||||
probably with a different hash before the `.bc`. If there's more than one
|
||||
`*.bc` file in that directory, delete the whole `deps/` directory and re-run
|
||||
the `cargo rustc` command above to regenerate it.
|
||||
|
||||
> If you want to take a look at the human-readable LLVM IR rather than the
|
||||
> bitcode, run this instead and look for a `.ll` file instead of a `.bc` file:
|
||||
>
|
||||
> ```bash
|
||||
> $ cargo rustc --release --lib -- --emit=llvm-ir
|
||||
> ```
|
||||
|
||||
## Importing the bitcode
|
||||
|
||||
The bitcode is a bunch of bytes that aren't particularly human-readable.
|
||||
Since Roc is designed to be distributed as a single binary, these bytes
|
||||
need to be included in the raw source somewhere.
|
||||
|
||||
The `llvm/src/build.rs` file statically imports these raw bytes
|
||||
using the [`include_bytes!` macro](https://doc.rust-lang.org/std/macro.include_bytes.html),
|
||||
so we just need to move the `.bc` file from the previous step to the correct
|
||||
location.
|
||||
|
||||
The current `.bc` file is located at:
|
||||
|
||||
```
|
||||
compiler/gen/src/llvm/builtins.bc
|
||||
```
|
||||
|
||||
...so you want to overwrite it with the new `.bc` file in `target/deps/`
|
||||
|
||||
Once that's done, `git status` should show that the `builtins.bc` file
|
||||
has been changed. Commit that change and you're done!
|
10
compiler/builtins/bitcode/src/lib.rs
Normal file
10
compiler/builtins/bitcode/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
||||
#![crate_type = "lib"]
|
||||
#![no_std]
|
||||
|
||||
// TODO replace this with a normal Inkwell build_cast call - this was just
|
||||
// used as a proof of concept for getting bitcode importing working!
|
||||
|
||||
#[no_mangle]
|
||||
pub fn i64_to_f64_(num: i64) -> f64 {
|
||||
num as f64
|
||||
}
|
@ -2,6 +2,7 @@ use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
use inkwell::memory_buffer::MemoryBuffer;
|
||||
use inkwell::module::{Linkage, Module};
|
||||
use inkwell::passes::PassManager;
|
||||
use inkwell::types::{BasicTypeEnum, IntType, StructType};
|
||||
@ -47,6 +48,14 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn module_from_builtins<'ctx>(ctx: &'ctx Context, module_name: &str) -> Module<'ctx> {
|
||||
let memory_buffer =
|
||||
MemoryBuffer::create_from_memory_range(include_bytes!("builtins.bc"), module_name);
|
||||
|
||||
Module::parse_bitcode_from_buffer(&memory_buffer, ctx)
|
||||
.unwrap_or_else(|err| panic!("Unable to import builtins bitcode. LLVM error: {:?}", err))
|
||||
}
|
||||
|
||||
pub fn add_passes(fpm: &PassManager<FunctionValue<'_>>) {
|
||||
// tail-call elimination is always on
|
||||
fpm.add_instruction_combining_pass();
|
||||
@ -1069,6 +1078,31 @@ fn call_with_args<'a, 'ctx, 'env>(
|
||||
|
||||
BasicValueEnum::IntValue(int_val)
|
||||
}
|
||||
Symbol::NUM_TO_FLOAT => {
|
||||
// TODO specialize this to be not just for i64!
|
||||
let builtin_fn_name = "i64_to_f64_";
|
||||
|
||||
let fn_val = env
|
||||
.module
|
||||
.get_function(builtin_fn_name)
|
||||
.unwrap_or_else(|| panic!("Unrecognized builtin function: {:?} - if you're working on the Roc compiler, do you need to rebuild the bitcode? See compiler/builtins/bitcode/README.md", builtin_fn_name));
|
||||
|
||||
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(args.len(), env.arena);
|
||||
|
||||
for (arg, _layout) in args.iter() {
|
||||
arg_vals.push(*arg);
|
||||
}
|
||||
|
||||
let call = env
|
||||
.builder
|
||||
.build_call(fn_val, arg_vals.into_bump_slice(), "call_builtin");
|
||||
|
||||
call.set_call_convention(DEFAULT_CALLING_CONVENTION);
|
||||
|
||||
call.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call for builtin {:?}", symbol))
|
||||
}
|
||||
Symbol::FLOAT_EQ => {
|
||||
debug_assert!(args.len() == 2);
|
||||
|
||||
|
BIN
compiler/gen/src/llvm/builtins.bc
Normal file
BIN
compiler/gen/src/llvm/builtins.bc
Normal file
Binary file not shown.
@ -344,4 +344,17 @@ mod gen_builtins {
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_to_float() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
Num.toFloat 0x9
|
||||
"#
|
||||
),
|
||||
9.0,
|
||||
f64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ macro_rules! assert_llvm_evals_to {
|
||||
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||
|
||||
let context = Context::create();
|
||||
let module = context.create_module("app");
|
||||
let module = roc_gen::llvm::build::module_from_builtins(&context, "app");
|
||||
let builder = context.create_builder();
|
||||
let fpm = inkwell::passes::PassManager::create(&module);
|
||||
|
||||
@ -142,7 +142,7 @@ macro_rules! assert_opt_evals_to {
|
||||
let (content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
||||
|
||||
let context = Context::create();
|
||||
let module = context.create_module("app");
|
||||
let module = roc_gen::llvm::build::module_from_builtins(&context, "app");
|
||||
let builder = context.create_builder();
|
||||
let fpm = PassManager::create(&module);
|
||||
|
||||
|
@ -133,6 +133,6 @@ impl std::fmt::Display for BinOp {
|
||||
Pizza => "|>",
|
||||
};
|
||||
|
||||
write!(f, "({})", as_str)
|
||||
write!(f, "{}", as_str)
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ edition = "2018"
|
||||
roc_collections = { path = "../collections" }
|
||||
roc_region = { path = "../region" }
|
||||
roc_module = { path = "../module" }
|
||||
roc_parse = { path = "../parse" }
|
||||
roc_problem = { path = "../problem" }
|
||||
roc_types = { path = "../types" }
|
||||
roc_load = { path = "../load" }
|
||||
@ -15,7 +16,6 @@ roc_can = { path = "../can" }
|
||||
roc_solve = { path = "../solve" }
|
||||
inlinable_string = "0.1.0"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
roc_constrain = { path = "../constrain" }
|
||||
roc_builtins = { path = "../builtins" }
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::report::ReportText::{Batch, Module, Region, Value};
|
||||
use crate::report::ReportText::{Batch, BinOp, Module, Region, Value};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
||||
@ -27,6 +27,7 @@ pub struct Palette {
|
||||
pub line_number: Color,
|
||||
pub gutter_bar: Color,
|
||||
pub module_name: Color,
|
||||
pub binop: Color,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -52,6 +53,7 @@ pub const TEST_PALETTE: Palette = Palette {
|
||||
line_number: Color::Cyan,
|
||||
gutter_bar: Color::Magenta,
|
||||
module_name: Color::Green,
|
||||
binop: Color::Green,
|
||||
};
|
||||
|
||||
impl Color {
|
||||
@ -314,10 +316,21 @@ pub fn can_problem(filename: PathBuf, problem: Problem) -> Report {
|
||||
texts.push(plain_text("\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used."));
|
||||
}
|
||||
Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => {
|
||||
texts.push(plain_text(&*format!(
|
||||
"You cannot mix {} and {} without parentheses",
|
||||
left_bin_op.value, right_bin_op.value
|
||||
)));
|
||||
if left_bin_op.value == right_bin_op.value {
|
||||
texts.push(plain_text("Using more than one "));
|
||||
texts.push(BinOp(left_bin_op.value));
|
||||
texts.push(plain_text(
|
||||
" like this requires parentheses, to clarify how things should be grouped.",
|
||||
))
|
||||
} else {
|
||||
texts.push(plain_text("Using "));
|
||||
texts.push(BinOp(left_bin_op.value));
|
||||
texts.push(plain_text(" and "));
|
||||
texts.push(BinOp(right_bin_op.value));
|
||||
texts.push(plain_text(
|
||||
" together requires parentheses, to clarify how they should be grouped.",
|
||||
))
|
||||
}
|
||||
texts.push(Region(region));
|
||||
}
|
||||
Problem::UnsupportedPattern(_pattern_type, _region) => {
|
||||
@ -371,6 +384,8 @@ pub enum ReportText {
|
||||
/// The documentation for this symbol.
|
||||
Docs(Symbol),
|
||||
|
||||
BinOp(roc_parse::operator::BinOp),
|
||||
|
||||
/// Many ReportText that should be concatenated together.
|
||||
Batch(Vec<ReportText>),
|
||||
}
|
||||
@ -575,6 +590,9 @@ impl ReportText {
|
||||
report_text.render_ci(buf, subs, home, src_lines, interns);
|
||||
}
|
||||
}
|
||||
BinOp(bin_op) => {
|
||||
buf.push_str(bin_op.to_string().as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -716,6 +734,9 @@ impl ReportText {
|
||||
report_text.render_color_terminal(buf, subs, home, src_lines, interns, palette);
|
||||
}
|
||||
}
|
||||
BinOp(bin_op) => {
|
||||
buf.push_str(&palette.binop.render(bin_op.to_string().as_str()));
|
||||
}
|
||||
_ => panic!("TODO implement more ReportTexts in render color terminal"),
|
||||
}
|
||||
}
|
||||
|
@ -304,23 +304,22 @@ mod test_reporting {
|
||||
)
|
||||
}
|
||||
|
||||
// hits a TODO in reporting
|
||||
// #[test]
|
||||
// fn report_shadow() {
|
||||
// report_problem_as(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// i = 1
|
||||
// #[test]
|
||||
// fn report_shadow() {
|
||||
// report_problem_as(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// i = 1
|
||||
//
|
||||
// s = \i ->
|
||||
// i + 1
|
||||
// s = \i ->
|
||||
// i + 1
|
||||
//
|
||||
// s i
|
||||
// "#
|
||||
// ),
|
||||
// indoc!(r#" "#),
|
||||
// )
|
||||
// }
|
||||
// s i
|
||||
// "#
|
||||
// ),
|
||||
// indoc!(r#" "#),
|
||||
// )
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn report_unsupported_top_level_def() {
|
||||
@ -355,7 +354,7 @@ mod test_reporting {
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
You cannot mix (!=) and (==) without parentheses
|
||||
Using != and == together requires parentheses, to clarify how they should be grouped.
|
||||
|
||||
3 ┆ if selectedId != thisId == adminsId then
|
||||
┆ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@ -372,7 +371,7 @@ mod test_reporting {
|
||||
r#"
|
||||
if
|
||||
1
|
||||
!= 2
|
||||
== 2
|
||||
== 3
|
||||
then
|
||||
2
|
||||
@ -383,10 +382,10 @@ mod test_reporting {
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
You cannot mix (!=) and (==) without parentheses
|
||||
Using more than one == like this requires parentheses, to clarify how things should be grouped.
|
||||
|
||||
2 ┆> 1
|
||||
3 ┆> != 2
|
||||
3 ┆> == 2
|
||||
4 ┆> == 3
|
||||
|
||||
"#
|
||||
|
@ -9,7 +9,9 @@ mod helpers;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_solve {
|
||||
use crate::helpers::{assert_correct_variable_usage, can_expr, infer_expr, CanExprOut};
|
||||
use crate::helpers::{
|
||||
assert_correct_variable_usage, can_expr, infer_expr, with_larger_debug_stack, CanExprOut,
|
||||
};
|
||||
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
||||
use roc_types::subs::Subs;
|
||||
|
||||
@ -2220,9 +2222,10 @@ mod test_solve {
|
||||
|
||||
#[test]
|
||||
fn quicksort_partition() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
with_larger_debug_stack(|| {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
swap : Int, Int, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
@ -2261,9 +2264,10 @@ mod test_solve {
|
||||
|
||||
partition
|
||||
"#
|
||||
),
|
||||
"Int, Int, List Int -> [ Pair Int (List Int) ]",
|
||||
);
|
||||
),
|
||||
"Int, Int, List Int -> [ Pair Int (List Int) ]",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -792,7 +792,7 @@ fn write_error_type_help(
|
||||
}
|
||||
}
|
||||
Function(arguments, result) => {
|
||||
let use_parens = parens != Parens::Unnecessary;
|
||||
let write_parens = parens != Parens::Unnecessary;
|
||||
|
||||
if write_parens {
|
||||
buf.push(')');
|
||||
@ -815,10 +815,10 @@ fn write_error_type_help(
|
||||
buf.push(')');
|
||||
}
|
||||
}
|
||||
Record(fields, ext) {
|
||||
buf.push('{');
|
||||
buf.push('}');
|
||||
write_ext(ext, buf);
|
||||
Record(fields, ext) => {
|
||||
buf.push('{');
|
||||
buf.push('}');
|
||||
write_type_ext(ext, buf);
|
||||
}
|
||||
|
||||
Infinite => {
|
||||
@ -836,6 +836,16 @@ pub enum TypeExt {
|
||||
RigidOpen(Lowercase),
|
||||
}
|
||||
|
||||
fn write_type_ext(ext: TypeExt, buf: &mut String) {
|
||||
use TypeExt::*;
|
||||
match ext {
|
||||
Closed => {}
|
||||
FlexOpen(lowercase) | RigidOpen(lowercase) => {
|
||||
buf.push_str(lowercase.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static THE_LETTER_A: u32 = 'a' as u32;
|
||||
|
||||
pub fn name_type_var(letters_used: u32, taken: &mut MutSet<Lowercase>) -> (Lowercase, u32) {
|
||||
|
@ -15,7 +15,7 @@ in our imaginations...so Rube Goldberg it is!)
|
||||
1. `cd` into `examples/hello-world/`
|
||||
2. Run `cargo run hello.roc` to compile the Roc source code into a `hello.o` file.
|
||||
3. Run `gcc -shared hello.o -o libhello_from_roc.so` to generate `libhello_from_roc.so`. (This filename must begin with `lib` and end in `.so` or else `host.rs` won't be able to find it!)
|
||||
4. Move `libhello_from_roc.so` onto the system library path, e.g. with `sudo mv libhello_from_roc.so /usr/local/lib/`
|
||||
4. Move `libhello_from_roc.so` onto the system library path, e.g. with `sudo mv libhello_from_roc.so /usr/local/lib/` on macOS, or `sudo mv libhello_from_roc.so /usr/local/lib /usr/lib` on Linux.
|
||||
5. Run `rustc host.rs -o hello` to generate the `hello` executable.
|
||||
6. Run `./hello` to see the greeting!
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user