Bootstrapped and tested on x86_64-pc-linux-gnu, okay for trunk?

-- 8< --

Empty packs were not being handled correctly, but merely terminating the
loop fails to handle a number of edge cases.  This patch instead skips
each expanded element before moving on to the next parameter to ensure
parameters that correspond to each other are compared.  It is unclear if
merely skipping pack elements is correct but it's probably the best thing
that can be done right now.  We certainly can't test them conditionally on
if the pack was used in a post condtion, as that would reject valid uses
of a pack index in a post condition.

        PR c++/124395
        PR c++/126837

gcc/cp/ChangeLog:

        * contracts.cc (check_postconditions_in_redecl): Skip packs.

gcc/testsuite/ChangeLog:

        * g++.dg/contracts/cpp26/pr124395.C: New test.
        * g++.dg/contracts/cpp26/pr126837.C: New test.

Signed-off-by: Waffl3x <[email protected]>
---
 gcc/cp/contracts.cc                           | 28 +++++++++++++++++++
 .../g++.dg/contracts/cpp26/pr124395.C         |  8 ++++++
 .../g++.dg/contracts/cpp26/pr126837.C         |  9 ++++++
 3 files changed, 45 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/pr124395.C
 create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/pr126837.C

diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc
index 25da963d0b4..7ca35184f30 100644
--- a/gcc/cp/contracts.cc
+++ b/gcc/cp/contracts.cc
@@ -585,6 +585,34 @@ check_postconditions_in_redecl (tree olddecl, tree newdecl)
   for (; t1 && t1 != void_list_node;
        t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
     {
+      if (DECL_PACK_P (t1))
+       {
+         /* Expanded params are checked in check_param_in_postcondition.  */
+         if (TREE_CHAIN (t1) == NULL_TREE
+             || TREE_CHAIN (t1) == void_list_node)
+           break;
+         /* Skip params that come from t1, we can't diagnose them here as they
+            might be indexed with a pack index.  */
+         const char *const pack_name = IDENTIFIER_POINTER (DECL_NAME (t1));
+         const size_t pack_name_len = IDENTIFIER_LENGTH (DECL_NAME (t1));
+         const auto parm_is_expanded_from_pack = [&] (const_tree expanded_id)
+           {
+             gcc_assert (TREE_CODE (expanded_id) == IDENTIFIER_NODE);
+             /* Expanded pack elements have #number added to their name.  */
+             if (!(IDENTIFIER_LENGTH (expanded_id) >= pack_name_len + 2u))
+               return false;
+             const char *const exp_name = IDENTIFIER_POINTER (expanded_id);
+             if (strncmp (pack_name, exp_name, pack_name_len) != 0)
+               return false;
+             return exp_name[pack_name_len] == '#';
+           };
+         /* Stop one node early so continuing works.  */
+         if (parm_is_expanded_from_pack (DECL_NAME (t2)))
+           while (parm_is_expanded_from_pack (DECL_NAME (TREE_CHAIN (t2))))
+             t2 = TREE_CHAIN (t2);
+         /* Keep going to check params after the pack.  */
+         continue;
+       }
       if (parm_used_in_post_p (t1))
        {
          set_parm_used_in_post (t2);
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/pr124395.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/pr124395.C
new file mode 100644
index 00000000000..29926a55685
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/pr124395.C
@@ -0,0 +1,8 @@
+// PR c++/124395
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-fcontracts" }
+
+template <typename... Ts>
+void f(Ts...) pre(true) {}
+
+template void f<>();
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/pr126837.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/pr126837.C
new file mode 100644
index 00000000000..a7a2b4d1253
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/pr126837.C
@@ -0,0 +1,9 @@
+// PR c++/126837
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-fcontracts" }
+
+template<typename... Args>
+void f(Args... args, int const a)
+  post(a) {}
+
+template void f<int, int>(int, int, int);
-- 
2.55.0

Reply via email to