]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++, contracts: C++26 base implementation as per P2900R14.
authorIain Sandoe <iain@sandoe.co.uk>
Mon, 30 Oct 2023 11:12:56 +0000 (13:12 +0200)
committerIain Sandoe <iain@sandoe.co.uk>
Wed, 28 Jan 2026 01:23:34 +0000 (01:23 +0000)
This implementation performs the contracts application in three
phases:
    1. Parsing and semantic checks.
       Most of the code for this is in the parser, with helpers provided here.
    2. Emitting contract assertion AST nodes into function bodies.
       This is initiated from "finish_function ()"
    3. Lowering the contract assertion AST nodes to control flow, constant
       data and calls to the violation handler.
       This is initiated from "cp_genericize ()".

  Contract Assertion State
  ========================

  contract_assert () does not require any special handling and can be
  represented directly by AST inserted in the function body.

  'pre' and 'post' function contract specifiers require most of the special
  handling, since they must be tracked across re-declarations of functions and
  there are contraints on how such specifiers may change in these cases.

  The contracts specification identifies a "first declaration" of any given
  function - which is the first encountered when parsing a given TU.
  Subsequent re-declarations may not add or change the function contract
  specifiers from any introduced on this first declaration.  It is, however,
  permitted to omit specifiers on re-declarations.

  Since the implementation of GCC's (re-)declarations is a destructive merge
  we need to keep some state on the side to determine whether the re-declaration
  rules are met.  In this current design we have chosen not to add another tree
  to each function decl but, instead, keep a map from function decl to contract
  specifier state.  In this state we record the 'first declaration' specifiers
  which are used to validate re-declaration(s) and to report the initial state
  in diagnostics.

  We need (for example) to compare
    pre ( x > 2 ) equal to
    pre ( z > 2 ) when x and z refer to the same function parameter in a
    re-declaration.

  The mechanism used to determine if two contracts are the same is to compare
  the folded trees.  This makes use of current compiler machinery, rather than
  constructing some new AST comparison scheme.  However, it does introduce an
  additional complexity in that we need to defer such comparison until parsing
  is complete - and function contract specifiers in class declarations must be
  deferred parses, since it is also permitted for specifiers to refer to class
  members.

  When we encounter a definition, the parameter names in a function decl are
  re-written to match those of the definition (thus the expected names will
  appear in debug information etc).  At this point, we also need to re-map
  any function parameter names that appear in function contract specifiers
  to agree with those of the definition - although we intend to keep the
  'first declaration' record consistent for diagnostics.

  Since we shared some code from the C++2a contracts implementation, pre and
  post specifiers are represented by chains of attributes, where the payload
  of the attribute is an AST node.  However during the parse, these are not
  inserted into the function bodies, but kept in the decl-keyed state described
  above.  A future improvement planned here is to store the specifiers using a
  tree vec instead of the attribute list.

  Emitting contract AST
  =====================

  When we reach `finish_function ()` and therefore are committed to potentially
  emitting code for an instance, we build a new variant of the function body
  with the pre-condition AST inserted before the user's function body, and the
  post condition AST (if any) linked into the function return.

  Lowering the contract assertion AST
  ===================================

  In all cases (pre, post, contract_assert) the AST node is lowered to control
  flow and (potentially) calls to the violation handler and/or termination.
  This is done during `cp_genericize ()`.  In the current implementation, the
  decision on the control flow is made on the basis of the setting of a command-
  line flag that determines a TU-wide contract evaluation semantic, which has
  the following initial set of behaviours:

    'ignore'     : contract assertion AST is lowered to 'nothing',
      i.e. omitted.
    'enforce'     : contract assertion AST is lowered to a check, if this
      fails a violation handler is called, followed by
      std::terminate().
    'quick_enforce' : contract assertion AST is lowered to a check, if this
      fails, std::terminate () is called.
    'observe'     : contract assertion AST is lowered to a check, if this
      fails, a violation handler is called, the code then
      continues.

  In each case, the "check" might be a simple 'if' (when it is determined that
  the assertion condition does not throw) or the condition evaluation will be
  wrapped in a try-catch block that treats any exception thrown when evaluating
  the check as equivalent to a failed check.  It is noted in the violation data
  object whether a check failed because of an exception raised in evaluation.

  At present, a simple (but potentially space-inefficient) scheme is used to
  store constant data objects that represent the read-only data for the
  violation.  The exact form of this is subject to revision as it represents
  ABI that must be agreed between implementations (as of this point, that
  discussion is not yet concluded).

gcc/c-family/ChangeLog:

* c-common.cc: Add contract_assert keyword.
* c-common.h (enum rid): Likewise.
* c-cppbuiltin.cc (c_cpp_builtins): Add C++26 contracts feature test
macro.
* c.opt: Add a flag to control the TU-wide evaluation semantic. Add
a flag to control whether P1494 observable_checkpoints are inserted
to separate contract checks.
* c.opt.urls: Regenerate.

gcc/ChangeLog:

* config/darwin.h (ASM_GENERATE_INTERNAL_LABEL): Add cases for contract
constant data that need to be in independent link-time 'atoms'.
* doc/invoke.texi: Document -fcontracts and
-fcontract-evaluation-semantic=.

gcc/cp/ChangeLog:

* class.cc (check_for_override): Diagnose attemtps to add contracts to
virtual functions.
* constexpr.cc (cxx_eval_builtin_function_call): Handle
observable_checkpoints emitted for contracts.
(cxx_eval_constant_expression): Check we do not see contract assertions
here...
(potential_constant_expression_1): ... but that we do here.
* contracts.cc: Implement base C++26 P2600R14 contracts.
* contracts.h: Likewise.
* cp-gimplify.cc (cp_genericize_r): Lower contract assertions to control
flow and calls (where required) to the violation handler.
(fold_builtin_source_location): Use revised source_location impl.
constructor.
(build_source_location_impl): Split out the core of the constructor of
source_location so that it can be re-used from the contracts code.
* cp-tree.def (ASSERTION_STMT, PRECONDITION_STMT,
POSTCONDITION_STMT): Revise to allow space for specifying a semantic,
an assertion kind, and (where required) a source location.
* cp-tree.h (enum cp_tree_index, builtin_contract_violation_type): Add
the contract violation object type.
(struct saved_scope): Add a contracts class pointer.
(processing_postcondition, contract_class_ptr): New.
(struct cp_declarator): Add contract_specifiers.
(build_call_a_1): New.
(build_source_location_impl): New.
* decl.cc (duplicate_decls): Check function contract specifiers on
redeclarations.  Handle contract specifiers on instantiations.
(cxx_init_decl_processing): Initialise the terminate function since
this can be called from contracts even when exception processing is
disabled.  Build the contract violation object layout.
(start_decl): Handle checking contract postcondition identifiers.
(grokfndecl): Handle function contract specifiers.
(grokdeclarator): Likewise.
(start_preparsed_function): Start function contracts where needed.
(finish_function): Emit contract specifier AST corresponding to the
checked contracts.
* decl2.cc (c_parse_final_cleanups): Emit helpers for contract
violation handler calls.
* lex.cc (cxx_init): Add keyword warning for contract_assert.
* parser.cc (make_call_declarator): Add contract specifiers.
(unparsed_contracts): New.
(cp_parser_lambda_introducer): Allow contract specifier lambdas to have
a capture default.
(cp_parser_lambda_declarator_opt): Parse function contract specifiers.
(make_dummy_lambda_op): Account for contract specifiers in declarator.
(cp_parser_lambda_body): We have to ensure that deferred contracts are
parsed before we finish the lambda.
(cp_parser_statement): Handle contract_assert and diagnostics for
misplaced pre and post conditions.
(attr_chainon): Make this defensive against being passed a missing
attribute list.
(cp_parser_using_directive): Use attr_chainon instead of chainon.
(contract_attribute_p): New.
(cp_parser_init_declarator): Handle contract specifier names in
attributes.
(cp_parser_direct_declarator): Rename attrs to std_attrs for these.
(cp_parser_type_specifier_seq): Allow for function contract specifiers.
(cp_parser_class_specifier): Handle deferred parsing for function
contract specifiers
(cp_next_tokens_can_be_contract_attribute_p): True if this can be
a function contract specifier (which appear in the same position
as the attribute variant).
(cp_next_tokens_can_be_std_attribute_p): Allow for contracts.
(cp_nth_tokens_can_be_std_attribute_p): Likewise.
(cp_next_tokens_can_be_attribute_p): Likewise.
(cp_parser_late_contract_condition, cp_parser_late_contracts,
cp_parser_contract_assert, cp_parser_function_contract_specifier,
cp_parser_function_contract_specifier_seq): New.
(cp_parser_skip_std_attribute_spec_seq): Handle contracts case.
(cp_parser_skip_attributes_opt): Likewise.
(cp_parser_save_default_args): Handle unparsed contract specs.
* pt.cc (check_explicit_specialization): Handle removing contract
specifiers from instances.
(tsubst_contract, tsubst_contract_attribute,
tsubst_contract_attributes): New.
(tsubst_function_decl): Set contract specs. on substituted func.
(tsubst_stmt): Handle contract_assert.
(tsubst_expr): Allow for uses of postcondition uses of parm names.
(regenerate_decl_from_template): Handle function contract specs.
* semantics.cc (set_one_cleanup_loc): Add a location to
postcondition specifiers.
(finish_non_static_data_member): Diagnose bad uses of members
in contract specifiers.
(finish_this_expr): Diagnose invalid use of this in contract
specifiers.
(process_outer_var_ref): Allow use of params in contract specs.
(finish_id_expression_1): Likewise.
(apply_deduced_return_type): Maybe update postconditions when
the return type is changed.
* tree.cc (cp_tree_equal): Handle special cases when comparing
contracts.

gcc/testsuite/ChangeLog:

* g++.dg/warn/Wkeyword-macro-1.C: Update for contract_assert.
* g++.dg/warn/Wkeyword-macro-2.C: Likewise.
* g++.dg/warn/Wkeyword-macro-4.C: Likewise.
* g++.dg/warn/Wkeyword-macro-5.C: Likewise.
* g++.dg/warn/Wkeyword-macro-7.C: Likewise.
* g++.dg/warn/Wkeyword-macro-8.C: Likewise.

Co-Authored-by: Nina Ranns <dinka.ranns@gmail.com>
Co-Authored-by: Ville Voutilainen <ville.voutilainen@gmail.com>
Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
28 files changed:
gcc/c-family/c-common.cc
gcc/c-family/c-common.h
gcc/c-family/c-cppbuiltin.cc
gcc/c-family/c.opt
gcc/c-family/c.opt.urls
gcc/config/darwin.h
gcc/cp/class.cc
gcc/cp/constexpr.cc
gcc/cp/contracts.cc
gcc/cp/contracts.h
gcc/cp/cp-gimplify.cc
gcc/cp/cp-tree.def
gcc/cp/cp-tree.h
gcc/cp/decl.cc
gcc/cp/decl2.cc
gcc/cp/lex.cc
gcc/cp/parser.cc
gcc/cp/pt.cc
gcc/cp/semantics.cc
gcc/cp/tree.cc
gcc/doc/invoke.texi
gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p8-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wkeyword-macro-1.C
gcc/testsuite/g++.dg/warn/Wkeyword-macro-2.C
gcc/testsuite/g++.dg/warn/Wkeyword-macro-4.C
gcc/testsuite/g++.dg/warn/Wkeyword-macro-5.C
gcc/testsuite/g++.dg/warn/Wkeyword-macro-7.C
gcc/testsuite/g++.dg/warn/Wkeyword-macro-8.C

index bf1cdaf34d3fb63c7fa9ecef1d0b5aa312df4d0f..7f891e1ef4935f22595a9e18ed9d9710a4ddcaa3 100644 (file)
@@ -103,7 +103,7 @@ machine_mode c_default_pointer_mode = VOIDmode;
        tree dfloat32_type_node;
        tree dfloat64_type_node;
        tree dfloat128_type_node;
-       tree dfloat64x_type_node; 
+       tree dfloat64x_type_node;
 
        tree intQI_type_node;
        tree intHI_type_node;
@@ -472,6 +472,7 @@ const struct c_common_resword c_common_reswords[] =
   { "__const",         RID_CONST,      0 },
   { "__const__",       RID_CONST,      0 },
   { "__constinit",     RID_CONSTINIT,  D_CXXONLY },
+  { "__contract_assert", RID_CONTASSERT, D_CXXONLY | D_CXXWARN  },
   { "__decltype",       RID_DECLTYPE,   D_CXXONLY },
   { "__extension__",   RID_EXTENSION,  0 },
   { "__func__",                RID_C99_FUNCTION_NAME, 0 },
@@ -519,6 +520,7 @@ const struct c_common_resword c_common_reswords[] =
   { "constinit",       RID_CONSTINIT,  D_CXXONLY | D_CXX20 | D_CXXWARN },
   { "const_cast",      RID_CONSTCAST,  D_CXXONLY | D_CXXWARN },
   { "continue",                RID_CONTINUE,   0 },
+  { "contract_assert", RID_CONTASSERT, D_CXXONLY | D_CXXWARN | D_CXX26 },
   { "decltype",         RID_DECLTYPE,   D_CXXONLY | D_CXX11 | D_CXXWARN },
   { "default",         RID_DEFAULT,    0 },
   { "delete",          RID_DELETE,     D_CXXONLY | D_CXXWARN },
index 537b9acb0689f87f1c383a681f21f0befb36a9bf..89517e2a80ccb8c6c87cc76c38ea9970d52e9c0f 100644 (file)
@@ -189,6 +189,9 @@ enum rid
   /* C++ coroutines */
   RID_CO_AWAIT, RID_CO_YIELD, RID_CO_RETURN,
 
+  /* C++26 */
+  RID_CONTASSERT,
+
   /* C++ transactional memory.  */
   RID_ATOMIC_NOEXCEPT, RID_ATOMIC_CANCEL, RID_SYNCHRONIZED,
 
@@ -258,6 +261,8 @@ enum rid
   RID_LAST_CXX11 = RID_STATIC_ASSERT,
   RID_FIRST_CXX20 = RID_CONSTINIT,
   RID_LAST_CXX20 = RID_CO_RETURN,
+  RID_FIRST_CXX26 = RID_CONTASSERT,
+  RID_LAST_CXX26 = RID_CONTASSERT,
   RID_FIRST_AT = RID_AT_ENCODE,
   RID_LAST_AT = RID_AT_IMPLEMENTATION,
   RID_FIRST_PQ = RID_IN,
index de768c527e1290940b4242277b46f3cb7b70f07e..12ddfa22074ccae2fd89e70c95f2797fff02d4ca 100644 (file)
@@ -1126,8 +1126,7 @@ c_cpp_builtins (cpp_reader *pfile)
       else if (cxx_dialect >= cxx20)
        cpp_warn (pfile, "__cpp_concepts");
       if (flag_contracts)
-       {
-       }
+       cpp_define (pfile, "__cpp_contracts=202502L");
       else if (cxx_dialect >= cxx26)
        cpp_warn (pfile, "__cpp_contracts");
       if (flag_modules)
index 186d3b14792b214c895927421d6afa077658a4a4..1e8a4683f0401fbf1592615f6c537267fa5e9d4b 100644 (file)
@@ -1902,7 +1902,28 @@ C++ ObjC++ Joined RejectNegative Host_Wide_Int Var(constexpr_ops_limit) Init(335
 
 fcontracts
 C++ ObjC++ Var(flag_contracts) Init(0)
-Enable certain features present in drafts of C++ Contracts.
+Enable features proposed for C++26 Contracts.
+
+; Keep these in step with the values in cp/contracts.h
+
+Enum
+Name(contract_semantic) Type(int)  UnknownError(unrecognized contract evaluation semantic %qs)
+
+EnumValue
+Enum(contract_semantic) String(ignore) Value(1)
+
+EnumValue
+Enum(contract_semantic) String(observe) Value(2)
+
+EnumValue
+Enum(contract_semantic) String(enforce) Value(3)
+
+EnumValue
+Enum(contract_semantic) String(quick_enforce) Value(4)
+
+fcontract-evaluation-semantic=
+C++ ObjC++ Joined RejectNegative Enum(contract_semantic) Var(flag_contract_evaluation_semantic) Init(3)
+-fcontract-evaluation-semantic=[ignore|observe|enforce|quick_enforce]  Select the contract evaluation semantic (defaults to enforce).
 
 fcoroutines
 C++ ObjC++ LTO Var(flag_coroutines)
index cf186466ccf7bf3d63c35dbd454a617595973282..d214b1a7d9e4f051dd5a957f863187198a22b29f 100644 (file)
@@ -1095,26 +1095,8 @@ UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fconstexpr-ops-limit)
 fcontracts
 UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fcontracts)
 
-fcontract-assumption-mode=
-UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fcontract-assumption-mode)
-
-fcontract-build-level=
-UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fcontract-build-level)
-
-fcontract-strict-declarations=
-UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fcontract-strict-declarations)
-
-fcontract-mode=
-UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fcontract-mode)
-
-fcontract-continuation-mode=
-UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fcontract-continuation-mode)
-
-fcontract-role=
-UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fcontract-role)
-
-fcontract-semantic=
-UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fcontract-semantic)
+fcontract-evaluation-semantic=
+UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fcontract-evaluation-semantic)
 
 fcoroutines
 UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-fcoroutines)
index b7379c8e099cbf3ceefe8e9961966832492dc5d0..53a7b5980e62b5e5816f634c378493d3d0263aea 100644 (file)
@@ -1022,6 +1022,12 @@ extern GTY(()) section * darwin_sections[NUM_DARWIN_SECTIONS];
       sprintf (LABEL, "*%s%ld", "lTRAMP", (long)(NUM));\
     else if (strncmp ("LANCHOR", PREFIX, 7) == 0)      \
       sprintf (LABEL, "*%s%ld", "lANCHOR", (long)(NUM));\
+    else if (strlen (PREFIX) == 19 \
+            && strncmp ("Lcontract_violation", PREFIX, 19) == 0)       \
+      sprintf (LABEL, "*%s%ld", "lcontract_violation", (long)(NUM));\
+    else if (strlen (PREFIX) == 13 \
+            && strncmp ("Lsrc_loc_impl", PREFIX, 13) == 0)     \
+      sprintf (LABEL, "*%s%ld", "lsrc_loc_impl", (long)(NUM));\
     else                                               \
       sprintf (LABEL, "*%s%ld", PREFIX, (long)(NUM));  \
   } while (0)
index 6f00ca671e473708bb63d96745423978b2783571..315ffd316ffbaf938f84d2d2a71a759230c73164 100644 (file)
@@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimplify.h"
 #include "intl.h"
 #include "asan.h"
+#include "contracts.h"
 
 /* Id for dumping the class hierarchy.  */
 int class_dump_id;
@@ -3254,6 +3255,10 @@ check_for_override (tree decl, tree ctype)
 
       if (DECL_DESTRUCTOR_P (decl))
        TYPE_HAS_NONTRIVIAL_DESTRUCTOR (ctype) = true;
+
+      if (DECL_HAS_CONTRACTS_P (decl))
+       error_at (DECL_SOURCE_LOCATION (decl),
+                 "contracts cannot be added to virtual functions");
     }
   else if (DECL_FINAL_P (decl))
     error ("%q+#D marked %<final%>, but is not virtual", decl);
index 8d9bd55140290f6228b31e3f28dcf42716db5cde..1527e9dcbac83c9e88c8dd79d586a41427a73717 100644 (file)
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "fold-const.h"
 #include "intl.h"
 #include "toplev.h"
+#include "contracts.h"
 
 static bool verify_constant (tree, bool, bool *, bool *);
 #define VERIFY_CONSTANT(X)                                             \
@@ -1206,6 +1207,12 @@ public:
   unsigned heap_dealloc_count;
   /* Number of uncaught exceptions.  */
   unsigned uncaught_exceptions;
+  /* A contract statement that failed or was not constant, we only store the
+     first one that fails.  */
+  tree contract_statement;
+  /* [basic.contract.eval]/7.3 if this expression would otherwise be constant
+     then a non-const contract makes the program ill-formed.  */
+  bool contract_condition_non_const;
   /* Some metafunctions aren't dependent just on their arguments, but also
      on various other dependencies, e.g. has_identifier on a function parameter
      reflection can change depending on further declarations of corresponding
@@ -1221,7 +1228,8 @@ public:
   constexpr_global_ctx ()
     : constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr),
       consteval_block (NULL_TREE), heap_dealloc_count (0),
-      uncaught_exceptions (0), metafns_called (false) {}
+      uncaught_exceptions (0), contract_statement (NULL_TREE),
+      contract_condition_non_const (false), metafns_called (false) {}
 
   bool is_outside_lifetime (tree t)
   {
@@ -2392,6 +2400,7 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
        break;
       case BUILT_IN_ASAN_POINTER_COMPARE:
       case BUILT_IN_ASAN_POINTER_SUBTRACT:
+      case BUILT_IN_OBSERVABLE_CHKPT:
        /* These builtins shall be ignored during constant expression
           evaluation.  */
        return void_node;
@@ -10324,7 +10333,47 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case ASSERTION_STMT:
     case PRECONDITION_STMT:
     case POSTCONDITION_STMT:
-      gcc_checking_assert (false && "hoo...");
+      {
+       r = void_node;
+       /* Only record the first fail, and do not go further is the semantic
+          is 'ignore'.  */
+       if (*non_constant_p || ctx->global->contract_statement
+           || contract_ignored_p (t))
+         break;
+
+       tree cond = CONTRACT_CONDITION (t);
+       if (!potential_rvalue_constant_expression (cond))
+         {
+           ctx->global->contract_statement = t;
+           ctx->global->contract_condition_non_const = true;
+           break;
+         }
+
+       /* We need to evaluate and stash the result of this here, since whether
+          it needs to be reported (and how) depends on whether the containing
+          expression is otherwise const.  */
+       bool ctrct_non_const_p = false;
+       bool ctrct_overflow_p = false;
+       tree jmp_target = NULL_TREE;
+       constexpr_ctx new_ctx = *ctx;
+       new_ctx.quiet = true;
+       /* Avoid modification of existing values.  */
+       modifiable_tracker ms (new_ctx.global);
+       tree eval =
+         cxx_eval_constant_expression (&new_ctx, cond, vc_prvalue,
+                                       &ctrct_non_const_p,
+                                       &ctrct_overflow_p, &jmp_target);
+       /* Not a constant.  */
+       if (ctrct_non_const_p)
+         {
+           ctx->global->contract_statement = t;
+           ctx->global->contract_condition_non_const = true;
+           break;
+         }
+       /* Constant, but check failed.  */
+       if (integer_zerop (eval))
+         ctx->global->contract_statement = t;
+      }
       break;
 
     case TEMPLATE_ID_EXPR:
@@ -10549,6 +10598,41 @@ mark_non_constant (tree t)
   return t;
 }
 
