Remove length param from string, use string pointers instead
This commit is contained in:
		
							parent
							
								
									1173e17fe6
								
							
						
					
					
						commit
						376baa2c9a
					
				| @ -369,7 +369,7 @@ impl ConstValue { | ||||
|             ConstValue::U32(_) => U32, | ||||
|             ConstValue::U64(_) => U64, | ||||
|             ConstValue::U128(_) => U128, | ||||
|             ConstValue::String(val) => String(val.len() as u32), | ||||
|             ConstValue::String(_) => Ptr(Box::new(I8)), | ||||
|             ConstValue::Bool(_) => Bool, | ||||
|         } | ||||
|     } | ||||
| @ -390,7 +390,6 @@ impl Type { | ||||
|             Type::U128 => true, | ||||
|             Type::Bool => true, | ||||
|             Type::Void => false, | ||||
|             Type::String(_) => false, | ||||
|             Type::Ptr(_) => false, | ||||
|         } | ||||
|     } | ||||
| @ -409,7 +408,6 @@ impl Type { | ||||
|             Type::U128 => false, | ||||
|             Type::Bool => false, | ||||
|             Type::Void => false, | ||||
|             Type::String(_) => false, | ||||
|             Type::Ptr(_) => false, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -457,9 +457,11 @@ impl ConstValue { | ||||
|                 ConstValue::U32(val) => LLVMConstInt(t, *val as u64, 1), | ||||
|                 ConstValue::U64(val) => LLVMConstInt(t, *val as u64, 1), | ||||
|                 ConstValue::U128(val) => LLVMConstInt(t, *val as u64, 1), | ||||
|                 ConstValue::String(val) => { | ||||
|                     LLVMBuildGlobalString(builder, into_cstring(val).as_ptr(), c"string".as_ptr()) | ||||
|                 } | ||||
|                 ConstValue::String(val) => LLVMBuildGlobalStringPtr( | ||||
|                     builder, | ||||
|                     into_cstring(val).as_ptr(), | ||||
|                     c"string".as_ptr(), | ||||
|                 ), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -478,7 +480,6 @@ impl Type { | ||||
|                 Bool => LLVMInt1TypeInContext(context), | ||||
|                 Void => LLVMVoidType(), | ||||
|                 Ptr(ty) => LLVMPointerType(ty.as_llvm(context), 0), | ||||
|                 String(length) => LLVMArrayType(LLVMInt8TypeInContext(context), *length), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -200,7 +200,6 @@ pub enum Type { | ||||
|     U128, | ||||
|     Bool, | ||||
|     Void, | ||||
|     String(u32), | ||||
|     Ptr(Box<Type>), | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -22,6 +22,7 @@ pub enum TypeKind { | ||||
|     U32, | ||||
|     U64, | ||||
|     U128, | ||||
|     String, | ||||
|     Array(Box<TypeKind>, u64), | ||||
| } | ||||
| 
 | ||||
| @ -157,6 +158,7 @@ pub enum BlockLevelStatement { | ||||
| #[derive(Debug)] | ||||
| pub enum TopLevelStatement { | ||||
|     Import(ImportStatement), | ||||
|     ExternFunction(FunctionSignature), | ||||
|     FunctionDefinition(FunctionDefinition), | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -38,6 +38,7 @@ impl Parse for Type { | ||||
|                     "u32" => TypeKind::U32, | ||||
|                     "u64" => TypeKind::U64, | ||||
|                     "u128" => TypeKind::U128, | ||||
|                     "string" => TypeKind::String, | ||||
|                     _ => Err(stream.expected_err("known type identifier")?)?, | ||||
|                 } | ||||
|             } else { | ||||
| @ -474,6 +475,13 @@ impl Parse for TopLevelStatement { | ||||
|         use TopLevelStatement as Stmt; | ||||
|         Ok(match stream.peek() { | ||||
|             Some(Token::ImportKeyword) => Stmt::Import(stream.parse()?), | ||||
|             Some(Token::Extern) => { | ||||
|                 stream.next(); // Consume Extern
 | ||||
|                 stream.expect(Token::FnKeyword)?; | ||||
|                 let extern_fn = Stmt::ExternFunction(stream.parse()?); | ||||
|                 stream.expect(Token::Semi)?; | ||||
|                 extern_fn | ||||
|             } | ||||
|             Some(Token::FnKeyword) => Stmt::FunctionDefinition(stream.parse()?), | ||||
|             _ => Err(stream.expected_err("import or fn")?)?, | ||||
|         }) | ||||
|  | ||||
| @ -42,6 +42,24 @@ impl ast::Module { | ||||
|                     }; | ||||
|                     functions.push(def); | ||||
|                 } | ||||
|                 ExternFunction(signature) => { | ||||
|                     let def = mir::FunctionDefinition { | ||||
|                         name: signature.name.clone(), | ||||
|                         return_type: signature | ||||
|                             .return_type | ||||
|                             .clone() | ||||
|                             .map(|r| r.0.into()) | ||||
|                             .unwrap_or(mir::TypeKind::Void), | ||||
|                         parameters: signature | ||||
|                             .args | ||||
|                             .iter() | ||||
|                             .cloned() | ||||
|                             .map(|p| (p.0, p.1.into())) | ||||
|                             .collect(), | ||||
|                         kind: mir::FunctionDefinitionKind::Extern, | ||||
|                     }; | ||||
|                     functions.push(def); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -226,6 +244,7 @@ impl From<ast::TypeKind> for mir::TypeKind { | ||||
|             ast::TypeKind::Array(type_kind, length) => { | ||||
|                 mir::TypeKind::Array(Box::new(mir::TypeKind::from(*type_kind.clone())), *length) | ||||
|             } | ||||
|             ast::TypeKind::String => mir::TypeKind::StringPtr, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -495,7 +495,7 @@ impl TypeKind { | ||||
|             TypeKind::U64 => Type::U64, | ||||
|             TypeKind::U128 => Type::U128, | ||||
|             TypeKind::Bool => Type::Bool, | ||||
|             TypeKind::String(length) => Type::String(*length as u32), | ||||
|             TypeKind::StringPtr => Type::Ptr(Box::new(Type::I8)), | ||||
|             TypeKind::Array(elem_t, _) => Type::Ptr(Box::new(elem_t.get_type())), | ||||
|             TypeKind::Void => Type::Void, | ||||
|             TypeKind::Vague(_) => panic!("Tried to compile a vague type!"), | ||||
|  | ||||
| @ -32,6 +32,8 @@ pub enum Token { | ||||
|     True, | ||||
|     /// `false`
 | ||||
|     False, | ||||
|     /// `extern`
 | ||||
|     Extern, | ||||
| 
 | ||||
|     // Symbols
 | ||||
|     /// `;`
 | ||||
| @ -205,6 +207,7 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error | ||||
|                     "else" => Token::Else, | ||||
|                     "true" => Token::True, | ||||
|                     "false" => Token::False, | ||||
|                     "extern" => Token::Extern, | ||||
|                     _ => Token::Identifier(value), | ||||
|                 }; | ||||
|                 variant | ||||
|  | ||||
| @ -59,7 +59,7 @@ pub enum TypeKind { | ||||
|     #[error("void")] | ||||
|     Void, | ||||
|     #[error("string")] | ||||
|     String(usize), | ||||
|     StringPtr, | ||||
|     #[error("[{0}; {1}]")] | ||||
|     Array(Box<TypeKind>, u64), | ||||
|     #[error(transparent)] | ||||
| @ -102,7 +102,7 @@ impl TypeKind { | ||||
|             TypeKind::U32 => false, | ||||
|             TypeKind::U64 => false, | ||||
|             TypeKind::U128 => false, | ||||
|             TypeKind::String(_) => false, | ||||
|             TypeKind::StringPtr => false, | ||||
|             TypeKind::Array(_, _) => false, | ||||
|         } | ||||
|     } | ||||
| @ -123,7 +123,7 @@ impl TypeKind { | ||||
|             Bool => true, | ||||
|             Vague(_) => false, | ||||
|             Void => false, | ||||
|             TypeKind::String(_) => false, | ||||
|             TypeKind::StringPtr => false, | ||||
|             Array(_, _) => false, | ||||
|         } | ||||
|     } | ||||
| @ -165,7 +165,7 @@ impl Literal { | ||||
|             Literal::U64(_) => TypeKind::U64, | ||||
|             Literal::U128(_) => TypeKind::U128, | ||||
|             Literal::Bool(_) => TypeKind::Bool, | ||||
|             Literal::String(val) => TypeKind::String(val.len()), | ||||
|             Literal::String(_) => TypeKind::StringPtr, | ||||
|             Literal::Vague(VagueLiteral::Number(_)) => TypeKind::Vague(VagueType::Number), | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
| use std::{convert::Infallible, iter}; | ||||
| 
 | ||||
| use crate::{mir::*, util::try_all}; | ||||
| use VagueType::*; | ||||
| use VagueType as Vague; | ||||
| 
 | ||||
| use super::{ | ||||
|     pass::{Pass, PassState, ScopeFunction, ScopeVariable, Storage}, | ||||
| @ -71,7 +71,7 @@ impl FunctionDefinition { | ||||
|         for param in &self.parameters { | ||||
|             let param_t = state.or_else( | ||||
|                 param.1.assert_known(), | ||||
|                 TypeKind::Vague(Unknown), | ||||
|                 TypeKind::Vague(Vague::Unknown), | ||||
|                 self.signature(), | ||||
|             ); | ||||
|             let res = state | ||||
| @ -94,7 +94,9 @@ impl FunctionDefinition { | ||||
|                 state.scope.return_type_hint = Some(self.return_type.clone()); | ||||
|                 block.typecheck(state, &hints, Some(&return_type)) | ||||
|             } | ||||
|             FunctionDefinitionKind::Extern => Ok((ReturnKind::Soft, TypeKind::Vague(Unknown))), | ||||
|             FunctionDefinitionKind::Extern => { | ||||
|                 Ok((ReturnKind::Soft, TypeKind::Vague(Vague::Unknown))) | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         match inferred { | ||||
| @ -128,12 +130,12 @@ impl Block { | ||||
| 
 | ||||
|                     // If expression resolution itself was erronous, resolve as
 | ||||
|                     // Unknown and note error.
 | ||||
|                     let res = state.or_else(res, TypeKind::Vague(Unknown), expression.1); | ||||
|                     let res = state.or_else(res, TypeKind::Vague(Vague::Unknown), expression.1); | ||||
| 
 | ||||
|                     // Make sure the expression and variable type really is the same
 | ||||
|                     let res_t = state.or_else( | ||||
|                         res.collapse_into(&var_t_resolved), | ||||
|                         TypeKind::Vague(Unknown), | ||||
|                         TypeKind::Vague(Vague::Unknown), | ||||
|                         variable_reference.2 + expression.1, | ||||
|                     ); | ||||
| 
 | ||||
| @ -141,7 +143,7 @@ impl Block { | ||||
|                         // Unable to infer variable type even from expression! Default it
 | ||||
|                         let res_t = state.or_else( | ||||
|                             res_t.or_default(), | ||||
|                             TypeKind::Vague(Unknown), | ||||
|                             TypeKind::Vague(Vague::Unknown), | ||||
|                             variable_reference.2, | ||||
|                         ); | ||||
| 
 | ||||
| @ -187,13 +189,14 @@ impl Block { | ||||
| 
 | ||||
|                         // If expression resolution itself was erronous, resolve as
 | ||||
|                         // Unknown.
 | ||||
|                         let expr_ty = state.or_else(res, TypeKind::Vague(Unknown), expression.1); | ||||
|                         let expr_ty = | ||||
|                             state.or_else(res, TypeKind::Vague(Vague::Unknown), expression.1); | ||||
| 
 | ||||
|                         // Make sure the expression and variable type to really
 | ||||
|                         // be the same
 | ||||
|                         let res_t = state.or_else( | ||||
|                             expr_ty.collapse_into(&var.ty.resolve_hinted(&hints)), | ||||
|                             TypeKind::Vague(Unknown), | ||||
|                             TypeKind::Vague(Vague::Unknown), | ||||
|                             variable_reference.meta + expression.1, | ||||
|                         ); | ||||
| 
 | ||||
| @ -241,7 +244,7 @@ impl Block { | ||||
|             let res = expr.typecheck(&mut state, &hints, hint.as_ref()); | ||||
|             return Ok(( | ||||
|                 ReturnKind::Hard, | ||||
|                 state.or_else(res, TypeKind::Vague(Unknown), expr.1), | ||||
|                 state.or_else(res, TypeKind::Vague(Vague::Unknown), expr.1), | ||||
|             )); | ||||
|         } | ||||
| 
 | ||||
| @ -254,7 +257,7 @@ impl Block { | ||||
|             let res = expr.typecheck(&mut state, &hints, ret_hint_t.as_ref()); | ||||
|             Ok(( | ||||
|                 *return_kind, | ||||
|                 state.or_else(res, TypeKind::Vague(Unknown), expr.1), | ||||
|                 state.or_else(res, TypeKind::Vague(Vague::Unknown), expr.1), | ||||
|             )) | ||||
|         } else { | ||||
|             Ok((ReturnKind::Soft, TypeKind::Void)) | ||||
| @ -280,7 +283,7 @@ impl Expression { | ||||
|                             .map(|var| &var.ty) | ||||
|                             .cloned() | ||||
|                             .ok_or(ErrorKind::VariableNotDefined(var_ref.1.clone())), | ||||
|                         TypeKind::Vague(Unknown), | ||||
|                         TypeKind::Vague(Vague::Unknown), | ||||
|                         var_ref.2, | ||||
|                     ) | ||||
|                     .resolve_hinted(hints); | ||||
| @ -288,7 +291,7 @@ impl Expression { | ||||
|                 // Update typing to be more accurate
 | ||||
|                 var_ref.0 = state.or_else( | ||||
|                     var_ref.0.resolve_hinted(hints).collapse_into(&existing), | ||||
|                     TypeKind::Vague(Unknown), | ||||
|                     TypeKind::Vague(Vague::Unknown), | ||||
|                     var_ref.2, | ||||
|                 ); | ||||
| 
 | ||||
| @ -302,9 +305,9 @@ impl Expression { | ||||
|                 // TODO make sure lhs and rhs can actually do this binary
 | ||||
|                 // operation once relevant
 | ||||
|                 let lhs_res = lhs.typecheck(state, &hints, None); | ||||
|                 let lhs_type = state.or_else(lhs_res, TypeKind::Vague(Unknown), lhs.1); | ||||
|                 let lhs_type = state.or_else(lhs_res, TypeKind::Vague(Vague::Unknown), lhs.1); | ||||
|                 let rhs_res = rhs.typecheck(state, &hints, Some(&lhs_type)); | ||||
|                 let rhs_type = state.or_else(rhs_res, TypeKind::Vague(Unknown), rhs.1); | ||||
|                 let rhs_type = state.or_else(rhs_res, TypeKind::Vague(Vague::Unknown), rhs.1); | ||||
| 
 | ||||
|                 if let Some(collapsed) = state.ok(rhs_type.collapse_into(&rhs_type), self.1) { | ||||
|                     // Try to coerce both sides again with collapsed type
 | ||||
| @ -343,14 +346,15 @@ impl Expression { | ||||
|                     let true_params_iter = f | ||||
|                         .params | ||||
|                         .into_iter() | ||||
|                         .chain(iter::repeat(TypeKind::Vague(Unknown))); | ||||
|                         .chain(iter::repeat(TypeKind::Vague(Vague::Unknown))); | ||||
| 
 | ||||
|                     for (param, true_param_t) in | ||||
|                         function_call.parameters.iter_mut().zip(true_params_iter) | ||||
|                     { | ||||
|                         // Typecheck every param separately
 | ||||
|                         let param_res = param.typecheck(state, &hints, Some(&true_param_t)); | ||||
|                         let param_t = state.or_else(param_res, TypeKind::Vague(Unknown), param.1); | ||||
|                         let param_t = | ||||
|                             state.or_else(param_res, TypeKind::Vague(Vague::Unknown), param.1); | ||||
|                         state.ok(param_t.collapse_into(&true_param_t), param.1); | ||||
|                     } | ||||
| 
 | ||||
| @ -368,7 +372,7 @@ impl Expression { | ||||
|             } | ||||
|             ExprKind::If(IfExpression(cond, lhs, rhs)) => { | ||||
|                 let cond_res = cond.typecheck(state, &hints, Some(&TypeKind::Bool)); | ||||
|                 let cond_t = state.or_else(cond_res, TypeKind::Vague(Unknown), cond.1); | ||||
|                 let cond_t = state.or_else(cond_res, TypeKind::Vague(Vague::Unknown), cond.1); | ||||
|                 state.ok(cond_t.collapse_into(&TypeKind::Bool), cond.1); | ||||
| 
 | ||||
|                 // Typecheck then/else return types and make sure they are the
 | ||||
| @ -376,14 +380,14 @@ impl Expression { | ||||
|                 let then_res = lhs.typecheck(state, &hints, hint_t); | ||||
|                 let (then_ret_kind, then_ret_t) = state.or_else( | ||||
|                     then_res, | ||||
|                     (ReturnKind::Soft, TypeKind::Vague(Unknown)), | ||||
|                     (ReturnKind::Soft, TypeKind::Vague(Vague::Unknown)), | ||||
|                     lhs.meta, | ||||
|                 ); | ||||
|                 let else_ret_t = if let Some(else_block) = rhs { | ||||
|                     let res = else_block.typecheck(state, &hints, hint_t); | ||||
|                     let (else_ret_kind, else_ret_t) = state.or_else( | ||||
|                         res, | ||||
|                         (ReturnKind::Soft, TypeKind::Vague(Unknown)), | ||||
|                         (ReturnKind::Soft, TypeKind::Vague(Vague::Unknown)), | ||||
|                         else_block.meta, | ||||
|                     ); | ||||
| 
 | ||||
| @ -437,7 +441,7 @@ impl Expression { | ||||
|                     } | ||||
|                     let ty = state.or_else( | ||||
|                         elem_ty.resolve_hinted(hints).collapse_into(&inferred_ty), | ||||
|                         TypeKind::Vague(Unknown), | ||||
|                         TypeKind::Vague(Vague::Unknown), | ||||
|                         self.1, | ||||
|                     ); | ||||
|                     *elem_ty = ty.clone(); | ||||
| @ -477,7 +481,7 @@ impl Expression { | ||||
|                     Err(errors) => { | ||||
|                         state.note_errors(errors, self.1); | ||||
|                         Ok(TypeKind::Array( | ||||
|                             Box::new(TypeKind::Vague(Unknown)), | ||||
|                             Box::new(TypeKind::Vague(Vague::Unknown)), | ||||
|                             expressions.len() as u64, | ||||
|                         )) | ||||
|                     } | ||||
| @ -532,16 +536,7 @@ impl Literal { | ||||
|                 (L::U64(_), TypeKind::U64) => self, | ||||
|                 (L::U128(_), TypeKind::U128) => self, | ||||
|                 (L::Bool(_), TypeKind::Bool) => self, | ||||
|                 (L::String(val), TypeKind::String(len)) => { | ||||
|                     if val.len() == *len { | ||||
|                         L::String(val) | ||||
|                     } else { | ||||
|                         Err(ErrorKind::LiteralIncompatible( | ||||
|                             L::String(val), | ||||
|                             TypeKind::String(*len), | ||||
|                         ))? | ||||
|                     } | ||||
|                 } | ||||
|                 (L::String(val), TypeKind::StringPtr) => self, | ||||
|                 // TODO make sure that v is actually able to fit in the
 | ||||
|                 // requested type
 | ||||
|                 (L::Vague(VagueL::Number(v)), TypeKind::I8) => L::I8(v as i8), | ||||
| @ -575,9 +570,9 @@ impl TypeKind { | ||||
|     fn or_default(&self) -> Result<TypeKind, ErrorKind> { | ||||
|         match self { | ||||
|             TypeKind::Vague(vague_type) => match &vague_type { | ||||
|                 Unknown => Err(ErrorKind::TypeIsVague(*vague_type)), | ||||
|                 Number => Ok(TypeKind::I32), | ||||
|                 TypeRef(_) => panic!("Hinted default!"), | ||||
|                 Vague::Unknown => Err(ErrorKind::TypeIsVague(*vague_type)), | ||||
|                 Vague::Number => Ok(TypeKind::I32), | ||||
|                 Vague::TypeRef(_) => panic!("Hinted default!"), | ||||
|             }, | ||||
|             _ => Ok(self.clone()), | ||||
|         } | ||||
| @ -585,7 +580,7 @@ impl TypeKind { | ||||
| 
 | ||||
|     fn resolve_hinted(&self, hints: &TypeRefs) -> TypeKind { | ||||
|         let resolved = match self { | ||||
|             TypeKind::Vague(TypeRef(idx)) => hints.retrieve_type(*idx).unwrap(), | ||||
|             TypeKind::Vague(Vague::TypeRef(idx)) => hints.retrieve_type(*idx).unwrap(), | ||||
|             _ => self.clone(), | ||||
|         }; | ||||
|         match resolved { | ||||
| @ -608,22 +603,24 @@ impl Collapsable for TypeKind { | ||||
|         } | ||||
| 
 | ||||
|         match (self, other) { | ||||
|             (TypeKind::Vague(Number), other) | (other, TypeKind::Vague(Number)) => match other { | ||||
|                 TypeKind::Vague(Unknown) => Ok(TypeKind::Vague(Number)), | ||||
|                 TypeKind::Vague(Number) => Ok(TypeKind::Vague(Number)), | ||||
|                 TypeKind::I8 | ||||
|                 | TypeKind::I16 | ||||
|                 | TypeKind::I32 | ||||
|                 | TypeKind::I64 | ||||
|                 | TypeKind::I128 | ||||
|                 | TypeKind::U8 | ||||
|                 | TypeKind::U16 | ||||
|                 | TypeKind::U32 | ||||
|                 | TypeKind::U64 | ||||
|                 | TypeKind::U128 => Ok(other.clone()), | ||||
|                 _ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())), | ||||
|             }, | ||||
|             (TypeKind::Vague(Unknown), other) | (other, TypeKind::Vague(Unknown)) => { | ||||
|             (TypeKind::Vague(Vague::Number), other) | (other, TypeKind::Vague(Vague::Number)) => { | ||||
|                 match other { | ||||
|                     TypeKind::Vague(Vague::Unknown) => Ok(TypeKind::Vague(Vague::Number)), | ||||
|                     TypeKind::Vague(Vague::Number) => Ok(TypeKind::Vague(Vague::Number)), | ||||
|                     TypeKind::I8 | ||||
|                     | TypeKind::I16 | ||||
|                     | TypeKind::I32 | ||||
|                     | TypeKind::I64 | ||||
|                     | TypeKind::I128 | ||||
|                     | TypeKind::U8 | ||||
|                     | TypeKind::U16 | ||||
|                     | TypeKind::U32 | ||||
|                     | TypeKind::U64 | ||||
|                     | TypeKind::U128 => Ok(other.clone()), | ||||
|                     _ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())), | ||||
|                 } | ||||
|             } | ||||
|             (TypeKind::Vague(Vague::Unknown), other) | (other, TypeKind::Vague(Vague::Unknown)) => { | ||||
|                 Ok(other.clone()) | ||||
|             } | ||||
|             _ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())), | ||||
|  | ||||
							
								
								
									
										10
									
								
								reid_src/hello_world.reid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								reid_src/hello_world.reid
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| 
 | ||||
| extern fn puts(message: string) -> i32; | ||||
| 
 | ||||
| fn main() -> u16 { | ||||
|     let hello = "hello world"; | ||||
| 
 | ||||
|     puts(hello); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user