From: Waffl3x <waff...@baylibre.com>

libgomp/ChangeLog:

        * testsuite/libgomp.c++/target-flex-10.C: New test.
        * testsuite/libgomp.c++/target-flex-100.C: New test.
        * testsuite/libgomp.c++/target-flex-101.C: New test.
        * testsuite/libgomp.c++/target-flex-11.C: New test.
        * testsuite/libgomp.c++/target-flex-12.C: New test.
        * testsuite/libgomp.c++/target-flex-2000.C: New test.
        * testsuite/libgomp.c++/target-flex-2001.C: New test.
        * testsuite/libgomp.c++/target-flex-2002.C: New test.
        * testsuite/libgomp.c++/target-flex-2003.C: New test.
        * testsuite/libgomp.c++/target-flex-30.C: New test.
        * testsuite/libgomp.c++/target-flex-300.C: New test.
        * testsuite/libgomp.c++/target-flex-31.C: New test.
        * testsuite/libgomp.c++/target-flex-32.C: New test.
        * testsuite/libgomp.c++/target-flex-33.C: New test.
        * testsuite/libgomp.c++/target-flex-41.C: New test.
        * testsuite/libgomp.c++/target-flex-60.C: New test.
        * testsuite/libgomp.c++/target-flex-61.C: New test.
        * testsuite/libgomp.c++/target-flex-62.C: New test.
        * testsuite/libgomp.c++/target-flex-70.C: New test.
        * testsuite/libgomp.c++/target-flex-80.C: New test.
        * testsuite/libgomp.c++/target-flex-81.C: New test.
        * testsuite/libgomp.c++/target-flex-90.C: New test.
        * testsuite/libgomp.c++/target-flex-common.h: New test.

Co-authored-by: Thomas Schwinge <tschwi...@baylibre.com>
---
 .../testsuite/libgomp.c++/target-flex-10.C    | 215 +++++
 .../testsuite/libgomp.c++/target-flex-100.C   | 210 +++++
 .../testsuite/libgomp.c++/target-flex-101.C   | 136 ++++
 .../testsuite/libgomp.c++/target-flex-11.C    | 444 +++++++++++
 .../testsuite/libgomp.c++/target-flex-12.C    | 736 ++++++++++++++++++
 .../testsuite/libgomp.c++/target-flex-2000.C  |  32 +
 .../testsuite/libgomp.c++/target-flex-2001.C  |  61 ++
 .../testsuite/libgomp.c++/target-flex-2002.C  |  97 +++
 .../testsuite/libgomp.c++/target-flex-2003.C  | 176 +++++
 .../testsuite/libgomp.c++/target-flex-30.C    |  51 ++
 .../testsuite/libgomp.c++/target-flex-300.C   |  49 ++
 .../testsuite/libgomp.c++/target-flex-31.C    |  80 ++
 .../testsuite/libgomp.c++/target-flex-32.C    |  50 ++
 .../testsuite/libgomp.c++/target-flex-33.C    |  52 ++
 .../testsuite/libgomp.c++/target-flex-41.C    |  94 +++
 .../testsuite/libgomp.c++/target-flex-60.C    |  46 ++
 .../testsuite/libgomp.c++/target-flex-61.C    |  54 ++
 .../testsuite/libgomp.c++/target-flex-62.C    |  50 ++
 .../testsuite/libgomp.c++/target-flex-70.C    |  26 +
 .../testsuite/libgomp.c++/target-flex-80.C    |  49 ++
 .../testsuite/libgomp.c++/target-flex-81.C    |  75 ++
 .../testsuite/libgomp.c++/target-flex-90.C    | 107 +++
 .../libgomp.c++/target-flex-common.h          |  40 +
 23 files changed, 2930 insertions(+)
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-10.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-100.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-101.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-11.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-12.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-2000.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-2001.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-2002.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-2003.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-30.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-300.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-31.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-32.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-33.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-41.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-60.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-61.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-62.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-70.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-80.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-81.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-90.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-flex-common.h