+/* If we have a successful constant evaluation, now check whether there is
+   a failed or non-constant contract that would invalidate this.  */
+
+static bool
+check_for_failed_contracts (constexpr_global_ctx *global_ctx)
+{
+  if (!flag_contracts || !global_ctx->contract_statement)
+    return false;
+
+  location_t loc = EXPR_LOCATION (global_ctx->contract_statement);
+  enum diagnostics::kind kind;
+  bool error = false;
+  /* [intro.compliance.general]/2.3.4. */
+  /* [basic.contract.eval]/8. */
+  if (contract_terminating_p (global_ctx->contract_statement))
+    {
+      kind = diagnostics::kind::error;
+      error = true;
+    }
+  else
+    kind = diagnostics::kind::warning;
+
+  /* [basic.contract.eval]/7.3 */
+  if (global_ctx->contract_condition_non_const)
+    {
+      emit_diagnostic (kind, loc, 0, "contract condition is not constant");
+      return error;
+    }
+
+  /* Otherwise, the evaluation was const, but determined to be false.  */
+  emit_diagnostic (kind, loc, 0,
+                  "contract predicate is false in constant expression");
+  return error;
+}
+
 /* ALLOW_NON_CONSTANT is false if T is required to be a constant expression.
    STRICT has the same sense as for constant_value_1: true if we only allow
    conforming C++ constant expressions, or false if we want a constant value
@@ -10908,7 +10992,10 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 
   if (constexpr_dtor)
     {
-      DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
+      if (check_for_failed_contracts (&global_ctx))
+       r = mark_non_constant (r);
+      else
+        DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true;
       return r;
     }
 
@@ -10958,6 +11045,8 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
   if (location_t loc = EXPR_LOCATION (t))
     protected_set_expr_location (r, loc);
 
+  if (check_for_failed_contracts (&global_ctx))
+    r = mark_non_constant (r);
   return r;
 }
 
@@ -12720,7 +12809,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
     case ASSERTION_STMT:
     case PRECONDITION_STMT:
     case POSTCONDITION_STMT:
-      gcc_checking_assert (false && "hmm...");
+      /* Contracts are not supposed to alter this; we have to check that this
+        is not violated at a later time.  */
+      return true;
 
     case LABEL_EXPR:
       t = LABEL_EXPR_LABEL (t);
