aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorJeff Chapman II <jchapman@lock3software.com>2021-06-29 16:52:56 -0400
committerJason Merrill <jason@redhat.com>2022-11-18 21:41:14 -0500
commit2efb237ffc68ec9bb17982434f5941bfa14f8b50 (patch)
tree1c4cc161c09a6cb9a87cd38f752ac9846e86fc54 /gcc/cp
parentea63396f6b08f88f1cde827e6cab94cd488f7fa7 (diff)
downloadgcc-2efb237ffc68ec9bb17982434f5941bfa14f8b50.zip
gcc-2efb237ffc68ec9bb17982434f5941bfa14f8b50.tar.gz
gcc-2efb237ffc68ec9bb17982434f5941bfa14f8b50.tar.bz2
c++: implement P1492 contracts
Implement the P1492 versions of contracts, along with extensions that support the emulation of N4820 and other proposals. This implementation assigns a concrete semantic (one of: ignore, assume, enforce, or observe) to each contract attribute depending on its labels and configuration options. Co-authored-by: Andrew Sutton <asutton@lock3software.com> Co-authored-by: Andrew Marmaduke <amarmaduke@lock3software.com> Co-authored-by: Michael Lopez <mlopez@lock3software.com> Co-authored-by: Jason Merrill <jason@redhat.com> gcc/ChangeLog: * doc/invoke.texi: Document contracts flags. gcc/c-family/ChangeLog: * c.opt: Add contracts flags. * c-cppbuiltin.cc (c_cpp_builtins): Add contracts feature-test macros. gcc/cp/ChangeLog: * cp-tree.h (enum cp_tree_index): Add CPTI_PSEUDO_CONTRACT_VIOLATION. (pseudo_contract_violation_type): New macro. (struct saved_scope): Add x_processing_contract_condition. (processing_contract_condition): New macro. (comparing_override_contracts): New variable decl. (find_contract): New inline. (set_decl_contracts): New inline. (get_contract_semantic): New inline. (set_contract_semantic): New inline. * constexpr.cc (cxx_eval_assert): Split out from... (cxx_eval_internal_function): ...here. (cxx_eval_constant_expression): Use it for contracts. (potential_constant_expression_1): Handle contracts. * cp-gimplify.cc (cp_genericize_r): Handle contracts. * cp-objcp-common.cc (cp_tree_size): Handle contracts. (cp_common_init_ts): Handle contracts. (cp_handle_option): Handle contracts. * decl.cc (duplicate_decls): Handle contracts. (check_tag_decl): Check for bogus contracts. (start_decl): Check flag_contracts. (grokfndecl): Call rebuild_postconditions. (grokdeclarator): Handle contract attributes. (start_preparsed_function): Call start_function_contracts. (finish_function): Call finish_function_contracts. * decl2.cc (cp_check_const_attributes): Skip contracts. (comdat_linkage): Handle outlined contracts. * error.cc (dump_type): Handle null TYPE_IDENTIFIER. * g++spec.cc (EXPERIMENTAL): New macro. (lang_specific_driver): Add -lstdc++exp if -fcontracts. * mangle.cc (write_encoding): Handle outlined contracts. * module.cc (trees_out::fn_parms_init): Handle outlined contracts. (trees_in::fn_parms_init): Likewise. (check_mergeable_decl): Likewise. (module_state_config::get_dialect): Record -fcontracts. * parser.h (struct cp_unparsed_functions_entry): Add contracts. * parser.cc (unparsed_contracts): New macro. (push_unparsed_function_queues): Adjust. (contract_attribute_p): New. (cp_parser_statement): Check contracts. (cp_parser_decl_specifier_seq): Handle contracts. (cp_parser_skip_to_closing_square_bracket): Split out... (cp_parser_skip_up_to_closing_square_bracket): ...this fn. (cp_parser_class_specifier): Do contract late parsing. (cp_parser_class_head): Check contracts. (cp_parser_contract_role): New. (cp_parser_contract_mode_opt): New. (find_error, contains_error_p): New. (cp_parser_contract_attribute_spec): New. (cp_parser_late_contract_condition): New. (cp_parser_std_attribute_spec): Handle contracts. (cp_parser_save_default_args): Also save contracts. * pt.cc (register_parameter_specializations): No longer static. (register_local_identity): New. (check_explicit_specialization): Call remove_contract_attributes. (tsubst_contract, tsubst_contract_attribute): New. (tsubst_contract_attributes): New. (tsubst_attribute): Add comment. (tsubst_copy): Also allow parm when processing_contract_condition. (tsubst_expr): Handle contracts. (regenerate_decl_from_template): Handle contracts. * search.cc (check_final_overrider): Compare contracts. * semantics.cc (set_cleanup_locs): Skip POSTCONDITION_STMT. (finish_non_static_data_member): Check contracts. (finish_this_expr): Check contracts. (process_outer_var_ref): Handle contracts. (finish_id_expression_1): Handle contracts. (apply_deduced_return_type): Adjust contracts. * tree.cc (handle_contract_attribute): New. (get_innermost_component, is_this_expression): New. (comparing_this_references): New. (equivalent_member_references): New. (cp_tree_equal): Check it. * typeck.cc (check_return_expr): Apply contracts. * Make-lang.in: Add contracts.o. * config-lang.in: Add contracts.cc. * cp-tree.def (ASSERTION_STMT, PRECONDITION_STMT) (POSTCONDITION_STMT): New. * contracts.h: New file. * contracts.cc: New file. gcc/testsuite/ChangeLog: * g++.dg/modules/modules.exp: Pass dg-options to link command. * lib/g++.exp: Add -L for libstdc++exp.a. * g++.dg/contracts/backtrace_handler/assert_fail.cpp: New test. * g++.dg/contracts/backtrace_handler/handle_contract_violation.cpp: New test. * g++.dg/contracts/contracts-access1.C: New test. * g++.dg/contracts/contracts-assume1.C: New test. * g++.dg/contracts/contracts-assume2.C: New test. * g++.dg/contracts/contracts-assume3.C: New test. * g++.dg/contracts/contracts-assume4.C: New test. * g++.dg/contracts/contracts-assume5.C: New test. * g++.dg/contracts/contracts-assume6.C: New test. * g++.dg/contracts/contracts-comdat1.C: New test. * g++.dg/contracts/contracts-config1.C: New test. * g++.dg/contracts/contracts-constexpr1.C: New test. * g++.dg/contracts/contracts-constexpr2.C: New test. * g++.dg/contracts/contracts-constexpr3.C: New test. * g++.dg/contracts/contracts-conversion1.C: New test. * g++.dg/contracts/contracts-ctor-dtor1.C: New test. * g++.dg/contracts/contracts-ctor-dtor2.C: New test. * g++.dg/contracts/contracts-cv1.C: New test. * g++.dg/contracts/contracts-deduced1.C: New test. * g++.dg/contracts/contracts-deduced2.C: New test. * g++.dg/contracts/contracts-friend1.C: New test. * g++.dg/contracts/contracts-ft1.C: New test. * g++.dg/contracts/contracts-ignore1.C: New test. * g++.dg/contracts/contracts-ignore2.C: New test. * g++.dg/contracts/contracts-large-return.C: New test. * g++.dg/contracts/contracts-multiline1.C: New test. * g++.dg/contracts/contracts-multiple-inheritance1.C: New test. * g++.dg/contracts/contracts-multiple-inheritance2.C: New test. * g++.dg/contracts/contracts-nested-class1.C: New test. * g++.dg/contracts/contracts-nested-class2.C: New test. * g++.dg/contracts/contracts-nocopy1.C: New test. * g++.dg/contracts/contracts-override.C: New test. * g++.dg/contracts/contracts-post1.C: New test. * g++.dg/contracts/contracts-post2.C: New test. * g++.dg/contracts/contracts-post3.C: New test. * g++.dg/contracts/contracts-post4.C: New test. * g++.dg/contracts/contracts-post5.C: New test. * g++.dg/contracts/contracts-post6.C: New test. * g++.dg/contracts/contracts-pre1.C: New test. * g++.dg/contracts/contracts-pre10.C: New test. * g++.dg/contracts/contracts-pre2.C: New test. * g++.dg/contracts/contracts-pre2a1.C: New test. * g++.dg/contracts/contracts-pre2a2.C: New test. * g++.dg/contracts/contracts-pre3.C: New test. * g++.dg/contracts/contracts-pre4.C: New test. * g++.dg/contracts/contracts-pre5.C: New test. * g++.dg/contracts/contracts-pre6.C: New test. * g++.dg/contracts/contracts-pre7.C: New test. * g++.dg/contracts/contracts-pre9.C: New test. * g++.dg/contracts/contracts-redecl1.C: New test. * g++.dg/contracts/contracts-redecl2.C: New test. * g++.dg/contracts/contracts-redecl3.C: New test. * g++.dg/contracts/contracts-redecl4.C: New test. * g++.dg/contracts/contracts-redecl5.C: New test. * g++.dg/contracts/contracts-redecl6.C: New test. * g++.dg/contracts/contracts-redecl7.C: New test. * g++.dg/contracts/contracts-redecl8.C: New test. * g++.dg/contracts/contracts-tmpl-attr1.C: New test. * g++.dg/contracts/contracts-tmpl-spec1.C: New test. * g++.dg/contracts/contracts-tmpl-spec2.C: New test. * g++.dg/contracts/contracts-tmpl-spec3.C: New test. * g++.dg/contracts/contracts1.C: New test. * g++.dg/contracts/contracts10.C: New test. * g++.dg/contracts/contracts11.C: New test. * g++.dg/contracts/contracts12.C: New test. * g++.dg/contracts/contracts13.C: New test. * g++.dg/contracts/contracts14.C: New test. * g++.dg/contracts/contracts15.C: New test. * g++.dg/contracts/contracts16.C: New test. * g++.dg/contracts/contracts17.C: New test. * g++.dg/contracts/contracts18.C: New test. * g++.dg/contracts/contracts19.C: New test. * g++.dg/contracts/contracts2.C: New test. * g++.dg/contracts/contracts20.C: New test. * g++.dg/contracts/contracts22.C: New test. * g++.dg/contracts/contracts24.C: New test. * g++.dg/contracts/contracts25.C: New test. * g++.dg/contracts/contracts3.C: New test. * g++.dg/contracts/contracts35.C: New test. * g++.dg/contracts/contracts4.C: New test. * g++.dg/contracts/contracts5.C: New test. * g++.dg/contracts/contracts6.C: New test. * g++.dg/contracts/contracts7.C: New test. * g++.dg/contracts/contracts8.C: New test. * g++.dg/contracts/contracts9.C: New test. * g++.dg/modules/contracts-1_a.C: New test. * g++.dg/modules/contracts-1_b.C: New test. * g++.dg/modules/contracts-2_a.C: New test. * g++.dg/modules/contracts-2_b.C: New test. * g++.dg/modules/contracts-2_c.C: New test. * g++.dg/modules/contracts-3_a.C: New test. * g++.dg/modules/contracts-3_b.C: New test. * g++.dg/modules/contracts-4_a.C: New test. * g++.dg/modules/contracts-4_b.C: New test. * g++.dg/modules/contracts-4_c.C: New test. * g++.dg/modules/contracts-4_d.C: New test. * g++.dg/modules/contracts-tpl-friend-1_a.C: New test. * g++.dg/modules/contracts-tpl-friend-1_b.C: New test. * g++.dg/contracts/backtrace_handler/Makefile: New test. * g++.dg/contracts/backtrace_handler/README: New test. * g++.dg/contracts/backtrace_handler/example_out.txt: New test. * g++.dg/contracts/backtrace_handler/example_pretty.txt: New test. * g++.dg/contracts/backtrace_handler/prettytrace.sh: New test. * g++.dg/contracts/except_preload_handler/Makefile: New test. * g++.dg/contracts/except_preload_handler/README: New test. * g++.dg/contracts/except_preload_handler/assert_fail.cpp: New test. * g++.dg/contracts/except_preload_handler/handle_contract_violation.cpp: New test. * g++.dg/contracts/noexcept_preload_handler/Makefile: New test. * g++.dg/contracts/noexcept_preload_handler/README: New test. * g++.dg/contracts/noexcept_preload_handler/assert_fail.cpp: New test. * g++.dg/contracts/noexcept_preload_handler/handle_contract_violation.cpp: New test. * g++.dg/contracts/preload_handler/Makefile: New test. * g++.dg/contracts/preload_handler/README: New test. * g++.dg/contracts/preload_handler/assert_fail.cpp: New test. * g++.dg/contracts/preload_handler/handle_contract_violation.cpp: New test. * g++.dg/contracts/preload_nocontinue_handler/Makefile: New test. * g++.dg/contracts/preload_nocontinue_handler/README: New test. * g++.dg/contracts/preload_nocontinue_handler/assert_fail.cpp: New test. * g++.dg/contracts/preload_nocontinue_handler/handle_contract_violation.cpp: New test. * g++.dg/contracts/preload_nocontinue_handler/nocontinue.cpp: New test.
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/Make-lang.in2
-rw-r--r--gcc/cp/config-lang.in1
-rw-r--r--gcc/cp/constexpr.cc121
-rw-r--r--gcc/cp/contracts.cc2240
-rw-r--r--gcc/cp/contracts.h305
-rw-r--r--gcc/cp/cp-gimplify.cc17
-rw-r--r--gcc/cp/cp-objcp-common.cc41
-rw-r--r--gcc/cp/cp-tree.def11
-rw-r--r--gcc/cp/cp-tree.h64
-rw-r--r--gcc/cp/decl.cc115
-rw-r--r--gcc/cp/decl2.cc15
-rw-r--r--gcc/cp/error.cc3
-rw-r--r--gcc/cp/g++spec.cc12
-rw-r--r--gcc/cp/mangle.cc7
-rw-r--r--gcc/cp/module.cc30
-rw-r--r--gcc/cp/parser.cc522
-rw-r--r--gcc/cp/parser.h3
-rw-r--r--gcc/cp/pt.cc164
-rw-r--r--gcc/cp/search.cc28
-rw-r--r--gcc/cp/semantics.cc28
-rw-r--r--gcc/cp/tree.cc67
-rw-r--r--gcc/cp/typeck.cc13
22 files changed, 3745 insertions, 64 deletions
diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in
index 291835d..af25bdc 100644
--- a/gcc/cp/Make-lang.in
+++ b/gcc/cp/Make-lang.in
@@ -89,7 +89,7 @@ CXX_AND_OBJCXX_OBJS = \
cp/call.o cp/class.o cp/constexpr.o cp/constraint.o \
cp/coroutines.o cp/cp-gimplify.o \
cp/cp-objcp-common.o cp/cp-ubsan.o \
- cp/cvt.o cp/cxx-pretty-print.o \
+ cp/cvt.o cp/contracts.o cp/cxx-pretty-print.o \
cp/decl.o cp/decl2.o cp/dump.o \
cp/error.o cp/except.o cp/expr.o \
cp/friend.o cp/init.o \
diff --git a/gcc/cp/config-lang.in b/gcc/cp/config-lang.in
index 9ac3dee..f9e5f32 100644
--- a/gcc/cp/config-lang.in
+++ b/gcc/cp/config-lang.in
@@ -39,6 +39,7 @@ gtfiles="\
\$(srcdir)/c-family/c-common.cc \$(srcdir)/c-family/c-format.cc \
\$(srcdir)/c-family/c-cppbuiltin.cc \$(srcdir)/c-family/c-pragma.cc \
\$(srcdir)/cp/call.cc \$(srcdir)/cp/class.cc \$(srcdir)/cp/constexpr.cc \
+\$(srcdir)/cp/contracts.cc \
\$(srcdir)/cp/constraint.cc \$(srcdir)/cp/coroutines.cc \
\$(srcdir)/cp/cp-gimplify.cc \
\$(srcdir)/cp/cp-lang.cc \$(srcdir)/cp/cp-objcp-common.cc \
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index a390cf9..ad43897 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see
#include "stringpool.h"
#include "attribs.h"
#include "fold-const.h"
+#include "intl.h"
static bool verify_constant (tree, bool, bool *, bool *);
#define VERIFY_CONSTANT(X) \
@@ -1988,6 +1989,61 @@ diagnose_failing_condition (tree bad, location_t cloc, bool show_expr_p,
inform (cloc, "%qE evaluates to false", bad);
}
+/* Process an assert/assume of ORIG_ARG. If it's not supposed to be evaluated,
+ do it without changing the current evaluation state. If it evaluates to
+ false, complain and return false; otherwise, return true. */
+
+static bool
+cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg,
+ location_t loc, bool evaluated,
+ bool *non_constant_p, bool *overflow_p)
+{
+ if (*non_constant_p)
+ return true;
+
+ tree eval;
+ if (!evaluated)
+ {
+ if (!potential_rvalue_constant_expression (arg))
+ return true;
+
+ constexpr_ctx new_ctx = *ctx;
+ new_ctx.quiet = true;
+ bool new_non_constant_p = false, new_overflow_p = false;
+ /* Avoid modification of existing values. */
+ modifiable_tracker ms (new_ctx.global);
+ eval = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
+ &new_non_constant_p,
+ &new_overflow_p);
+ }
+ else
+ eval = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
+ non_constant_p,
+ overflow_p);
+ if (!*non_constant_p && integer_zerop (eval))
+ {
+ if (!ctx->quiet)
+ {
+ /* See if we can find which clause was failing
+ (for logical AND). */
+ tree bad = find_failing_clause (ctx, arg);
+ /* If not, or its location is unusable, fall back to the
+ previous location. */
+ location_t cloc = cp_expr_loc_or_loc (bad, loc);
+
+ /* Report the error. */
+ auto_diagnostic_group d;
+ error_at (cloc, msg);
+ diagnose_failing_condition (bad, cloc, true, ctx);
+ return bad;
+ }
+ *non_constant_p = true;
+ return false;
+ }
+
+ return true;
+}
+
/* Evaluate a call T to a GCC internal function when possible and return
the evaluated result or, under the control of CTX, give an error, set
NON_CONSTANT_P, and return the unevaluated call T otherwise. */
@@ -2008,41 +2064,11 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
return void_node;
case IFN_ASSUME:
- if (potential_rvalue_constant_expression (CALL_EXPR_ARG (t, 0)))
- {
- constexpr_ctx new_ctx = *ctx;
- new_ctx.quiet = true;
- tree arg = CALL_EXPR_ARG (t, 0);
- bool new_non_constant_p = false, new_overflow_p = false;
- /* Avoid modification of existing values. */
- modifiable_tracker ms (new_ctx.global);
- arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
- &new_non_constant_p,
- &new_overflow_p);
- if (!new_non_constant_p && !new_overflow_p && integer_zerop (arg))
- {
- if (!*non_constant_p && !ctx->quiet)
- {
- /* See if we can find which clause was failing
- (for logical AND). */
- tree bad = find_failing_clause (&new_ctx,
- CALL_EXPR_ARG (t, 0));
- /* If not, or its location is unusable, fall back to the
- previous location. */
- location_t cloc = cp_expr_loc_or_loc (bad, EXPR_LOCATION (t));
-
- auto_diagnostic_group d;
-
- /* Report the error. */
- error_at (cloc,
- "failed %<assume%> attribute assumption");
- diagnose_failing_condition (bad, cloc, false, &new_ctx);
- }
-
- *non_constant_p = true;
- return t;
- }
- }
+ if (!cxx_eval_assert (ctx, CALL_EXPR_ARG (t, 0),
+ G_("failed %<assume%> attribute assumption"),
+ EXPR_LOCATION (t), /*eval*/false,
+ non_constant_p, overflow_p))
+ return t;
return void_node;
case IFN_ADD_OVERFLOW:
@@ -7901,6 +7927,24 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
r = void_node;
break;
+ case ASSERTION_STMT:
+ case PRECONDITION_STMT:
+ case POSTCONDITION_STMT:
+ {
+ contract_semantic semantic = get_contract_semantic (t);
+ if (semantic == CCS_IGNORE)
+ break;
+
+ if (!cxx_eval_assert (ctx, CONTRACT_CONDITION (t),
+ G_("contract predicate is false in "
+ "constant expression"),
+ EXPR_LOCATION (t), checked_contract_p (semantic),
+ non_constant_p, overflow_p))
+ *non_constant_p = true;
+ r = void_node;
+ }
+ break;
+
case TEMPLATE_ID_EXPR:
{
/* We can evaluate template-id that refers to a concept only if
@@ -9894,6 +9938,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
return false;
}
+ case ASSERTION_STMT:
+ case PRECONDITION_STMT:
+ case POSTCONDITION_STMT:
+ if (!checked_contract_p (get_contract_semantic (t)))
+ return true;
+ return RECUR (CONTRACT_CONDITION (t), rval);
+
case LABEL_EXPR:
t = LABEL_EXPR_LABEL (t);
if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23)
diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc
new file mode 100644
index 0000000..2639643
--- /dev/null
+++ b/gcc/cp/contracts.cc
@@ -0,0 +1,2240 @@
+/* Definitions for C++ contract levels
+ Copyright (C) 2020-2022 Free Software Foundation, Inc.
+ Contributed by Jeff Chapman II (jchapman@lock3software.com)
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+/* Design Notes
+
+ A function is called a "guarded" function if it has pre or post contract
+ attributes. A contract is considered an "active" contract if runtime code is
+ needed for the contract under the current contract configuration.
+
+ pre and post contract attributes are parsed and stored in DECL_ATTRIBUTES.
+ assert contracts are parsed and wrapped in statements. When genericizing, all
+ active and assumed contracts are transformed into an if block. An observed
+ contract:
+
+ [[ pre: v > 0 ]]
+
+ is transformed into:
+
+ if (!(v > 0)) {
+ handle_contract_violation(__pseudo_contract_violation{
+ 5, // line_number,
+ "main.cpp", // file_name,
+ "fun", // function_name,
+ "v > 0", // comment,
+ "default", // assertion_level,
+ "default", // assertion_role,
+ MAYBE_CONTINUE, // continuation_mode
+ });
+ terminate (); // if NEVER_CONTINUE
+ }
+
+ We use an internal type with the same layout as contract_violation rather
+ than try to define the latter internally and somehow deal with its actual
+ definition in a TU that includes <contract>.
+
+ ??? is it worth factoring out the calls to handle_contract_violation and
+ terminate into a local function?
+
+ Assumed contracts use the same implementation as C++23 [[assume]].
+
+ Parsing of pre and post contract conditions need to be deferred when the
+ contracts are attached to a member function. The postcondition identifier
+ cannot be used before the deduced return type of an auto function is used,
+ except when used in a defining declaration in which case they conditions are
+ fully parsed once the body is finished (see cpp2a/contracts-deduced{1,2}.C).
+
+ A list of pre and post contracts can either be repeated in their entirety or
+ completely absent in subsequent declarations. If contract lists appear on two
+ matching declarations, their contracts have to be equivalent. In general this
+ means that anything before the colon have to be token equivalent and the
+ condition must be cp_tree_equal (primarily to allow for parameter renaming).
+
+ Contracts on overrides must match those present on (all of) the overridee(s).
+
+ Template specializations may have their own contracts. If no contracts are
+ specified on the initial specialization they're assumed to be the same as
+ the primary template. Specialization redeclarations must then match either
+ the primary template (if they were unspecified originally), or those
+ specified on the specialization.
+
+
+ For non-cdtors two functions are generated for ease of implementation and to
+ avoid some cases where code bloat may occurr. These are the DECL_PRE_FN and
+ DECL_POST_FN. Each handles checking either the set of pre or post contracts
+ of a guarded function.
+
+ int fun(int v)
+ [[ pre: v > 0 ]]
+ [[ post r: r < 0 ]]
+ {
+ return -v;
+ }
+
+ The original decl is left alone and instead calls are generated to pre/post
+ functions within the body:
+
+ void fun.pre(int v)
+ {
+ [[ assert: v > 0 ]];
+ }
+ int fun.post(int v, int __r)
+ {
+ [[ assert: __r < 0 ]];
+ return __r;
+ }
+ int fun(int v)
+ {
+ fun.pre(v);
+ return fun.post(v, -v);
+ }
+
+ If fun returns in memory, the return value is not passed through the post
+ function; instead, the return object is initialized directly and then passed
+ to the post function by invisible reference.
+
+ This sides steps a number of issues with having to rewrite the bodies or
+ rewrite the parsed conditions as the parameters to the original function
+ changes (as happens during redeclaration). The ultimate goal is to get
+ something that optimizes well along the lines of
+
+ int fun(int v)
+ {
+ [[ assert: v > 0 ]];
+ auto &&__r = -v;
+ goto out;
+ out:
+ [[ assert: __r < 0 ]];
+ return __r;
+ }
+
+ With the idea being that multiple return statements could collapse the
+ function epilogue after inlining the pre/post functions. clang is able
+ to collapse common function epilogues, while gcc needs -O3 -Os combined.
+
+ Directly laying the pre contracts down in the function body doesn't have
+ many issues. The post contracts may need to be repeated multiple times, once
+ for each return, or a goto epilogue would need to be generated.
+ For this initial implementation, generating function calls and letting
+ later optimizations decide whether to inline and duplicate the actual
+ checks or whether to collapse the shared epilogue was chosen.
+
+ For cdtors a post contract is implemented using a CLEANUP_STMT.
+
+ FIXME the compiler already shores cleanup code on multiple exit paths, so
+ this outlining seems unnecessary if we represent the postcondition as a
+ cleanup for all functions.
+
+ More helpful for optimization might be to make the contracts a wrapper
+ function (for non-variadic functions), that could be inlined into a
+ caller while preserving the call to the actual function? Either that or
+ mirror a never-continue post contract with an assume in the caller. */
+
+#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"
+
+const int max_custom_roles = 32;
+static contract_role contract_build_roles[max_custom_roles] = {
+};
+
+bool valid_configs[CCS_MAYBE + 1][CCS_MAYBE + 1] = {
+ { 0, 0, 0, 0, 0, },
+ { 0, 1, 0, 0, 0, },
+ { 0, 1, 1, 1, 1, },
+ { 0, 1, 1, 1, 1, },
+ { 0, 1, 0, 0, 1, },
+};
+
+void
+validate_contract_role (contract_role *role)
+{
+ gcc_assert (role);
+ if (!unchecked_contract_p (role->axiom_semantic))
+ error ("axiom contract semantic must be %<assume%> or %<ignore%>");
+
+ if (!valid_configs[role->default_semantic][role->audit_semantic] )
+ warning (0, "the %<audit%> semantic should be at least as strong as "
+ "the %<default%> semantic");
+}
+
+contract_semantic
+lookup_concrete_semantic (const char *name)
+{
+ if (strcmp (name, "ignore") == 0)
+ return CCS_IGNORE;
+ if (strcmp (name, "assume") == 0)
+ return CCS_ASSUME;
+ if (strcmp (name, "check_never_continue") == 0
+ || strcmp (name, "never") == 0
+ || strcmp (name, "abort") == 0)
+ return CCS_NEVER;
+ if (strcmp (name, "check_maybe_continue") == 0
+ || strcmp (name, "maybe") == 0)
+ return CCS_MAYBE;
+ error ("'%s' is not a valid explicit concrete semantic", name);
+ return CCS_INVALID;
+}
+
+/* Compare role and name up to either the NUL terminator or the first
+ occurrence of colon. */
+
+static bool
+role_name_equal (const char *role, const char *name)
+{
+ size_t role_len = strchrnul (role, ':') - role;
+ size_t name_len = strchrnul (name, ':') - name;
+ if (role_len != name_len)
+ return false;
+ return strncmp (role, name, role_len) == 0;
+}
+
+static bool
+role_name_equal (contract_role *role, const char *name)
+{
+ if (role->name == NULL)
+ return false;
+ return role_name_equal (role->name, name);
+}
+
+contract_role *
+get_contract_role (const char *name)
+{
+ for (int i = 0; i < max_custom_roles; ++i)
+ {
+ contract_role *potential = contract_build_roles + i;
+ if (role_name_equal (potential, name))
+ return potential;
+ }
+ if (role_name_equal (name, "default") || role_name_equal (name, "review"))
+ {
+ setup_default_contract_role (false);
+ return get_contract_role (name);
+ }
+ return NULL;
+}
+
+contract_role *
+add_contract_role (const char *name,
+ contract_semantic des,
+ contract_semantic aus,
+ contract_semantic axs,
+ bool update)
+{
+ for (int i = 0; i < max_custom_roles; ++i)
+ {
+ contract_role *potential = contract_build_roles + i;
+ if (potential->name != NULL
+ && !role_name_equal (potential, name))
+ continue;
+ if (potential->name != NULL && !update)
+ return potential;
+ potential->name = name;
+ potential->default_semantic = des;
+ potential->audit_semantic = aus;
+ potential->axiom_semantic = axs;
+ return potential;
+ }
+ return NULL;
+}
+
+enum contract_build_level { OFF, DEFAULT, AUDIT };
+static bool flag_contract_continuation_mode = false;
+static bool flag_contract_assumption_mode = true;
+static int flag_contract_build_level = DEFAULT;
+
+static bool contracts_p1332_default = false, contracts_p1332_review = false,
+ contracts_std = false, contracts_p1429 = false;
+
+static contract_semantic
+get_concrete_check ()
+{
+ return flag_contract_continuation_mode ? CCS_MAYBE : CCS_NEVER;
+}
+
+static contract_semantic
+get_concrete_axiom_semantic ()
+{
+ return flag_contract_assumption_mode ? CCS_ASSUME : CCS_IGNORE;
+}
+
+void
+setup_default_contract_role (bool update)
+{
+ contract_semantic check = get_concrete_check ();
+ contract_semantic axiom = get_concrete_axiom_semantic ();
+ switch (flag_contract_build_level)
+ {
+ case OFF:
+ add_contract_role ("default", CCS_IGNORE, CCS_IGNORE, axiom, update);
+ add_contract_role ("review", CCS_IGNORE, CCS_IGNORE, CCS_IGNORE, update);
+ break;
+ case DEFAULT:
+ add_contract_role ("default", check, CCS_IGNORE, axiom, update);
+ add_contract_role ("review", check, CCS_IGNORE, CCS_IGNORE, update);
+ break;
+ case AUDIT:
+ add_contract_role ("default", check, check, axiom, update);
+ add_contract_role ("review", check, check, CCS_IGNORE, update);
+ break;
+ }
+}
+
+contract_semantic
+map_contract_semantic (const char *ident)
+{
+ if (strcmp (ident, "ignore") == 0)
+ return CCS_IGNORE;
+ else if (strcmp (ident, "assume") == 0)
+ return CCS_ASSUME;
+ else if (strcmp (ident, "check_never_continue") == 0)
+ return CCS_NEVER;
+ else if (strcmp (ident, "check_maybe_continue") == 0)
+ return CCS_MAYBE;
+ return CCS_INVALID;
+}
+
+contract_level
+map_contract_level (const char *ident)
+{
+ if (strcmp (ident, "default") == 0)
+ return CONTRACT_DEFAULT;
+ else if (strcmp (ident, "audit") == 0)
+ return CONTRACT_AUDIT;
+ else if (strcmp (ident, "axiom") == 0)
+ return CONTRACT_AXIOM;
+ return CONTRACT_INVALID;
+}
+
+
+void
+handle_OPT_fcontract_build_level_ (const char *arg)
+{
+ if (contracts_p1332_default || contracts_p1332_review || contracts_p1429)
+ {
+ error ("%<-fcontract-build-level=%> cannot be mixed with p1332/p1429");
+ return;
+ }
+ else
+ contracts_std = true;
+
+ if (strcmp (arg, "off") == 0)
+ flag_contract_build_level = OFF;
+ else if (strcmp (arg, "default") == 0)
+ flag_contract_build_level = DEFAULT;
+ else if (strcmp (arg, "audit") == 0)
+ flag_contract_build_level = AUDIT;
+ else
+ error ("%<-fcontract-build-level=%> must be off|default|audit");
+
+ setup_default_contract_role ();
+}
+
+void
+handle_OPT_fcontract_assumption_mode_ (const char *arg)
+{
+ if (contracts_p1332_default || contracts_p1332_review || contracts_p1429)
+ {
+ error ("%<-fcontract-assumption-mode=%> cannot be mixed with p1332/p1429");
+ return;
+ }
+ else
+ contracts_std = true;
+
+ if (strcmp (arg, "on") == 0)
+ flag_contract_assumption_mode = true;
+ else if (strcmp (arg, "off") == 0)
+ flag_contract_assumption_mode = false;
+ else
+ error ("%<-fcontract-assumption-mode=%> must be %<on%> or %<off%>");
+
+ setup_default_contract_role ();
+}
+
+void
+handle_OPT_fcontract_continuation_mode_ (const char *arg)
+{
+ if (contracts_p1332_default || contracts_p1332_review || contracts_p1429)
+ {
+ error ("%<-fcontract-continuation-mode=%> cannot be mixed with p1332/p1429");
+ return;
+ }
+ else
+ contracts_std = true;
+
+ if (strcmp (arg, "on") == 0)
+ flag_contract_continuation_mode = true;
+ else if (strcmp (arg, "off") == 0)
+ flag_contract_continuation_mode = false;
+ else
+ error ("%<-fcontract-continuation-mode=%> must be %<on%> or %<off%>");
+
+ setup_default_contract_role ();
+}
+
+void
+handle_OPT_fcontract_role_ (const char *arg)
+{
+ const char *name = arg;
+ const char *vals = strchr (name, ':');
+ if (vals == NULL)
+ {
+ error ("%<-fcontract-role=%> must be in the form role:semantics");
+ return;
+ }
+
+ contract_semantic dess = CCS_INVALID, auss = CCS_INVALID, axss = CCS_INVALID;
+ char *des = NULL, *aus = NULL, *axs = NULL;
+ des = xstrdup (vals + 1);
+
+ aus = strchr (des, ',');
+ if (aus == NULL)
+ {
+ error ("%<-fcontract-role=%> semantics must include default,audit,axiom values");
+ goto validate;
+ }
+ *aus = '\0'; // null terminate des
+ aus = aus + 1; // move past null
+
+ axs = strchr (aus, ',');
+ if (axs == NULL)
+ {
+ error ("%<-fcontract-role=%> semantics must include default,audit,axiom values");
+ goto validate;
+ }
+ *axs = '\0'; // null terminate aus
+ axs = axs + 1; // move past null
+
+ dess = lookup_concrete_semantic (des);
+ auss = lookup_concrete_semantic (aus);
+ axss = lookup_concrete_semantic (axs);
+validate:
+ free (des);
+ if (dess == CCS_INVALID || auss == CCS_INVALID || axss == CCS_INVALID)
+ return;
+
+ bool is_defalult_role = role_name_equal (name, "default");
+ bool is_review_role = role_name_equal (name, "review");
+ bool is_std_role = is_defalult_role || is_review_role;
+ if ((contracts_std && is_std_role) || (contracts_p1429 && is_defalult_role))
+ {
+ error ("%<-fcontract-role=%> cannot be mixed with std/p1429 contract flags");
+ return;
+ }
+ else if (is_std_role)
+ {
+ contracts_p1332_default |= is_defalult_role;
+ contracts_p1332_review |= is_review_role;
+ }
+
+ contract_role *role = add_contract_role (name, dess, auss, axss);
+
+ if (role == NULL)
+ {
+ // TODO: not enough space?
+ error ("%<-fcontract-level=%> too many custom roles");
+ return;
+ }
+ else
+ validate_contract_role (role);
+}
+
+void
+handle_OPT_fcontract_semantic_ (const char *arg)
+{
+ if (!strchr (arg, ':'))
+ {
+ error ("%<-fcontract-semantic=%> must be in the form level:semantic");
+ return;
+ }
+
+ if (contracts_std || contracts_p1332_default)
+ {
+ error ("%<-fcontract-semantic=%> cannot be mixed with std/p1332 contract flags");
+ return;
+ }
+ contracts_p1429 = true;
+
+ contract_role *role = get_contract_role ("default");
+ if (!role)
+ {
+ error ("%<-fcontract-semantic=%> cannot find default role");
+ return;
+ }
+
+ const char *semantic = strchr (arg, ':') + 1;
+ contract_semantic sem = lookup_concrete_semantic (semantic);
+ if (sem == CCS_INVALID)
+ return;
+
+ if (strncmp ("default:", arg, 8) == 0)
+ role->default_semantic = sem;
+ else if (strncmp ("audit:", arg, 6) == 0)
+ role->audit_semantic = sem;
+ else if (strncmp ("axiom:", arg, 6) == 0)
+ role->axiom_semantic = sem;
+ else
+ error ("%<-fcontract-semantic=%> level must be default, audit, or axiom");
+ validate_contract_role (role);
+}
+
+/* Convert a contract CONFIG into a contract_mode. */
+
+static contract_mode
+contract_config_to_mode (tree config)
+{
+ if (config == NULL_TREE)
+ return contract_mode (CONTRACT_DEFAULT, get_default_contract_role ());
+
+ /* TREE_LIST has TREE_VALUE is a level and TREE_PURPOSE is role. */
+ if (TREE_CODE (config) == TREE_LIST)
+ {
+ contract_role *role = NULL;
+ if (TREE_PURPOSE (config))
+ role = get_contract_role (IDENTIFIER_POINTER (TREE_PURPOSE (config)));
+ if (!role)
+ role = get_default_contract_role ();
+
+ contract_level level =
+ map_contract_level (IDENTIFIER_POINTER (TREE_VALUE (config)));
+ return contract_mode (level, role);
+ }
+
+ /* Literal semantic. */
+ gcc_assert (TREE_CODE (config) == IDENTIFIER_NODE);
+ contract_semantic semantic =
+ map_contract_semantic (IDENTIFIER_POINTER (config));
+ return contract_mode (semantic);
+}
+
+/* Convert a contract's config into a concrete semantic using the current
+ contract semantic mapping. */
+
+static contract_semantic
+compute_concrete_semantic (tree contract)
+{
+ contract_mode mode = contract_config_to_mode (CONTRACT_MODE (contract));
+ /* Compute the concrete semantic for the contract. */
+ if (!flag_contract_mode)
+ /* If contracts are off, treat all contracts as ignore. */
+ return CCS_IGNORE;
+ else if (mode.kind == contract_mode::cm_invalid)
+ return CCS_INVALID;
+ else if (mode.kind == contract_mode::cm_explicit)
+ return mode.get_semantic ();
+ else
+ {
+ gcc_assert (mode.get_role ());
+ gcc_assert (mode.get_level () != CONTRACT_INVALID);
+ contract_level level = mode.get_level ();
+ contract_role *role = mode.get_role ();
+ if (level == CONTRACT_DEFAULT)
+ return role->default_semantic;
+ else if (level == CONTRACT_AUDIT)
+ return role->audit_semantic;
+ else if (level == CONTRACT_AXIOM)
+ return role->axiom_semantic;
+ }
+ gcc_assert (false);
+}
+
+/* Return true if any contract in CONTRACT_ATTRs is not yet parsed. */
+
+bool
+contract_any_deferred_p (tree contract_attr)
+{
+ for (; contract_attr; contract_attr = CONTRACT_CHAIN (contract_attr))
+ if (CONTRACT_CONDITION_DEFERRED_P (CONTRACT_STATEMENT (contract_attr)))
+ return true;
+ return false;
+}
+
+/* Returns true if all attributes are contracts. */
+
+bool
+all_attributes_are_contracts_p (tree attributes)
+{
+ for (; attributes; attributes = TREE_CHAIN (attributes))
+ if (!cxx_contract_attribute_p (attributes))
+ return false;
+ return true;
+}
+
+/* Mark most of a contract as being invalid. */
+
+tree
+invalidate_contract (tree t)
+{
+ if (TREE_CODE (t) == POSTCONDITION_STMT && POSTCONDITION_IDENTIFIER (t))
+ POSTCONDITION_IDENTIFIER (t) = error_mark_node;
+ CONTRACT_CONDITION (t) = error_mark_node;
+ CONTRACT_COMMENT (t) = error_mark_node;
+ return t;
+}
+
+/* Returns an invented parameter declration 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;
+
+ tree decl = build_lang_decl (PARM_DECL, id, type);
+ DECL_ARTIFICIAL (decl) = true;
+ DECL_SOURCE_LOCATION (decl) = id.get_location ();
+
+ pushdecl (decl);
+ return 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. Emit a
+ diagnostic if it is not. Returns TRUE if the result is OK and false
+ otherwise. */
+
+bool
+check_postcondition_result (tree decl, tree type, location_t loc)
+{
+ if (VOID_TYPE_P (type))
+ {
+ const char* what;
+ if (DECL_CONSTRUCTOR_P (decl))
+ what = "constructor";
+ else if (DECL_DESTRUCTOR_P (decl))
+ what = "destructor";
+ else
+ what = "function";
+ error_at (loc, "%s does not return a value to test", what);
+ return false;
+ }
+
+ return true;
+}
+
+/* Instantiate each postcondition with the return type to finalize the
+ attribute. */
+
+void
+rebuild_postconditions (tree decl)
+{
+ tree type = TREE_TYPE (TREE_TYPE (decl));
+ tree attributes = DECL_CONTRACTS (decl);
+
+ for (; attributes ; attributes = TREE_CHAIN (attributes))
+ {
+ if (!cxx_contract_attribute_p (attributes))
+ continue;
+ tree contract = TREE_VALUE (TREE_VALUE (attributes));
+ if (TREE_CODE (contract) != POSTCONDITION_STMT)
+ continue;
+ tree condition = CONTRACT_CONDITION (contract);
+
+ /* 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;
+
+ /* Always update the context of the result variable so that it can
+ be remapped by remap_contracts. */
+ DECL_CONTEXT (oldvar) = decl;
+
+ /* If the return type is undeduced, defer until later. */
+ if (TREE_CODE (type) == TEMPLATE_TYPE_PARM)
+ return;
+
+ /* Check the postcondition variable. */
+ location_t loc = DECL_SOURCE_LOCATION (oldvar);
+ if (!check_postcondition_result (decl, type, loc))
+ {
+ invalidate_contract (contract);
+ continue;
+ }
+
+ /* "Instantiate" the result variable using the known type. Also update
+ the context so the inliner will actually remap this the parameter when
+ generating contract checks. */
+ 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 (decl); t != NULL_TREE; t = TREE_CHAIN (t))
+ register_local_identity (t);
+ register_local_specialization (newvar, oldvar);
+
+ ++processing_contract_condition;
+ condition = tsubst_expr (condition, make_tree_vec (0),
+ tf_warning_or_error, decl);
+ --processing_contract_condition;
+
+ /* Update the contract condition and result. */
+ POSTCONDITION_IDENTIFIER (contract) = newvar;
+ CONTRACT_CONDITION (contract) = finish_contract_condition (condition);
+ }
+}
+
+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 (condition.get_start (),
+ condition.get_finish ());
+ if (!str)
+ {
+ /* FIXME cases where we end up here
+ #line macro usage (oof)
+ contracts10.C
+ contracts11.C */
+ 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 attribute, tree mode, tree result, cp_expr condition,
+ location_t loc)
+{
+ tree_code code;
+ if (is_attribute_p ("assert", attribute))
+ code = ASSERTION_STMT;
+ else if (is_attribute_p ("pre", attribute))
+ code = PRECONDITION_STMT;
+ else if (is_attribute_p ("post", attribute))
+ code = POSTCONDITION_STMT;
+ 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;
+ tree type = void_type_node;
+ if (code != POSTCONDITION_STMT)
+ contract = build3_loc (loc, code, type, mode, NULL_TREE, NULL_TREE);
+ else
+ contract = build4_loc (loc, code, type, mode, NULL_TREE, NULL_TREE, result);
+
+ /* Determine the concrete semantic. */
+ set_contract_semantic (contract, compute_concrete_semantic (contract));
+
+ /* 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);
+ CONTRACT_CONDITION (contract) = condition;
+
+ return contract;
+}
+
+/* Build the contract attribute specifier where IDENTIFIER is one of 'pre',
+ 'post' or 'assert' and CONTRACT is the underlying statement. */
+tree
+finish_contract_attribute (tree identifier, tree contract)
+{
+ if (contract == error_mark_node)
+ return error_mark_node;
+
+ tree attribute = build_tree_list (build_tree_list (NULL_TREE, identifier),
+ build_tree_list (NULL_TREE, contract));
+
+
+ /* Mark the attribute as dependent if the condition is dependent.
+
+ TODO: I'm not sure this is strictly necessary. It's going to be marked as
+ such by a subroutine of cplus_decl_attributes. */
+ tree condition = CONTRACT_CONDITION (contract);
+ if (TREE_CODE (condition) == DEFERRED_PARSE
+ || value_dependent_expression_p (condition))
+ ATTR_IS_DEPENDENT (attribute) = true;
+
+ return attribute;
+}
+
+/* Update condition of a late-parsed contract and postcondition variable,
+ if any. */
+
+void
+update_late_contract (tree contract, tree result, tree 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;
+}
+
+/* Return TRUE iff ATTR has been parsed by the front-end as a c++2a contract
+ attribute. */
+
+bool
+cxx_contract_attribute_p (const_tree attr)
+{
+ if (attr == NULL_TREE
+ || TREE_CODE (attr) != TREE_LIST)
+ return false;
+
+ if (!TREE_PURPOSE (attr) || TREE_CODE (TREE_PURPOSE (attr)) != TREE_LIST)
+ return false;
+ if (!TREE_VALUE (attr) || TREE_CODE (TREE_VALUE (attr)) != TREE_LIST)
+ return false;
+ if (!TREE_VALUE (TREE_VALUE (attr)))
+ return false;
+
+ return (TREE_CODE (TREE_VALUE (TREE_VALUE (attr))) == PRECONDITION_STMT
+ || TREE_CODE (TREE_VALUE (TREE_VALUE (attr))) == POSTCONDITION_STMT
+ || TREE_CODE (TREE_VALUE (TREE_VALUE (attr))) == ASSERTION_STMT);
+}
+
+/* True if ATTR is an assertion. */
+
+bool
+cp_contract_assertion_p (const_tree attr)
+{
+ /* This is only an assertion if it is a valid cxx contract attribute and the
+ statement is an ASSERTION_STMT. */
+ return cxx_contract_attribute_p (attr)
+ && TREE_CODE (CONTRACT_STATEMENT (attr)) == ASSERTION_STMT;
+}
+
+/* Remove all c++2a style contract attributes from the DECL_ATTRIBUTEs of the
+ FUNCTION_DECL FNDECL. */
+
+void
+remove_contract_attributes (tree fndecl)
+{
+ tree list = NULL_TREE;
+ for (tree p = DECL_ATTRIBUTES (fndecl); p; p = TREE_CHAIN (p))
+ if (!cxx_contract_attribute_p (p))
+ list = tree_cons (TREE_PURPOSE (p), TREE_VALUE (p), NULL_TREE);
+ DECL_ATTRIBUTES (fndecl) = nreverse (list);
+}
+
+static tree find_first_non_contract (tree attributes)
+{
+ tree head = attributes;
+ tree p = find_contract (attributes);
+
+ /* There are no contracts. */
+ if (!p)
+ return head;
+
+ /* There are leading contracts. */
+ if (p == head)
+ {
+ while (cxx_contract_attribute_p (p))
+ p = TREE_CHAIN (p);
+ head = p;
+ }
+
+ return head;
+}
+
+/* Remove contracts from ATTRIBUTES. */
+
+tree splice_out_contracts (tree attributes)
+{
+ tree head = find_first_non_contract (attributes);
+ if (!head)
+ return NULL_TREE;
+
+ /* Splice out remaining contracts. */
+ tree p = TREE_CHAIN (head);
+ tree q = head;
+ while (p)
+ {
+ if (cxx_contract_attribute_p (p))
+ {
+ /* Skip a sequence of contracts and then link q to the next
+ non-contract attribute. */
+ do
+ p = TREE_CHAIN (p);
+ while (cxx_contract_attribute_p (p));
+ TREE_CHAIN (q) = p;
+ }
+ else
+ p = TREE_CHAIN (p);
+ }
+
+ return head;
+}
+
+/* Copy contract attributes from NEWDECL onto the attribute list of OLDDECL. */
+
+void copy_contract_attributes (tree olddecl, tree newdecl)
+{
+ tree attrs = NULL_TREE;
+ for (tree c = DECL_CONTRACTS (newdecl); c; c = TREE_CHAIN (c))
+ {
+ if (!cxx_contract_attribute_p (c))
+ continue;
+ attrs = tree_cons (TREE_PURPOSE (c), TREE_VALUE (c), attrs);
+ }
+ attrs = chainon (DECL_ATTRIBUTES (olddecl), nreverse (attrs));
+ DECL_ATTRIBUTES (olddecl) = attrs;
+
+ /* And update DECL_CONTEXT of the postcondition result identifier. */
+ rebuild_postconditions (olddecl);
+}
+
+/* Returns the parameter corresponding to the return value of a guarded
+ function D. Returns NULL_TREE if D has no postconditions or is void. */
+
+static tree
+get_postcondition_result_parameter (tree d)
+{
+ if (!d || d == error_mark_node)
+ return NULL_TREE;
+
+ if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (d))))
+ return NULL_TREE;
+
+ tree post = DECL_POST_FN (d);
+ if (!post || post == error_mark_node)
+ return NULL_TREE;
+
+ for (tree arg = DECL_ARGUMENTS (post); arg; arg = TREE_CHAIN (arg))
+ if (!TREE_CHAIN (arg))
+ return arg;
+
+ return NULL_TREE;
+}
+
+
+/* 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;
+}
+
+/* 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.
+
+ This, along with remap_contracts, are subroutines of duplicate_decls.
+ 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 contacts
+ in terms of a new set of parameters. In this case, we can retain local
+ variables appearing in the contract because the contract is not being
+ prepared for insertion into a new function. Importantly, this preserves the
+ references to postcondition results, which are not replaced during merging.
+
+ If false, we're preparing to emit the contract condition into the body
+ of a new function, so we need to make copies of all local variables
+ appearing in the contract (e.g., if it includes a lambda expression). Note
+ that in this case, postcondition results are mapped to the last parameter
+ of DST.
+
+ This is also used to reuse a parent type's contracts on virtual methods. */
+
+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. */
+ if (TREE_CODE (src) == FUNCTION_DECL)
+ src = DECL_ARGUMENTS (src);
+ if (TREE_CODE (dst) == FUNCTION_DECL)
+ dst = DECL_ARGUMENTS (dst);
+
+ for (tree sp = src, dp = 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;
+ }
+ if (!do_remap)
+ return;
+
+ walk_tree (&CONTRACT_CONDITION (contract), copy_tree_body_r, &id, NULL);
+}
+
+/* Rewrite any references to SRC's PARM_DECLs to the corresponding PARM_DECL in
+ DST in all of the contract attributes in CONTRACTS by calling remap_contract
+ on each.
+
+ This is used for two purposes: to rewrite contract attributes during
+ duplicate_decls, and to prepare contracts for emission into a function's
+ respective precondition and postcondition functions. DUPLICATE_P is used
+ to determine the context in which this function is called. See above for
+ the behavior described by this flag. */
+
+void
+remap_contracts (tree src, tree dst, tree contracts, bool duplicate_p)
+{
+ for (tree attr = contracts; attr; attr = CONTRACT_CHAIN (attr))
+ {
+ if (!cxx_contract_attribute_p (attr))
+ continue;
+ tree contract = CONTRACT_STATEMENT (attr);
+ if (TREE_CODE (CONTRACT_CONDITION (contract)) != DEFERRED_PARSE)
+ remap_contract (src, dst, contract, duplicate_p);
+ }
+}
+
+/* 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 FN. */
+
+static void
+remap_dummy_this (tree fn, tree *expr)
+{
+ walk_tree (expr, remap_dummy_this_1, fn, NULL);
+}
+
+/* Contract matching. */
+
+/* True if the contract is valid. */
+
+static bool
+contract_valid_p (tree contract)
+{
+ return CONTRACT_CONDITION (contract) != error_mark_node;
+}
+
+/* True if the contract attribute is valid. */
+
+static bool
+contract_attribute_valid_p (tree attribute)
+{
+ return contract_valid_p (TREE_VALUE (TREE_VALUE (attribute)));
+}
+
+/* Compare the contract conditions of OLD_ATTR and NEW_ATTR. Returns false
+ if the conditions are equivalent, and true otherwise. */
+
+static bool
+check_for_mismatched_contracts (tree old_attr, tree new_attr,
+ contract_matching_context ctx)
+{
+ tree old_contract = CONTRACT_STATEMENT (old_attr);
+ tree new_contract = CONTRACT_STATEMENT (new_attr);
+
+ /* 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),
+ ctx == cmc_declaration
+ ? "mismatched contract attribute in declaration"
+ : "mismatched contract attribute in override");
+ 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. We fold immediately to avoid
+ issues comparing contracts on overrides that use parameters -- see
+ contracts-pre3. */
+ tree t1 = cp_fully_fold_init (CONTRACT_CONDITION (old_contract));
+ tree t2 = cp_fully_fold_init (CONTRACT_CONDITION (new_contract));
+
+ /* Compare the contracts. The fold doesn't eliminate conversions to members.
+ Set the comparing_override_contracts flag to ensure that references
+ through 'this' are equal if they designate the same member, regardless of
+ the path those members. */
+ bool saved_comparing_contracts = comparing_override_contracts;
+ comparing_override_contracts = (ctx == cmc_override);
+ bool matching_p = cp_tree_equal (t1, t2);
+ comparing_override_contracts = saved_comparing_contracts;
+
+ if (!matching_p)
+ {
+ auto_diagnostic_group d;
+ error_at (EXPR_LOCATION (CONTRACT_CONDITION (new_contract)),
+ ctx == cmc_declaration
+ ? "mismatched contract condition in declaration"
+ : "mismatched contract condition in override");
+ inform (EXPR_LOCATION (CONTRACT_CONDITION (old_contract)),
+ "previous contract here");
+ return true;
+ }
+
+ return false;
+}
+
+/* Compare the contract attributes of OLDDECL and NEWDECL. Returns true
+ if the contracts match, and false if they differ. */
+
+bool
+match_contract_conditions (location_t oldloc, tree old_attrs,
+ location_t newloc, tree new_attrs,
+ contract_matching_context ctx)
+{
+ /* Contracts only match if they are both specified. */
+ if (!old_attrs || !new_attrs)
+ return true;
+
+ /* Compare each contract in turn. */
+ while (old_attrs && new_attrs)
+ {
+ /* If either contract is ill-formed, skip the rest of the comparison,
+ since we've already diagnosed an error. */
+ if (!contract_attribute_valid_p (new_attrs)
+ || !contract_attribute_valid_p (old_attrs))
+ return false;
+
+ if (check_for_mismatched_contracts (old_attrs, new_attrs, ctx))
+ return false;
+ old_attrs = CONTRACT_CHAIN (old_attrs);
+ new_attrs = CONTRACT_CHAIN (new_attrs);
+ }
+
+ /* If we didn't compare all attributes, the contracts don't match. */
+ if (old_attrs || new_attrs)
+ {
+ auto_diagnostic_group d;
+ error_at (newloc,
+ ctx == cmc_declaration
+ ? "declaration has a different number of contracts than "
+ "previously declared"
+ : "override has a different number of contracts than "
+ "previously declared");
+ inform (oldloc,
+ new_attrs
+ ? "original declaration with fewer contracts here"
+ : "original declaration with more contracts here");
+ return false;
+ }
+
+ return true;
+}
+
+/* Deferred contract mapping.
+
+ This is used to compare late-parsed contracts on overrides with their
+ base class functions.
+
+ TODO: It seems like this could be replaced by a simple list that maps from
+ overrides to their base functions. It's not clear that we really need
+ a map to a function + a list of contracts. */
+
+/* Map from FNDECL to a tree list of contracts that have not been matched or
+ diagnosed yet. The TREE_PURPOSE is the basefn we're overriding, and the
+ TREE_VALUE is the list of contract attrs for BASEFN. */
+
+static hash_map<tree_decl_hash, tree> pending_guarded_decls;
+
+void
+defer_guarded_contract_match (tree fndecl, tree fn, tree contracts)
+{
+ if (!pending_guarded_decls.get (fndecl))
+ {
+ pending_guarded_decls.put (fndecl, build_tree_list (fn, contracts));
+ return;
+ }
+ for (tree pending = *pending_guarded_decls.get (fndecl);
+ pending;
+ pending = TREE_CHAIN (pending))
+ {
+ if (TREE_VALUE (pending) == contracts)
+ return;
+ if (TREE_CHAIN (pending) == NULL_TREE)
+ TREE_CHAIN (pending) = build_tree_list (fn, contracts);
+ }
+}
+
+/* If the FUNCTION_DECL DECL has any contracts that had their matching
+ deferred earlier, do that checking now. */
+
+void
+match_deferred_contracts (tree decl)
+{
+ tree *tp = pending_guarded_decls.get (decl);
+ if (!tp)
+ return;
+
+ gcc_assert(!contract_any_deferred_p (DECL_CONTRACTS (decl)));
+
+ processing_template_decl_sentinel ptds;
+ processing_template_decl = uses_template_parms (decl);
+
+ /* Do late contract matching. */
+ for (tree pending = *tp; pending; pending = TREE_CHAIN (pending))
+ {
+ tree new_contracts = TREE_VALUE (pending);
+ location_t new_loc = CONTRACT_SOURCE_LOCATION (new_contracts);
+ tree old_contracts = DECL_CONTRACTS (decl);
+ location_t old_loc = CONTRACT_SOURCE_LOCATION (old_contracts);
+ tree base = TREE_PURPOSE (pending);
+ match_contract_conditions (new_loc, new_contracts,
+ old_loc, old_contracts,
+ base ? cmc_override : cmc_declaration);
+ }
+
+ /* Clear out deferred match list so we don't check it twice. */
+ pending_guarded_decls.remove (decl);
+}
+
+/* Map from FUNCTION_DECL to a FUNCTION_DECL for either the PRE_FN or POST_FN.
+ These are used to parse contract conditions and are called inside the body
+ of the guarded function. */
+static GTY(()) hash_map<tree, tree> *decl_pre_fn;
+static GTY(()) hash_map<tree, tree> *decl_post_fn;
+
+/* Returns the precondition funtion for D, or null if not set. */
+
+tree
+get_precondition_function (tree d)
+{
+ hash_map_maybe_create<hm_ggc> (decl_pre_fn);
+ tree *result = decl_pre_fn->get (d);
+ return result ? *result : NULL_TREE;
+}
+
+/* Returns the postcondition funtion for D, or null if not set. */
+
+tree
+get_postcondition_function (tree d)
+{
+ hash_map_maybe_create<hm_ggc> (decl_post_fn);
+ tree *result = decl_post_fn->get (d);
+ return result ? *result : NULL_TREE;
+}
+
+/* Makes PRE the precondition function for D. */
+
+void
+set_precondition_function (tree d, tree pre)
+{
+ gcc_assert (pre);
+ hash_map_maybe_create<hm_ggc> (decl_pre_fn);
+ gcc_assert (!decl_pre_fn->get (d));
+ decl_pre_fn->put (d, pre);
+}
+
+/* Makes POST the postcondition function for D. */
+
+void
+set_postcondition_function (tree d, tree post)
+{
+ gcc_assert (post);
+ hash_map_maybe_create<hm_ggc> (decl_post_fn);
+ gcc_assert (!decl_post_fn->get (d));
+ decl_post_fn->put (d, post);
+}
+
+/* Set the PRE and POST functions for D. Note that PRE and POST can be
+ null in this case. If so the functions are not recorded. */
+
+void
+set_contract_functions (tree d, tree pre, tree post)
+{
+ if (pre)
+ set_precondition_function (d, pre);
+ if (post)
+ set_postcondition_function (d, post);
+}
+
+/* Return a copy of the FUNCTION_DECL IDECL with its own unshared
+ PARM_DECL and DECL_ATTRIBUTEs. */
+
+static tree
+copy_fn_decl (tree idecl)
+{
+ tree decl = copy_decl (idecl);
+ DECL_ATTRIBUTES (decl) = copy_list (DECL_ATTRIBUTES (idecl));
+
+ if (DECL_RESULT (idecl))
+ {
+ DECL_RESULT (decl) = copy_decl (DECL_RESULT (idecl));
+ DECL_CONTEXT (DECL_RESULT (decl)) = decl;
+ }
+ if (!DECL_ARGUMENTS (idecl) || VOID_TYPE_P (DECL_ARGUMENTS (idecl)))
+ return decl;
+
+ tree last = DECL_ARGUMENTS (decl) = copy_decl (DECL_ARGUMENTS (decl));
+ DECL_CONTEXT (last) = decl;
+ for (tree p = TREE_CHAIN (DECL_ARGUMENTS (idecl)); p; p = TREE_CHAIN (p))
+ {
+ if (VOID_TYPE_P (p))
+ {
+ TREE_CHAIN (last) = void_list_node;
+ break;
+ }
+ last = TREE_CHAIN (last) = copy_decl (p);
+ DECL_CONTEXT (last) = decl;
+ }
+ return decl;
+}
+
+/* Build a declaration for the pre- or postcondition of a guarded FNDECL. */
+
+static tree
+build_contract_condition_function (tree fndecl, bool pre)
+{
+ if (TREE_TYPE (fndecl) == error_mark_node)
+ return error_mark_node;
+ if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl)
+ && !TYPE_METHOD_BASETYPE (TREE_TYPE (fndecl)))
+ return error_mark_node;
+
+ /* Create and rename the unchecked function and give an internal name. */
+ tree fn = copy_fn_decl (fndecl);
+ DECL_RESULT (fn) = NULL_TREE;
+ tree value_type = pre ? void_type_node : TREE_TYPE (TREE_TYPE (fn));
+
+ /* Don't propagate declaration attributes to the checking function,
+ including the original contracts. */
+ DECL_ATTRIBUTES (fn) = NULL_TREE;
+
+ tree arg_types = NULL_TREE;
+ tree *last = &arg_types;
+
+ /* FIXME will later optimizations delete unused args to prevent extra arg
+ passing? do we care? */
+ tree class_type = NULL_TREE;
+ for (tree arg_type = TYPE_ARG_TYPES (TREE_TYPE (fn));
+ arg_type && arg_type != void_list_node;
+ arg_type = TREE_CHAIN (arg_type))
+ {
+ if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl)
+ && TYPE_ARG_TYPES (TREE_TYPE (fn)) == arg_type)
+ {
+ class_type = TREE_TYPE (TREE_VALUE (arg_type));
+ continue;
+ }
+ *last = build_tree_list (TREE_PURPOSE (arg_type), TREE_VALUE (arg_type));
+ last = &TREE_CHAIN (*last);
+ }
+
+ if (pre || VOID_TYPE_P (value_type))
+ *last = void_list_node;
+ else
+ {
+ tree name = get_identifier ("__r");
+ tree parm = build_lang_decl (PARM_DECL, name, value_type);
+ DECL_CONTEXT (parm) = fn;
+ DECL_ARGUMENTS (fn) = chainon (DECL_ARGUMENTS (fn), parm);
+
+ *last = build_tree_list (NULL_TREE, value_type);
+ TREE_CHAIN (*last) = void_list_node;
+
+ if (aggregate_value_p (value_type, fndecl))
+ /* If FNDECL returns in memory, don't return the value from the
+ postcondition. */
+ value_type = void_type_node;
+ }
+
+ TREE_TYPE (fn) = build_function_type (value_type, arg_types);
+ if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl))
+ TREE_TYPE (fn) = build_method_type (class_type, TREE_TYPE (fn));
+
+ DECL_NAME (fn) = copy_node (DECL_NAME (fn));
+ DECL_INITIAL (fn) = error_mark_node;
+ DECL_ABSTRACT_ORIGIN (fn) = fndecl;
+
+ IDENTIFIER_VIRTUAL_P (DECL_NAME (fn)) = false;
+ DECL_VIRTUAL_P (fn) = false;
+
+ /* Make these functions internal if we can, i.e. if the guarded function is
+ not vague linkage, or if we can put them in a comdat group with the
+ guarded function. */
+ if (!DECL_WEAK (fndecl) || HAVE_COMDAT_GROUP)
+ {
+ TREE_PUBLIC (fn) = false;
+ DECL_EXTERNAL (fn) = false;
+ DECL_WEAK (fn) = false;
+ DECL_COMDAT (fn) = false;
+
+ /* We haven't set the comdat group on the guarded function yet, we'll add
+ this to the same group in comdat_linkage later. */
+ gcc_assert (!DECL_ONE_ONLY (fndecl));
+
+ DECL_INTERFACE_KNOWN (fn) = true;
+ }
+
+ DECL_ARTIFICIAL (fn) = true;
+
+ /* Update various inline related declaration properties. */
+ //DECL_DECLARED_INLINE_P (fn) = true;
+ DECL_DISREGARD_INLINE_LIMITS (fn) = true;
+ TREE_NO_WARNING (fn) = 1;
+
+ return fn;
+}
+
+/* Return true if CONTRACT is checked or assumed under the current build
+ configuration. */
+
+bool
+contract_active_p (tree contract)
+{
+ return get_contract_semantic (contract) != CCS_IGNORE;
+}
+
+static bool
+has_active_contract_condition (tree d, tree_code c)
+{
+ for (tree as = DECL_CONTRACTS (d) ; 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 D has any checked or assumed preconditions. */
+
+static bool
+has_active_preconditions (tree d)
+{
+ return has_active_contract_condition (d, PRECONDITION_STMT);
+}
+
+/* True if D has any checked or assumed postconditions. */
+
+static bool
+has_active_postconditions (tree d)
+{
+ return has_active_contract_condition (d, POSTCONDITION_STMT);
+}
+
+/* Return true if any contract in the CONTRACT list is checked or assumed
+ under the current build configuration. */
+
+bool
+contract_any_active_p (tree contract)
+{
+ for (; contract != NULL_TREE; contract = CONTRACT_CHAIN (contract))
+ if (contract_active_p (TREE_VALUE (TREE_VALUE (contract))))
+ return true;
+ return false;
+}
+
+/* Do we need to mess with contracts for DECL1? */
+
+static bool
+handle_contracts_p (tree decl1)
+{
+ return (flag_contracts
+ && !processing_template_decl
+ && DECL_ABSTRACT_ORIGIN (decl1) == NULL_TREE
+ && contract_any_active_p (DECL_CONTRACTS (decl1)));
+}
+
+/* Should we break out DECL1's pre/post contracts into separate functions?
+ FIXME I'd like this to default to 0, but that will need an overhaul to the
+ return identifier handling to just refer to the RESULT_DECL. */
+
+static bool
+outline_contracts_p (tree decl1)
+{
+ return (!DECL_CONSTRUCTOR_P (decl1)
+ && !DECL_DESTRUCTOR_P (decl1));
+}
+
+/* Build the precondition checking function for D. */
+
+static tree
+build_precondition_function (tree d)
+{
+ if (!has_active_preconditions (d))
+ return NULL_TREE;
+
+ return build_contract_condition_function (d, /*pre=*/true);
+}
+
+/* Build the postcondition checking function for D. If the return
+ type is undeduced, don't build the function yet. We do that in
+ apply_deduced_return_type. */
+
+static tree
+build_postcondition_function (tree d)
+{
+ if (!has_active_postconditions (d))
+ return NULL_TREE;
+
+ tree type = TREE_TYPE (TREE_TYPE (d));
+ if (is_auto (type))
+ return NULL_TREE;
+
+ return build_contract_condition_function (d, /*pre=*/false);
+}
+
+static void
+build_contract_function_decls (tree d)
+{
+ /* Constructors and destructors have their contracts inserted inline. */
+ if (!outline_contracts_p (d))
+ return;
+
+ /* Build the pre/post functions (or not). */
+ tree pre = build_precondition_function (d);
+ tree post = build_postcondition_function (d);
+ set_contract_functions (d, pre, post);
+}
+
+static const char *
+get_contract_level_name (tree contract)
+{
+ if (CONTRACT_LITERAL_MODE_P (contract))
+ return "";
+ if (tree mode = CONTRACT_MODE (contract))
+ if (tree level = TREE_VALUE (mode))
+ return IDENTIFIER_POINTER (level);
+ return "default";
+}
+
+static const char *
+get_contract_role_name (tree contract)
+{
+ if (CONTRACT_LITERAL_MODE_P (contract))
+ return "";
+ if (tree mode = CONTRACT_MODE (contract))
+ if (tree role = TREE_PURPOSE (mode))
+ return IDENTIFIER_POINTER (role);
+ return "default";
+}
+
+/* Build a layout-compatible internal version of std::contract_violation. */
+
+static tree
+get_pseudo_contract_violation_type ()
+{
+ if (!pseudo_contract_violation_type)
+ {
+ /* Must match <contract>:
+ class contract_violation {
+ const char* _M_file;
+ const char* _M_function;
+ const char* _M_comment;
+ const char* _M_level;
+ const char* _M_role;
+ uint_least32_t _M_line;
+ signed char _M_continue;
+ If this changes, also update the initializer in
+ build_contract_violation. */
+ const tree types[] = { const_string_type_node,
+ const_string_type_node,
+ const_string_type_node,
+ const_string_type_node,
+ const_string_type_node,
+ uint_least32_type_node,
+ signed_char_type_node };
+ tree fields = NULL_TREE;
+ for (tree type : types)
+ {
+ /* finish_builtin_struct wants fieldss chained in reverse. */
+ tree next = build_decl (BUILTINS_LOCATION, FIELD_DECL,
+ NULL_TREE, type);
+ DECL_CHAIN (next) = fields;
+ fields = next;
+ }
+ iloc_sentinel ils (input_location);
+ input_location = BUILTINS_LOCATION;
+ pseudo_contract_violation_type = make_class_type (RECORD_TYPE);
+ finish_builtin_struct (pseudo_contract_violation_type,
+ "__pseudo_contract_violation",
+ fields, NULL_TREE);
+ CLASSTYPE_AS_BASE (pseudo_contract_violation_type)
+ = pseudo_contract_violation_type;
+ DECL_CONTEXT (TYPE_NAME (pseudo_contract_violation_type))
+ = FROB_CONTEXT (global_namespace);
+ TREE_PUBLIC (TYPE_NAME (pseudo_contract_violation_type)) = true;
+ CLASSTYPE_LITERAL_P (pseudo_contract_violation_type) = true;
+ CLASSTYPE_LAZY_COPY_CTOR (pseudo_contract_violation_type) = true;
+ xref_basetypes (pseudo_contract_violation_type, /*bases=*/NULL_TREE);
+ pseudo_contract_violation_type
+ = cp_build_qualified_type (pseudo_contract_violation_type,
+ TYPE_QUAL_CONST);
+ }
+ return pseudo_contract_violation_type;
+}
+
+/* Return a VAR_DECL to pass to handle_contract_violation. */
+
+static tree
+build_contract_violation (tree contract, contract_continuation cmode)
+{
+ expanded_location loc = expand_location (EXPR_LOCATION (contract));
+ const char *function = fndecl_name (DECL_ORIGIN (current_function_decl));
+ const char *level = get_contract_level_name (contract);
+ const char *role = get_contract_role_name (contract);
+
+ /* Must match the type layout in get_pseudo_contract_violation_type. */
+ tree ctor = build_constructor_va
+ (init_list_type_node, 7,
+ NULL_TREE, build_string_literal (loc.file),
+ NULL_TREE, build_string_literal (function),
+ NULL_TREE, CONTRACT_COMMENT (contract),
+ NULL_TREE, build_string_literal (level),
+ NULL_TREE, build_string_literal (role),
+ NULL_TREE, build_int_cst (uint_least32_type_node, loc.line),
+ NULL_TREE, build_int_cst (signed_char_type_node, cmode));
+
+ ctor = finish_compound_literal (get_pseudo_contract_violation_type (),
+ ctor, tf_none);
+ protected_set_expr_location (ctor, EXPR_LOCATION (contract));
+ return ctor;
+}
+
+/* Return handle_contract_violation(), declaring it if needed. */
+
+static tree
+declare_handle_contract_violation ()
+{
+ 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 id_exp = get_identifier ("experimental");
+ tree ns_exp = lookup_qualified_name (std_node, id_exp);
+
+ tree violation = error_mark_node;
+ if (TREE_CODE (ns_exp) == NAMESPACE_DECL)
+ violation = lookup_qualified_name (ns_exp, viol_name,
+ LOOK_want::TYPE
+ |LOOK_want::HIDDEN_FRIEND);
+
+ if (TREE_CODE (violation) == TYPE_DECL)
+ violation = TREE_TYPE (violation);
+ else
+ {
+ push_nested_namespace (std_node);
+ push_namespace (id_exp, /*inline*/false);
+ violation = make_class_type (RECORD_TYPE);
+ create_implicit_typedef (viol_name, violation);
+ DECL_SOURCE_LOCATION (TYPE_NAME (violation)) = BUILTINS_LOCATION;
+ DECL_CONTEXT (TYPE_NAME (violation)) = current_namespace;
+ pushdecl_namespace_level (TYPE_NAME (violation), /*hidden*/true);
+ pop_namespace ();
+ pop_nested_namespace (std_node);
+ }
+
+ tree argtype = cp_build_qualified_type (violation, TYPE_QUAL_CONST);
+ argtype = cp_build_reference_type (argtype, /*rval*/false);
+ tree fntype = build_function_type_list (void_type_node, argtype, NULL_TREE);
+
+ push_nested_namespace (global_namespace);
+ tree fn = build_cp_library_fn_ptr ("handle_contract_violation", fntype,
+ ECF_COLD);
+ pushdecl_namespace_level (fn, /*hiding*/true);
+ pop_nested_namespace (global_namespace);
+
+ return fn;
+}
+
+/* Build the call to handle_contract_violation for CONTRACT. */
+
+static void
+build_contract_handler_call (tree contract,
+ contract_continuation cmode)
+{
+ tree violation = build_contract_violation (contract, cmode);
+ tree violation_fn = declare_handle_contract_violation ();
+ tree call = build_call_n (violation_fn, 1, build_address (violation));
+ finish_expr_stmt (call);
+}
+
+/* Generate the code that checks or assumes a contract, but do not attach
+ it to the current context. This is called during genericization. */
+
+tree
+build_contract_check (tree contract)
+{
+ contract_semantic semantic = get_contract_semantic (contract);
+ if (semantic == CCS_INVALID)
+ return NULL_TREE;
+
+ /* Ignored contracts are never checked or assumed. */
+ if (semantic == CCS_IGNORE)
+ return void_node;
+
+ remap_dummy_this (current_function_decl, &CONTRACT_CONDITION (contract));
+ tree condition = CONTRACT_CONDITION (contract);
+ if (condition == error_mark_node)
+ return NULL_TREE;
+
+ location_t loc = EXPR_LOCATION (contract);
+
+ if (semantic == CCS_ASSUME)
+ return build_assume_call (loc, condition);
+
+ tree if_stmt = begin_if_stmt ();
+ tree cond = build_x_unary_op (loc,
+ TRUTH_NOT_EXPR,
+ condition, NULL_TREE,
+ tf_warning_or_error);
+ finish_if_stmt_cond (cond, if_stmt);
+
+ /* Get the continuation mode. */
+ contract_continuation cmode;
+ switch (semantic)
+ {
+ case CCS_NEVER: cmode = NEVER_CONTINUE; break;
+ case CCS_MAYBE: cmode = MAYBE_CONTINUE; break;
+ default: gcc_unreachable ();
+ }
+
+ build_contract_handler_call (contract, cmode);
+ if (cmode == NEVER_CONTINUE)
+ finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
+
+ finish_then_clause (if_stmt);
+ tree scope = IF_SCOPE (if_stmt);
+ IF_SCOPE (if_stmt) = NULL;
+ return do_poplevel (scope);
+}
+
+/* Add the contract statement CONTRACT to the current block if valid. */
+
+static void
+emit_contract_statement (tree contract)
+{
+ /* Only add valid contracts. */
+ if (get_contract_semantic (contract) != CCS_INVALID
+ && CONTRACT_CONDITION (contract) != error_mark_node)
+ add_stmt (contract);
+}
+
+/* Generate the statement for the given contract attribute by adding the
+ statement to the current block. Returns the next contract in the chain. */
+
+static tree
+emit_contract_attr (tree attr)
+{
+ gcc_assert (TREE_CODE (attr) == TREE_LIST);
+
+ emit_contract_statement (CONTRACT_STATEMENT (attr));
+
+ return CONTRACT_CHAIN (attr);
+}
+
+/* Add the statements of contract attributes ATTRS to the current block. */
+
+static void
+emit_contract_conditions (tree attrs, tree_code code)
+{
+ if (!attrs) return;
+ gcc_assert (TREE_CODE (attrs) == TREE_LIST);
+ gcc_assert (code == PRECONDITION_STMT || code == POSTCONDITION_STMT);
+ while (attrs)
+ {
+ tree contract = CONTRACT_STATEMENT (attrs);
+ if (TREE_CODE (contract) == code)
+ attrs = emit_contract_attr (attrs);
+ else
+ attrs = CONTRACT_CHAIN (attrs);
+ }
+}
+
+/* Emit the statement for an assertion attribute. */
+
+void
+emit_assertion (tree attr)
+{
+ emit_contract_attr (attr);
+}
+
+/* Emit statements for precondition attributes. */
+
+static void
+emit_preconditions (tree attr)
+{
+ return emit_contract_conditions (attr, PRECONDITION_STMT);
+}
+
+/* Emit statements for postcondition attributes. */
+
+static void
+emit_postconditions_cleanup (tree contracts)
+{
+ tree stmts = push_stmt_list ();
+ emit_contract_conditions (contracts, POSTCONDITION_STMT);
+ stmts = pop_stmt_list (stmts);
+ push_cleanup (NULL_TREE, stmts, /*eh_only*/false);
+}
+
+/* We're compiling the pre/postcondition function CONDFN; remap any FN
+ attributes that match CODE and emit them. */
+
+static void
+remap_and_emit_conditions (tree fn, tree condfn, tree_code code)
+{
+ gcc_assert (code == PRECONDITION_STMT || code == POSTCONDITION_STMT);
+ for (tree attr = DECL_CONTRACTS (fn); attr;
+ attr = CONTRACT_CHAIN (attr))
+ {
+ tree contract = CONTRACT_STATEMENT (attr);
+ if (TREE_CODE (contract) == code)
+ {
+ contract = copy_node (contract);
+ remap_contract (fn, condfn, contract, /*duplicate_p=*/false);
+ emit_contract_statement (contract);
+ }
+ }
+}
+
+/* Converts a contract condition to bool and ensures it has a locaiton. */
+
+tree
+finish_contract_condition (cp_expr 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. */
+ if (!CAN_HAVE_LOCATION_P (condition) || EXCEPTIONAL_CLASS_P (condition))
+ {
+ condition = build1_loc (condition.get_location (), VIEW_CONVERT_EXPR,
+ TREE_TYPE (condition), condition);
+ EXPR_LOCATION_WRAPPER_P (condition) = 1;
+ }
+
+ if (condition == error_mark_node || type_dependent_expression_p (condition))
+ return condition;
+
+ return condition_conversion (condition);
+}
+
+void
+maybe_update_postconditions (tree fco)
+{
+ /* 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 (fco))
+ {
+ rebuild_postconditions (fco);
+ tree post = build_postcondition_function (fco);
+ set_postcondition_function (fco, post);
+ }
+}
+
+/* Called on attribute lists that must not contain contracts. If any
+ contracts are present, issue an error diagnostic and return true. */
+
+bool
+diagnose_misapplied_contracts (tree attributes)
+{
+ if (attributes == NULL_TREE)
+ return false;
+
+ tree contract_attr = find_contract (attributes);
+ if (!contract_attr)
+ return false;
+
+ error_at (EXPR_LOCATION (CONTRACT_STATEMENT (contract_attr)),
+ "contracts must appertain to a function type");
+
+ /* Invalidate the contract so we don't treat it as valid later on. */
+ invalidate_contract (TREE_VALUE (TREE_VALUE (contract_attr)));
+
+ return true;
+}
+
+/* Build and return an argument list containing all the parameters of the
+ (presumably guarded) FUNCTION_DECL FN. This can be used to forward all of
+ FN's arguments to a function taking the same list of arguments -- namely
+ the unchecked form of FN.
+
+ We use CALL_FROM_THUNK_P instead of forward_parm for forwarding
+ semantics. */
+
+static vec<tree, va_gc> *
+build_arg_list (tree fn)
+{
+ vec<tree, va_gc> *args = make_tree_vector ();
+ for (tree t = DECL_ARGUMENTS (fn); t; t = DECL_CHAIN (t))
+ vec_safe_push (args, t);
+ return args;
+}
+
+void
+start_function_contracts (tree decl1)
+{
+ if (!handle_contracts_p (decl1))
+ return;
+
+ if (!outline_contracts_p (decl1))
+ {
+ emit_preconditions (DECL_CONTRACTS (current_function_decl));
+ emit_postconditions_cleanup (DECL_CONTRACTS (current_function_decl));
+ return;
+ }
+
+ /* Contracts may have just been added without a chance to parse them, though
+ we still need the PRE_FN available to generate a call to it. */
+ if (!DECL_PRE_FN (decl1))
+ build_contract_function_decls (decl1);
+
+ /* If we're starting a guarded function with valid contracts, we need to
+ insert a call to the pre function. */
+ if (DECL_PRE_FN (decl1)
+ && DECL_PRE_FN (decl1) != error_mark_node)
+ {
+ releasing_vec args = build_arg_list (decl1);
+ tree call = build_call_a (DECL_PRE_FN (decl1),
+ args->length (),
+ args->address ());
+ CALL_FROM_THUNK_P (call) = true;
+ finish_expr_stmt (call);
+ }
+}
+
+/* Finish up the pre & post function definitions for a guarded FNDECL,
+ and compile those functions all the way to assembler language output. */
+
+void
+finish_function_contracts (tree fndecl)
+{
+ if (!handle_contracts_p (fndecl)
+ || !outline_contracts_p (fndecl))
+ return;
+
+ for (tree ca = DECL_CONTRACTS (fndecl); ca; ca = CONTRACT_CHAIN (ca))
+ {
+ tree contract = CONTRACT_STATEMENT (ca);
+ if (!CONTRACT_CONDITION (contract)
+ || CONTRACT_CONDITION_DEFERRED_P (contract)
+ || CONTRACT_CONDITION (contract) == error_mark_node)
+ return;
+ }
+
+ int flags = SF_DEFAULT | SF_PRE_PARSED;
+
+ /* If either the pre or post functions are bad, don't bother emitting
+ any contracts. The program is already ill-formed. */
+ tree pre = DECL_PRE_FN (fndecl);
+ tree post = DECL_POST_FN (fndecl);
+ if (pre == error_mark_node || post == error_mark_node)
+ return;
+
+ if (pre && DECL_INITIAL (fndecl) != error_mark_node)
+ {
+ DECL_PENDING_INLINE_P (pre) = false;
+ start_preparsed_function (pre, DECL_ATTRIBUTES (pre), flags);
+ remap_and_emit_conditions (fndecl, pre, PRECONDITION_STMT);
+ tree finished_pre = finish_function (false);
+ expand_or_defer_fn (finished_pre);
+ }
+
+ if (post && DECL_INITIAL (fndecl) != error_mark_node)
+ {
+ DECL_PENDING_INLINE_P (post) = false;
+ start_preparsed_function (post,
+ DECL_ATTRIBUTES (post),
+ flags);
+ remap_and_emit_conditions (fndecl, post, POSTCONDITION_STMT);
+ if (!VOID_TYPE_P (TREE_TYPE (TREE_TYPE (post))))
+ finish_return_stmt (get_postcondition_result_parameter (fndecl));
+
+ tree finished_post = finish_function (false);
+ expand_or_defer_fn (finished_post);
+ }
+}
+
+/* Rewrite the expression of a returned expression so that it invokes the
+ postcondition function as needed. */
+
+tree
+apply_postcondition_to_return (tree expr)
+{
+ tree fn = current_function_decl;
+ tree post = DECL_POST_FN (fn);
+ if (!post)
+ return NULL_TREE;
+
+ /* If FN returns in memory, POST has a void return type and we call it when
+ EXPR is DECL_RESULT (fn). If FN returns a scalar, POST has the same
+ return type and we call it when EXPR is the value being returned. */
+ if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (post)))
+ != (expr == DECL_RESULT (fn)))
+ return NULL_TREE;
+
+ releasing_vec args = build_arg_list (fn);
+ if (get_postcondition_result_parameter (fn))
+ vec_safe_push (args, expr);
+ tree call = build_call_a (post,
+ args->length (),
+ args->address ());
+ CALL_FROM_THUNK_P (call) = true;
+
+ return call;
+}
+
+/* A subroutine of duplicate_decls. Diagnose issues in the redeclaration of
+ guarded functions. */
+
+void
+duplicate_contracts (tree newdecl, tree olddecl)
+{
+ if (TREE_CODE (newdecl) == TEMPLATE_DECL)
+ newdecl = DECL_TEMPLATE_RESULT (newdecl);
+ if (TREE_CODE (olddecl) == TEMPLATE_DECL)
+ olddecl = DECL_TEMPLATE_RESULT (olddecl);
+
+ /* Compare contracts to see if they match. */
+ tree old_contracts = DECL_CONTRACTS (olddecl);
+ tree new_contracts = DECL_CONTRACTS (newdecl);
+
+ if (!old_contracts && !new_contracts)
+ return;
+
+ location_t old_loc = DECL_SOURCE_LOCATION (olddecl);
+ location_t new_loc = DECL_SOURCE_LOCATION (newdecl);
+
+ /* If both declarations specify contracts, ensure they match.
+
+ TODO: This handles a potential error a little oddly. Consider:
+
+ struct B {
+ virtual void f(int n) [[pre: n == 0]];
+ };
+ struct D : B {
+ void f(int n) override; // inherits contracts
+ };
+ void D::f(int n) [[pre: n == 0]] // OK
+ { }
+
+ It's okay because we're explicitly restating the inherited contract.
+ Changing the precondition on the definition D::f causes match_contracts
+ to complain about the mismatch.
+
+ This would previously have been diagnosed as adding contracts to an
+ override, but this seems like it should be well-formed. */
+ if (old_contracts && new_contracts)
+ {
+ if (!match_contract_conditions (old_loc, old_contracts,
+ new_loc, new_contracts,
+ cmc_declaration))
+ return;
+ if (DECL_UNIQUE_FRIEND_P (newdecl))
+ /* Newdecl's contracts are still DEFERRED_PARSE, and we're about to
+ collapse it into olddecl, so stash away olddecl's contracts for
+ later comparison. */
+ defer_guarded_contract_match (olddecl, olddecl, old_contracts);
+ }
+
+ /* Handle cases where contracts are omitted in one or the other
+ declaration. */
+ if (old_contracts)
+ {
+ /* Contracts have been previously specified by are no omitted. The
+ new declaration inherits the existing contracts. */
+ if (!new_contracts)
+ copy_contract_attributes (newdecl, olddecl);
+
+ /* In all cases, remove existing contracts from OLDDECL to prevent the
+ attribute merging function from adding excess contracts. */
+ remove_contract_attributes (olddecl);
+ }
+ else if (!old_contracts)
+ {
+ /* We are adding contracts to a declaration. */
+ if (new_contracts)
+ {
+ /* We can't add to a previously defined function. */
+ if (DECL_INITIAL (olddecl))
+ {
+ auto_diagnostic_group d;
+ error_at (new_loc, "cannot add contracts after definition");
+ inform (DECL_SOURCE_LOCATION (olddecl), "original definition here");
+ return;
+ }
+
+ /* We can't add to an unguarded virtual function declaration. */
+ if (DECL_VIRTUAL_P (olddecl) && new_contracts)
+ {
+ auto_diagnostic_group d;
+ error_at (new_loc, "cannot add contracts to a virtual function");
+ inform (DECL_SOURCE_LOCATION (olddecl), "original declaration here");
+ return;
+ }
+
+ /* Depending on the "first declaration" rule, we may not be able
+ to add contracts to a function after the fact. */
+ if (flag_contract_strict_declarations)
+ {
+ warning_at (new_loc,
+ OPT_fcontract_strict_declarations_,
+ "declaration adds contracts to %q#D",
+ olddecl);
+ return;
+ }
+
+ /* Copy the contracts from NEWDECL to OLDDECL. We shouldn't need to
+ remap them because NEWDECL's parameters will replace those of
+ OLDDECL. Remove the contracts from NEWDECL so they aren't
+ cloned when merging. */
+ copy_contract_attributes (olddecl, newdecl);
+ remove_contract_attributes (newdecl);
+ }
+ }
+}
+
+/* Replace the any contract attributes on OVERRIDER with a copy where any
+ references to BASEFN's PARM_DECLs have been rewritten to the corresponding
+ PARM_DECL in OVERRIDER. */
+
+void
+inherit_base_contracts (tree overrider, tree basefn)
+{
+ tree last = NULL_TREE, contract_attrs = NULL_TREE;
+ for (tree a = DECL_CONTRACTS (basefn);
+ a != NULL_TREE;
+ a = CONTRACT_CHAIN (a))
+ {
+ tree c = copy_node (a);
+ TREE_VALUE (c) = build_tree_list (TREE_PURPOSE (TREE_VALUE (c)),
+ copy_node (CONTRACT_STATEMENT (c)));
+
+ tree src = basefn;
+ tree dst = overrider;
+ remap_contract (src, dst, CONTRACT_STATEMENT (c), /*duplicate_p=*/true);
+
+ CONTRACT_COMMENT (CONTRACT_STATEMENT (c)) =
+ copy_node (CONTRACT_COMMENT (CONTRACT_STATEMENT (c)));
+
+ chainon (last, c);
+ last = c;
+ if (!contract_attrs)
+ contract_attrs = c;
+ }
+
+ set_decl_contracts (overrider, contract_attrs);
+}
+
+#include "gt-cp-contracts.h"
diff --git a/gcc/cp/contracts.h b/gcc/cp/contracts.h
new file mode 100644
index 0000000..4050a38
--- /dev/null
+++ b/gcc/cp/contracts.h
@@ -0,0 +1,305 @@
+/* Definitions for C++ contract levels. Implements functionality described in
+ the N4820 working draft version of contracts, P1290, P1332, and P1429.
+ Copyright (C) 2020-2022 Free Software Foundation, Inc.
+ Contributed by Jeff Chapman II (jchapman@lock3software.com)
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_CP_CONTRACT_H
+#define GCC_CP_CONTRACT_H
+
+/* Contract levels approximate the complexity of the expression. */
+
+enum contract_level
+{
+ CONTRACT_INVALID,
+ CONTRACT_DEFAULT,
+ CONTRACT_AUDIT,
+ CONTRACT_AXIOM
+};
+
+/* The concrete semantics determine the behavior of a contract. */
+
+enum contract_semantic
+{
+ CCS_INVALID,
+ CCS_IGNORE,
+ CCS_ASSUME,
+ CCS_NEVER,
+ CCS_MAYBE
+};
+
+/* True if the contract is unchecked. */
+
+inline bool
+unchecked_contract_p (contract_semantic cs)
+{
+ return cs == CCS_IGNORE || cs == CCS_ASSUME;
+}
+
+/* True if the contract is checked. */
+
+inline bool
+checked_contract_p (contract_semantic cs)
+{
+ return cs >= CCS_NEVER;
+}
+
+/* Must match std::contract_violation_continuation_mode in <contract>. */
+enum contract_continuation
+{
+ NEVER_CONTINUE,
+ MAYBE_CONTINUE
+};
+
+/* Assertion role info. */
+struct contract_role
+{
+ const char *name;
+ contract_semantic default_semantic;
+ contract_semantic audit_semantic;
+ contract_semantic axiom_semantic;
+};
+
+/* Information for configured contract semantics. */
+
+struct contract_configuration
+{
+ contract_level level;
+ contract_role* role;
+};
+
+/* A contract mode contains information used to derive the checking
+ and assumption semantics of a contract. This is either a dynamic
+ configuration, meaning it derives from the build mode, or it is
+ explicitly specified. */
+
+struct contract_mode
+{
+ contract_mode () : kind(cm_invalid) {}
+ contract_mode (contract_level level, contract_role *role = NULL)
+ : kind(cm_dynamic)
+ {
+ contract_configuration cc;
+ cc.level = level;
+ cc.role = role;
+ u.config = cc;
+ }
+ contract_mode (contract_semantic semantic) : kind(cm_explicit)
+ {
+ u.semantic = semantic;
+ }
+
+ contract_level get_level () const
+ {
+ gcc_assert (kind == cm_dynamic);
+ return u.config.level;
+ }
+
+ contract_role *get_role () const
+ {
+ gcc_assert (kind == cm_dynamic);
+ return u.config.role;
+ }
+
+ contract_semantic get_semantic () const
+ {
+ gcc_assert (kind == cm_explicit);
+ return u.semantic;
+ }
+
+ enum { cm_invalid, cm_dynamic, cm_explicit } kind;
+
+ union
+ {
+ contract_configuration config;
+ contract_semantic semantic;
+ } u;
+};
+
+extern contract_role *get_contract_role (const char *);
+extern contract_role *add_contract_role (const char *,
+ contract_semantic,
+ contract_semantic,
+ contract_semantic,
+ bool = true);
+extern void validate_contract_role (contract_role *);
+extern void setup_default_contract_role (bool = true);
+extern contract_semantic lookup_concrete_semantic (const char *);
+
+/* Map a source level semantic or level name to its value, or invalid. */
+extern contract_semantic map_contract_semantic (const char *);
+extern contract_level map_contract_level (const char *);
+
+/* Check if an attribute is a cxx contract attribute. */
+extern bool cxx_contract_attribute_p (const_tree);
+extern bool cp_contract_assertion_p (const_tree);
+
+/* Returns the default role. */
+
+inline contract_role *
+get_default_contract_role ()
+{
+ return get_contract_role ("default");
+}
+
+/* Handle various command line arguments related to semantic mapping. */
+extern void handle_OPT_fcontract_build_level_ (const char *);
+extern void handle_OPT_fcontract_assumption_mode_ (const char *);
+extern void handle_OPT_fcontract_continuation_mode_ (const char *);
+extern void handle_OPT_fcontract_role_ (const char *);
+extern void handle_OPT_fcontract_semantic_ (const char *);
+
+enum contract_matching_context
+{
+ cmc_declaration,
+ cmc_override
+};
+
+/* 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)
+
+#define CONTRACT_CHECK(NODE) \
+ (TREE_CHECK3 (NODE, ASSERTION_STMT, PRECONDITION_STMT, POSTCONDITION_STMT))
+
+/* True iff the FUNCTION_DECL NODE currently has any contracts. */
+#define DECL_HAS_CONTRACTS_P(NODE) \
+ (DECL_CONTRACTS (NODE) != NULL_TREE)
+
+/* For a FUNCTION_DECL of a guarded function, this points to a list of the pre
+ and post contracts of the first decl of NODE in original order. */
+#define DECL_CONTRACTS(NODE) \
+ (find_contract (DECL_ATTRIBUTES (NODE)))
+
+/* The next contract (if any) after this one in an attribute list. */
+#define CONTRACT_CHAIN(NODE) \
+ (find_contract (TREE_CHAIN (NODE)))
+
+/* 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 attribute. */
+#define CONTRACT_STATEMENT(NODE) \
+ (TREE_VALUE (TREE_VALUE (NODE)))
+
+/* True if the contract semantic was specified literally. If true, the
+ contract mode is an identifier containing the semantic. Otherwise,
+ it is a TREE_LIST whose TREE_VALUE is the level and whose TREE_PURPOSE
+ is the role. */
+#define CONTRACT_LITERAL_MODE_P(NODE) \
+ (CONTRACT_MODE (NODE) != NULL_TREE \
+ && TREE_CODE (CONTRACT_MODE (NODE)) == IDENTIFIER_NODE)
+
+/* The identifier denoting the literal semantic of the contract. */
+#define CONTRACT_LITERAL_SEMANTIC(NODE) \
+ (TREE_OPERAND (NODE, 0))
+
+/* The written "mode" of the contract. Either an IDENTIFIER with the
+ literal semantic or a TREE_LIST containing the level and role. */
+#define CONTRACT_MODE(NODE) \
+ (TREE_OPERAND (CONTRACT_CHECK (NODE), 0))
+
+/* The identifier denoting the build level of the contract. */
+#define CONTRACT_LEVEL(NODE) \
+ (TREE_VALUE (CONTRACT_MODE (NODE)))
+
+/* The identifier denoting the role of the contract */
+#define CONTRACT_ROLE(NODE) \
+ (TREE_PURPOSE (CONTRACT_MODE (NODE)))
+
+/* The parsed condition of the contract. */
+#define CONTRACT_CONDITION(NODE) \
+ (TREE_OPERAND (CONTRACT_CHECK (NODE), 1))
+
+/* 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), 2))
+
+/* The VAR_DECL of a postcondition result. For deferred contracts, this
+ is an IDENTIFIER. */
+#define POSTCONDITION_IDENTIFIER(NODE) \
+ (TREE_OPERAND (POSTCONDITION_STMT_CHECK (NODE), 3))
+
+/* For a FUNCTION_DECL of a guarded function, this holds the function decl
+ where pre contract checks are emitted. */
+#define DECL_PRE_FN(NODE) \
+ (get_precondition_function ((NODE)))
+
+/* For a FUNCTION_DECL of a guarded function, this holds the function decl
+ where post contract checks are emitted. */
+#define DECL_POST_FN(NODE) \
+ (get_postcondition_function ((NODE)))
+
+/* True iff the FUNCTION_DECL is the pre function for a guarded function. */
+#define DECL_IS_PRE_FN_P(NODE) \
+ (DECL_ABSTRACT_ORIGIN (NODE) && DECL_PRE_FN (DECL_ABSTRACT_ORIGIN (NODE)) == NODE)
+
+/* True iff the FUNCTION_DECL is the post function for a guarded function. */
+#define DECL_IS_POST_FN_P(NODE) \
+ (DECL_ABSTRACT_ORIGIN (NODE) && DECL_POST_FN (DECL_ABSTRACT_ORIGIN (NODE)) == NODE)
+
+extern void remove_contract_attributes (tree);
+extern void copy_contract_attributes (tree, tree);
+extern void remap_contracts (tree, tree, tree, bool);
+extern void maybe_update_postconditions (tree);
+extern void rebuild_postconditions (tree);
+extern bool check_postcondition_result (tree, tree, location_t);
+extern tree get_precondition_function (tree);
+extern tree get_postcondition_function (tree);
+extern void duplicate_contracts (tree, tree);
+extern void match_deferred_contracts (tree);
+extern void defer_guarded_contract_match (tree, tree, tree);
+extern bool diagnose_misapplied_contracts (tree);
+extern tree finish_contract_attribute (tree, tree);
+extern tree invalidate_contract (tree);
+extern void update_late_contract (tree, tree, tree);
+extern tree splice_out_contracts (tree);
+extern bool all_attributes_are_contracts_p (tree);
+extern void inherit_base_contracts (tree, tree);
+extern tree apply_postcondition_to_return (tree);
+extern void start_function_contracts (tree);
+extern void finish_function_contracts (tree);
+extern void set_contract_functions (tree, tree, tree);
+extern tree build_contract_check (tree);
+extern void emit_assertion (tree);
+
+#endif /* ! GCC_CP_CONTRACT_H */
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index cc8bfad..983f2a5 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -1427,6 +1427,23 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
wtd->bind_expr_stack.pop ();
break;
+ case ASSERTION_STMT:
+ case PRECONDITION_STMT:
+ case POSTCONDITION_STMT:
+ {
+ if (tree check = build_contract_check (stmt))
+ {
+ *stmt_p = check;
+ return cp_genericize_r (stmt_p, walk_subtrees, data);
+ }
+
+ /* If we didn't build a check, replace it with void_node so we don't
+ leak contracts into GENERIC. */
+ *stmt_p = void_node;
+ *walk_subtrees = 0;
+ }
+ break;
+
case USING_STMT:
{
tree block = NULL_TREE;
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 7f76f2c..6ed3826 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
#include "cp-objcp-common.h"
#include "dwarf2.h"
#include "stringpool.h"
+#include "contracts.h"
/* Special routine to get the alias set for C++. */
@@ -86,6 +87,9 @@ cp_tree_size (enum tree_code code)
case CONSTRAINT_INFO: return sizeof (tree_constraint_info);
case USERDEF_LITERAL: return sizeof (tree_userdef_literal);
case TEMPLATE_DECL: return sizeof (tree_template_decl);
+ case ASSERTION_STMT: return sizeof (tree_exp);
+ case PRECONDITION_STMT: return sizeof (tree_exp);
+ case POSTCONDITION_STMT: return sizeof (tree_exp);
default:
switch (TREE_CODE_CLASS (code))
{
@@ -564,6 +568,10 @@ cp_common_init_ts (void)
MARK_TS_EXP (CO_YIELD_EXPR);
MARK_TS_EXP (CO_RETURN_EXPR);
+ MARK_TS_EXP (ASSERTION_STMT);
+ MARK_TS_EXP (PRECONDITION_STMT);
+ MARK_TS_EXP (POSTCONDITION_STMT);
+
c_common_init_ts ();
}
@@ -576,6 +584,39 @@ cp_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
{
if (handle_module_option (unsigned (scode), arg, value))
return true;
+
+ enum opt_code code = (enum opt_code) scode;
+ bool handled_p = true;
+
+ switch (code)
+ {
+ case OPT_fcontract_build_level_:
+ handle_OPT_fcontract_build_level_ (arg);
+ break;
+
+ case OPT_fcontract_assumption_mode_:
+ handle_OPT_fcontract_assumption_mode_ (arg);
+ break;
+
+ case OPT_fcontract_continuation_mode_:
+ handle_OPT_fcontract_continuation_mode_ (arg);
+ break;
+
+ case OPT_fcontract_role_:
+ handle_OPT_fcontract_role_ (arg);
+ break;
+
+ case OPT_fcontract_semantic_:
+ handle_OPT_fcontract_semantic_ (arg);
+ break;
+
+ default:
+ handled_p = false;
+ break;
+ }
+ if (handled_p)
+ return handled_p;
+
return c_common_handle_option (scode, arg, value, kind, loc, handlers);
}
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 3de8278..d8aab10 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -582,6 +582,17 @@ DEFTREECODE (CO_YIELD_EXPR, "co_yield", tcc_expression, 2)
DEFTREECODE (CO_RETURN_EXPR, "co_return", tcc_statement, 2)
+/* Different flavors of contracts.
+
+ Assertions and preconditions have two operands: a node containing
+ the their mode and condition. Postconditions have an additional
+ 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)
+
/*
Local variables:
mode:c
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 811a834..548b533 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
#include "hard-reg-set.h"
#include "function.h"
#include "tristate.h"
+#include "contracts.h"
/* In order for the format checking to accept the C++ front end
diagnostic framework extensions, you must include this file before
@@ -232,6 +233,8 @@ enum cp_tree_index
CPTI_DSO_HANDLE,
CPTI_DCAST,
+ CPTI_PSEUDO_CONTRACT_VIOLATION,
+
CPTI_SOURCE_LOCATION_IMPL,
CPTI_FALLBACK_DFLOAT32_TYPE,
@@ -266,6 +269,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
#define current_aggr cp_global_trees[CPTI_AGGR_TAG]
/* std::align_val_t */
#define align_type_node cp_global_trees[CPTI_ALIGN_TYPE]
+#define pseudo_contract_violation_type cp_global_trees[CPTI_PSEUDO_CONTRACT_VIOLATION]
/* We cache these tree nodes so as to call get_identifier less frequently.
For identifiers for functions, including special member functions such
@@ -455,6 +459,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
ALIGNOF_EXPR_STD_P (in ALIGNOF_EXPR)
OVL_DEDUP_P (in OVERLOAD)
ATOMIC_CONSTR_MAP_INSTANTIATED_P (in ATOMIC_CONSTR)
+ contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
TI_PENDING_TEMPLATE_FLAG.
TEMPLATE_PARMS_FOR_INLINE.
@@ -493,6 +498,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
LAMBDA_EXPR_CAPTURE_OPTIMIZED (in LAMBDA_EXPR)
IMPLICIT_CONV_EXPR_BRACED_INIT (in IMPLICIT_CONV_EXPR)
PACK_EXPANSION_AUTO_P (in *_PACK_EXPANSION)
+ contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
3: IMPLICIT_RVALUE_P (in NON_LVALUE_EXPR or STATIC_CAST_EXPR)
ICS_BAD_FLAG (in _CONV)
FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -505,6 +511,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
PACK_EXPANSION_FORCE_EXTRA_ARGS_P (in *_PACK_EXPANSION)
LAMBDA_EXPR_STATIC_P (in LAMBDA_EXPR)
TARGET_EXPR_ELIDING_P (in TARGET_EXPR)
+ contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
CALL_EXPR, or FIELD_DECL).
@@ -1863,6 +1870,7 @@ struct GTY(()) saved_scope {
int x_processing_template_decl;
int x_processing_specialization;
int x_processing_constraint;
+ int x_processing_contract_condition;
int suppress_location_wrappers;
BOOL_BITFIELD x_processing_explicit_instantiation : 1;
BOOL_BITFIELD need_pop_function_context : 1;
@@ -1937,6 +1945,12 @@ extern GTY(()) struct saved_scope *scope_chain;
#define processing_specialization scope_chain->x_processing_specialization
#define processing_explicit_instantiation scope_chain->x_processing_explicit_instantiation
+/* Nonzero if we are parsing the conditional expression of a contract
+ condition. These expressions appear outside the paramter list (like a
+ trailing return type), but are potentially evaluated. */
+
+#define processing_contract_condition scope_chain->x_processing_contract_condition
+
#define in_discarded_stmt scope_chain->discarded_stmt
#define in_consteval_if_p scope_chain->consteval_if_p
@@ -5651,6 +5665,11 @@ extern int comparing_specializations;
FIXME we should always do this except during deduction/ordering. */
extern int comparing_dependent_aliases;
+/* Nonzero if we want to consider different member expressions to compare
+ equal if they designate the same entity. This is set when comparing
+ contract conditions of overrides. */
+extern bool comparing_override_contracts;
+
/* In parser.cc. */
/* Nonzero if we are parsing an unevaluated operand: an operand to
@@ -7453,8 +7472,10 @@ extern hashval_t iterative_hash_template_arg (tree arg, hashval_t val);
extern tree coerce_template_parms (tree, tree, tree, tsubst_flags_t,
bool = true);
extern tree canonicalize_type_argument (tree, tsubst_flags_t);
+extern void register_local_identity (tree);
extern void register_local_specialization (tree, tree);
extern tree retrieve_local_specialization (tree);
+extern void register_parameter_specializations (tree, tree);
extern tree extract_fnparm_pack (tree, tree *);
extern tree template_parm_to_arg (tree);
extern tree dguide_name (tree);
@@ -8526,6 +8547,49 @@ extern tree coro_get_actor_function (tree);
extern tree coro_get_destroy_function (tree);
extern tree coro_get_ramp_function (tree);
+/* contracts.cc */
+extern tree make_postcondition_variable (cp_expr);
+extern tree make_postcondition_variable (cp_expr, tree);
+extern tree grok_contract (tree, tree, tree, cp_expr, location_t);
+extern tree finish_contract_condition (cp_expr);
+
+/* Return the first contract in ATTRS, or NULL_TREE if there are none. */
+
+inline tree
+find_contract (tree attrs)
+{
+ while (attrs && !cxx_contract_attribute_p (attrs))
+ attrs = TREE_CHAIN (attrs);
+ return attrs;
+}
+
+inline void
+set_decl_contracts (tree decl, tree contract_attrs)
+{
+ remove_contract_attributes (decl);
+ DECL_ATTRIBUTES (decl) = chainon (DECL_ATTRIBUTES (decl), contract_attrs);
+}
+
+/* Returns the computed semantic of the node. */
+
+inline contract_semantic
+get_contract_semantic (const_tree t)
+{
+ return (contract_semantic) (TREE_LANG_FLAG_3 (CONTRACT_CHECK (t))
+ | (TREE_LANG_FLAG_2 (t) << 1)
+ | (TREE_LANG_FLAG_0 ((t)) << 2));
+}
+
+/* Sets the computed semantic of the node. */
+
+inline void
+set_contract_semantic (tree t, contract_semantic semantic)
+{
+ TREE_LANG_FLAG_3 (CONTRACT_CHECK (t)) = semantic & 0x01;
+ TREE_LANG_FLAG_2 (t) = (semantic & 0x02) >> 1;
+ TREE_LANG_FLAG_0 (t) = (semantic & 0x04) >> 2;
+}
+
/* Inline bodies. */
inline tree
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 341b4bb..544efdc 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -2268,6 +2268,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
= DECL_OVERLOADED_OPERATOR_CODE_RAW (olddecl);
new_defines_function = DECL_INITIAL (newdecl) != NULL_TREE;
+ duplicate_contracts (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. */
@@ -2341,6 +2343,13 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
specializations. */
gcc_assert (!DECL_TEMPLATE_SPECIALIZATIONS (newdecl));
+ /* Make sure the contracts are equivalent. */
+ duplicate_contracts (newdecl, olddecl);
+
+ /* Remove contracts from old_result so they aren't appended to
+ old_result by the merge function. */
+ remove_contract_attributes (old_result);
+
DECL_ATTRIBUTES (old_result)
= (*targetm.merge_decl_attributes) (old_result, new_result);
@@ -2863,11 +2872,23 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
}
if (! types_match || new_defines_function)
{
+ /* These are the final DECL_ARGUMENTS that will be used within the
+ body; update any references to old DECL_ARGUMENTS in the
+ contracts, if present. */
+ if (tree contracts = DECL_CONTRACTS (newdecl))
+ remap_contracts (olddecl, newdecl, contracts, true);
+
/* These need to be copied so that the names are available.
Note that if the types do match, we'll preserve inline
info and other bits, but if not, we won't. */
DECL_ARGUMENTS (olddecl) = DECL_ARGUMENTS (newdecl);
DECL_RESULT (olddecl) = DECL_RESULT (newdecl);
+
+ /* In some cases, duplicate_contracts will remove contracts from
+ OLDDECL, to avoid duplications. Sometimes, the contracts end up
+ shared. If we removed them, re-add them. */
+ if (!DECL_CONTRACTS (olddecl))
+ copy_contract_attributes (olddecl, newdecl);
}
/* If redeclaring a builtin function, it stays built in
if newdecl is a gnu_inline definition, or if newdecl is just
@@ -2911,7 +2932,38 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
/* Don't clear out the arguments if we're just redeclaring a
function. */
if (DECL_ARGUMENTS (olddecl))
- DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl);
+ {
+ /* If we removed contracts from previous definition, re-attach
+ them. Otherwise, rewrite the contracts so they match the
+ parameters of the new declaration. */
+ if (DECL_INITIAL (olddecl)
+ && DECL_CONTRACTS (newdecl)
+ && !DECL_CONTRACTS (olddecl))
+ copy_contract_attributes (olddecl, newdecl);
+ else
+ {
+ /* Temporarily undo the re-contexting of parameters so we can
+ actually remap parameters. The inliner won't replace
+ parameters if we don't do this. */
+ tree args = DECL_ARGUMENTS (newdecl);
+ for (tree p = args; p; p = DECL_CHAIN (p))
+ DECL_CONTEXT (p) = newdecl;
+
+ /* Save new argument names for use in contracts parsing,
+ unless we've already started parsing the body of olddecl
+ (particular issues arise when newdecl is from a prior
+ friend decl with no argument names, see
+ modules/contracts-tpl-friend-1). */
+ if (tree contracts = DECL_CONTRACTS (olddecl))
+ remap_contracts (newdecl, olddecl, contracts, true);
+
+ /* And reverse this operation again. */
+ for (tree p = args; p; p = DECL_CHAIN (p))
+ DECL_CONTEXT (p) = olddecl;
+ }
+
+ DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl);
+ }
}
}
else if (TREE_CODE (newdecl) == NAMESPACE_DECL)
@@ -5522,6 +5574,12 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
warn_misplaced_attr_for_class_type (loc, declared_type);
}
+ /* Diagnose invalid application of contracts, if any. */
+ if (find_contract (declspecs->attributes))
+ diagnose_misapplied_contracts (declspecs->attributes);
+ else
+ diagnose_misapplied_contracts (declspecs->std_attributes);
+
return declared_type;
}
@@ -5813,9 +5871,16 @@ start_decl (const cp_declarator *declarator,
if (DECL_EXTERNAL (decl) && ! DECL_TEMPLATE_SPECIALIZATION (decl)
/* Aliases are definitions. */
&& !alias)
- permerror (declarator->id_loc,
- "declaration of %q#D outside of class is not definition",
- decl);
+ {
+ if (DECL_VIRTUAL_P (decl) || !flag_contracts)
+ permerror (declarator->id_loc,
+ "declaration of %q#D outside of class is not definition",
+ decl);
+ else if (flag_contract_strict_declarations)
+ warning_at (declarator->id_loc, OPT_fcontract_strict_declarations_,
+ "declaration of %q#D outside of class is not definition",
+ decl);
+ }
}
/* Create a DECL_LANG_SPECIFIC so that DECL_DECOMPOSITION_P works. */
@@ -10597,6 +10662,9 @@ grokfndecl (tree ctype,
*attrlist = NULL_TREE;
}
+ if (DECL_HAS_CONTRACTS_P (decl))
+ rebuild_postconditions (decl);
+
/* Check main's type after attributes have been applied. */
if (ctype == NULL_TREE && DECL_MAIN_P (decl))
{
@@ -12802,7 +12870,8 @@ grokdeclarator (const cp_declarator *declarator,
}
}
- if (declspecs->std_attributes)
+ if (declspecs->std_attributes
+ && !diagnose_misapplied_contracts (declspecs->std_attributes))
{
location_t attr_loc = declspecs->locations[ds_std_attribute];
if (warning_at (attr_loc, OPT_Wattributes, "attribute ignored"))
@@ -12810,6 +12879,9 @@ grokdeclarator (const cp_declarator *declarator,
"is ignored");
}
+ if (attrlist)
+ diagnose_misapplied_contracts (*attrlist);
+
/* Determine the type of the entity declared by recurring on the
declarator. */
for (; declarator; declarator = declarator->declarator)
@@ -12847,6 +12919,12 @@ grokdeclarator (const cp_declarator *declarator,
inner_declarator = declarator->declarator;
+ /* Check that contracts aren't misapplied. */
+ if (tree contract_attr = find_contract (declarator->std_attributes))
+ if (declarator->kind != cdk_function
+ || innermost_code != cdk_function)
+ diagnose_misapplied_contracts (contract_attr);
+
/* We don't want to warn in parameter context because we don't
yet know if the parse will succeed, and this might turn out
to be a constructor call. */
@@ -13243,6 +13321,23 @@ grokdeclarator (const cp_declarator *declarator,
else
returned_attrs = attr_chainon (returned_attrs, att);
}
+
+ /* Actually apply the contract attributes to the declaration. */
+ for (tree *p = &attrs; *p;)
+ {
+ tree l = *p;
+ if (cxx_contract_attribute_p (l))
+ {
+ *p = TREE_CHAIN (l);
+ /* Intentionally reverse order of contracts so they're
+ reversed back into their lexical order. */
+ TREE_CHAIN (l) = NULL_TREE;
+ returned_attrs = chainon (l, returned_attrs);
+ }
+ else
+ p = &TREE_CHAIN (l);
+ }
+
if (attrs)
/* [dcl.fct]/2:
@@ -14265,7 +14360,10 @@ grokdeclarator (const cp_declarator *declarator,
{
/* Packages tend to use GNU attributes on friends, so we only
warn for standard attributes. */
- if (attrlist && !funcdef_flag && cxx11_attribute_p (*attrlist))
+ if (attrlist
+ && !funcdef_flag
+ && cxx11_attribute_p (*attrlist)
+ && !all_attributes_are_contracts_p (*attrlist))
{
*attrlist = NULL_TREE;
if (warning_at (id_loc, OPT_Wattributes, "attribute ignored"))
@@ -17486,6 +17584,8 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
store_parm_decls (current_function_parms);
+ start_function_contracts (decl1);
+
if (!processing_template_decl
&& (flag_lifetime_dse > 1)
&& DECL_CONSTRUCTOR_P (decl1)
@@ -18206,6 +18306,9 @@ finish_function (bool inline_p)
current_function_decl = NULL_TREE;
invoke_plugin_callbacks (PLUGIN_FINISH_PARSE_FUNCTION, fndecl);
+
+ finish_function_contracts (fndecl);
+
return fndecl;
}
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index eeb59ea..f95529a 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -1561,6 +1561,9 @@ cp_check_const_attributes (tree attributes)
tree attr;
for (attr = attributes; attr; attr = TREE_CHAIN (attr))
{
+ if (cxx_contract_attribute_p (attr))
+ continue;
+
tree arg;
/* As we implement alignas using gnu::aligned attribute and
alignas argument is a constant expression, force manifestly
@@ -2106,7 +2109,17 @@ void
comdat_linkage (tree decl)
{
if (flag_weak)
- make_decl_one_only (decl, cxx_comdat_group (decl));
+ {
+ make_decl_one_only (decl, cxx_comdat_group (decl));
+ if (HAVE_COMDAT_GROUP && flag_contracts && DECL_CONTRACTS (decl))
+ {
+ symtab_node *n = symtab_node::get (decl);
+ if (tree pre = DECL_PRE_FN (decl))
+ cgraph_node::get_create (pre)->add_to_same_comdat_group (n);
+ if (tree post = DECL_POST_FN (decl))
+ cgraph_node::get_create (post)->add_to_same_comdat_group (n);
+ }
+ }
else if (TREE_CODE (decl) == FUNCTION_DECL
|| (VAR_P (decl) && DECL_ARTIFICIAL (decl)))
/* We can just emit function and compiler-generated variables
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index da8c95c..12b28e8 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -567,7 +567,8 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags)
else
{
pp_cxx_cv_qualifier_seq (pp, t);
- pp_cxx_tree_identifier (pp, TYPE_IDENTIFIER (t));
+ if (tree id = TYPE_IDENTIFIER (t))
+ pp_cxx_tree_identifier (pp, id);
}
break;
diff --git a/gcc/cp/g++spec.cc b/gcc/cp/g++spec.cc
index b63d835..257e49b 100644
--- a/gcc/cp/g++spec.cc
+++ b/gcc/cp/g++spec.cc
@@ -31,6 +31,8 @@ along with GCC; see the file COPYING3. If not see
#define WITHLIBC (1<<3)
/* Skip this option. */
#define SKIPOPT (1<<4)
+/* Add -lstdc++exp for experimental features that need library support. */
+#define EXPERIMENTAL (1<<5)
#ifndef MATH_LIBRARY
#define MATH_LIBRARY "m"
@@ -158,6 +160,11 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options,
switch (decoded_options[i].opt_index)
{
+ case OPT_fcontracts:
+ args[i] |= EXPERIMENTAL;
+ ++added;
+ break;
+
case OPT_nostdlib:
case OPT_nostdlib__:
case OPT_nodefaultlibs:
@@ -348,6 +355,11 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options,
&new_decoded_options[j]);
}
+ if ((args[i] & EXPERIMENTAL)
+ && which_library == USE_LIBSTDCXX)
+ generate_option (OPT_l, "stdc++exp", 1, CL_DRIVER,
+ &new_decoded_options[++j]);
+
if ((args[i] & SKIPOPT) != 0)
--j;
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index e97428e..e363ef3 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -856,6 +856,13 @@ write_encoding (const tree decl)
mangle_return_type_p (decl),
d);
+ /* If this is the pre/post function for a guarded function, append
+ .pre/post, like something from create_virtual_clone. */
+ if (DECL_IS_PRE_FN_P (decl))
+ write_string (".pre");
+ else if (DECL_IS_POST_FN_P (decl))
+ write_string (".post");
+
/* If this is a coroutine helper, then append an appropriate string to
identify which. */
if (tree ramp = DECL_RAMP_FN (decl))
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 0e9af31..a176435 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -10186,6 +10186,20 @@ trees_out::fn_parms_init (tree fn)
base_tag - ix, ix, parm, fn);
tree_node_vals (parm);
}
+
+ if (!streaming_p ())
+ {
+ /* We must walk contract attrs so the dependency graph is complete. */
+ for (tree contract = DECL_CONTRACTS (fn);
+ contract;
+ contract = CONTRACT_CHAIN (contract))
+ tree_node (contract);
+ }
+
+ /* Write a reference to contracts pre/post functions, if any, to avoid
+ regenerating them in importers. */
+ tree_node (DECL_PRE_FN (fn));
+ tree_node (DECL_POST_FN (fn));
}
/* Build skeleton parm nodes, read their flags, type & parm indices. */
@@ -10220,6 +10234,11 @@ trees_in::fn_parms_init (tree fn)
return 0;
}
+ /* Reload references to contract functions, if any. */
+ tree pre_fn = tree_node ();
+ tree post_fn = tree_node ();
+ set_contract_functions (fn, pre_fn, post_fn);
+
return base_tag;
}
@@ -10807,7 +10826,15 @@ check_mergeable_decl (merge_kind mk, tree decl, tree ovl, merge_key const &key)
Matches decls_match behaviour. */
&& (!DECL_IS_UNDECLARED_BUILTIN (m_inner)
|| !DECL_EXTERN_C_P (m_inner)
- || DECL_EXTERN_C_P (d_inner)))
+ || DECL_EXTERN_C_P (d_inner))
+ /* Reject if one is a different member of a
+ guarded/pre/post fn set. */
+ && (!flag_contracts
+ || (DECL_IS_PRE_FN_P (d_inner)
+ == DECL_IS_PRE_FN_P (m_inner)))
+ && (!flag_contracts
+ || (DECL_IS_POST_FN_P (d_inner)
+ == DECL_IS_POST_FN_P (m_inner))))
{
tree m_reqs = get_constraints (m_inner);
if (m_reqs)
@@ -14575,6 +14602,7 @@ module_state_config::get_dialect ()
cxx_dialect < cxx20 && flag_concepts ? "/concepts" : "",
flag_coroutines ? "/coroutines" : "",
flag_module_implicit_inline ? "/implicit-inline" : "",
+ flag_contracts ? "/contracts" : "",
NULL);
return dialect;
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index c5929a6..aec625e 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3. If not see
#include "cp-name-hint.h"
#include "memmodel.h"
#include "c-family/known-headers.h"
+#include "contracts.h"
#include "bitmap.h"
@@ -2192,11 +2193,14 @@ cp_parser_context_new (cp_parser_context* next)
parser->unparsed_queues->last ().nsdmis
#define unparsed_noexcepts \
parser->unparsed_queues->last ().noexcepts
+#define unparsed_contracts \
+ parser->unparsed_queues->last ().contracts
static void
push_unparsed_function_queues (cp_parser *parser)
{
- cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL };
+ cp_unparsed_functions_entry e
+ = { NULL, make_tree_vector (), NULL, NULL, NULL };
vec_safe_push (parser->unparsed_queues, e);
}
@@ -2695,6 +2699,10 @@ static tree cp_parser_transaction_cancel
static tree cp_parser_yield_expression
(cp_parser *);
+/* Contracts */
+
+static void cp_parser_late_contract_condition
+ (cp_parser *, tree, tree);
enum pragma_context {
pragma_external,
@@ -2907,6 +2915,8 @@ static bool cp_parser_array_designator_p
(cp_parser *);
static bool cp_parser_init_statement_p
(cp_parser *);
+static bool cp_parser_skip_up_to_closing_square_bracket
+ (cp_parser *);
static bool cp_parser_skip_to_closing_square_bracket
(cp_parser *);
static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t);
@@ -12024,6 +12034,16 @@ cp_parser_handle_statement_omp_attributes (cp_parser *parser, tree attrs)
return attrs;
}
+/* True if and only if the name is one of the contract types. */
+
+static bool
+contract_attribute_p (const_tree id)
+{
+ return is_attribute_p ("assert", id)
+ || is_attribute_p ("pre", id)
+ || is_attribute_p ("post", id);
+}
+
/* Handle omp::directive and omp::sequence attributes in *PATTRS
(if any) at the start or after declaration-id of a declaration. */
@@ -12157,7 +12177,10 @@ cp_parser_handle_directive_omp_attributes (cp_parser *parser, tree *pattrs,
is a (possibly labeled) if statement which is not enclosed in braces
and has an else clause. This is used to implement -Wparentheses.
- CHAIN is a vector of if-else-if conditions. */
+ CHAIN is a vector of if-else-if conditions.
+
+ Note that this version of parsing restricts assertions to be attached to
+ empty statements. */
static void
cp_parser_statement (cp_parser* parser, tree in_statement_expr,
@@ -12199,6 +12222,23 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
/* Peek at the next token. */
token = cp_lexer_peek_token (parser->lexer);
+
+ /* If we have contracts, check that they're valid in this context. */
+ if (std_attrs != error_mark_node)
+ {
+ if (tree pre = lookup_attribute ("pre", std_attrs))
+ error_at (EXPR_LOCATION (TREE_VALUE (pre)),
+ "preconditions cannot be statements");
+ else if (tree post = lookup_attribute ("post", std_attrs))
+ error_at (EXPR_LOCATION (TREE_VALUE (post)),
+ "postconditions cannot be statements");
+
+ /* Check that assertions are null statements. */
+ if (cp_contract_assertion_p (std_attrs))
+ if (token->type != CPP_SEMICOLON)
+ error_at (token->location, "assertions must be followed by %<;%>");
+ }
+
bool omp_attrs_forbidden_p;
omp_attrs_forbidden_p = parser->omp_attrs_forbidden_p;
@@ -12514,6 +12554,15 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
"%<fallthrough%> attribute not followed by %<;%>");
std_attrs = NULL_TREE;
}
+
+ /* Handle [[assert: ...]]; */
+ if (cp_contract_assertion_p (std_attrs))
+ {
+ /* Add the assertion as a statement in the current block. */
+ gcc_assert (!statement || statement == error_mark_node);
+ emit_assertion (std_attrs);
+ std_attrs = NULL_TREE;
+ }
}
/* Set the line number for the statement. */
@@ -15697,7 +15746,12 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
declared. */;
else
{
- if (decl_specs->type && CLASS_TYPE_P (decl_specs->type))
+ if (find_contract (attrs))
+ {
+ diagnose_misapplied_contracts (attrs);
+ attrs = NULL_TREE;
+ }
+ else if (decl_specs->type && CLASS_TYPE_P (decl_specs->type))
{
/* This is an attribute following a
class-specifier. */
@@ -25398,11 +25452,11 @@ cp_parser_braced_list (cp_parser* parser, bool* non_constant_p)
return result;
}
-/* Consume tokens up to, and including, the next non-nested closing `]'.
+/* Consume tokens up to, but not including, the next non-nested closing `]'.
Returns true iff we found a closing `]'. */
static bool
-cp_parser_skip_to_closing_square_bracket (cp_parser *parser)
+cp_parser_skip_up_to_closing_square_bracket (cp_parser *parser)
{
unsigned square_depth = 0;
@@ -25427,21 +25481,30 @@ cp_parser_skip_to_closing_square_bracket (cp_parser *parser)
case CPP_CLOSE_SQUARE:
if (!square_depth--)
- {
- cp_lexer_consume_token (parser->lexer);
- return true;
- }
+ return true;
break;
default:
break;
}
- /* Consume the token. */
+ /* Consume the current token, skipping it. */
cp_lexer_consume_token (parser->lexer);
}
}
+/* Consume tokens up to, and including, the next non-nested closing `]'.
+ Returns true iff we found a closing `]'. */
+
+static bool
+cp_parser_skip_to_closing_square_bracket (cp_parser *parser)
+{
+ bool found = cp_parser_skip_up_to_closing_square_bracket (parser);
+ if (found)
+ cp_lexer_consume_token (parser->lexer);
+ return found;
+}
+
/* Return true if we are looking at an array-designator, false otherwise. */
static bool
@@ -26284,6 +26347,56 @@ cp_parser_class_specifier (cp_parser* parser)
cp_parser_late_parsing_nsdmi (parser, decl);
}
vec_safe_truncate (unparsed_nsdmis, 0);
+
+ /* Now contract attributes. */
+ FOR_EACH_VEC_SAFE_ELT (unparsed_contracts, ix, decl)
+ {
+ tree ctx = DECL_CONTEXT (decl);
+ if (class_type != ctx)
+ {
+ if (pushed_scope)
+ pop_scope (pushed_scope);
+ class_type = ctx;
+ pushed_scope = push_scope (class_type);
+ }
+
+ 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. */
+ for (tree a = DECL_ATTRIBUTES (decl); a; a = TREE_CHAIN (a))
+ {
+ if (cxx_contract_attribute_p (a))
+ cp_parser_late_contract_condition (parser, decl, a);
+ }
+
+ /* 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 ();
+
+ /* Perform any deferred contract matching. */
+ match_deferred_contracts (decl);
+ }
+ vec_safe_truncate (unparsed_contracts, 0);
+
current_class_ptr = save_ccp;
current_class_ref = save_ccr;
if (pushed_scope)
@@ -26387,6 +26500,8 @@ cp_parser_class_head (cp_parser* parser,
/* Parse the attributes. */
attributes = cp_parser_attributes_opt (parser);
+ if (find_contract (attributes))
+ diagnose_misapplied_contracts (attributes);
/* If the next token is `::', that is invalid -- but sometimes
people do try to write:
@@ -29316,10 +29431,361 @@ cp_parser_std_attribute_list (cp_parser *parser, tree attr_ns)
return attributes;
}
+/* Optionally parse a C++20 contract role. A NULL return means that no
+ contract role was specified.
+
+ contract-role:
+ % default
+ % identifier
+
+ If the identifier does not name a known contract role, it will
+ be assumed to be default. Returns the identifier for the role
+ token. */
+
+static tree
+cp_parser_contract_role (cp_parser *parser)
+{
+ gcc_assert (cp_lexer_next_token_is (parser->lexer, CPP_MOD));
+ cp_lexer_consume_token (parser->lexer);
+
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ tree role_id = NULL_TREE;
+ if (token->type == CPP_NAME)
+ role_id = token->u.value;
+ else if (token->type == CPP_KEYWORD && token->keyword == RID_DEFAULT)
+ role_id = get_identifier ("default");
+ else
+ {
+ error_at (token->location, "expected contract-role");
+ return error_mark_node;
+ }
+ cp_lexer_consume_token (parser->lexer);
+
+ /* FIXME: Warn about invalid/unknown roles? */
+ return role_id;
+}
+
+/* Parse an optional contract mode.
+
+ contract-mode:
+ contract-semantic
+ [contract-level] [contract-role]
+
+ contract-semantic:
+ check_never_continue
+ check_maybe_continue
+ check_always_continue
+
+ contract-level:
+ default
+ audit
+ axiom
+
+ contract-role:
+ default
+ identifier
+
+ This grammar is taken from P1332R0. During parsing, this sets options
+ on the MODE object to determine the configuration of the contract.
+
+ Returns a tree containing the identifiers used in the configuration.
+ This is either an IDENTIFIER with the literal semantic or a TREE_LIST
+ whose TREE_VALUE is the contract-level and whose TREE_PURPOSE is the
+ contract-role, if any. NULL_TREE is returned if no information is
+ given (i.e., all defaults selected). */
+
+static tree
+cp_parser_contract_mode_opt (cp_parser *parser,
+ bool postcondition_p)
+{
+ /* The mode is empty; the level and role are default. */
+ if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+ return NULL_TREE;
+
+ /* There is only a role; the level is default. */
+ if (cp_lexer_next_token_is (parser->lexer, CPP_MOD))
+ {
+ tree role_id = cp_parser_contract_role (parser);
+ return build_tree_list (role_id, get_identifier ("default"));
+ }
+
+ /* Otherwise, match semantic or level. */
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ contract_level level = CONTRACT_INVALID;
+ contract_semantic semantic = CCS_INVALID;
+ tree config_id;
+ if (token->type == CPP_NAME)
+ {
+ config_id = token->u.value;
+
+ /* Either a named level, a concrete semantic, or an identifier
+ for a postcondition. */
+ const char *ident = IDENTIFIER_POINTER (token->u.value);
+ level = map_contract_level (ident);
+ semantic = map_contract_semantic (ident);
+
+ /* The identifier is the return value for a postcondition. */
+ if (level == CONTRACT_INVALID && semantic == CCS_INVALID
+ && postcondition_p)
+ return NULL_TREE;
+ }
+ else if (token->type == CPP_KEYWORD && token->keyword == RID_DEFAULT)
+ {
+ config_id = get_identifier ("default");
+ level = CONTRACT_DEFAULT;
+ }
+ else
+ {
+ /* We got some other token other than a ':'. */
+ error_at (token->location, "expected contract semantic or level");
+ return NULL_TREE;
+ }
+
+ /* Consume the literal semantic or level token. */
+ cp_lexer_consume_token (parser->lexer);
+
+ if (semantic == CCS_INVALID && level == CONTRACT_INVALID)
+ {
+ error_at (token->location,
+ "expected contract level: "
+ "%<default%>, %<audit%>, or %<axiom%>");
+ return NULL_TREE;
+ }
+
+ /* We matched an explicit semantic. */
+ if (semantic != CCS_INVALID)
+ {
+ if (cp_lexer_next_token_is (parser->lexer, CPP_MOD))
+ {
+ error ("invalid use of contract role for explicit semantic");
+ cp_lexer_consume_token (parser->lexer);
+ cp_lexer_consume_token (parser->lexer);
+ }
+ return config_id;
+ }
+
+ /* We matched a level, there may be a role; otherwise this is default. */
+ if (cp_lexer_next_token_is (parser->lexer, CPP_MOD))
+ {
+ tree role_id = cp_parser_contract_role (parser);
+ return build_tree_list (role_id, config_id);
+ }
+
+ return build_tree_list (NULL_TREE, config_id);
+}
+
+static tree
+find_error (tree *tp, int *, void *)
+{
+ if (*tp == error_mark_node)
+ return *tp;
+ return NULL_TREE;
+}
+
+static bool
+contains_error_p (tree t)
+{
+ return walk_tree (&t, find_error, NULL, NULL);
+}
+
+/* Parse a standard C++20 contract attribute specifier.
+
+ contract-attribute-specifier:
+ [ [ assert contract-level [opt] : conditional-expression ] ]
+ [ [ pre contract-level [opt] : conditional-expression ] ]
+ [ [ post contract-level [opt] identifier [opt] : conditional-expression ] ]
+
+ For free functions, we cannot determine the type of the postcondition
+ identifier because the we haven't called grokdeclarator yet. In those
+ cases we parse the postcondition as if the identifier was declared as
+ 'auto <identifier>'. We then instantiate the postcondition once the
+ return type is known.
+
+ For member functions, contracts are in the complete-class context, so the
+ parse is deferred. We also have the return type avaialable (unless it's
+ deduced), so we don't need to parse the postcondition in terms of a
+ placeholder. */
+
+static tree
+cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute)
+{
+ gcc_assert (contract_attribute_p (attribute));
+ cp_token *token = cp_lexer_consume_token (parser->lexer);
+ location_t loc = token->location;
+
+ bool assertion_p = is_attribute_p ("assert", attribute);
+ bool postcondition_p = is_attribute_p ("post", attribute);
+
+ /* Parse the optional mode. */
+ tree mode = cp_parser_contract_mode_opt (parser, postcondition_p);
+
+ /* Check for postcondition identifiers. */
+ cp_expr identifier;
+ if (postcondition_p && cp_lexer_next_token_is (parser->lexer, CPP_NAME))
+ identifier = cp_parser_identifier (parser);
+ if (identifier == error_mark_node)
+ return error_mark_node;
+
+ cp_parser_require (parser, CPP_COLON, RT_COLON);
+
+ /* Defer the parsing of pre/post contracts inside class definitions. */
+ tree contract;
+ if (!assertion_p &&
+ current_class_type &&
+ TYPE_BEING_DEFINED (current_class_type))
+ {
+ /* Skip until we reach an unenclose ']'. If we ran into an unnested ']'
+ that doesn't close the attribute, return an error and let the attribute
+ handling code emit an error for missing ']]'. */
+ cp_token *first = cp_lexer_peek_token (parser->lexer);
+ cp_parser_skip_to_closing_parenthesis_1 (parser,
+ /*recovering=*/false,
+ CPP_CLOSE_SQUARE,
+ /*consume_paren=*/false);
+ if (cp_lexer_peek_token (parser->lexer)->type != CPP_CLOSE_SQUARE
+ || cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_CLOSE_SQUARE)
+ return error_mark_node;
+ cp_token *last = cp_lexer_peek_token (parser->lexer);
+
+ /* 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. */
+ contract = grok_contract (attribute, mode, identifier, condition, loc);
+ }
+ else
+ {
+ /* Enable location wrappers when parsing contracts. */
+ auto suppression = make_temp_override (suppress_location_wrappers, 0);
+
+ /* Build a fake variable for the result identifier. */
+ tree result = NULL_TREE;
+ if (identifier)
+ {
+ begin_scope (sk_block, NULL_TREE);
+ result = make_postcondition_variable (identifier);
+ ++processing_template_decl;
+ }
+
+ /* Parse the condition, ensuring that parameters or the return variable
+ aren't flagged for use outside the body of a function. */
+ ++processing_contract_condition;
+ cp_expr condition = cp_parser_conditional_expression (parser);
+ --processing_contract_condition;
+
+ /* Try to recover from errors by scanning up to the end of the
+ attribute. Sometimes we get partially parsed expressions, so
+ we need to search the condition for errors. */
+ if (contains_error_p (condition))
+ cp_parser_skip_up_to_closing_square_bracket (parser);
+
+ /* Build the contract. */
+ contract = grok_contract (attribute, mode, result, condition, loc);
+
+ /* Leave our temporary scope for the postcondition result. */
+ if (result)
+ {
+ --processing_template_decl;
+ pop_bindings_and_leave_scope ();
+ }
+ }
+
+ if (!flag_contracts)
+ {
+ error_at (loc, "contracts are only available with %<-fcontracts%>");
+ return error_mark_node;
+ }
+
+ return finish_contract_attribute (attribute, contract);
+}
+
+/* Parse a contract condition for a deferred contract. */
+
+void cp_parser_late_contract_condition (cp_parser *parser,
+ tree fn,
+ tree attribute)
+{
+ tree contract = TREE_VALUE (TREE_VALUE (attribute));
+
+ /* Make sure we've gotten something that hasn't been parsed yet or that
+ we're not parsing an invalid contract. */
+ tree condition = CONTRACT_CONDITION (contract);
+ if (TREE_CODE (condition) != DEFERRED_PARSE)
+ return;
+
+ tree identifier = NULL_TREE;
+ if (TREE_CODE (contract) == POSTCONDITION_STMT)
+ identifier = POSTCONDITION_IDENTIFIER (contract);
+
+ /* Build a fake variable for the result identifier. */
+ tree result = NULL_TREE;
+ if (identifier)
+ {
+ /* TODO: Can we guarantee that the identifier has a location? */
+ location_t loc = cp_expr_location (contract);
+ tree type = TREE_TYPE (TREE_TYPE (fn));
+ if (!check_postcondition_result (fn, type, loc))
+ {
+ invalidate_contract (contract);
+ return;
+ }
+
+ begin_scope (sk_block, NULL_TREE);
+ result = make_postcondition_variable (identifier, type);
+ ++processing_template_decl;
+ }
+
+ /* 'this' is not allowed in preconditions of constructors or in postconditions
+ of destructors. Note that the previous value of this variable is
+ established by the calling function, so we need to save it here. */
+ tree saved_ccr = current_class_ref;
+ tree saved_ccp = current_class_ptr;
+ if ((DECL_CONSTRUCTOR_P (fn) && PRECONDITION_P (contract)) ||
+ (DECL_DESTRUCTOR_P (fn) && POSTCONDITION_P (contract)))
+ {
+ current_class_ref = current_class_ptr = NULL_TREE;
+ parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
+ }
+
+ 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. */
+ ++processing_contract_condition;
+ condition = cp_parser_conditional_expression (parser);
+ --processing_contract_condition;
+
+ /* 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;
+
+ /* Commit to changes. */
+ update_late_contract (contract, result, condition);
+
+ /* Leave our temporary scope for the postcondition result. */
+ if (result)
+ {
+ --processing_template_decl;
+ pop_bindings_and_leave_scope ();
+ }
+}
+
/* Parse a standard C++-11 attribute specifier.
attribute-specifier:
[ [ attribute-using-prefix [opt] attribute-list ] ]
+ contract-attribute-specifier
alignment-specifier
attribute-using-prefix:
@@ -29327,7 +29793,15 @@ cp_parser_std_attribute_list (cp_parser *parser, tree attr_ns)
alignment-specifier:
alignas ( type-id ... [opt] )
- alignas ( alignment-expression ... [opt] ). */
+ alignas ( alignment-expression ... [opt] ).
+
+ Extensions for contracts:
+
+ contract-attribute-specifier:
+ [ [ assert : contract-mode [opt] : conditional-expression ] ]
+ [ [ pre : contract-mode [opt] : conditional-expression ] ]
+ [ [ post : contract-mode [opt] identifier [opt] :
+ conditional-expression ] ] */
static tree
cp_parser_std_attribute_spec (cp_parser *parser)
@@ -29339,10 +29813,27 @@ cp_parser_std_attribute_spec (cp_parser *parser)
&& cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_SQUARE)
{
tree attr_ns = NULL_TREE;
+ tree attr_name = NULL_TREE;
cp_lexer_consume_token (parser->lexer);
cp_lexer_consume_token (parser->lexer);
+ token = cp_lexer_peek_token (parser->lexer);
+ if (token->type == CPP_NAME)
+ {
+ attr_name = token->u.value;
+ attr_name = canonicalize_attr_name (attr_name);
+ }
+
+ /* Handle contract-attribute-specs specially. */
+ if (attr_name && contract_attribute_p (attr_name))
+ {
+ tree attrs = cp_parser_contract_attribute_spec (parser, attr_name);
+ if (attrs != error_mark_node)
+ attributes = attrs;
+ goto finish_attrs;
+ }
+
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING))
{
token = cp_lexer_peek_nth_token (parser->lexer, 2);
@@ -29371,6 +29862,7 @@ cp_parser_std_attribute_spec (cp_parser *parser)
attributes = cp_parser_std_attribute_list (parser, attr_ns);
+ finish_attrs:
if (!cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE)
|| !cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE))
cp_parser_skip_to_end_of_statement (parser);
@@ -32397,6 +32889,14 @@ cp_parser_save_default_args (cp_parser* parser, tree decl)
tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
if (UNPARSED_NOEXCEPT_SPEC_P (spec))
vec_safe_push (unparsed_noexcepts, decl);
+
+ /* Contracts are deferred. */
+ for (tree attr = DECL_ATTRIBUTES (decl); attr; attr = TREE_CHAIN (attr))
+ if (cxx_contract_attribute_p (attr))
+ {
+ vec_safe_push (unparsed_contracts, decl);
+ break;
+ }
}
/* DEFAULT_ARG contains the saved tokens for the initializer of DECL,
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 3e95bfc..5737146 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -178,6 +178,9 @@ struct GTY(()) cp_unparsed_functions_entry {
/* Functions with noexcept-specifiers that require post-processing. */
vec<tree, va_gc> *noexcepts;
+
+ /* Functions with contract attributes that require post-processing. */
+ vec<tree, va_gc> *contracts;
};
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 0310e38..aa459a7 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -217,7 +217,6 @@ static tree get_underlying_template (tree);
static tree tsubst_attributes (tree, tree, tsubst_flags_t, tree);
static tree canonicalize_expr_argument (tree, tsubst_flags_t);
static tree make_argument_pack (tree);
-static void register_parameter_specializations (tree, tree);
static tree enclosing_instantiation_of (tree tctx);
static void instantiate_body (tree pattern, tree args, tree d, bool nested);
static tree maybe_dependent_member_ref (tree, tree, tsubst_flags_t, tree);
@@ -1968,6 +1967,16 @@ register_local_specialization (tree spec, tree tmpl)
local_specializations->put (tmpl, spec);
}
+/* Registers T as a specialization of itself. This is used to preserve
+ the references to already-parsed parameters when instantiating
+ postconditions. */
+
+void
+register_local_identity (tree t)
+{
+ local_specializations->put (t, t);
+}
+
/* TYPE is a class type. Returns true if TYPE is an explicitly
specialized class. */
@@ -3161,8 +3170,10 @@ check_explicit_specialization (tree declarator,
parm = DECL_CHAIN (parm))
DECL_CONTEXT (parm) = result;
}
- return register_specialization (tmpl, gen_tmpl, targs,
+ decl = register_specialization (tmpl, gen_tmpl, targs,
is_friend, 0);
+ remove_contract_attributes (result);
+ return decl;
}
/* Set up the DECL_TEMPLATE_INFO for DECL. */
@@ -3262,6 +3273,10 @@ check_explicit_specialization (tree declarator,
is_friend, 0);
}
+ /* If this is a specialization, splice any contracts that may have
+ been inherited from the template, removing them. */
+ if (decl != error_mark_node && DECL_TEMPLATE_SPECIALIZATION (decl))
+ remove_contract_attributes (decl);
/* A 'structor should already have clones. */
gcc_assert (decl == error_mark_node
@@ -11575,6 +11590,113 @@ can_complete_type_without_circularity (tree type)
static tree tsubst_omp_clauses (tree, enum c_omp_region_type, tree,
tsubst_flags_t, tree);
+/* Instantiate the contract statement. */
+
+static tree
+tsubst_contract (tree decl, tree t, tree args, tsubst_flags_t complain,
+ tree in_decl)
+{
+ tree type = decl ? TREE_TYPE (TREE_TYPE (decl)) : NULL_TREE;
+ bool auto_p = type_uses_auto (type);
+
+ tree r = copy_node (t);
+
+ /* Rebuild the result variable. */
+ if (POSTCONDITION_P (t) && POSTCONDITION_IDENTIFIER (t))
+ {
+ tree oldvar = POSTCONDITION_IDENTIFIER (t);
+
+ tree 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);
+
+ /* Make the variable available for lookup. */
+ register_local_specialization (newvar, oldvar);
+ }
+
+ /* Instantiate the condition. If the return type is undeduced, process
+ the expression as if inside a template to avoid spurious type errors. */
+ if (auto_p)
+ ++processing_template_decl;
+ ++processing_contract_condition;
+ CONTRACT_CONDITION (r)
+ = tsubst_expr (CONTRACT_CONDITION (t), args, complain, in_decl);
+ --processing_contract_condition;
+ if (auto_p)
+ --processing_template_decl;
+
+ /* And the comment. */
+ CONTRACT_COMMENT (r)
+ = tsubst_expr (CONTRACT_COMMENT (r), args, complain, in_decl);
+
+ return r;
+}
+
+/* Update T by instantiating its contract attribute. */
+
+static void
+tsubst_contract_attribute (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_NONSTATIC_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);
+}
+
+/* Rebuild the attribute list for DECL, substituting into contracts
+ as needed. */
+
+void
+tsubst_contract_attributes (tree decl, tree args, tsubst_flags_t complain, tree in_decl)
+{
+ tree list = copy_list (DECL_ATTRIBUTES (decl));
+ for (tree attr = list; attr; attr = CONTRACT_CHAIN (attr))
+ {
+ if (cxx_contract_attribute_p (attr))
+ tsubst_contract_attribute (decl, attr, args, complain, in_decl);
+ }
+ DECL_ATTRIBUTES (decl) = 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. */
@@ -11584,6 +11706,10 @@ tsubst_attribute (tree t, tree *decl_p, tree args,
{
gcc_assert (ATTR_IS_DEPENDENT (t));
+ /* Note that contract attributes are never substituted from this function.
+ Their instantiation is triggered by regenerate_from_template_decl when
+ we instantiate the body of the function. */
+
tree val = TREE_VALUE (t);
if (val == NULL_TREE)
/* Nothing to do. */;
@@ -17108,7 +17234,9 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
= do_auto_deduction (TREE_TYPE (r), init, auto_node,
complain, adc_variable_type);
}
- gcc_assert (cp_unevaluated_operand || TREE_STATIC (r)
+ gcc_assert (cp_unevaluated_operand
+ || processing_contract_condition
+ || TREE_STATIC (r)
|| decl_constant_var_p (r)
|| seen_error ());
if (!processing_template_decl
@@ -18606,6 +18734,19 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
finish_using_directive (USING_STMT_NAMESPACE (t), /*attribs=*/NULL_TREE);
break;
+ case PRECONDITION_STMT:
+ case POSTCONDITION_STMT:
+ gcc_unreachable ();
+
+ case ASSERTION_STMT:
+ {
+ r = tsubst_contract (NULL_TREE, t, args, complain, in_decl);
+ if (r != error_mark_node)
+ add_stmt (r);
+ RETURN (r);
+ }
+ break;
+
case DECL_EXPR:
{
tree decl, pattern_decl;
@@ -26088,6 +26229,21 @@ regenerate_decl_from_template (tree decl, tree tmpl, tree args)
DECL_CONTEXT (t) = decl;
}
+ if (DECL_CONTRACTS (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))
+ {
+ remove_contract_attributes (decl);
+ copy_contract_attributes (decl, code_pattern);
+ }
+
+ tsubst_contract_attributes (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))
@@ -26333,7 +26489,7 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain)
/* We're starting to process the function INST, an instantiation of PATTERN;
add their parameters to local_specializations. */
-static void
+void
register_parameter_specializations (tree pattern, tree inst)
{
tree tmpl_parm = DECL_ARGUMENTS (pattern);
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index 10863a4..0dbb3be 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see
#include "spellcheck-tree.h"
#include "stringpool.h"
#include "attribs.h"
+#include "tree-inline.h"
static int is_subobject_of_p (tree, tree);
static tree dfs_lookup_base (tree, void *);
@@ -2082,6 +2083,33 @@ check_final_overrider (tree overrider, tree basefn)
}
return 0;
}
+
+ if (!DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
+ {
+ auto_diagnostic_group d;
+ error ("function with contracts %q+D overriding contractless function",
+ overrider);
+ inform (DECL_SOURCE_LOCATION (basefn),
+ "overridden function is %qD", basefn);
+ return 0;
+ }
+ else if (DECL_HAS_CONTRACTS_P (basefn) && !DECL_HAS_CONTRACTS_P (overrider))
+ {
+ /* We're inheriting basefn's contracts; create a copy of them but
+ replace references to their parms to our parms. */
+ inherit_base_contracts (overrider, basefn);
+ }
+ else if (DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
+ {
+ /* We're in the process of completing the overrider's class, which means
+ our conditions definitely are not parsed so simply chain on the
+ basefn for later checking.
+
+ Note that OVERRIDER's contracts will have been fully parsed at the
+ point the deferred match is run. */
+ defer_guarded_contract_match (overrider, basefn, DECL_CONTRACTS (basefn));
+ }
+
if (DECL_FINAL_P (basefn))
{
auto_diagnostic_group d;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index b191749..9401b35 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -610,7 +610,8 @@ set_cleanup_locs (tree stmts, location_t loc)
if (TREE_CODE (stmts) == CLEANUP_STMT)
{
tree t = CLEANUP_EXPR (stmts);
- protected_set_expr_location (t, loc);
+ if (t && 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
value, and this conversion ends up carrying the location, and when it
@@ -2169,7 +2170,8 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope,
/* DR 613/850: Can use non-static data members without an associated
object in sizeof/decltype/alignof. */
- if (is_dummy_object (object) && cp_unevaluated_operand == 0
+ if (is_dummy_object (object)
+ && !cp_unevaluated_operand
&& (!processing_template_decl || !current_class_ref))
{
if (complain & tf_error)
@@ -2177,6 +2179,14 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope,
if (current_function_decl
&& DECL_STATIC_FUNCTION_P (current_function_decl))
error ("invalid use of member %qD in static member function", decl);
+ else if (current_function_decl
+ && processing_contract_condition
+ && DECL_CONSTRUCTOR_P (current_function_decl))
+ error ("invalid use of member %qD in constructor %<pre%> contract", decl);
+ else if (current_function_decl
+ && processing_contract_condition
+ && DECL_DESTRUCTOR_P (current_function_decl))
+ error ("invalid use of member %qD in destructor %<post%> contract", decl);
else
error ("invalid use of non-static data member %qD", decl);
inform (DECL_SOURCE_LOCATION (decl), "declared here");
@@ -3014,6 +3024,10 @@ finish_this_expr (void)
tree fn = current_nonlambda_function ();
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%> before it is valid");
+ else if (fn && processing_contract_condition && DECL_DESTRUCTOR_P (fn))
+ error ("invalid use of %<this%> after it is valid");
else if (fn)
error ("invalid use of %<this%> in non-member function");
else
@@ -3987,6 +4001,9 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use)
}
return error_mark_node;
}
+ else if (processing_contract_condition && (TREE_CODE (decl) == PARM_DECL))
+ /* Use of a parameter in a contract condition is fine. */
+ return decl;
else
{
if (complain & tf_error)
@@ -4119,7 +4136,8 @@ finish_id_expression_1 (tree id_expression,
body, except inside an unevaluated context (i.e. decltype). */
if (TREE_CODE (decl) == PARM_DECL
&& DECL_CONTEXT (decl) == NULL_TREE
- && !cp_unevaluated_operand)
+ && !cp_unevaluated_operand
+ && !processing_contract_condition)
{
*error_msg = G_("use of parameter outside function body");
return error_mark_node;
@@ -12290,6 +12308,10 @@ apply_deduced_return_type (tree fco, tree return_type)
TREE_TYPE (fco) = change_return_type (return_type, TREE_TYPE (fco));
+ maybe_update_postconditions (fco);
+
+ /* Apply the type to the result object. */
+
result = DECL_RESULT (fco);
if (result == NULL_TREE)
return;
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index e352cf8..04a055d 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *);
static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
+static tree handle_contract_attribute (tree *, tree, tree, int, bool *);
/* If REF is an lvalue, returns the kind of lvalue that REF is.
Otherwise, returns clk_none. */
@@ -3885,6 +3886,50 @@ called_fns_equal (tree t1, tree t2)
return cp_tree_equal (t1, t2);
}
+bool comparing_override_contracts;
+
+/* In a component reference, return the innermost object of
+ the postfix-expression. */
+
+static tree
+get_innermost_component (tree t)
+{
+ gcc_assert (TREE_CODE (t) == COMPONENT_REF);
+ while (TREE_CODE (t) == COMPONENT_REF)
+ t = TREE_OPERAND (t, 0);
+ return t;
+}
+
+/* Returns true if T is a possibly converted 'this' or '*this' expression. */
+
+static bool
+is_this_expression (tree t)
+{
+ t = get_innermost_component (t);
+ /* See through deferences and no-op conversions. */
+ if (TREE_CODE (t) == INDIRECT_REF)
+ t = TREE_OPERAND (t, 0);
+ if (TREE_CODE (t) == NOP_EXPR)
+ t = TREE_OPERAND (t, 0);
+ return is_this_parameter (t);
+}
+
+static bool
+comparing_this_references (tree t1, tree t2)
+{
+ return is_this_expression (t1) && is_this_expression (t2);
+}
+
+static bool
+equivalent_member_references (tree t1, tree t2)
+{
+ if (!comparing_this_references (t1, t2))
+ return false;
+ t1 = TREE_OPERAND (t1, 1);
+ t2 = TREE_OPERAND (t2, 1);
+ return t1 == t2;
+}
+
/* Return truthvalue of whether T1 is the same tree structure as T2.
Return 1 if they are the same. Return 0 if they are different. */
@@ -4219,6 +4264,13 @@ cp_tree_equal (tree t1, tree t2)
return false;
return true;
+ case COMPONENT_REF:
+ /* If we're comparing contract conditions of overrides, member references
+ compare equal if they designate the same member. */
+ if (comparing_override_contracts)
+ return equivalent_member_references (t1, t2);
+ break;
+
default:
break;
}
@@ -5062,6 +5114,10 @@ const struct attribute_spec std_attribute_table[] =
handle_noreturn_attribute, attr_noreturn_exclusions },
{ "carries_dependency", 0, 0, true, false, false, false,
handle_carries_dependency_attribute, NULL },
+ { "pre", 0, -1, false, false, false, false,
+ handle_contract_attribute, NULL },
+ { "post", 0, -1, false, false, false, false,
+ handle_contract_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
@@ -5306,6 +5362,17 @@ handle_abi_tag_attribute (tree* node, tree name, tree args,
return NULL_TREE;
}
+/* Perform checking for contract attributes. */
+
+tree
+handle_contract_attribute (tree *ARG_UNUSED (node), tree ARG_UNUSED (name),
+ tree ARG_UNUSED (args), int ARG_UNUSED (flags),
+ bool *ARG_UNUSED (no_add_attrs))
+{
+ /* TODO: Is there any checking we could do here? */
+ return NULL_TREE;
+}
+
/* Return a new PTRMEM_CST of the indicated TYPE. The MEMBER is the
thing pointed to by the constant. */
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 281d281..d5757b2 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -11247,11 +11247,22 @@ check_return_expr (tree retval, bool *no_warning)
/* Actually copy the value returned into the appropriate location. */
if (retval && retval != result)
- retval = cp_build_init_expr (result, retval);
+ {
+ /* If there's a postcondition for a scalar return value, wrap
+ retval in a call to the postcondition function. */
+ if (tree post = apply_postcondition_to_return (retval))
+ retval = post;
+ retval = cp_build_init_expr (result, retval);
+ }
if (tree set = maybe_set_retval_sentinel ())
retval = build2 (COMPOUND_EXPR, void_type_node, retval, set);
+ /* If there's a postcondition for an aggregate return value, call the
+ postcondition function after the return object is initialized. */
+ if (tree post = apply_postcondition_to_return (result))
+ retval = build2 (COMPOUND_EXPR, void_type_node, retval, post);
+
return retval;
}