diff --git a/libgomp/testsuite/libgomp.c++/target-flex-10.C 
b/libgomp/testsuite/libgomp.c++/target-flex-10.C
new file mode 100644
index 00000000000..8fa9af7e414
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-10.C
@@ -0,0 +1,215 @@
+/* Basic container usage.  */
+
+#include <vector>
+#include <deque>
+#include <list>
+#include <set>
+#include <map>
+#if __cplusplus >= 201103L
+#include <array>
+#include <forward_list>
+#include <unordered_set>
+#include <unordered_map>
+#endif
+
+bool vector_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::vector<int> vector;
+      ok = vector.empty();
+    }
+  return ok;
+}
+
+bool deque_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::deque<int> deque;
+      ok = deque.empty();
+    }
+  return ok;
+}
+
+bool list_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::list<int> list;
+      ok = list.empty();
+    }
+  return ok;
+}
+
+bool map_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::map<int, int> map;
+      ok = map.empty();
+    }
+  return ok;
+}
+
+bool set_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::set<int> set;
+      ok = set.empty();
+    }
+  return ok;
+}
+
+bool multimap_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::multimap<int, int> multimap;
+      ok = multimap.empty();
+    }
+  return ok;
+}
+
+bool multiset_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::multiset<int, int> multiset;
+      ok = multiset.empty();
+    }
+  return ok;
+}
+
+#if __cplusplus >= 201103L
+
+bool array_test()
+{
+  static constexpr std::size_t array_size = 42;
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::array<int, array_size> array{};
+      ok = array[0] == 0
+          && array[array_size - 1] == 0;
+    }
+  return ok;
+}
+
+bool forward_list_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::forward_list<int> forward_list;
+      ok = forward_list.empty();
+    }
+  return ok;
+}
+
+bool unordered_map_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::unordered_map<int, int> unordered_map;
+      ok = unordered_map.empty();
+    }
+  return ok;
+}
+
+bool unordered_set_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::unordered_set<int> unordered_set;
+      ok = unordered_set.empty();
+    }
+  return ok;
+}
+
+bool unordered_multimap_test()
+{
+
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::unordered_multimap<int, int> unordered_multimap;
+      ok = unordered_multimap.empty();
+    }
+  return ok;
+}
+
+bool unordered_multiset_test()
+{
+
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      std::unordered_multiset<int> unordered_multiset;
+      ok = unordered_multiset.empty();
+    }
+  return ok;
+}
+
+#else
+bool array_test() { return true; }
+bool forward_list_test() { return true; }
+bool unordered_map_test() { return true; }
+bool unordered_set_test() { return true; }
+bool unordered_multimap_test() { return true; }
+bool unordered_multiset_test() { return true; }
+#endif
+
+int main()
+{
+  const bool vec_res                = vector_test();
+  __builtin_printf("vector            : %s\n", vec_res                ? "PASS" 
: "FAIL");
+  const bool deque_res              = deque_test();
+  __builtin_printf("deque             : %s\n", deque_res              ? "PASS" 
: "FAIL");
+  const bool list_res               = list_test();
+  __builtin_printf("list              : %s\n", list_res               ? "PASS" 
: "FAIL");
+  const bool map_res                = map_test();
+  __builtin_printf("map               : %s\n", map_res                ? "PASS" 
: "FAIL");
+  const bool set_res                = set_test();
+  __builtin_printf("set               : %s\n", set_res                ? "PASS" 
: "FAIL");
+  const bool multimap_res           = multimap_test();
+  __builtin_printf("multimap          : %s\n", multimap_res           ? "PASS" 
: "FAIL");
+  const bool multiset_res           = multiset_test();
+  __builtin_printf("multiset          : %s\n", multiset_res           ? "PASS" 
: "FAIL");
+  const bool array_res              = array_test();
+  __builtin_printf("array             : %s\n", array_res              ? "PASS" 
: "FAIL");
+  const bool forward_list_res       = forward_list_test();
+  __builtin_printf("forward_list      : %s\n", forward_list_res       ? "PASS" 
: "FAIL");
+  const bool unordered_map_res      = unordered_map_test();
+  __builtin_printf("unordered_map     : %s\n", unordered_map_res      ? "PASS" 
: "FAIL");
+  const bool unordered_set_res      = unordered_set_test();
+  __builtin_printf("unordered_set     : %s\n", unordered_set_res      ? "PASS" 
: "FAIL");
+  const bool unordered_multimap_res = unordered_multimap_test();
+  __builtin_printf("unordered_multimap: %s\n", unordered_multimap_res ? "PASS" 
: "FAIL");
+  const bool unordered_multiset_res = unordered_multiset_test();
+  __builtin_printf("unordered_multiset: %s\n", unordered_multiset_res ? "PASS" 
: "FAIL");
+  const bool ok = vec_res
+                 && deque_res
+                 && list_res
+                 && map_res
+                 && set_res
+                 && multimap_res
+                 && multiset_res
+                 && array_res
+                 && forward_list_res
+                 && unordered_map_res
+                 && unordered_set_res
+                 && unordered_multimap_res
+                 && unordered_multiset_res;
+  return ok ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-100.C 
b/libgomp/testsuite/libgomp.c++/target-flex-100.C
new file mode 100644
index 00000000000..7ab047ffb49
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-100.C
@@ -0,0 +1,210 @@
+/* Container adaptors in target region.
+   Does not test comparison operators other than equality to allow these tests
+   to be generalized to arbitrary input data.  */
+
+#include <algorithm>
+#include <cstdio>
+#include <deque>
+#include <queue>
+#include <stack>
+#include <vector>
+
+#include "target-flex-common.h"
+
+template<typename T, std::size_t Size>
+bool test_stack(T (&arr)[Size])
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      const std::size_t half_size = Size / 2;
+      const T first_element = arr[0];
+      const T middle_element = arr[half_size - 1];
+      const T last_element = arr[Size - 1];
+      typedef std::stack<T, std::vector<T> > stack_type;
+      stack_type stack;
+      VERIFY (stack.empty());
+      VERIFY (stack.size() == 0);
+      {
+       /* Do half with push.  */
+       std::size_t idx = 0;
+       for (; idx < half_size; ++idx)
+         {
+           stack.push(arr[idx]);
+           VERIFY (stack.top() == arr[idx]);
+         }
+       VERIFY (stack.size() == half_size);
+       VERIFY (static_cast<const stack_type&>(stack).size() == half_size);
+       for (; idx < Size; ++idx)
+         {
+           #if __cplusplus >= 201103L
+             /* Do the rest with emplace if C++11 or higher.  */
+             stack.emplace(arr[idx]);
+           #else
+             /* Otherwise just use push again.  */
+             stack.push(arr[idx]);
+           #endif
+           VERIFY (stack.top() == arr[idx]);
+         }
+       VERIFY (stack.size() == Size);
+       VERIFY (static_cast<const stack_type&>(stack).size() == Size);
+
+       const stack_type stack_orig = stack_type(std::vector<T>(arr, arr + 
Size));
+       VERIFY (stack == stack_orig);
+       /* References are contained in their own scope so we don't accidently
+          add tests referencing them after they have been invalidated.  */
+       {
+         const T& const_top = static_cast<const stack_type&>(stack).top();
+         VERIFY (const_top == last_element);
+         T& mutable_top = stack.top();
+         mutable_top = first_element;
+         VERIFY (const_top == first_element);
+       }
+       /* Will only compare inequal if the first and last elements are 
different.  */
+       VERIFY (first_element != last_element || stack != stack_orig);
+       for (std::size_t count = Size - half_size; count != 0; --count)
+         stack.pop();
+       VERIFY (stack.top() == middle_element);
+       const stack_type stack_half_orig = stack_type(std::vector<T>(arr, arr + 
half_size));
+       VERIFY (stack == stack_half_orig);
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+template<typename T, std::size_t Size>
+bool test_queue(T (&arr)[Size])
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      const std::size_t half_size = Size / 2;
+      const T first_element = arr[0];
+      const T last_element = arr[Size - 1];
+      typedef std::queue<T, std::deque<T> > queue_type;
+      queue_type queue;
+      VERIFY (queue.empty());
+      VERIFY (queue.size() == 0);
+      {
+       /* Do half with push.  */
+       std::size_t idx = 0;
+       for (; idx < half_size; ++idx)
+         {
+           queue.push(arr[idx]);
+           VERIFY (queue.back() == arr[idx]);
+           VERIFY (queue.front() == first_element);
+         }
+       VERIFY (queue.size() == half_size);
+       VERIFY (static_cast<const queue_type&>(queue).size() == half_size);
+       for (; idx < Size; ++idx)
+         {
+           #if __cplusplus >= 201103L
+             /* Do the rest with emplace if C++11 or higher.  */
+             queue.emplace(arr[idx]);
+           #else
+             /* Otherwise just use push again.  */
+             queue.push(arr[idx]);
+           #endif
+           VERIFY (queue.back() == arr[idx]);
+         }
+       VERIFY (queue.size() == Size);
+       VERIFY (static_cast<const queue_type&>(queue).size() == Size);
+
+       const queue_type queue_orig = queue_type(std::deque<T>(arr, arr + 
Size));
+       VERIFY (queue == queue_orig);
+
+       /* References are contained in their own scope so we don't accidently
+          add tests referencing them after they have been invalidated.  */
+       {
+         const T& const_front = static_cast<const queue_type&>(queue).front();
+         VERIFY (const_front == first_element);
+         T& mutable_front = queue.front();
+
+         const T& const_back = static_cast<const queue_type&>(queue).back();
+         VERIFY (const_back == last_element);
+         T& mutable_back = queue.back();
+         {
+           using std::swap;
+           swap(mutable_front, mutable_back);
+         }
+         VERIFY (const_front == last_element);
+         VERIFY (const_back == first_element);
+         /* Will only compare inequal if the first and last elements are 
different.  */
+         VERIFY (first_element != last_element || queue != queue_orig);
+         /* Return the last element to normal for the next comparison.  */
+         mutable_back = last_element;
+       }
+
+       const T middle_element = arr[half_size];
+       for (std::size_t count = Size - half_size; count != 0; --count)
+         queue.pop();
+       VERIFY (queue.front() == middle_element);
+       const queue_type queue_upper_half = queue_type(std::deque<T>(arr + 
half_size, arr + Size));
+       VERIFY (queue == queue_upper_half);
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+template<typename T, std::size_t Size>
+bool test_priority_queue(T (&arr)[Size], const T min_value, const T max_value)
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      typedef std::priority_queue<T, std::vector<T> > priority_queue_type;
+      {
+       priority_queue_type pqueue;
+       VERIFY (pqueue.empty());
+       VERIFY (pqueue.size() == 0);
+      }
+      {
+       priority_queue_type pqueue(arr, arr + Size);
+       VERIFY (!pqueue.empty());
+       VERIFY (pqueue.size() == Size);
+       VERIFY (static_cast<const priority_queue_type&>(pqueue).size() == Size);
+
+       const T old_max = pqueue.top();
+
+       #if __cplusplus >= 201103L
+         pqueue.emplace(max_value);
+       #else
+         pqueue.push(max_value);
+       #endif
+       VERIFY (pqueue.top() == max_value);
+       pqueue.pop();
+       VERIFY (pqueue.top() == old_max);
+       pqueue.push(min_value);
+       VERIFY (pqueue.top() == old_max);
+       pqueue.push(max_value);
+       VERIFY (pqueue.top() == max_value);
+       pqueue.pop();
+       VERIFY (pqueue.top() == old_max);
+       VERIFY (pqueue.size() == Size + 1);
+
+       for (std::size_t count = Size; count != 0; --count)
+         pqueue.pop();
+       VERIFY (pqueue.size() == 1);
+       VERIFY (pqueue.top() == min_value);
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+int main()
+{
+  int arr[10] = {0,1,2,3,4,5,6,7,8,9};
+
+  return test_stack(arr)
+        && test_queue(arr)
+        && test_priority_queue(arr, 0, 1000) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-101.C 
b/libgomp/testsuite/libgomp.c++/target-flex-101.C
new file mode 100644
index 00000000000..be9037e9718
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-101.C
@@ -0,0 +1,136 @@
+/* { dg-additional-options -std=c++23 } */
+
+/* C++23 container adaptors in target region.
+   Severely needs additional tests.  */
+
+#include <cstdio>
+#include <utility>
+#include <version>
+
+#if __cpp_lib_flat_map >= 202207L
+#define ENABLE_FLAT_MAP 1
+#endif
+#if __cpp_lib_flat_set >= 202207L
+#define ENABLE_FLAT_SET 1
+#endif
+
+#ifdef ENABLE_FLAT_MAP
+#include <flat_map>
+#endif
+#ifdef ENABLE_FLAT_SET
+#include <flat_set>
+#endif
+
+#include "target-flex-common.h"
+
+#ifdef ENABLE_FLAT_MAP
+template<typename K, typename V, typename std::size_t Size>
+bool test_flat_map(std::pair<K, V> (&arr)[Size])
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       using flat_map_type = std::flat_map<K, V>;
+       flat_map_type map = {arr, arr + Size};
+
+       VERIFY (!map.empty());
+       for (const auto& element : arr)
+         VERIFY (map.contains(element.first));
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+template<typename K, typename V, typename std::size_t Size>
+bool test_flat_multimap(std::pair<K, V> (&arr)[Size])
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       using flat_map_type = std::flat_map<K, V>;
+       flat_map_type map = {arr, arr + Size};
+
+       VERIFY (!map.empty());
+       for (const auto& element : arr)
+         VERIFY (map.contains(element.first));
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+#else
+template<typename K, typename V, typename std::size_t Size>
+bool test_flat_map(std::pair<K, V> (&arr)[Size]) { return true; }
+
+template<typename K, typename V, typename std::size_t Size>
+bool test_flat_multimap(std::pair<K, V> (&arr)[Size]) { return true; }
+#endif
+
+#ifdef ENABLE_FLAT_SET
+template<typename T, typename std::size_t Size>
+bool test_flat_set(T (&arr)[Size])
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       using flat_set_type = std::flat_set<T>;
+       flat_set_type set = {arr, arr + Size};
+
+       VERIFY (!set.empty());
+       for (const auto& element : arr)
+         VERIFY (set.contains(element));
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+template<typename T, typename std::size_t Size>
+bool test_flat_multiset(T (&arr)[Size])
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       using flat_multiset_type = std::flat_multiset<T>;
+       flat_multiset_type multiset = {arr, arr + Size};
+
+       VERIFY (!multiset.empty());
+       for (const auto& element : arr)
+         VERIFY (multiset.contains(element));
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+#else
+template<typename T, typename std::size_t Size>
+bool test_flat_set(T (&arr)[Size]) { return true; }
+
+template<typename T, typename std::size_t Size>
+bool test_flat_multiset(T (&arr)[Size]) { return true; }
+#endif
+
+int main()
+{
+  int arr[10] = {0,1,2,3,4,5,6,7,8,9};
+  std::pair<int, int> pairs[10] = {{ 1,  2}, { 2,  4}, { 3,  6}, { 4,  8}, { 
5, 10},
+                                  { 6, 12}, { 7, 14}, { 8, 16}, { 9, 18}, {10, 
20}};
+
+  return test_flat_set(arr)
+        && test_flat_multiset(arr)
+        && test_flat_map(pairs)
+        && test_flat_multimap(pairs) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-11.C 
b/libgomp/testsuite/libgomp.c++/target-flex-11.C
new file mode 100644
index 00000000000..6d55129e6a8
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-11.C
@@ -0,0 +1,444 @@
+/* Check constructors/destructors are called in containers.  */
+
+#include <vector>
+#include <deque>
+#include <list>
+#include <set>
+#include <map>
+#include <utility>
+#if __cplusplus >= 201103L
+#include <array>
+#include <forward_list>
+#include <unordered_set>
+#include <unordered_map>
+#endif
+
+#include "target-flex-common.h"
+
+struct indirect_counter
+{
+  typedef int counter_value_type;
+  counter_value_type *_count_ptr;
+
+  indirect_counter(counter_value_type *count_ptr) BL_NOEXCEPT : 
_count_ptr(count_ptr) {
+    ++(*_count_ptr);
+  }
+  indirect_counter(const indirect_counter& other) BL_NOEXCEPT : 
_count_ptr(other._count_ptr) {
+    ++(*_count_ptr);
+  }
+  /* Don't declare a move constructor, we want to copy no matter what.  */
+  ~indirect_counter() {
+    --(*_count_ptr);
+  }
+};
+
+bool operator==(indirect_counter const& lhs, indirect_counter const& rhs) 
BL_NOEXCEPT
+  { return lhs._count_ptr == rhs._count_ptr; }
+bool operator<(indirect_counter const& lhs, indirect_counter const& rhs) 
BL_NOEXCEPT
+  { return lhs._count_ptr < rhs._count_ptr; }
+
+#if __cplusplus >= 201103L
+template<>
+struct std::hash<indirect_counter>
+{
+  std::size_t operator()(const indirect_counter& ic) const noexcept
+    { return std::hash<indirect_counter::counter_value_type 
*>{}(ic._count_ptr); }
+};
+#endif
+
+/* Not a container, just a sanity check really.  */
+bool automatic_lifetime_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter = 0;
+      {
+       indirect_counter c = indirect_counter(&counter);
+       indirect_counter(static_cast<int*>(&counter));
+      }
+      VERIFY (counter == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool vector_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter = 0;
+      {
+       std::vector<indirect_counter> vec(42, indirect_counter(&counter));
+       VERIFY (counter == 42);
+       vec.resize(32, indirect_counter(&counter));
+       VERIFY (counter == 32);
+       vec.push_back(indirect_counter(&counter));
+       VERIFY (counter == 33);
+       vec.pop_back();
+       VERIFY (counter == 32);
+       vec.pop_back();
+       VERIFY (counter == 31);
+       vec.resize(100, indirect_counter(&counter));
+       VERIFY (counter == 100);
+      }
+      VERIFY (counter == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool deque_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter = 0;
+      {
+       std::deque<indirect_counter> vec(42, indirect_counter(&counter));
+       VERIFY (counter == 42);
+       vec.resize(32, indirect_counter(&counter));
+       VERIFY (counter == 32);
+       vec.push_back(indirect_counter(&counter));
+       VERIFY (counter == 33);
+       vec.pop_back();
+       VERIFY (counter == 32);
+       vec.pop_back();
+       VERIFY (counter == 31);
+       vec.resize(100, indirect_counter(&counter));
+       VERIFY (counter == 100);
+      }
+      VERIFY (counter == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool list_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter = 0;
+      {
+       std::list<indirect_counter> list(42, indirect_counter(&counter));
+       VERIFY (counter == 42);
+       list.resize(32, indirect_counter(&counter));
+       VERIFY (counter == 32);
+       list.push_back(indirect_counter(&counter));
+       VERIFY (counter == 33);
+       list.pop_back();
+       VERIFY (counter == 32);
+       list.pop_back();
+       VERIFY (counter == 31);
+       list.resize(100, indirect_counter(&counter));
+       VERIFY (counter == 100);
+      }
+      VERIFY (counter == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool map_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter = 0;
+      {
+       std::map<int, indirect_counter> map;
+       map.insert(std::make_pair(1, indirect_counter(&counter)));
+       VERIFY (counter == 1);
+       map.insert(std::make_pair(1, indirect_counter(&counter)));
+       VERIFY (counter == 1);
+       map.insert(std::make_pair(2, indirect_counter(&counter)));
+       VERIFY (counter == 2);
+      }
+      VERIFY (counter == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool set_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter0 = 0;
+      int counter1 = 0;
+      {
+       std::set<indirect_counter> set;
+       set.insert(indirect_counter(&counter0));
+       VERIFY (counter0 == 1);
+       set.insert(indirect_counter(&counter0));
+       VERIFY (counter0 == 1);
+       set.insert(indirect_counter(&counter1));
+       VERIFY (counter0 == 1 && counter1 == 1);
+      }
+      VERIFY (counter0 == 0 && counter1 == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool multimap_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter = 0;
+      {
+       std::multimap<int, indirect_counter> multimap;
+       multimap.insert(std::make_pair(1, indirect_counter(&counter)));
+       VERIFY (counter == 1);
+       multimap.insert(std::make_pair(1, indirect_counter(&counter)));
+       VERIFY (counter == 2);
+       multimap.insert(std::make_pair(2, indirect_counter(&counter)));
+       VERIFY (counter == 3);
+      }
+      VERIFY (counter == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool multiset_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter0 = 0;
+      int counter1 = 0;
+      {
+       std::multiset<indirect_counter> multiset;
+       multiset.insert(indirect_counter(&counter0));
+       VERIFY (counter0 == 1);
+       multiset.insert(indirect_counter(&counter0));
+       VERIFY (counter0 == 2);
+       multiset.insert(indirect_counter(&counter1));
+       VERIFY (counter0 == 2 && counter1 == 1);
+      }
+      VERIFY (counter0 == 0 && counter1 == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+#if __cplusplus >= 201103L
+
+bool array_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter = 0;
+      {
+       indirect_counter ic(&counter);
+       std::array<indirect_counter, 10> array{ic, ic, ic, ic, ic,
+                                              ic, ic, ic, ic, ic};
+       VERIFY (counter == 11);
+      }
+      VERIFY (counter == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool forward_list_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter = 0;
+      {
+       std::forward_list<indirect_counter> forward_list(42, 
indirect_counter(&counter));
+       VERIFY (counter == 42);
+       forward_list.resize(32, indirect_counter(&counter));
+       VERIFY (counter == 32);
+       forward_list.push_front(indirect_counter(&counter));
+       VERIFY (counter == 33);
+       forward_list.pop_front();
+       VERIFY (counter == 32);
+       forward_list.pop_front();
+       VERIFY (counter == 31);
+       forward_list.resize(100, indirect_counter(&counter));
+       VERIFY (counter == 100);
+      }
+      VERIFY (counter == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool unordered_map_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter = 0;
+      {
+       std::unordered_map<int, indirect_counter> unordered_map;
+       unordered_map.insert({1, indirect_counter(&counter)});
+       VERIFY (counter == 1);
+       unordered_map.insert({1, indirect_counter(&counter)});
+       VERIFY (counter == 1);
+       unordered_map.insert({2, indirect_counter(&counter)});
+       VERIFY (counter == 2);
+      }
+      VERIFY (counter == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool unordered_set_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter0 = 0;
+      int counter1 = 0;
+      {
+       std::unordered_set<indirect_counter> unordered_set;
+       unordered_set.insert(indirect_counter(&counter0));
+       VERIFY (counter0 == 1);
+       unordered_set.insert(indirect_counter(&counter0));
+       VERIFY (counter0 == 1);
+       unordered_set.insert(indirect_counter(&counter1));
+       VERIFY (counter0 == 1 && counter1 == 1);
+      }
+      VERIFY (counter0 == 0 && counter1 == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool unordered_multimap_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter = 0;
+      {
+       std::unordered_multimap<int, indirect_counter> unordered_multimap;
+       unordered_multimap.insert({1, indirect_counter(&counter)});
+       VERIFY (counter == 1);
+       unordered_multimap.insert({1, indirect_counter(&counter)});
+       VERIFY (counter == 2);
+       unordered_multimap.insert({2, indirect_counter(&counter)});
+       VERIFY (counter == 3);
+      }
+      VERIFY (counter == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+bool unordered_multiset_test()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      bool inner_ok = true;
+      int counter0 = 0;
+      int counter1 = 0;
+      {
+       std::unordered_multiset<indirect_counter> unordered_multiset;
+       unordered_multiset.insert(indirect_counter(&counter0));
+       VERIFY (counter0 == 1);
+       unordered_multiset.insert(indirect_counter(&counter0));
+       VERIFY (counter0 == 2);
+       unordered_multiset.insert(indirect_counter(&counter1));
+       VERIFY (counter0 == 2 && counter1 == 1);
+      }
+      VERIFY (counter0 == 0 && counter1 == 0);
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+#else
+bool array_test() { return true; }
+bool forward_list_test() { return true; }
+bool unordered_map_test() { return true; }
+bool unordered_set_test() { return true; }
+bool unordered_multimap_test() { return true; }
+bool unordered_multiset_test() { return true; }
+#endif
+
+int main()
+{
+  const bool auto_res               = automatic_lifetime_test();
+  const bool vec_res                = vector_test();
+  const bool deque_res              = deque_test();
+  const bool list_res               = list_test();
+  const bool map_res                = map_test();
+  const bool set_res                = set_test();
+  const bool multimap_res           = multimap_test();
+  const bool multiset_res           = multiset_test();
+  const bool array_res              = array_test();
+  const bool forward_list_res       = forward_list_test();
+  const bool unordered_map_res      = unordered_map_test();
+  const bool unordered_set_res      = unordered_set_test();
+  const bool unordered_multimap_res = unordered_multimap_test();
+  const bool unordered_multiset_res = unordered_multiset_test();
+  std::printf("sanity check      : %s\n", auto_res               ? "PASS" : 
"FAIL");
+  std::printf("vector            : %s\n", vec_res                ? "PASS" : 
"FAIL");
+  std::printf("deque             : %s\n", deque_res              ? "PASS" : 
"FAIL");
+  std::printf("list              : %s\n", list_res               ? "PASS" : 
"FAIL");
+  std::printf("map               : %s\n", map_res                ? "PASS" : 
"FAIL");
+  std::printf("set               : %s\n", set_res                ? "PASS" : 
"FAIL");
+  std::printf("multimap          : %s\n", multimap_res           ? "PASS" : 
"FAIL");
+  std::printf("multiset          : %s\n", multiset_res           ? "PASS" : 
"FAIL");
+  std::printf("array             : %s\n", array_res              ? "PASS" : 
"FAIL");
+  std::printf("forward_list      : %s\n", forward_list_res       ? "PASS" : 
"FAIL");
+  std::printf("unordered_map     : %s\n", unordered_map_res      ? "PASS" : 
"FAIL");
+  std::printf("unordered_set     : %s\n", unordered_set_res      ? "PASS" : 
"FAIL");
+  std::printf("unordered_multimap: %s\n", unordered_multimap_res ? "PASS" : 
"FAIL");
+  std::printf("unordered_multiset: %s\n", unordered_multiset_res ? "PASS" : 
"FAIL");
+  const bool ok = auto_res
+                 && vec_res
+                 && deque_res
+                 && list_res
+                 && map_res
+                 && set_res
+                 && multimap_res
+                 && multiset_res
+                 && array_res
+                 && forward_list_res
+                 && unordered_map_res
+                 && unordered_set_res
+                 && unordered_multimap_res
+                 && unordered_multiset_res;
+  return ok ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-12.C 
b/libgomp/testsuite/libgomp.c++/target-flex-12.C
new file mode 100644
index 00000000000..024fb731fd8
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-12.C
@@ -0,0 +1,736 @@
+/* Populated with mapped data, validate, mutate, validate again.
+   The cases using sets do not mutate.
+   Note: Some of the code in here really sucks due to being made to be
+   compatible with c++98.  */
+
+#include <vector>
+#include <deque>
+#include <list>
+#include <set>
+#include <map>
+#if __cplusplus >= 201103L
+#include <array>
+#include <forward_list>
+#include <unordered_set>
+#include <unordered_map>
+#endif
+
+#include <limits>
+#include <iterator>
+
+#include "target-flex-common.h"
+
+template<bool B, class T = void>
+struct enable_if {};
+ 
+template<class T>
+struct enable_if<true, T> { typedef T type; };
+
+struct identity_func
+{
+#if __cplusplus < 201103L
+  template<typename T>
+  T& operator()(T& arg) const BL_NOEXCEPT { return arg; }
+  template<typename T>
+  T const& operator()(T const& arg) const BL_NOEXCEPT { return arg; }
+#else
+  template<typename T>
+  constexpr T&& operator()(T&& arg) const BL_NOEXCEPT { return 
std::forward<T>(arg); }
+#endif
+};
+
+/* Applies projection to the second iterator.  */
+template<typename It0, typename It1, typename Proj>
+bool validate_sequential_elements(const It0 begin0, const It0 end0,
+                                 const It1 begin1, const It1 end1,
+                                 Proj proj) BL_NOEXCEPT
+{
+  It0 it0 = begin0;
+  It1 it1 = begin1;
+  for (; it0 != end0; ++it0, ++it1)
+    {
+      /* Sizes mismatch, don't bother aborting though just fail the test.  */
+      if (it1 == end1)
+       return false;
+      if (*it0 != proj(*it1))
+       return false;
+    }
+  /* Sizes mismatch, do as above.  */
+  if (it1 != end1)
+    return false;
+  return true;
+}
+
+template<typename It0, typename It1>
+bool validate_sequential_elements(const It0 begin0, const It0 end0,
+                                 const It1 begin1, const It1 end1) BL_NOEXCEPT
+{
+  return validate_sequential_elements(begin0, end0, begin1, end1, 
identity_func());
+}
+
+/* Inefficient, but simple.  */
+template<typename It, typename OutIt>
+void simple_copy(const It begin, const It end, OutIt out) BL_NOEXCEPT
+{
+  for (It it = begin; it != end; ++it, ++out)
+    *out = *it;
+}
+
+template<typename It, typename MutateFn>
+void simple_mutate(const It begin, const It end, MutateFn mut_fn) BL_NOEXCEPT
+{
+  for (It it = begin; it != end; ++it)
+    *it = mut_fn(*it);
+}
+
+template<typename MutationFunc, typename T, std::size_t Size>
+bool vector_test(const T (&arr)[Size])
+{
+  bool ok;
+  T out_arr[Size];
+  T out_mut_arr[Size];
+  #pragma omp target map(from: ok, out_arr[:Size], out_mut_arr[:Size]) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::vector<T> vector(arr, arr + Size);
+       VERIFY (validate_sequential_elements(vector.begin(), vector.end(),
+                                            arr, arr + Size));
+       simple_copy(vector.begin(), vector.end(), out_arr);
+       simple_mutate(vector.begin(), vector.end(), MutationFunc());
+       VERIFY (validate_sequential_elements(vector.begin(), vector.end(),
+                                            arr, arr + Size, MutationFunc()));
+       simple_copy(vector.begin(), vector.end(), out_mut_arr);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_sequential_elements(out_arr, out_arr + Size,
+                                                 arr, arr + Size));
+  VERIFY_NON_TARGET (validate_sequential_elements(out_mut_arr, out_mut_arr + 
Size,
+                                                 arr, arr + Size, 
MutationFunc()));
+  return true;
+}
+
+template<typename MutationFunc, typename T, std::size_t Size>
+bool deque_test(const T (&arr)[Size])
+{
+  bool ok;
+  T out_arr[Size];
+  T out_mut_arr[Size];
+  #pragma omp target map(from: ok, out_arr[:Size], out_mut_arr[:Size]) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::deque<T> deque(arr, arr + Size);
+       VERIFY (validate_sequential_elements(deque.begin(), deque.end(),
+                                            arr, arr + Size));
+       simple_copy(deque.begin(), deque.end(), out_arr);
+       simple_mutate(deque.begin(), deque.end(), MutationFunc());
+       VERIFY (validate_sequential_elements(deque.begin(), deque.end(),
+                                            arr, arr + Size, MutationFunc()));
+       simple_copy(deque.begin(), deque.end(), out_mut_arr);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_sequential_elements(out_arr, out_arr + Size,
+                                                 arr, arr + Size));
+  VERIFY_NON_TARGET (validate_sequential_elements(out_mut_arr, out_mut_arr + 
Size,
+                                                 arr, arr + Size, 
MutationFunc()));
+  return true;
+}
+
+template<typename MutationFunc, typename T, std::size_t Size>
+bool list_test(const T (&arr)[Size])
+{
+  bool ok;
+  T out_arr[Size];
+  T out_mut_arr[Size];
+  #pragma omp target map(from: ok, out_arr[:Size], out_mut_arr[:Size]) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::list<T> list(arr, arr + Size);
+       VERIFY (validate_sequential_elements(list.begin(), list.end(),
+                                            arr, arr + Size));
+       simple_copy(list.begin(), list.end(), out_arr);
+       simple_mutate(list.begin(), list.end(), MutationFunc());
+       VERIFY (validate_sequential_elements(list.begin(), list.end(),
+                                            arr, arr + Size, MutationFunc()));
+       simple_copy(list.begin(), list.end(), out_mut_arr);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_sequential_elements(out_arr, out_arr + Size,
+                                                 arr, arr + Size));
+  VERIFY_NON_TARGET (validate_sequential_elements(out_mut_arr, out_mut_arr + 
Size,
+                                                 arr, arr + Size, 
MutationFunc()));
+  return true;
+}
+
+template<typename T>
+const T& get_key(const T& arg) BL_NOEXCEPT
+  { return arg; }
+template<typename K, typename V>
+const K& get_key(const std::pair<K, V>& pair) BL_NOEXCEPT
+  { return pair.first; }
+template<typename T>
+const T& get_value(const T& arg) BL_NOEXCEPT
+  { return arg; }
+template<typename K, typename V>
+const K& get_value(const std::pair<K, V>& pair) BL_NOEXCEPT
+  { return pair.second; }
+
+template<typename T>
+struct key_type { typedef T type; };
+template<typename K, typename V>
+struct key_type<std::pair<K, V> > { typedef K type; };
+
+template<typename Proj, typename Container, typename It>
+bool validate_associative(const Container& container,
+                         const It compare_begin,
+                         const It compare_end,
+                         Proj proj) BL_NOEXCEPT
+{
+  const typename Container::const_iterator elem_end = container.end();
+  for (It compare_it = compare_begin; compare_it != compare_end; ++compare_it)
+    {
+      const typename Container::const_iterator elem_it = 
container.find(get_key(*compare_it));
+      VERIFY_NON_TARGET (elem_it != elem_end);
+      VERIFY_NON_TARGET (proj(get_value(*compare_it)) == get_value(*elem_it));
+    }
+  return true;
+}
+
+template<typename Container, typename It>
+bool validate_associative(const Container& container,
+                         const It compare_begin,
+                         const It compare_end) BL_NOEXCEPT
+{
+  return validate_associative(container, compare_begin, compare_end, 
identity_func());
+}
+
+template<typename It, typename MutateFn>
+void simple_mutate_map(const It begin, const It end, MutateFn mut_fn) 
BL_NOEXCEPT
+{
+  for (It it = begin; it != end; ++it)
+    it->second = mut_fn(it->second);
+}
+
+template<typename It, typename OutIter>
+void simple_copy_unique(const It begin, const It end, OutIter out) BL_NOEXCEPT
+{
+  /* In case anyone reads this, I want it to be known that I hate c++98.  */
+  typedef typename key_type<typename 
std::iterator_traits<It>::value_type>::type key_t;
+  std::set<key_t> already_seen;
+  for (It it = begin; it != end; ++it, ++out)
+    {
+      key_t key = get_key(*it);
+      if (already_seen.find(key) != already_seen.end())
+       continue;
+      already_seen.insert(key);
+      *out = *it;
+    }
+}
+
+template<typename MutationFunc, typename K, typename V, std::size_t Size>
+bool map_test(const std::pair<K, V> (&arr)[Size])
+{
+  std::map<K, V> reference_map(arr, arr + Size);
+  bool ok;
+  /* Both sizes should be the same.  */
+  std::pair<K, V> out_pairs[Size];
+  std::size_t out_size;
+  std::pair<K, V> out_pairs_mut[Size];
+  std::size_t out_size_mut;
+  #pragma omp target map(from: ok, out_pairs[:Size], out_size, \
+                              out_pairs_mut[:Size], out_size_mut) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::vector<std::pair<K, V> > unique_elems;
+       simple_copy_unique(arr, arr + Size,
+                          std::back_insert_iterator<std::vector<std::pair<K, 
V> > >(unique_elems));
+
+       std::map<K, V> map(arr, arr + Size);
+       VERIFY (validate_associative(map, unique_elems.begin(), 
unique_elems.end()));
+       simple_copy(map.begin(), map.end(), out_pairs);
+       out_size = map.size();
+       simple_mutate_map(map.begin(), map.end(), MutationFunc());
+       VERIFY (validate_associative(map, unique_elems.begin(), 
unique_elems.end(),
+                                    MutationFunc()));
+       simple_copy(map.begin(), map.end(), out_pairs_mut);
+       out_size_mut = map.size();
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (out_size == out_size_mut);
+  VERIFY_NON_TARGET (validate_associative(reference_map,
+                                         out_pairs, out_pairs + out_size));
+  simple_mutate_map(reference_map.begin(), reference_map.end(), 
MutationFunc());
+  VERIFY_NON_TARGET (validate_associative(reference_map,
+                                         out_pairs_mut, out_pairs_mut + 
out_size_mut));
+  return true;
+}
+
+template<typename T, std::size_t Size>
+bool set_test(const T (&arr)[Size])
+{
+  std::set<T> reference_set(arr, arr + Size);
+  bool ok;
+  /* Both sizes should be the same.  */
+  T out_arr[Size];
+  std::size_t out_size;
+  #pragma omp target map(from: ok, out_arr[:Size], out_size) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::vector<T> unique_elems;
+       simple_copy_unique(arr, arr + Size,
+                          std::back_insert_iterator<std::vector<T> 
>(unique_elems));
+
+       std::set<T> set(arr, arr + Size);
+       VERIFY (validate_associative(set, unique_elems.begin(), 
unique_elems.end()));
+       simple_copy(set.begin(), set.end(), out_arr);
+       out_size = set.size();
+       /* Sets can't be mutated, we could create another set with mutated
+          but it gets a little annoying and probably isn't an interesting 
test.  */
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_associative(reference_set,
+                                         out_arr, out_arr + out_size));
+  return true;
+}
+
+template<typename Proj, typename Container, typename It>
+bool validate_multi_associative(const Container& container,
+                               const It compare_begin,
+                               const It compare_end,
+                               Proj proj) BL_NOEXCEPT
+{
+  /* Once again, for the poor soul reviewing these, I hate c++98.  */
+  typedef typename key_type<typename 
std::iterator_traits<It>::value_type>::type key_t;
+  typedef std::map<key_t, std::size_t> counter_map; 
+  counter_map key_count_map;
+  for (It it = compare_begin; it != compare_end; ++it)
+    {
+      const key_t& key = get_key(*it);
+      typename counter_map::iterator counter_it
+       = key_count_map.find(key);
+      if (counter_it != key_count_map.end())
+       ++counter_it->second;
+      else
+       key_count_map.insert(std::pair<const key_t, std::size_t>(key, 
std::size_t(1)));
+    }
+  const typename Container::const_iterator elem_end = container.end();
+  for (It compare_it = compare_begin; compare_it != compare_end; ++compare_it)
+    {
+      const key_t& key = get_key(*compare_it);
+      typename counter_map::iterator count_it = key_count_map.find(key);
+      std::size_t key_count = count_it != key_count_map.end() ? 
count_it->second
+                                                             : std::size_t(0);
+      VERIFY_NON_TARGET (key_count > std::size_t(0) && "this will never 
happen");
+      /* This gets tested multiple times but that should be fine.  */
+      VERIFY_NON_TARGET (key_count == container.count(key));
+      typename Container::const_iterator elem_it = container.find(key);
+      /* This will never happen if the previous case passed.  */
+      VERIFY_NON_TARGET (elem_it != elem_end);
+      bool found_element = false;
+      for (; elem_it != elem_end; ++elem_it)
+       if (proj(get_value(*compare_it)) == get_value(*elem_it))
+         {
+           found_element = true;
+           break;
+         }
+      VERIFY_NON_TARGET (found_element);
+    }
+  return true;
+}
+
+template<typename Container, typename It>
+bool validate_multi_associative(const Container& container,
+                               const It compare_begin,
+                               const It compare_end) BL_NOEXCEPT
+{
+  return validate_multi_associative(container, compare_begin, compare_end, 
identity_func());
+}
+
+template<typename MutationFunc, typename K, typename V, std::size_t Size>
+bool multimap_test(const std::pair<K, V> (&arr)[Size])
+{
+  std::multimap<K, V> reference_multimap(arr, arr + Size);
+  bool ok;
+  std::pair<K, V> out_pairs[Size];
+  std::pair<K, V> out_pairs_mut[Size];
+  #pragma omp target map(from: ok, out_pairs[:Size], out_pairs_mut[:Size]) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::multimap<K, V> multimap(arr, arr + Size);
+       VERIFY (validate_multi_associative(multimap, arr, arr + Size));
+       simple_copy(multimap.begin(), multimap.end(), out_pairs);
+       simple_mutate_map(multimap.begin(), multimap.end(), MutationFunc());
+       VERIFY (validate_multi_associative(multimap, arr, arr + Size, 
MutationFunc()));
+       simple_copy(multimap.begin(), multimap.end(), out_pairs_mut);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_multi_associative(reference_multimap,
+                                               out_pairs, out_pairs + Size));
+  simple_mutate_map(reference_multimap.begin(), reference_multimap.end(), 
MutationFunc());
+  VERIFY_NON_TARGET (validate_multi_associative(reference_multimap,
+                                               out_pairs_mut, out_pairs_mut + 
Size));
+  return true;
+}
+
+template<typename T, std::size_t Size>
+bool multiset_test(const T (&arr)[Size])
+{
+  std::multiset<T> reference_multiset(arr, arr + Size);
+  bool ok;
+  T out_arr[Size];
+  #pragma omp target map(from: ok, out_arr[:Size]) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::multiset<T> set(arr, arr + Size);
+       VERIFY (validate_multi_associative(set, arr, arr + Size));
+       simple_copy(set.begin(), set.end(), out_arr);
+       /* Sets can't be mutated, we could create another set with mutated
+          but it gets a little annoying and probably isn't an interesting 
test.  */
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_multi_associative(reference_multiset,
+                                               out_arr, out_arr + Size));
+  return true;
+}
+
+#if __cplusplus >= 201103L
+
+template<typename MutationFunc, typename T, std::size_t Size>
+bool array_test(const T (&arr)[Size])
+{
+  bool ok;
+  T out_arr[Size];
+  T out_mut_arr[Size];
+  #pragma omp target map(from: ok, out_arr[:Size], out_mut_arr[:Size]) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::array<T, Size> std_array{};
+       /* Special case for std::array since it can't be initialized
+          with iterators.  */
+       {
+         T zero_val = T{};
+         for (auto it = std_array.begin(); it != std_array.end(); ++it)
+           VERIFY (*it == zero_val);
+       }
+       simple_copy(arr, arr + Size, std_array.begin());
+       VERIFY (validate_sequential_elements(std_array.begin(), std_array.end(),
+                                            arr, arr + Size));
+       simple_copy(std_array.begin(), std_array.end(), out_arr);
+       simple_mutate(std_array.begin(), std_array.end(), MutationFunc());
+       VERIFY (validate_sequential_elements(std_array.begin(), std_array.end(),
+                                            arr, arr + Size, MutationFunc()));
+       simple_copy(std_array.begin(), std_array.end(), out_mut_arr);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_sequential_elements(out_arr, out_arr + Size,
+                                                 arr, arr + Size));
+  VERIFY_NON_TARGET (validate_sequential_elements(out_mut_arr, out_mut_arr + 
Size,
+                                                 arr, arr + Size, 
MutationFunc()));
+  return true;
+}
+
+template<typename MutationFunc, typename T, std::size_t Size>
+bool forward_list_test(const T (&arr)[Size])
+{
+  bool ok;
+  T out_arr[Size];
+  T out_mut_arr[Size];
+  #pragma omp target map(from: ok, out_arr[:Size], out_mut_arr[:Size]) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::forward_list<T> fwd_list(arr, arr + Size);
+       VERIFY (validate_sequential_elements(fwd_list.begin(), fwd_list.end(),
+                                            arr, arr + Size));
+       simple_copy(fwd_list.begin(), fwd_list.end(), out_arr);
+       simple_mutate(fwd_list.begin(), fwd_list.end(), MutationFunc());
+       VERIFY (validate_sequential_elements(fwd_list.begin(), fwd_list.end(),
+                                            arr, arr + Size, MutationFunc()));
+       simple_copy(fwd_list.begin(), fwd_list.end(), out_mut_arr);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_sequential_elements(out_arr, out_arr + Size,
+                                                 arr, arr + Size));
+  VERIFY_NON_TARGET (validate_sequential_elements(out_mut_arr, out_mut_arr + 
Size,
+                                                 arr, arr + Size, 
MutationFunc()));
+  return true;
+}
+
+template<typename MutationFunc, typename K, typename V, std::size_t Size>
+bool unordered_map_test(const std::pair<K, V> (&arr)[Size])
+{
+  std::unordered_map<K, V> reference_map(arr, arr + Size);
+  bool ok;
+  /* Both sizes should be the same.  */
+  std::pair<K, V> out_pairs[Size];
+  std::size_t out_size;
+  std::pair<K, V> out_pairs_mut[Size];
+  std::size_t out_size_mut;
+  #pragma omp target map(from: ok, out_pairs[:Size], out_size, \
+                              out_pairs_mut[:Size], out_size_mut) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::vector<std::pair<K, V> > unique_elems;
+       simple_copy_unique(arr, arr + Size,
+                          std::back_insert_iterator<std::vector<std::pair<K, 
V> > >(unique_elems));
+
+       std::unordered_map<K, V> map(arr, arr + Size);
+       VERIFY (validate_associative(map, unique_elems.begin(), 
unique_elems.end()));
+       simple_copy(map.begin(), map.end(), out_pairs);
+       out_size = map.size();
+       simple_mutate_map(map.begin(), map.end(), MutationFunc());
+       VERIFY (validate_associative(map, unique_elems.begin(), 
unique_elems.end(),
+                                    MutationFunc()));
+       simple_copy(map.begin(), map.end(), out_pairs_mut);
+       out_size_mut = map.size();
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (out_size == out_size_mut);
+  VERIFY_NON_TARGET (validate_associative(reference_map,
+                                         out_pairs, out_pairs + out_size));
+  simple_mutate_map(reference_map.begin(), reference_map.end(), 
MutationFunc());
+  VERIFY_NON_TARGET (validate_associative(reference_map,
+                                         out_pairs_mut, out_pairs_mut + 
out_size_mut));
+  return true;
+}
+
+template<typename T, std::size_t Size>
+bool unordered_set_test(const T (&arr)[Size])
+{
+  std::unordered_set<T> reference_set(arr, arr + Size);
+  bool ok;
+  /* Both sizes should be the same.  */
+  T out_arr[Size];
+  std::size_t out_size;
+  #pragma omp target map(from: ok, out_arr[:Size], out_size) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::vector<T> unique_elems;
+       simple_copy_unique(arr, arr + Size,
+                          std::back_insert_iterator<std::vector<T> 
>(unique_elems));
+
+       std::unordered_set<T> set(arr, arr + Size);
+       VERIFY (validate_associative(set, unique_elems.begin(), 
unique_elems.end()));
+       simple_copy(set.begin(), set.end(), out_arr);
+       out_size = set.size();
+       /* Sets can't be mutated, we could create another set with mutated
+          but it gets a little annoying and probably isn't an interesting 
test.  */
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_associative(reference_set,
+                                         out_arr, out_arr + out_size));
+  return true;
+}
+
+template<typename MutationFunc, typename K, typename V, std::size_t Size>
+bool unordered_multimap_test(const std::pair<K, V> (&arr)[Size])
+{
+  std::unordered_multimap<K, V> reference_multimap(arr, arr + Size);
+  bool ok;
+  std::pair<K, V> out_pairs[Size];
+  std::pair<K, V> out_pairs_mut[Size];
+  #pragma omp target map(from: ok, out_pairs[:Size], out_pairs_mut[:Size]) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::unordered_multimap<K, V> multimap(arr, arr + Size);
+       VERIFY (validate_multi_associative(multimap, arr, arr + Size));
+       simple_copy(multimap.begin(), multimap.end(), out_pairs);
+       simple_mutate_map(multimap.begin(), multimap.end(), MutationFunc());
+       VERIFY (validate_multi_associative(multimap, arr, arr + Size, 
MutationFunc()));
+       simple_copy(multimap.begin(), multimap.end(), out_pairs_mut);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_multi_associative(reference_multimap,
+                                               out_pairs, out_pairs + Size));
+  simple_mutate_map(reference_multimap.begin(), reference_multimap.end(), 
MutationFunc());
+  VERIFY_NON_TARGET (validate_multi_associative(reference_multimap,
+                                               out_pairs_mut, out_pairs_mut + 
Size));
+  return true;
+}
+
+template<typename T, std::size_t Size>
+bool unordered_multiset_test(const T (&arr)[Size])
+{
+  std::unordered_multiset<T> reference_multiset(arr, arr + Size);
+  bool ok;
+  T out_arr[Size];
+  #pragma omp target map(from: ok, out_arr[:Size]) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::unordered_multiset<T> set(arr, arr + Size);
+       VERIFY (validate_multi_associative(set, arr, arr + Size));
+       simple_copy(set.begin(), set.end(), out_arr);
+       /* Sets can't be mutated, we could create another set with mutated
+          but it gets a little annoying and probably isn't an interesting 
test.  */
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (validate_multi_associative(reference_multiset,
+                                               out_arr, out_arr + Size));
+  return true;
+}
+
+#else
+template<typename, typename T, std::size_t Size> bool array_test(const T 
(&arr)[Size]) { return true; }
+template<typename, typename T, std::size_t Size> bool forward_list_test(const 
T (&arr)[Size]) { return true; }
+template<typename, typename T, std::size_t Size> bool unordered_map_test(const 
T (&arr)[Size]) { return true; }
+template<typename T, std::size_t Size> bool unordered_set_test(const T 
(&arr)[Size]) { return true; }
+template<typename, typename T, std::size_t Size> bool 
unordered_multimap_test(const T (&arr)[Size]) { return true; }
+template<typename T, std::size_t Size> bool unordered_multiset_test(const T 
(&arr)[Size]) { return true; }
+#endif
+
+/* This clamps to the maximum value to guard against overflowing,
+   assuming std::numeric_limits is specialized for T.  */
+struct multiply_by_2
+{
+  template<typename T>
+  typename enable_if<std::numeric_limits<T>::is_specialized, T>::type
+  operator()(T arg) const BL_NOEXCEPT {
+    if (arg < static_cast<T>(0))
+      {
+       if (std::numeric_limits<T>::min() / static_cast<T>(2) >= arg)
+         return std::numeric_limits<T>::min();
+      }
+    else
+      {
+       if (std::numeric_limits<T>::max() / static_cast<T>(2) <= arg)
+         return std::numeric_limits<T>::max();
+      }
+    return arg * 2;
+  }
+  template<typename T>
+  typename enable_if<!std::numeric_limits<T>::is_specialized, T>::type
+  operator()(T arg) const BL_NOEXCEPT {
+    return arg * 2;
+  }
+};
+
+int main()
+{
+  int data[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+  std::pair<int, int> pairs[10] = {std::pair<int, int>( 1,  2),
+                                  std::pair<int, int>( 2,  4),
+                                  std::pair<int, int>( 3,  6),
+                                  std::pair<int, int>( 4,  8),
+                                  std::pair<int, int>( 5, 10),
+                                  std::pair<int, int>( 6, 12),
+                                  std::pair<int, int>( 7, 14),
+                                  std::pair<int, int>( 8, 16),
+                                  std::pair<int, int>( 9, 18),
+                                  std::pair<int, int>(10, 20)};
+  const bool vec_res                = vector_test<multiply_by_2>(data);
+  const bool deque_res              = deque_test<multiply_by_2>(data);
+  const bool list_res               = list_test<multiply_by_2>(data);
+  const bool map_res                = map_test<multiply_by_2>(pairs);
+  const bool set_res                = set_test(data);
+  const bool multimap_res           = multimap_test<multiply_by_2>(pairs);
+  const bool multiset_res           = multiset_test(data);
+  const bool array_res              = array_test<multiply_by_2>(data);
+  const bool forward_list_res       = forward_list_test<multiply_by_2>(data);
+  const bool unordered_map_res      = unordered_map_test<multiply_by_2>(pairs);
+  const bool unordered_set_res      = unordered_set_test(data);
+  const bool unordered_multimap_res = 
unordered_multimap_test<multiply_by_2>(pairs);
+  const bool unordered_multiset_res = unordered_multiset_test(data);
+  std::printf("vector            : %s\n", vec_res                ? "PASS" : 
"FAIL");
+  std::printf("deque             : %s\n", deque_res              ? "PASS" : 
"FAIL");
+  std::printf("list              : %s\n", list_res               ? "PASS" : 
"FAIL");
+  std::printf("map               : %s\n", map_res                ? "PASS" : 
"FAIL");
+  std::printf("set               : %s\n", set_res                ? "PASS" : 
"FAIL");
+  std::printf("multimap          : %s\n", multimap_res           ? "PASS" : 
"FAIL");
+  std::printf("multiset          : %s\n", multiset_res           ? "PASS" : 
"FAIL");
+  std::printf("array             : %s\n", array_res              ? "PASS" : 
"FAIL");
+  std::printf("forward_list      : %s\n", forward_list_res       ? "PASS" : 
"FAIL");
+  std::printf("unordered_map     : %s\n", unordered_map_res      ? "PASS" : 
"FAIL");
+  std::printf("unordered_set     : %s\n", unordered_set_res      ? "PASS" : 
"FAIL");
+  std::printf("unordered_multimap: %s\n", unordered_multimap_res ? "PASS" : 
"FAIL");
+  std::printf("unordered_multiset: %s\n", unordered_multiset_res ? "PASS" : 
"FAIL");
+  const bool ok = vec_res
+                 && deque_res
+                 && list_res
+                 && map_res
+                 && set_res
+                 && multimap_res
+                 && multiset_res
+                 && array_res
+                 && forward_list_res
+                 && unordered_map_res
+                 && unordered_set_res
+                 && unordered_multimap_res
+                 && unordered_multiset_res;
+  return ok ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-2000.C 
b/libgomp/testsuite/libgomp.c++/target-flex-2000.C
new file mode 100644
index 00000000000..688c014eb9d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-2000.C
@@ -0,0 +1,32 @@
+/* Tiny tuple test.  */
+
+#include <tuple>
+
+#include "target-flex-common.h"
+
+bool test(int arg)
+{
+  bool ok;
+  int out;
+  std::tuple tup = {'a', arg, 3.14f};
+  #pragma omp target map(from: ok, out) map(to: tup)
+    {
+      bool inner_ok = true;
+      {
+       VERIFY (std::get<0>(tup) == 'a');
+       out = std::get<1>(tup);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (out == arg);
+  return true;
+}
+
+int main()
+{
+  volatile int arg = 42u;
+  return test(arg) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-2001.C 
b/libgomp/testsuite/libgomp.c++/target-flex-2001.C
new file mode 100644
index 00000000000..f1a6c12ed11
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-2001.C
@@ -0,0 +1,61 @@
+/* { dg-additional-options "-std=c++20" } */
+
+/* Functional  */
+
+#include <functional>
+#include <utility>
+
+#include "target-flex-common.h"
+
+template<typename T,typename Fn>
+auto invoke_unary(T&& a, Fn&& fn) noexcept
+{
+  return std::invoke(std::forward<Fn>(fn),
+                    std::forward<T>(a));
+}
+
+template<typename T, typename U, typename Fn>
+auto invoke_binary(T&& a, U&& b, Fn&& fn) noexcept
+{
+  return std::invoke(std::forward<Fn>(fn),
+                    std::forward<T>(a),
+                    std::forward<U>(b));
+}
+
+bool test(unsigned arg)
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arg)
+    {
+      bool inner_ok = true;
+      {
+       VERIFY (std::plus{}(arg, 2) == arg + 2);
+       auto bound_plus_arg = std::bind_front(std::plus{}, arg);
+       VERIFY (bound_plus_arg(10) == arg + 10);
+       VERIFY (bound_plus_arg(20) == arg + 20);
+
+       VERIFY (std::not_fn(std::not_equal_to{})(arg, arg));
+       VERIFY (invoke_binary(arg, arg, std::not_fn(std::not_equal_to{})));
+       auto bound_equals_arg = 
std::bind_front(std::not_fn(std::not_equal_to{}), arg);
+       VERIFY (bound_equals_arg(arg));
+       VERIFY (std::not_fn(bound_equals_arg)(arg + 1));
+       VERIFY (invoke_unary(arg, bound_equals_arg));
+
+       VERIFY (std::not_fn(std::ranges::not_equal_to{})(arg, arg));
+       VERIFY (invoke_binary(arg, arg, 
std::not_fn(std::ranges::not_equal_to{})));
+       auto bound_ranges_equals_arg = 
std::bind_front(std::not_fn(std::ranges::not_equal_to{}), arg);
+       VERIFY (bound_ranges_equals_arg(arg));
+       VERIFY (std::not_fn(bound_ranges_equals_arg)(arg + 1));
+       VERIFY (invoke_unary(arg, bound_ranges_equals_arg));
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+int main()
+{
+  volatile unsigned arg = 42u;
+  return test(arg) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-2002.C 
b/libgomp/testsuite/libgomp.c++/target-flex-2002.C
new file mode 100644
index 00000000000..f7388065bf2
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-2002.C
@@ -0,0 +1,97 @@
+/* { dg-additional-options "-std=c++23" } */
+
+/* expected/optional  */
+
+#include <optional>
+#include <expected>
+
+#include "target-flex-common.h"
+
+std::optional<unsigned> make_optional(bool b, unsigned arg = 0u) noexcept
+{
+  if (!b)
+    return std::nullopt;
+  return {arg};
+}
+
+bool test_optional(unsigned arg)
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arg)
+    {
+      bool inner_ok = true;
+      {
+       auto null_opt = make_optional(false);
+       VERIFY (!null_opt);
+       VERIFY (!null_opt.has_value());
+       VERIFY (null_opt.value_or(arg * 2u) == arg * 2u);
+       VERIFY (null_opt.or_else([&](){ return std::optional<unsigned>{arg}; })
+                       .transform([](int a){ return a * 2u; })
+                       .value_or(0) == arg * 2u);
+
+       auto opt = make_optional(true, arg);
+       VERIFY (opt);
+       VERIFY (opt.has_value());
+       VERIFY (opt.value() == arg);
+       VERIFY (*opt == arg);
+       VERIFY (opt.value_or(arg + 42) == arg);
+       VERIFY (opt.or_else([&](){ return std::optional<unsigned>{arg + 42}; })
+                  .transform([](int a){ return a * 2u; })
+                  .value_or(0) == arg * 2u);
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+struct my_error
+{
+  int _e;
+};
+
+std::expected<unsigned, my_error> make_expected(bool b, unsigned arg = 0u) 
noexcept
+{
+  if (!b)
+    return std::unexpected{my_error{-1}};
+  return {arg};
+}
+
+bool test_expected(unsigned arg)
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arg)
+    {
+      bool inner_ok = true;
+      {
+       auto unexpected = make_expected(false);
+       VERIFY (!unexpected);
+       VERIFY (!unexpected.has_value());
+       VERIFY (unexpected.error()._e == -1);
+       VERIFY (unexpected.value_or(arg * 2u) == arg * 2u);
+       VERIFY (unexpected.or_else([&](my_error e){ return 
std::expected<unsigned, my_error>{arg}; })
+                         .transform([](int a){ return a * 2u; })
+                         .value_or(0) == arg * 2u);
+
+       auto expected = make_expected(true, arg);
+       VERIFY (expected);
+       VERIFY (expected.has_value());
+       VERIFY (expected.value() == arg);
+       VERIFY (*expected == arg);
+       VERIFY (expected.value_or(arg + 42) == arg);
+       VERIFY (expected.or_else([&](my_error e){ return 
std::expected<unsigned, my_error>{std::unexpected{e}}; })
+                       .transform([](int a){ return a * 2u; })
+                       .value_or(0) == arg * 2u);
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+int main()
+{
+  volatile unsigned arg = 42;
+  return test_optional(arg)
+        && test_expected(arg) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-2003.C 
b/libgomp/testsuite/libgomp.c++/target-flex-2003.C
new file mode 100644
index 00000000000..8e8ca8eabb3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-2003.C
@@ -0,0 +1,176 @@
+/* { dg-additional-options "-std=c++20" } */
+
+/* bit_cast and memcpy  */
+
+#include <bit>
+#include <cstring>
+
+#include "target-flex-common.h"
+
+struct S0
+{
+  int _v0;
+  char _v1;
+  long long _v2;
+};
+
+struct S1
+{
+  int _v0;
+  char _v1;
+  long long _v2;
+};
+
+bool test_bit_cast(int arg)
+{
+  bool ok;
+  S1 s1_out;
+  #pragma omp target map(from: ok, s1_out) map(to: arg)
+    {
+      bool inner_ok = true;
+      {
+       long long v = static_cast<long long>(arg + 42ll);
+       S0 s = {arg, 'a', v};
+       VERIFY (std::bit_cast<S1>(s)._v0 == arg);
+       VERIFY (std::bit_cast<S1>(s)._v1 == 'a');
+       VERIFY (std::bit_cast<S1>(s)._v2 == v);
+       s1_out = std::bit_cast<S1>(s);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  long long v = static_cast<long long>(arg + 42ll);
+  VERIFY_NON_TARGET (std::bit_cast<S0>(s1_out)._v0 == arg);
+  VERIFY_NON_TARGET (std::bit_cast<S0>(s1_out)._v1 == 'a');
+  VERIFY_NON_TARGET (std::bit_cast<S0>(s1_out)._v2 == v);
+  return true;
+}
+
+
+struct OutStruct
+{
+  std::size_t _id;
+  void *_next;
+};
+
+struct Extendable1
+{
+  std::size_t _id;
+  void *_next;
+  int _v;
+};
+
+struct Extendable2
+{
+  std::size_t _id;
+  void *_next;
+  char _str[256];
+};
+
+struct Extendable3
+{
+  std::size_t _id;
+  void *_next;
+  const int *_nums;
+  std::size_t _size;
+};
+
+struct ExtendableUnknown
+{
+  std::size_t _id;
+  void *_next;
+};
+
+template<typename To, std::size_t Id>
+To *get_extendable(void *p)
+{
+  while (p != nullptr)
+    {
+      OutStruct out;
+      std::memcpy(&out, p, sizeof(OutStruct));
+      if (out._id == Id)
+       return static_cast<To *>(p);
+      p = out._next;
+    }
+  return nullptr;
+}
+
+bool test_memcpy(int arg, const int *nums, std::size_t nums_size)
+{
+  bool ok;
+  Extendable2 e2_out;
+  #pragma omp target map(from: ok, e2_out) map(to: arg, nums[:nums_size], 
nums_size)
+    {
+      bool inner_ok = true;
+      {
+       Extendable3 e3 = {3u, nullptr, nums, nums_size};
+       ExtendableUnknown u1 = {100u, &e3};
+       Extendable2 e2 = {2u, &u1, {'H', 'e', 'l', 'l', 'o', '!', '\000'}};
+       ExtendableUnknown u2 = {101u, &e2};
+       ExtendableUnknown u3 = {102u, &u2};
+       ExtendableUnknown u4 = {142u, &u3};
+       Extendable1 e1 = {1u, &u4, arg};
+
+       void *p = &e1;
+       while (p != nullptr)
+         {
+           /* You can always cast a pointer to a struct to a pointer to
+              the type of it's first member.  */
+           switch (*static_cast<std::size_t *>(p))
+             {
+               case 1:
+                 {
+                   Extendable1 *e1_p = static_cast<Extendable1 *>(p);
+                   p = e1_p->_next;
+                   VERIFY (e1_p->_v == arg);
+                   break;
+                 }
+               case 2:
+                 {
+                   Extendable2 *e2_p = static_cast<Extendable2 *>(p);
+                   p = e2_p->_next;
+                   VERIFY (std::strcmp(e2_p->_str, "Hello!") == 0);
+                   break;
+                 }
+               case 3:
+                 {
+                   Extendable3 *e3_p = static_cast<Extendable3 *>(p);
+                   p = e3_p->_next;
+                   VERIFY (nums == e3_p->_nums);
+                   VERIFY (nums_size == e3_p->_size);
+                   break;
+                 }
+               default:
+                 {
+                   /* Casting to a pointer to OutStruct invokes undefined
+                      behavior though, memcpy is required to extract the _next
+                      member.  */
+                   OutStruct out;
+                   std::memcpy(&out, p, sizeof(OutStruct));
+                   p = out._next;
+                 }
+             }
+         }
+       Extendable2 *e2_p = get_extendable<Extendable2, 2u>(&e1);
+       VERIFY (e2_p != nullptr);
+       e2_out = *e2_p;
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (e2_out._id == 2u);
+  VERIFY_NON_TARGET (std::strcmp(e2_out._str, "Hello!") == 0);
+  return true;
+}
+
+int main()
+{
+  volatile int arg = 42;
+  int arr[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+  return test_bit_cast(arg)
+        && test_memcpy(arg, arr, 8) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-30.C 
b/libgomp/testsuite/libgomp.c++/target-flex-30.C
new file mode 100644
index 00000000000..c66075b0532
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-30.C
@@ -0,0 +1,51 @@
+/* std::initializer_list in target region.  */
+
+#include <initializer_list>
+#include <array>
+
+#include "target-flex-common.h"
+
+bool test_initializer_list(int arg)
+{
+  static constexpr std::size_t out_arr_size = 7;
+  int out_arr[out_arr_size];
+  bool ok;
+  #pragma omp target map(from: ok, out_arr[:out_arr_size]) map(to: arg)
+    {
+      bool inner_ok = true;
+      {
+       auto il = {0, 1, 2, 3, 4, 5, arg};
+
+       int sum = 0;
+       for (auto const& e : il)
+         sum += e;
+       VERIFY (sum == 0 + 1 + 2 + 3 + 4 + 5 + arg);
+
+       auto* out_it = out_arr;
+       const auto* const out_end = out_arr + out_arr_size;
+       for (auto const& e : il)
+         {
+           VERIFY (out_it != out_end);
+           *out_it = e;
+           ++out_it;
+         }
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+
+  std::array<int, out_arr_size> reference_array = {0, 1, 2, 3, 4, 5, arg};
+  const auto *out_arr_it = out_arr;
+  for (auto const& e : reference_array)
+    VERIFY_NON_TARGET (e == *(out_arr_it++));
+
+  return true;
+}
+
+int main()
+{
+  volatile int arg = 42;
+  return test_initializer_list(arg) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-300.C 
b/libgomp/testsuite/libgomp.c++/target-flex-300.C
new file mode 100644
index 00000000000..ef9e5a9b29b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-300.C
@@ -0,0 +1,49 @@
+/* { dg-additional-options -std=c++23 } */
+
+/* numerics  */
+
+#include <algorithm>
+#include <numeric>
+#include <ranges>
+#include <span>
+#include <vector>
+
+//TODO PR120454 "C++ constexpr vs. OpenMP implicit mapping"
+#pragma omp declare target(std::ranges::all_of, std::ranges::iota)
+
+#include "target-flex-common.h"
+
+namespace stdr = std::ranges;
+
+bool test(std::size_t arg)
+{
+  bool ok;
+  int midpoint_out;
+  std::vector<int> vec(arg);
+  int *data = vec.data();
+  std::size_t size = vec.size();
+  #pragma omp target defaultmap(none) map(from: ok, midpoint_out) map(tofrom: 
data[:size]) map(to: arg, size)
+    {
+      std::span span = {data, size};
+      bool inner_ok = true;
+      {
+       VERIFY (stdr::all_of(span, [](int v){ return v == int{}; }));
+       stdr::iota(span, 0);
+       midpoint_out = *std::midpoint(span.data(), span.data() + span.size());
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (stdr::equal(vec, std::views::iota(0, 
static_cast<int>(vec.size()))));
+  VERIFY_NON_TARGET (*std::midpoint(vec.data(), vec.data() + vec.size())
+                    == midpoint_out);
+  return true;
+}
+
+int main()
+{
+  volatile std::size_t arg = 42;
+  return test(arg) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-31.C 
b/libgomp/testsuite/libgomp.c++/target-flex-31.C
new file mode 100644
index 00000000000..adaf18fe59c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-31.C
@@ -0,0 +1,80 @@
+/* std::initializer_list in target region.  */
+
+#include <initializer_list>
+
+#include "target-flex-common.h"
+
+struct S0
+{
+  int _v;
+  S0(std::initializer_list<int> il)
+    : _v(0)
+  {
+    for (auto const& e : il)
+      _v += e;
+  }
+};
+
+struct S1
+{
+  int _v;
+  template<typename T>
+  S1(std::initializer_list<T> il)
+    : _v(0)
+  {
+    for (auto const& e : il)
+      _v += e;
+  }
+};
+
+template<typename T>
+struct S2
+{
+  T _v;
+  S2(std::initializer_list<T> il)
+    : _v(0)
+  {
+    for (auto const& e : il)
+      _v += e;
+  }
+};
+
+#if __cplusplus >= 201703L
+template<typename T>
+S2(std::initializer_list<T>) -> S2<T>;
+#endif
+
+bool test_initializer_list(int arg)
+{
+  bool ok;
+  #pragma omp target map(from: ok) map(to: arg)
+    {
+      bool inner_ok = true;
+      {
+       static constexpr int partial_sum = 0 + 1 + 2 + 3 + 4 + 5;
+
+       S0 s0{0, 1, 2, 3, 4, 5, arg};
+       VERIFY (s0._v == partial_sum + arg);
+
+       S1 s1{0, 1, 2, 3, 4, 5, arg};
+       VERIFY (s1._v == partial_sum + arg);
+
+       S2<int> s2{0, 1, 2, 3, 4, 5, arg};
+       VERIFY (s2._v == partial_sum + arg);
+
+       #if __cplusplus >= 201703L
+         S2 s2_ctad{0, 1, 2, 3, 4, 5, arg};
+         VERIFY (s2_ctad._v == partial_sum + arg);
+       #endif
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+int main()
+{
+  volatile int arg = 42;
+  return test_initializer_list(arg) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-32.C 
b/libgomp/testsuite/libgomp.c++/target-flex-32.C
new file mode 100644
index 00000000000..7f74401a684
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-32.C
@@ -0,0 +1,50 @@
+/* std::initializer_list constructor of std::vector (explicit template arg) */
+
+#include <vector>
+#include <array>
+
+#include "target-flex-common.h"
+
+bool test_initializer_list(int arg)
+{
+  static constexpr std::size_t out_arr_size = 7;
+  int out_arr[out_arr_size];
+  bool ok;
+  #pragma omp target map(from: ok, out_arr[:out_arr_size]) map(to: arg)
+    {
+      bool inner_ok = true;
+      {
+       std::vector<int> vec{0, 1, 2, 3, 4, 5, arg};
+       int sum = 0;
+       for (auto const& e : vec)
+         sum += e;
+       VERIFY (sum == 0 + 1 + 2 + 3 + 4 + 5 + arg);
+
+       auto* out_it = out_arr;
+       const auto* const out_end = out_arr + out_arr_size;
+       for (auto const& e : vec)
+         {
+           VERIFY (out_it != out_end);
+           *out_it = e;
+           ++out_it;
+         }
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+
+  std::array<int, out_arr_size> reference_array = {0, 1, 2, 3, 4, 5, arg};
+  const auto *out_arr_it = out_arr;
+  for (auto const& e : reference_array)
+    VERIFY_NON_TARGET (e == *(out_arr_it++));
+
+  return true;
+}
+
+int main()
+{
+  volatile int arg = 42;
+  return test_initializer_list(arg) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-33.C 
b/libgomp/testsuite/libgomp.c++/target-flex-33.C
new file mode 100644
index 00000000000..bb8a39bb265
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-33.C
@@ -0,0 +1,52 @@
+/* { dg-additional-options "-std=c++17" } */
+
+/* deduced std::initializer_list constructor of std::vector (CTAD) */
+
+#include <vector>
+#include <array>
+
+#include "target-flex-common.h"
+
+bool test_initializer_list(int arg)
+{
+  static constexpr std::size_t out_arr_size = 7;
+  int out_arr[out_arr_size];
+  bool ok;
+  #pragma omp target map(from: ok, out_arr[:out_arr_size]) map(to: arg)
+    {
+      bool inner_ok = true;
+      {
+       std::vector vec{0, 1, 2, 3, 4, 5, arg};
+       int sum = 0;
+       for (auto const& e : vec)
+         sum += e;
+       VERIFY (sum == 0 + 1 + 2 + 3 + 4 + 5 + arg);
+
+       auto* out_it = out_arr;
+       const auto* const out_end = out_arr + out_arr_size;
+       for (auto const& e : vec)
+         {
+           VERIFY (out_it != out_end);
+           *out_it = e;
+           ++out_it;
+         }
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+
+  std::array<int, out_arr_size> reference_array = {0, 1, 2, 3, 4, 5, arg};
+  const auto *out_arr_it = out_arr;
+  for (auto const& e : reference_array)
+    VERIFY_NON_TARGET (e == *(out_arr_it++));
+
+  return true;
+}
+
+int main()
+{
+  volatile int arg = 42;
+  return test_initializer_list(arg) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-41.C 
b/libgomp/testsuite/libgomp.c++/target-flex-41.C
new file mode 100644
index 00000000000..4d36341f567
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-41.C
@@ -0,0 +1,94 @@
+/* { dg-additional-options "-std=c++20" } */
+
+/* <iterator> c++20  */
+
+/* std::common_iterator uses std::variant.  */
+
+#include <vector>
+#include <iterator>
+#include <span>
+
+//TODO PR120454 "C++ constexpr vs. OpenMP implicit mapping"
+#pragma omp declare target(std::ranges::distance, std::ranges::next)
+
+#include "target-flex-common.h"
+
+namespace stdr = std::ranges;
+
+template<typename It0, typename It1>
+bool simple_equal(const It0 begin0, const It0 end0,
+                 const It1 begin1, const It1 end1) BL_NOEXCEPT
+{
+  It0 it0 = begin0;
+  It1 it1 = begin1;
+  for (; it0 != end0; ++it0, ++it1)
+    if (it1 == end1 || *it0 != *it1)
+      return false;
+  return true;
+}
+
+template<typename It, typename OutIt>
+void simple_copy(const It begin, const It end, OutIt out) BL_NOEXCEPT
+{
+  for (It it = begin; it != end; ++it, ++out)
+    *out = *it;
+}
+
+template<typename T, std::size_t Size>
+bool test(const T (&arr)[Size])
+{
+  bool ok;
+  T out_rev_arr[Size];
+  T out_fwd_arr[Size];
+  T out_first_half_arr[Size / 2];
+  #pragma omp target defaultmap(none) \
+                    map(from: ok, out_rev_arr[:Size], out_fwd_arr[:Size], \
+                              out_first_half_arr[:Size / 2]) \
+                    map(to: arr[:Size])
+    {
+      bool inner_ok = true;
+      {
+       std::span<const T> span = {arr, Size};
+       std::vector<T> rev_vec(std::reverse_iterator{span.end()},
+                              std::reverse_iterator{span.begin()});
+       VERIFY (std::distance(span.begin(), span.end())
+               == std::distance(rev_vec.begin(), rev_vec.end()));
+       VERIFY (stdr::distance(span.begin(), span.end())
+               == stdr::distance(rev_vec.begin(), rev_vec.end()));
+       VERIFY (stdr::distance(span) == stdr::distance(rev_vec));
+       VERIFY (simple_equal(span.begin(), span.end(),
+                            std::reverse_iterator{rev_vec.end()},
+                            std::reverse_iterator{rev_vec.begin()}));
+       simple_copy(rev_vec.begin(), rev_vec.end(), out_rev_arr);
+       simple_copy(std::reverse_iterator{rev_vec.end()},
+                   std::reverse_iterator{rev_vec.begin()},
+                   out_fwd_arr);
+       using counted_iter = std::counted_iterator<decltype(span.begin())>;
+       using common_iter = std::common_iterator<counted_iter,
+                                                std::default_sentinel_t>;
+       std::vector<T> front_half;
+       simple_copy(common_iter{counted_iter{span.begin(), Size / 2}},
+                   common_iter{std::default_sentinel},
+                   std::back_insert_iterator{front_half});
+       VERIFY (simple_equal(span.begin(), stdr::next(span.begin(), Size / 2),
+                            front_half.begin(), front_half.end()));
+       simple_copy(front_half.begin(), front_half.end(), out_first_half_arr);
+      }
+      end:
+      ok = inner_ok;
+    }
+  VERIFY_NON_TARGET (simple_equal(std::reverse_iterator{arr + Size},
+                                 std::reverse_iterator{arr},
+                                 out_rev_arr, out_rev_arr + Size));
+  VERIFY_NON_TARGET (simple_equal(arr, arr + Size,
+                                 out_fwd_arr, out_fwd_arr + Size));
+  VERIFY_NON_TARGET (simple_equal(arr, arr + Size / 2,
+                                 out_first_half_arr, out_first_half_arr + Size 
/ 2));
+  return ok;
+}
+
+int main()
+{
+  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7};
+  return test(arr) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-60.C 
b/libgomp/testsuite/libgomp.c++/target-flex-60.C
new file mode 100644
index 00000000000..014b9f5807a
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-60.C
@@ -0,0 +1,46 @@
+/* algorithms pre c++20 */
+
+#include <algorithm>
+#include <vector>
+
+#include "target-flex-common.h"
+
+template<typename T, std::size_t Size>
+bool test(const T (&arr)[Size])
+{
+  bool ok;
+  T out_2x_arr[Size];
+  T out_shifted_arr[Size];
+  #pragma omp target map(from: ok, out_2x_arr[:Size], out_shifted_arr[:Size]) \
+                    map(to: arr[:Size])
+    {
+      std::vector<T> vec(Size);
+      std::vector<T> mutated(Size);
+      bool inner_ok = true;
+      {
+       std::copy(arr, arr + Size, vec.begin());
+       VERIFY (std::equal(arr, arr + Size, vec.begin()));
+       std::transform(vec.begin(), vec.end(), mutated.begin(),
+                      [](const T& v){ return v * 2; });
+       std::copy(mutated.begin(), mutated.end(), out_2x_arr);
+       std::rotate(vec.begin(), std::next(vec.begin(), Size / 2), vec.end());
+       std::copy(vec.begin(), vec.end(), out_shifted_arr);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (std::equal(arr, arr + Size, out_2x_arr,
+                               [](const T& a, const T& b){ return a * 2 == b; 
}));
+  std::vector<T> shifted(arr, arr + Size);
+  std::rotate(shifted.begin(), std::next(shifted.begin(), Size / 2), 
shifted.end());
+  VERIFY_NON_TARGET (std::equal(out_shifted_arr, out_shifted_arr + Size, 
shifted.begin()));
+  return true;
+}
+
+int main()
+{
+  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7};
+  return test(arr) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-61.C 
b/libgomp/testsuite/libgomp.c++/target-flex-61.C
new file mode 100644
index 00000000000..9070c2d34b8
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-61.C
@@ -0,0 +1,54 @@
+/* { dg-additional-options "-std=c++20" } */
+
+/* ranged algorithms c++20 */
+
+#include <algorithm>
+#include <ranges>
+#include <vector>
+
+//TODO PR120454 "C++ constexpr vs. OpenMP implicit mapping"
+#pragma omp declare target(std::ranges::copy, std::ranges::equal, 
std::ranges::rotate, std::ranges::transform)
+
+#include "target-flex-common.h"
+
+namespace stdr = std::ranges;
+
+template<typename T, std::size_t Size>
+bool test(const T (&arr)[Size])
+{
+  bool ok;
+  T out_2x_arr[Size];
+  T out_shifted_arr[Size];
+  #pragma omp target defaultmap(none) \
+                    map(from: ok, out_2x_arr[:Size], out_shifted_arr[:Size]) \
+                    map(to: arr[:Size])
+    {
+      std::vector<T> vec(Size);
+      std::vector<T> mutated(Size);
+      bool inner_ok = true;
+      {
+       stdr::copy(arr, vec.begin());
+       VERIFY (stdr::equal(arr, vec));
+       stdr::transform(vec, mutated.begin(),
+                       [](const T& v){ return v * 2; });
+       stdr::copy(mutated, out_2x_arr);
+       stdr::rotate(vec, std::next(vec.begin(), Size / 2));
+       stdr::copy(vec, out_shifted_arr);
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (stdr::equal(arr, out_2x_arr, stdr::equal_to{}, [](const 
T& v){ return v * 2; }));
+  std::vector<T> shifted(arr, arr + Size);
+  stdr::rotate(shifted, std::next(shifted.begin(), Size / 2));
+  VERIFY_NON_TARGET (stdr::equal(out_shifted_arr, shifted));
+  return true;
+}
+
+int main()
+{
+  int arr[] = {0, 1, 2, 3, 4, 5, 6, 7};
+  return test(arr) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-62.C 
b/libgomp/testsuite/libgomp.c++/target-flex-62.C
new file mode 100644
index 00000000000..ef6b942059c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-62.C
@@ -0,0 +1,50 @@
+/* { dg-additional-options -std=c++23 } */
+
+/* std::views stuff.  Also tests std::tuple with std::views::zip.  */
+
+#include <algorithm>
+#include <ranges>
+#include <span>
+
+//TODO PR120454 "C++ constexpr vs. OpenMP implicit mapping"
+#pragma omp declare target(std::ranges::all_of, std::ranges::equal, 
std::ranges::fold_left, std::views::reverse, std::views::zip)
+
+#include "target-flex-common.h"
+
+namespace stdr = std::ranges;
+namespace stdv = std::views;
+
+bool f()
+{
+  const int arr_fwd[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+  const int arr_rev[8] = {7, 6, 5, 4, 3, 2, 1, 0};
+
+  bool ok;
+  #pragma omp target defaultmap(none) map(from: ok) map(to: arr_fwd[:8], 
arr_rev[:8])
+    {
+      std::span<const int> fwd = {arr_fwd, 8};
+      std::span<const int> rev = {arr_rev, 8};
+      bool inner_ok = true;
+      {
+       VERIFY(stdr::equal(fwd, rev | stdv::reverse));
+       VERIFY(stdr::equal(fwd | stdv::drop(4) | stdv::reverse,
+                          rev | stdv::take(4)));
+       for (auto [first, second] : stdv::zip(fwd, rev))
+         VERIFY(first + second == 7);
+       auto plus = [](int a, int b){ return a + b; };
+       auto is_even = [](int v){ return v % 2 == 0; };
+       VERIFY(stdr::fold_left(fwd | stdv::filter(is_even), 0, plus)
+              == 12);
+       VERIFY(stdr::all_of(fwd | stdv::transform([](int v){ return v * 2; }),
+                           is_even));
+      }
+      end:
+      ok = inner_ok;
+    }
+  return ok;
+}
+
+int main()
+{
+  return f() ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-70.C 
b/libgomp/testsuite/libgomp.c++/target-flex-70.C
new file mode 100644
index 00000000000..9e9383d71e2
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-70.C
@@ -0,0 +1,26 @@
+/* CTAD in target regions.  */
+
+template<typename T>
+struct S
+{
+  T _v;
+};
+
+template<typename T>
+S(T) -> S<T>;
+
+bool f()
+{
+  bool ok;
+  #pragma omp target map(from: ok)
+    {
+      S s{42};
+      ok = s._v == 42;
+    }
+  return ok;
+}
+
+int main()
+{
+  return f() ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-80.C 
b/libgomp/testsuite/libgomp.c++/target-flex-80.C
new file mode 100644
index 00000000000..f41a1bbed36
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-80.C
@@ -0,0 +1,49 @@
+// { dg-additional-options "-std=c++20" }
+
+/* std::span  */
+
+#include <span>
+
+#include "target-flex-common.h"
+
+template<typename It0, typename It1>
+bool simple_equal(It0 it0, const It0 end0,
+                 It1 it1, const It1 end1) noexcept
+{
+  for (; it0 != end0; ++it0, ++it1)
+    if (it1 == end1 || *it0 != *it1)
+      return false;
+  return true;
+}
+
+template<typename T, std::size_t Size>
+bool test(const T (&arr)[Size])
+{
+  bool ok;
+  T out_arr[Size];
+  #pragma omp target map(from: ok) map(to: arr[:Size])
+    {
+      std::span span = {arr, Size};
+      bool inner_ok = true;
+      {
+       VERIFY (!span.empty());
+       VERIFY (span.size() == Size);
+       auto out_it = out_arr;
+       for (auto elem : span)
+         *(out_it++) = elem;
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (simple_equal(arr, arr + Size,
+                                 out_arr, out_arr + Size));
+  return true;
+}
+
+int main()
+{
+  int arr[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+  return test(arr) ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-81.C 
b/libgomp/testsuite/libgomp.c++/target-flex-81.C
new file mode 100644
index 00000000000..a86fefb8d79
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-81.C
@@ -0,0 +1,75 @@
+/* { dg-additional-options "-std=c++20" } */
+
+#include <ranges>
+#include <span>
+#include <type_traits>
+#include <vector>
+
+#include "target-flex-common.h"
+
+namespace stdr = std::ranges;
+
+template<typename It0, typename It1>
+bool simple_equal(It0 it0, const It0 end0,
+                 It1 it1, const It1 end1) noexcept
+{
+  for (; it0 != end0; ++it0, ++it1)
+    if (it1 == end1 || *it0 != *it1)
+      return false;
+  return true;
+}
+
+template<typename Rn0, typename Rn1>
+bool simple_equal(Rn0&& rn0, Rn1&& rn1) noexcept
+{
+  return simple_equal(stdr::begin(rn0), stdr::end(rn0),
+                     stdr::begin(rn1), stdr::end(rn1));
+}
+
+template<typename Rn>
+bool test(Rn&& range)
+{
+  using value_type = stdr::range_value_t<std::remove_cvref_t<Rn>>;
+  std::vector<value_type> vec = {stdr::begin(range), stdr::end(range)};
+  value_type *data = vec.data();
+  std::size_t size = vec.size();
+  bool ok;
+  #pragma omp target map(from: ok) map(tofrom: data[:size]) map(to: size)
+    {
+      std::vector<value_type> orig = {data, data + size};
+      std::span<value_type> span = {data, size};
+      bool inner_ok = true;
+      {
+       auto mul_by_2 = [](const value_type& v){ return v * 2; };
+       VERIFY (simple_equal(orig, span));
+       for (auto& elem : span)
+         elem = mul_by_2(elem);
+       VERIFY (simple_equal(orig | std::views::transform(mul_by_2), span));
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  auto mul_by_2 = [](const value_type& v){ return v * 2; };
+  VERIFY_NON_TARGET (simple_equal(range | std::views::transform(mul_by_2), 
vec));
+  return true;
+}
+
+struct my_int
+{
+  int _v;
+  bool operator==(my_int const&) const = default;
+  my_int operator*(int rhs) const noexcept {
+    return {_v * rhs};
+  }
+};
+
+int main()
+{
+  std::vector<int> ints = {1, 2, 3, 4, 5};
+  const bool ints_res = test(ints);
+  std::vector<my_int> my_ints = {my_int{1}, my_int{2}, my_int{3}, my_int{4}, 
my_int{5}};
+  const bool my_ints_res = test(my_ints);
+  return ints_res && my_ints_res ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-90.C 
b/libgomp/testsuite/libgomp.c++/target-flex-90.C
new file mode 100644
index 00000000000..b3f11973dbc
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-90.C
@@ -0,0 +1,107 @@
+/* structured bindings  */
+
+#include <array>
+#include <tuple>
+
+#include "target-flex-common.h"
+
+template<typename Array, typename Tuple, typename Struct>
+bool test(Array array, Tuple tuple, Struct s)
+{
+  bool ok;
+  auto array_2nd_in = std::get<2>(array);
+  auto tuple_2nd_in = std::get<2>(tuple);
+  auto s_2nd_in = s._2;
+  decltype(array_2nd_in) array_2nd_out_0;
+  decltype(tuple_2nd_in) tuple_2nd_out_0;
+  decltype(s_2nd_in) s_2nd_out_0;
+  decltype(array_2nd_in) array_2nd_out_1;
+  decltype(tuple_2nd_in) tuple_2nd_out_1;
+  decltype(s_2nd_in) s_2nd_out_1;
+  decltype(array_2nd_in) array_2nd_out_2;
+  decltype(tuple_2nd_in) tuple_2nd_out_2;
+  decltype(s_2nd_in) s_2nd_out_2;
+  #pragma omp target map(from: ok, \
+                              array_2nd_out_0, tuple_2nd_out_0, s_2nd_out_0, \
+                              array_2nd_out_1, tuple_2nd_out_1, s_2nd_out_1, \
+                              array_2nd_out_2, tuple_2nd_out_2, s_2nd_out_2) \
+                    map(to: array_2nd_in, tuple_2nd_in, s_2nd_in, array, 
tuple, s)
+    {
+      bool inner_ok = true;
+      {
+       {
+         auto [array_0th, array_1st, array_2nd] = array;
+         VERIFY (array_2nd_in == array_2nd);
+         VERIFY (std::get<2>(array) == array_2nd);
+         array_2nd_out_0 = array_2nd;
+         auto [tuple_0th, tuple_1st, tuple_2nd] = tuple;
+         VERIFY (tuple_2nd_in == tuple_2nd);
+         VERIFY (std::get<2>(tuple) == tuple_2nd);
+         tuple_2nd_out_0 = tuple_2nd;
+         auto [s_0th, s_1st, s_2nd] = s;
+         VERIFY (s_2nd_in == s_2nd);
+         VERIFY (s._2 == s_2nd);
+         s_2nd_out_0 = s_2nd;
+       }
+       {
+         auto& [array_0th, array_1st, array_2nd] = array;
+         VERIFY (array_2nd_in == array_2nd);
+         VERIFY (std::get<2>(array) == array_2nd);
+         array_2nd_out_1 = array_2nd;
+         auto& [tuple_0th, tuple_1st, tuple_2nd] = tuple;
+         VERIFY (tuple_2nd_in == tuple_2nd);
+         VERIFY (std::get<2>(tuple) == tuple_2nd);
+         tuple_2nd_out_1 = tuple_2nd;
+         auto& [s_0th, s_1st, s_2nd] = s;
+         VERIFY (s_2nd_in == s_2nd);
+         VERIFY (s._2 == s_2nd);
+         s_2nd_out_1 = s_2nd;
+       }
+       {
+         const auto& [array_0th, array_1st, array_2nd] = array;
+         VERIFY (array_2nd_in == array_2nd);
+         VERIFY (std::get<2>(array) == array_2nd);
+         array_2nd_out_2 = array_2nd;
+         const auto& [tuple_0th, tuple_1st, tuple_2nd] = tuple;
+         VERIFY (tuple_2nd_in == tuple_2nd);
+         VERIFY (std::get<2>(tuple) == tuple_2nd);
+         tuple_2nd_out_2 = tuple_2nd;
+         const auto& [s_0th, s_1st, s_2nd] = s;
+         VERIFY (s_2nd_in == s_2nd);
+         VERIFY (s._2 == s_2nd);
+         s_2nd_out_2 = s_2nd;
+       }
+      }
+      end:
+      ok = inner_ok;
+    }
+  if (!ok)
+    return false;
+  VERIFY_NON_TARGET (array_2nd_out_0 == array_2nd_in);
+  VERIFY_NON_TARGET (tuple_2nd_out_0 == tuple_2nd_in);
+  VERIFY_NON_TARGET (s_2nd_out_0 == s_2nd_in);
+  VERIFY_NON_TARGET (array_2nd_out_1 == array_2nd_in);
+  VERIFY_NON_TARGET (tuple_2nd_out_1 == tuple_2nd_in);
+  VERIFY_NON_TARGET (s_2nd_out_1 == s_2nd_in);
+  VERIFY_NON_TARGET (array_2nd_out_2 == array_2nd_in);
+  VERIFY_NON_TARGET (tuple_2nd_out_2 == tuple_2nd_in);
+  VERIFY_NON_TARGET (s_2nd_out_2 == s_2nd_in);
+
+  return true;
+}
+
+struct S
+{
+  char _0;
+  float _1;
+  int _2;
+};
+
+int main()
+{
+  const bool test_res
+    = test(std::array{0, 1, 2},
+          std::tuple{'a', 3.14f, 42},
+          S{'a', 3.14f, 42});
+  return test_res ? 0 : 1;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-flex-common.h 
b/libgomp/testsuite/libgomp.c++/target-flex-common.h
new file mode 100644
index 00000000000..14523c435f6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-flex-common.h
@@ -0,0 +1,40 @@
+#include <cstdio>
+
+#if __cplusplus >= 201103L
+  #define BL_NOEXCEPT noexcept
+#else
+  #define BL_NOEXCEPT throw()
+#endif
+
+#if defined __has_builtin
+# if __has_builtin (__builtin_LINE)
+#  define VERIFY_LINE __builtin_LINE ()
+# endif
+#endif
+#if !defined VERIFY_LINE
+# define VERIFY_LINE __LINE__
+#endif
+
+/* I'm not a huge fan of macros but in the interest of keeping the code that
+   isn't being tested as simple as possible, we use them.  */
+
+#define VERIFY(EXPR) \
+  do {                                                                         
\
+    if (!(EXPR))                                                               
\
+      {                                                                        
        \
+       std::printf("VERIFY ln: %d `" #EXPR "` evaluated to false\n",           
\
+                   VERIFY_LINE);                                               
\
+       inner_ok = false;                                                       
\
+       goto end;                                                               
\
+      }                                                                        
        \
+  } while (false)
+
+#define VERIFY_NON_TARGET(EXPR) \
+  do {                                                                         
\
+    if (!(EXPR))                                                               
\
+      {                                                                        
        \
+       std::printf("VERIFY ln: %d `" #EXPR "` evaluated to false\n",           
\
+                   VERIFY_LINE);                                               
\
+       return false;                                                           
\
+      }                                                                        
        \
+  } while (false)
-- 
2.34.1

Reply via email to