// #target: macos-x64
module foo;

struct Foo
{
    int[3] a;
}

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

macro int* Foo.@operator_element_at_ref(&f, int a) @operator(&[])
{
    return &f.a[a];
}

macro int Foo.@operator_len(&f) @operator(len)
{
    return 3;
}

macro int Foo.@operator_element_at(&f, int a) @operator([])
{
    return f.a[a];
}

fn int[5] getFields()
{
    printf("getFields\n");
    return (int[5]) { 3, 5, 2, 10, 111};
}
fn Foo *call(Foo* f)
{
    printf("Call made\n");
    return f;
}
fn void main()
{
    Foo x = { { 1, 5, 7} };
    printf("%d %d %d\n", x[0], x[1], x[2]);
    foreach (i, int y : *call(&x))
    {
        printf("Hello %d: %d\n", i, y);
    }
    foreach (i, int* &y : x)
    {
        *y += 1;
        printf("Hello %d: %d\n", i, *y);
    }
    foreach (i, int y : x)
    {
        printf("After one %d: %d\n", i, y);
    }

    foreach (i, int y : &x)
    {
        printf("By pointer %d: %d\n", i, y);
    }

    foreach (i, int y : x)
    {
        printf("Adding %d: %d\n", i, y);
        i++;
    }

    foreach(i, y : (int[5]) { 1, 2, 10, 111, 123 } )
    {
        printf("Adding %d: %d\n", i, y);
        i++;
    }
    foreach(i, y : getFields() )
    {
        printf("Pull value %d: %d\n", i, y);
    }
    foreach(i, y : &&getFields())
    {
        printf("Pull value tempptr %d: %d\n", i, y);
    }
    printf("%d %d\n", x[0], x[1]);
    int* y = &x[1];
    *y += 1;
    printf("%d %d\n", x[0], x[1]);
}

