[PATCH] c++, contracts: Always regenerate contracts from the current template pattern [PR125712]

Wang Jinghao <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.