index 7da5f2114cf30e33b17e4adbf27f86f4738c6bd5..205ea52774b9de82521f020b386b2c5be301f2b7 100644 (file)
@@ -1,6 +1,12 @@
-/* Definitions for C++ contract levels
+/* C++ contracts.
+
    Copyright (C) 2020-2026 Free Software Foundation, Inc.
-   Contributed by Jeff Chapman II (jchapman@lock3software.com)
+   Originally by Jeff Chapman II (jchapman@lock3software.com) for proposed
+   C++20 contracts.
+   Rewritten for C++26 contracts by:
+     Nina Ranns (dinka.ranns@googlemail.com)
+     Iain Sandoe (iain@sandoe.co.uk)
+     Ville Voutilainen (ville.voutilainen@gmail.com).
 
 This file is part of GCC.
 
@@ -17,3 +23,2052 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "cp-tree.h"
+#include "stringpool.h"
+#include "diagnostic.h"
+#include "options.h"
+#include "contracts.h"
+#include "tree.h"
+#include "tree-inline.h"
+#include "attribs.h"
+#include "tree-iterator.h"
+#include "print-tree.h"
+#include "stor-layout.h"
+#include "intl.h"
+#include "cgraph.h"
+#include "opts.h"
+#include "output.h"
+
+/*  Design notes.
+
+  There are three phases:
+    1. Parsing and semantic checks.
+       Most of the code for this is in the parser, with helpers provided here.
+    2. Emitting contract assertion AST nodes into function bodies.
+       This is initiated from "finish_function ()"
+    3. Lowering the contract assertion AST nodes to control flow, constant
+       data and calls to the violation handler.
+       This is initiated from "cp_genericize ()".
+
+  The organisation of the code in this file is intended to follow those three
+  phases where possible.
+
+  Contract Assertion State
+  ========================
+
+  contract_assert () does not require any special handling and can be
+  represented directly by AST inserted in the function body.
+
+  'pre' and 'post' function contract specifiers require most of the special
+  handling, since they must be tracked across re-declarations of functions and
+  there are contraints on how such specifiers may change in these cases.
+
+  The contracts specification identifies a "first declaration" of any given
+  function - which is the first encountered when parsing a given TU.
+  Subsequent re-declarations may not add or change the function contract
+  specifiers from any introduced on this first declaration.  It is, however,
+  permitted to omit specifiers on re-declarations.
+
+  Since the implementation of GCC's (re-)declarations is a destructive merge
+  we need to keep some state on the side to determine whether the re-declaration
+  rules are met.  In this current design we have chosen not to add another tree
+  to each function decl but, instead, keep a map from function decl to contract
+  specifier state.  In this state we record the 'first declaration' specifiers
+  which are used to validate re-declaration(s) and to report the initial state
+  in diagnostics.
+
+  We need (for example) to compare
+    pre ( x > 2 ) equal to
+    pre ( z > 2 ) when x and z refer to the same function parameter in a
+    re-declaration.
+
+  The mechanism used to determine if two contracts are the same is to compare
+  the folded trees.  This makes use of current compiler machinery, rather than
+  constructing some new AST comparison scheme.  However, it does introduce an
+  additional complexity in that we need to defer such comparison until parsing
+  is complete - and function contract specifiers in class declarations must be
+  deferred parses, since it is also permitted for specifiers to refer to class
+  members.
+
+  When we encounter a definition, the parameter names in a function decl are
+  re-written to match those of the definition (thus the expected names will
+  appear in debug information etc).  At this point, we also need to re-map
+  any function parameter names that appear in function contract specifiers
+  to agree with those of the definition - although we intend to keep the
+  'first declaration' record consistent for diagnostics.
+
+  Since we shared some code from the C++2a contracts implementation, pre and
+  post specifiers are represented by chains of attributes, where the payload
+  of the attribute is an AST node.  However during the parse, these are not
+  inserted into the function bodies, but kept in the decl-keyed state described
+  above.  A future improvement planned here is to store the specifiers using a
+  tree vec instead of the attribute list.
+
+  Emitting contract AST
+  =====================
+
+  When we reach `finish_function ()` and therefore are committed to potentially
+  emitting code for an instance, we build a new variant of the function body
+  with the pre-condition AST inserted before the user's function body, and the
+  post condition AST (if any) linked into the function return.
+
+  Lowering the contract assertion AST
+  ===================================
+
+  In all cases (pre, post, contract_assert) the AST node is lowered to control
+  flow and (potentially) calls to the violation handler and/or termination.
+  This is done during `cp_genericize ()`.  In the current implementation, the
+  decision on the control flow is made on the basis of the setting of a command-
+  line flag that determines a TU-wide contract evaluation semantic, which has
+  the following initial set of behaviours:
+
+    'ignore'       : contract assertion AST is lowered to 'nothing',
+                     i.e. omitted.
+    'enforce'      : contract assertion AST is lowered to a check, if this
+                     fails a violation handler is called, followed by
+                     std::terminate().
+    'quick_enforce' : contract assertion AST is lowered to a check, if this
+                     fails, std::terminate () is called.
+    'observe'      : contract assertion AST is lowered to a check, if this
+                     fails, a violation handler is called, the code then
+                     continues.
+
+  In each case, the "check" might be a simple 'if' (when it is determined that
+  the assertion condition does not throw) or the condition evaluation will be
+  wrapped in a try-catch block that treats any exception thrown when evaluating
+  the check as equivalent to a failed check.  It is noted in the violation data
+  object whether a check failed because of an exception raised in evaluation.
+
+  At present, a simple (but potentially space-inefficient) scheme is used to
+  store constant data objects that represent the read-only data for the
+  violation.  The exact form of this is subject to revision as it represents
+  ABI that must be agreed between implementations (as of this point, that
+  discussion is not yet concluded).  */
+
+/* Contract matching.  */
+
+bool comparing_contracts;
+
+/* True if the contract is valid.  */
+
+static bool
+contract_valid_p (tree contract)
+{
+  return CONTRACT_CONDITION (contract) != error_mark_node;
+}
+
+/* True if the contract specifier is valid.  */
+
+static bool
+contract_specifier_valid_p (tree specifier)
+{
+  return contract_valid_p (TREE_VALUE (TREE_VALUE (specifier)));
+}
+
+/* Compare the contract conditions of OLD_CONTRACT and NEW_CONTRACT.
+   Returns false if the conditions are equivalent, and true otherwise.  */
+
+static bool
+mismatched_contracts_p (tree old_contract, tree new_contract)
+{
+  /* Different kinds of contracts do not match.  */
+  if (TREE_CODE (old_contract) != TREE_CODE (new_contract))
+    {
+      auto_diagnostic_group d;
+      error_at (EXPR_LOCATION (new_contract),
+               "mismatched contract specifier in declaration");
+      inform (EXPR_LOCATION (old_contract), "previous contract here");
+      return true;
+    }
+
+  /* A deferred contract tentatively matches.  */
+  if (CONTRACT_CONDITION_DEFERRED_P (new_contract))
+    return false;
+
+  /* Compare the conditions of the contracts.  */
+  tree t1 = cp_fully_fold_init (CONTRACT_CONDITION (old_contract));
+  tree t2 = cp_fully_fold_init (CONTRACT_CONDITION (new_contract));
+
+  /* Compare the contracts. */
+
+  bool saved_comparing_contracts = comparing_contracts;
+  comparing_contracts = true;
+  bool matching_p = cp_tree_equal (t1, t2);
+  comparing_contracts = saved_comparing_contracts;
+
+  if (!matching_p)
+    {
+      auto_diagnostic_group d;
+      error_at (EXPR_LOCATION (CONTRACT_CONDITION (new_contract)),
+               "mismatched contract condition in declaration");
+      inform (EXPR_LOCATION (CONTRACT_CONDITION (old_contract)),
+             "previous contract here");
+      return true;
+    }
+
+  return false;
+}
+
+/* Compare the contract specifiers of OLDDECL and NEWDECL. Returns true
+   if the contracts match, and false if they differ.  */
+
+static bool
+match_contract_specifiers (location_t oldloc, tree old_contracts,
+                          location_t newloc, tree new_contracts)
+{
+  /* Contracts only match if they are both specified.  */
+  if (!old_contracts || !new_contracts)
+    return true;
+
+  /* Compare each contract in turn.  */
+  while (old_contracts && new_contracts)
+    {
+      /* If either contract is ill-formed, skip the rest of the comparison,
+        since we've already diagnosed an error.  */
+      if (!contract_specifier_valid_p (new_contracts)
+         || !contract_specifier_valid_p (old_contracts))
+       return false;
+
+      if (mismatched_contracts_p (CONTRACT_STATEMENT (old_contracts),
+                                 CONTRACT_STATEMENT (new_contracts)))
+       return false;
+      old_contracts = TREE_CHAIN (old_contracts);
+      new_contracts = TREE_CHAIN (new_contracts);
+    }
+
+  /* If we didn't compare all specifiers, the contracts don't match.  */
+  if (old_contracts || new_contracts)
+    {
+      auto_diagnostic_group d;
+      error_at (newloc,
+               "declaration has a different number of contracts than "
+               "previously declared");
+      inform (oldloc,
+             new_contracts
+             ? "previous declaration with fewer contracts here"
+             : "previous declaration with more contracts here");
+      return false;
+    }
+
+  return true;
+}
+
+/* Return true if CONTRACT is checked or assumed under the current build
+   configuration. */
+
+static bool
+contract_active_p (tree contract)
+{
+  return get_evaluation_semantic (contract) != CES_IGNORE;
+}
+
+/* True if FNDECL has any checked or assumed contracts whose TREE_CODE is
+   C.  */
+
+static bool
+has_active_contract_condition (tree fndecl, tree_code c)
+{
+  tree as = get_fn_contract_specifiers (fndecl);
+  for (; as != NULL_TREE; as = TREE_CHAIN (as))
+    {
+      tree contract = TREE_VALUE (TREE_VALUE (as));
+      if (TREE_CODE (contract) == c && contract_active_p (contract))
+       return true;
+    }
+  return false;
+}
+
+/* True if FNDECL has any checked or assumed preconditions.  */
+
+static bool
+has_active_preconditions (tree fndecl)
+{
+  return has_active_contract_condition (fndecl, PRECONDITION_STMT);
+}
+
+/* True if FNDECL has any checked or assumed postconditions.  */
+
+static bool
+has_active_postconditions (tree fndecl)
+{
+  return has_active_contract_condition (fndecl, POSTCONDITION_STMT);
+}
+
+/* Return true if any contract in the CONTRACT list is checked or assumed
+   under the current build configuration.  */
+
+static bool
+contract_any_active_p (tree fndecl)
+{
+  tree as = get_fn_contract_specifiers (fndecl);
+  for (; as; as = TREE_CHAIN (as))
+    if (contract_active_p (TREE_VALUE (TREE_VALUE (as))))
+      return true;
+  return false;
+}
+
+/* Return true if any contract in CONTRACTS is not yet parsed.  */
+
+bool
+contract_any_deferred_p (tree contracts)
+{
+  for (; contracts; contracts = TREE_CHAIN (contracts))
+    if (CONTRACT_CONDITION_DEFERRED_P (CONTRACT_STATEMENT (contracts)))
+      return true;
+  return false;
+}
+
+/* Returns true if function decl FNDECL has contracts and we need to
+   process them for the purposes of either building caller or definition
+   contract checks.
+   This function does not take into account whether caller or definition
+   side checking is enabled. Those checks will be done from the calling
+   function which will be able to determine whether it is doing caller
+   or definition contract handling.  */
+
+static bool
+handle_contracts_p (tree fndecl)
+{
+  return (flag_contracts
+         && !processing_template_decl
+         && contract_any_active_p (fndecl));
+}
+
+
+/* For use with the tree inliner. This preserves non-mapped local variables,
+   such as postcondition result variables, during remapping.  */
+
+static tree
+retain_decl (tree decl, copy_body_data *)
+{
+  return decl;
+}
+
+/* Lookup a name in std::, or inject it.  */
+
+static tree
+lookup_std_type (tree name_id)
+{
+  tree res_type = lookup_qualified_name
+    (std_node, name_id, LOOK_want::TYPE | LOOK_want::HIDDEN_FRIEND);
+
+  if (TREE_CODE (res_type) == TYPE_DECL)
+    res_type = TREE_TYPE (res_type);
+  else
+    {
+      push_nested_namespace (std_node);
+      res_type = make_class_type (RECORD_TYPE);
+      create_implicit_typedef (name_id, res_type);
+      DECL_SOURCE_LOCATION (TYPE_NAME (res_type)) = BUILTINS_LOCATION;
+      DECL_CONTEXT (TYPE_NAME (res_type)) = current_namespace;
+      pushdecl_namespace_level (TYPE_NAME (res_type), /*hidden*/true);
+      pop_nested_namespace (std_node);
+    }
+  return res_type;
+}
+
+/* Get constract_assertion_kind of the specified contract. Used when building
+  contract_violation object.  */
+
+static contract_assertion_kind
+get_contract_assertion_kind (tree contract)
+{
+  if (CONTRACT_ASSERTION_KIND (contract))
+    {
+      tree s = CONTRACT_ASSERTION_KIND (contract);
+      tree i = (TREE_CODE (s) == INTEGER_CST) ? s
+                                             : DECL_INITIAL (STRIP_NOPS (s));
+      gcc_checking_assert (!type_dependent_expression_p (s) && i);
+      return (contract_assertion_kind) tree_to_uhwi (i);
+    }
+
+  switch (TREE_CODE (contract))
+  {
+    case ASSERTION_STMT:       return CAK_ASSERT;
+    case PRECONDITION_STMT:    return CAK_PRE;
+    case POSTCONDITION_STMT:   return CAK_POST;
+    default: break;
+  }
+
+  gcc_unreachable ();
+}
+
+/* Get contract_evaluation_semantic of the specified contract.  */
+
+contract_evaluation_semantic
+get_evaluation_semantic (const_tree contract)
+{
+  if (CONTRACT_EVALUATION_SEMANTIC (contract))
+    {
+      tree s = CONTRACT_EVALUATION_SEMANTIC (contract);
+      tree i = (TREE_CODE (s) == INTEGER_CST) ? s
+                                             : DECL_INITIAL (STRIP_NOPS (s));
+      gcc_checking_assert (!type_dependent_expression_p (s) && i);
+      switch (contract_evaluation_semantic ev =
+             (contract_evaluation_semantic) tree_to_uhwi (i))
+       {
+       /* This needs to be kept in step with any added semantics.  */
+       case CES_IGNORE:
+       case CES_OBSERVE:
+       case CES_ENFORCE:
+       case CES_QUICK:
+         return ev;
+       default:
+         break;
+       }
+    }
+
+  gcc_unreachable ();
+}
+
+/* Get location of the last contract in the CONTRACTS tree chain.  */
+
+static location_t
+get_contract_end_loc (tree contracts)
+{
+  tree last = NULL_TREE;
+  for (tree a = contracts; a; a = TREE_CHAIN (a))
+    last = a;
+  gcc_checking_assert (last);
+  last = CONTRACT_STATEMENT (last);
+  return EXPR_LOCATION (last);
+}
+
+struct GTY(()) contract_decl
+{
+  tree contract_specifiers;
+  location_t note_loc;
+};
+
+static GTY(()) hash_map<tree, contract_decl> *contract_decl_map;
+
+/* Converts a contract condition to bool and ensures it has a location.  */
+
+tree
+finish_contract_condition (cp_expr condition)
+{
+  if (!condition || error_operand_p (condition))
+    return condition;
+
+  /* Ensure we have the condition location saved in case we later need to
+     emit a conversion error during template instantiation and wouldn't
+     otherwise have it.  This differs from maybe_wrap_with_location in that
+     it allows wrappers on EXCEPTIONAL_CLASS_P which includes CONSTRUCTORs.  */
+  if (!CAN_HAVE_LOCATION_P (condition)
+      && condition.get_location () != UNKNOWN_LOCATION)
+    {
+      tree_code code
+       = (((CONSTANT_CLASS_P (condition) && TREE_CODE (condition) != STRING_CST)
+           || (TREE_CODE (condition) == CONST_DECL && !TREE_STATIC (condition)))
+         ? NON_LVALUE_EXPR : VIEW_CONVERT_EXPR);
+      condition = build1_loc (condition.get_location (), code,
+                             TREE_TYPE (condition), condition);
+      EXPR_LOCATION_WRAPPER_P (condition) = true;
+    }
+
+  if (type_dependent_expression_p (condition))
+    return condition;
+
+  return condition_conversion (condition);
+}
+
+void
+maybe_update_postconditions (tree fndecl)
+{
+  /* Update any postconditions and the postcondition checking function
+     as needed.  If there are postconditions, we'll use those to rewrite
+     return statements to check postconditions.  */
+  if (has_active_postconditions (fndecl))
+    rebuild_postconditions (fndecl);
+}
+
+void
+start_function_contracts (tree fndecl)
+{
+  if (error_operand_p (fndecl))
+    return;
+
+  if (!handle_contracts_p (fndecl))
+    return;
+
+  /* Check that the user did not try to shadow a function parameter with the
+     specified postcondition result name.  */
+  for (tree ca = get_fn_contract_specifiers (fndecl); ca; ca = TREE_CHAIN (ca))
+    if (POSTCONDITION_P (CONTRACT_STATEMENT (ca)))
+      if (tree id = POSTCONDITION_IDENTIFIER (CONTRACT_STATEMENT (ca)))
+       {
+         if (id == error_mark_node)
+           {
+             CONTRACT_CONDITION (CONTRACT_STATEMENT (ca)) = error_mark_node;
+             continue;
+           }
+         tree r_name = tree_strip_any_location_wrapper (id);
+         if (TREE_CODE (id) == PARM_DECL)
+           r_name = DECL_NAME (id);
+         gcc_checking_assert (r_name && TREE_CODE (r_name) == IDENTIFIER_NODE);
+         tree seen = lookup_name (r_name);
+         if (seen
+             && TREE_CODE (seen) == PARM_DECL
+             && DECL_CONTEXT (seen) == fndecl)
+           {
+               auto_diagnostic_group d;
+               location_t id_l = location_wrapper_p (id)
+                                 ? EXPR_LOCATION (id)
+                                 : DECL_SOURCE_LOCATION (id);
+               location_t co_l = EXPR_LOCATION (CONTRACT_STATEMENT (ca));
+               if (id_l != UNKNOWN_LOCATION)
+                 co_l = make_location (id_l, co_l, co_l);
+               error_at (co_l,
+                         "contract postcondition result name shadows a"
+                         " function parameter");
+               inform (DECL_SOURCE_LOCATION (seen),
+                       "parameter declared here");
+               POSTCONDITION_IDENTIFIER (CONTRACT_STATEMENT (ca))
+                 = error_mark_node;
+               CONTRACT_CONDITION (CONTRACT_STATEMENT (ca)) = error_mark_node;
+           }
+       }
+}
+
+/* Allow specifying a sub-set of contract kinds to copy.  */
+
+enum contract_match_kind
+{
+  cmk_pre,
+  cmk_post,
+  cmk_all
+};
+
+/* Copy (possibly a sub-set of) contracts from CONTRACTS on FNDECL.  */
+
+static tree
+copy_contracts_list (tree contracts, tree fndecl,
+                    contract_match_kind remap_kind = cmk_all)
+{
+  tree last = NULL_TREE, new_contracts = NULL_TREE;
+  for (; contracts; contracts = TREE_CHAIN (contracts))
+    {
+      if ((remap_kind == cmk_pre
+          && (TREE_CODE (CONTRACT_STATEMENT (contracts))
+              == POSTCONDITION_STMT))
+         || (remap_kind == cmk_post
+             && (TREE_CODE (CONTRACT_STATEMENT (contracts))
+                 == PRECONDITION_STMT)))
+       continue;
+
+      tree c = copy_node (contracts);
+      TREE_VALUE (c) = build_tree_list (TREE_PURPOSE (TREE_VALUE (c)),
+                                       copy_node (CONTRACT_STATEMENT (c)));
+
+      copy_body_data id;
+      hash_map<tree, tree> decl_map;
+
+      memset (&id, 0, sizeof (id));
+
+      id.src_fn = fndecl;
+      id.dst_fn = fndecl;
+      id.src_cfun = DECL_STRUCT_FUNCTION (fndecl);
+      id.decl_map = &decl_map;
+
+      id.copy_decl = retain_decl;
+
+      id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+      id.transform_new_cfg = false;
+      id.transform_return_to_modify = false;
+      id.transform_parameter = true;
+
+      /* Make sure not to unshare trees behind the front-end's back
+        since front-end specific mechanisms may rely on sharing.  */
+      id.regimplify = false;
+      id.do_not_unshare = true;
+      id.do_not_fold = true;
+
+      /* We're not inside any EH region.  */
+      id.eh_lp_nr = 0;
+      walk_tree (&CONTRACT_CONDITION (CONTRACT_STATEMENT (c)),
+                                     copy_tree_body_r, &id, NULL);
+
+
+      CONTRACT_COMMENT (CONTRACT_STATEMENT (c))
+       = copy_node (CONTRACT_COMMENT (CONTRACT_STATEMENT (c)));
+
+      chainon (last, c);
+      last = c;
+      if (!new_contracts)
+       new_contracts = c;
+    }
+  return new_contracts;
+}
+
+/* Returns a copy of FNDECL contracts. This is used when emiting a contract.
+ If we were to emit the original contract tree, any folding of the contract
+ condition would affect the original contract too. The original contract
+ tree needs to be preserved in case it is used to apply to a different
+ function (for inheritance or wrapping reasons). */
+
+static tree
+copy_contracts (tree fndecl, contract_match_kind remap_kind = cmk_all)
+{
+  tree contracts = get_fn_contract_specifiers (fndecl);
+  return copy_contracts_list (contracts, fndecl, remap_kind);
+}
+
+/* Add the contract statement CONTRACT to the current block if valid.  */
+
+static bool
+emit_contract_statement (tree contract)
+{
+  /* Only add valid contracts.  */
+  if (contract == error_mark_node
+      || CONTRACT_CONDITION (contract) == error_mark_node)
+    return false;
+
+  if (get_evaluation_semantic (contract) == CES_INVALID)
+    return false;
+
+  add_stmt (contract);
+  return true;
+}
+
+/* Generate the statement for the given contract by adding the
+   statement to the current block. Returns the next contract in the chain.  */
+
+static tree
+emit_contract (tree contract)
+{
+  gcc_assert (TREE_CODE (contract) == TREE_LIST);
+
+  emit_contract_statement (CONTRACT_STATEMENT (contract));
+
+  return TREE_CHAIN (contract);
+}
+
+/* Add a call or a direct evaluation of the pre checks.  */
+
+static void
+apply_preconditions (tree fndecl)
+{
+  tree contract_copy = copy_contracts (fndecl, cmk_pre);
+  for (; contract_copy; contract_copy = TREE_CHAIN (contract_copy))
+    emit_contract (contract_copy);
+}
+
+/* Add a call or a direct evaluation of the post checks.  */
+
+static void
+apply_postconditions (tree fndecl)
+{
+  tree contract_copy = copy_contracts (fndecl, cmk_post);
+  for (; contract_copy; contract_copy = TREE_CHAIN (contract_copy))
+    emit_contract (contract_copy);
+}
+
+/* Add contract handling to the function in FNDECL.
+
+   When we have only pre-conditions, this simply prepends a call (or a direct
+   evaluation, for cdtors) to the existing function body.
+
+   When we have post conditions we build a try-finally block.
+   If the function might throw then the handler in the try-finally is an
+   EH_ELSE expression, where the post condition check is applied to the
+   non-exceptional path, and an empty statement is added to the EH path.  If
+   the function has a non-throwing eh spec, then the handler is simply the
+   post-condition checker.  */
+
+void
+maybe_apply_function_contracts (tree fndecl)
+{
+  if (!handle_contracts_p (fndecl))
+    /* We did nothing and the original function body statement list will be
+       popped by our caller.  */
+    return;
+
+  bool do_pre = has_active_preconditions (fndecl);
+  bool do_post = has_active_postconditions (fndecl);
+  /* We should not have reached here with nothing to do... */
+  gcc_checking_assert (do_pre || do_post);
+
+  /* If the function is noexcept, the user's written body will be wrapped in a
+     MUST_NOT_THROW expression.  In that case we leave the MUST_NOT_THROW in
+     place and do our replacement inside it.  */
+  tree fnbody;
+  if (TYPE_NOEXCEPT_P (TREE_TYPE (fndecl)))
+    {
+      tree m_n_t_expr = expr_first (DECL_SAVED_TREE (fndecl));
+      gcc_checking_assert (TREE_CODE (m_n_t_expr) == MUST_NOT_THROW_EXPR);
+      fnbody = TREE_OPERAND (m_n_t_expr, 0);
+      TREE_OPERAND (m_n_t_expr, 0) = push_stmt_list ();
+    }
+  else
+    {
+      fnbody = DECL_SAVED_TREE (fndecl);
+      DECL_SAVED_TREE (fndecl) = push_stmt_list ();
+    }
+
+  /* Now add the pre and post conditions to the existing function body.
+     This copies the approach used for function try blocks.  */
+  tree compound_stmt = begin_compound_stmt (0);
+  current_binding_level->artificial = true;
+
+  /* Do not add locations for the synthesised code.  */
+  location_t loc = UNKNOWN_LOCATION;
+
+  /* For other cases, we call a function to process the check.  */
+
+  /* If we have a pre, but not a post, then just emit that and we are done.  */
+  if (!do_post)
+    {
+      apply_preconditions (fndecl);
+      add_stmt (fnbody);
+      finish_compound_stmt (compound_stmt);
+      return;
+    }
+
+  if (do_pre)
+    /* Add a precondition call, if we have one. */
+    apply_preconditions (fndecl);
+  tree try_fin = build_stmt (loc, TRY_FINALLY_EXPR, fnbody, NULL_TREE);
+  add_stmt (try_fin);
+  TREE_OPERAND (try_fin, 1) = push_stmt_list ();
+  /* If we have exceptions, and a function that might throw, then add
+     an EH_ELSE clause that allows the exception to propagate upwards
+     without encountering the post-condition checks.  */
+  if (flag_exceptions && !type_noexcept_p (TREE_TYPE (fndecl)))
+    {
+      tree eh_else = build_stmt (loc, EH_ELSE_EXPR, NULL_TREE, NULL_TREE);
+      add_stmt (eh_else);
+      TREE_OPERAND (eh_else, 0) = push_stmt_list ();
+      apply_postconditions (fndecl);
+      TREE_OPERAND (eh_else, 0) = pop_stmt_list (TREE_OPERAND (eh_else, 0));
+      TREE_OPERAND (eh_else, 1) = void_node;
+    }
+  else
+    apply_postconditions (fndecl);
+  TREE_OPERAND (try_fin, 1) = pop_stmt_list (TREE_OPERAND (try_fin, 1));
+  finish_compound_stmt (compound_stmt);
+  /* The DECL_SAVED_TREE stmt list will be popped by our caller.  */
+}
+
+/* Rewrite the condition of contract in place, so that references to SRC's
+   parameters are updated to refer to DST's parameters. The postcondition
+   result variable is left unchanged.
+
+   When declarations are merged, we sometimes need to update contracts to
+   refer to new parameters.
+
+   If DUPLICATE_P is true, this is called by duplicate_decls to rewrite
+   contracts in terms of a new set of parameters.  This also preserves the
+   references to postcondition results, which are not replaced during
+   merging.  */
+
+static void
+remap_contract (tree src, tree dst, tree contract, bool duplicate_p)
+{
+  copy_body_data id;
+  hash_map<tree, tree> decl_map;
+
+  memset (&id, 0, sizeof (id));
+  id.src_fn = src;
+  id.dst_fn = dst;
+  id.src_cfun = DECL_STRUCT_FUNCTION (src);
+  id.decl_map = &decl_map;
+
+  /* If we're merging contracts, don't copy local variables.  */
+  id.copy_decl = duplicate_p ? retain_decl : copy_decl_no_change;
+
+  id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+  id.transform_new_cfg = false;
+  id.transform_return_to_modify = false;
+  id.transform_parameter = true;
+
+  /* Make sure not to unshare trees behind the front-end's back
+     since front-end specific mechanisms may rely on sharing.  */
+  id.regimplify = false;
+  id.do_not_unshare = true;
+  id.do_not_fold = true;
+
+  /* We're not inside any EH region.  */
+  id.eh_lp_nr = 0;
+
+  bool do_remap = false;
+
+  /* Insert parameter remappings.  */
+  gcc_checking_assert (TREE_CODE (src) == FUNCTION_DECL);
+  gcc_checking_assert (TREE_CODE (dst) == FUNCTION_DECL);
+
+  int src_num_artificial_args = num_artificial_parms_for (src);
+  int dst_num_artificial_args = num_artificial_parms_for (dst);
+
+  for (tree sp = DECL_ARGUMENTS (src), dp = DECL_ARGUMENTS (dst);
+       sp || dp;
+       sp = DECL_CHAIN (sp), dp = DECL_CHAIN (dp))
+    {
+      if (!sp && dp
+         && TREE_CODE (contract) == POSTCONDITION_STMT
+         && DECL_CHAIN (dp) == NULL_TREE)
+       {
+         gcc_assert (!duplicate_p);
+         if (tree result = POSTCONDITION_IDENTIFIER (contract))
+           {
+             gcc_assert (DECL_P (result));
+             insert_decl_map (&id, result, dp);
+             do_remap = true;
+           }
+         break;
+       }
+      gcc_assert (sp && dp);
+
+      if (sp == dp)
+       continue;
+
+      insert_decl_map (&id, sp, dp);
+      do_remap = true;
+
+      /* First artificial arg is *this. We want to remap that.  However, we
+        want to skip _in_charge param and __vtt_parm.  Do so now.  */
+      if (src_num_artificial_args > 0)
+       {
+         while (--src_num_artificial_args,src_num_artificial_args > 0)
+           sp = DECL_CHAIN (sp);
+       }
+      if (dst_num_artificial_args > 0)
+       {
+         while (--dst_num_artificial_args,dst_num_artificial_args > 0)
+           dp = DECL_CHAIN (dp);
+       }
+    }
+
+  if (!do_remap)
+    return;
+
+  walk_tree (&CONTRACT_CONDITION (contract), copy_tree_body_r, &id, NULL);
+}
+
+/* Returns a copy of SOURCE contracts where any references to SOURCE's
+   PARM_DECLs have been rewritten to the corresponding PARM_DECL in DEST.  */
+
+tree
+copy_and_remap_contracts (tree dest, tree source)
+{
+  tree last = NULL_TREE, contracts_copy= NULL_TREE;
+  tree contracts = get_fn_contract_specifiers (source);
+  for (; contracts; contracts = TREE_CHAIN (contracts))
+    {
+      /* The first part is copying of the legacy attribute layout - eventually
+        this will go away.  */
+      tree c = copy_node (contracts);
+      TREE_VALUE (c) = build_tree_list (TREE_PURPOSE (TREE_VALUE (c)),
+                                       copy_node (CONTRACT_STATEMENT (c)));
+      /* This is the copied contract statement.  */
+      tree stmt = CONTRACT_STATEMENT (c);
+
+      /* If we have an erroneous postcondition identifier, we also mark the
+        condition as invalid so only need to check that.  */
+      if (CONTRACT_CONDITION (stmt) != error_mark_node)
+       remap_contract (source, dest, stmt, /*duplicate_p=*/true);
+
+      if (TREE_CODE (stmt) == POSTCONDITION_STMT)
+       {
+         /* If we have a postcondition return value placeholder, then
+            ensure the copied one has the correct context.  */
+         tree var = POSTCONDITION_IDENTIFIER (stmt);
+         if (var && var != error_mark_node)
+           DECL_CONTEXT (var) = dest;
+       }
+
+      if (CONTRACT_COMMENT (stmt) != error_mark_node)
+       CONTRACT_COMMENT (stmt) = copy_node (CONTRACT_COMMENT (stmt));
+
+      chainon (last, c);
+      last = c;
+      if (!contracts_copy)
+       contracts_copy = c;
+    }
+
+  return contracts_copy;
+}
+
+/* Set the (maybe) parsed contract specifier LIST for DECL.  */
+
+void
+set_fn_contract_specifiers (tree decl, tree list)
+{
+  if (!decl || error_operand_p (decl))
+    return;
+
+  bool existed = false;
+  contract_decl& rd
+    = hash_map_safe_get_or_insert<hm_ggc> (contract_decl_map, decl, &existed);
+  if (!existed)
+    {
+      /* This is the first time we encountered this decl, save the location
+        for error messages.  This will ensure all error messages refer to the
+        contracts used for the function.  */
+      location_t decl_loc = DECL_SOURCE_LOCATION (decl);
+      location_t cont_end = decl_loc;
+      if (list)
+       cont_end = get_contract_end_loc (list);
+      rd.note_loc = make_location (decl_loc, decl_loc, cont_end);
+    }
+  rd.contract_specifiers = list;
+}
+
+/* Update the entry for DECL in the map of contract specifiers with the
+  contracts in LIST. */
+
+void
+update_fn_contract_specifiers (tree decl, tree list)
+{
+  if (!decl || error_operand_p (decl))
+    return;
+
+  bool existed = false;
+  contract_decl& rd
+    = hash_map_safe_get_or_insert<hm_ggc> (contract_decl_map, decl, &existed);
+  gcc_checking_assert (existed);
+
+  /* We should only get here when we parse deferred contracts.  */
+  gcc_checking_assert (!contract_any_deferred_p (list));
+
+  rd.contract_specifiers = list;
+}
+
+/* When a decl is about to be removed, then we need to release its content and
+   then take it out of the map.  */
+
+void
+remove_decl_with_fn_contracts_specifiers (tree decl)
+{
+  if (contract_decl *p = hash_map_safe_get (contract_decl_map, decl))
+    {
+      p->contract_specifiers = NULL_TREE;
+      contract_decl_map->remove (decl);
+    }
+}
+
+/* If this function has contract specifiers, then remove them, but leave the
+   function registered.  */
+
+void
+remove_fn_contract_specifiers (tree decl)
+{
+  if (contract_decl *p = hash_map_safe_get (contract_decl_map, decl))
+    {
+      p->contract_specifiers = NULL_TREE;
+    }
+}
+
+/* Get the contract specifier list for this DECL if there is one.  */
+
+tree
+get_fn_contract_specifiers (tree decl)
+{
+  if (contract_decl *p = hash_map_safe_get (contract_decl_map, decl))
+    return p->contract_specifiers;
+  return NULL_TREE;
+}
+
+/* A subroutine of duplicate_decls. Diagnose issues in the redeclaration of
+   guarded functions.  */
+
+void
+check_redecl_contract (tree newdecl, tree olddecl)
+{
+  if (!flag_contracts)
+    return;
+
+  if (TREE_CODE (newdecl) == TEMPLATE_DECL)
+    newdecl = DECL_TEMPLATE_RESULT (newdecl);
+  if (TREE_CODE (olddecl) == TEMPLATE_DECL)
+    olddecl = DECL_TEMPLATE_RESULT (olddecl);
+
+  tree new_contracts = get_fn_contract_specifiers (newdecl);
+  tree old_contracts = get_fn_contract_specifiers (olddecl);
+
+  if (!old_contracts && !new_contracts)
+    return;
+
+  /* We should always be comparing with the 'first' declaration which should
+   have been recorded already (if it has contract specifiers).  However
+   if the new decl is trying to add contracts, that is an error and we do
+   not want to create a map entry yet.  */
+  contract_decl *rdp = hash_map_safe_get (contract_decl_map, olddecl);
+  gcc_checking_assert(rdp || !old_contracts);
+
+  location_t new_loc = DECL_SOURCE_LOCATION (newdecl);
+  if (new_contracts && !old_contracts)
+    {
+      auto_diagnostic_group d;
+      /* If a re-declaration has contracts, they must be the same as those
+       that appear on the first declaration seen (they cannot be added).  */
+      location_t cont_end = get_contract_end_loc (new_contracts);
+      cont_end = make_location (new_loc, new_loc, cont_end);
+      error_at (cont_end, "declaration adds contracts to %q#D", olddecl);
+      inform (DECL_SOURCE_LOCATION (olddecl), "first declared here");
+      return;
+    }
+
+  if (old_contracts && !new_contracts)
+    return;
+  else if (old_contracts && new_contracts
+          && !contract_any_deferred_p (old_contracts)
+          && contract_any_deferred_p (new_contracts)
+          && DECL_UNIQUE_FRIEND_P (newdecl))
+    {
+      /* Put the defered contracts on the olddecl so we parse it when
+        we can.  */
+      set_fn_contract_specifiers (olddecl, old_contracts);
+    }
+  else if (contract_any_deferred_p (old_contracts)
+          || contract_any_deferred_p (new_contracts))
+    {
+      /* TODO: ignore these and figure out how to process them later.  */
+      /* Note that a friend declaration has deferred contracts, but the
+        declaration of the same function outside the class definition
+        doesn't.  */
+    }
+  else
+    {
+      gcc_checking_assert (old_contracts);
+      location_t cont_end = get_contract_end_loc (new_contracts);
+      cont_end = make_location (new_loc, new_loc, cont_end);
+      /* We have two sets - they should match or we issue a diagnostic.  */
+      match_contract_specifiers (rdp->note_loc, old_contracts,
+                                cont_end, new_contracts);
+    }
+
+  return;
+}
+
+/* Update the contracts of DEST to match the argument names from contracts
+  of SRC. When we merge two declarations in duplicate_decls, we preserve the
+  arguments from the new declaration, if the new declaration is a
+  definition. We need to update the contracts accordingly.  */
+
+void
+update_contract_arguments (tree srcdecl, tree destdecl)
+{
+  tree src_contracts = get_fn_contract_specifiers (srcdecl);
+  tree dest_contracts = get_fn_contract_specifiers (destdecl);
+
+  if (!src_contracts && !dest_contracts)
+    return;
+
+  /* Check if src even has contracts. It is possible that a redeclaration
+    does not have contracts. Is this is the case, first apply contracts
+    to src.  */
+  if (!src_contracts)
+    {
+      if (contract_any_deferred_p (dest_contracts))
+       {
+         set_fn_contract_specifiers (srcdecl, dest_contracts);
+         /* Nothing more to do here.  */
+         return;
+       }
+      else
+       set_fn_contract_specifiers
+         (srcdecl, copy_and_remap_contracts (srcdecl, destdecl));
+    }
+
+  /* For deferred contracts, we currently copy the tokens from the redeclaration
+    onto the decl that will be preserved. This is not ideal because the
+    redeclaration may have erroneous contracts.
+    For non deferred contracts we currently do copy and remap, which is doing
+    more than we need.  */
+  if (contract_any_deferred_p (src_contracts))
+    set_fn_contract_specifiers (destdecl, src_contracts);
+  else
+    {
+      /* Temporarily rename the arguments to get the right mapping.  */
+      tree tmp_arguments = DECL_ARGUMENTS (destdecl);
+      DECL_ARGUMENTS (destdecl) = DECL_ARGUMENTS (srcdecl);
+      set_fn_contract_specifiers (destdecl,
+                                 copy_and_remap_contracts (destdecl, srcdecl));
+      DECL_ARGUMENTS (destdecl) = tmp_arguments;
+    }
+}
+
+/* Mark most of a contract as being invalid.  */
+
+tree
+invalidate_contract (tree contract)
+{
+  if (TREE_CODE (contract) == POSTCONDITION_STMT
+      && POSTCONDITION_IDENTIFIER (contract))
+    POSTCONDITION_IDENTIFIER (contract) = error_mark_node;
+  CONTRACT_CONDITION (contract) = error_mark_node;
+  CONTRACT_COMMENT (contract) = error_mark_node;
+  return contract;
+}
+
+/* Returns an invented parameter declaration of the form 'TYPE ID' for the
+   purpose of parsing the postcondition.
+
+   We use a PARM_DECL instead of a VAR_DECL so that tsubst forces a lookup
+   in local specializations when we instantiate these things later.  */
+
+tree
+make_postcondition_variable (cp_expr id, tree type)
+{
+  if (id == error_mark_node)
+    return id;
+  gcc_checking_assert (scope_chain && scope_chain->bindings
+                      && scope_chain->bindings->kind == sk_contract);
+
+  tree decl = build_lang_decl (PARM_DECL, id, type);
+  DECL_ARTIFICIAL (decl) = true;
+  DECL_SOURCE_LOCATION (decl) = id.get_location ();
+  return pushdecl (decl);
+}
+
+/* As above, except that the type is unknown.  */
+
+tree
+make_postcondition_variable (cp_expr id)
+{
+  return make_postcondition_variable (id, make_auto ());
+}
+
+/* Check that the TYPE is valid for a named postcondition variable on
+   function decl FNDECL. Emit a diagnostic if it is not.  Returns TRUE if
+   the result is OK and false otherwise.  */
+
+bool
+check_postcondition_result (tree fndecl, tree type, location_t loc)
+{
+  /* Do not be confused by targetm.cxx.cdtor_return_this ();
+     conceptually, cdtors have no return value.  */
+  if (VOID_TYPE_P (type)
+      || DECL_CONSTRUCTOR_P (fndecl)
+      || DECL_DESTRUCTOR_P (fndecl))
+    {
+      error_at (loc,
+               DECL_CONSTRUCTOR_P (fndecl)
+               ? G_("constructor does not return a value to test")
+               : DECL_DESTRUCTOR_P (fndecl)
+               ? G_("destructor does not return a value to test")
+               : G_("function does not return a value to test"));
+      return false;
+    }
+
+  return true;
+}
+
+/* Instantiate each postcondition with the return type to finalize the
+   contract specifiers on a function decl FNDECL.  */
+
+void
+rebuild_postconditions (tree fndecl)
+{
+  if (!fndecl || fndecl == error_mark_node)
+    return;
+
+  tree type = TREE_TYPE (TREE_TYPE (fndecl));
+
+  /* If the return type is undeduced, defer until later.  */
+  if (TREE_CODE (type) == TEMPLATE_TYPE_PARM)
+    return;
+
+  tree contract_spec = get_fn_contract_specifiers (fndecl);
+  for (; contract_spec ; contract_spec = TREE_CHAIN (contract_spec))
+    {
+      tree contract = TREE_VALUE (TREE_VALUE (contract_spec));
+      if (TREE_CODE (contract) != POSTCONDITION_STMT)
+       continue;
+      tree condition = CONTRACT_CONDITION (contract);
+      if (!condition || condition == error_mark_node)
+       continue;
+
+      /* If any conditions are deferred, they're all deferred.  Note that
+        we don't have to instantiate postconditions in that case because
+        the type is available through the declaration.  */
+      if (TREE_CODE (condition) == DEFERRED_PARSE)
+       return;
+
+      tree oldvar = POSTCONDITION_IDENTIFIER (contract);
+      if (!oldvar)
+       continue;
+
+      gcc_checking_assert (!DECL_CONTEXT (oldvar)
+                          || DECL_CONTEXT (oldvar) == fndecl);
+      DECL_CONTEXT (oldvar) = fndecl;
+
+      /* Check the postcondition variable.  */
+      location_t loc = DECL_SOURCE_LOCATION (oldvar);
+      if (!check_postcondition_result (fndecl, type, loc))
+       {
+         invalidate_contract (contract);
+         continue;
+       }
+
+      /* "Instantiate" the result variable using the known type.  */
+      tree newvar = copy_node (oldvar);
+      TREE_TYPE (newvar) = type;
+
+      /* Make parameters and result available for substitution.  */
+      local_specialization_stack stack (lss_copy);
+      for (tree t = DECL_ARGUMENTS (fndecl); t != NULL_TREE; t = TREE_CHAIN (t))
+       register_local_identity (t);
+      register_local_specialization (newvar, oldvar);
+
+      begin_scope (sk_contract, fndecl);
+      bool old_pc = processing_postcondition;
+      processing_postcondition = true;
+
+      condition = tsubst_expr (condition, make_tree_vec (0),
+                              tf_warning_or_error, fndecl);
+
+      /* Update the contract condition and result.  */
+      POSTCONDITION_IDENTIFIER (contract) = newvar;
+      CONTRACT_CONDITION (contract) = finish_contract_condition (condition);
+      processing_postcondition = old_pc;
+      gcc_checking_assert (scope_chain && scope_chain->bindings
+                          && scope_chain->bindings->kind == sk_contract);
+      pop_bindings_and_leave_scope ();
+    }
+}
+
+/* Make a string of the contract condition, if it is available.  */
+
+static tree
+build_comment (cp_expr condition)
+{
+  /* Try to get the actual source text for the condition; if that fails pretty
+     print the resulting tree.  */
+  char *str = get_source_text_between (global_dc->get_file_cache (),
+                                      condition.get_start (),
+                                      condition.get_finish ());
+  if (!str)
+    {
+      const char *str = expr_to_string (condition);
+      return build_string_literal (strlen (str) + 1, str);
+    }
+
+  tree t = build_string_literal (strlen (str) + 1, str);
+  free (str);
+  return t;
+}
+
+/* Build a contract statement.  */
+
+tree
+grok_contract (tree contract_spec, tree mode, tree result, cp_expr condition,
+              location_t loc)
+{
+  if (condition == error_mark_node)
+    return error_mark_node;
+
+  tree_code code;
+  contract_assertion_kind kind = CAK_INVALID;
+  if (id_equal (contract_spec, "contract_assert"))
+    {
+      code = ASSERTION_STMT;
+      kind = CAK_ASSERT;
+    }
+  else if (id_equal (contract_spec, "pre"))
+    {
+      code = PRECONDITION_STMT;
+      kind = CAK_PRE;
+    }
+  else if (id_equal (contract_spec,"post"))
+    {
+      code = POSTCONDITION_STMT;
+      kind = CAK_POST;
+    }
+  else
+    gcc_unreachable ();
+
+  /* Build the contract. The condition is added later.  In the case that
+     the contract is deferred, result an plain identifier, not a result
+     variable.  */
+  tree contract;
+  if (code != POSTCONDITION_STMT)
+    contract = build5_loc (loc, code, void_type_node, mode,
+                          NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
+  else
+    {
+      contract = build_nt (code, mode, NULL_TREE, NULL_TREE,
+                          NULL_TREE, NULL_TREE, result);
+      TREE_TYPE (contract) = void_type_node;
+      SET_EXPR_LOCATION (contract, loc);
+    }
+
+  /* Determine the assertion kind.  */
+  CONTRACT_ASSERTION_KIND (contract) = build_int_cst (uint16_type_node, kind);
+
+  /* Determine the evaluation semantic.  This is now an override, so that if
+     not set we will get the default (currently enforce).  */
+  CONTRACT_EVALUATION_SEMANTIC (contract)
+    = build_int_cst (uint16_type_node, (uint16_t)
+                    flag_contract_evaluation_semantic);
+
+  /* If the contract is deferred, don't do anything with the condition.  */
+  if (TREE_CODE (condition) == DEFERRED_PARSE)
+    {
+      CONTRACT_CONDITION (contract) = condition;
+      return contract;
+    }
+
+  /* Generate the comment from the original condition.  */
+  CONTRACT_COMMENT (contract) = build_comment (condition);
+
+  /* The condition is converted to bool.  */
+  condition = finish_contract_condition (condition);
+
+  if (condition == error_mark_node)
+    return error_mark_node;
+
+  CONTRACT_CONDITION (contract) = condition;
+
+  return contract;
+}
+
+/* Build the contract specifier where IDENTIFIER is one of 'pre',
+   'post' or 'assert' and CONTRACT is the underlying statement.  */
+
+tree
+finish_contract_specifier (tree identifier, tree contract)
+{
+  if (contract == error_mark_node)
+    return error_mark_node;
+
+  tree contract_spec = build_tree_list (build_tree_list (NULL_TREE, identifier),
+                                       build_tree_list (NULL_TREE, contract));
+
+  /* Mark the contract as dependent if the condition is dependent.  */
+  tree condition = CONTRACT_CONDITION (contract);
+  if (TREE_CODE (condition) != DEFERRED_PARSE
+      && value_dependent_expression_p (condition))
+    ATTR_IS_DEPENDENT (contract_spec) = true;
+
+  return contract_spec;
+}
+
+/* Update condition of a late-parsed contract and postcondition variable,
+   if any.  */
+
+void
+update_late_contract (tree contract, tree result, cp_expr condition)
+{
+  if (TREE_CODE (contract) == POSTCONDITION_STMT)
+    POSTCONDITION_IDENTIFIER (contract) = result;
+
+  /* Generate the comment from the original condition.  */
+  CONTRACT_COMMENT (contract) = build_comment (condition);
+
+  /* The condition is converted to bool.  */
+  condition = finish_contract_condition (condition);
+  CONTRACT_CONDITION (contract) = condition;
+}
+
+/* ===== Code generation ===== */
+
+/* Insert a BUILT_IN_OBSERVABLE_CHECKPOINT epoch marker.  */
+
+static void
+emit_builtin_observable_checkpoint ()
+{
+  tree fn = builtin_decl_explicit (BUILT_IN_OBSERVABLE_CHKPT);
+  releasing_vec vec;
+  fn = finish_call_expr (fn, &vec, false, false, tf_warning_or_error);
+  finish_expr_stmt (fn);
+}
+
+/* Shared code between TU-local wrappers for the violation handler.  */
+
+static tree
+declare_one_violation_handler_wrapper (tree fn_name, tree fn_type,
+                                      tree p1_type, tree p2_type)
+{
+  location_t loc = BUILTINS_LOCATION;
+  tree fn_decl = build_lang_decl_loc (loc, FUNCTION_DECL, fn_name, fn_type);
+  DECL_CONTEXT (fn_decl) = FROB_CONTEXT (global_namespace);
+  DECL_ARTIFICIAL (fn_decl) = true;
+  DECL_INITIAL (fn_decl) = error_mark_node;
+  /* Let the start function code fill in the result decl.  */
+  DECL_RESULT (fn_decl) = NULL_TREE;
+  /* Two args violation ref, dynamic info.  */
+  tree parms = cp_build_parm_decl (fn_decl, NULL_TREE, p1_type);
+  TREE_USED (parms) = true;
+  DECL_READ_P (parms) = true;
+  tree p2 = cp_build_parm_decl (fn_decl, NULL_TREE, p2_type);
+  TREE_USED (p2) = true;
+  DECL_READ_P (p2) = true;
+  DECL_CHAIN (parms) = p2;
+  DECL_ARGUMENTS (fn_decl) = parms;
+  /* Make this function internal.  */
+  TREE_PUBLIC (fn_decl) = false;
+  DECL_EXTERNAL (fn_decl) = false;
+  DECL_WEAK (fn_decl) = false;
+  return fn_decl;
+}
+
+static GTY(()) tree tu_has_violation = NULL_TREE;
+static GTY(()) tree tu_has_violation_exception = NULL_TREE;
+
+static void
+maybe_declare_violation_handler_wrappers ()
+{
+  if (tu_has_violation && tu_has_violation_exception)
+    return;
+
+  iloc_sentinel ils (input_location);
+  input_location = BUILTINS_LOCATION;
+  tree v_obj_type = builtin_contract_violation_type;
+  v_obj_type = cp_build_qualified_type (v_obj_type, TYPE_QUAL_CONST);
+  v_obj_type = cp_build_reference_type (v_obj_type, /*rval*/false);
+  tree fn_type = build_function_type_list (void_type_node, v_obj_type,
+                                          uint16_type_node, NULL_TREE);
+  tree fn_name = get_identifier ("__tu_has_violation_exception");
+  tu_has_violation_exception
+    = declare_one_violation_handler_wrapper (fn_name, fn_type, v_obj_type,
+                                            uint16_type_node);
+  fn_name = get_identifier ("__tu_has_violation");
+  tu_has_violation
+    = declare_one_violation_handler_wrapper (fn_name, fn_type, v_obj_type,
+                                            uint16_type_node);
+}
+
+/* Lookup a name in std::contracts, or inject it.  */
+
+static tree
+lookup_std_contracts_type (tree name_id)
+{
+  tree id_ns = get_identifier ("contracts");
+  tree ns = lookup_qualified_name (std_node, id_ns);
+
+  tree res_type = error_mark_node;
+  if (TREE_CODE (ns) == NAMESPACE_DECL)
+    res_type = lookup_qualified_name
+      (ns, name_id, LOOK_want::TYPE | LOOK_want::HIDDEN_FRIEND);
+
+  if (TREE_CODE (res_type) == TYPE_DECL)
+    res_type = TREE_TYPE (res_type);
+  else
+    {
+      push_nested_namespace (std_node);
+      push_namespace (id_ns, /*inline*/false);
+      res_type = make_class_type (RECORD_TYPE);
+      create_implicit_typedef (name_id, res_type);
+      DECL_SOURCE_LOCATION (TYPE_NAME (res_type)) = BUILTINS_LOCATION;
+      DECL_CONTEXT (TYPE_NAME (res_type)) = current_namespace;
+      pushdecl_namespace_level (TYPE_NAME (res_type), /*hidden*/true);
+      pop_namespace ();
+      pop_nested_namespace (std_node);
+    }
+  return res_type;
+}
+
+/* Return handle_contract_violation (), declaring it if needed.  */
+
+static tree
+declare_handle_contract_violation ()
+{
+  /* We may need to declare new types, ensure they are not considered
+     attached to a named module.  */
+  auto module_kind_override = make_temp_override
+    (module_kind, module_kind & ~(MK_PURVIEW | MK_ATTACH | MK_EXPORTING));
+  tree fnname = get_identifier ("handle_contract_violation");
+  tree viol_name = get_identifier ("contract_violation");
+  tree l = lookup_qualified_name (global_namespace, fnname,
+                                 LOOK_want::HIDDEN_FRIEND);
+  for (tree f: lkp_range (l))
+    if (TREE_CODE (f) == FUNCTION_DECL)
+       {
+         tree parms = TYPE_ARG_TYPES (TREE_TYPE (f));
+         if (remaining_arguments (parms) != 1)
+           continue;
+         tree parmtype = non_reference (TREE_VALUE (parms));
+         if (CLASS_TYPE_P (parmtype)
+             && TYPE_IDENTIFIER (parmtype) == viol_name)
+           return f;
+       }
+
+  tree violation = lookup_std_contracts_type (viol_name);
+  tree fntype = NULL_TREE;
+  tree v_obj_ref = cp_build_qualified_type (violation, TYPE_QUAL_CONST);
+  v_obj_ref = cp_build_reference_type (v_obj_ref, /*rval*/false);
+  fntype = build_function_type_list (void_type_node, v_obj_ref, NULL_TREE);
+
+  push_nested_namespace (global_namespace);
+  tree fndecl
+    = build_cp_library_fn_ptr ("handle_contract_violation", fntype, ECF_COLD);
+  pushdecl_namespace_level (fndecl, /*hiding*/true);
+  pop_nested_namespace (global_namespace);
+
+  /* Build the parameter(s).  */
+  tree parms = cp_build_parm_decl (fndecl, NULL_TREE, v_obj_ref);
+  TREE_USED (parms) = true;
+  DECL_READ_P (parms) = true;
+  DECL_ARGUMENTS (fndecl) = parms;
+  return fndecl;
+}
+
+/* Build the call to handle_contract_violation for VIOLATION.  */
+
+static void
+build_contract_handler_call (tree violation)
+{
+  tree violation_fn = declare_handle_contract_violation ();
+  tree call = build_call_n (violation_fn, 1, violation);
+  finish_expr_stmt (call);
+}
+
+/* If we have emitted any contracts in this TU that will call a violation
+   handler, then emit the wrappers for the handler.  */
+
+void
+maybe_emit_violation_handler_wrappers ()
+{
+  if (!tu_has_violation && !tu_has_violation_exception)
+    return;
+
+  /* tu_has_violation */
+  start_preparsed_function (tu_has_violation, NULL_TREE,
+                           SF_DEFAULT | SF_PRE_PARSED);
+  tree body = begin_function_body ();
+  tree compound_stmt = begin_compound_stmt (BCS_FN_BODY);
+  tree v = DECL_ARGUMENTS (tu_has_violation);
+  tree semantic = DECL_CHAIN (v);
+
+  /* We are going to call the handler.  */
+  build_contract_handler_call (v);
+
+  tree if_observe = begin_if_stmt ();
+  /* if (observe) return; */
+  tree cond = build2 (EQ_EXPR, uint16_type_node, semantic,
+                     build_int_cst (uint16_type_node, (uint16_t)CES_OBSERVE));
+  finish_if_stmt_cond (cond, if_observe);
+  emit_builtin_observable_checkpoint ();
+  finish_then_clause (if_observe);
+  begin_else_clause (if_observe);
+  /* else terminate.  */
+  finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
+  finish_else_clause (if_observe);
+  finish_if_stmt (if_observe);
+  finish_return_stmt (NULL_TREE);
+
+  finish_compound_stmt (compound_stmt);
+  finish_function_body (body);
+  tu_has_violation = finish_function (false);
+  expand_or_defer_fn (tu_has_violation);
+
+  /* tu_has_violation_exception */
+  start_preparsed_function (tu_has_violation_exception, NULL_TREE,
+                           SF_DEFAULT | SF_PRE_PARSED);
+  body = begin_function_body ();
+  compound_stmt = begin_compound_stmt (BCS_FN_BODY);
+  v = DECL_ARGUMENTS (tu_has_violation_exception);
+  semantic = DECL_CHAIN (v);
+  location_t loc = DECL_SOURCE_LOCATION (tu_has_violation_exception);
+
+  tree a_type = strip_top_quals (non_reference (TREE_TYPE (v)));
+  tree v2 = build_decl (loc, VAR_DECL, NULL_TREE, a_type);
+  DECL_SOURCE_LOCATION (v2) = loc;
+  DECL_CONTEXT (v2) = current_function_decl;
+  DECL_ARTIFICIAL (v2) = true;
+  layout_decl (v2, 0);
+  v2 = pushdecl (v2);
+  add_decl_expr (v2);
+  tree r = cp_build_init_expr (v2, convert_from_reference (v));
+  finish_expr_stmt (r);
+  tree memb = lookup_member (a_type, get_identifier ("_M_detection_mode"),
+                    /*protect=*/1, /*want_type=*/0, tf_warning_or_error);
+  r = build_class_member_access_expr (v2, memb, NULL_TREE, false,
+                                     tf_warning_or_error);
+  r = cp_build_modify_expr
+   (loc, r, NOP_EXPR,
+    build_int_cst (uint16_type_node, (uint16_t)CDM_EVAL_EXCEPTION),
+    tf_warning_or_error);
+  finish_expr_stmt (r);
+  /* We are going to call the handler.  */
+  build_contract_handler_call (v);
+
+  if_observe = begin_if_stmt ();
+  /* if (observe) return; */
+  cond = build2 (EQ_EXPR, uint16_type_node, semantic,
+                build_int_cst (uint16_type_node, (uint16_t)CES_OBSERVE));
+  finish_if_stmt_cond (cond, if_observe);
+  emit_builtin_observable_checkpoint ();
+  finish_then_clause (if_observe);
+  begin_else_clause (if_observe);
+  /* else terminate.  */
+  finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
+  finish_else_clause (if_observe);
+  finish_if_stmt (if_observe);
+  finish_return_stmt (NULL_TREE);
+  finish_compound_stmt (compound_stmt);
+  finish_function_body (body);
+  tu_has_violation_exception = finish_function (false);
+  expand_or_defer_fn (tu_has_violation_exception);
+}
+
+/* Build a layout-compatible internal version of contract_violation type.  */
+
+static tree
+get_contract_violation_fields ()
+{
+  tree fields = NULL_TREE;
+  /* Must match <contracts>:
+  class contract_violation {
+    uint16_t _M_version;
+    assertion_kind _M_assertion_kind;
+    evaluation_semantic _M_evaluation_semantic;
+    detection_mode _M_detection_mode;
+    const char* _M_comment;
+    void *_M_src_loc_ptr;
+    __vendor_ext* _M_ext;
+  };
+    If this changes, also update the initializer in
+    build_contract_violation.  */
+  const tree types[] = { uint16_type_node,
+                        uint16_type_node,
+                        uint16_type_node,
+                        uint16_type_node,
+                        const_string_type_node,
+                        ptr_type_node,
+                        ptr_type_node
+                       };
+ const char *names[] = { "_M_version",
+                        "_M_assertion_kind",
+                        "_M_evaluation_semantic",
+                        "_M_detection_mode",
+                        "_M_comment",
+                        "_M_src_loc_ptr",
+                        "_M_ext",
+                       };
+  unsigned n = 0;
+  for (tree type : types)
+    {
+      /* finish_builtin_struct wants fields chained in reverse.  */
+      tree next = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+                                 get_identifier(names[n++]), type);
+      DECL_CHAIN (next) = fields;
+      fields = next;
+    }
+ return fields;
+}
+
+/* Build a type to represent contract violation objects.  */
+
+static tree
+init_builtin_contract_violation_type ()
+{
+  if (builtin_contract_violation_type)
+    return builtin_contract_violation_type;
+
+  tree fields = get_contract_violation_fields ();
+
+  iloc_sentinel ils (input_location);
+  input_location = BUILTINS_LOCATION;
+  builtin_contract_violation_type = make_class_type (RECORD_TYPE);
+  finish_builtin_struct (builtin_contract_violation_type,
+                        "__builtin_contract_violation_type", fields, NULL_TREE);
+  CLASSTYPE_AS_BASE (builtin_contract_violation_type)
+    = builtin_contract_violation_type;
+  DECL_CONTEXT (TYPE_NAME (builtin_contract_violation_type))
+    = FROB_CONTEXT (global_namespace);
+  CLASSTYPE_LITERAL_P (builtin_contract_violation_type) = true;
+  CLASSTYPE_LAZY_COPY_CTOR (builtin_contract_violation_type) = true;
+  xref_basetypes (builtin_contract_violation_type, /*bases=*/NULL_TREE);
+  DECL_CONTEXT (TYPE_NAME (builtin_contract_violation_type))
+    = FROB_CONTEXT (global_namespace);
+  DECL_ARTIFICIAL (TYPE_NAME (builtin_contract_violation_type)) = true;
+  TYPE_ARTIFICIAL (builtin_contract_violation_type) = true;
+  builtin_contract_violation_type
+    = cp_build_qualified_type (builtin_contract_violation_type,
+                              TYPE_QUAL_CONST);
+  return builtin_contract_violation_type;
+}
+
+/* Early initialisation of types and functions we will use.  */
+void
+init_contracts ()
+{
+  init_terminate_fn ();
+  init_builtin_contract_violation_type ();
+}
+
+static GTY(()) tree contracts_source_location_impl_type;
+
+/* Build a layout-compatible internal version of source location __impl
+   type.  */
+
+static tree
+get_contracts_source_location_impl_type (tree context = NULL_TREE)
+{
+  if (contracts_source_location_impl_type)
+     return contracts_source_location_impl_type;
+
+  /* First see if we have a declaration that we can use.  */
+  tree contracts_source_location_type
+    = lookup_std_type (get_identifier ("source_location"));
+
+  if (contracts_source_location_type
+      && contracts_source_location_type != error_mark_node
+      && TYPE_FIELDS (contracts_source_location_type))
+    {
+      contracts_source_location_impl_type = get_source_location_impl_type ();
+      return contracts_source_location_impl_type;
+    }
+
+  /* We do not, so build the __impl layout equivalent type, which must
+     match <source_location>:
+     struct __impl
+      {
+         const char* _M_file_name;
+         const char* _M_function_name;
+         unsigned _M_line;
+         unsigned _M_column;
+      }; */
+  const tree types[] = { const_string_type_node,
+                       const_string_type_node,
+                       uint_least32_type_node,
+                       uint_least32_type_node };
+
+ const char *names[] = { "_M_file_name",
+                        "_M_function_name",
+                        "_M_line",
+                        "_M_column",
+                       };
+  tree fields = NULL_TREE;
+  unsigned n = 0;
+  for (tree type : types)
+  {
+    /* finish_builtin_struct wants fields chained in reverse.  */
+    tree next = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+                           get_identifier (names[n++]), type);
+    DECL_CHAIN (next) = fields;
+    fields = next;
+  }
+
+  iloc_sentinel ils (input_location);
+  input_location = BUILTINS_LOCATION;
+  contracts_source_location_impl_type = cxx_make_type (RECORD_TYPE);
+  finish_builtin_struct (contracts_source_location_impl_type,
+                        "__impl", fields, NULL_TREE);
+  DECL_CONTEXT (TYPE_NAME (contracts_source_location_impl_type)) = context;
+  DECL_ARTIFICIAL (TYPE_NAME (contracts_source_location_impl_type)) = true;
+  TYPE_ARTIFICIAL (contracts_source_location_impl_type) = true;
+  contracts_source_location_impl_type
+    = cp_build_qualified_type (contracts_source_location_impl_type,
+                              TYPE_QUAL_CONST);
+
+  return contracts_source_location_impl_type;
+}
+
+static tree
+get_src_loc_impl_ptr (location_t loc)
+{
+  if (!contracts_source_location_impl_type)
+    get_contracts_source_location_impl_type ();
+
+  tree fndecl = current_function_decl;
+
+  gcc_checking_assert (fndecl);
+  tree impl__
+    = build_source_location_impl (loc, fndecl,
+                                 contracts_source_location_impl_type);
+  tree p = build_pointer_type (contracts_source_location_impl_type);
+  return build_fold_addr_expr_with_type_loc (loc, impl__, p);
+}
+
+/* Build a contract_violation layout compatible object. */
+
+/* Constructor.  At present, this should always be constant. */
+
+static tree
+build_contract_violation_ctor (tree contract)
+{
+  bool can_be_const = true;
+  uint16_t version = 1;
+  /* Default CDM_PREDICATE_FALSE. */
+  uint16_t detection_mode = CDM_PREDICATE_FALSE;
+
+  tree assertion_kind = CONTRACT_ASSERTION_KIND (contract);
+  if (!assertion_kind || really_constant_p (assertion_kind))
+    {
+      contract_assertion_kind kind = get_contract_assertion_kind (contract);
+      assertion_kind = build_int_cst (uint16_type_node, kind);
+    }
+  else
+    can_be_const = false;
+
+  tree eval_semantic = CONTRACT_EVALUATION_SEMANTIC (contract);
+  gcc_checking_assert (eval_semantic);
+  if (!really_constant_p (eval_semantic))
+    can_be_const = false;
+
+  tree comment = CONTRACT_COMMENT (contract);
+  if (comment && !really_constant_p (comment))
+    can_be_const = false;
+
+  tree std_src_loc_impl_ptr = CONTRACT_STD_SOURCE_LOC (contract);
+  if (std_src_loc_impl_ptr)
+    {
+      std_src_loc_impl_ptr = convert_from_reference (std_src_loc_impl_ptr);
+      if (!really_constant_p (std_src_loc_impl_ptr))
+       can_be_const = false;
+    }
+  else
+    std_src_loc_impl_ptr = get_src_loc_impl_ptr (EXPR_LOCATION (contract));
+
+  /* Must match the type layout in builtin_contract_violation_type.  */
+  tree f0 = next_aggregate_field (TYPE_FIELDS (builtin_contract_violation_type));
+  tree f1 = next_aggregate_field (DECL_CHAIN (f0));
+  tree f2 = next_aggregate_field (DECL_CHAIN (f1));
+  tree f3 = next_aggregate_field (DECL_CHAIN (f2));
+  tree f4 = next_aggregate_field (DECL_CHAIN (f3));
+  tree f5 = next_aggregate_field (DECL_CHAIN (f4));
+  tree f6 = next_aggregate_field (DECL_CHAIN (f5));
+  tree ctor = build_constructor_va
+    (builtin_contract_violation_type, 7,
+     f0, build_int_cst (uint16_type_node, version),
+     f1, assertion_kind,
+     f2, eval_semantic,
+     f3, build_int_cst (uint16_type_node, detection_mode),
+     f4, comment,
+     f5, std_src_loc_impl_ptr,
+     f6, build_zero_cst (nullptr_type_node)); // __vendor_ext
+
+  TREE_READONLY (ctor) = true;
+  if (can_be_const)
+    TREE_CONSTANT (ctor) = true;
+
+  return ctor;
+}
+
+/* Build a named TU-local constant of TYPE.  */
+
+static tree
+contracts_tu_local_named_var (location_t loc, const char *name, tree type)
+{
+  tree var_ = build_decl (loc, VAR_DECL, NULL, type);
+  DECL_NAME (var_) = generate_internal_label (name);
+  TREE_PUBLIC (var_) = false;
+  DECL_EXTERNAL (var_) = false;
+  TREE_STATIC (var_) = true;
+  /* Compiler-generated.  */
+  DECL_ARTIFICIAL (var_) = true;
+  TREE_CONSTANT (var_) = true;
+  layout_decl (var_, 0);
+  return var_;
+}
+
+/* Create a read-only violation object.  */
+
+static tree
+build_contract_violation_constant (tree ctor, tree contract)
+{
+  tree viol_ = contracts_tu_local_named_var
+    (EXPR_LOCATION (contract), "Lcontract_violation",
+     builtin_contract_violation_type);
+
+  TREE_CONSTANT (viol_) = true;
+  DECL_INITIAL (viol_) = ctor;
+  varpool_node::finalize_decl (viol_);
+
+  return viol_;
+}
+
+/* Helper to replace references to dummy this parameters with references to
+   the first argument of the FUNCTION_DECL DATA.  */
+
+static tree
+remap_dummy_this_1 (tree *tp, int *, void *data)
+{
+  if (!is_this_parameter (*tp))
+    return NULL_TREE;
+  tree fn = (tree)data;
+  *tp = DECL_ARGUMENTS (fn);
+  return NULL_TREE;
+}
+
+/* Replace all references to dummy this parameters in EXPR with references to
+   the first argument of the FUNCTION_DECL FNDECL.  */
+
+static void
+remap_dummy_this (tree fndecl, tree *expr)
+{
+  walk_tree (expr, remap_dummy_this_1, fndecl, NULL);
+}
+
+/* Replace uses of user's placeholder var with the actual return value.  */
+
+struct replace_tree
+{
+  tree from, to;
+};
+
+static tree
+remap_retval_1 (tree *here, int *do_subtree, void *d)
+{
+  replace_tree *data = (replace_tree *) d;
+
+  if (*here == data->from)
+    {
+      *here = data->to;
+      *do_subtree = 0;
+    }
+  else
+    *do_subtree = 1;
+  return NULL_TREE;
+}
+
+static void
+remap_retval (tree fndecl, tree contract)
+{
+  struct replace_tree data;
+  data.from = POSTCONDITION_IDENTIFIER (contract);
+  gcc_checking_assert (DECL_RESULT (fndecl));
+  data.to = DECL_RESULT (fndecl);
+  walk_tree (&CONTRACT_CONDITION (contract), remap_retval_1, &data, NULL);
+}
+
+
+/* Genericize a CONTRACT tree, but do not attach it to the current context,
+   the caller is responsible for that.
+   This is called during genericization.  */
+
+tree
+build_contract_check (tree contract)
+{
+  contract_evaluation_semantic semantic = get_evaluation_semantic (contract);
+  bool quick = false;
+  bool calls_handler = false;
+  switch (semantic)
+    {
+    case CES_IGNORE:
+      return void_node;
+    case CES_ENFORCE:
+    case CES_OBSERVE:
+      calls_handler = true;
+      break;
+    case CES_QUICK:
+      quick = true;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  location_t loc = EXPR_LOCATION (contract);
+
+  remap_dummy_this (current_function_decl, &CONTRACT_CONDITION (contract));
+  tree condition = CONTRACT_CONDITION (contract);
+  if (condition == error_mark_node)
+    return NULL_TREE;
+
+  if (POSTCONDITION_P (contract))
+    {
+      remap_retval (current_function_decl, contract);
+      condition = CONTRACT_CONDITION (contract);
+      if (condition == error_mark_node)
+       return NULL_TREE;
+    }
+
+  if (calls_handler)
+    maybe_declare_violation_handler_wrappers ();
+
+  bool check_might_throw = (flag_exceptions
+                           && !expr_noexcept_p (condition, tf_none));
+
+  /* Build a statement expression to hold a contract check, with the check
+     potentially wrapped in a try-catch expr.  */
+  tree cc_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL);
+  BIND_EXPR_BODY (cc_bind) = push_stmt_list ();
+
+  if (TREE_CODE (contract) == ASSERTION_STMT)
+    emit_builtin_observable_checkpoint ();
+  tree cond = build_x_unary_op (loc, TRUTH_NOT_EXPR, condition, NULL_TREE,
+                               tf_warning_or_error);
+  tree violation;
+  bool viol_is_var = false;
+  if (quick)
+    /* We will not be calling a handler.  */
+    violation = build_zero_cst (nullptr_type_node);
+  else
+    {
+      /* Build a violation object, with the contract settings.  */
+      tree ctor = build_contract_violation_ctor (contract);
+      gcc_checking_assert (TREE_CONSTANT (ctor));
+      violation = build_contract_violation_constant (ctor, contract);
+      violation = build_address (violation);
+    }
+
+  tree s_const = build_int_cst (uint16_type_node, semantic);
+  /* So now do we need a try-catch?  */
+  if (check_might_throw)
+    {
+      /* This will hold the computed condition.  */
+      tree check_failed = build_decl (loc, VAR_DECL, NULL, boolean_type_node);
+      DECL_ARTIFICIAL (check_failed) = true;
+      DECL_IGNORED_P (check_failed) = true;
+      DECL_CONTEXT (check_failed) = current_function_decl;
+      layout_decl (check_failed, 0);
+      add_decl_expr (check_failed);
+      DECL_CHAIN (check_failed) = BIND_EXPR_VARS (cc_bind);
+      BIND_EXPR_VARS (cc_bind) = check_failed;
+      tree check_try = begin_try_block ();
+      finish_expr_stmt (cp_build_init_expr (check_failed, cond));
+      finish_try_block (check_try);
+
+      tree handler = begin_handler ();
+      finish_handler_parms (NULL_TREE, handler); /* catch (...) */
+      if (quick)
+       finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
+      else
+       {
+         if (viol_is_var)
+           {
+             /* We can update the detection mode here.  */
+             tree memb
+               = lookup_member (builtin_contract_violation_type,
+                                get_identifier ("_M_detection_mode"),
+                                1, 0, tf_warning_or_error);
+             tree r = cp_build_indirect_ref (loc, violation, RO_UNARY_STAR,
+                                             tf_warning_or_error);
+             r = build_class_member_access_expr (r, memb, NULL_TREE, false,
+                                                 tf_warning_or_error);
+             r = cp_build_modify_expr
+               (loc, r, NOP_EXPR,
+                build_int_cst (uint16_type_node, (uint16_t)CDM_EVAL_EXCEPTION),
+                tf_warning_or_error);
+             finish_expr_stmt (r);
+             finish_expr_stmt (build_call_n (tu_has_violation, 2,
+                                             violation, s_const));
+           }
+         else
+           /* We need to make a copy of the violation object to update.  */
+           finish_expr_stmt (build_call_n (tu_has_violation_exception, 2,
+                                           violation, s_const));
+         /* If we reach here, we have handled the exception thrown and do not
+            need further action.  */
+         tree e = cp_build_modify_expr (loc, check_failed, NOP_EXPR,
+                                        boolean_false_node,
+                                        tf_warning_or_error);
+         finish_expr_stmt (e);
+       }
+      finish_handler (handler);
+      finish_handler_sequence (check_try);
+      cond = check_failed;
+      BIND_EXPR_VARS (cc_bind) = nreverse (BIND_EXPR_VARS (cc_bind));
+    }
+
+  tree do_check = begin_if_stmt ();
+  finish_if_stmt_cond (cond, do_check);
+  if (quick)
+    finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
+  else
+    finish_expr_stmt (build_call_n (tu_has_violation, 2, violation, s_const));
+  finish_then_clause (do_check);
+  finish_if_stmt (do_check);
+
+  BIND_EXPR_BODY (cc_bind) = pop_stmt_list (BIND_EXPR_BODY (cc_bind));
+  return cc_bind;
+}
+
+#include "gt-cp-contracts.h"
index 5b4b2ffce1dddd8e0ea328f5352bc3f61d03a689..67ba65a1752d365acded9345d291c6ca2171b51f 100644 (file)
@@ -1,7 +1,12 @@
-/* Definitions for C++ contract levels.  Implements functionality described in
-   the N4820 working draft version of contracts, P1290, P1332, and P1429.
+/* Definitions for C++26 contracts.
+
    Copyright (C) 2020-2026 Free Software Foundation, Inc.
-   Contributed by Jeff Chapman II (jchapman@lock3software.com)
+   Originally by Jeff Chapman II (jchapman@lock3software.com) for proposed
+   C++20 contracts.
+   Rewritten for C++26 contracts by:
+     Nina Ranns (dinka.ranns@googlemail.com)
+     Iain Sandoe (iain@sandoe.co.uk)
+     Ville Voutilainen (ville.voutilainen@gmail.com).
 
 This file is part of GCC.
 
@@ -22,5 +27,167 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_CP_CONTRACT_H
 #define GCC_CP_CONTRACT_H
 
+#include <cstdint>
+
+/* Contract assertion kind */
+/* Must match relevant enums in <contracts> header  */
+
+enum contract_assertion_kind : uint16_t {
+  CAK_INVALID = 0 ,
+  CAK_PRE = 1 ,
+  CAK_POST = 2 ,
+  CAK_ASSERT = 3,
+};
+
+/* Per P2900R14 + D3290R3 + extensions.  */
+enum contract_evaluation_semantic : uint16_t {
+  CES_INVALID = 0,
+  CES_IGNORE = 1,
+  CES_OBSERVE = 2,
+  CES_ENFORCE = 3,
+  CES_QUICK = 4,
+};
+
+enum detection_mode : uint16_t {
+  CDM_UNSPECIFIED = 0,
+  CDM_PREDICATE_FALSE = 1,
+  CDM_EVAL_EXCEPTION = 2
+};
+
+/* Contract evaluation_semantic */
+#define CONTRACT_EVALUATION_SEMANTIC(NODE) \
+  (TREE_OPERAND (CONTRACT_CHECK (NODE), 0))
+
+#define CONTRACT_ASSERTION_KIND(NODE) \
+  (TREE_OPERAND (CONTRACT_CHECK (NODE), 1))
+
+#define CONTRACT_CHECK(NODE) \
+  (TREE_CHECK3 (NODE, ASSERTION_STMT, PRECONDITION_STMT, POSTCONDITION_STMT))
+
+/* True if NODE is any kind of contract.  */
+#define CONTRACT_P(NODE)                       \
+  (TREE_CODE (NODE) == ASSERTION_STMT          \
+   || TREE_CODE (NODE) == PRECONDITION_STMT    \
+   || TREE_CODE (NODE) == POSTCONDITION_STMT)
+
+/* True if NODE is a contract condition.  */
+#define CONTRACT_CONDITION_P(NODE)             \
+  (TREE_CODE (NODE) == PRECONDITION_STMT       \
+   || TREE_CODE (NODE) == POSTCONDITION_STMT)
+
+/* True if NODE is a precondition.  */
+#define PRECONDITION_P(NODE)           \
+  (TREE_CODE (NODE) == PRECONDITION_STMT)
+
+/* True if NODE is a postcondition.  */
+#define POSTCONDITION_P(NODE)          \
+  (TREE_CODE (NODE) == POSTCONDITION_STMT)
+
+/* True iff the FUNCTION_DECL NODE currently has any contracts.  */
+#define DECL_HAS_CONTRACTS_P(NODE) \
+  (get_fn_contract_specifiers (NODE) != NULL_TREE)
+
+/* The wrapper of the original source location of a list of contracts.  */
+#define CONTRACT_SOURCE_LOCATION_WRAPPER(NODE) \
+  (TREE_PURPOSE (TREE_VALUE (NODE)))
+
+/* The original source location of a list of contracts.  */
+#define CONTRACT_SOURCE_LOCATION(NODE) \
+  (EXPR_LOCATION (CONTRACT_SOURCE_LOCATION_WRAPPER (NODE)))
+
+/* The actual code _STMT for a contract specifier.  */
+#define CONTRACT_STATEMENT(NODE) \
+  (TREE_VALUE (TREE_VALUE (NODE)))
+
+/* The parsed condition of the contract.  */
+#define CONTRACT_CONDITION(NODE) \
+  (TREE_OPERAND (CONTRACT_CHECK (NODE), 2))
+
+/* True iff the condition of the contract NODE is not yet parsed.  */
+#define CONTRACT_CONDITION_DEFERRED_P(NODE) \
+  (TREE_CODE (CONTRACT_CONDITION (NODE)) == DEFERRED_PARSE)
+
+/* The raw comment of the contract.  */
+#define CONTRACT_COMMENT(NODE) \
+  (TREE_OPERAND (CONTRACT_CHECK (NODE), 3))
+
+/* A std::source_location, if provided.  */
+#define CONTRACT_STD_SOURCE_LOC(NODE) \
+  (TREE_OPERAND (CONTRACT_CHECK (NODE), 4))
+
+/* The VAR_DECL of a postcondition result. For deferred contracts, this
+   is an IDENTIFIER.  */
+#define POSTCONDITION_IDENTIFIER(NODE) \
+  (TREE_OPERAND (POSTCONDITION_STMT_CHECK (NODE), 5))
+
+/* contracts.cc */
+
+extern void init_contracts                     (void);
+
+extern tree grok_contract                      (tree, tree, tree, cp_expr, location_t);
+extern tree finish_contract_specifier          (tree, tree);
+extern tree finish_contract_condition          (cp_expr);
+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 void set_fn_contract_specifiers         (tree, tree);
+extern void update_fn_contract_specifiers      (tree, tree);
+extern tree get_fn_contract_specifiers         (tree);
+extern void remove_decl_with_fn_contracts_specifiers (tree);
+extern void remove_fn_contract_specifiers      (tree);
+extern void update_contract_arguments          (tree, tree);
+
+extern tree make_postcondition_variable                (cp_expr);
+extern tree make_postcondition_variable                (cp_expr, tree);
+extern void check_param_in_postcondition       (tree, location_t);
+extern void check_postconditions_in_redecl     (tree, tree);
+extern void maybe_update_postconditions                (tree);
+extern void rebuild_postconditions             (tree);
+extern bool check_postcondition_result         (tree, tree, location_t);
+
+extern bool contract_any_deferred_p            (tree);
+
+extern void start_function_contracts           (tree);
+extern void maybe_apply_function_contracts     (tree);
+
+extern void maybe_emit_violation_handler_wrappers (void);
+
+extern tree build_contract_check               (tree);
+
+inline void
+set_decl_contracts (tree decl, tree contract_attrs)
+{
+  set_fn_contract_specifiers (decl, contract_attrs);
+}
+
+/* TODO : decide if we should push the tests into contracts.cc  */
+extern contract_evaluation_semantic get_evaluation_semantic (const_tree);
+
+/* Will this contract be ignored.  */
+
+inline bool
+contract_ignored_p (const_tree contract)
+{
+  return (get_evaluation_semantic (contract) <= CES_IGNORE);
+}
+
+/* Will this contract be evaluated?  */
+
+inline bool
+contract_evaluated_p (const_tree contract)
+{
+  return (get_evaluation_semantic (contract) >= CES_OBSERVE);
+}
+
+/* Is the contract terminating?  */
+
+inline bool
+contract_terminating_p (const_tree contract)
+{
+  return (get_evaluation_semantic (contract) == CES_ENFORCE
+         || get_evaluation_semantic (contract) == CES_QUICK);
+}
 
 #endif /* ! GCC_CP_CONTRACT_H */
