[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