Bootstrapped/regtested on x86_64-pc-linux-gnu.

-- >8 --

`tsubst_function_decl()' associates uninstantiated contracts with a
function template instantiation.  If an explicit instantiation is
encountered before a later redeclaration provides the definition,
those contracts can still refer to PARM_DECLs from the earlier
declaration.

Therefore, we should always regererate the contracts from the current
code_pattern in `regenerate_decl_from_template()' when either DECL
or pattern has contracts.

        PR c++/125712

gcc/cp/ChangeLog:

        * pt.cc (regenerate_decl_from_template): Regenerate contracts
        from the current code pattern.

gcc/testsuite/ChangeLog:

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

Signed-off-by: Wang Jinghao <[email protected]>
---
 gcc/cp/pt.cc                                  | 25 +++++-----
 .../g++.dg/contracts/cpp26/pr125712.C         | 48 +++++++++++++++++++
 2 files changed, 61 insertions(+), 12 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/pr125712.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 4b4b0ac8dea..217ff8c503a 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -28546,18 +28546,19 @@ regenerate_decl_from_template (tree decl, tree tmpl, 
tree args)
              OLD_PARM_DECL_P (t) = 1;
        }
 
-      if (tree attr = get_fn_contract_specifiers (decl))
-       {
-         /* If we're regenerating a specialization, the contracts will have
-            been copied from the most general template. Replace those with
-            the ones from the actual specialization.  */
-         tree tmpl = DECL_TI_TEMPLATE (decl);
-         if (DECL_TEMPLATE_SPECIALIZATION (tmpl))
-           attr = get_fn_contract_specifiers (code_pattern);
-
-         tsubst_contract_specifiers (attr, decl, args,
-                                     tf_warning_or_error, code_pattern);
-       }
+      /* The contracts on DECL may predate a later redeclaration of the
+        template, or have been copied from a more general template.  We should
+        use the contracts from the current pattern.  */
+      tree decl_contracts = get_fn_contract_specifiers (decl);
+      tree pattern_contracts = get_fn_contract_specifiers (code_pattern);
+      /* There are four cases:
+        neither has contracts, so there is nothing to do;
+        only the pattern has contracts, so add them to DECL;
+        only DECL has contracts, so remove them;
+        or both have contracts, so rebuild DECL's from the pattern.  */
+      if (decl_contracts || pattern_contracts)
+       tsubst_contract_specifiers (pattern_contracts, decl, args,
+                                   tf_warning_or_error, code_pattern);
 
       /* Merge additional specifiers from the CODE_PATTERN.  */
       if (DECL_DECLARED_INLINE_P (code_pattern)
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/pr125712.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/pr125712.C
new file mode 100644
index 00000000000..daa2c8f3bd4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/pr125712.C
@@ -0,0 +1,48 @@
+// PR c++/125712
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-fcontracts 
-fcontract-evaluation-semantic=observe" }
+// { dg-skip-if "requires hosted libstdc++ for stdc++exp" { ! hostedlib } }
+
+template<typename>
+  void f (bool p) pre (p);
+template void f<int> (bool);
+template<typename>
+  void f (bool) {}
+
+template<typename T>
+  struct Add
+  {
+    template<typename U> void f (U) {}
+  };
+template<> template<typename U>
+  void Add<int>::f (U u) pre (u) {}
+
+template<typename T>
+  struct Remove
+  {
+    template<typename U> void f (U u) pre (u) {}
+  };
+template<> template<typename U>
+  void Remove<int>::f (U) {}
+
+template<typename T>
+  struct Replace
+  {
+    template<typename U> void f (U u) pre (u) {}
+  };
+template<> template<typename U>
+  void Replace<int>::f (U u) pre (u) {}
+
+int
+main ()
+{
+  f<int> (true);
+  Add<int>{}.f (false);
+  Remove<int>{}.f (false);
+  Replace<int>{}.f (false);
+}
+
+// { dg-output {contract violation in function void Add<T>::f\(U\) \[with U = 
bool; T = int\] at .*:18: u(\n|\r\n|\r)} }
+// { dg-output {\[assertion_kind: pre, semantic: observe, mode: 
predicate_false, terminating: no\](\n|\r\n|\r)} }
+// { dg-output {contract violation in function void Replace<T>::f\(U\) \[with 
U = bool; T = int\] at .*:34: u(\n|\r\n|\r)} }
+// { dg-output {\[assertion_kind: pre, semantic: observe, mode: 
predicate_false, terminating: no\](\n|\r\n|\r)} }
-- 
2.52.0

Reply via email to