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 <[email protected]> wrote: > > On 12/1/25 9:51 PM, Iain Sandoe wrote: >> From: Nina Ranns <[email protected]> >> 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 <[email protected]> >> --- >> 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 >
