// #target: macos-x64
module foo;
extern fn void printf(char*, ...);

fn void test(any x)
{
    switch (x.type)
    {
        case int:
            printf("Was int\n");
        case double:
            printf("Was double\n");
        case any:
            printf("Was variant\n");
        case int*:
            printf("Was int*\n");
        default:
            printf("Unknown type\n");
    }
}

fn void test_all(any... y)
{
    foreach (element : y)
    {
        test(element);
    }
}

fn void main()
{
    any x = &&1;
    int z;
    any y = &z;
    typeid g = y.type;
    typeid h = x.type;
    if (y.type == int.typeid)
    {
        printf("y int match\n");
    }
    if (x.type == int.typeid)
    {
        printf("x int match\n");
    }
    y = &&1.0;
    x = &x;
    if (y.type == int.typeid)
    {
        printf("y int match\n");
    }
    if (x.type == int.typeid)
    {
        printf("x int match\n");
    }
    test(x);
    test(&&1.0);
    test(&&1);
    test(&&true);
    printf("----\n");
    int* df = null;
    test_all(x, x, &&1.0, &x, &df);
}

/* #expect: foo.ll

%any = type { ptr, i64 }
%"any[]" = type { ptr, i64 }
@"$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.any$" = linkonce global %.introspect { i8 7, i64 0, ptr null, i64 16, i64 0, i64 0, [0 x i64] zeroinitializer }, align 8
@"$ct.p$int" = linkonce global %.introspect { i8 19, i64 0, ptr null, i64 8, i64 ptrtoint (ptr @"$ct.int" to i64), 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:
  %x = alloca %any, align 8
  %switch = alloca i64, align 8
  store i64 %0, ptr %x, align 8
  %ptradd = getelementptr inbounds i8, ptr %x, i64 8
  store ptr %1, ptr %ptradd, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %x, 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
  call void (ptr, ...) @printf(ptr @.str)
  br label %switch.exit

next_if:                                          ; preds = %result_block
  br label %check_subtype3

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

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

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

switch.case9:                                     ; preds = %result_block8
  call void (ptr, ...) @printf(ptr @.str.1)
  br label %switch.exit

next_if10:                                        ; preds = %result_block8
  br label %check_subtype11

check_subtype11:                                  ; preds = %parent_type_block13, %next_if10
  %12 = phi i64 [ %3, %next_if10 ], [ %typeid.parent15, %parent_type_block13 ]
  %eq12 = icmp eq i64 ptrtoint (ptr @"$ct.any$" to i64), %12
  br i1 %eq12, label %result_block16, label %parent_type_block13

parent_type_block13:                              ; preds = %check_subtype11
  %13 = inttoptr i64 %12 to ptr
  %ptradd14 = getelementptr inbounds i8, ptr %13, i64 8
  %typeid.parent15 = load i64, ptr %ptradd14, align 8
  %14 = icmp eq i64 %typeid.parent15, 0
  br i1 %14, label %result_block16, label %check_subtype11

result_block16:                                   ; preds = %parent_type_block13, %check_subtype11
  %15 = phi i1 [ false, %parent_type_block13 ], [ true, %check_subtype11 ]
  br i1 %15, label %switch.case17, label %next_if18

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

next_if18:                                        ; preds = %result_block16
  br label %check_subtype19

check_subtype19:                                  ; preds = %parent_type_block21, %next_if18
  %16 = phi i64 [ %3, %next_if18 ], [ %typeid.parent23, %parent_type_block21 ]
  %eq20 = icmp eq i64 ptrtoint (ptr @"$ct.p$int" to i64), %16
  br i1 %eq20, label %result_block24, label %parent_type_block21

parent_type_block21:                              ; preds = %check_subtype19
  %17 = inttoptr i64 %16 to ptr
  %ptradd22 = getelementptr inbounds i8, ptr %17, i64 8
  %typeid.parent23 = load i64, ptr %ptradd22, align 8
  %18 = icmp eq i64 %typeid.parent23, 0
  br i1 %18, label %result_block24, label %check_subtype19

result_block24:                                   ; preds = %parent_type_block21, %check_subtype19
  %19 = phi i1 [ false, %parent_type_block21 ], [ true, %check_subtype19 ]
  br i1 %19, label %switch.case25, label %next_if26

switch.case25:                                    ; preds = %result_block24
  call void (ptr, ...) @printf(ptr @.str.3)
  br label %switch.exit

next_if26:                                        ; preds = %result_block24
  br label %switch.default

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

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

; Function Attrs:
define void @foo.test_all(ptr %0, i64 %1) #0 {
entry:
  %y = alloca %"any[]", align 8
  %.anon = alloca i64, align 8
  %element = alloca %any, align 8
  store ptr %0, ptr %y, align 8
  %ptradd = getelementptr inbounds i8, ptr %y, i64 8
  store i64 %1, ptr %ptradd, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %y, i64 8
  %2 = load i64, ptr %ptradd1, align 8
  store i64 0, ptr %.anon, align 8
  br label %loop.cond

loop.cond:                                        ; preds = %loop.body, %entry
  %3 = load i64, ptr %.anon, align 8
  %lt = icmp ult i64 %3, %2
  br i1 %lt, label %loop.body, label %loop.exit

loop.body:                                        ; preds = %loop.cond
  %4 = load ptr, ptr %y, align 8
  %5 = load i64, ptr %.anon, align 8
  %ptroffset = getelementptr inbounds [16 x i8], ptr %4, i64 %5
  call void @llvm.memcpy.p0.p0.i32(ptr align 8 %element, ptr align 8 %ptroffset, i32 16, i1 false)
  %lo = load i64, ptr %element, align 8
  %ptradd2 = getelementptr inbounds i8, ptr %element, i64 8
  %hi = load ptr, ptr %ptradd2, align 8
  call void @foo.test(i64 %lo, ptr %hi)
  %6 = load i64, ptr %.anon, align 8
  %addnuw = add nuw i64 %6, 1
  store i64 %addnuw, ptr %.anon, align 8
  br label %loop.cond

loop.exit:                                        ; preds = %loop.cond
  ret void
}

; Function Attrs:
define void @foo.main() #0 {
entry:
  %x = alloca %any, align 8
  %taddr = alloca i32, align 4
  %z = alloca i32, align 4
  %y = alloca %any, align 8
  %g = alloca i64, align 8
  %h = alloca i64, align 8
  %taddr7 = alloca double, align 8
  %taddr17 = alloca double, align 8
  %taddr18 = alloca %any, align 8
  %taddr22 = alloca i32, align 4
  %taddr23 = alloca %any, align 8
  %taddr27 = alloca i8, align 1
  %taddr28 = alloca %any, align 8
  %df = alloca ptr, align 8
  %varargslots = alloca [5 x %any], align 16
  %taddr33 = alloca double, align 8
  store i32 1, ptr %taddr, align 4
  %0 = insertvalue %any undef, ptr %taddr, 0
  %1 = insertvalue %any %0, i64 ptrtoint (ptr @"$ct.int" to i64), 1
  store %any %1, ptr %x, align 8
  store i32 0, ptr %z, align 4
  %2 = insertvalue %any undef, ptr %z, 0
  %3 = insertvalue %any %2, i64 ptrtoint (ptr @"$ct.int" to i64), 1
  store %any %3, ptr %y, align 8
  %ptradd = getelementptr inbounds i8, ptr %y, i64 8
  %4 = load i64, ptr %ptradd, align 8
  store i64 %4, ptr %g, align 8
  %ptradd1 = getelementptr inbounds i8, ptr %x, i64 8
  %5 = load i64, ptr %ptradd1, align 8
  store i64 %5, ptr %h, align 8
  %ptradd2 = getelementptr inbounds i8, ptr %y, i64 8
  %6 = load i64, ptr %ptradd2, align 8
  %eq = icmp eq i64 %6, ptrtoint (ptr @"$ct.int" to i64)
  br i1 %eq, label %if.then, label %if.exit

if.then:                                          ; preds = %entry
  call void (ptr, ...) @printf(ptr @.str.5)
  br label %if.exit

if.exit:                                          ; preds = %if.then, %entry
  %ptradd3 = getelementptr inbounds i8, ptr %x, i64 8
  %7 = load i64, ptr %ptradd3, align 8
  %eq4 = icmp eq i64 %7, ptrtoint (ptr @"$ct.int" to i64)
  br i1 %eq4, label %if.then5, label %if.exit6

if.then5:                                         ; preds = %if.exit
  call void (ptr, ...) @printf(ptr @.str.6)
  br label %if.exit6

if.exit6:                                         ; preds = %if.then5, %if.exit
  store double 1.000000e+00, ptr %taddr7, align 8
  %8 = insertvalue %any undef, ptr %taddr7, 0
  %9 = insertvalue %any %8, i64 ptrtoint (ptr @"$ct.double" to i64), 1
  store %any %9, ptr %y, align 8
  %10 = insertvalue %any undef, ptr %x, 0
  %11 = insertvalue %any %10, i64 ptrtoint (ptr @"$ct.any$" to i64), 1
  store %any %11, ptr %x, align 8
  %ptradd8 = getelementptr inbounds i8, ptr %y, i64 8
  %12 = load i64, ptr %ptradd8, align 8
  %eq9 = icmp eq i64 %12, ptrtoint (ptr @"$ct.int" to i64)
  br i1 %eq9, label %if.then10, label %if.exit11

if.then10:                                        ; preds = %if.exit6
  call void (ptr, ...) @printf(ptr @.str.7)
  br label %if.exit11

if.exit11:                                        ; preds = %if.then10, %if.exit6
  %ptradd12 = getelementptr inbounds i8, ptr %x, i64 8
  %13 = load i64, ptr %ptradd12, align 8
  %eq13 = icmp eq i64 %13, ptrtoint (ptr @"$ct.int" to i64)
  br i1 %eq13, label %if.then14, label %if.exit15

if.then14:                                        ; preds = %if.exit11
  call void (ptr, ...) @printf(ptr @.str.8)
  br label %if.exit15

if.exit15:                                        ; preds = %if.then14, %if.exit11
  %lo = load i64, ptr %x, align 8
  %ptradd16 = getelementptr inbounds i8, ptr %x, i64 8
  %hi = load ptr, ptr %ptradd16, align 8
  call void @foo.test(i64 %lo, ptr %hi)
  store double 1.000000e+00, ptr %taddr17, align 8
  %14 = insertvalue %any undef, ptr %taddr17, 0
  %15 = insertvalue %any %14, i64 ptrtoint (ptr @"$ct.double" to i64), 1
  store %any %15, ptr %taddr18, align 8
  %lo19 = load i64, ptr %taddr18, align 8
  %ptradd20 = getelementptr inbounds i8, ptr %taddr18, i64 8
  %hi21 = load ptr, ptr %ptradd20, align 8
  call void @foo.test(i64 %lo19, ptr %hi21)
  store i32 1, ptr %taddr22, align 4
  %16 = insertvalue %any undef, ptr %taddr22, 0
  %17 = insertvalue %any %16, i64 ptrtoint (ptr @"$ct.int" to i64), 1
  store %any %17, ptr %taddr23, align 8
  %lo24 = load i64, ptr %taddr23, align 8
  %ptradd25 = getelementptr inbounds i8, ptr %taddr23, i64 8
  %hi26 = load ptr, ptr %ptradd25, align 8
  call void @foo.test(i64 %lo24, ptr %hi26)
  store i8 1, ptr %taddr27, align 1
  %18 = insertvalue %any undef, ptr %taddr27, 0
  %19 = insertvalue %any %18, i64 ptrtoint (ptr @"$ct.bool" to i64), 1
  store %any %19, ptr %taddr28, align 8
  %lo29 = load i64, ptr %taddr28, align 8
  %ptradd30 = getelementptr inbounds i8, ptr %taddr28, i64 8
  %hi31 = load ptr, ptr %ptradd30, align 8
  call void @foo.test(i64 %lo29, ptr %hi31)
  call void (ptr, ...) @printf(ptr @.str.9)
  store ptr null, ptr %df, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 16 %varargslots, ptr align 8 %x, i32 16, i1 false)
  %ptradd32 = getelementptr inbounds i8, ptr %varargslots, i64 16
  call void @llvm.memcpy.p0.p0.i32(ptr align 16 %ptradd32, ptr align 8 %x, i32 16, i1 false)
  store double 1.000000e+00, ptr %taddr33, align 8
  %20 = insertvalue %any undef, ptr %taddr33, 0
  %21 = insertvalue %any %20, i64 ptrtoint (ptr @"$ct.double" to i64), 1
  %ptradd34 = getelementptr inbounds i8, ptr %varargslots, i64 32
  store %any %21, ptr %ptradd34, align 16
  %22 = insertvalue %any undef, ptr %x, 0
  %23 = insertvalue %any %22, i64 ptrtoint (ptr @"$ct.any$" to i64), 1
  %ptradd35 = getelementptr inbounds i8, ptr %varargslots, i64 48
  store %any %23, ptr %ptradd35, align 16
  %24 = insertvalue %any undef, ptr %df, 0
  %25 = insertvalue %any %24, i64 ptrtoint (ptr @"$ct.p$int" to i64), 1
  %ptradd36 = getelementptr inbounds i8, ptr %varargslots, i64 64
  store %any %25, ptr %ptradd36, align 16
  call void @foo.test_all(ptr %varargslots, i64 5)
  ret void
}
