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
> 

Reply via email to