// RUN: %clangxx -target x86_64-unknown-unknown -g %s -emit-llvm -S -o - | FileCheck %s
// PR14471

enum X {
  Y
};
class C
{
  static int a;
  const static bool const_a = true;
protected:
  static int b;
  const static float const_b = 3.14;
public:
  static int c;
  const static int const_c = 18;
  int d;
  static X x_a;
};

int C::a = 4;
int C::b = 2;
int C::c = 1;

int main()
{
        C instance_C;
        instance_C.d = 8;
        return C::c;
}

// The definition of C::a drives the emission of class C, which is
// why the definition of "a" comes before the declarations while
// "b" and "c" come after.

// CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, name: "X"{{.*}}, identifier: "_ZTS1X")
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "C"{{.*}}, identifier: "_ZTS1C")
//
// CHECK: ![[DECL_A:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "a"
// CHECK-NOT:                                 size:
// CHECK-NOT:                                 align:
// CHECK-NOT:                                 offset:
// CHECK-SAME:                                flags: DIFlagStaticMember)
//
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "const_a"
// CHECK-NOT:            size:
// CHECK-NOT:            align:
// CHECK-NOT:            offset:
// CHECK-SAME:           flags: DIFlagStaticMember,
// CHECK-SAME:           extraData: i1 true)
//
// CHECK: ![[DECL_B:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "b"
// CHECK-NOT:                                 size:
// CHECK-NOT:                                 align:
// CHECK-NOT:                                 offset:
// CHECK-SAME:                                flags: DIFlagProtected | DIFlagStaticMember)
//
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "const_b"
// CHECK-NOT:            size:
// CHECK-NOT:            align:
// CHECK-NOT:            offset:
// CHECK-SAME:           flags: DIFlagProtected | DIFlagStaticMember,
// CHECK-SAME:           extraData: float 0x{{.*}})
//
// CHECK: ![[DECL_C:[0-9]+]] = !DIDerivedType(tag: DW_TAG_member, name: "c"
// CHECK-NOT:                                 size:
// CHECK-NOT:                                 align:
// CHECK-NOT:                                 offset:
// CHECK-SAME:                                flags: DIFlagPublic | DIFlagStaticMember)
//
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "const_c"
// CHECK-NOT:            size:
// CHECK-NOT:            align:
// CHECK-NOT:            offset:
// CHECK-SAME:           flags: DIFlagPublic | DIFlagStaticMember,
// CHECK-SAME:           extraData: i32 18)
//
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "x_a"
// CHECK-SAME:           flags: DIFlagPublic | DIFlagStaticMember)

// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "static_decl_templ<int>"
// CHECK-NOT:              DIFlagFwdDecl
// CHECK-SAME:             ){{$}}
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "static_decl_templ_var"

// CHECK: [[NS_X:![0-9]+]] = !DINamespace(name: "x"

// Test this in an anonymous namespace to ensure the type is retained even when
// it doesn't get automatically retained by the string type reference machinery.
namespace {
struct anon_static_decl_struct {
  static const int anon_static_decl_var = 117;
};
}


// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "anon_static_decl_struct"
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "anon_static_decl_var"

int ref() {
  return anon_static_decl_struct::anon_static_decl_var;
}

template<typename T>
struct static_decl_templ {
  static const int static_decl_templ_var = 7;
};

template<typename T>
const int static_decl_templ<T>::static_decl_templ_var;

int static_decl_templ_ref() {
  return static_decl_templ<int>::static_decl_templ_var;
}

// CHECK: !DIGlobalVariable(name: "a", {{.*}}variable: i32* @_ZN1C1aE, declaration: ![[DECL_A]])
// CHECK: !DIGlobalVariable(name: "b", {{.*}}variable: i32* @_ZN1C1bE, declaration: ![[DECL_B]])
// CHECK: !DIGlobalVariable(name: "c", {{.*}}variable: i32* @_ZN1C1cE, declaration: ![[DECL_C]])

// CHECK-NOT: !DIGlobalVariable(name: "anon_static_decl_var"

// Verify that even when a static member declaration is created lazily when
// creating the definition, the declaration line is that of the canonical
// declaration, not the definition. Also, since we look at the canonical
// definition, we should also correctly emit the constant value (42) into the
// debug info.
struct V {
  virtual ~V(); // cause the definition of 'V' to be omitted by no-standalone-debug optimization
  static const int const_va = 42;
};
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "const_va",
// CHECK-SAME:           line: [[@LINE-3]]
// CHECK-SAME:           extraData: i32 42
const int V::const_va;

namespace x {
struct y {
  static int z;
};
int y::z;
}

// CHECK: !DIGlobalVariable(name: "z",
// CHECK-SAME:              scope: [[NS_X]]
