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;
{ "__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 },
{ "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 },
/* 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,
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,
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)
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)
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)
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)
#include "gimplify.h"
#include "intl.h"
#include "asan.h"
+#include "contracts.h"
/* Id for dumping the class hierarchy. */
int class_dump_id;
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);
#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) \
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
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)
{
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;
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:
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
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;
}
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;
}
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);
-/* 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.
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"
-/* 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.
#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 */
#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
return ret;
}
-static inline bool
+bool
is_invisiref_parm (const_tree t)
{
return ((TREE_CODE (t) == PARM_DECL || TREE_CODE (t) == RESULT_DECL)
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;
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);
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));
}
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.
CPTI_DSO_HANDLE,
CPTI_DCAST,
CPTI_META_INFO_TYPE,
+ CPTI_CONTRACT_VIOLATION_TYPE,
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
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)
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;
#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. */
unsigned coroutine_p : 1;
unsigned implicit_constexpr : 1;
unsigned escalated_p : 1;
+
unsigned xobj_func : 1;
unsigned spare : 7;
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;
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. */
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);
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);
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);
#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"
= 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. */
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);
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;
}
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
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);
if (flag_exceptions)
init_exception_processing ();
+ if (flag_contracts)
+ init_contracts ();
+
if (modules_p ())
init_modules (parse_in);
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);
int template_count,
tree in_namespace,
tree* attrlist,
+ tree contract_specifiers,
location_t location)
{
tree decl;
/* 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);
*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))
{
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
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:
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
|| 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)
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;
store_parm_decls (current_function_parms);
+ start_function_contracts (decl1);
+
if (!processing_template_decl
&& flag_lifetime_dse > 1
&& DECL_CONSTRUCTOR_P (decl1)
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;
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. */
/* Clean up. */
invoke_plugin_callbacks (PLUGIN_FINISH_PARSE_FUNCTION, fndecl);
- /* Build outlined functions for coroutines. */
+ /* Build outlined functions for coroutines and contracts. */
if (coroutine)
{
#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;
reconsider = true;
}
+ if (flag_contracts)
+ maybe_emit_violation_handler_wrappers ();
+
/* All templates have been instantiated. */
at_eof = 2;
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)
#include "c-family/known-headers.h"
#include "bitmap.h"
#include "builtins.h"
+#include "contracts.h"
#include "analyzer/analyzer-language.h"
\f
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;
}
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
tree exception_specification,
tree late_return_type,
tree requires_clause,
+ tree contract_specifiers,
tree std_attrs,
location_t parens_loc)
{
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)
{
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)
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,
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");
}
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))
exception_spec,
return_type,
trailing_requires_clause,
+ contract_specifiers,
std_attrs,
UNKNOWN_LOCATION);
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);
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);
}
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;
}
}
{
/* 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)
{
/* 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);
exception_specification,
late_return,
requires_clause,
+ contract_specifiers,
attrs,
parens_loc);
declarator->attributes = gnu_attrs;
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)
}
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);
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
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:
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,
#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. */
}
decl = register_specialization (tmpl, gen_tmpl, targs,
is_friend, 0);
+ if (flag_contracts)
+ remove_fn_contract_specifiers (result);
return decl;
}
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)
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. */
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),
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;
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))
#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
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
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");
}
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
}
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)
&& 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");
}
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. */
&& !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,
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);
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;
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))
-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
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
--- /dev/null
+// 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 );
+}
#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 } }
#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 } }
#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 } }
#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 } }
// { 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 } }
#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 } }
// { 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 } }
#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 } }