From: Richard Smith Date: Thu, 16 Feb 2012 20:41:22 +0000 (+0000) Subject: C++11 allows unions to have static data members. Remove the corresponding X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b9c64d84ea3edd5e2fffb0a2e85ca1308be4f429;p=clang C++11 allows unions to have static data members. Remove the corresponding restriction and add some tests. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150721 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 18b0295b30..4f0eea3830 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -973,8 +973,13 @@ def note_nontrivial_user_defined : Note< "because type %0 has a user-declared %select{constructor|copy constructor|" "move constructor|copy assignment operator|move assignment operator|" "destructor}1">; -def err_static_data_member_not_allowed_in_union_or_anon_struct : Error< - "static data member %0 not allowed in %select{anonymous struct|union}1">; +def err_static_data_member_not_allowed_in_anon_struct : Error< + "static data member %0 not allowed in anonymous struct">; +def ext_static_data_member_in_union : ExtWarn< + "static data member %0 in union is a C++11 extension">, InGroup; +def warn_cxx98_compat_static_data_member_in_union : Warning< + "static data member %0 in union is incompatible with C++98">, + InGroup, DefaultIgnore; def err_union_member_of_reference_type : Error< "union member %0 has reference type %1">; def ext_anonymous_struct_union_qualified : Extension< diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 9fa0349723..64a67d9a64 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3920,20 +3920,24 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, } else if (SC == SC_None) SC = SC_Static; } - if (SC == SC_Static) { + if (SC == SC_Static && CurContext->isRecord()) { if (const CXXRecordDecl *RD = dyn_cast(DC)) { if (RD->isLocalClass()) Diag(D.getIdentifierLoc(), diag::err_static_data_member_not_allowed_in_local_class) << Name << RD->getDeclName(); - // C++ [class.union]p1: If a union contains a static data member, - // the program is ill-formed. - // - // We also disallow static data members in anonymous structs. - if (CurContext->isRecord() && (RD->isUnion() || !RD->getDeclName())) + // C++98 [class.union]p1: If a union contains a static data member, + // the program is ill-formed. C++11 drops this restriction. + if (RD->isUnion()) + Diag(D.getIdentifierLoc(), + getLangOptions().CPlusPlus0x + ? diag::warn_cxx98_compat_static_data_member_in_union + : diag::ext_static_data_member_in_union) << Name; + // We conservatively disallow static data members in anonymous structs. + else if (!RD->getDeclName()) Diag(D.getIdentifierLoc(), - diag::err_static_data_member_not_allowed_in_union_or_anon_struct) + diag::err_static_data_member_not_allowed_in_anon_struct) << Name << RD->isUnion(); } } diff --git a/test/CXX/class/class.union/p1.cpp b/test/CXX/class/class.union/p1.cpp index 011185fb49..54b256e3c2 100644 --- a/test/CXX/class/class.union/p1.cpp +++ b/test/CXX/class/class.union/p1.cpp @@ -91,8 +91,9 @@ union U3 { }; union U4 { - static int i1; // expected-error {{static data member 'i1' not allowed in union}} + static int i1; // expected-warning {{static data member 'i1' in union is a C++11 extension}} }; +int U4::i1 = 10; union U5 { int& i1; // expected-error {{union member 'i1' has reference type 'int &'}} diff --git a/test/CXX/class/class.union/p2-0x.cpp b/test/CXX/class/class.union/p2-0x.cpp new file mode 100644 index 0000000000..d353bda27c --- /dev/null +++ b/test/CXX/class/class.union/p2-0x.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -verify -std=c++11 %s + +// Unlike in C++98, C++11 allows unions to have static data members. + +union U1 { + static constexpr int k1 = 0; + static const int k2 = k1; + static int k3 = k2; // expected-error {{non-const static data member must be initialized out of line}} + static constexpr double k4 = k2; + static const double k5 = k4; // expected-warning {{GNU extension}} expected-note {{use 'constexpr'}} + int n[k1 + 3]; +}; + +constexpr int U1::k1; +constexpr int U1::k2; +int U1::k3; + +const double U1::k4; +const double U1::k5; + +template +union U2 { + static const int k1; + static double k2; + T t; +}; +template constexpr int U2::k1 = sizeof(U2); +template double U2::k2 = 5.3; + +static_assert(U2::k1 == sizeof(int), ""); +static_assert(U2::k1 == sizeof(char), ""); + +union U3 { + static const int k; + U3() : k(0) {} // expected-error {{does not name a non-static data member}} +}; diff --git a/test/CodeGenCXX/static-data-member.cpp b/test/CodeGenCXX/static-data-member.cpp index 4eeda3d428..4ad339db98 100644 --- a/test/CodeGenCXX/static-data-member.cpp +++ b/test/CodeGenCXX/static-data-member.cpp @@ -5,6 +5,12 @@ // CHECK: @_ZN5test31AIiE1xE = weak_odr global i32 0, align 4 // CHECK: @_ZGVN5test31AIiE1xE = weak_odr global i64 0 +// CHECK: _ZN5test51U2k0E = global i32 0 +// CHECK: _ZN5test51U2k1E = global i32 0 +// CHECK: _ZN5test51U2k2E = constant i32 76 +// CHECK-NOT: test51U2k3E +// CHECK-NOT: test51U2k4E + // PR5564. namespace test1 { struct A { @@ -78,3 +84,21 @@ namespace test4 { return a->n; } } + +// Test that static data members in unions behave properly. +namespace test5 { + union U { + static int k0; + static const int k1; + static const int k2 = 76; + static const int k3; + static const int k4 = 81; + }; + int U::k0; + const int U::k1 = (k0 = 9, 42); + const int U::k2; + + // CHECK: store i32 9, i32* @_ZN5test51U2k0E + // CHECK: store i32 {{.*}}, i32* @_ZN5test51U2k1E + // CHECK-NOT: store {{.*}} i32* @_ZN5test51U2k2E +} diff --git a/test/SemaCXX/cxx0x-nontrivial-union.cpp b/test/SemaCXX/cxx0x-nontrivial-union.cpp index 6275af6bac..2bf7056521 100644 --- a/test/SemaCXX/cxx0x-nontrivial-union.cpp +++ b/test/SemaCXX/cxx0x-nontrivial-union.cpp @@ -11,8 +11,13 @@ union u { non_trivial nt; }; +union static_data_member { + static int i; +}; +int static_data_member::i; + union bad { - static int i; // expected-error {{static data member}} + int &i; // expected-error {{union member 'i' has reference type 'int &'}} }; struct s { diff --git a/test/SemaCXX/cxx98-compat.cpp b/test/SemaCXX/cxx98-compat.cpp index f162c6f32d..b7abe266f2 100644 --- a/test/SemaCXX/cxx98-compat.cpp +++ b/test/SemaCXX/cxx98-compat.cpp @@ -34,8 +34,7 @@ namespace TemplateParsing { } void Lambda() { - // FIXME: Enable when lambdas are minimally working. - //[]{}; // FIXME-warning {{lambda expressions are incompatible with C++98}} + []{}(); // expected-warning {{lambda expressions are incompatible with C++98}} } int InitList() { @@ -239,6 +238,11 @@ namespace UnionOrAnonStructMembers { NonTrivDtor ntd; // expected-warning {{anonymous struct member 'ntd' with a non-trivial destructor is incompatible with C++98}} }; }; + union WithStaticDataMember { + static constexpr double d = 0.0; // expected-warning {{static data member 'd' in union is incompatible with C++98}} expected-warning {{'constexpr' specifier is incompatible with C++98}} + static const int n = 0; // expected-warning {{static data member 'n' in union is incompatible with C++98}} + static int k; // expected-warning {{static data member 'k' in union is incompatible with C++98}} + }; } int EnumNNS = Enum::enum_val; // expected-warning {{enumeration type in nested name specifier is incompatible with C++98}}