From ec92bc78979aae6ec436fe51d082f7467e6f96c0 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sat, 3 Mar 2012 23:51:05 +0000 Subject: [PATCH] Add a pile of tests for unrestricted unions, and advertise support for them. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151992 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LanguageExtensions.html | 2 +- docs/ReleaseNotes.html | 1 + lib/Lex/PPMacroExpansion.cpp | 2 +- test/CXX/special/class.copy/p11.0x.copy.cpp | 31 +++++ test/CXX/special/class.copy/p11.0x.move.cpp | 52 ++++++++ test/CodeGenCXX/cxx11-unrestricted-union.cpp | 76 ++++++++++++ test/Lexer/has_feature_cxx0x.cpp | 9 ++ test/SemaCXX/cxx0x-nontrivial-union.cpp | 93 +++++++++++++++ test/SemaCXX/discrim-union.cpp | 118 +++++++++++++++++++ www/cxx_status.html | 2 +- 10 files changed, 383 insertions(+), 3 deletions(-) create mode 100644 test/CodeGenCXX/cxx11-unrestricted-union.cpp create mode 100644 test/SemaCXX/discrim-union.cpp diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index 23aa4fe9b9..9f271900cb 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -814,7 +814,7 @@ support for Unicode string literals is enabled.

C++11 unrestricted unions

-

Use __has_feature(cxx_unrestricted_unions) to determine if support for unrestricted unions is enabled. Clang does not currently support this feature.

+

Use __has_feature(cxx_unrestricted_unions) to determine if support for unrestricted unions is enabled.

C++11 user-defined literals

