diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-03-03 23:51:05 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2012-03-03 23:51:05 +0000 |
commit | ec92bc78979aae6ec436fe51d082f7467e6f96c0 (patch) | |
tree | 504cf54faeeec4e6c9c8ab78267473d11f02e3c2 | |
parent | 1aa0be86358002fe876e5a4a00c3038c96be28ee (diff) |
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
-rw-r--r-- | docs/LanguageExtensions.html | 2 | ||||
-rw-r--r-- | docs/ReleaseNotes.html | 1 | ||||
-rw-r--r-- | lib/Lex/PPMacroExpansion.cpp | 2 | ||||
-rw-r--r-- | test/CXX/special/class.copy/p11.0x.copy.cpp | 31 | ||||
-rw-r--r-- | test/CXX/special/class.copy/p11.0x.move.cpp | 52 | ||||
-rw-r--r-- | test/CodeGenCXX/cxx11-unrestricted-union.cpp | 76 | ||||
-rw-r--r-- | test/Lexer/has_feature_cxx0x.cpp | 9 | ||||
-rw-r--r-- | test/SemaCXX/cxx0x-nontrivial-union.cpp | 93 | ||||
-rw-r--r-- | test/SemaCXX/discrim-union.cpp | 118 | ||||
-rw-r--r-- | www/cxx_status.html | 2 |
10 files changed, 383 insertions, 3 deletions
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.</p> <h4 id="cxx_unrestricted_unions">C++11 unrestricted unions</h4> -<p>Use <tt>__has_feature(cxx_unrestricted_unions)</tt> to determine if support for unrestricted unions is enabled. Clang does not currently support this feature.</p> +<p>Use <tt>__has_feature(cxx_unrestricted_unions)</tt> to determine if support for unrestricted unions is enabled.</p> <h4 id="cxx_user_literals">C++11 user-defined literals</h4> 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: <li>Generalized constant expressions</li> <li>Lambda expressions</li> <li>Generalized initializers</li> + <li>Unrestricted unions</li> </ul> <!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = --> 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<typename T> TemplateCtor(T); }; union TemplateCtorMember { TemplateCtor s; }; + +template<typename T> struct remove_ref { typedef T type; }; +template<typename T> struct remove_ref<T&> { typedef T type; }; +template<typename T> struct remove_ref<T&&> { typedef T type; }; +template<typename T> T &&forward(typename remove_ref<T>::type &&t); +template<typename T> T &&forward(typename remove_ref<T>::type &t); +template<typename T> typename remove_ref<T>::type &&move(T &&t); + +using size_t = decltype(sizeof(int)); +void *operator new(size_t, void *p) noexcept { return p; } + +namespace disabled_dtor { + template<typename T> + union disable_dtor { + T val; + template<typename...U> + disable_dtor(U &&...u) : val(forward<U>(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<deleted_dtor> dd(4, 'x'); +} + +namespace optional { + template<typename T> struct optional { + bool has; + union { T value; }; + + optional() : has(false) {} + template<typename...U> + optional(U &&...u) : has(true), value(forward<U>(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<non_trivial> o1; + optional<non_trivial> o2{non_trivial()}; + optional<non_trivial> o3{*o2}; + void f() { + if (o2) + o1 = o2; + o2 = optional<non_trivial>(); + } +} 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<typename T> struct remove_reference { typedef T type; }; +template<typename T> struct remove_reference<T&> { typedef T type; }; +template<typename T> struct remove_reference<T&&> { typedef T type; }; + +template<typename T> constexpr T &&forward(typename remove_reference<T>::type &t) noexcept { return static_cast<T&&>(t); } +template<typename T> constexpr T &&forward(typename remove_reference<T>::type &&t) noexcept { return static_cast<T&&>(t); } +template<typename T> constexpr typename remove_reference<T>::type &&move(T &&t) noexcept { return static_cast<typename remove_reference<T>::type&&>(t); } + +template<typename T> T declval() noexcept; + +namespace detail { + template<unsigned N> struct select {}; // : integral_constant<unsigned, N> {}; + template<typename T> struct type {}; + + template<typename...T> union either_impl; + + template<> union either_impl<> { + void get(...); + void destroy(...) { throw "logic_error"; } + }; + + template<typename T, typename...Ts> union either_impl<T, Ts...> { + private: + T val; + either_impl<Ts...> rest; + typedef either_impl<Ts...> rest_t; + + public: + constexpr either_impl(select<0>, T &&t) : val(move(t)) {} + + template<unsigned N, typename U> + constexpr either_impl(select<N>, U &&u) : rest(select<N-1>(), move(u)) {} + + constexpr static unsigned index(type<T>) { return 0; } + template<typename U> + constexpr static unsigned index(type<U> 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<unsigned N> constexpr const decltype(static_cast<const rest_t&>(rest).get(select<N-1>{})) get(select<N>) { + return rest.get(select<N-1>{}); + } + }; +} + +template<typename T> +struct a { + T value; + template<typename...U> + constexpr a(U &&...u) : value{forward<U>(u)...} {} +}; +template<typename T> using an = a<T>; + +template<typename T, typename U> T throw_(const U &u) { throw u; } + +template<typename...T> +class either { + unsigned elem; + detail::either_impl<T...> impl; + typedef decltype(impl) impl_t; + +public: + template<typename U> + constexpr either(a<U> &&t) : + elem(impl_t::index(detail::type<U>())), + impl(detail::select<impl_t::index(detail::type<U>())>(), 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<unsigned N> using const_get_result = + decltype(static_cast<const impl_t&>(impl).get(detail::select<N>{})); + + template<unsigned N> + constexpr const_get_result<N> 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_<const_get_result<N>>("bad_either_get") + : impl.get(detail::select<N>{})); + } + + template<typename U> + constexpr const U &get() { + return get<impl_t::index(detail::type<U>())>(); + } +}; + +typedef either<int, char, double> icd; +constexpr icd icd1 = an<int>(4); +constexpr icd icd2 = a<char>('x'); +constexpr icd icd3 = a<double>(6.5); + +static_assert(icd1.get<int>() == 4, ""); +static_assert(icd2.get<char>() == 'x', ""); +static_assert(icd3.get<double>() == 6.5, ""); + +struct non_triv { + constexpr non_triv() : n(5) {} + int n; +}; +constexpr either<const icd*, non_triv> icd4 = a<const icd*>(&icd2); +constexpr either<const icd*, non_triv> icd5 = a<non_triv>(); + +static_assert(icd4.get<const icd*>()->get<char>() == 'x', ""); +static_assert(icd5.get<non_triv>().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.</p> <tr> <td>Unrestricted unions</td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf">N2544</a></td> - <td class="none" align="center">No</td> + <td class="svn" align="center">SVN</td> </tr> <tr> <td>Local and unnamed types as template arguments</td> |