// #target: macos-x64
module demo;
import libc;
import std::io;

faultdef DIVISION_BY_ZERO;

fn int foo() { return 123; }
fn int bar() { return 0; }

fn double? divide(int a, int b)
{
    if (b == 0) return DIVISION_BY_ZERO~;
    return (double)(a) / (double)(b);

}

// Rethrowing an error uses "!" suffix
fn void? testMayError()
{
    divide(foo(), bar())!;
}

fn void main()
{
    // ratio has an optional type.
    double? ratio = divide(foo(), bar());

    // Handle the error
    if (catch err = ratio)
    {
        if (err == DIVISION_BY_ZERO)
        {
            libc::printf("Division by zero\n");
            return;
        }
        libc::printf("Unexpected error!");
        return;
    }
    // Flow typing makes "ratio"
    // have the type double here.
    libc::printf("Ratio was %f\n", ratio);
}

/* #expect: demo.ll


define i64 @demo.divide(ptr %0, i32 %1, i32 %2) #0 {
entry:
  %reterr = alloca i64, align 8
  %eq = icmp eq i32 %2, 0
  br i1 %eq, label %if.then, label %if.exit

if.then:                                          ; preds = %entry
  ret i64 ptrtoint (ptr @demo.DIVISION_BY_ZERO to i64)

if.exit:                                          ; preds = %entry
  %sifp = sitofp i32 %1 to double
  %sifp1 = sitofp i32 %2 to double
  %fdiv = fdiv double %sifp, %sifp1
  store double %fdiv, ptr %0, align 8
  ret i64 0
}

define i64 @demo.testMayError() #0 {
entry:
  %error_var = alloca i64, align 8
  %retparam = alloca double, align 8
  %0 = call i32 @demo.foo()
  %1 = call i32 @demo.bar()
  %2 = call i64 @demo.divide(ptr %retparam, i32 %0, i32 %1)
  %not_err = icmp eq i64 %2, 0
  %3 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
  br i1 %3, label %after_check, label %assign_optional

assign_optional:                                  ; preds = %entry
  store i64 %2, ptr %error_var, align 8
  br label %guard_block

after_check:                                      ; preds = %entry
  br label %noerr_block

guard_block:                                      ; preds = %assign_optional
  %4 = load i64, ptr %error_var, align 8
  ret i64 %4

noerr_block:                                      ; preds = %after_check
  ret i64 0
}

define void @demo.main() #0 {
entry:
  %ratio = alloca double, align 8
  %ratio.f = alloca i64, align 8
  %retparam = alloca double, align 8
  %err = alloca i64, align 8
  %0 = call i32 @demo.foo()
  %1 = call i32 @demo.bar()
  %2 = call i64 @demo.divide(ptr %retparam, i32 %0, i32 %1)
  %not_err = icmp eq i64 %2, 0
  %3 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
  br i1 %3, label %after_check, label %assign_optional

assign_optional:                                  ; preds = %entry
  store i64 %2, ptr %ratio.f, align 8
  br label %after_assign

after_check:                                      ; preds = %entry
  %4 = load double, ptr %retparam, align 8
  store double %4, ptr %ratio, align 8
  store i64 0, ptr %ratio.f, align 8
  br label %after_assign

after_assign:                                     ; preds = %after_check, %assign_optional
  br label %testblock

testblock:                                        ; preds = %after_assign
  %optval = load i64, ptr %ratio.f, align 8
  %not_err1 = icmp eq i64 %optval, 0
  %5 = call i1 @llvm.expect.i1(i1 %not_err1, i1 true)
  br i1 %5, label %after_check3, label %assign_optional2

assign_optional2:                                 ; preds = %testblock
  store i64 %optval, ptr %err, align 8
  br label %end_block

after_check3:                                     ; preds = %testblock
  store i64 0, ptr %err, align 8
  br label %end_block

end_block:                                        ; preds = %after_check3, %assign_optional2
  %6 = load i64, ptr %err, align 8
  %i2b = icmp ne i64 %6, 0
  br i1 %i2b, label %if.then, label %if.exit5

if.then:                                          ; preds = %end_block
  %7 = load i64, ptr %err, align 8
  %eq = icmp eq i64 %7, ptrtoint (ptr @demo.DIVISION_BY_ZERO to i64)
  br i1 %eq, label %if.then4, label %if.exit

if.then4:                                         ; preds = %if.then
  %8 = call i32 (ptr, ...) @printf(ptr @.str)
  ret void

if.exit:                                          ; preds = %if.then
  %9 = call i32 (ptr, ...) @printf(ptr @.str.1)
  ret void

if.exit5:                                         ; preds = %end_block
  %10 = load double, ptr %ratio, align 8
  %11 = call i32 (ptr, ...) @printf(ptr @.str.2, double %10)
  ret void
}