index ec8fb1fd2a7918069ead68764d3d4b1f53386cbf..38cee83ab763494bed315d4312664a6d7ae4b5d9 100644 (file)
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "omp-general.h"
 #include "opts.h"
 #include "gcc-urlifier.h"
+#include "contracts.h" // build_contract_check ()
 
 /* Keep track of forward references to immediate-escalating functions in
    case they become consteval.  This vector contains ADDR_EXPRs and
@@ -1044,7 +1045,7 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
   return ret;
 }
 
-static inline bool
+bool
 is_invisiref_parm (const_tree t)
 {
   return ((TREE_CODE (t) == PARM_DECL || TREE_CODE (t) == RESULT_DECL)
@@ -2122,6 +2123,20 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
       wtd->bind_expr_stack.pop ();
       break;
 
+    case ASSERTION_STMT:
+    case PRECONDITION_STMT:
+    case POSTCONDITION_STMT:
+      if (tree check = build_contract_check (stmt))
+       {
+         *stmt_p = check;
+         return cp_genericize_r (stmt_p, walk_subtrees, data);
+       }
+      /* If we didn't build a check, replace it with void_node so we don't
+        leak contracts into GENERIC.  */
+      *stmt_p = void_node;
+      *walk_subtrees = 0;
+      break;
+
     case USING_STMT:
       {
        tree block = NULL_TREE;
@@ -4251,20 +4266,12 @@ struct source_location_table_entry_hash
 static GTY(()) hash_table <source_location_table_entry_hash>
   *source_location_table;
 
-/* Fold the __builtin_source_location () call T.  */
+/* Build a std::source_location::__impl from a location_t.  */
 
 tree
-fold_builtin_source_location (const_tree t)
+build_source_location_impl (location_t loc, tree fndecl,
+                           tree source_location_impl)
 {
-  gcc_assert (TREE_CODE (t) == CALL_EXPR);
-  /* TREE_TYPE (t) is const std::source_location::__impl*  */
-  tree source_location_impl = TREE_TYPE (TREE_TYPE (t));
-  if (source_location_impl == error_mark_node)
-    return build_zero_cst (const_ptr_type_node);
-  gcc_assert (CLASS_TYPE_P (source_location_impl)
-             && id_equal (TYPE_IDENTIFIER (source_location_impl), "__impl"));
-
-  location_t loc = EXPR_LOCATION (t);
   if (source_location_table == NULL)
     source_location_table
       = hash_table <source_location_table_entry_hash>::create_ggc (64);
@@ -4273,78 +4280,94 @@ fold_builtin_source_location (const_tree t)
   entry.loc
     = linemap_resolve_location (line_table, loc, LRK_MACRO_EXPANSION_POINT,
                                &map);
-  entry.uid = current_function_decl ? DECL_UID (current_function_decl) : -1;
+  entry.uid = fndecl ? DECL_UID (fndecl) : -1;
   entry.var = error_mark_node;
   source_location_table_entry *entryp
     = source_location_table->find_slot (entry, INSERT);
-  tree var;
+
   if (entryp->var)
-    var = entryp->var;
-  else
+    return entryp->var;
+
+  tree var = build_decl (loc, VAR_DECL, generate_internal_label ("Lsrc_loc"),
+                        source_location_impl);
+  TREE_STATIC (var) = 1;
+  TREE_PUBLIC (var) = 0;
+  DECL_ARTIFICIAL (var) = 1;
+  DECL_IGNORED_P (var) = 1;
+  DECL_EXTERNAL (var) = 0;
+  DECL_DECLARED_CONSTEXPR_P (var) = 1;
+  DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (var) = 1;
+  layout_decl (var, 0);
+
+  vec<constructor_elt, va_gc> *v = NULL;
+  vec_alloc (v, 4);
+  for (tree field = TYPE_FIELDS (source_location_impl);
+       (field = next_aggregate_field (field)) != NULL_TREE;
+       field = DECL_CHAIN (field))
     {
-      var = build_decl (loc, VAR_DECL, generate_internal_label ("Lsrc_loc"),
-                       source_location_impl);
-      TREE_STATIC (var) = 1;
-      TREE_PUBLIC (var) = 0;
-      DECL_ARTIFICIAL (var) = 1;
-      DECL_IGNORED_P (var) = 1;
-      DECL_EXTERNAL (var) = 0;
-      DECL_DECLARED_CONSTEXPR_P (var) = 1;
-      DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (var) = 1;
-      layout_decl (var, 0);
-
-      vec<constructor_elt, va_gc> *v = NULL;
-      vec_alloc (v, 4);
-      for (tree field = TYPE_FIELDS (source_location_impl);
-          (field = next_aggregate_field (field)) != NULL_TREE;
-          field = DECL_CHAIN (field))
+      const char *n = IDENTIFIER_POINTER (DECL_NAME (field));
+      tree val = NULL_TREE;
+      if (strcmp (n, "_M_file_name") == 0)
        {
-         const char *n = IDENTIFIER_POINTER (DECL_NAME (field));
-         tree val = NULL_TREE;
-         if (strcmp (n, "_M_file_name") == 0)
+         if (const char *fname = LOCATION_FILE (loc))
            {
-             if (const char *fname = LOCATION_FILE (loc))
-               {
-                 fname = remap_macro_filename (fname);
-                 val = build_string_literal (fname);
-               }
-             else
-               val = build_string_literal ("");
+             fname = remap_macro_filename (fname);
+             val = build_string_literal (fname);
            }
-         else if (strcmp (n, "_M_function_name") == 0)
-           {
-             const char *name = "";
-
-             if (current_function_decl)
-               {
-                 /* If this is a coroutine, we should get the name of the user
-                    function rather than the actor we generate.  */
-                 if (tree ramp = DECL_RAMP_FN (current_function_decl))
-                   name = cxx_printable_name (ramp, 2);
-                 else
-                   name = cxx_printable_name (current_function_decl, 2);
-               }
-
-             val = build_string_literal (name);
-           }
-         else if (strcmp (n, "_M_line") == 0)
-           val = build_int_cst (TREE_TYPE (field), LOCATION_LINE (loc));
-         else if (strcmp (n, "_M_column") == 0)
-           val = build_int_cst (TREE_TYPE (field), LOCATION_COLUMN (loc));
          else
-           gcc_unreachable ();
-         CONSTRUCTOR_APPEND_ELT (v, field, val);
+           val = build_string_literal ("");
        }
+      else if (strcmp (n, "_M_function_name") == 0)
+       {
+         const char *name = "";
 
-      tree ctor = build_constructor (source_location_impl, v);
-      TREE_CONSTANT (ctor) = 1;
-      TREE_STATIC (ctor) = 1;
-      DECL_INITIAL (var) = ctor;
-      varpool_node::finalize_decl (var);
-      *entryp = entry;
-      entryp->var = var;
+         if (fndecl)
+           {
+             /* If this is a coroutine, we should get the name of the user
+                function rather than the actor we generate.  */
+             if (tree ramp = DECL_RAMP_FN (fndecl))
+               name = cxx_printable_name (ramp, 2);
+             else
+               name = cxx_printable_name (fndecl, 2);
+           }
+
+         val = build_string_literal (name);
+       }
+      else if (strcmp (n, "_M_line") == 0)
+       val = build_int_cst (TREE_TYPE (field), LOCATION_LINE (loc));
+      else if (strcmp (n, "_M_column") == 0)
+       val = build_int_cst (TREE_TYPE (field), LOCATION_COLUMN (loc));
+      else
+       gcc_unreachable ();
+      CONSTRUCTOR_APPEND_ELT (v, field, val);
     }
 
+  tree ctor = build_constructor (source_location_impl, v);
+  TREE_CONSTANT (ctor) = 1;
+  TREE_STATIC (ctor) = 1;
+  DECL_INITIAL (var) = ctor;
+  varpool_node::finalize_decl (var);
+  *entryp = entry;
+  entryp->var = var;
+  return var;
+}
+
+/* Fold the __builtin_source_location () call T.  */
+
+tree
+fold_builtin_source_location (const_tree t)
+{
+  gcc_assert (TREE_CODE (t) == CALL_EXPR);
+  /* TREE_TYPE (t) is const std::source_location::__impl*  */
+  tree source_location_impl = TREE_TYPE (TREE_TYPE (t));
+  if (source_location_impl == error_mark_node)
+    return build_zero_cst (const_ptr_type_node);
+  gcc_assert (CLASS_TYPE_P (source_location_impl)
+             && id_equal (TYPE_IDENTIFIER (source_location_impl), "__impl"));
+
+  location_t loc = EXPR_LOCATION (t);
+  tree var = build_source_location_impl (loc, current_function_decl,
+                                        source_location_impl);
   return build_fold_addr_expr_with_type_loc (loc, var, TREE_TYPE (t));
 }
 
index 770a681e124d27fa9ef984f663dd89d67e700114..3826b143a96714eb8530ee6b42ca74ede3f2fa92 100644 (file)
@@ -576,9 +576,9 @@ DEFTREECODE (CO_RETURN_EXPR, "co_return", tcc_statement, 2)
    operand to store the optional name for the result value.
 
    CONTRACT_SEMANTIC has the computed behavior of the contract.  */
-DEFTREECODE (ASSERTION_STMT, "assertion_stmt", tcc_statement, 3)
-DEFTREECODE (PRECONDITION_STMT, "precondition_stmt", tcc_statement, 3)
-DEFTREECODE (POSTCONDITION_STMT, "postcondition_stmt", tcc_statement, 4)
+DEFTREECODE (ASSERTION_STMT, "assertion_stmt", tcc_statement, 5)
+DEFTREECODE (PRECONDITION_STMT, "precondition_stmt", tcc_statement, 5)
+DEFTREECODE (POSTCONDITION_STMT, "postcondition_stmt", tcc_statement, 6)
 
 /* A reference to a translation-unit local entity.
 
index 2046818301f866c2c5423fc6f343bdb4150fd739..ef66390568074bd95086bd5a5914ed8a339f7fe1 100644 (file)
@@ -237,6 +237,7 @@ enum cp_tree_index
     CPTI_DSO_HANDLE,
     CPTI_DCAST,
     CPTI_META_INFO_TYPE,
+    CPTI_CONTRACT_VIOLATION_TYPE,
 
     CPTI_MAX
 };
@@ -268,6 +269,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
 /* std::align_val_t */
 #define align_type_node                        cp_global_trees[CPTI_ALIGN_TYPE]
 #define meta_info_type_node            cp_global_trees[CPTI_META_INFO_TYPE]
+#define builtin_contract_violation_type        cp_global_trees[CPTI_CONTRACT_VIOLATION_TYPE]
 
 /* We cache these tree nodes so as to call get_identifier less frequently.
    For identifiers for functions, including special member functions such
@@ -529,6 +531,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       LOOKUP_FOUND_P (in RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, NAMESPACE_DECL)
       FNDECL_MANIFESTLY_CONST_EVALUATED (in FUNCTION_DECL)
       TARGET_EXPR_INTERNAL_P (in TARGET_EXPR)
+      CONTRACT_CONST (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
    5: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
       FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
@@ -2071,10 +2074,14 @@ struct GTY(()) saved_scope {
   tree x_current_class_ptr;
   tree x_current_class_ref;
 
+  /* Only used for uses of this in contract assertion.  */
+  tree x_contract_class_ptr;
+
   int x_processing_template_decl;
   int x_processing_specialization;
   int x_processing_constraint;
   int suppress_location_wrappers;
+  BOOL_BITFIELD x_processing_postcondition : 1;
   BOOL_BITFIELD x_processing_explicit_instantiation : 1;
   BOOL_BITFIELD need_pop_function_context : 1;
   BOOL_BITFIELD x_processing_omp_trait_property_expr : 1;
@@ -2161,12 +2168,18 @@ extern GTY(()) struct saved_scope *scope_chain;
 #define processing_contract_condition \
   (scope_chain->bindings->kind == sk_contract)
 
+#define processing_postcondition scope_chain->x_processing_postcondition
+
 #define in_discarded_stmt scope_chain->discarded_stmt
 #define in_consteval_if_p scope_chain->consteval_if_p
 #define in_expansion_stmt scope_chain->expansion_stmt
 
 #define current_ref_temp_count scope_chain->ref_temp_count
 
+/* Nonzero if we're parsing a precondition on a constructor or postcondition
+   on destructor.  */
+#define contract_class_ptr scope_chain->x_contract_class_ptr
+
 /* RAII sentinel to handle clearing processing_template_decl and restoring
    it when done.  */
 
@@ -3196,6 +3209,7 @@ struct GTY(()) lang_decl_fn {
   unsigned coroutine_p : 1;
   unsigned implicit_constexpr : 1;
   unsigned escalated_p : 1;
+
   unsigned xobj_func : 1;
 
   unsigned spare : 7;
@@ -6151,6 +6165,11 @@ extern int comparing_specializations;
    FIXME we should always do this except during deduction/ordering.  */
 extern int comparing_dependent_aliases;
 
+/* True if we are matching contracts of two functions.  Depending on
+   whether a decl has been genericized or not, PARM_DECL may be adjusted
+   to be an invisible reference.  */
+extern bool comparing_contracts;
+
 /* In parser.cc.  */
 
 extern bool cp_preserve_using_decl;
@@ -6937,6 +6956,9 @@ struct cp_declarator {
       tree late_return_type;
       /* The trailing requires-clause, if any.  */
       tree requires_clause;
+      /* The function-contract-specifier-seq, if any.  */
+      tree contract_specifiers;
+      /* The position of the opening brace for a function definition.  */
       location_t parens_loc;
     } function;
     /* For arrays.  */
@@ -7126,6 +7148,7 @@ extern tree build_conditional_expr                (const op_location_t &,
 extern tree build_addr_func                    (tree, tsubst_flags_t);
 extern void set_flags_from_callee              (tree);
 extern tree build_call_a                       (tree, int, tree*);
+extern tree build_call_a_1                     (tree, int, tree*);
 extern tree build_call_n                       (tree, int, ...);
 extern bool null_ptr_cst_p                     (tree);
 extern bool null_member_pointer_value_p                (tree);
@@ -9055,6 +9078,7 @@ extern tree process_stmt_hotness_attribute        (tree, location_t);
 extern tree build_assume_call                  (location_t, tree);
 extern tree process_stmt_assume_attribute      (tree, tree, location_t);
 extern bool simple_empty_class_p               (tree, tree, tree_code);
+extern tree build_source_location_impl         (location_t, tree, tree);
 extern tree fold_builtin_source_location       (const_tree);
 extern tree get_source_location_impl_type      ();
 extern bool immediate_escalating_function_p    (tree);
@@ -9062,6 +9086,7 @@ extern void promote_function_to_consteval (tree);
 extern tree cp_fold_immediate                  (tree *, mce_value,
                                                 tree = current_function_decl);
 extern void process_and_check_pending_immediate_escalating_fns ();
+extern bool is_invisiref_parm                  (const_tree);
 
 /* in name-lookup.cc */
 extern tree strip_using_decl                    (tree);
index 885c5ce9ed83e64f36f8337753c3926fb396e507..1e353ef1ebcf69f5a8ef99f0b52a1d52cfb0c2e8 100644 (file)
@@ -60,6 +60,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "opts.h"
 #include "langhooks-def.h"  /* For lhd_simulate_record_decl  */
 #include "coroutines.h"
+#include "contracts.h"
 #include "gcc-urlifier.h"
 #include "diagnostic-highlight-colors.h"
 #include "pretty-print-markup.h"
@@ -2527,6 +2528,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
          = DECL_OVERLOADED_OPERATOR_CODE_RAW (olddecl);
       new_defines_function = DECL_INITIAL (newdecl) != NULL_TREE;
 
+      check_redecl_contract (newdecl, olddecl);
+
       /* Optionally warn about more than one declaration for the same
         name, but don't warn about a function declaration followed by a
         definition.  */
@@ -2607,6 +2610,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
         specializations.  */
       gcc_assert (!DECL_TEMPLATE_SPECIALIZATIONS (newdecl));
 
+      /* Make sure the contracts are equivalent.  */
+      check_redecl_contract (newdecl, olddecl);
+
       DECL_ATTRIBUTES (old_result)
        = (*targetm.merge_decl_attributes) (old_result, new_result);
 
@@ -2675,6 +2681,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
          DECL_INITIAL (old_result) = DECL_INITIAL (new_result);
          if (DECL_FUNCTION_TEMPLATE_P (newdecl))
            {
+             update_contract_arguments (new_result, old_result);
+
              DECL_ARGUMENTS (old_result) = DECL_ARGUMENTS (new_result);
              for (tree p = DECL_ARGUMENTS (old_result); p; p = DECL_CHAIN (p))
                DECL_CONTEXT (p) = old_result;
@@ -3202,6 +3210,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
        }
       if (! types_match || new_defines_function)
        {
+         /* Update the contracts to reflect the new parameter names. */
+         update_contract_arguments (newdecl, olddecl);
 
          /* Mark the old PARM_DECLs in case std::meta::parameters_of has
             been called on the old declaration and reflections of those
@@ -3531,6 +3541,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
   if (flag_concepts)
     remove_constraints (newdecl);
 
+  if (flag_contracts)
+    /* Remove the specifiers, and then remove the decl from the lookup.  */
+    remove_decl_with_fn_contracts_specifiers (newdecl);
+
   /* And similarly for any module tracking data.  */
   if (modules_p ())
     remove_defining_module (newdecl);
@@ -5710,6 +5724,9 @@ cxx_init_decl_processing (void)
   if (flag_exceptions)
     init_exception_processing ();
 
+  if (flag_contracts)
+    init_contracts ();
+
   if (modules_p ())
     init_modules (parse_in);
 
@@ -6516,6 +6533,20 @@ start_decl (const cp_declarator *declarator,
       return error_mark_node;
     }
 
+  if (flag_contracts
+      && TREE_CODE (decl) == FUNCTION_DECL
+      && !processing_template_decl
+      && DECL_RESULT (decl)
+      && is_auto (TREE_TYPE (DECL_RESULT (decl))))
+    for (tree ca = get_fn_contract_specifiers (decl); ca; ca = TREE_CHAIN (ca))
+      if (POSTCONDITION_P (CONTRACT_STATEMENT (ca))
+         && POSTCONDITION_IDENTIFIER (CONTRACT_STATEMENT (ca)))
+       {
+         error_at (DECL_SOURCE_LOCATION (decl),
+                   "postconditions with deduced result name types must only"
+                   " appear on function definitions");
+         return error_mark_node;
+       }
   /* Save the DECL_INITIAL value in case it gets clobbered to assist
      with attribute validation.  */
   initial = DECL_INITIAL (decl);
@@ -12045,6 +12076,7 @@ grokfndecl (tree ctype,
            int template_count,
            tree in_namespace,
            tree* attrlist,
+           tree contract_specifiers,
            location_t location)
 {
   tree decl;
@@ -12490,7 +12522,11 @@ grokfndecl (tree ctype,
 
   /* Caller will do the rest of this.  */
   if (check < 0)
-    return decl;
+    {
+      if (decl && decl != error_mark_node && contract_specifiers)
+       set_fn_contract_specifiers (decl, contract_specifiers);
+      return decl;
+    }
 
   if (ctype != NULL_TREE)
     grokclassfn (ctype, decl, flags);
@@ -12521,6 +12557,16 @@ grokfndecl (tree ctype,
       *attrlist = NULL_TREE;
     }
 
+  /* Update now we have a decl and maybe know the return type.  */
+  if (contract_specifiers)
+    {
+      tree t = decl;
+      if (TREE_CODE (decl) == TEMPLATE_DECL)
+       t = DECL_TEMPLATE_RESULT (decl);
+      set_fn_contract_specifiers (t, contract_specifiers);
+      rebuild_postconditions (t);
+    }
+
   /* Check main's type after attributes have been applied.  */
   if (ctype == NULL_TREE && DECL_MAIN_P (decl))
     {
@@ -13870,6 +13916,7 @@ grokdeclarator (const cp_declarator *declarator,
   tree raises = NULL_TREE;
   int template_count = 0;
   tree returned_attrs = NULL_TREE;
+  tree contract_specifiers = NULL_TREE;
   tree parms = NULL_TREE;
   const cp_declarator *id_declarator;
   /* The unqualified name of the declarator; either an
@@ -15450,6 +15497,12 @@ grokdeclarator (const cp_declarator *declarator,
                  returned_attrs = attr_chainon (returned_attrs, att);
              }
 
+           /* Actually apply the contract attributes to the declaration.  */
+           if (flag_contracts)
+             contract_specifiers
+               = attr_chainon (contract_specifiers,
+                               declarator->u.function.contract_specifiers);
+
            if (attrs)
              /* [dcl.fct]/2:
 
@@ -16411,8 +16464,8 @@ grokdeclarator (const cp_declarator *declarator,
                               is_xobj_member_function, sfk,
                               funcdef_flag, late_return_type_p,
                               template_count, in_namespace,
-                              attrlist, id_loc);
-            decl = set_virt_specifiers (decl, virt_specifiers);
+                              attrlist, contract_specifiers, id_loc);
+           decl = set_virt_specifiers (decl, virt_specifiers);
            if (decl == NULL_TREE)
              return error_mark_node;
 #if 0
@@ -16738,8 +16791,7 @@ grokdeclarator (const cp_declarator *declarator,
                   || storage_class != sc_static);
 
        decl = grokfndecl (ctype, type, original_name, parms, unqualified_id,
-                          declspecs,
-                           reqs, virtualp, flags, memfn_quals, rqual, raises,
+                          declspecs, reqs, virtualp, flags, memfn_quals, rqual, raises,
                           1, friendp,
                           publicp,
                           inlinep | (2 * constexpr_p) | (4 * concept_p)
@@ -16749,7 +16801,7 @@ grokdeclarator (const cp_declarator *declarator,
                           funcdef_flag,
                           late_return_type_p,
                           template_count, in_namespace, attrlist,
-                          id_loc);
+                          contract_specifiers, id_loc);
        if (decl == NULL_TREE)
          return error_mark_node;
 
@@ -19920,6 +19972,8 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
 
   store_parm_decls (current_function_parms);
 
+  start_function_contracts (decl1);
+
   if (!processing_template_decl
       && flag_lifetime_dse > 1
       && DECL_CONSTRUCTOR_P (decl1)
@@ -20352,6 +20406,10 @@ finish_function (bool inline_p)
        finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS
                              (TREE_TYPE (fndecl)),
                              current_eh_spec_block);
+
+     /* If outlining succeeded, then add contracts handling if needed.  */
+     if (coroutine->cp_valid_coroutine ())
+       maybe_apply_function_contracts (fndecl);
     }
   else
   /* For a cloned function, we've already got all the code we need;
@@ -20367,6 +20425,9 @@ finish_function (bool inline_p)
        finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS
                              (TREE_TYPE (current_function_decl)),
                              current_eh_spec_block);
+
+      maybe_apply_function_contracts (current_function_decl);
+
     }
 
   /* If we're saving up tree structure, tie off the function now.  */
@@ -20630,7 +20691,7 @@ finish_function (bool inline_p)
   /* Clean up.  */
   invoke_plugin_callbacks (PLUGIN_FINISH_PARSE_FUNCTION, fndecl);
 
-  /* Build outlined functions for coroutines.  */
+  /* Build outlined functions for coroutines and contracts.  */
 
   if (coroutine)
     {
index 6faf4b040f25558e3e94034e07895486b59c2384..50b4857793a39d330ff8c63a77604dba7dc02e4d 100644 (file)
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "escaped_string.h"
 #include "gcc-rich-location.h"
 #include "tree-pretty-print-markup.h"
+#include "contracts.h"
 
 /* Id for dumping the raw trees.  */
 int raw_dump_id;
@@ -6104,6 +6105,9 @@ c_parse_final_cleanups (void)
        reconsider = true;
     }
 
+  if (flag_contracts)
+    maybe_emit_violation_handler_wrappers ();
+
   /* All templates have been instantiated.  */
   at_eof = 2;
 
index 27c5192ab894217a1325e888db9baf7bd97c6eac..88b0b24e097c39cdbf4d4a3b767c4202a293894d 100644 (file)
@@ -419,7 +419,11 @@ cxx_init (void)
       if (cxx_dialect >= cxx23)
        cpp_warn (parse_in, "assume");
       if (cxx_dialect >= cxx26)
-       cpp_warn (parse_in, "indeterminate");
+       {
+         if (flag_contracts)
+           cpp_warn (parse_in, "contract_assert");
+         cpp_warn (parse_in, "indeterminate");
+       }
     }
 
   if (c_common_init () == false)
index 79a33f3ef12f7c2c9eae971827718351da2c7ee6..a8d373ea6b77a4b986c5c7cc6a95e94ccfa55d56 100644 (file)
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/known-headers.h"
 #include "bitmap.h"
 #include "builtins.h"
+#include "contracts.h"
 #include "analyzer/analyzer-language.h"
 
 \f
@@ -1183,6 +1184,20 @@ cp_lexer_get_preprocessor_token (unsigned flags, cp_token *token)
                 particular identifier-turned-keyword again.  */
              C_SET_RID_CODE (token->u.value, RID_MAX);
            }
+         if (warn_cxx26_compat
+             && C_RID_CODE (token->u.value) >= RID_FIRST_CXX26
+             && C_RID_CODE (token->u.value) <= RID_LAST_CXX26)
+           {
+             /* Warn about the C++26 keyword (but still treat it as
+                an identifier).  */
+             warning_at (token->location, OPT_Wc__26_compat,
+                         "identifier %qE is a keyword in C++26",
+                         token->u.value);
+
+             /* Clear out the C_RID_CODE so we don't warn about this
+                particular identifier-turned-keyword again.  */
+             C_SET_RID_CODE (token->u.value, RID_MAX);
+           }
 
          token->keyword = RID_MAX;
        }
@@ -1844,7 +1859,7 @@ clear_decl_specs (cp_decl_specifier_seq *decl_specs)
 
 static cp_declarator *make_call_declarator
   (cp_declarator *, tree, cp_cv_quals, cp_virt_specifiers, cp_ref_qualifier,
-   tree, tree, tree, tree, tree, location_t);
+   tree, tree, tree, tree, tree, tree, location_t);
 static cp_declarator *make_array_declarator
   (cp_declarator *, tree, tree);
 static cp_declarator *make_pointer_declarator
@@ -2030,6 +2045,7 @@ make_call_declarator (cp_declarator *target,
                      tree exception_specification,
                      tree late_return_type,
                      tree requires_clause,
+                     tree contract_specifiers,
                      tree std_attrs,
                      location_t parens_loc)
 {
@@ -2045,6 +2061,7 @@ make_call_declarator (cp_declarator *target,
   declarator->u.function.exception_specification = exception_specification;
   declarator->u.function.late_return_type = late_return_type;
   declarator->u.function.requires_clause = requires_clause;
+  declarator->u.function.contract_specifiers = contract_specifiers;
   declarator->u.function.parens_loc = parens_loc;
   if (target)
     {
@@ -2480,6 +2497,8 @@ cp_parser_context_new (cp_parser_context* next)
   parser->unparsed_queues->last ().nsdmis
 #define unparsed_noexcepts \
   parser->unparsed_queues->last ().noexcepts
+#define unparsed_contracts \
+  parser->unparsed_queues->last ().contracts
 
 static void
 push_unparsed_function_queues (cp_parser *parser)
@@ -2978,6 +2997,21 @@ static tree cp_parser_transaction_cancel
 static tree cp_parser_yield_expression
   (cp_parser *);
 
+/* Contracts */
+
+static tree cp_parser_contract_assert
+  (cp_parser *parser, cp_token *token);
+
+static tree cp_maybe_function_contract_specifier
+  (cp_parser *parser);
+
+static tree cp_parser_function_contract_specifier
+  (cp_parser *);
+static tree cp_parser_function_contract_specifier_seq
+  (cp_parser *);
+static void cp_parser_late_contracts
+  (cp_parser *, tree);
+
 enum pragma_context {
   pragma_external,
   pragma_member,
@@ -12909,7 +12943,9 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
       first = false;
 
       tree scope = current_nonlambda_scope (/*only_skip_closures_p=*/true);
-      if (TREE_CODE (scope) != FUNCTION_DECL && !parsing_nsdmi ())
+      if (TREE_CODE (scope) != FUNCTION_DECL
+         && !parsing_nsdmi ()
+         && current_binding_level->kind != sk_contract)
        error ("non-local lambda expression cannot have a capture-default");
     }
 
@@ -13471,6 +13507,10 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr,
       return_type = cp_parser_trailing_type_id (parser);
     }
 
+  tree contract_specifiers = NULL_TREE;
+  if (flag_contracts)
+    contract_specifiers = cp_parser_function_contract_specifier_seq (parser);
+
   /* Also allow GNU attributes at the very end of the declaration, the usual
      place for GNU attributes.  */
   if (cp_next_tokens_can_be_gnu_attribute_p (parser))
@@ -13539,6 +13579,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr,
                                       exception_spec,
                                       return_type,
                                       trailing_requires_clause,
+                                      contract_specifiers,
                                       std_attrs,
                                       UNKNOWN_LOCATION);
 
@@ -13595,7 +13636,7 @@ make_dummy_lambda_op ()
                                     VIRT_SPEC_UNSPECIFIED,
                                     REF_QUAL_NONE, NULL_TREE,
                                     NULL_TREE, NULL_TREE, NULL_TREE,
-                                    NULL_TREE, UNKNOWN_LOCATION);
+                                    NULL_TREE, NULL_TREE, UNKNOWN_LOCATION);
 
   tree fco = grokmethod (&return_type_specs, declarator, NULL_TREE);
   obstack_free (&declarator_obstack, p);
@@ -13690,6 +13731,10 @@ cp_parser_lambda_body (cp_parser* parser, tree lambda_expr)
        removed the need for that.  */
     cp_parser_function_body (parser, false);
 
+    /* We need to parse deferred contract conditions before we try to call
+       finish_function (which will try to emit the contracts).  */
+    cp_parser_late_contracts (parser, fco);
+
     finish_lambda_function (body);
   }
 
@@ -14280,10 +14325,12 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
          std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc);
          statement = cp_parser_transaction_cancel (parser);
          break;