/* #expect: foo.ll

%Foo = type { [3 x i32] }

@"$ct.foo.Foo" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 12, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8
@.str = private unnamed_addr constant [11 x i8] c"getFields\0A\00", align 1
@.__const = private unnamed_addr constant [5 x i32] [i32 3, i32 5, i32 2, i32 10, i32 111], align 16
@.str.1 = private unnamed_addr constant [11 x i8] c"Call made\0A\00", align 1
@.__const.2 = private unnamed_addr constant %Foo { [3 x i32] [i32 1, i32 5, i32 7] }, align 4
@.str.3 = private unnamed_addr constant [10 x i8] c"%d %d %d\0A\00", align 1
@.str.4 = private unnamed_addr constant [14 x i8] c"Hello %d: %d\0A\00", align 1
@.str.5 = private unnamed_addr constant [14 x i8] c"Hello %d: %d\0A\00", align 1
@.str.6 = private unnamed_addr constant [18 x i8] c"After one %d: %d\0A\00", align 1
@.str.7 = private unnamed_addr constant [19 x i8] c"By pointer %d: %d\0A\00", align 1
@.str.8 = private unnamed_addr constant [15 x i8] c"Adding %d: %d\0A\00", align 1
@.__const.9 = private unnamed_addr constant [5 x i32] [i32 1, i32 2, i32 10, i32 111, i32 123], align 16
@.str.10 = private unnamed_addr constant [15 x i8] c"Adding %d: %d\0A\00", align 1
@.str.11 = private unnamed_addr constant [19 x i8] c"Pull value %d: %d\0A\00", align 1
@.str.12 = private unnamed_addr constant [27 x i8] c"Pull value tempptr %d: %d\0A\00", align 1
@.str.13 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1
@.str.14 = private unnamed_addr constant [7 x i8] c"%d %d\0A\00", align 1

; Function Attrs:
declare void @printf(ptr, ...) #0

; Function Attrs:
define void @foo.getFields(ptr noalias sret([5 x i32]) align 4 %0) #0 {
entry:
  %literal = alloca [5 x i32], align 16
  call void (ptr, ...) @printf(ptr @.str)
  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %literal, ptr align 16 @.__const, i32 20, i1 false)
  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %0, ptr align 4 %literal, i32 20, i1 false)
  ret void
}

; Function Attrs:
define ptr @foo.call(ptr %0) #0 {
entry:
  call void (ptr, ...) @printf(ptr @.str.1)
  ret ptr %0
}

; Function Attrs:
define void @foo.main() #0 {
entry:
  %x = alloca %Foo, align 4
  %.anon = alloca i32, align 4
  %i = alloca i32, align 4
  %y = alloca i32, align 4
  %a = alloca i32, align 4
  %.anon7 = alloca i32, align 4
  %i11 = alloca i32, align 4
  %y12 = alloca ptr, align 8
  %a13 = alloca i32, align 4
  %.anon20 = alloca i32, align 4
  %i24 = alloca i32, align 4
  %y25 = alloca i32, align 4
  %a26 = alloca i32, align 4
  %.anon33 = alloca i32, align 4
  %i37 = alloca i32, align 4
  %y38 = alloca i32, align 4
  %a39 = alloca i32, align 4
  %.anon46 = alloca i32, align 4
  %i50 = alloca i32, align 4
  %y51 = alloca i32, align 4
  %a52 = alloca i32, align 4
  %.anon59 = alloca [5 x i32], align 16
  %.anon60 = alloca i64, align 8
  %i63 = alloca i64, align 8
  %y64 = alloca i32, align 4
  %.anon68 = alloca [5 x i32], align 16
  %.anon69 = alloca i64, align 8
  %i73 = alloca i64, align 8
  %y74 = alloca i32, align 4
  %sretparam = alloca [5 x i32], align 4
  %.anon78 = alloca i64, align 8
  %i82 = alloca i64, align 8
  %y83 = alloca i32, align 4
  %y90 = alloca ptr, align 8
  call void @llvm.memcpy.p0.p0.i32(ptr align 4 %x, ptr align 4 @.__const.2, i32 12, i1 false)
  %neq = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq)
  %neq1 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq1)
  %ptradd = getelementptr inbounds i8, ptr %x, i64 4
  %neq2 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq2)
  %ptradd3 = getelementptr inbounds i8, ptr %x, i64 8
  %0 = load i32, ptr %x, align 4
  %1 = load i32, ptr %ptradd, align 4
  %2 = load i32, ptr %ptradd3, align 4
  call void (ptr, ...) @printf(ptr @.str.3, i32 %0, i32 %1, i32 %2)
  %3 = call ptr @foo.call(ptr %x)
  %neq4 = icmp ne ptr %3, null
  call void @llvm.assume(i1 %neq4)
  store i32 0, ptr %.anon, align 4
  br label %loop.cond

loop.cond:                                        ; preds = %loop.body, %entry
  %4 = load i32, ptr %.anon, align 4
  %lt = icmp slt i32 %4, 3
  br i1 %lt, label %loop.body, label %loop.exit

loop.body:                                        ; preds = %loop.cond
  %5 = load i32, ptr %.anon, align 4
  store i32 %5, ptr %i, align 4
  %6 = load i32, ptr %.anon, align 4
  store i32 %6, ptr %a, align 4
  %neq5 = icmp ne ptr %3, null
  call void @llvm.assume(i1 %neq5)
  %7 = load i32, ptr %a, align 4
  %sext = sext i32 %7 to i64
  %ptroffset = getelementptr inbounds [4 x i8], ptr %3, i64 %sext
  %8 = load i32, ptr %ptroffset, align 4
  store i32 %8, ptr %y, align 4
  %9 = load i32, ptr %i, align 4
  %10 = load i32, ptr %y, align 4
  call void (ptr, ...) @printf(ptr @.str.4, i32 %9, i32 %10)
  %11 = load i32, ptr %.anon, align 4
  %addnsw = add nsw i32 %11, 1
  store i32 %addnsw, ptr %.anon, align 4
  br label %loop.cond

loop.exit:                                        ; preds = %loop.cond
  %neq6 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq6)
  store i32 0, ptr %.anon7, align 4
  br label %loop.cond8

loop.cond8:                                       ; preds = %loop.body10, %loop.exit
  %12 = load i32, ptr %.anon7, align 4
  %lt9 = icmp slt i32 %12, 3
  br i1 %lt9, label %loop.body10, label %loop.exit18

loop.body10:                                      ; preds = %loop.cond8
  %13 = load i32, ptr %.anon7, align 4
  store i32 %13, ptr %i11, align 4
  %14 = load i32, ptr %.anon7, align 4
  store i32 %14, ptr %a13, align 4
  %neq14 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq14)
  %15 = load i32, ptr %a13, align 4
  %sext15 = sext i32 %15 to i64
  %ptroffset16 = getelementptr inbounds [4 x i8], ptr %x, i64 %sext15
  store ptr %ptroffset16, ptr %y12, align 8
  %16 = load ptr, ptr %y12, align 8
  %17 = load i32, ptr %16, align 4
  %add = add i32 %17, 1
  store i32 %add, ptr %16, align 4
  %18 = load ptr, ptr %y12, align 8
  %19 = load i32, ptr %i11, align 4
  %20 = load i32, ptr %18, align 4
  call void (ptr, ...) @printf(ptr @.str.5, i32 %19, i32 %20)
  %21 = load i32, ptr %.anon7, align 4
  %addnsw17 = add nsw i32 %21, 1
  store i32 %addnsw17, ptr %.anon7, align 4
  br label %loop.cond8

loop.exit18:                                      ; preds = %loop.cond8
  %neq19 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq19)
  store i32 0, ptr %.anon20, align 4
  br label %loop.cond21

loop.cond21:                                      ; preds = %loop.body23, %loop.exit18
  %22 = load i32, ptr %.anon20, align 4
  %lt22 = icmp slt i32 %22, 3
  br i1 %lt22, label %loop.body23, label %loop.exit31

loop.body23:                                      ; preds = %loop.cond21
  %23 = load i32, ptr %.anon20, align 4
  store i32 %23, ptr %i24, align 4
  %24 = load i32, ptr %.anon20, align 4
  store i32 %24, ptr %a26, align 4
  %neq27 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq27)
  %25 = load i32, ptr %a26, align 4
  %sext28 = sext i32 %25 to i64
  %ptroffset29 = getelementptr inbounds [4 x i8], ptr %x, i64 %sext28
  %26 = load i32, ptr %ptroffset29, align 4
  store i32 %26, ptr %y25, align 4
  %27 = load i32, ptr %i24, align 4
  %28 = load i32, ptr %y25, align 4
  call void (ptr, ...) @printf(ptr @.str.6, i32 %27, i32 %28)
  %29 = load i32, ptr %.anon20, align 4
  %addnsw30 = add nsw i32 %29, 1
  store i32 %addnsw30, ptr %.anon20, align 4
  br label %loop.cond21

loop.exit31:                                      ; preds = %loop.cond21
  %neq32 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq32)
  store i32 0, ptr %.anon33, align 4
  br label %loop.cond34

loop.cond34:                                      ; preds = %loop.body36, %loop.exit31
  %30 = load i32, ptr %.anon33, align 4
  %lt35 = icmp slt i32 %30, 3
  br i1 %lt35, label %loop.body36, label %loop.exit44

loop.body36:                                      ; preds = %loop.cond34
  %31 = load i32, ptr %.anon33, align 4
  store i32 %31, ptr %i37, align 4
  %32 = load i32, ptr %.anon33, align 4
  store i32 %32, ptr %a39, align 4
  %neq40 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq40)
  %33 = load i32, ptr %a39, align 4
  %sext41 = sext i32 %33 to i64
  %ptroffset42 = getelementptr inbounds [4 x i8], ptr %x, i64 %sext41
  %34 = load i32, ptr %ptroffset42, align 4
  store i32 %34, ptr %y38, align 4
  %35 = load i32, ptr %i37, align 4
  %36 = load i32, ptr %y38, align 4
  call void (ptr, ...) @printf(ptr @.str.7, i32 %35, i32 %36)
  %37 = load i32, ptr %.anon33, align 4
  %addnsw43 = add nsw i32 %37, 1
  store i32 %addnsw43, ptr %.anon33, align 4
  br label %loop.cond34

loop.exit44:                                      ; preds = %loop.cond34
  %neq45 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq45)
  store i32 0, ptr %.anon46, align 4
  br label %loop.cond47

loop.cond47:                                      ; preds = %loop.body49, %loop.exit44
  %38 = load i32, ptr %.anon46, align 4
  %lt48 = icmp slt i32 %38, 3
  br i1 %lt48, label %loop.body49, label %loop.exit58

loop.body49:                                      ; preds = %loop.cond47
  %39 = load i32, ptr %.anon46, align 4
  store i32 %39, ptr %i50, align 4
  %40 = load i32, ptr %.anon46, align 4
  store i32 %40, ptr %a52, align 4
  %neq53 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq53)
  %41 = load i32, ptr %a52, align 4
  %sext54 = sext i32 %41 to i64
  %ptroffset55 = getelementptr inbounds [4 x i8], ptr %x, i64 %sext54
  %42 = load i32, ptr %ptroffset55, align 4
  store i32 %42, ptr %y51, align 4
  %43 = load i32, ptr %i50, align 4
  %44 = load i32, ptr %y51, align 4
  call void (ptr, ...) @printf(ptr @.str.8, i32 %43, i32 %44)
  %45 = load i32, ptr %i50, align 4
  %add56 = add i32 %45, 1
  store i32 %add56, ptr %i50, align 4
  %46 = load i32, ptr %.anon46, align 4
  %addnsw57 = add nsw i32 %46, 1
  store i32 %addnsw57, ptr %.anon46, align 4
  br label %loop.cond47

loop.exit58:                                      ; preds = %loop.cond47
  call void @llvm.memcpy.p0.p0.i32(ptr align 16 %.anon59, ptr align 16 @.__const.9, i32 20, i1 false)
  store i64 0, ptr %.anon60, align 8
  br label %loop.cond61

loop.cond61:                                      ; preds = %loop.body62, %loop.exit58
  %47 = load i64, ptr %.anon60, align 8
  %gt = icmp ugt i64 5, %47
  br i1 %gt, label %loop.body62, label %loop.exit67

loop.body62:                                      ; preds = %loop.cond61
  %48 = load i64, ptr %.anon60, align 8
  store i64 %48, ptr %i63, align 8
  %49 = load i64, ptr %.anon60, align 8
  %ptroffset65 = getelementptr inbounds [4 x i8], ptr %.anon59, i64 %49
  %50 = load i32, ptr %ptroffset65, align 4
  store i32 %50, ptr %y64, align 4
  %51 = load i64, ptr %i63, align 8
  %52 = load i32, ptr %y64, align 4
  call void (ptr, ...) @printf(ptr @.str.10, i64 %51, i32 %52)
  %53 = load i64, ptr %i63, align 8
  %add66 = add i64 %53, 1
  store i64 %add66, ptr %i63, align 8
  %54 = load i64, ptr %.anon60, align 8
  %addnuw = add nuw i64 %54, 1
  store i64 %addnuw, ptr %.anon60, align 8
  br label %loop.cond61

loop.exit67:                                      ; preds = %loop.cond61
  call void @foo.getFields(ptr sret([5 x i32]) align 4 %.anon68)
  store i64 0, ptr %.anon69, align 8
  br label %loop.cond70

loop.cond70:                                      ; preds = %loop.body72, %loop.exit67
  %55 = load i64, ptr %.anon69, align 8
  %gt71 = icmp ugt i64 5, %55
  br i1 %gt71, label %loop.body72, label %loop.exit77

loop.body72:                                      ; preds = %loop.cond70
  %56 = load i64, ptr %.anon69, align 8
  store i64 %56, ptr %i73, align 8
  %57 = load i64, ptr %.anon69, align 8
  %ptroffset75 = getelementptr inbounds [4 x i8], ptr %.anon68, i64 %57
  %58 = load i32, ptr %ptroffset75, align 4
  store i32 %58, ptr %y74, align 4
  %59 = load i64, ptr %i73, align 8
  %60 = load i32, ptr %y74, align 4
  call void (ptr, ...) @printf(ptr @.str.11, i64 %59, i32 %60)
  %61 = load i64, ptr %.anon69, align 8
  %addnuw76 = add nuw i64 %61, 1
  store i64 %addnuw76, ptr %.anon69, align 8
  br label %loop.cond70

loop.exit77:                                      ; preds = %loop.cond70
  call void @foo.getFields(ptr sret([5 x i32]) align 4 %sretparam)
  store i64 0, ptr %.anon78, align 8
  br label %loop.cond79

loop.cond79:                                      ; preds = %loop.body81, %loop.exit77
  %62 = load i64, ptr %.anon78, align 8
  %gt80 = icmp ugt i64 5, %62
  br i1 %gt80, label %loop.body81, label %loop.exit86

loop.body81:                                      ; preds = %loop.cond79
  %63 = load i64, ptr %.anon78, align 8
  store i64 %63, ptr %i82, align 8
  %64 = load i64, ptr %.anon78, align 8
  %ptroffset84 = getelementptr inbounds [4 x i8], ptr %sretparam, i64 %64
  %65 = load i32, ptr %ptroffset84, align 4
  store i32 %65, ptr %y83, align 4
  %66 = load i64, ptr %i82, align 8
  %67 = load i32, ptr %y83, align 4
  call void (ptr, ...) @printf(ptr @.str.12, i64 %66, i32 %67)
  %68 = load i64, ptr %.anon78, align 8
  %addnuw85 = add nuw i64 %68, 1
  store i64 %addnuw85, ptr %.anon78, align 8
  br label %loop.cond79

loop.exit86:                                      ; preds = %loop.cond79
  %neq87 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq87)
  %neq88 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq88)
  %ptradd89 = getelementptr inbounds i8, ptr %x, i64 4
  %69 = load i32, ptr %x, align 4
  %70 = load i32, ptr %ptradd89, align 4
  call void (ptr, ...) @printf(ptr @.str.13, i32 %69, i32 %70)
  %neq91 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq91)
  %ptradd92 = getelementptr inbounds i8, ptr %x, i64 4
  store ptr %ptradd92, ptr %y90, align 8
  %71 = load ptr, ptr %y90, align 8
  %72 = load i32, ptr %71, align 4
  %add93 = add i32 %72, 1
  store i32 %add93, ptr %71, align 4
  %neq94 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq94)
  %neq95 = icmp ne ptr %x, null
  call void @llvm.assume(i1 %neq95)
  %ptradd96 = getelementptr inbounds i8, ptr %x, i64 4
  %73 = load i32, ptr %x, align 4
  %74 = load i32, ptr %ptradd96, align 4
  call void (ptr, ...) @printf(ptr @.str.14, i32 %73, i32 %74)
  ret void
}
