// =======================================================
// ReturnInst
// =======================================================

///|
/// ReturnInst represents a return instruction that terminates the current function and optionally returns a value.
///
/// **Note**:
///
/// Use `IRBuilder::createRet` or `IRBuilder::createRetVoid` to create a `ReturnInst`.
///
/// ```mbt check
/// test {
///   let ctx = Context::new()
///   let mod = ctx.addModule("demo")
///   let builder = ctx.createBuilder()
///   let i32_ty = ctx.getInt32Ty()
///   let void_ty = ctx.getVoidTy()
///   let fty = ctx.getFunctionType(i32_ty, [i32_ty])
///   let fval = mod.addFunction(fty, "return_demo")
///   let bb = fval.addBasicBlock(name="entry")
///   let arg = fval.getArg(0).unwrap()
///   builder.setInsertPoint(bb)
///   let ret = builder.createRet(arg)
///   inspect(ret, content="  ret i32 %0")
///   assert_true(ret.asValueEnum() is ReturnInst(_))
///   let void_fty = ctx.getFunctionType(void_ty, [])
///   let void_fval = mod.addFunction(void_fty, "void_return_demo")
///   let void_bb = void_fval.addBasicBlock(name="entry")
///   builder.setInsertPoint(void_bb)
///   let void_ret = builder.createRetVoid()
///   inspect(void_ret, content="  ret void")
/// }
/// ```
pub struct ReturnInst {
  uid : UInt64
  vty : VoidType
  retVal : &Value?
  parent : Function

  // --- InstBase ---
  bb : Ref[BasicBlock?]
  prev : Ref[&Instruction?]
  next : Ref[&Instruction?]
}

///|
fn ReturnInst::new(retVal : &Value?, parent : Function) -> ReturnInst {
  let uid = valueUIDAssigner.assign()
  let bb : Ref[BasicBlock?] = Ref::new(None)
  let prev : Ref[&Instruction?] = Ref::new(None)
  let next : Ref[&Instruction?] = Ref::new(None)
  let vty = parent.getContext().getVoidTy()
  let inst = ReturnInst::{ uid, vty, retVal, parent, bb, prev, next }
  if retVal is Some(val) {
    val.addUser(inst)
  }
  inst
}

///|
pub impl Value for ReturnInst with getValueBase(self) {
  ValueBase::{
    uid: self.uid,
    vty: self.vty, // ReturnInst does not have a value type
    users: [],
  }
}

///|
pub impl Value for ReturnInst with asValueEnum(self) {
  ReturnInst(self)
}

///|
pub impl Value for ReturnInst with getValueRepr(_) {
  ""
}

///|
pub impl Value for ReturnInst with getName(_) {
  None
}

///|
pub impl Value for ReturnInst with setName(_, _) {
  let msg = "Calling always failed function `ReturnInst::setName`. " +
    "Set name for ReturnInst is not allowed."
  raise LLVMValueError(msg)
}

///|
pub impl Value for ReturnInst with removeName(_) {
  ()
}

///|
pub impl Value for ReturnInst with getNameOrSlot(_) {
  None
}

///|
pub impl User for ReturnInst with asUserEnum(self) {
  ReturnInst(self)
}

///|
pub impl User for ReturnInst with getUserBase(self) {
  let operands : Array[&Value] = match self.retVal {
    Some(val) => [val]
    None => []
  }
  UserBase::{ operands, }
}

///|
pub impl Instruction for ReturnInst with getInstBase(self) {
  InstBase::{ bb: self.bb, prev: self.prev, next: self.next }
}

///|
pub impl Instruction for ReturnInst with asInstEnum(self) {
  ReturnInst(self)
}

///|
pub impl Instruction for ReturnInst with getParent(self) {
  self.parent
}

///|
pub impl Show for ReturnInst with output(self, logger) {
  let retStr = match self.retVal {
    Some(val) => "\{val.getType()} \{val.getValueRepr()}"
    None => "void"
  }
  logger.write_string("  ret \{retStr}")
}