Add scaffolding to return some errors from codegen
This commit is contained in:
parent
bd356f11db
commit
efeefe0bfe
@ -4,8 +4,8 @@
|
|||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Block, BlockData, CustomTypeKind, FunctionData, Instr, InstructionData, ModuleData,
|
Block, BlockData, CompileResult, CustomTypeKind, ErrorKind, FunctionData, Instr,
|
||||||
NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
|
InstructionData, ModuleData, NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
|
||||||
debug_information::{
|
debug_information::{
|
||||||
DebugInformation, DebugLocationValue, DebugMetadataValue, DebugProgramValue,
|
DebugInformation, DebugLocationValue, DebugMetadataValue, DebugProgramValue,
|
||||||
InstructionDebugRecordData,
|
InstructionDebugRecordData,
|
||||||
@ -151,7 +151,7 @@ impl Builder {
|
|||||||
block_val: &BlockValue,
|
block_val: &BlockValue,
|
||||||
data: InstructionData,
|
data: InstructionData,
|
||||||
name: String,
|
name: String,
|
||||||
) -> Result<InstructionValue, ()> {
|
) -> CompileResult<InstructionValue> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut modules = self.modules.borrow_mut();
|
let mut modules = self.modules.borrow_mut();
|
||||||
let module = modules.get_unchecked_mut(block_val.0.0.0);
|
let module = modules.get_unchecked_mut(block_val.0.0.0);
|
||||||
@ -236,14 +236,14 @@ impl Builder {
|
|||||||
&self,
|
&self,
|
||||||
block: &BlockValue,
|
block: &BlockValue,
|
||||||
value: TerminatorKind,
|
value: TerminatorKind,
|
||||||
) -> Result<(), ()> {
|
) -> CompileResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut modules = self.modules.borrow_mut();
|
let mut modules = self.modules.borrow_mut();
|
||||||
let module = modules.get_unchecked_mut(block.0.0.0);
|
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||||
let function = module.functions.get_unchecked_mut(block.0.1);
|
let function = module.functions.get_unchecked_mut(block.0.1);
|
||||||
let block = function.blocks.get_unchecked_mut(block.1);
|
let block = function.blocks.get_unchecked_mut(block.1);
|
||||||
if let Some(_) = &block.data.terminator {
|
if let Some(_) = &block.data.terminator {
|
||||||
Err(())
|
Err(ErrorKind::Null)
|
||||||
} else {
|
} else {
|
||||||
block.data.terminator = Some(value);
|
block.data.terminator = Some(value);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -255,14 +255,14 @@ impl Builder {
|
|||||||
&self,
|
&self,
|
||||||
block: &BlockValue,
|
block: &BlockValue,
|
||||||
location: DebugLocationValue,
|
location: DebugLocationValue,
|
||||||
) -> Result<(), ()> {
|
) -> CompileResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut modules = self.modules.borrow_mut();
|
let mut modules = self.modules.borrow_mut();
|
||||||
let module = modules.get_unchecked_mut(block.0.0.0);
|
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||||
let function = module.functions.get_unchecked_mut(block.0.1);
|
let function = module.functions.get_unchecked_mut(block.0.1);
|
||||||
let block = function.blocks.get_unchecked_mut(block.1);
|
let block = function.blocks.get_unchecked_mut(block.1);
|
||||||
if let Some(_) = &block.data.terminator_location {
|
if let Some(_) = &block.data.terminator_location {
|
||||||
Err(())
|
Err(ErrorKind::Null)
|
||||||
} else {
|
} else {
|
||||||
block.data.terminator_location = Some(location);
|
block.data.terminator_location = Some(location);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -270,7 +270,7 @@ impl Builder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> Result<(), ()> {
|
pub(crate) unsafe fn delete_block(&self, block: &BlockValue) -> CompileResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut modules = self.modules.borrow_mut();
|
let mut modules = self.modules.borrow_mut();
|
||||||
let module = modules.get_unchecked_mut(block.0.0.0);
|
let module = modules.get_unchecked_mut(block.0.0.0);
|
||||||
@ -349,7 +349,7 @@ impl Builder {
|
|||||||
self.modules.clone()
|
self.modules.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_instruction(&self, instruction: &InstructionValue) -> Result<(), ()> {
|
pub fn check_instruction(&self, instruction: &InstructionValue) -> CompileResult<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
match self.instr_data(&instruction).kind {
|
match self.instr_data(&instruction).kind {
|
||||||
Instr::Param(_) => Ok(()),
|
Instr::Param(_) => Ok(()),
|
||||||
@ -372,7 +372,7 @@ impl Builder {
|
|||||||
if t.comparable() || t.category() != TypeCategory::Integer {
|
if t.comparable() || t.category() != TypeCategory::Integer {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(()) // TODO error: Types not comparable
|
Err(ErrorKind::Null) // TODO error: Types not comparable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instr::FCmp(_, lhs, rhs) => {
|
Instr::FCmp(_, lhs, rhs) => {
|
||||||
@ -380,17 +380,17 @@ impl Builder {
|
|||||||
if t.comparable() || t.category() != TypeCategory::Real {
|
if t.comparable() || t.category() != TypeCategory::Real {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(()) // TODO error: Types not comparable
|
Err(ErrorKind::Null) // TODO error: Types not comparable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instr::FunctionCall(fun, params) => {
|
Instr::FunctionCall(fun, params) => {
|
||||||
let param_types = self.function_data(&fun).params;
|
let param_types = self.function_data(&fun).params;
|
||||||
if param_types.len() != params.len() {
|
if param_types.len() != params.len() {
|
||||||
return Err(()); // TODO error: invalid amount of params
|
return Err(ErrorKind::Null); // TODO error: invalid amount of params
|
||||||
}
|
}
|
||||||
for (a, b) in param_types.iter().zip(params) {
|
for (a, b) in param_types.iter().zip(params) {
|
||||||
if *a != b.get_type(&self)? {
|
if *a != b.get_type(&self)? {
|
||||||
return Err(()); // TODO error: params do not match
|
return Err(ErrorKind::Null); // TODO error: params do not match
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -403,7 +403,7 @@ impl Builder {
|
|||||||
// incoming values come from blocks that are added later
|
// incoming values come from blocks that are added later
|
||||||
// than the one where this one exists.
|
// than the one where this one exists.
|
||||||
|
|
||||||
let first = iter.next().ok_or(())?;
|
let first = iter.next().ok_or(ErrorKind::Null)?;
|
||||||
for item in iter {
|
for item in iter {
|
||||||
match_types(first, item, &self)?;
|
match_types(first, item, &self)?;
|
||||||
}
|
}
|
||||||
@ -416,10 +416,10 @@ impl Builder {
|
|||||||
if *ptr_ty_inner == load_ty {
|
if *ptr_ty_inner == load_ty {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(()) // TODO error: inner type mismatch
|
Err(ErrorKind::Null) // TODO error: inner type mismatch
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(()) // TODO error: not a pointer
|
Err(ErrorKind::Null) // TODO error: not a pointer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instr::Store(ptr, _) => {
|
Instr::Store(ptr, _) => {
|
||||||
@ -427,7 +427,7 @@ impl Builder {
|
|||||||
if let Type::Ptr(_) = ty {
|
if let Type::Ptr(_) = ty {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(()) // TODO error: not a pointer
|
Err(ErrorKind::Null) // TODO error: not a pointer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instr::ArrayAlloca(_, _) => Ok(()),
|
Instr::ArrayAlloca(_, _) => Ok(()),
|
||||||
@ -435,7 +435,7 @@ impl Builder {
|
|||||||
let ptr_ty = ptr_val.get_type(&self)?;
|
let ptr_ty = ptr_val.get_type(&self)?;
|
||||||
match ptr_ty {
|
match ptr_ty {
|
||||||
Type::Ptr(_) => Ok(()),
|
Type::Ptr(_) => Ok(()),
|
||||||
_ => Err(()),
|
_ => Err(ErrorKind::Null),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instr::GetStructElemPtr(ptr_val, idx) => {
|
Instr::GetStructElemPtr(ptr_val, idx) => {
|
||||||
@ -445,16 +445,16 @@ impl Builder {
|
|||||||
match self.type_data(&val).kind {
|
match self.type_data(&val).kind {
|
||||||
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
|
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
|
||||||
if fields.len() <= idx as usize {
|
if fields.len() <= idx as usize {
|
||||||
return Err(()); // TODO error: no such field
|
return Err(ErrorKind::Null); // TODO error: no such field
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(()) // TODO error: not a struct
|
Err(ErrorKind::Null) // TODO error: not a struct
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(()) // TODO error: not a pointer
|
Err(ErrorKind::Null) // TODO error: not a pointer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instr::ExtractValue(val, _) => {
|
Instr::ExtractValue(val, _) => {
|
||||||
@ -464,7 +464,7 @@ impl Builder {
|
|||||||
CustomTypeKind::NamedStruct(_) => Ok(()),
|
CustomTypeKind::NamedStruct(_) => Ok(()),
|
||||||
},
|
},
|
||||||
Type::Array(_, _) => Ok(()),
|
Type::Array(_, _) => Ok(()),
|
||||||
_ => Err(()),
|
_ => Err(ErrorKind::Null),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Instr::Trunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
Instr::Trunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
|
||||||
@ -545,7 +545,7 @@ impl InstructionValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
|
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
|
||||||
use Instr::*;
|
use Instr::*;
|
||||||
unsafe {
|
unsafe {
|
||||||
match &builder.instr_data(self).kind {
|
match &builder.instr_data(self).kind {
|
||||||
@ -554,7 +554,7 @@ impl InstructionValue {
|
|||||||
.params
|
.params
|
||||||
.get(*nth)
|
.get(*nth)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or(()),
|
.ok_or(ErrorKind::Null),
|
||||||
Constant(c) => Ok(c.get_type()),
|
Constant(c) => Ok(c.get_type()),
|
||||||
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
|
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||||
FAdd(lhs, rhs) => match_types(lhs, rhs, &builder),
|
FAdd(lhs, rhs) => match_types(lhs, rhs, &builder),
|
||||||
@ -572,7 +572,10 @@ impl InstructionValue {
|
|||||||
ICmp(_, _, _) => Ok(Type::Bool),
|
ICmp(_, _, _) => Ok(Type::Bool),
|
||||||
FCmp(_, _, _) => Ok(Type::Bool),
|
FCmp(_, _, _) => Ok(Type::Bool),
|
||||||
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
|
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
|
||||||
Phi(values) => values.first().ok_or(()).and_then(|v| v.get_type(&builder)),
|
Phi(values) => values
|
||||||
|
.first()
|
||||||
|
.ok_or(ErrorKind::Null)
|
||||||
|
.and_then(|v| v.get_type(&builder)),
|
||||||
Alloca(ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
|
Alloca(ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
|
||||||
Load(_, ty) => Ok(ty.clone()),
|
Load(_, ty) => Ok(ty.clone()),
|
||||||
Store(_, value) => value.get_type(builder),
|
Store(_, value) => value.get_type(builder),
|
||||||
@ -614,7 +617,7 @@ impl InstructionValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type::Array(elem_ty, _) => *elem_ty.clone(),
|
Type::Array(elem_ty, _) => *elem_ty.clone(),
|
||||||
_ => return Err(()),
|
_ => return Err(ErrorKind::Null),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Trunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
Trunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
|
||||||
@ -633,9 +636,9 @@ impl InstructionValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cast_to(&self, builder: &Builder, ty: &Type) -> Result<Instr, ()> {
|
fn cast_to(&self, builder: &Builder, ty: &Type) -> CompileResult<Instr> {
|
||||||
self.get_type(builder)?
|
self.get_type(builder)?
|
||||||
.cast_instruction(*self, &ty)
|
.cast_instruction(*self, &ty)
|
||||||
.ok_or(())
|
.ok_or(ErrorKind::Null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,14 @@ mod fmt;
|
|||||||
mod pad_adapter;
|
mod pad_adapter;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
#[error("NULL error, should never occur!")]
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CompileResult<T> = Result<T, ErrorKind>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
builder: Builder,
|
builder: Builder,
|
||||||
@ -254,7 +262,7 @@ impl<'builder> Block<'builder> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: T,
|
name: T,
|
||||||
instruction: Instr,
|
instruction: Instr,
|
||||||
) -> Result<InstructionValue, ()> {
|
) -> CompileResult<InstructionValue> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.builder.add_instruction(
|
self.builder.add_instruction(
|
||||||
&self.value,
|
&self.value,
|
||||||
@ -268,7 +276,7 @@ impl<'builder> Block<'builder> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(&mut self, instruction: Instr) -> Result<InstructionValue, ()> {
|
pub fn build(&mut self, instruction: Instr) -> CompileResult<InstructionValue> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let name = instruction.default_name().to_owned();
|
let name = instruction.default_name().to_owned();
|
||||||
self.builder.add_instruction(
|
self.builder.add_instruction(
|
||||||
@ -297,16 +305,16 @@ impl<'builder> Block<'builder> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminate(&mut self, instruction: TerminatorKind) -> Result<(), ()> {
|
pub fn terminate(&mut self, instruction: TerminatorKind) -> CompileResult<()> {
|
||||||
unsafe { self.builder.terminate(&self.value, instruction) }
|
unsafe { self.builder.terminate(&self.value, instruction) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_terminator_location(&mut self, location: DebugLocationValue) -> Result<(), ()> {
|
pub fn set_terminator_location(&mut self, location: DebugLocationValue) -> CompileResult<()> {
|
||||||
unsafe { self.builder.set_terminator_location(&self.value, location) }
|
unsafe { self.builder.set_terminator_location(&self.value, location) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete block if it is unused. Return true if deleted, false if not.
|
/// Delete block if it is unused. Return true if deleted, false if not.
|
||||||
pub fn delete_if_unused(&mut self) -> Result<bool, ()> {
|
pub fn delete_if_unused(&mut self) -> CompileResult<bool> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if !self.builder.is_block_used(self.value()) {
|
if !self.builder.is_block_used(self.value()) {
|
||||||
self.builder.delete_block(&self.value)?;
|
self.builder.delete_block(&self.value)?;
|
||||||
@ -682,7 +690,7 @@ pub enum TypeCategory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TerminatorKind {
|
impl TerminatorKind {
|
||||||
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
|
pub(crate) fn get_type(&self, builder: &Builder) -> CompileResult<Type> {
|
||||||
use TerminatorKind::*;
|
use TerminatorKind::*;
|
||||||
match self {
|
match self {
|
||||||
Ret(instr_val) => instr_val.get_type(builder),
|
Ret(instr_val) => instr_val.get_type(builder),
|
||||||
|
@ -14,7 +14,7 @@ use llvm_sys::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Type,
|
CompileResult, ErrorKind, Type,
|
||||||
builder::{Builder, InstructionValue},
|
builder::{Builder, InstructionValue},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,12 +117,16 @@ pub fn match_types(
|
|||||||
lhs: &InstructionValue,
|
lhs: &InstructionValue,
|
||||||
rhs: &InstructionValue,
|
rhs: &InstructionValue,
|
||||||
builder: &Builder,
|
builder: &Builder,
|
||||||
) -> Result<Type, ()> {
|
) -> CompileResult<Type> {
|
||||||
let lhs_type = lhs.get_type(&builder);
|
let lhs_type = lhs.get_type(&builder);
|
||||||
let rhs_type = rhs.get_type(&builder);
|
let rhs_type = rhs.get_type(&builder);
|
||||||
if let (Ok(lhs_t), Ok(rhs_t)) = (lhs_type, rhs_type) {
|
if let (Ok(lhs_t), Ok(rhs_t)) = (lhs_type, rhs_type) {
|
||||||
if lhs_t == rhs_t { Ok(lhs_t) } else { Err(()) }
|
if lhs_t == rhs_t {
|
||||||
|
Ok(lhs_t)
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(ErrorKind::Null)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ErrorKind::Null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,15 @@ use crate::{
|
|||||||
self, implement::TypeCategory, CustomTypeKey, Metadata, NamedVariableRef, SourceModuleId,
|
self, implement::TypeCategory, CustomTypeKey, Metadata, NamedVariableRef, SourceModuleId,
|
||||||
StructField, StructType, TypeDefinition, TypeDefinitionKind, TypeKind, VagueLiteral,
|
StructField, StructType, TypeDefinition, TypeDefinitionKind, TypeKind, VagueLiteral,
|
||||||
},
|
},
|
||||||
|
util::try_all,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
#[error("NULL error, should never occur!")]
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
/// Context that contains all of the given modules as complete codegenerated
|
/// Context that contains all of the given modules as complete codegenerated
|
||||||
/// LLIR that can then be finally compiled into LLVM IR.
|
/// LLIR that can then be finally compiled into LLVM IR.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -39,16 +46,16 @@ impl<'ctx> CodegenContext<'ctx> {
|
|||||||
|
|
||||||
impl mir::Context {
|
impl mir::Context {
|
||||||
/// Compile MIR [`Context`] into [`CodegenContext`] containing LLIR.
|
/// Compile MIR [`Context`] into [`CodegenContext`] containing LLIR.
|
||||||
pub fn codegen<'ctx>(&self, context: &'ctx Context) -> CodegenContext<'ctx> {
|
pub fn codegen<'ctx>(&self, context: &'ctx Context) -> Result<CodegenContext<'ctx>, ErrorKind> {
|
||||||
let mut modules = HashMap::new();
|
let mut modules = HashMap::new();
|
||||||
let mut modules_sorted = self.modules.iter().map(|(_, m)| m).collect::<Vec<_>>();
|
let mut modules_sorted = self.modules.iter().map(|(_, m)| m).collect::<Vec<_>>();
|
||||||
modules_sorted.sort_by(|m1, m2| m2.module_id.cmp(&m1.module_id));
|
modules_sorted.sort_by(|m1, m2| m2.module_id.cmp(&m1.module_id));
|
||||||
|
|
||||||
for module in &modules_sorted {
|
for module in &modules_sorted {
|
||||||
let codegen = module.codegen(context, modules.clone());
|
let codegen = module.codegen(context, modules.clone())?;
|
||||||
modules.insert(module.module_id, codegen);
|
modules.insert(module.module_id, codegen);
|
||||||
}
|
}
|
||||||
CodegenContext { context }
|
Ok(CodegenContext { context })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +201,7 @@ impl mir::Module {
|
|||||||
&'ctx self,
|
&'ctx self,
|
||||||
context: &'ctx Context,
|
context: &'ctx Context,
|
||||||
modules: HashMap<SourceModuleId, ModuleCodegen<'ctx>>,
|
modules: HashMap<SourceModuleId, ModuleCodegen<'ctx>>,
|
||||||
) -> ModuleCodegen<'ctx> {
|
) -> Result<ModuleCodegen<'ctx>, ErrorKind> {
|
||||||
let mut module = context.module(&self.name, self.is_main);
|
let mut module = context.module(&self.name, self.is_main);
|
||||||
let tokens = &self.tokens;
|
let tokens = &self.tokens;
|
||||||
|
|
||||||
@ -430,7 +437,7 @@ impl mir::Module {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let state = State::default();
|
let state = State::default();
|
||||||
if let Some(ret) = block.codegen(&mut scope, &state) {
|
if let Some(ret) = block.codegen(&mut scope, &state)? {
|
||||||
scope.block.terminate(Term::Ret(ret.instr())).unwrap();
|
scope.block.terminate(Term::Ret(ret.instr())).unwrap();
|
||||||
} else {
|
} else {
|
||||||
if !scope.block.delete_if_unused().unwrap() {
|
if !scope.block.delete_if_unused().unwrap() {
|
||||||
@ -451,7 +458,7 @@ impl mir::Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleCodegen { module }
|
Ok(ModuleCodegen { module })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,9 +467,9 @@ impl mir::Block {
|
|||||||
&self,
|
&self,
|
||||||
mut scope: &mut Scope<'ctx, 'a>,
|
mut scope: &mut Scope<'ctx, 'a>,
|
||||||
state: &State,
|
state: &State,
|
||||||
) -> Option<StackValue> {
|
) -> Result<Option<StackValue>, ErrorKind> {
|
||||||
for stmt in &self.statements {
|
for stmt in &self.statements {
|
||||||
stmt.codegen(&mut scope, state).map(|s| {
|
stmt.codegen(&mut scope, state)?.map(|s| {
|
||||||
if let Some(debug) = &scope.debug {
|
if let Some(debug) = &scope.debug {
|
||||||
let location = stmt.1.into_debug(scope.tokens, debug.scope).unwrap();
|
let location = stmt.1.into_debug(scope.tokens, debug.scope).unwrap();
|
||||||
let loc_val = debug.info.location(&debug.scope, location);
|
let loc_val = debug.info.location(&debug.scope, location);
|
||||||
@ -472,22 +479,30 @@ impl mir::Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some((kind, expr)) = &self.return_expression {
|
if let Some((kind, expr)) = &self.return_expression {
|
||||||
let ret = expr.codegen(&mut scope, &mut state.load(true));
|
let ret = expr.codegen(&mut scope, &mut state.load(true))?;
|
||||||
match kind {
|
match kind {
|
||||||
mir::ReturnKind::Hard => {
|
mir::ReturnKind::Hard => {
|
||||||
scope.block.terminate(Term::Ret(ret?.instr())).unwrap();
|
if let Some(ret) = ret {
|
||||||
None
|
scope.block.terminate(Term::Ret(ret.instr())).unwrap();
|
||||||
|
} else {
|
||||||
|
scope.block.terminate(Term::RetVoid).unwrap();
|
||||||
}
|
}
|
||||||
mir::ReturnKind::Soft => ret,
|
Ok(None)
|
||||||
|
}
|
||||||
|
mir::ReturnKind::Soft => Ok(ret),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl mir::Statement {
|
impl mir::Statement {
|
||||||
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, state: &State) -> Option<StackValue> {
|
fn codegen<'ctx, 'a>(
|
||||||
|
&self,
|
||||||
|
scope: &mut Scope<'ctx, 'a>,
|
||||||
|
state: &State,
|
||||||
|
) -> Result<Option<StackValue>, ErrorKind> {
|
||||||
let location = scope.debug.clone().map(|d| {
|
let location = scope.debug.clone().map(|d| {
|
||||||
let location = self.1.into_debug(scope.tokens, d.scope).unwrap();
|
let location = self.1.into_debug(scope.tokens, d.scope).unwrap();
|
||||||
d.info.location(&d.scope, location)
|
d.info.location(&d.scope, location)
|
||||||
@ -495,7 +510,7 @@ impl mir::Statement {
|
|||||||
|
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
mir::StmtKind::Let(NamedVariableRef(ty, name, _), mutable, expression) => {
|
mir::StmtKind::Let(NamedVariableRef(ty, name, _), mutable, expression) => {
|
||||||
let value = expression.codegen(scope, &state).unwrap();
|
let value = expression.codegen(scope, &state)?.unwrap();
|
||||||
|
|
||||||
let alloca = scope
|
let alloca = scope
|
||||||
.block
|
.block
|
||||||
@ -545,14 +560,17 @@ impl mir::Statement {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
None
|
Ok(None)
|
||||||
}
|
}
|
||||||
mir::StmtKind::Set(lhs, rhs) => {
|
mir::StmtKind::Set(lhs, rhs) => {
|
||||||
let lhs_value = lhs
|
let lhs_value = lhs
|
||||||
.codegen(scope, &state.load(false))
|
.codegen(scope, &state.load(false))?
|
||||||
.expect("non-returning LHS snuck into codegen!");
|
.expect("non-returning LHS snuck into codegen!");
|
||||||
|
|
||||||
let rhs_value = rhs.codegen(scope, state)?;
|
let rhs_value = rhs.codegen(scope, state)?;
|
||||||
|
let Some(rhs_value) = rhs_value else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
let backing_var = if let Some(var) = lhs.backing_var() {
|
let backing_var = if let Some(var) = lhs.backing_var() {
|
||||||
&format!("store.{}", var.1)
|
&format!("store.{}", var.1)
|
||||||
@ -576,7 +594,7 @@ impl mir::Statement {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
None
|
Ok(None)
|
||||||
}
|
}
|
||||||
mir::StmtKind::Import(_) => todo!(),
|
mir::StmtKind::Import(_) => todo!(),
|
||||||
mir::StmtKind::Expression(expression) => expression.codegen(scope, state),
|
mir::StmtKind::Expression(expression) => expression.codegen(scope, state),
|
||||||
@ -585,7 +603,11 @@ impl mir::Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl mir::Expression {
|
impl mir::Expression {
|
||||||
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, state: &State) -> Option<StackValue> {
|
fn codegen<'ctx, 'a>(
|
||||||
|
&self,
|
||||||
|
scope: &mut Scope<'ctx, 'a>,
|
||||||
|
state: &State,
|
||||||
|
) -> Result<Option<StackValue>, ErrorKind> {
|
||||||
let location = if let Some(debug) = &scope.debug {
|
let location = if let Some(debug) = &scope.debug {
|
||||||
Some(debug.info.location(
|
Some(debug.info.location(
|
||||||
&debug.scope,
|
&debug.scope,
|
||||||
@ -634,11 +656,11 @@ impl mir::Expression {
|
|||||||
)),
|
)),
|
||||||
mir::ExprKind::BinOp(binop, lhs_exp, rhs_exp) => {
|
mir::ExprKind::BinOp(binop, lhs_exp, rhs_exp) => {
|
||||||
let lhs = lhs_exp
|
let lhs = lhs_exp
|
||||||
.codegen(scope, state)
|
.codegen(scope, state)?
|
||||||
.expect("lhs has no return value")
|
.expect("lhs has no return value")
|
||||||
.instr();
|
.instr();
|
||||||
let rhs = rhs_exp
|
let rhs = rhs_exp
|
||||||
.codegen(scope, state)
|
.codegen(scope, state)?
|
||||||
.expect("rhs has no return value")
|
.expect("rhs has no return value")
|
||||||
.instr();
|
.instr();
|
||||||
let lhs_type = lhs_exp
|
let lhs_type = lhs_exp
|
||||||
@ -721,11 +743,17 @@ impl mir::Expression {
|
|||||||
|
|
||||||
let ret_type = ret_type_kind.get_type(scope.type_values, scope.types);
|
let ret_type = ret_type_kind.get_type(scope.type_values, scope.types);
|
||||||
|
|
||||||
let params = call
|
let params = try_all(
|
||||||
.parameters
|
call.parameters
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| e.codegen(scope, state).unwrap())
|
.map(|e| e.codegen(scope, state))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.map_err(|e| e.first().cloned().unwrap())?
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| v.unwrap())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let param_instrs = params.iter().map(|e| e.instr()).collect();
|
let param_instrs = params.iter().map(|e| e.instr()).collect();
|
||||||
let callee = scope
|
let callee = scope
|
||||||
.functions
|
.functions
|
||||||
@ -782,13 +810,13 @@ impl mir::Expression {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mir::ExprKind::If(if_expression) => if_expression.codegen(scope, state),
|
mir::ExprKind::If(if_expression) => if_expression.codegen(scope, state)?,
|
||||||
mir::ExprKind::Block(block) => {
|
mir::ExprKind::Block(block) => {
|
||||||
let inner = scope.function.ir.block("inner");
|
let inner = scope.function.ir.block("inner");
|
||||||
scope.block.terminate(Term::Br(inner.value())).unwrap();
|
scope.block.terminate(Term::Br(inner.value())).unwrap();
|
||||||
|
|
||||||
let mut inner_scope = scope.with_block(inner);
|
let mut inner_scope = scope.with_block(inner);
|
||||||
let ret = if let Some(ret) = block.codegen(&mut inner_scope, state) {
|
let ret = if let Some(ret) = block.codegen(&mut inner_scope, state)? {
|
||||||
Some(ret)
|
Some(ret)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -800,10 +828,10 @@ impl mir::Expression {
|
|||||||
}
|
}
|
||||||
mir::ExprKind::Indexed(expression, val_t, idx_expr) => {
|
mir::ExprKind::Indexed(expression, val_t, idx_expr) => {
|
||||||
let StackValue(kind, ty) = expression
|
let StackValue(kind, ty) = expression
|
||||||
.codegen(scope, &state.load(false))
|
.codegen(scope, &state.load(false))?
|
||||||
.expect("array returned none!");
|
.expect("array returned none!");
|
||||||
let idx = idx_expr
|
let idx = idx_expr
|
||||||
.codegen(scope, &state.load(true))
|
.codegen(scope, &state.load(true))?
|
||||||
.expect("index returned none!")
|
.expect("index returned none!")
|
||||||
.instr();
|
.instr();
|
||||||
|
|
||||||
@ -877,10 +905,17 @@ impl mir::Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mir::ExprKind::Array(expressions) => {
|
mir::ExprKind::Array(expressions) => {
|
||||||
let stack_value_list = expressions
|
let stack_value_list: Vec<_> = try_all(
|
||||||
|
expressions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| e.codegen(scope, state).unwrap())
|
.map(|e| e.codegen(scope, state))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.map_err(|e| e.first().cloned().unwrap())?
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| v.unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let instr_list = stack_value_list
|
let instr_list = stack_value_list
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.instr())
|
.map(|s| s.instr())
|
||||||
@ -944,7 +979,7 @@ impl mir::Expression {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
mir::ExprKind::Accessed(expression, type_kind, field) => {
|
mir::ExprKind::Accessed(expression, type_kind, field) => {
|
||||||
let struct_val = expression.codegen(scope, &state.load(false)).unwrap();
|
let struct_val = expression.codegen(scope, &state.load(false))?.unwrap();
|
||||||
|
|
||||||
let TypeKind::CodegenPtr(inner) = &struct_val.1 else {
|
let TypeKind::CodegenPtr(inner) = &struct_val.1 else {
|
||||||
panic!("tried accessing non-pointer");
|
panic!("tried accessing non-pointer");
|
||||||
@ -996,7 +1031,12 @@ impl mir::Expression {
|
|||||||
}
|
}
|
||||||
mir::ExprKind::Struct(name, items) => {
|
mir::ExprKind::Struct(name, items) => {
|
||||||
let type_key = CustomTypeKey(name.clone(), scope.module_id);
|
let type_key = CustomTypeKey(name.clone(), scope.module_id);
|
||||||
let struct_ty = Type::CustomType(*scope.type_values.get(&type_key)?);
|
let struct_ty = Type::CustomType({
|
||||||
|
let Some(a) = scope.type_values.get(&type_key) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
*a
|
||||||
|
});
|
||||||
|
|
||||||
let load_n = format!("{}.load", name);
|
let load_n = format!("{}.load", name);
|
||||||
|
|
||||||
@ -1015,7 +1055,7 @@ impl mir::Expression {
|
|||||||
.build_named(gep_n, Instr::GetStructElemPtr(struct_ptr, i as u32))
|
.build_named(gep_n, Instr::GetStructElemPtr(struct_ptr, i as u32))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.maybe_location(&mut scope.block, location);
|
.maybe_location(&mut scope.block, location);
|
||||||
if let Some(val) = exp.codegen(scope, state) {
|
if let Some(val) = exp.codegen(scope, state)? {
|
||||||
scope
|
scope
|
||||||
.block
|
.block
|
||||||
.build_named(store_n, Instr::Store(elem_ptr, val.instr()))
|
.build_named(store_n, Instr::Store(elem_ptr, val.instr()))
|
||||||
@ -1101,6 +1141,7 @@ impl mir::Expression {
|
|||||||
}
|
}
|
||||||
mir::ExprKind::CastTo(expression, type_kind) => {
|
mir::ExprKind::CastTo(expression, type_kind) => {
|
||||||
let val = expression.codegen(scope, state)?;
|
let val = expression.codegen(scope, state)?;
|
||||||
|
let Some(val) = val else { return Ok(None) };
|
||||||
|
|
||||||
if val.1 == *type_kind {
|
if val.1 == *type_kind {
|
||||||
Some(val)
|
Some(val)
|
||||||
@ -1161,13 +1202,17 @@ impl mir::Expression {
|
|||||||
if let Some(value) = &value {
|
if let Some(value) = &value {
|
||||||
value.instr().maybe_location(&mut scope.block, location);
|
value.instr().maybe_location(&mut scope.block, location);
|
||||||
}
|
}
|
||||||
value
|
Ok(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl mir::IfExpression {
|
impl mir::IfExpression {
|
||||||
fn codegen<'ctx, 'a>(&self, scope: &mut Scope<'ctx, 'a>, state: &State) -> Option<StackValue> {
|
fn codegen<'ctx, 'a>(
|
||||||
let condition = self.0.codegen(scope, state).unwrap();
|
&self,
|
||||||
|
scope: &mut Scope<'ctx, 'a>,
|
||||||
|
state: &State,
|
||||||
|
) -> Result<Option<StackValue>, ErrorKind> {
|
||||||
|
let condition = self.0.codegen(scope, state)?.unwrap();
|
||||||
|
|
||||||
// Create blocks
|
// Create blocks
|
||||||
let mut then_b = scope.function.ir.block("then");
|
let mut then_b = scope.function.ir.block("then");
|
||||||
@ -1199,7 +1244,7 @@ impl mir::IfExpression {
|
|||||||
|
|
||||||
// Generate then-block content
|
// Generate then-block content
|
||||||
let mut then_scope = scope.with_block(then_b);
|
let mut then_scope = scope.with_block(then_b);
|
||||||
let then_res = self.1.codegen(&mut then_scope, state);
|
let then_res = self.1.codegen(&mut then_scope, state)?;
|
||||||
then_scope.block.terminate(Term::Br(after_bb)).ok();
|
then_scope.block.terminate(Term::Br(after_bb)).ok();
|
||||||
|
|
||||||
let else_res = if let Some(else_expr) = self.2.as_ref() {
|
let else_res = if let Some(else_expr) = self.2.as_ref() {
|
||||||
@ -1210,7 +1255,7 @@ impl mir::IfExpression {
|
|||||||
.terminate(Term::CondBr(condition.instr(), then_bb, else_bb))
|
.terminate(Term::CondBr(condition.instr(), then_bb, else_bb))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let opt = else_expr.codegen(&mut else_scope, state);
|
let opt = else_expr.codegen(&mut else_scope, state)?;
|
||||||
|
|
||||||
else_scope.block.terminate(Term::Br(after_bb)).ok();
|
else_scope.block.terminate(Term::Br(after_bb)).ok();
|
||||||
|
|
||||||
@ -1232,7 +1277,7 @@ impl mir::IfExpression {
|
|||||||
scope.swap_block(after_b);
|
scope.swap_block(after_b);
|
||||||
|
|
||||||
if then_res.is_none() && else_res.is_none() {
|
if then_res.is_none() && else_res.is_none() {
|
||||||
None
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
let mut incoming = Vec::from(then_res.as_slice());
|
let mut incoming = Vec::from(then_res.as_slice());
|
||||||
incoming.extend(else_res.clone());
|
incoming.extend(else_res.clone());
|
||||||
@ -1260,7 +1305,7 @@ impl mir::IfExpression {
|
|||||||
(Mutable(_), Mutable(_)) => StackValue(Mutable(instr), lhs_val.1),
|
(Mutable(_), Mutable(_)) => StackValue(Mutable(instr), lhs_val.1),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Some(value)
|
Ok(Some(value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
codegen,
|
||||||
lexer::{self, Cursor, FullToken, Position},
|
lexer::{self, Cursor, FullToken, Position},
|
||||||
mir::{self, pass, Metadata, SourceModuleId},
|
mir::{self, pass, Metadata, SourceModuleId},
|
||||||
token_stream::{self, TokenRange},
|
token_stream::{self, TokenRange},
|
||||||
@ -30,6 +31,8 @@ pub enum ErrorKind {
|
|||||||
TypeInferenceError(#[source] mir::pass::Error<mir::typecheck::ErrorKind>),
|
TypeInferenceError(#[source] mir::pass::Error<mir::typecheck::ErrorKind>),
|
||||||
#[error("{}{}", label("(Linker) "), .0.kind)]
|
#[error("{}{}", label("(Linker) "), .0.kind)]
|
||||||
LinkerError(#[from] mir::pass::Error<mir::linker::ErrorKind>),
|
LinkerError(#[from] mir::pass::Error<mir::linker::ErrorKind>),
|
||||||
|
#[error("{}{}", label("(Codegen) "), .0)]
|
||||||
|
CodegenError(#[from] codegen::ErrorKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorKind {
|
impl ErrorKind {
|
||||||
@ -50,6 +53,9 @@ impl ErrorKind {
|
|||||||
ErrorKind::TypeCheckError(error) => error.metadata,
|
ErrorKind::TypeCheckError(error) => error.metadata,
|
||||||
ErrorKind::TypeInferenceError(error) => error.metadata,
|
ErrorKind::TypeInferenceError(error) => error.metadata,
|
||||||
ErrorKind::LinkerError(error) => error.metadata,
|
ErrorKind::LinkerError(error) => error.metadata,
|
||||||
|
ErrorKind::CodegenError(error) => match error {
|
||||||
|
codegen::ErrorKind::Null => Default::default(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,7 +161,7 @@ impl ReidError {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_kind<U>(errors: Vec<ErrorKind>, map: ErrorModules) -> ReidError {
|
pub fn from_kind(errors: Vec<ErrorKind>, map: ErrorModules) -> ReidError {
|
||||||
ReidError { map, errors }
|
ReidError { map, errors }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ pub fn perform_all_passes<'map>(
|
|||||||
dbg!(&state);
|
dbg!(&state);
|
||||||
|
|
||||||
if !state.errors.is_empty() {
|
if !state.errors.is_empty() {
|
||||||
return Err(ReidError::from_kind::<()>(
|
return Err(ReidError::from_kind(
|
||||||
state.errors.iter().map(|e| e.clone().into()).collect(),
|
state.errors.iter().map(|e| e.clone().into()).collect(),
|
||||||
module_map.clone(),
|
module_map.clone(),
|
||||||
));
|
));
|
||||||
@ -157,7 +157,7 @@ pub fn perform_all_passes<'map>(
|
|||||||
dbg!(&state);
|
dbg!(&state);
|
||||||
|
|
||||||
if !state.errors.is_empty() {
|
if !state.errors.is_empty() {
|
||||||
return Err(ReidError::from_kind::<()>(
|
return Err(ReidError::from_kind(
|
||||||
state
|
state
|
||||||
.errors
|
.errors
|
||||||
.iter()
|
.iter()
|
||||||
@ -177,7 +177,7 @@ pub fn perform_all_passes<'map>(
|
|||||||
dbg!(&state);
|
dbg!(&state);
|
||||||
|
|
||||||
if !state.errors.is_empty() {
|
if !state.errors.is_empty() {
|
||||||
return Err(ReidError::from_kind::<()>(
|
return Err(ReidError::from_kind(
|
||||||
state
|
state
|
||||||
.errors
|
.errors
|
||||||
.iter()
|
.iter()
|
||||||
@ -214,7 +214,10 @@ pub fn compile_and_pass<'map>(
|
|||||||
println!("{}", &mir_context);
|
println!("{}", &mir_context);
|
||||||
|
|
||||||
let mut context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
|
let mut context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION")));
|
||||||
let codegen_modules = mir_context.codegen(&mut context);
|
let codegen_modules = match mir_context.codegen(&mut context) {
|
||||||
|
Ok(modules) => modules,
|
||||||
|
Err(e) => Err(ReidError::from_kind(vec![e.into()], module_map.clone()))?,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
println!("{}", &codegen_modules.context);
|
println!("{}", &codegen_modules.context);
|
||||||
|
Loading…
Reference in New Issue
Block a user