From: Nina Ranns <[email protected]>

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 2d4466abff9..43ba7275b45 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;
+    }
+  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)
+          /* 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. */
+          && !(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))
+       {
+         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));
+}
+
+/* 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;
 }
 
diff --git a/gcc/cp/contracts.h b/gcc/cp/contracts.h
index 7af8709cc76..c77adaf6e1e 100644
--- a/gcc/cp/contracts.h
+++ b/gcc/cp/contracts.h
@@ -134,6 +134,8 @@ extern void update_late_contract            (tree, tree, 
cp_expr);
 extern void check_redecl_contract              (tree, tree);
 extern tree invalidate_contract                        (tree);
 extern tree copy_and_remap_contracts           (tree, tree);
+extern tree constify_contract_access           (tree);
+extern tree view_as_const                      (tree);
 
 extern void set_fn_contract_specifiers         (tree, tree);
 extern void update_fn_contract_specifiers      (tree, tree);
@@ -166,6 +168,29 @@ set_decl_contracts (tree decl, tree contract_attrs)
   set_fn_contract_specifiers (decl, contract_attrs);
 }
 
+/* Test if EXP is a contract const wrapper node.  */
+
+inline bool
+contract_const_wrapper_p (const_tree exp)
+{
+  /* A wrapper node has code VIEW_CONVERT_EXPR, and the flag base.private_flag
+     is set. The wrapper node is used to Used to constify entities inside
+     contract assertions.  */
+  return ((TREE_CODE (exp) == VIEW_CONVERT_EXPR) && exp->base.private_flag);
+}
+
+/* If EXP is a contract_const_wrapper_p, return the wrapped expression.
+   Otherwise, do nothing. */
+
+inline tree
+strip_contract_const_wrapper (tree exp)
+{
+  if (contract_const_wrapper_p (exp))
+    return TREE_OPERAND (exp, 0);
+  else
+    return exp;
+}
+
 /* TODO : decide if we should push the tests into contracts.cc  */
 extern contract_evaluation_semantic get_evaluation_semantic (const_tree);
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e2505e097d0..7ef91f29f27 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -569,6 +569,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       DECL_SELF_REFERENCE_P (in a TYPE_DECL)
       DECL_INVALID_OVERRIDER_P (in a FUNCTION_DECL)
       DECL_UNINSTANIATED_TEMPLATE_FRIEND_P (in TEMPLATE_DECL)
+      parm_used_in_post_p (in PARM_DECL)
    5: DECL_INTERFACE_KNOWN.
    6: DECL_THIS_STATIC (in VAR_DECL, FUNCTION_DECL or PARM_DECL)
       DECL_FIELD_IS_BASE (in FIELD_DECL)
diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index c2655a9949a..1002251690e 100644
--- a/gcc/cp/lambda.cc
+++ b/gcc/cp/lambda.cc
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "target.h"
 #include "decl.h"
 #include "flags.h"
+#include "contracts.h"
 
 /* Constructor for a lambda expression.  */
 
@@ -497,6 +498,8 @@ build_capture_proxy (tree member, tree init, bool early_p)
            init = PACK_EXPANSION_PATTERN (init);
        }
 