diff --git a/docs/ReleaseNotes.html b/docs/ReleaseNotes.html index 60a3f73fe5..2a1e62fd16 100644 --- a/docs/ReleaseNotes.html +++ b/docs/ReleaseNotes.html @@ -123,6 +123,7 @@ following are now considered to be of production quality:
  • Generalized constant expressions
  • Lambda expressions
  • Generalized initializers
  • +
  • Unrestricted unions
  • diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 56ce407c18..99f2b23dff 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -667,7 +667,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_static_assert", LangOpts.CPlusPlus0x) .Case("cxx_trailing_return", LangOpts.CPlusPlus0x) .Case("cxx_unicode_literals", LangOpts.CPlusPlus0x) - //.Case("cxx_unrestricted_unions", false) + .Case("cxx_unrestricted_unions", LangOpts.CPlusPlus0x) //.Case("cxx_user_literals", false) .Case("cxx_variadic_templates", LangOpts.CPlusPlus0x) // Type traits diff --git a/test/CXX/special/class.copy/p11.0x.copy.cpp b/test/CXX/special/class.copy/p11.0x.copy.cpp index 65fd985924..cbe62b41cb 100644 --- a/test/CXX/special/class.copy/p11.0x.copy.cpp +++ b/test/CXX/special/class.copy/p11.0x.copy.cpp @@ -4,6 +4,9 @@ struct NonTrivial { NonTrivial(const NonTrivial&); }; +// A defaulted copy constructor for a class X is defined as deleted if X has: + +// -- a variant member with a non-trivial corresponding constructor union DeletedNTVariant { // expected-note{{here}} NonTrivial NT; DeletedNTVariant(); @@ -20,6 +23,9 @@ struct DeletedNTVariant2 { // expected-note{{here}} DeletedNTVariant2 DV2a; DeletedNTVariant2 DV2b(DV2a); // expected-error{{call to implicitly-deleted copy constructor}} +// -- a non-static data member of class type M (or array thereof) that cannot be +// copied because overload resolution results in an ambiguity or a function +// that is deleted or inaccessible struct NoAccess { NoAccess() = default; private: @@ -63,6 +69,25 @@ struct Deleted { // expected-note{{here}} Deleted Da; Deleted Db(Da); // expected-error{{call to implicitly-deleted copy constructor}} +// -- a direct or virtual base class B that cannot be copied because overload +// resolution results in an ambiguity or a function that is deleted or +// inaccessible +struct AmbiguousCopyBase : Ambiguity { // expected-note {{here}} + NonConst NC; +}; +extern AmbiguousCopyBase ACBa; +AmbiguousCopyBase ACBb(ACBa); // expected-error {{deleted copy constructor}} + +struct DeletedCopyBase : AmbiguousCopyBase {}; // expected-note {{here}} +extern DeletedCopyBase DCBa; +DeletedCopyBase DCBb(DCBa); // expected-error {{deleted copy constructor}} + +struct InaccessibleCopyBase : NoAccess {}; // expected-note {{here}} +extern InaccessibleCopyBase ICBa; +InaccessibleCopyBase ICBb(ICBa); // expected-error {{deleted copy constructor}} + +// -- any direct or virtual base class or non-static data member of a type with +// a destructor that is deleted or inaccessible struct NoAccessDtor { private: ~NoAccessDtor(); @@ -83,6 +108,12 @@ struct HasAccessDtor { HasAccessDtor HADa; HasAccessDtor HADb(HADa); +struct HasNoAccessDtorBase : NoAccessDtor { // expected-note{{here}} +}; +extern HasNoAccessDtorBase HNADBa; +HasNoAccessDtorBase HNADBb(HNADBa); // expected-error{{implicitly-deleted copy constructor}} + +// -- a non-static data member of rvalue reference type struct RValue { // expected-note{{here}} int && ri = 1; }; diff --git a/test/CXX/special/class.copy/p11.0x.move.cpp b/test/CXX/special/class.copy/p11.0x.move.cpp index 402bc31d5e..5315b060a0 100644 --- a/test/CXX/special/class.copy/p11.0x.move.cpp +++ b/test/CXX/special/class.copy/p11.0x.move.cpp @@ -4,6 +4,9 @@ struct NonTrivial { NonTrivial(NonTrivial&&); }; +// A defaulted move constructor for a class X is defined as deleted if X has: + +// -- a variant member with a non-trivial corresponding constructor union DeletedNTVariant { NonTrivial NT; DeletedNTVariant(DeletedNTVariant&&); @@ -18,6 +21,9 @@ struct DeletedNTVariant2 { }; DeletedNTVariant2::DeletedNTVariant2(DeletedNTVariant2&&) = default; // expected-error{{would delete}} +// -- a non-static data member of class type M (or array thereof) that cannot be +// copied because overload resolution results in an ambiguity or a function +// that is deleted or inaccessible struct NoAccess { NoAccess() = default; private: @@ -38,6 +44,43 @@ struct HasAccess { }; HasAccess::HasAccess(HasAccess&&) = default; +struct Ambiguity { + Ambiguity(const Ambiguity&&); + Ambiguity(volatile Ambiguity&&); +}; + +struct IsAmbiguous { + Ambiguity A; + IsAmbiguous(IsAmbiguous&&); +}; +IsAmbiguous::IsAmbiguous(IsAmbiguous&&) = default; // expected-error{{would delete}} + +struct Deleted { + IsAmbiguous IA; + Deleted(Deleted&&); +}; +Deleted::Deleted(Deleted&&) = default; // expected-error{{would delete}} + +// -- a direct or virtual base class B that cannot be moved because overload +// resolution results in an ambiguity or a function that is deleted or +// inaccessible +struct AmbiguousMoveBase : Ambiguity { + AmbiguousMoveBase(AmbiguousMoveBase&&); +}; +AmbiguousMoveBase::AmbiguousMoveBase(AmbiguousMoveBase&&) = default; // expected-error{{would delete}} + +struct DeletedMoveBase : AmbiguousMoveBase { + DeletedMoveBase(DeletedMoveBase&&); +}; +DeletedMoveBase::DeletedMoveBase(DeletedMoveBase&&) = default; // expected-error{{would delete}} + +struct InaccessibleMoveBase : NoAccess { + InaccessibleMoveBase(InaccessibleMoveBase&&); +}; +InaccessibleMoveBase::InaccessibleMoveBase(InaccessibleMoveBase&&) = default; // expected-error{{would delete}} + +// -- any direct or virtual base class or non-static data member of a type with +// a destructor that is deleted or inaccessible struct NoAccessDtor { NoAccessDtor(NoAccessDtor&&); private: @@ -57,12 +100,21 @@ struct HasAccessDtor { }; HasAccessDtor::HasAccessDtor(HasAccessDtor&&) = default; +struct HasNoAccessDtorBase : NoAccessDtor { // expected-note{{here}} +}; +extern HasNoAccessDtorBase HNADBa; +HasNoAccessDtorBase HNADBb(HNADBa); // expected-error{{implicitly-deleted copy constructor}} + +// The restriction on rvalue reference members applies to only the copy +// constructor. struct RValue { int &&ri = 1; RValue(RValue&&); }; RValue::RValue(RValue&&) = default; +// -- a non-static data member or direct or virtual base class with a type that +// does not have a move constructor and is not trivially copyable struct CopyOnly { CopyOnly(const CopyOnly&); }; diff --git a/test/CodeGenCXX/cxx11-unrestricted-union.cpp b/test/CodeGenCXX/cxx11-unrestricted-union.cpp new file mode 100644 index 0000000000..0397775b7e --- /dev/null +++ b/test/CodeGenCXX/cxx11-unrestricted-union.cpp @@ -0,0 +1,76 @@ +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - | FileCheck %s + +struct A { + A(); A(const A&); A(A&&); A &operator=(const A&); A &operator=(A&&); ~A(); +}; +struct B { + B(); B(const B&); B(B&&); B &operator=(const B&); B &operator=(B&&); ~B(); +}; + +union U { + U(); + U(const U &); + U(U &&); + U &operator=(const U&); + U &operator=(U&&); + ~U(); + + A a; + int n; +}; + +// CHECK-NOT: _ZN1A +U::U() {} +U::U(const U&) {} +U::U(U&&) {} +U &U::operator=(const U&) { return *this; } +U &U::operator=(U &&) { return *this; } +U::~U() {} + +struct S { + S(); + S(const S &); + S(S &&); + S &operator=(const S&); + S &operator=(S&&); + ~S(); + + union { + A a; + int n; + }; + B b; + int m; +}; + +// CHECK: _ZN1SC2Ev +// CHECK-NOT: _ZN1A +// CHECK: _ZN1BC1Ev +S::S() {} + +// CHECK-NOT: _ZN1A + +// CHECK: _ZN1SC2ERKS_ +// CHECK-NOT: _ZN1A +// CHECK: _ZN1BC1Ev +S::S(const S&) {} + +// CHECK-NOT: _ZN1A + +// CHECK: _ZN1SC2EOS_ +// CHECK-NOT: _ZN1A +// CHECK: _ZN1BC1Ev +S::S(S&&) {} + +// CHECK-NOT: _ZN1A +// CHECK-NOT: _ZN1B +S &S::operator=(const S&) { return *this; } + +S &S::operator=(S &&) { return *this; } + +// CHECK: _ZN1SD2Ev +// CHECK-NOT: _ZN1A +// CHECK: _ZN1BD1Ev +S::~S() {} + +// CHECK-NOT: _ZN1A diff --git a/test/Lexer/has_feature_cxx0x.cpp b/test/Lexer/has_feature_cxx0x.cpp index d520208d2e..40d651dff5 100644 --- a/test/Lexer/has_feature_cxx0x.cpp +++ b/test/Lexer/has_feature_cxx0x.cpp @@ -235,3 +235,12 @@ int no_generalized_initializers(); // CHECK-0X: has_generalized_initializers // CHECK-NO-0X: no_generalized_initializers + +#if __has_feature(cxx_unrestricted_unions) +int has_unrestricted_unions(); +#else +int no_unrestricted_unions(); +#endif + +// CHECK-0X: has_unrestricted_unions +// CHECK-NO-0X: no_unrestricted_unions diff --git a/test/SemaCXX/cxx0x-nontrivial-union.cpp b/test/SemaCXX/cxx0x-nontrivial-union.cpp index e706d7346d..0e4add8495 100644 --- a/test/SemaCXX/cxx0x-nontrivial-union.cpp +++ b/test/SemaCXX/cxx0x-nontrivial-union.cpp @@ -10,6 +10,12 @@ struct non_trivial { union u { non_trivial nt; }; +union u2 { + non_trivial nt; + int k; + u2(int k) : k(k) {} + u2() : nt() {} +}; union static_data_member { static int i; @@ -29,3 +35,90 @@ struct s { // Don't crash on this. struct TemplateCtor { template TemplateCtor(T); }; union TemplateCtorMember { TemplateCtor s; }; + +template struct remove_ref { typedef T type; }; +template struct remove_ref { typedef T type; }; +template struct remove_ref { typedef T type; }; +template T &&forward(typename remove_ref::type &&t); +template T &&forward(typename remove_ref::type &t); +template typename remove_ref::type &&move(T &&t); + +using size_t = decltype(sizeof(int)); +void *operator new(size_t, void *p) noexcept { return p; } + +namespace disabled_dtor { + template + union disable_dtor { + T val; + template + disable_dtor(U &&...u) : val(forward(u)...) {} + ~disable_dtor() {} + }; + + struct deleted_dtor { + deleted_dtor(int n, char c) : n(n), c(c) {} + int n; + char c; + ~deleted_dtor() = delete; + }; + + disable_dtor dd(4, 'x'); +} + +namespace optional { + template struct optional { + bool has; + union { T value; }; + + optional() : has(false) {} + template + optional(U &&...u) : has(true), value(forward(u)...) {} + + optional(const optional &o) : has(o.has) { + if (has) new (&value) T(o.value); + } + optional(optional &&o) : has(o.has) { + if (has) new (&value) T(move(o.value)); + } + + optional &operator=(const optional &o) { + if (has) { + if (o.has) + value = o.value; + else + value.~T(); + } else if (o.has) { + new (&value) T(o.value); + } + has = o.has; + } + optional &operator=(optional &&o) { + if (has) { + if (o.has) + value = move(o.value); + else + value.~T(); + } else if (o.has) { + new (&value) T(move(o.value)); + } + has = o.has; + } + + ~optional() { + if (has) + value.~T(); + } + + explicit operator bool() const { return has; } + T &operator*() const { return value; } + }; + + optional o1; + optional o2{non_trivial()}; + optional o3{*o2}; + void f() { + if (o2) + o1 = o2; + o2 = optional(); + } +} diff --git a/test/SemaCXX/discrim-union.cpp b/test/SemaCXX/discrim-union.cpp new file mode 100644 index 0000000000..15c9a225ed --- /dev/null +++ b/test/SemaCXX/discrim-union.cpp @@ -0,0 +1,118 @@ +// RUN: %clang_cc1 -std=c++11 %s -fsyntax-only -fcxx-exceptions + +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; + +template constexpr T &&forward(typename remove_reference::type &t) noexcept { return static_cast(t); } +template constexpr T &&forward(typename remove_reference::type &&t) noexcept { return static_cast(t); } +template constexpr typename remove_reference::type &&move(T &&t) noexcept { return static_cast::type&&>(t); } + +template T declval() noexcept; + +namespace detail { + template struct select {}; // : integral_constant {}; + template struct type {}; + + template union either_impl; + + template<> union either_impl<> { + void get(...); + void destroy(...) { throw "logic_error"; } + }; + + template union either_impl { + private: + T val; + either_impl rest; + typedef either_impl rest_t; + + public: + constexpr either_impl(select<0>, T &&t) : val(move(t)) {} + + template + constexpr either_impl(select, U &&u) : rest(select(), move(u)) {} + + constexpr static unsigned index(type) { return 0; } + template + constexpr static unsigned index(type t) { + return decltype(rest)::index(t) + 1; + } + + void destroy(unsigned elem) { + if (elem) + rest.destroy(elem - 1); + else + val.~T(); + } + + constexpr const T &get(select<0>) { return val; } + template constexpr const decltype(static_cast(rest).get(select{})) get(select) { + return rest.get(select{}); + } + }; +} + +template +struct a { + T value; + template + constexpr a(U &&...u) : value{forward(u)...} {} +}; +template using an = a; + +template T throw_(const U &u) { throw u; } + +template +class either { + unsigned elem; + detail::either_impl impl; + typedef decltype(impl) impl_t; + +public: + template + constexpr either(a &&t) : + elem(impl_t::index(detail::type())), + impl(detail::select())>(), move(t.value)) {} + + // Destruction disabled to allow use in a constant expression. + // FIXME: declare a destructor iff any element has a nontrivial destructor + //~either() { impl.destroy(elem); } + + constexpr unsigned index() noexcept { return elem; } + + template using const_get_result = + decltype(static_cast(impl).get(detail::select{})); + + template + constexpr const_get_result get() { + // Can't just use throw here, since that makes the conditional a prvalue, + // which means we return a reference to a temporary. + return (elem != N ? throw_>("bad_either_get") + : impl.get(detail::select{})); + } + + template + constexpr const U &get() { + return get())>(); + } +}; + +typedef either icd; +constexpr icd icd1 = an(4); +constexpr icd icd2 = a('x'); +constexpr icd icd3 = a(6.5); + +static_assert(icd1.get() == 4, ""); +static_assert(icd2.get() == 'x', ""); +static_assert(icd3.get() == 6.5, ""); + +struct non_triv { + constexpr non_triv() : n(5) {} + int n; +}; +constexpr either icd4 = a(&icd2); +constexpr either icd5 = a(); + +static_assert(icd4.get()->get() == 'x', ""); +static_assert(icd5.get().n == 5, ""); diff --git a/www/cxx_status.html b/www/cxx_status.html index ea1992a577..d5405e24c9 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -249,7 +249,7 @@ with clang; other versions have not been tested.

    Unrestricted unions N2544 - No + SVN Local and unnamed types as template arguments -- 2.50.1