// RUN: %clang_cc1 %s -fblocks -triple x86_64-apple-darwin -emit-llvm -o - | FileCheck %s

// CHECK: @[[BLOCK_DESCRIPTOR22:.*]] = internal constant { i64, i64, ptr, ptr, ptr, ptr } { i64 0, i64 36, ptr @__copy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE, ptr @__destroy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE, ptr @{{.*}}, ptr null }, align 8

namespace test0 {
  // CHECK-LABEL: define{{.*}} void @_ZN5test04testEi(
  // CHECK: define internal void @___ZN5test04testEi_block_invoke{{.*}}(
  // CHECK: define internal void @___ZN5test04testEi_block_invoke_2{{.*}}(
  void test(int x) {
    ^{ ^{ (void) x; }; };
  }
}

extern void (^out)();

namespace test1 {
  // Capturing const objects doesn't require a local block.
  // CHECK-LABEL: define{{.*}} void @_ZN5test15test1Ev()
  // CHECK:   store ptr @__block_literal_global{{.*}}, ptr @out
  void test1() {
    const int NumHorsemen = 4;
    out = ^{ (void) NumHorsemen; };
  }

  // That applies to structs too...
  // CHECK-LABEL: define{{.*}} void @_ZN5test15test2Ev()
  // CHECK:   store ptr @__block_literal_global{{.*}}, ptr @out
  struct loc { double x, y; };
  void test2() {
    const loc target = { 5, 6 };
    out = ^{ (void) target; };
  }

  // ...unless they have mutable fields...
  // CHECK-LABEL: define{{.*}} void @_ZN5test15test3Ev()
  // CHECK:   [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
  // CHECK:   store ptr [[BLOCK]], ptr @out
  struct mut { mutable int x; };
  void test3() {
    const mut obj = { 5 };
    out = ^{ (void) obj; };
  }

  // ...or non-trivial destructors...
  // CHECK-LABEL: define{{.*}} void @_ZN5test15test4Ev()
  // CHECK:   [[OBJ:%.*]] = alloca
  // CHECK:   [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]],
  // CHECK:   store ptr [[BLOCK]], ptr @out
  struct scope { int x; ~scope(); };
  void test4() {
    const scope obj = { 5 };
    out = ^{ (void) obj; };
  }

  // ...or non-trivial copy constructors, but it's not clear how to do
  // that and still have a constant initializer in '03.
}

namespace test2 {
  struct A {
    A();
    A(const A &);
    ~A();
  };

  struct B {
    B();
    B(const B &);
    ~B();
  };

  // CHECK-LABEL: define{{.*}} void @_ZN5test24testEv()
  void test() {
    __block A a;
    __block B b;
    ^{ (void)a; (void)b; };
  }

  // CHECK-LABEL: define internal void @__Block_byref_object_copy
  // CHECK: call void @_ZN5test21AC1ERKS0_(

  // CHECK-LABEL: define internal void @__Block_byref_object_dispose
  // CHECK: call void @_ZN5test21AD1Ev(

  // CHECK-LABEL: define internal void @__Block_byref_object_copy
  // CHECK: call void @_ZN5test21BC1ERKS0_(

  // CHECK-LABEL: define internal void @__Block_byref_object_dispose
  // CHECK: call void @_ZN5test21BD1Ev(
}

// Make sure we mark destructors for parameters captured in blocks.
namespace test3 {
  struct A {
    A(const A&);
    ~A();
  };

  struct B : A {
  };

  void test(B b) {
    extern void consume(void(^)());
    consume(^{ (void) b; });
  }
}

namespace test4 {
  struct A {
    A();
    ~A();
  };

  void foo(A a);

  void test() {
    extern void consume(void(^)());
    consume(^{ return foo(A()); });
  }
  // CHECK-LABEL: define{{.*}} void @_ZN5test44testEv()
  // CHECK-LABEL: define internal void @___ZN5test44testEv_block_invoke
  // CHECK: [[TMP:%.*]] = alloca [[A:%.*]], align 1
  // CHECK-NEXT: store ptr [[BLOCKDESC:%.*]], ptr {{.*}}, align 8
  // CHECK:      call void @_ZN5test41AC1Ev(ptr {{[^,]*}} [[TMP]])
  // CHECK-NEXT: call void @_ZN5test43fooENS_1AE(ptr noundef [[TMP]])
  // CHECK-NEXT: call void @_ZN5test41AD1Ev(ptr {{[^,]*}} [[TMP]])
  // CHECK-NEXT: ret void
}

namespace test5 {
  struct A {
    unsigned afield;
    A();
    A(const A&);
    ~A();
    void foo() const;
  };

  void doWithBlock(void(^)());

  void test(bool cond) {
    A x;
    void (^b)() = (cond ? ^{ x.foo(); } : (void(^)()) 0);
    doWithBlock(b);
  }