+      init = strip_contract_const_wrapper (init);
+
       if (INDIRECT_REF_P (init))
        init = TREE_OPERAND (init, 0);
       STRIP_NOPS (init);
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index a7cf9e5af97..0b44ca2a7e5 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -32047,6 +32047,12 @@ void cp_parser_late_contract_condition (cp_parser 
*parser,
   cp_token_cache *tokens = DEFPARSE_TOKENS (condition);
   cp_parser_push_lexer_for_tokens (parser, tokens);
 
+  /* If we have a current class object, we need to consider
+     it const when processing the contract condition.  */
+  tree current_class_ref_copy = current_class_ref;
+  if (flag_contracts && current_class_ref_copy)
+    current_class_ref = view_as_const (current_class_ref_copy);
+
   /* Parse the condition, ensuring that parameters or the return variable
      aren't flagged for use outside the body of a function.  */
   begin_scope (sk_contract, fn);
@@ -32149,6 +32155,12 @@ cp_parser_contract_assert (cp_parser *parser, cp_token 
*token)
   /* Enable location wrappers when parsing contracts.  */
   auto suppression = make_temp_override (suppress_location_wrappers, 0);
 
+  /* If we have a current class object, see if we need to consider
+     it const when processing the contract condition.  */
+  tree current_class_ref_copy = current_class_ref;
+  if (current_class_ref_copy)
+    current_class_ref = view_as_const (current_class_ref_copy);
+
   /* Parse the condition.  */
   begin_scope (sk_contract, current_function_decl);
   bool old_pc = processing_postcondition;
@@ -32162,6 +32174,9 @@ cp_parser_contract_assert (cp_parser *parser, cp_token 
*token)
   processing_postcondition = old_pc;
   pop_bindings_and_leave_scope ();
 
+  /* Revert (any) constification of the current class object.  */
+  current_class_ref = current_class_ref_copy;
+
   parens.require_close (parser);
 
   if (!contract || contract == error_mark_node)
@@ -32279,6 +32294,12 @@ cp_parser_function_contract_specifier (cp_parser 
*parser)
       /* Enable location wrappers when parsing contracts.  */
       auto suppression = make_temp_override (suppress_location_wrappers, 0);
 
+      /* If we have a current class object, see if we need to consider
+       it const when processing the contract condition.  */
+      tree current_class_ref_copy = current_class_ref;
+      if (current_class_ref_copy)
+      current_class_ref = view_as_const (current_class_ref_copy);
+
       /* Parse the condition, ensuring that parameters or the return variable
        aren't flagged for use outside the body of a function.  */
       begin_scope (sk_contract, current_function_decl);
@@ -32302,6 +32323,9 @@ cp_parser_function_contract_specifier (cp_parser 
*parser)
                           && scope_chain->bindings->kind == sk_contract);
       pop_bindings_and_leave_scope ();
 
+      /* Revert (any) constification of the current class object.  */
+      current_class_ref = current_class_ref_copy;
+
       if (contract != error_mark_node)
        {
          location_t end = cp_lexer_peek_token (parser->lexer)->location;
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 192a50e7bda..1f56df00231 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -15174,6 +15174,8 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t 
complain,
   if (tree ctrct = get_fn_contract_specifiers (t))
     set_fn_contract_specifiers (r, ctrct);
 
+  /* The parms have now been substituted, check for incorrect const cases.  */
+  check_postconditions_in_redecl (t,r);
 
   if (DECL_FRIEND_CONTEXT (t))
     SET_DECL_FRIEND_CONTEXT (r,
@@ -22789,6 +22791,11 @@ tsubst_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
          /* force_paren_expr can also create a VIEW_CONVERT_EXPR.  */
          RETURN (finish_parenthesized_expr (op));
 
+       check_param_in_postcondition (op, EXPR_LOCATION (t));
+
+       if (flag_contracts && processing_contract_condition)
+           op = constify_contract_access(op);
+
        /* Otherwise, we're dealing with a wrapper to make a C++20 template
           parameter object const.  */
        if (TREE_TYPE (op) == NULL_TREE
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index b1ad8bfca51..a011d8fa1f4 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -4873,6 +4873,10 @@ finish_id_expression_1 (tree id_expression,
                   "integral or enumeration type", decl, TREE_TYPE (decl));
          *non_integral_constant_expression_p = true;
        }
+
+      if (flag_contracts && processing_contract_condition)
+       r = constify_contract_access(r);
+
       return r;
     }
   else if (TREE_CODE (decl) == UNBOUND_CLASS_TEMPLATE)
@@ -5073,6 +5077,10 @@ finish_id_expression_1 (tree id_expression,
        }
     }
 
+  check_param_in_postcondition (decl, location);
+  if (flag_contracts && processing_contract_condition)
+    decl = constify_contract_access(decl);
+
   return cp_expr (decl, location);
 }
 