-
+       case RID_CONTASSERT:
+         statement = cp_parser_contract_assert (parser, token);
+         break;
        default:
          /* It might be a keyword like `int' that can start a
-            declaration-statement.  */
+          declaration-statement.  */
          break;
        }
     }
@@ -26009,6 +26056,7 @@ cp_parser_init_declarator (cp_parser* parser,
     {
       /* If the init-declarator isn't initialized and isn't followed by a
         `,' or `;', it's not a valid init-declarator.  */
+
       if (token->type != CPP_COMMA
          && token->type != CPP_SEMICOLON)
        {
@@ -26614,6 +26662,11 @@ cp_parser_direct_declarator (cp_parser* parser,
                  /* Parse the virt-specifier-seq.  */
                  virt_specifiers = cp_parser_virt_specifier_seq_opt (parser);
 
+                 tree contract_specifiers = NULL_TREE;
+                 if (flag_contracts)
+                   contract_specifiers
+                     = cp_parser_function_contract_specifier_seq (parser);
+
                  location_t parens_loc = make_location (parens_start,
                                                         parens_start,
                                                         parens_end);
@@ -26627,6 +26680,7 @@ cp_parser_direct_declarator (cp_parser* parser,
                                                     exception_specification,
                                                     late_return,
                                                     requires_clause,
+                                                    contract_specifiers,
                                                     attrs,
                                                     parens_loc);
                  declarator->attributes = gnu_attrs;
@@ -27838,6 +27892,12 @@ cp_parser_type_specifier_seq (cp_parser* parser,
          continue;
        }
 
+      /* We might have contract specifiers after a trailing return.  */
+      if (seen_type_specifier
+         && is_trailing_return
+         && cp_maybe_function_contract_specifier (parser))
+       break;
+
       /* record the token of the beginning of the type specifier seq,
          for error reporting purposes*/
      if (!start_token)
@@ -29865,6 +29925,42 @@ cp_parser_class_specifier (cp_parser* parser)
        }
       vec_safe_truncate (unparsed_nsdmis, 0);
 
+      /* Now contract specifiers.  */
+      FOR_EACH_VEC_SAFE_ELT (unparsed_contracts, ix, decl)
+       {
+         tree ctx = DECL_CONTEXT (decl);
+         switch_to_class (ctx);
+
+         temp_override<tree> cfd (current_function_decl, decl);
+
+         /* Make sure that any template parameters are in scope.  */
+         maybe_begin_member_template_processing (decl);
+
+         /* Make sure that any member-function parameters are in scope.
+            This function doesn't expect ccp to be set.  */
+         current_class_ptr = current_class_ref = NULL_TREE;
+         inject_parm_decls (decl);
+
+         /* 'this' is not allowed in static member functions.  */
+         unsigned char local_variables_forbidden_p
+           = parser->local_variables_forbidden_p;
+         if (DECL_THIS_STATIC (decl))
+           parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
+
+         /* Now we can parse contract conditions.  */
+         cp_parser_late_contracts (parser, decl);
+
+         /* Restore the state of local_variables_forbidden_p.  */
+         parser->local_variables_forbidden_p = local_variables_forbidden_p;
+
+         /* Remove any member-function parameters from the symbol table.  */
+         pop_injected_parms ();
+
+         /* Remove any template parameters from the symbol table.  */
+         maybe_end_member_template_processing ();
+       }
+      vec_safe_truncate (unparsed_contracts, 0);
+
       current_class_ptr = NULL_TREE;
       current_class_ref = NULL_TREE;
       switch_to_class (NULL_TREE);
@@ -32587,9 +32683,9 @@ cp_nth_tokens_can_be_std_attribute_p (cp_parser *parser, size_t n)
   cp_token *token = cp_lexer_peek_nth_token (parser->lexer, n);
 
   return ((token->type == CPP_KEYWORD && token->keyword == RID_ALIGNAS)
-         || (token->type == CPP_OPEN_SQUARE
-             && (token = cp_lexer_peek_nth_token (parser->lexer, n + 1))
-             && token->type == CPP_OPEN_SQUARE));
+          || (token->type == CPP_OPEN_SQUARE
+               && (token = cp_lexer_peek_nth_token (parser->lexer, n + 1))
+               && token->type == CPP_OPEN_SQUARE));
 }
 
 /* Return TRUE iff the next Nth tokens in the stream are possibly the
@@ -33327,6 +33423,388 @@ cp_parser_annotation_list (cp_parser *parser)
   return attributes;
 }
 
+/* Parse a contract condition for a deferred contract.  */
+
+void
+cp_parser_late_contract_condition (cp_parser *parser, tree fn, tree contract)
+{
+  tree condition = CONTRACT_CONDITION (contract);
+  tree r_ident = NULL_TREE;
+  if (TREE_CODE (contract) == POSTCONDITION_STMT)
+    r_ident = POSTCONDITION_IDENTIFIER (contract);
+
+  tree type = TREE_TYPE (TREE_TYPE (fn));
+  location_t r_loc = UNKNOWN_LOCATION;
+  if (r_ident)
+    {
+      r_loc = EXPR_LOCATION (r_ident);
+      r_ident = tree_strip_any_location_wrapper (r_ident);
+      if (r_loc == UNKNOWN_LOCATION)
+       r_loc = cp_expr_location (contract);
+      if (!check_postcondition_result (fn, type, r_loc))
+       {
+         invalidate_contract (contract);
+         return;
+       }
+    }
+
+  /* Contracts allow access to members only through explicit use of 'this'
+     pointer. */
+   tree saved_ccr = current_class_ref;
+   tree saved_ccp = current_class_ptr;
+   tree saved_contract_ccp = contract_class_ptr;
+
+   if ((DECL_CONSTRUCTOR_P (fn) && PRECONDITION_P (contract))
+       || (DECL_DESTRUCTOR_P (fn) && POSTCONDITION_P (contract)))
+    contract_class_ptr = current_class_ptr;
+  else
+    contract_class_ptr = NULL_TREE;
+
+  push_unparsed_function_queues (parser);
+
+  /* Push the saved tokens onto the parser's lexer stack.  */
+  cp_token_cache *tokens = DEFPARSE_TOKENS (condition);
+  cp_parser_push_lexer_for_tokens (parser, tokens);
+
+  /* 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);
+  bool old_pc = processing_postcondition;
+  processing_postcondition = POSTCONDITION_P (contract);
+  /* Build a fake variable for the result identifier.  */
+  tree result = NULL_TREE;
+  if (r_ident)
+    {
+      cp_expr result_id (r_ident, r_loc);
+      result = make_postcondition_variable (result_id, type);
+      ++processing_template_decl;
+    }
+  cp_expr parsed_condition = cp_parser_conditional_expression (parser);
+  /* Commit to changes.  */
+  update_late_contract (contract, result, parsed_condition);
+  if (r_ident)
+    --processing_template_decl;
+
+  /* Rebuild the postcondition since we didn't do it in grokfndecl. */
+  rebuild_postconditions (fn);
+
+  /* Leave our temporary scope for the postcondition result.  */
+  processing_postcondition = old_pc;
+  gcc_checking_assert (scope_chain && scope_chain->bindings
+                      && scope_chain->bindings->kind == sk_contract);
+  pop_bindings_and_leave_scope ();
+
+  if (cp_lexer_next_token_is_not (parser->lexer, CPP_EOF))
+    error_at (input_location, "expected conditional-expression");
+
+  /* Revert to the main lexer.  */
+  cp_parser_pop_lexer (parser);
+
+  /* Restore the queue.  */
+  pop_unparsed_function_queues (parser);
+
+  current_class_ref = saved_ccr;
+  current_class_ptr = saved_ccp;
+  contract_class_ptr = saved_contract_ccp;
+}
+
+/* Parse deferred contracts of FNDECL.  */
+
+void
+cp_parser_late_contracts (cp_parser *parser, tree fndecl)
+{
+
+  tree new_contracts = NULL_TREE;
+  tree old_contracts = get_fn_contract_specifiers (fndecl);
+
+  if (old_contracts == NULL_TREE || !contract_any_deferred_p (old_contracts))
+    return;
+
+  for (; old_contracts; old_contracts = TREE_CHAIN (old_contracts))
+    {
+      tree contract = TREE_VALUE (TREE_VALUE (old_contracts));
+
+      tree condition = CONTRACT_CONDITION (contract);
+      /* All contracts should be deferred if one of them is deferred */
+      gcc_checking_assert (TREE_CODE (condition) == DEFERRED_PARSE);
+
+      cp_parser_late_contract_condition (parser, fndecl, contract);
+      tree list = tree_cons (TREE_PURPOSE (old_contracts),
+                            TREE_VALUE (old_contracts), NULL_TREE);
+      new_contracts = chainon (new_contracts, list);
+    }
+
+  update_fn_contract_specifiers (fndecl, new_contracts);
+}
+
+static tree
+cp_parser_contract_assert (cp_parser *parser, cp_token *token)
+{
+  if (!flag_contracts)
+    {
+      error_at (token->location, "%qs is only available with %qs",
+               "contract_assert", "-fcontracts");
+      cp_parser_skip_to_end_of_statement (parser);
+      return error_mark_node;
+    }
+
+  tree cont_assert = token->u.value;
+
+  token = cp_lexer_consume_token (parser->lexer);
+  location_t loc = token->location;
+
+  location_t attrs_loc = cp_lexer_peek_token (parser->lexer)->location;
+  tree std_attrs = cp_parser_std_attribute_spec_seq (parser);
+  if (std_attrs)
+    {
+      attrs_loc = make_location (attrs_loc, attrs_loc, input_location);
+      warning_at (attrs_loc, OPT_Wattributes, "attributes are ignored on %qs",
+                 "contract_assert");
+      std_attrs = NULL_TREE;
+    }
+
+  matching_parens parens;
+  parens.require_open (parser);
+  /* Enable location wrappers when parsing contracts.  */
+  auto suppression = make_temp_override (suppress_location_wrappers, 0);
+
+  /* Parse the condition.  */
+  begin_scope (sk_contract, current_function_decl);
+  bool old_pc = processing_postcondition;
+  processing_postcondition = false;
+  cp_expr condition = cp_parser_conditional_expression (parser);
+  gcc_checking_assert (scope_chain && scope_chain->bindings
+                      && scope_chain->bindings->kind == sk_contract);
+  /* Build the contract.  */
+  tree contract = grok_contract (cont_assert, /*mode*/NULL_TREE,
+                           /*result*/NULL_TREE, condition, loc);
+  processing_postcondition = old_pc;
+  pop_bindings_and_leave_scope ();
+
+  parens.require_close (parser);
+
+  if (!contract || contract == error_mark_node)
+    {
+      cp_parser_skip_to_end_of_statement (parser);
+      return error_mark_node;
+    }
+
+  if (!cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON))
+    return error_mark_node;
+
+  add_stmt (contract);
+  return contract;
+}
+
+/* Can the next tokens introduce a function contract specifier.  */
+
+static tree
+cp_function_contract_specifier_intro (cp_parser *parser)
+{
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+
+  /* Look for the contextual keywords 'pre' and 'post' as an introducer.  */
+  if (token->type != CPP_NAME)
+    return NULL_TREE;
+
+  tree contract_name = token->u.value;
+
+  if (!id_equal (contract_name, "pre") && !id_equal (contract_name, "post"))
+    /* If we don't have a valid contract start, we are done.  */
+    return NULL_TREE;
+  return contract_name;
+}
+
+/* Look ahead to see if this might introduce a function contract specifier.
+   If not return NULL_TREE, if successful return the name (pre or post).  */
+
+static tree
+cp_maybe_function_contract_specifier (cp_parser *parser)
+{
+  tree contract_name = cp_function_contract_specifier_intro (parser);
+  if (!contract_name)
+    return NULL_TREE;
+
+  size_t n = 2;
+  if (cp_nth_tokens_can_be_std_attribute_p (parser, n))
+    n = cp_parser_skip_std_attribute_spec_seq (parser, n);
+  if (cp_lexer_nth_token_is (parser->lexer, n, CPP_OPEN_PAREN))
+    return contract_name;
+  return NULL_TREE;
+}
+
+/* Parse a contract specifier.
+
+  function-contract-specifier :
+    precondition-specifier
+    postcondition-specifier
+  precondition-specifier :
+    pre attribute-specifier-seqopt ( conditional-expression )
+  postcondition-specifier :
+    post attribute-specifier-seqopt ( result-name-introduceropt conditional-expression )
+  result-name-introducer :
+    attributed-identifier :
+
+   Return void_list_node if the current token doesn't start a
+   contract specifier.  */
+
+static tree
+cp_parser_function_contract_specifier (cp_parser *parser)
+{
+  tree contract_name = cp_function_contract_specifier_intro (parser);
+  if (!contract_name)
+    return NULL_TREE;
+
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  cp_lexer_consume_token (parser->lexer);
+  location_t loc = token->location;
+  bool postcondition_p = id_equal (contract_name, "post");
+
+  location_t attrs_loc = cp_lexer_peek_token (parser->lexer)->location;
+  tree std_attrs = cp_parser_std_attribute_spec_seq (parser);
+  if (std_attrs)
+    {
+      attrs_loc = make_location (attrs_loc, attrs_loc, input_location);
+      warning_at (attrs_loc, OPT_Wattributes, "attributes are ignored on"
+                 " function contract specifiers");
+      std_attrs = NULL_TREE;
+    }
+
+  matching_parens parens;
+  parens.require_open (parser);
+
+  /* Check for postcondition identifiers.  */
+  cp_expr identifier;
+  if (postcondition_p && cp_lexer_next_token_is (parser->lexer, CPP_NAME)
+      && cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_COLON)
+    identifier = cp_parser_identifier (parser);
+
+  if (identifier == error_mark_node)
+    {
+      cp_parser_skip_to_closing_parenthesis (parser,
+                                            /*recovering=*/true,
+                                            /*or_comma=*/false,
+                                            /*consume_paren=*/true);
+      return error_mark_node;
+    }
+
+  if (identifier)
+    cp_parser_require (parser, CPP_COLON, RT_COLON);
+
+  tree contract;
+  if (current_class_type && TYPE_BEING_DEFINED (current_class_type))
+    {
+      /* Defer the parsing of pre/post contracts inside class definitions.  */
+      cp_token *first = cp_lexer_peek_token (parser->lexer);
+
+      /* Skip until we reach a closing token ).  */
+      cp_parser_skip_to_closing_parenthesis (parser,
+                                            /*recovering=*/false,
+                                            /*or_comma=*/false,
+                                            /*consume_paren=*/false);
+
+      cp_token *last = cp_lexer_peek_token (parser->lexer);
+      location_t end = last->location;
+      loc = make_location (loc, loc, end);
+
+      parens.require_close (parser);
+
+      /* Build a deferred-parse node.  */
+      tree condition = make_node (DEFERRED_PARSE);
+      DEFPARSE_TOKENS (condition) = cp_token_cache_new (first, last);
+      DEFPARSE_INSTANTIATIONS (condition) = NULL;
+
+      /* And its corresponding contract.  */
+      if (identifier)
+       identifier.maybe_add_location_wrapper ();
+      contract = grok_contract (contract_name, /*mode*/NULL_TREE, identifier,
+                               condition, loc);
+    }
+  else
+    {
+      /* Enable location wrappers when parsing contracts.  */
+      auto suppression = make_temp_override (suppress_location_wrappers, 0);
+
+      /* 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);
+      bool old_pc = processing_postcondition;
+      processing_postcondition = postcondition_p;
+      tree result = NULL_TREE;
+      if (identifier)
+       {
+         /* Build a fake variable for the result identifier.  */
+         result = make_postcondition_variable (identifier);
+         ++processing_template_decl;
+       }
+      cp_expr condition = cp_parser_conditional_expression (parser);
+      /* Build the contract.  */
+      contract = grok_contract (contract_name, /*mode*/NULL_TREE, result,
+                               condition, loc);
+      if (identifier)
+       --processing_template_decl;
+      processing_postcondition = old_pc;
+      gcc_checking_assert (scope_chain && scope_chain->bindings
+                          && scope_chain->bindings->kind == sk_contract);
+      pop_bindings_and_leave_scope ();
+
+      if (contract != error_mark_node)
+       {
+         location_t end = cp_lexer_peek_token (parser->lexer)->location;
+         loc = make_location (loc, loc, end);
+         SET_EXPR_LOCATION (contract, loc);
+       }
+
+      parens.require_close (parser);
+    }
+
+  if (!flag_contracts)
+    {
+      error_at (loc, "contracts are only available with %qs", "-fcontracts");
+      return error_mark_node;
+    }
+
+  return contract;
+}
+
+/* Parse a contract specifier seq. Returns a list of preconditions and
+  postconditions in an attribute tree.
+
+  function-contract-specifier-seq :
+    function-contract-specifier function-contract-specifier-seq.  */
+
+static tree
+cp_parser_function_contract_specifier_seq (cp_parser *parser)
+{
+  tree contract_specs = NULL_TREE;
+
+  while (true)
+    {
+      tree contract_spec = cp_parser_function_contract_specifier (parser);
+
+      /* If there are no more contracts, done.  */
+      if (contract_spec == NULL_TREE)
+       break;
+
+      /* Ignore any erroneous contracts and attempt to continue parsing.  */
+      if (contract_spec == error_mark_node)
+       continue;
+
+      /* For now, turn this into an attribute.  */
+      tree contract_name = TREE_CODE (contract_spec) == PRECONDITION_STMT
+                          ? get_identifier ("pre")
+                          : get_identifier ("post");
+      contract_spec = finish_contract_specifier (contract_name, contract_spec);
+      /* Arrange to build the list in the correct order.  */
+      if (contract_specs)
+       contract_specs = attr_chainon (contract_specs, contract_spec);
+      else
+       contract_specs = contract_spec;
+    }
+
+    return contract_specs;
+}
+
 /* Parse a standard C++-11 attribute specifier.
 
    attribute-specifier:
@@ -36429,6 +36907,9 @@ cp_parser_save_default_args (cp_parser* parser, tree decl)
       vec_safe_push (unparsed_noexcepts, entry);
     }
 
+  /* Contracts are deferred.  */
+  if (DECL_HAS_CONTRACTS_P (decl))
+    vec_safe_push (unparsed_contracts, decl);
 }
 
 /* DEFAULT_ARG contains the saved tokens for the initializer of DECL,
index 6f6ca0876ceccbfa0d8f62be51020a6aced14069..60392f1a089c4b6af2ff9611167bb044c72a75be 100644 (file)
@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "omp-general.h"
 #include "pretty-print-markup.h"
+#include "contracts.h"
 
 /* The type of functions taking a tree, and some additional data, and
    returning an int.  */
@@ -3270,6 +3271,8 @@ check_explicit_specialization (tree declarator,
                }
              decl = register_specialization (tmpl, gen_tmpl, targs,
                                              is_friend, 0);
+             if (flag_contracts)
+               remove_fn_contract_specifiers (result);
              return decl;
            }
 
@@ -3362,6 +3365,11 @@ check_explicit_specialization (tree declarator,
                                              is_friend, 0);
            }
 
+         if (flag_contracts
+             && decl != error_mark_node
+             && DECL_TEMPLATE_SPECIALIZATION (decl))
+           remove_fn_contract_specifiers (decl);
+
          /* A 'structor should already have clones.  */
          gcc_assert (decl == error_mark_node
                      || variable_template_p (tmpl)
@@ -12169,6 +12177,142 @@ can_complete_type_without_circularity (tree type)
 static tree tsubst_omp_clauses (tree, enum c_omp_region_type, tree,
                                tsubst_flags_t, tree);
 
+/* Instantiate the contract statement.  */
+
+static tree
+tsubst_contract (tree decl, tree t, tree args, tsubst_flags_t complain,
+                tree in_decl)
+{
+  tree type = decl ? TREE_TYPE (TREE_TYPE (decl)) : NULL_TREE;
+  bool auto_p  = type_uses_auto (type);
+
+  tree r = copy_node (t);
+
+  /* Rebuild the result variable, if present.  */
+  tree oldvar = NULL_TREE;
+  tree newvar = NULL_TREE;
+  if (type && POSTCONDITION_P (t) && POSTCONDITION_IDENTIFIER (t))
+    {
+      oldvar = POSTCONDITION_IDENTIFIER (t);
+      if (oldvar == error_mark_node)
+       return invalidate_contract (r);
+
+      newvar = copy_node (oldvar);
+      TREE_TYPE (newvar) = type;
+      DECL_CONTEXT (newvar) = decl;
+      POSTCONDITION_IDENTIFIER (r) = newvar;
+
+      /* Make sure the postcondition is valid.  */
+      location_t loc = DECL_SOURCE_LOCATION (oldvar);
+      if (!auto_p)
+       if (!check_postcondition_result (decl, type, loc))
+         return invalidate_contract (r);
+    }
+
+  /* Instantiate the condition.  If the return type is undeduced, process
+     the expression as if inside a template to avoid spurious type errors.  */
+  begin_scope (sk_contract, decl);
+  bool old_pc = processing_postcondition;
+  processing_postcondition = POSTCONDITION_P (t);
+  if (auto_p)
+    ++processing_template_decl;
+  if (newvar)
+    /* Make the variable available for lookup.  */
+    register_local_specialization (newvar, oldvar);
+
+  /* Contract conditions have a wider application of location wrappers than
+     other trees (which will not work with the generic handling in tsubst_expr),
+     remove the wrapper here...  */
+  location_t cond_l = EXPR_LOCATION (CONTRACT_CONDITION (t));
+  tree cond_t = tree_strip_any_location_wrapper (CONTRACT_CONDITION (t));
+
+  /* ... and substitute the contained expression.  */
+  cond_t = tsubst_expr (cond_t, args, complain, in_decl);
+
+  /* Convert to bool, if possible, and then re-apply a location wrapper
+     when required.  */
+  cp_expr new_condition (cond_t, cond_l);
+  CONTRACT_CONDITION (r) = finish_contract_condition (new_condition);
+
+  /* At present, the semantic, kind and comment cannot be dependent.  */
+  gcc_checking_assert
+    (!type_dependent_expression_p (CONTRACT_EVALUATION_SEMANTIC (r))
+     && !type_dependent_expression_p (CONTRACT_ASSERTION_KIND (r))
+     && !type_dependent_expression_p (CONTRACT_COMMENT (r)));
+
+  if (auto_p)
+    --processing_template_decl;
+  processing_postcondition = old_pc;
+  gcc_checking_assert (scope_chain && scope_chain->bindings
+                      && scope_chain->bindings->kind == sk_contract);
+  pop_bindings_and_leave_scope ();
+
+  return r;
+}
+
+/* Update T instantiating a contract specifier.  */
+
+static void
+tsubst_contract_specifier (tree decl, tree t, tree args,
+                          tsubst_flags_t complain, tree in_decl)
+{
+  /* For non-specializations, adjust the current declaration to the most general
+     version of in_decl. Because we defer the instantiation of contracts as long
+     as possible, they are still written in terms of the parameters (and return
+     type) of the most general template.  */
+  tree tmpl = DECL_TI_TEMPLATE (in_decl);
+  if (!DECL_TEMPLATE_SPECIALIZATION (tmpl))
+    in_decl = DECL_TEMPLATE_RESULT (most_general_template (in_decl));
+  local_specialization_stack specs (lss_copy);
+  register_parameter_specializations (in_decl, decl);
+
+  /* Get the contract to be instantiated.  */
+  tree contract = CONTRACT_STATEMENT (t);
+
+  /* Use the complete set of template arguments for instantiation. The
+     contract may not have been instantiated and still refer to outer levels
+     of template parameters.  */
+  args = DECL_TI_ARGS (decl);
+
+  /* For member functions, make this available for semantic analysis.  */
+  tree save_ccp = current_class_ptr;
+  tree save_ccr = current_class_ref;
+  if (DECL_IOBJ_MEMBER_FUNCTION_P (decl))
+    {
+      tree arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl));
+      tree this_type = TREE_TYPE (TREE_VALUE (arg_types));
+      inject_this_parameter (this_type, cp_type_quals (this_type));
+    }
+
+  contract = tsubst_contract (decl, contract, args, complain, in_decl);
+
+  current_class_ptr = save_ccp;
+  current_class_ref = save_ccr;
+
+  /* Rebuild the attribute.  */
+  TREE_VALUE (t) = build_tree_list (NULL_TREE, contract);
+}
+
+/* For unsubstituted list of contracts in SPECIFIERS, instantiate contracts
+ for DECL and set the list as contracts for decl. Substitution creates a deep
+ copy of the contract.  */
+
+void
+tsubst_contract_specifiers (tree specfiers, tree decl, tree args,
+                           tsubst_flags_t complain, tree in_decl)
+{
+  tree subst_contract_list = NULL_TREE;
+  for (tree spec = specfiers; spec; spec = TREE_CHAIN (spec))
+    {
+      tree nc = copy_node (spec);
+      tsubst_contract_specifier (decl, nc, args, complain, in_decl);
+      TREE_CHAIN (nc) = subst_contract_list;
+      subst_contract_list = nc;
+    }
+  if (flag_contracts)
+    set_fn_contract_specifiers (decl, nreverse (subst_contract_list));
+}
+
 /* Instantiate a single dependent attribute T (a TREE_LIST), and return either
    T or a new TREE_LIST, possibly a chain in the case of a pack expansion.  */
 
@@ -15121,6 +15265,11 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
   if (tree ci = get_constraints (t))
     set_constraints (r, ci);
 
+  /* copy_decl () does not know about contract specifiers.  NOTE these are not
+     substituted at this point.  */
+  if (tree ctrct = get_fn_contract_specifiers (t))
+    set_fn_contract_specifiers (r, ctrct);
+
   if (DECL_FRIEND_CONTEXT (t))
     SET_DECL_FRIEND_CONTEXT (r,
                             tsubst (DECL_FRIEND_CONTEXT (t),
@@ -19243,6 +19392,19 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       finish_using_directive (USING_STMT_NAMESPACE (t), /*attribs=*/NULL_TREE);
       break;
 
+    case PRECONDITION_STMT:
+    case POSTCONDITION_STMT:
+      gcc_unreachable ();
+
+    case ASSERTION_STMT:
+      {
+       r = tsubst_contract (NULL_TREE, t, args, complain, in_decl);
+       if (r != error_mark_node)
+         add_stmt (r);
+       RETURN (r);
+      }
+      break;
+
     case DECL_EXPR:
       {
        tree decl, pattern_decl;
@@ -27822,6 +27984,19 @@ regenerate_decl_from_template (tree decl, tree tmpl, tree args)
            DECL_CONTEXT (t) = decl;
        }
 
+      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);
+       }
+
       /* Merge additional specifiers from the CODE_PATTERN.  */
       if (DECL_DECLARED_INLINE_P (code_pattern)
          && !DECL_DECLARED_INLINE_P (decl))
index c188a7e7c1b7aeadf0cf1ce42fc1fb8c9181b9e9..9191472c7b12e5c2951e3d933888b930a700c3a7 100644 (file)
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "predict.h"
 #include "memmodel.h"
 #include "gimplify.h"
+#include "contracts.h"
 
 /* There routines provide a modular interface to perform many parsing
    operations.  They may therefore be used during actual parsing, or
@@ -609,7 +610,8 @@ set_one_cleanup_loc (tree t, location_t loc)
   if (!t)
     return;
 
-  protected_set_expr_location (t, loc);
+  if (TREE_CODE (t) != POSTCONDITION_STMT)
+    protected_set_expr_location (t, loc);
 
   /* Avoid locus differences for C++ cdtor calls depending on whether
      cdtor_returns_this: a conversion to void is added to discard the return
@@ -2763,6 +2765,14 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope,
          if (current_function_decl
              && DECL_STATIC_FUNCTION_P (current_function_decl))
            error ("invalid use of member %qD in static member function", decl);
+         else if (current_function_decl
+                  && processing_contract_condition
+                  && DECL_CONSTRUCTOR_P (current_function_decl))
+           error ("invalid use of member %qD in constructor %<pre%> contract", decl);
+         else if (current_function_decl
+                  && processing_contract_condition
+                  && DECL_DESTRUCTOR_P (current_function_decl))
+           error ("invalid use of member %qD in destructor %<post%> contract", decl);
          else
            error ("invalid use of non-static data member %qD", decl);
          inform (DECL_SOURCE_LOCATION (decl), "declared here");
@@ -3656,6 +3666,10 @@ finish_this_expr (void)
     }
   else if (fn && DECL_STATIC_FUNCTION_P (fn))
     error ("%<this%> is unavailable for static member functions");
+  else if (fn && processing_contract_condition && DECL_CONSTRUCTOR_P (fn))
+    error ("invalid use of %<this%> in a constructor %<pre%> condition");
+  else if (fn && processing_contract_condition && DECL_DESTRUCTOR_P (fn))
+    error ("invalid use of %<this%> in a destructor %<post%> condition");
   else if (fn)
     error ("invalid use of %<this%> in non-member function");
   else
@@ -4679,6 +4693,9 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use)
        }
       return error_mark_node;
     }
+  else if (processing_contract_condition && (TREE_CODE (decl) == PARM_DECL))
+    /* Use of a parameter in a contract condition is fine.  */
+    return decl;
   else
     {
       if (complain & tf_error)
@@ -4815,6 +4832,7 @@ finish_id_expression_1 (tree id_expression,
          && DECL_CONTEXT (decl) == NULL_TREE
          && !CONSTRAINT_VAR_P (decl)
          && !cp_unevaluated_operand
+         && !processing_contract_condition
          && !processing_omp_trait_property_expr)
        {
          *error_msg = G_("use of parameter outside function body");
@@ -4991,6 +5009,14 @@ finish_id_expression_1 (tree id_expression,
        }
       else if (TREE_CODE (decl) == FIELD_DECL)
        {
+         if (flag_contracts && processing_contract_condition
+             && contract_class_ptr == current_class_ptr)
+           {
+             error ("%qD 'this' required when accessing a member within a "
+                 "constructor precondition or destructor postcondition "
+                 "contract check", decl);
+                     return error_mark_node;
+           }
          /* Since SCOPE is NULL here, this is an unqualified name.
             Access checking has been performed during name lookup
             already.  Turn off checking to avoid duplicate errors.  */
@@ -5014,6 +5040,14 @@ finish_id_expression_1 (tree id_expression,
                      && !shared_member_p (decl))))
            {
              /* A set of member functions.  */
+             if (flag_contracts && processing_contract_condition
+                 && contract_class_ptr == current_class_ptr)
+               {
+                 error ("%qD 'this' required when accessing a member within a "
+                     "constructor precondition or destructor postcondition "
+                     "contract check", decl);
+                 return error_mark_node;
+               }
              decl = maybe_dummy_object (DECL_CONTEXT (first_fn), 0);
              return finish_class_member_access_expr (decl, id_expression,
                                                      /*template_p=*/false,
@@ -14438,6 +14472,8 @@ apply_deduced_return_type (tree fco, tree return_type)
 
   TREE_TYPE (fco) = change_return_type (return_type, TREE_TYPE (fco));
 
+  maybe_update_postconditions (fco);
+
   /* Apply the type to the result object.  */
 
   result = DECL_RESULT (fco);
index ab707c0a9d3a8350b5f98dbe440d1c9186c10731..55afa3efb273b703ecd90137e50ea8f387dee138 100644 (file)
@@ -4119,6 +4119,26 @@ cp_tree_equal (tree t1, tree t2)
   code1 = TREE_CODE (t1);
   code2 = TREE_CODE (t2);
 
+  if (comparing_contracts)
+    {
+      /* When comparing contracts, one declaration may already be
+        genericized. Check for invisible references and unravel them
+        for comparison purposes. Remember that a parameter is an invisible
+        reference so we can compare the parameter types accordingly.  */
+      if (code1 == VIEW_CONVERT_EXPR
+         && is_invisiref_parm (TREE_OPERAND(t1, 0)))
+       {
+         t1 = TREE_OPERAND(t1, 0);
+         code1 = TREE_CODE(t1);
+       }
+      if (code2 == VIEW_CONVERT_EXPR
+         && is_invisiref_parm (TREE_OPERAND(t2, 0)))
+       {
+         t2 = TREE_OPERAND(t2, 0);
+         code2 = TREE_CODE(t2);
+       }
+    }
+
   if (code1 != code2)
     return false;
 
@@ -4281,8 +4301,14 @@ cp_tree_equal (tree t1, tree t2)
           with parameters with identical contexts.  */
        return false;
 
-      if (same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+      if (same_type_p (TREE_TYPE (t1), TREE_TYPE (t2)) || comparing_contracts)
        {
+         /* When comparing contracts, we already know the declarations match,
+           and that the arguments have the same type. If one of the declarations
+           has been genericised, then the type of arguments in that declaration
+           will be adjusted for an invisible reference and the type comparison
+           would spuriosly fail. The only thing we care about when comparing
+           contractsis that we're using the same parameter.  */
          if (DECL_ARTIFICIAL (t1) ^ DECL_ARTIFICIAL (t2))
            return false;
          if (CONSTRAINT_VAR_P (t1) ^ CONSTRAINT_VAR_P (t2))
index 95817e36a4fcc63944f0e18744d6c70f6313c811..dbffbc131141619e5f66c479c3832844000a2bcc 100644 (file)
@@ -224,6 +224,8 @@ in the following sections.
 -fconcepts  -fconcepts-diagnostics-depth=@var{n}
 -fconstexpr-depth=@var{n}  -fconstexpr-cache-depth=@var{n}
 -fconstexpr-loop-limit=@var{n}  -fconstexpr-ops-limit=@var{n}
+-fcontracts
+-fcontract-evaluation-semantic=@r{[}ignore@r{|}observe@r{|}enforce@r{|}quick_enforce@r{]}
 -fcoroutines  -fdiagnostics-all-candidates
 -fno-elide-constructors
 -fno-enforce-eh-specs
@@ -3311,6 +3313,48 @@ of a loop too many expressions need to be evaluated, the resulting constexpr
 evaluation might take too long.
 The default is 33554432 (1<<25).
 
+@opindex fcontracts
+@opindex fno-contracts
+@item -fcontracts
+Enable support for the C++ Contracts feature, as specified in the C++26
+working draft (N5003).
+
+On violation of an enforced or observed contract (see @option{-fcontract-evaluation-semantic}
+below), a violation handler is called; the standard library provides a default
+handler that emits information about the contract that failed.
+
+Users can replace the default violation handler by defining
+@smallexample
+void
+handle_contract_violation (const std::contracts::contract_violation&);
+@end smallexample
+
+@opindex fcontract-evaluation-semantic
+@item -fcontract-evaluation-semantic=@var{semantic}
+Set the semantic with which contracts will be evaluated to @var{semantic}.
+
+Available values for the evaluation mode are:
+
+'@code{ignored}' The contract checks will be elided, with no checking added at
+either compile or runtime.
+
+'@code{observed}' The contract checks are performed (at runtime) and, if one
+fails, a handler is called that allows actions such as logging or emitting
+run-time warnings. When the handler returns, the execution of the code will
+continue.  At compile-time the checks are performed for @code{constexpr}
+evaluations, in this case a diagnostic is emitted if the check fails.
+
+'@code{enforce}' The contract checks are performed and the handler is called if
+one fails.  When the handler returns the execution of the code is terminated
+preventing it from continuing past the failed case.  At compile-time the checks
+are applied to @code{constexpr} and, if one fails, the program is ill-formed;
+an error will be emitted.
+
+'@code{quick_enforce}' The contract checks are performed (at runtime) and, if one
+fails, the execution is terminated immediately (without calling any handler).
+At compile-time there is no difference in behaviour between this option and
+the @code{enforce} case, since no handler is invoked at compile time.
+
 @opindex fcoroutines
 @opindex fno-coroutines
 @item -fcoroutines
diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p8-2.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p8-2.C
new file mode 100644 (file)
index 0000000..5f889db
--- /dev/null
@@ -0,0 +1,12 @@
+// basic.contract.eval/p8
+// If a contract violation occurs in a context that is manifestly
+// constant-evaluated ([expr.const]), and the evaluation semantic is not
+// terminating, then a diagnostic shall be emitted.
+// { dg-do compile { target c++23 } }
+// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " }
+
+consteval void foo( auto x ) pre( false ) {}
+// { dg-warning {contract predicate is false in constant expression} "" { target *-*-* } .-1 }
+int main() {
+  foo( 42 );
+}
index 1e2e29cee2db94db88f48386c42c898ba49d5d7d..ab21db84e4e0085731d410037e9abcf3a855a493 100644 (file)
@@ -26,7 +26,7 @@
 #define constexpr 1                    // { dg-error "keyword 'constexpr' defined as macro" "" { target c++26 } }
 #define constinit 1                    // { dg-error "keyword 'constinit' defined as macro" "" { target c++26 } }
 #define continue 1                     // { dg-error "keyword 'continue' defined as macro" "" { target c++26 } }
-#define contract_assert 1
+#define contract_assert 1              // { dg-error "keyword 'contract_assert' defined as macro" "" { target c++26 } }
 #define co_return 1                    // { dg-error "keyword 'co_return' defined as macro" "" { target c++26 } }
 #define co_yield 1                     // { dg-error "keyword 'co_yield' defined as macro" "" { target c++26 } }
 #define decltype 1                     // { dg-error "keyword 'decltype' defined as macro" "" { target c++26 } }
index 3e63c3e7bc4d0fcae59e5a0ee169d8bf0794d597..3d1382f9ec45d922669a4784b7bc470bed067b50 100644 (file)
@@ -26,7 +26,7 @@
 #define constexpr 1                    // { dg-warning "keyword 'constexpr' defined as macro" "" { target c++26 } }
 #define constinit 1                    // { dg-warning "keyword 'constinit' defined as macro" "" { target c++26 } }
 #define continue 1                     // { dg-warning "keyword 'continue' defined as macro" "" { target c++26 } }
-#define contract_assert 1
+#define contract_assert 1              // { dg-warning "keyword 'contract_assert' defined as macro" "" { target c++26 } }
 #define co_return 1                    // { dg-warning "keyword 'co_return' defined as macro" "" { target c++26 } }
 #define co_yield 1                     // { dg-warning "keyword 'co_yield' defined as macro" "" { target c++26 } }
 #define decltype 1                     // { dg-warning "keyword 'decltype' defined as macro" "" { target c++26 } }
index 334508cb0f83e76c69d324f60503481bf95e51b2..7f27857e4f3cde425677d85d8e25ccf92ea0f123 100644 (file)
@@ -26,7 +26,7 @@
 #undef constexpr                       // { dg-error "undefining keyword 'constexpr'" "" { target c++26 } }
 #undef constinit                       // { dg-error "undefining keyword 'constinit'" "" { target c++26 } }
 #undef continue                        // { dg-error "undefining keyword 'continue'" "" { target c++26 } }
-#undef contract_assert
+#undef contract_assert                 // { dg-error "undefining keyword 'contract_assert'" "" { target c++26 } }
 #undef co_return                       // { dg-error "undefining keyword 'co_return'" "" { target c++26 } }
 #undef co_yield                        // { dg-error "undefining keyword 'co_yield'" "" { target c++26 } }
 #undef decltype                        // { dg-error "undefining keyword 'decltype'" "" { target c++26 } }
index 87c53499a0b0af84138ffdead9197b60488db94e..d5c9f4d03bfcc4e45ee21b643489cd554179889e 100644 (file)
@@ -26,7 +26,7 @@
 #undef constexpr                       // { dg-warning "undefining keyword 'constexpr'" "" { target c++26 } }
 #undef constinit                       // { dg-warning "undefining keyword 'constinit'" "" { target c++26 } }
 #undef continue                        // { dg-warning "undefining keyword 'continue'" "" { target c++26 } }
-#undef contract_assert
+#undef contract_assert                 // { dg-warning "undefining keyword 'contract_assert'" "" { target c++26 } }
 #undef co_return                       // { dg-warning "undefining keyword 'co_return'" "" { target c++26 } }
 #undef co_yield                        // { dg-warning "undefining keyword 'co_yield'" "" { target c++26 } }
 #undef decltype                        // { dg-warning "undefining keyword 'decltype'" "" { target c++26 } }
index 66bfa463ba5b3f815766d8a45767831bc409c6c0..1f1ea19710b49cd9f18ad30309559c1437901cc2 100644 (file)
@@ -3,6 +3,7 @@
 // { dg-do preprocess }
 // { dg-options "-Wkeyword-macro" }
 // { dg-additional-options "-fmodules" { target c++20 } }
+// { dg-additional-options "-fcontracts" { target c++26 } }
 
 // [lex.key]
 #define alignas 1                      // { dg-warning "keyword 'alignas' defined as macro" "" { target c++11 } }
@@ -26,7 +27,7 @@
 #define constexpr 1                    // { dg-warning "keyword 'constexpr' defined as macro" "" { target c++11 } }
 #define constinit 1                    // { dg-warning "keyword 'constinit' defined as macro" "" { target c++20 } }
 #define continue 1                     // { dg-warning "keyword 'continue' defined as macro" }
-#define contract_assert 1
+#define contract_assert 1              // { dg-warning "keyword 'contract_assert' defined as macro" "" { target c++26 } }
 #define co_return 1                    // { dg-warning "keyword 'co_return' defined as macro" "" { target c++20 } }
 #define co_yield 1                     // { dg-warning "keyword 'co_yield' defined as macro" "" { target c++20 } }
 #define decltype 1                     // { dg-warning "keyword 'decltype' defined as macro" "" { target c++11 } }
index e7cc35d22d8888323228b6691bf8c570c5c01ff6..1c5bf7917e52d13daf1f8be951d5f4e2cb7f38a8 100644 (file)
@@ -3,6 +3,7 @@
 // { dg-do preprocess }
 // { dg-options "-Wkeyword-macro" }
 // { dg-additional-options "-fmodules" { target c++20 } }
+// { dg-additional-options "-fcontracts" { target c++26 } }
 
 // [lex.key]
 #undef alignas                         // { dg-warning "undefining keyword 'alignas'" "" { target c++11 } }
@@ -26,7 +27,7 @@
 #undef constexpr                       // { dg-warning "undefining keyword 'constexpr'" "" { target c++11 } }
 #undef constinit                       // { dg-warning "undefining keyword 'constinit'" "" { target c++20 } }
 #undef continue                                // { dg-warning "undefining keyword 'continue'" }
-#undef contract_assert
+#undef contract_assert                 // { dg-warning "undefining keyword 'contract_assert'" "" { target c++26 } }
 #undef co_return                       // { dg-warning "undefining keyword 'co_return'" "" { target c++20 } }
 #undef co_yield                                // { dg-warning "undefining keyword 'co_yield'" "" { target c++20 } }
 #undef decltype                                // { dg-warning "undefining keyword 'decltype'" "" { target c++11 } }