  // CHECK-LABEL:    define{{.*}} void @_ZN5test54testEb(
  // CHECK:      [[COND:%.*]] = alloca i8
  // CHECK-NEXT: [[X:%.*]] = alloca [[A:%.*]], align 4
  // CHECK-NEXT: [[B:%.*]] = alloca ptr, align 8
  // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:.*]], align 8
  // CHECK-NEXT: [[COND_CLEANUP_SAVE:%.*]] = alloca ptr, align 8
  // CHECK-NEXT: [[CLEANUP_ACTIVE:%.*]] = alloca i1
  // CHECK-NEXT: [[T0:%.*]] = zext i1
  // CHECK-NEXT: store i8 [[T0]], ptr [[COND]], align 1
  // CHECK-NEXT: call void @_ZN5test51AC1Ev(ptr {{[^,]*}} [[X]])
  // CHECK-NEXT: [[T0:%.*]] = load i8, ptr [[COND]], align 1
  // CHECK-NEXT: [[T1:%.*]] = trunc i8 [[T0]] to i1
  // CHECK-NEXT: store i1 false, ptr [[CLEANUP_ACTIVE]]
  // CHECK-NEXT: br i1 [[T1]],

  // CHECK-NOT:  br
  // CHECK:      [[CAPTURE:%.*]] = getelementptr inbounds nuw [[BLOCK_T]], ptr [[BLOCK]], i32 0, i32 5
  // CHECK-NEXT: call void @_ZN5test51AC1ERKS0_(ptr {{[^,]*}} [[CAPTURE]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[X]])
  // CHECK-NEXT: store ptr [[CAPTURE]], ptr [[COND_CLEANUP_SAVE]], align 8
  // CHECK-NEXT: store i1 true, ptr [[CLEANUP_ACTIVE]]
  // CHECK-NEXT: br label
  // CHECK:      br label
  // CHECK:      phi
  // CHECK-NEXT: store
  // CHECK-NEXT: load
  // CHECK-NEXT: call void @_ZN5test511doWithBlockEU13block_pointerFvvE(
  // CHECK-NEXT: [[T0:%.*]] = load i1, ptr [[CLEANUP_ACTIVE]]
  // CHECK-NEXT: br i1 [[T0]]
  // CHECK:      [[T3:%.*]] = load ptr, ptr [[COND_CLEANUP_SAVE]], align 8
  // CHECK-NEXT: call void @_ZN5test51AD1Ev(ptr {{[^,]*}} [[T3]])
  // CHECK-NEXT: br label
  // CHECK:      call void @_ZN5test51AD1Ev(ptr {{[^,]*}} [[X]])
  // CHECK-NEXT: ret void
}

namespace test6 {
  struct A {
    A();
    ~A();
  };

  void foo(const A &, void (^)());
  void bar();

  void test() {
    // Make sure that the temporary cleanup isn't somehow captured
    // within the block.
    foo(A(), ^{ bar(); });
    bar();
  }

  // CHECK-LABEL:    define{{.*}} void @_ZN5test64testEv()
  // CHECK:      [[TEMP:%.*]] = alloca [[A:%.*]], align 1
  // CHECK-NEXT: call void @_ZN5test61AC1Ev(ptr {{[^,]*}} [[TEMP]])
  // CHECK-NEXT: call void @_ZN5test63fooERKNS_1AEU13block_pointerFvvE(
  // CHECK-NEXT: call void @_ZN5test61AD1Ev(ptr {{[^,]*}} [[TEMP]])
  // CHECK-NEXT: call void @_ZN5test63barEv()
  // CHECK-NEXT: ret void
}

namespace test7 {
  int f() {
    static int n;
    int *const p = &n;
    return ^{ return *p; }();
  }
}

namespace test8 {
  // failure to capture this after skipping rebuild of the 'this' pointer.
  struct X {
    int x;

    template<typename T>
    int foo() {
      return ^ { return x; }();
    }
  };

  template int X::foo<int>();
}

namespace test9 {
  struct B {
    void *p;
    B();
    B(const B&);
    ~B();
  };

  void use_block(void (^)());
  void use_block_2(void (^)(), const B &a);

  // Ensuring that creating a non-trivial capture copy expression
  // doesn't end up stealing the block registration for the block we
  // just parsed.  That block must have captures or else it won't
  // force registration.  Must occur within a block for some reason.
  void test() {
    B x;
    use_block(^{
        int y;
        use_block_2(^{ (void)y; }, x);
    });
  }
}

namespace test10 {
  // Check that 'v' is included in the copy helper function name to indicate
  // the constructor taking a volatile parameter is called to copy the captured
  // object.

  // CHECK-LABEL: define linkonce_odr hidden void @__copy_helper_block_8_32c16_ZTSVN6test101BE(
  // CHECK: call void @_ZN6test101BC1ERVKS0_(
  // CHECK-LABEL: define linkonce_odr hidden void @__destroy_helper_block_8_32c16_ZTSVN6test101BE(
  // CHECK: call void @_ZN6test101BD1Ev(

  struct B {
    int a;
    B();
    B(const B &);
    B(const volatile B &);
    ~B();
  };

  void test() {
    volatile B x;
    ^{ (void)x; };
  }
}

// Copy/dispose helper functions and block descriptors of blocks that capture
// objects that are non-external and non-trivial have internal linkage.

// CHECK-LABEL: define internal void @_ZN12_GLOBAL__N_14testEv(
// CHECK: store ptr @[[BLOCK_DESCRIPTOR22]], ptr %{{.*}}, align 8

// CHECK-LABEL: define internal void @__copy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE(
// CHECK-LABEL: define internal void @__destroy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE(

namespace {
  struct B {
    int a;
    B();
    B(const B &);
    ~B();
  };

  void test() {
    B x;
    ^{ (void)x; };
  }
}

void callTest() {
  test();
}
