Skip to content

Conversation

JMazurkiewicz
Copy link
Contributor

Mark LWG3382 as "Nothing To Do" and add tests.

@JMazurkiewicz JMazurkiewicz requested a review from a team as a code owner March 19, 2024 16:13
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Mar 19, 2024
@llvmbot
Copy link
Member

llvmbot commented Mar 19, 2024

@llvm/pr-subscribers-libcxx

Author: Jakub Mazurkiewicz (JMazurkiewicz)

Changes

Mark LWG3382 as "Nothing To Do" and add tests.


Full diff: https://github.com/llvm/llvm-project/pull/85811.diff

5 Files Affected:

  • (modified) libcxx/docs/Status/Cxx20Issues.csv (+1-1)
  • (added) libcxx/test/std/containers/sequences/array/array.overview/nttp.equivalence.compile.pass.cpp (+69)
  • (added) libcxx/test/std/containers/sequences/array/array.overview/nttp.verify.cpp (+80)
  • (added) libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.equivalence.compile.pass.cpp (+72)
  • (added) libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.verify.cpp (+95)
diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index f0e9c4090f9cf6..6ab515535b53a9 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -285,7 +285,7 @@
 "`3379 <https://wg21.link/LWG3379>`__","""``safe``\ "" in several library names is misleading","Prague","|Complete|","15.0","|ranges|"
 "`3380 <https://wg21.link/LWG3380>`__","``common_type``\  and comparison categories","Prague","|Complete|","15.0","|spaceship|"
 "`3381 <https://wg21.link/LWG3381>`__","``begin``\  and ``data``\  must agree for ``contiguous_range``\ ","Prague","|Nothing To Do|","","|ranges|"
-"`3382 <https://wg21.link/LWG3382>`__","NTTP for ``pair``\  and ``array``\ ","Prague","",""
+"`3382 <https://wg21.link/LWG3382>`__","NTTP for ``pair``\  and ``array``\ ","Prague","|Nothing To Do|",""
 "`3383 <https://wg21.link/LWG3383>`__","|sect|\ [time.zone.leap.nonmembers] ``sys_seconds``\  should be replaced with ``seconds``\ ","Prague","","","|chrono|"
 "`3384 <https://wg21.link/LWG3384>`__","``transform_view::*sentinel*``\  has an incorrect ``operator-``\ ","Prague","|Complete|","15.0","|ranges|"
 "`3385 <https://wg21.link/LWG3385>`__","``common_iterator``\  is not sufficiently constrained for non-copyable iterators","Prague","|Complete|","15.0","|ranges|"
diff --git a/libcxx/test/std/containers/sequences/array/array.overview/nttp.equivalence.compile.pass.cpp b/libcxx/test/std/containers/sequences/array/array.overview/nttp.equivalence.compile.pass.cpp
new file mode 100644
index 00000000000000..5ce39ad4ed957e
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/array.overview/nttp.equivalence.compile.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <array>
+
+// Two values a1 and a2 of type array<T, N> are template-argument-equivalent if and only if each pair of corresponding
+// elements in a1 and a2 are template-argument-equivalent.
+
+#include <array>
+
+#include <type_traits>
+
+namespace test_full_type {
+template <class T, std::size_t S, std::array<T, S> A>
+struct test : std::false_type {};
+
+template <>
+struct test<int, 3, std::array<int, 3>{1, 2, 3}> : std::true_type {};
+
+static_assert(!test<int*, 4, std::array<int*, 4>{}>::value);
+static_assert(!test<int*, 3, std::array<int*, 3>{}>::value);
+static_assert(!test<int, 3, std::array<int, 3>{}>::value);
+static_assert(!test<int, 3, std::array<int, 3>{1}>::value);
+static_assert(!test<int, 3, std::array<int, 3>{1, 2}>::value);
+static_assert(!test<long, 3, std::array<long, 3>{1, 2, 3}>::value);
+static_assert(!test<unsigned int, 3, std::array<unsigned int, 3>{1, 2, 3}>::value);
+static_assert(test<int, 3, std::array<int, 3>{1, 2, 3}>::value);
+} // namespace test_full_type
+
+namespace test_ctad {
+template <std::array A>
+struct test : std::false_type {};
+
+template <>
+struct test<std::array<int, 3>{4, 5, 6}> : std::true_type {};
+
+static_assert(!test<std::array<int*, 4>{}>::value);
+static_assert(!test<std::array<int*, 3>{}>::value);
+static_assert(!test<std::array<int, 3>{}>::value);
+static_assert(!test<std::array<int, 3>{4}>::value);
+static_assert(!test<std::array<int, 3>{4, 5}>::value);
+static_assert(!test<std::array<long, 3>{4, 5, 6}>::value);
+static_assert(!test<std::array<unsigned int, 3>{4, 5, 6}>::value);
+static_assert(test<std::array<int, 3>{4, 5, 6}>::value);
+} // namespace test_ctad
+
+namespace test_auto {
+template <auto A>
+struct test : std::false_type {};
+
+template <>
+struct test<std::array<int, 3>{7, 8, 9}> : std::true_type {};
+
+static_assert(!test<std::array<int*, 4>{}>::value);
+static_assert(!test<std::array<int*, 3>{}>::value);
+static_assert(!test<std::array<int, 3>{}>::value);
+static_assert(!test<std::array<int, 3>{7}>::value);
+static_assert(!test<std::array<int, 3>{7, 8}>::value);
+static_assert(!test<std::array<long, 3>{7, 8, 9}>::value);
+static_assert(!test<std::array<unsigned int, 3>{7, 8, 9}>::value);
+static_assert(test<std::array<int, 3>{7, 8, 9}>::value);
+} // namespace test_auto
diff --git a/libcxx/test/std/containers/sequences/array/array.overview/nttp.verify.cpp b/libcxx/test/std/containers/sequences/array/array.overview/nttp.verify.cpp
new file mode 100644
index 00000000000000..5e3d5dfa7f755b
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/array/array.overview/nttp.verify.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <array>
+
+// array<T, N> is a structural type ([temp.param]) if T is a structural type.
+
+#include <array>
+
+#include <cstddef>
+#include <string>
+
+struct LiteralBase {};
+struct LiteralNSDM {};
+
+struct LiteralType : LiteralBase {
+  LiteralNSDM nsdm;
+};
+
+struct NotALiteral {
+  NotALiteral() {}
+};
+
+int i;
+NotALiteral not_a_literal;
+
+namespace test_full_type {
+template <class T, std::size_t S, std::array<T, S> A>
+struct test {};
+
+using A = test<int, 2, std::array{2, 3}>;
+using B = test<LiteralType, 0, std::array<LiteralType, 0>{}>;
+using C = test<int*, 1, std::array<int*, 1>{&i}>;
+using D = test<NotALiteral*, 1, std::array<NotALiteral*, 1>{&not_a_literal}>;
+
+using E = test<NotALiteral, 1, std::array<NotALiteral, 1>{}>;
+// expected-error@*:* {{non-type template parameter has non-literal type 'std::array<NotALiteral, 1UL>'}}
+
+using F = test<std::string, 2, std::array<std::string, 2>{}>;
+// expected-error@*:* {{type 'std::array<std::string, 2UL>' (aka 'array<basic_string<char>, 2UL>') of non-type template parameter is not a structural type}}
+} // namespace test_full_type
+
+namespace test_ctad {
+template <std::array A>
+struct test {};
+
+using A = test<std::array{2, 3}>;
+using B = test<std::array<LiteralType, 0>{}>;
+using C = test<std::array<int*, 1>{&i}>;
+using D = test<std::array<NotALiteral*, 1>{&not_a_literal}>;
+
+using E = test<std::array<NotALiteral, 1>{}>;
+// expected-error@-1 {{non-type template parameter has non-literal type 'std::array<NotALiteral, 1>'}}
+
+using F = test<std::array<std::string, 2>{}>;
+// expected-error@-1 {{type 'std::array<string, 2>' (aka 'std::array<std::string, 2>') of non-type template parameter is not a structural type}}
+} // namespace test_ctad
+
+namespace test_auto {
+template <auto A>
+struct test {};
+
+using A = test<std::array{2, 3}>;
+using B = test<std::array<LiteralType, 0>{}>;
+using C = test<std::array<int*, 1>{&i}>;
+using D = test<std::array<NotALiteral*, 1>{&not_a_literal}>;
+
+using E = test<std::array<NotALiteral, 1>{}>;
+// expected-error@-1 {{non-type template parameter has non-literal type 'std::array<NotALiteral, 1>'}}
+
+using F = test<std::array<std::string, 2>{}>;
+// expected-error@-1 {{type 'std::array<std::string, 2>' (aka 'array<basic_string<char>, 2>') of non-type template parameter is not a structural type}}
+} // namespace test_auto
diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.equivalence.compile.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.equivalence.compile.pass.cpp
new file mode 100644
index 00000000000000..e8529c3d75221e
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.equivalence.compile.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <utility>
+
+// Two values p1 and p2 of type pair<T, U> are template-argument-equivalent ([temp.type]) if and only if
+// p1.first and p2.first are template-argument-equivalent and p1.second and p2.second are template-argument-equivalent.
+
+#include <utility>
+
+#include <type_traits>
+
+int i = 0;
+int j = 1;
+
+namespace test_full_type {
+template <class T, class U, std::pair<T, U> P>
+struct test : std::false_type {};
+
+template <>
+struct test<int&, int, std::pair<int&, int>{i, 5}> : std::true_type {};
+
+static_assert(!test<int*, int*, std::pair<int*, int*>{}>::value);
+static_assert(!test<int*, int, std::pair<int*, int>{}>::value);
+static_assert(!test<int&, int*, std::pair<int&, int*>{i, nullptr}>::value);
+static_assert(!test<int&, int, std::pair<int&, int>{j, 0}>::value);
+static_assert(!test<int&, int, std::pair<int&, int>{j, 5}>::value);
+static_assert(!test<int&, int, std::pair<int&, int>{i, 0}>::value);
+static_assert(!test<int&, unsigned int, std::pair<int&, unsigned int>{j, 0}>::value);
+static_assert(test<int&, int, std::pair<int&, int>{i, 5}>::value);
+} // namespace test_full_type
+
+namespace test_ctad {
+template <std::pair P>
+struct test : std::false_type {};
+
+template <>
+struct test<std::pair<int&, int>{i, 10}> : std::true_type {};
+
+static_assert(!test<std::pair<int*, int*>{}>::value);
+static_assert(!test<std::pair<int*, int>{}>::value);
+static_assert(!test<std::pair<int&, int*>{i, nullptr}>::value);
+static_assert(!test<std::pair<int&, int>{j, 0}>::value);
+static_assert(!test<std::pair<int&, int>{j, 10}>::value);
+static_assert(!test<std::pair<int&, int>{i, 0}>::value);
+static_assert(!test<std::pair<int&, unsigned int>{j, 0}>::value);
+static_assert(test<std::pair<int&, int>{i, 10}>::value);
+} // namespace test_ctad
+
+namespace test_auto {
+template <auto P>
+struct test : std::false_type {};
+
+template <>
+struct test<std::pair<int&, int>{i, 15}> : std::true_type {};
+
+static_assert(!test<std::pair<int*, int*>{}>::value);
+static_assert(!test<std::pair<int*, int>{}>::value);
+static_assert(!test<std::pair<int&, int*>{i, nullptr}>::value);
+static_assert(!test<std::pair<int&, int>{j, 0}>::value);
+static_assert(!test<std::pair<int&, int>{j, 15}>::value);
+static_assert(!test<std::pair<int&, int>{i, 0}>::value);
+static_assert(!test<std::pair<int&, unsigned int>{j, 0}>::value);
+static_assert(test<std::pair<int&, int>{i, 15}>::value);
+} // namespace test_auto
diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.verify.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.verify.cpp
new file mode 100644
index 00000000000000..f09e3862448c8b
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/nttp.verify.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <utility>
+
+// pair<T, U> is a structural type ([temp.param]) if T and U are both structural types.
+
+#include <utility>
+
+#include <functional>
+#include <string>
+
+struct LiteralBase {};
+struct LiteralNSDM {};
+
+struct LiteralType : LiteralBase {
+  LiteralNSDM nsdm;
+};
+
+struct NotALiteral {
+  NotALiteral() {}
+};
+
+int i;
+NotALiteral not_a_literal;
+
+namespace test_full_type {
+template <class T, class U, std::pair<T, U> P>
+struct test {};
+
+using A = test<int, int, std::pair{0, 1}>;
+using B = test<int&, int&, std::make_pair(std::ref(i), std::ref(i))>;
+using C = test<const int&, const int&, std::make_pair(std::cref(i), std::cref(i))>;
+using D = test<LiteralType, LiteralType, std::pair<LiteralType, LiteralType>{}>;
+using E = test<int*, int*, std::pair<int*, int*>{&i, &i}>;
+using F = test<NotALiteral&, NotALiteral&, std::make_pair(std::ref(not_a_literal), std::ref(not_a_literal))>;
+
+using G = test<int&&, int&&, std::pair<int&&, int&&>{std::move(i), std::move(i)}>;
+// expected-error@*:* {{type 'std::pair<int &&, int &&>' of non-type template parameter is not a structural type}}
+
+using H = test<NotALiteral, NotALiteral, std::pair<NotALiteral, NotALiteral>{}>;
+// expected-error@*:* {{non-type template parameter has non-literal type 'std::pair<NotALiteral, NotALiteral>'}}
+
+using I = test<std::string, std::string, std::pair<std::string, std::string>{}>;
+// expected-error@*:* {{type 'std::pair<std::string, std::string>' (aka 'pair<basic_string<char>, basic_string<char>>') of non-type template parameter is not a structural type}}
+} // namespace test_full_type
+
+namespace test_ctad {
+template <std::pair P>
+struct test {};
+
+using A = test<std::pair{2, 3}>;
+using B = test<std::make_pair(std::ref(i), std::ref(i))>;
+using C = test<std::make_pair(std::cref(i), std::cref(i))>;
+using D = test<std::pair<LiteralType, LiteralType>{}>;
+using E = test<std::pair<int*, int*>{&i, &i}>;
+using F = test<std::make_pair(std::ref(not_a_literal), std::ref(not_a_literal))>;
+
+using G = test<std::pair<int&&, int&&>{std::move(i), std::move(i)}>;
+// expected-error@-1 {{type 'std::pair<int &&, int &&>' of non-type template parameter is not a structural type}}
+
+using H = test<std::pair<NotALiteral, NotALiteral>{}>;
+// expected-error@-1 {{non-type template parameter has non-literal type 'std::pair<NotALiteral, NotALiteral>'}}
+
+using I = test<std::pair<std::string, std::string>{}>;
+// expected-error@-1 {{type 'std::pair<string, string>' (aka 'std::pair<std::string, std::string>') of non-type template parameter is not a structural type}}
+} // namespace test_ctad
+
+namespace test_auto {
+template <auto P>
+struct test {};
+
+using A = test<std::pair{4, 5}>;
+using B = test<std::make_pair(std::ref(i), std::ref(i))>;
+using C = test<std::make_pair(std::cref(i), std::cref(i))>;
+using D = test<std::pair<LiteralType, LiteralType>{}>;
+using E = test<std::pair<int*, int*>{&i, &i}>;
+using F = test<std::make_pair(std::ref(not_a_literal), std::ref(not_a_literal))>;
+
+using G = test<std::pair<int&&, int&&>{std::move(i), std::move(i)}>;
+// expected-error@-1 {{type 'std::pair<int &&, int &&>' of non-type template parameter is not a structural type}}
+
+using H = test<std::pair<NotALiteral, NotALiteral>{}>;
+// expected-error@-1 {{non-type template parameter has non-literal type 'std::pair<NotALiteral, NotALiteral>'}}
+
+using I = test<std::pair<std::string, std::string>{}>;
+// expected-error@-1 {{type 'std::pair<std::string, std::string>' (aka 'pair<basic_string<char>, basic_string<char>>') of non-type template parameter is not a structural type}}
+} // namespace test_auto

@JMazurkiewicz JMazurkiewicz force-pushed the libcxx/lwg/nttp-for-pair-and-array branch 4 times, most recently from b0589bc to 1569809 Compare March 26, 2024 14:14
@JMazurkiewicz JMazurkiewicz force-pushed the libcxx/lwg/nttp-for-pair-and-array branch from 1569809 to dba1b0c Compare March 27, 2024 11:34
@JMazurkiewicz JMazurkiewicz force-pushed the libcxx/lwg/nttp-for-pair-and-array branch from dba1b0c to 31eba85 Compare April 9, 2024 09:03
@JMazurkiewicz JMazurkiewicz force-pushed the libcxx/lwg/nttp-for-pair-and-array branch from 31eba85 to 942455b Compare April 9, 2024 19:29
@ldionne ldionne self-assigned this Apr 26, 2024
Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling this! I have some comments to clarify my understanding but I think this will be good without too many changes.


// <array>

// array<T, N> is a structural type ([temp.param]) if T is a structural type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what you're testing here. Isn't it sufficient to check for the NTTP behavior as far as the LWG issue is concerned? I think I'm just missing the obvious here, but if you can explain that would be great!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm testing whether array (or pair) is a structural type based on its template parameters. LWG-3382 states that:

  • pair<T, U> is a structural type (13.2 [temp.param]) if T and U are both structural types
  • array<T, N> is a structural type (13.2 [temp.param]) if T is a structural type

IMHO those tests address the LWG issue, although they might be an overkill.

@ldionne ldionne merged commit bb075ee into llvm:main Jun 25, 2024
@JMazurkiewicz JMazurkiewicz deleted the libcxx/lwg/nttp-for-pair-and-array branch June 25, 2024 23:53
AlexisPerry pushed a commit to llvm-project-tlp/llvm-project that referenced this pull request Jul 9, 2024
Mark LWG3382 as "Nothing To Do" and add tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants