[PATCH 5/9 v2] c++, contracts: Apply P200R14 constification.
Iain Sandoe
iain@sandoe.co.uk
Tue Jan 27 00:29:19 GMT 2026
Hi Jason
revised patch is posted as part of the verison 4 series.
cross-referencing this.
> On 18 Dec 2025, at 06:44, Jason Merrill <jason@redhat.com> wrote:
>
> On 12/1/25 9:51 PM, Iain Sandoe wrote:
>> From: Nina Ranns <dinka.ranns@gmail.com>
>> Changes since v1
>> - fixed a merge error in the removal of C++2a code.
>> - rebased onto r16-5785-g3b30d09ac7bbf8 (includes change to default to
>> C++20).
>> --- 8< ---
>> Split from the main patch as it was potentially contentious and might
>> have been altered by WG21 NB comment resolution. However, it most likely
>> makes sense to review in isolation (although we would expect to apply it
>> squashed into the base patch).
>> gcc/cp/ChangeLog:
>> * contracts.cc (view_as_const, constify_contract_access,
>> set_parm_used_in_post, check_param_in_postcondition,
>> parm_used_in_post_p, check_postconditions_in_redecl): New.
>> (check_redecl_contract): Handle constification.
>> * contracts.h (constify_contract_access, view_as_const,
>> contract_const_wrapper_p, strip_contract_const_wrapper): New.
>> * cp-tree.h: Update tree flag usage comment.
>> * lambda.cc (build_capture_proxy): Handle constification.
>> * parser.cc (cp_parser_late_contract_condition, cp_parser_contract_assert,
>> cp_parser_function_contract_specifier): Likewise.
>> * pt.cc (tsubst_function_decl, tsubst_expr): Likewise.
>> * semantics.cc (finish_id_expression_1, finish_decltype_type): Likewise.
>> gcc/ChangeLog:
>> * tree-core.h (struct tree_base): Update tree flag usage comment.
>> gcc/testsuite/ChangeLog:
>> * g++.dg/contracts/cpp26/dcl.contract.func.p7.C: New test.
>> * g++.dg/contracts/cpp26/dcl.contract.res.p1-2.C: New test.
>> * g++.dg/contracts/cpp26/expr.prim.id.unqual.p7-2.C: New test.
>> * g++.dg/contracts/cpp26/expr.prim.id.unqual.p7.C: New test.
>> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
>> ---
>> gcc/cp/contracts.cc | 126 ++++++-
>> gcc/cp/contracts.h | 25 ++
>> gcc/cp/cp-tree.h | 1 +
>> gcc/cp/lambda.cc | 3 +
>> gcc/cp/parser.cc | 24 ++
>> gcc/cp/pt.cc | 7 +
>> gcc/cp/semantics.cc | 14 +
>> .../contracts/cpp26/dcl.contract.func.p7.C | 347 ++++++++++++++++++
>> .../contracts/cpp26/dcl.contract.res.p1-2.C | 69 ++++
>> .../cpp26/expr.prim.id.unqual.p7-2.C | 91 +++++
>> .../contracts/cpp26/expr.prim.id.unqual.p7.C | 114 ++++++
>> gcc/tree-core.h | 3 +
>> 12 files changed, 822 insertions(+), 2 deletions(-)
>> create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7.C
>> create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1-2.C
>> create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7-2.C
>> create mode 100644 gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7.C
>> diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc
>> index a766f281885..5b7920c4a62 100644
>> --- a/gcc/cp/contracts.cc
>> +++ b/gcc/cp/contracts.cc
>> @@ -538,6 +538,126 @@ finish_contract_condition (cp_expr condition)
>> return condition_conversion (condition);
>> }
>> +/* Wrap the DECL into VIEW_CONVERT_EXPR representing const qualified version
>> + of the declaration. */
>> +
>> +tree
>> +view_as_const (tree decl)
>> +{
>> + if (!contract_const_wrapper_p (decl))
>> + {
>> + tree ctype = TREE_TYPE (decl);
>> + ctype = cp_build_qualified_type (ctype, (cp_type_quals (ctype)
>> + | TYPE_QUAL_CONST));
>> + decl = build1 (VIEW_CONVERT_EXPR, ctype, decl);
>> + /* Mark the VCE as contract const wrapper. */
>> + decl->base.private_flag = true;
>
> This needs a macro.
done
>
>> + }
>> + return decl;
>> +}
>> +
>> +/* Constify access to DECL from within the contract condition. */
>> +
>> +tree
>> +constify_contract_access (tree decl)
>> +{
>> + /* We check if we have a variable, a parameter, a variable of reference type,
>> + * or a parameter of reference type
>> + */
>> + if (!TREE_READONLY (decl)
>> + && (VAR_P (decl)
>> + || (TREE_CODE (decl) == PARM_DECL)
>> + || (REFERENCE_REF_P (decl)
>> + && (VAR_P (TREE_OPERAND (decl, 0))
>> + || (TREE_CODE (TREE_OPERAND (decl, 0)) == PARM_DECL)
>> + || (TREE_CODE (TREE_OPERAND (decl, 0))
>> + == TEMPLATE_PARM_INDEX)))))
>> + decl = view_as_const (decl);
>> +
>> + return decl;
>> +}
>> +
>> +/* Indicate if PARM_DECL DECL is ODR used in a postcondition. */
>> +
>> +static void
>> +set_parm_used_in_post (tree decl, bool constify = true)
>> +{
>> + gcc_checking_assert (TREE_CODE (decl) == PARM_DECL);
>> + DECL_LANG_FLAG_4 (decl) = constify;
>> +}
>> +
>> +/* If declaration DECL is a PARM_DECL and it appears in a postcondition, then
>> + check that it is not a non-const by-value param. LOCATION is where the
>> + expression was found and is used for diagnostic purposes. */
>> +
>> +void
>> +check_param_in_postcondition (tree decl, location_t location)
>> +{
>> + if (TREE_CODE (decl) == PARM_DECL
>> + && processing_postcondition
>> + && !cp_unevaluated_operand
>> + && !(REFERENCE_REF_P (decl)
>> + && TREE_CODE (TREE_OPERAND (decl, 0)) == PARM_DECL)
>
> This will always be true, decl can't be both PARM_DECL and REFERENCE_REF_P.
fixed
>
>> + /* Return value parameter has DECL_ARTIFICIAL flag set. The flag
>> + * presence of the flag should be sufficient to distinguish the
>> + * return value parameter in this context. */
>
> We don't add * at the beginning of later lines of comments. Also this and the line below are indented too far.
fixed
>
>> + && !(DECL_ARTIFICIAL (decl)))
>> + {
>> + set_parm_used_in_post (decl);
>> +
>> + if (!dependent_type_p (TREE_TYPE (decl))
>> + && !CP_TYPE_CONST_P (TREE_TYPE (decl))
>> + && !TREE_READONLY (decl))
>
> You shouldn't need to check both the type and the _READONLY flag.
fixed
>
>> + {
>> + error_at (location,
>> + "a value parameter used in a postcondition must be const");
>> + inform (DECL_SOURCE_LOCATION (decl), "parameter declared here");
>> + }
>> + }
>> +}
>> +
>> +/* Test if PARM_DECL is ODR used in a postcondition. */
>> +
>> +static bool
>> +parm_used_in_post_p (const_tree decl)
>> +{
>> + /* Check if this parameter is odr used within a function's postcondition */
>> + return ((TREE_CODE (decl) == PARM_DECL) && DECL_LANG_FLAG_4 (decl));
>> +}
>
> Let's move this up to be with set_parm_used_in_post_p.
moved
>
>> +/* Check if parameters used in postconditions are const qualified on
>> + a redeclaration that does not specify contracts or on an instantiation
>> + of a function template. */
>> +
>> +void
>> +check_postconditions_in_redecl (tree olddecl, tree newdecl)
>> +{
>> + tree attr = get_fn_contract_specifiers (olddecl);
>> + if (!attr)
>> + return;
>> +
>> + tree t1 = FUNCTION_FIRST_USER_PARM (olddecl);
>> + tree t2 = FUNCTION_FIRST_USER_PARM (newdecl);
>> +
>> + for (; t1 && t1 != void_list_node;
>> + t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2))
>> + {
>> + if (parm_used_in_post_p (t1))
>> + {
>> + set_parm_used_in_post (t2);
>> + if (!dependent_type_p (TREE_TYPE (t2))
>> + && !CP_TYPE_CONST_P (TREE_TYPE (t2))
>> + && !TREE_READONLY (t2))
>> + {
>> + error_at (DECL_SOURCE_LOCATION (t2),
>> + "value parameter %qE used in a postcondition must be const", t2);
>> + inform (DECL_SOURCE_LOCATION (olddecl),
>> + "previous declaration here");
>> + }
>> + }
>> + }
>> +}
>> +
>> void
>> maybe_update_postconditions (tree fndecl)
>> {
>> @@ -1020,7 +1140,10 @@ check_redecl_contract (tree newdecl, tree olddecl)
>> }
>> if (old_contracts && !new_contracts)
>> - return;
>> + /* We allow re-declarations to omit contracts declared on the initial decl.
>> + In fact, this is required if the conditions contain lambdas. Check if
>> + all the parameters are correctly const qualified. */
>> + check_postconditions_in_redecl (olddecl, newdecl);
>> else if (old_contracts && new_contracts
>> && !contract_any_deferred_p (
>> old_contracts) && contract_any_deferred_p (new_contracts)
>> @@ -1047,7 +1170,6 @@ check_redecl_contract (tree newdecl, tree olddecl)
>> match_contract_attributes (rdp->note_loc, rdp->original_contracts,
>> cont_end, new_contracts);
>> }
>> -
>> return;
>> }
>
> Let's keep this blank line.
fixed
>
>> diff --git a/gcc/cp/contracts.h b/gcc/cp/contracts.h
>> + op = constify_contract_access(op);
>
> Space before (.
>
>> + r = constify_contract_access(r);
>
> Space before (.
>
>> + if (flag_contracts && processing_contract_condition)
>> + decl = constify_contract_access(decl);
>
> Space before (.
formating issues fixed.
>
>> +#include <type_traits>
>> +struct NTClass {
>> + //TODO, make non trivial when https://github.com/NinaRanns/gcc/issues/21 is solved
>
> If this is still broken, please file a GCC bugzilla and refer to that. And instead of having only one class type, have both trivial and non-trivial classes and test both, with xfails as needed referring to the BZ.
We have a tracking issue at present and will file a BZ as soon as the code is in trunk.
>
>> + post ( check (l))
>> + post ( check (m));
>
> I don't see any test that these are diagnosed on instantiation?
>> + post ( check (m));
>
> Likewise.
>> +};
>
> Likewise, etc.
>
>>
>> + //, int* p, int& r, X x, X* px)
>
> What's up with this line?
>
>> +
>> + void f(int i) pre (++g); // { dg-error "increment of read-only location" }
>> + void f2(int i) pre (++i); // { dg-error "increment of read-only location" }
>> + void f3(int* p) pre (++(*p)); // OK
>> + void f4(int& r) pre (++r); // { dg-error "increment of read-only location" }
>> + void f5(X x) pre (x.m()); // { dg-error " argument discards qualifiers" }
>> + void f6(X* px) pre (px->m()) // OK
>> +
>> + // TODO when lambdas are fixed
>
> Need a bugzilla for this as well. And uncomment the test and add xfails as appropriate.
We have local tracking issues
TODO add a BZ once the code lands
>
>>
>> +// template tests
>
> Please add tests.
>>
>> +
>
> More missing tests.
We have added tests
>> UMERAL_TYPE
>> + contract_const_wrapper_p in
>> + VIEW_CONVERT_EXPR
>
> This needs to mention C++.
done
>
>> +
>> protected_flag:
>> TREE_PROTECTED in
>
More information about the Gcc-patches
mailing list