@@ -12865,6 +12873,12 @@ finish_decltype_type (tree expr, bool 
id_expression_or_member_access_p,
       if (identifier_p (expr))
         expr = lookup_name (expr);
 
+      /* If e is a constified expression inside a contract assertion,
+        strip the const wrapper. Per P2900R14, "For a function f with the
+        return type T , the result name is an lvalue of type const T , 
decltype(r)
+        is T , and decltype((r)) is const T&."  */
+      expr = strip_contract_const_wrapper (expr);
+
       if (INDIRECT_REF_P (expr)
          || TREE_CODE (expr) == VIEW_CONVERT_EXPR)
         /* This can happen when the expression is, e.g., "a.b". Just
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7.C
new file mode 100644
index 00000000000..60d40f69717
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7.C
@@ -0,0 +1,347 @@
+// N5008:
+// dcl.contract.func/p7
+// If the predicate of a postcondition assertion of a function f odr-uses 
(6.3) a non-reference parameter of f,
+// that parameter and the corresponding parameter on all declarations of f 
shall have const type
+// { dg-do compile { target c++23 } }
+// { dg-additional-options "-fcontracts" }
+#include <type_traits>
+struct NTClass {
+  //TODO, make non trivial when https://github.com/NinaRanns/gcc/issues/21 is 
solved
+//  NTClass(){};
+//  ~NTClass(){};
+};
+
+template <typename... ARGS>
+bool check(ARGS... args){ return true;}
+
+namespace checkDeclaration{
+
+int f (int i, int &j, int *k, const int * l, int * const m)
+  post ( check (i)) // { dg-error "used in a postcondition must be const" }
+  post ( check (j))
+  post ( check (k)) // { dg-error "used in a postcondition must be const" }
+  post ( check (l)) // { dg-error "used in a postcondition must be const" }
+  post ( check (m));
+
+int f (NTClass i, NTClass &j, NTClass *k, const NTClass * l, NTClass * const m)
+  post ( check (i)) // { dg-error "used in a postcondition must be const" }
+  post ( check (j))
+  post ( check (k)) // { dg-error "used in a postcondition must be const" }
+  post ( check (l)) // { dg-error "used in a postcondition must be const" }
+  post ( check (m));
+
+template <class T>
+int f (T i, T &j, T *k, const T * l, T * const m)
+  post ( check (i))
+  post ( check (j))
+  post ( check (k))
+  post ( check (l))
+  post ( check (m));
+
+struct PostCond {
+
+  PostCond (NTClass i, NTClass &j, NTClass *k, const NTClass * l, NTClass * 
const m)
+    post ( check (i)) // { dg-error "used in a postcondition must be const" }
+    post ( check (j))
+    post ( check (k)) // { dg-error "used in a postcondition must be const" }
+    post ( check (l)) // { dg-error "used in a postcondition must be const" }
+    post ( check (m));
+
+  template <class T>
+  PostCond  (T i, T &j, T *k, const T * l, T * const m)
+    post ( check (i))
+    post ( check (j))
+    post ( check (k))
+    post ( check (l))
+    post ( check (m));
+
+  int f (NTClass i, NTClass &j, NTClass *k, const NTClass * l, NTClass * const 
m)
+    post ( check (i)) // { dg-error "used in a postcondition must be const" }
+    post ( check (j))
+    post ( check (k)) // { dg-error "used in a postcondition must be const" }
+    post ( check (l)) // { dg-error "used in a postcondition must be const" }
+    post ( check (m));
+
+  template <class T>
+  int f (T i, T &j, T *k, const T * l, T * const m)
+    post ( check (i))
+    post ( check (j))
+    post ( check (k))
+    post ( check (l))
+    post ( check (m));
+};
+
+template <typename T>
+struct PostCondT
+{
+
+  PostCondT (NTClass i, NTClass &j, NTClass *k, const NTClass * l, NTClass * 
const m)
+    post ( check (i)) // { dg-error "used in a postcondition must be const" }
+    post ( check (j))
+    post ( check (k)) // { dg-error "used in a postcondition must be const" }
+    post ( check (l)) // { dg-error "used in a postcondition must be const" }
+    post ( check (m));
+
+  template <class U>
+  PostCondT (U i, U &j, U *k, const U * l, U * const m)
+    post ( check (i))
+    post ( check (j))
+    post ( check (k))
+    post ( check (l))
+    post ( check (m));
+
+  int f (NTClass i, NTClass &j, NTClass *k, const NTClass * l, NTClass * const 
m)
+    post ( check (i)) // { dg-error "used in a postcondition must be const" }
+    post ( check (j))
+    post ( check (k)) // { dg-error "used in a postcondition must be const" }
+    post ( check (l)) // { dg-error "used in a postcondition must be const" }
+    post ( check (m));
+
+  int f (T i, T &j, T *k, const T * l, T * const m)
+    post ( check (i))
+    post ( check (j))
+    post ( check (k))
+    post ( check (l))
+    post ( check (m));
+
+  template <class U>
+  int f (U i, U &j, U *k, const U * l, U * const m)
+    post ( check (i))
+    post ( check (j))
+    post ( check (k))
+    post ( check (l))
+    post ( check (m));
+};
+
+
+// P29000R14  [dcl.contract.func]/p7
+int f(const int i[10])
+post(r : r == i[0]); // { dg-error "used in a postcondition must be const" }
+
+template <typename T>
+int f(const T i[10])
+post(r : r == i[0]);
+
+template <typename T>
+int f2(const T i[10]) // { dg-error "used in a postcondition must be const" }
+post(r : r == i[0]);
+
+void foo()
+{
+  int i[10];
+  f2<const int>(i);
+}
+
+}
+
+// P3520
+using const_int_t = const int;
+void f3(const_int_t i) post (i > 0);  //ok
+
+template <typename T>
+void f4(std::add_const_t<T> t) post(t > 0); //ok
+
+template <typename T>
+void f5(T t) post (t > 0);
+template <typename T>
+void f6(T t) post (t > 0);
+template <typename T>
+void f7(T t) post (t > 0);
+
+
+void bar(){
+  f3(1);
+  f4<int>(1);
+  f5(1); // { dg-error "used in a postcondition must be const" "" { target 
*-*-* } 145 }
+  f6<int>(1); // { dg-error "used in a postcondition must be const" "" { 
target *-*-* } 147  }
+  f7<const int>(1); // OK
+}
+
+namespace nonFirstDeclaration
+{
+
+  int f (const NTClass i)
+    post ( check (i));
+
+  template <class T>
+  int f (const T i)
+    post ( check (i));
+
+  struct PostCond {
+    void f (const NTClass i)
+      post ( check (i));
+
+    template <class T>
+    void f (const T i)
+      post ( check (i));
+  };
+
+  template <typename T>
+  struct PostCondT
+  {
+    void f (const NTClass i)
+      post ( check (i));
+
+    void f (const T i)
+      post ( check (i));
+
+    template <class U>
+    void f (const U i)
+      post ( check (i));
+  };
+
+  int
+  f (NTClass i); // { dg-error "used in a postcondition must be const" }
+
+  template<class T>
+  int
+  f (T i);
+
+  void
+  PostCond::f (NTClass i){} // { dg-error "used in a postcondition must be 
const" }
+
+  template<class T>
+  void
+  PostCond::f (T i){}
+
+  template<typename T>
+  void PostCondT<T>::f (NTClass i){} // { dg-error "used in a postcondition 
must be const" }
+
+  template<typename T>
+  void PostCondT<T>::f (T i){}
+
+  template<typename T>
+  template<typename U>
+  void PostCondT<T>::f (U i){}
+
+}
+
+namespace nonFirstDeclarationNegative
+{
+
+  int f (const NTClass i)
+    post ( check (true))
+    pre ( check (i));
+
+  template <class T>
+  int f (const T i)
+    post ( check (true))
+    pre ( check (i));
+
+  struct PostCond {
+    void f (const NTClass i)
+      post ( check (true))
+      pre ( check (i));
+
+    template <class T>
+    void f (const T i)
+      post ( check (true))
+      pre ( check (i));
+  };
+
+  template <typename T>
+  struct PostCondT
+  {
+    void f (const NTClass i)
+      post ( check (true))
+      pre ( check (i));
+
+    void f (const T i)
+      post ( check (true))
+      pre ( check (i));
+
+    template <class U>
+    void f (const U i)
+      post ( check (true))
+      pre ( check (i));
+  };
+
+  int
+  f (NTClass i);
+
+  template<class T>
+  int
+  f (T i);
+
+  void
+  PostCond::f (NTClass i){}
+
+  template<class T>
+  void
+  PostCond::f (T i){}
+
+  template<typename T>
+  void PostCondT<T>::f (NTClass i){}
+
+  template<typename T>
+  void PostCondT<T>::f (T i){}
+
+  template<typename T>
+  template<typename U>
+  void PostCondT<T>::f (U i){}
+
+}
+
+namespace checkNonOdr{
+
+int f (int i, int *j)
+  post ( sizeof(i) )
+  post ( noexcept(check (&j)));
+
+
+template <class T>
+int f (T i, T *j)
+  post ( sizeof(i) )
+  post ( noexcept(check (&j)));
+
+struct PostCond {
+
+  PostCond (int i, int *j)
+  post ( sizeof(i) )
+  post ( noexcept(check (&j)));
+
+
+  template <class T>
+  PostCond  (T i, T *j)
+  post ( sizeof(i) )
+  post ( noexcept(check (&j)));
+
+  int f (int i, int *j)
+  post ( sizeof(i) )
+  post ( noexcept(check (&j)));
+
+  template <class T>
+  int f (T i, T *j)
+  post ( sizeof(i) )
+  post ( noexcept(check (&j)));
+};
+
+template <typename T>
+struct PostCondT
+{
+
+  PostCondT (int i, int *j)
+   post ( sizeof(i) )
+   post ( noexcept(check (&j)));
+
+  template <class U>
+  PostCondT (U i, U *j)
+  post ( sizeof(i) )
+  post ( noexcept(check (&j)));
+
+  int f (int i, int *j)
+  post ( sizeof(i) )
+  post ( noexcept(check (&j)));
+
+  int f (T i, T *j)
+  post ( sizeof(i) )
+  post ( noexcept(check (&j)));
+
+  template <class U>
+  int f (U i, U *j)
+  post ( sizeof(i) )
+  post ( noexcept(check (&j)));
+};
+
+
+}
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1-2.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1-2.C
new file mode 100644
index 00000000000..74bf7d018af
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1-2.C
@@ -0,0 +1,69 @@
+// N5008 :
+// dcl.contract.res/p1
+// The result-name-introducer of a postcondition-specifier is a declaration. 
The result-name-introducer introduces
+// the identifier as the name of a result binding of the associated function. 
If a postcondition assertion has a
+// result-name-introducer and the return type of the function is cv void, the 
program is ill-formed. A result
+// binding denotes the object or reference returned by invocation of that 
function. The type of a result binding
+// is the return type of its associated function. The optional 
attribute-specifier-seq of the attributed-identifier in
+// the result-name-introducer appertains to the result binding so introduced.
+// [Note 1 : An id-expression that names a result binding is a const lvalue 
(7.5.5.2). ??? end note]
+//
+// check that the return value has correct const qualification
+// { dg-do compile { target c++23 } }
+// { dg-additional-options "-fcontracts" }
+
+
+#include <type_traits>
+
+bool is_constified(int &){ return false;}
+
+bool is_constified(const int &){ return true;}
+
+
+int f1(int i)
+     pre (is_constified(i))
+     pre (std::is_same<decltype(i), int>::value)
+     pre (std::is_same<decltype((i)), const int&>::value)
+
+     post (r: is_constified(r))
+     post (r: std::is_same<decltype(r), int>::value)
+     post (r: std::is_same<decltype((r)), const int&>::value)
+     { return i; };
+
+int& f2(int& i)
+    pre (is_constified(i))
+    pre (std::is_same<decltype(i), int&>::value)
+    pre (std::is_same<decltype((i)), const int&>::value)
+
+    post (r: is_constified(r))
+    post (r: std::is_same<decltype(r), int&>::value)
+    post (r: std::is_same<decltype((r)), const int&>::value)
+    {
+      static_assert(std::is_same<decltype(i), int&>::value);
+      return i;
+    }
+
+int* f2(int* i)
+    pre (std::is_same<decltype(i), int*>::value)
+    pre (std::is_same<decltype((i)),int * const>::value)
+
+    post (r: std::is_same<decltype(r), int *>::value)
+    post (r: std::is_same<decltype((r)),int * const>::value)
+    { return i;}
+
+int const * f2(int const * i)
+    pre (std::is_same<decltype(i), int const *>::value)
+    pre (std::is_same<decltype((i)),int const * const>::value)
+
+    post (r: std::is_same<decltype(r), int const *>::value)
+    post (r: std::is_same<decltype((r)),int const * const>::value)
+    { return i;}
+
+int main()
+{
+
+  int i = 4;
+  f1(i);
+  f2(i);
+
+}
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7-2.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7-2.C
new file mode 100644
index 00000000000..c320853c2b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7-2.C
@@ -0,0 +1,91 @@
+// N5008 [expr.prim.id.unqual]/p7
+// Otherwise, if the unqualified-id appears in the predicate of a contract 
assertion C (6.10) and the entity is
+// (7.1) ??? a variable declared outside of C of object type T,
+// (7.2) ??? a variable or template parameter declared outside of C of type 
???reference to T???, or
+// (7.3) ??? a structured binding of type T whose corresponding variable is 
declared outside of C,
+// then the type of the expression is const T
+// { dg-do compile { target c++23 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=enforce 
-O" }
+
+struct S{
+  S(){};
+  S(const S&){}
+  ~S(){};
+  int x = 0;
+};
+
+int i = 3;
+int *pi = &i;
+int *pi2 = &i;
+
+
+void f1() pre(i++) {} // { dg-error "increment of read-only location" }
+void f2() pre((*pi)++) {};
+void f3() pre(pi2++) {} // { dg-error "increment of read-only location" }
+
+int a[2] = {1, 2};
+auto [x, y] = a;
+auto& [xr, yr] = a;
+
+void f4() pre(x++) {} // { dg-error "increment of read-only location" }
+void f5() pre(xr++) {}; // { dg-error "increment of read-only location" }
+
+void f7(int x) pre([](int& i)
+             {
+               return true;
+             }(x)){} // { dg-error "discards qualifiers" }
+// { dg-error {no match for call to} "" { target *-*-* } .-1 }
+
+
+int g = 0;
+struct X { bool m(); };
+struct Y {
+  int z = 0;
+  //, int* p, int& r, X x, X* px)
+
+  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
+ /*void f7 pre ([=,&i,*this] mutable
+        {
+           ++g;        // error: attempting to modify const lvalue
+           ++i;        // error: attempting to modify const lvalue
+           ++p;        // OK, refers to member of closure type
+           ++r;        // OK, refers to non???reference member of closure type
+           ++this->z; // OK, captured *this
+           ++z;        // OK, captured *this
+           int j = 17;
+           [&]{
+             int k = 34;
+             ++i; // error: attempting to modify const lvalue
+             ++j; // OK
+             ++k; // OK
+           }();
+           return true;
+        }())*/;
+
+  template <int N, int& R, int* P>
+  void tf1() pre(++N);         // { dg-error "increment of read-only location" 
}
+  // { dg-error {lvalue required as increment operand} "" { target *-*-* } .-1 
}
+
+  template <int N, int& R, int* P>
+  void tf2() pre(++R); // { dg-error "increment of read-only location" }
+
+  template <int N, int& R, int* P>
+  void tf3() pre(++(*P)); // OK
+
+  int h1() post(r : ++r); // { dg-error "increment of read-only" }
+
+  // TODO when lambdas are fixed
+/*  int h2() post(r : [=]() mutable {
+                       ++r;    // OK, refers to member of closure type
+                       return true;
+           }());
+*/
+  int& k() post(r : ++r); // { dg-error "increment of read-only" }
+};
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7.C 
b/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7.C
new file mode 100644
index 00000000000..68a3e0cf667
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7.C
@@ -0,0 +1,114 @@
+// N5008 [expr.prim.id.unqual]/p7
+// Otherwise, if the unqualified-id appears in the predicate of a contract 
assertion C (6.10) and the entity is
+// (7.1) ??? a variable declared outside of C of object type T,
+// (7.2) ??? a variable or template parameter declared outside of C of type 
???reference to T???, or
+// (7.3) ??? a structured binding of type T whose corresponding variable is 
declared outside of C,
+// then the type of the expression is const T
+// { dg-do compile { target c++23 } }
+// { dg-additional-options "-fcontracts" }
+
+
+static_assert (__cpp_contracts >= 202502L);
+
+int gi = 7;
+int &gri = gi;
+
+bool f(int&&){ return true;}
+int temporary(){ return 4;}
+
+void f1(int i) pre(i++); // { dg-error "increment of read-only location" }
+void f2(int &i) pre(i++); // { dg-error "increment of read-only location" }
+void f3(int *i) pre(i++); // { dg-error "increment of read-only location" }
+void f4(int *i) pre((*i)++); // ok, not deep const
+void f5(int *i) pre(gi++); // { dg-error "increment of read-only location" }
+void f6() pre(f(temporary())); // ok, lifetime started within pre condition
+
+// todo structured binding test
+// lambda tests
+// template tests
+
+struct S{
+
+  int i;
+  mutable int mi;
+  int *pi = &i;
+  int &ri = i;
+  int &rmi = mi;
+  int *pmi = &mi;
+
+  void f(){
+    contract_assert(i++); // { dg-error "increment of member" }
+    contract_assert(mi++); // ok, mutable
+
+    contract_assert(ri++); // ok, not deep const
+    contract_assert(rmi++); // ok, not deep const
+
+    contract_assert(pi++); // { dg-error "increment of member" }
+    contract_assert((*pi)++); // ok, not deep const
+
+
+    contract_assert(pmi++); // { dg-error "increment of member" }
+    contract_assert((*pmi)++); // ok, not deep const
+  }
+
+};
+
+template <class T> void perfect_forward(T&& t) pre(++t) {} // { dg-error 
"increment of read-only" }
+
+struct S2
+{
+  int i = 0;
+  template <class Self> void perfect_forward(this Self&& self) pre(++self.i) 
{} // { dg-error "increment of member.*in read-only object" }
+};
+
+void template_related_tests()
+{
+  int i = 0;
+  const int ci = 42;
+  perfect_forward(i);
+  perfect_forward(666);
+  perfect_forward(ci);
+  perfect_forward((const int&&) ci);
+  S2 s;
+  const S2 cs;
+  s.perfect_forward();
+  cs.perfect_forward();
+  S2().perfect_forward();
+  ((const S2&&)S2()).perfect_forward();
+}
+
+void class_related_tests()
+{
+
+
+
+
+}
+
+int main()
+{
+  int i;
+  contract_assert(i++); // { dg-error "increment of read-only location" }
+  i = 5;
+
+  int& ri = i;
+  contract_assert(ri++); // { dg-error "increment of read-only location" }
+  ri = 6;
+
+  contract_assert(gi++); // { dg-error "increment of read-only location" }
+  contract_assert(gri++); // { dg-error "increment of read-only location" }
+
+  int& rgi = gi;
+  contract_assert(rgi++); // { dg-error "increment of read-only location" }
+  rgi = 6;
+
+
+  int *pi= &i;
+  contract_assert(pi++); // { dg-error "increment of read-only location" }
+  contract_assert((*pi)++); // ok, not deep const
+
+  contract_assert(i == 4 ? i : i ); // ok, no name clash
+
+
+  return 0;
+}
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 33a6a78f00d..6f6f7883195 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1375,6 +1375,9 @@ struct GTY(()) tree_base {
        ENUM_IS_OPAQUE in
           ENUMERAL_TYPE
 
+      contract_const_wrapper_p in
+          VIEW_CONVERT_EXPR
+
    protected_flag:
 
        TREE_PROTECTED in
-- 
2.39.5 (Apple Git-154)

Reply via email to