// #target: macos-x64
module foo;

extern fn void printf(char*, ...);

fn void test(any z)
{
    switch (z.type)
    {
        case int:
            printf("int: %d\n", *(int*)z);
        case double:
            printf("double %f\n", *(double*)z);
        default:
            printf("Unknown type.\n");
    }
}

fn int main()
{
    test(&&123.0);
    test(&&1);
    test(&&true);
    return 0;
}

/* #expect: foo.ll

@"$ct.int" = linkonce global %.introspect { i8 2, i64 0, ptr null, i64 4, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8
@"$ct.double" = linkonce global %.introspect { i8 4, i64 0, ptr null, i64 8, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8
@"$ct.bool" = linkonce global %.introspect { i8 1, i64 0, ptr null, i64 1, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8

define void @foo.test(i64 %0, ptr %1) #0 {
entry:
  %z = alloca %any, align 8
  %switch = alloca i64, align 8
  store i64 %0, ptr %z, align 8
  %ptradd = getelementptr inbounds i8, ptr %z, i64 8
  store ptr %1, ptr %ptradd, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %z, i64 8
  %2 = load i64, ptr %ptradd1, align 8
  store i64 %2, ptr %switch, align 8
  br label %switch.entry

switch.entry:                                     ; preds = %entry
  %3 = load i64, ptr %switch, align 8
  br label %check_subtype

check_subtype:                                    ; preds = %parent_type_block, %switch.entry
  %4 = phi i64 [ %3, %switch.entry ], [ %typeid.parent, %parent_type_block ]
  %eq = icmp eq i64 ptrtoint (ptr @"$ct.int" to i64), %4
  br i1 %eq, label %result_block, label %parent_type_block

parent_type_block:                                ; preds = %check_subtype
  %5 = inttoptr i64 %4 to ptr
  %ptradd2 = getelementptr inbounds i8, ptr %5, i64 8
  %typeid.parent = load i64, ptr %ptradd2, align 8
  %6 = icmp eq i64 %typeid.parent, 0
  br i1 %6, label %result_block, label %check_subtype

result_block:                                     ; preds = %parent_type_block, %check_subtype
  %7 = phi i1 [ false, %parent_type_block ], [ true, %check_subtype ]
  br i1 %7, label %switch.case, label %next_if

switch.case:                                      ; preds = %result_block
  %8 = load ptr, ptr %z, align 8
  %9 = load i32, ptr %8, align 4
  call void (ptr, ...) @printf(ptr @.str, i32 %9)
  br label %switch.exit

next_if:                                          ; preds = %result_block
  br label %check_subtype3

check_subtype3:                                   ; preds = %parent_type_block5, %next_if
  %10 = phi i64 [ %3, %next_if ], [ %typeid.parent7, %parent_type_block5 ]
  %eq4 = icmp eq i64 ptrtoint (ptr @"$ct.double" to i64), %10
  br i1 %eq4, label %result_block8, label %parent_type_block5

parent_type_block5:                               ; preds = %check_subtype3
  %11 = inttoptr i64 %10 to ptr
  %ptradd6 = getelementptr inbounds i8, ptr %11, i64 8
  %typeid.parent7 = load i64, ptr %ptradd6, align 8
  %12 = icmp eq i64 %typeid.parent7, 0
  br i1 %12, label %result_block8, label %check_subtype3

result_block8:                                    ; preds = %parent_type_block5, %check_subtype3
  %13 = phi i1 [ false, %parent_type_block5 ], [ true, %check_subtype3 ]
  br i1 %13, label %switch.case9, label %next_if10

switch.case9:                                     ; preds = %result_block8
  %14 = load ptr, ptr %z, align 8
  %15 = load double, ptr %14, align 8
  call void (ptr, ...) @printf(ptr @.str.1, double %15)
  br label %switch.exit

next_if10:                                        ; preds = %result_block8
  br label %switch.default

switch.default:                                   ; preds = %next_if10
  call void (ptr, ...) @printf(ptr @.str.2)
  br label %switch.exit

switch.exit:                                      ; preds = %switch.default, %switch.case9, %switch.case
  ret void
}

define i32 @main() #0 {
entry:
  %taddr = alloca double, align 8
  %taddr1 = alloca %any, align 8
  %taddr2 = alloca i32, align 4
  %taddr3 = alloca %any, align 8
  %taddr7 = alloca i8, align 1
  %taddr8 = alloca %any, align 8
  store double 1.230000e+02, ptr %taddr, align 8
  %0 = insertvalue %any undef, ptr %taddr, 0
  %1 = insertvalue %any %0, i64 ptrtoint (ptr @"$ct.double" to i64), 1
  store %any %1, ptr %taddr1, align 8
  %lo = load i64, ptr %taddr1, align 8
  %ptradd = getelementptr inbounds i8, ptr %taddr1, i64 8
  %hi = load ptr, ptr %ptradd, align 8
  call void @foo.test(i64 %lo, ptr %hi)
  store i32 1, ptr %taddr2, align 4
  %2 = insertvalue %any undef, ptr %taddr2, 0
  %3 = insertvalue %any %2, i64 ptrtoint (ptr @"$ct.int" to i64), 1
  store %any %3, ptr %taddr3, align 8
  %lo4 = load i64, ptr %taddr3, align 8
  %ptradd5 = getelementptr inbounds i8, ptr %taddr3, i64 8
  %hi6 = load ptr, ptr %ptradd5, align 8
  call void @foo.test(i64 %lo4, ptr %hi6)
  store i8 1, ptr %taddr7, align 1
  %4 = insertvalue %any undef, ptr %taddr7, 0
  %5 = insertvalue %any %4, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
  store %any %5, ptr %taddr8, align 8
  %lo9 = load i64, ptr %taddr8, align 8
  %ptradd10 = getelementptr inbounds i8, ptr %taddr8, i64 8
  %hi11 = load ptr, ptr %ptradd10, align 8
  call void @foo.test(i64 %lo9, ptr %hi11)
  ret i32 0
}