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
diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html
index 23aa4fe..9f27190 100644
--- a/docs/LanguageExtensions.html
+++ b/docs/LanguageExtensions.html
@@ -814,7 +814,7 @@
<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 60a3f73..2a1e62f 100644
--- a/docs/ReleaseNotes.html
+++ b/docs/ReleaseNotes.html
@@ -123,6 +123,7 @@
<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 56ce407..99f2b23 100644
--- a/lib/Lex/PPMacroExpansion.cpp
+++ b/lib/Lex/PPMacroExpansion.cpp
@@ -667,7 +667,7 @@
.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 65fd985..cbe62b4 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 @@
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 @@
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 @@
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 @@
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 402bc31..5315b06 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 @@
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 @@
};
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 @@
};
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 @@
};
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 0000000..0397775
--- /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 d520208..40d651d 100644
--- a/test/Lexer/has_feature_cxx0x.cpp
+++ b/test/Lexer/has_feature_cxx0x.cpp
@@ -235,3 +235,12 @@
// 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 e706d73..0e4add8 100644
--- a/test/SemaCXX/cxx0x-nontrivial-union.cpp
+++ b/test/SemaCXX/cxx0x-nontrivial-union.cpp
@@ -10,6 +10,12 @@
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 @@
// 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 0000000..15c9a22
--- /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 ea1992a..d5405e2 100644
--- a/www/cxx_status.html
+++ b/www/cxx_status.html
@@ -249,7 +249,7 @@
<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>