diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e9207a3a255..27ebb406083 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3132,6 +3132,85 @@ of a loop too many expressions need to be evaluated, the resulting constexpr
 evaluation might take too long.
 The default is 33554432 (1<<25).
 
+@item -fcontracts
+@opindex fcontracts
+Enable experimental support for the C++ Contracts feature, as briefly
+added to and then removed from the C++20 working paper (N4820).  The
+implementation also includes proposed enhancements from papers P1290,
+P1332, and P1429.  This functionality is intended mostly for those
+interested in experimentation towards refining the feature to get it
+into shape for a future C++ standard.
+
+On violation of a checked contract, the violation handler is called.
+Users can replace the violation handler by defining
+@smallexample
+void handle_contract_violation (const std::experimental::contract_violation&);
+@end smallexample
+
+There are different sets of additional flags that can be used together
+to specify which contracts will be checked and how, for N4820
+contracts, P1332 contracts, or P1429 contracts; these sets cannot be
+used together.
+
+@table @gcctabopt
+@item -fcontract-mode=[on|off]
+@opindex fcontract-mode
+Control whether any contracts have any semantics at all.  Defaults to on.
+
+@item -fcontract-assumption-mode=[on|off]
+@opindex fcontract-assumption-mode
+[N4820] Control whether contracts with level @samp{axiom}
+should have the assume semantic.  Defaults to on.
+
+@item -fcontract-build-level=[off|default|audit]
+@opindex fcontract-build-level
+[N4820] Specify which level of contracts to generate checks
+for.  Defaults to @samp{default}.
+
+@item -fcontract-continuation-mode=[on|off]
+@opindex fcontract-continuation-mode
+[N4820] Control whether to allow the program to continue executing
+after a contract violation.  That is, do checked contracts have the
+@samp{maybe} semantic described below rather than the @samp{never}
+semantic.  Defaults to off.
+
+@item -fcontract-role=<name>:<default>,<audit>,<axiom>
+@opindex fcontract-role
+[P1332] Specify the concrete semantics for each contract level
+of a particular contract role.
+
+@item -fcontract-semantic=[default|audit|axiom]:<semantic>
+[P1429] Specify the concrete semantic for a particular
+contract level.
+
+@item -fcontract-strict-declarations=[on|off]
+@opindex fcontract-strict-declarations
+Control whether to reject adding contracts to a function after its
+first declaration.  Defaults to off.
+@end table
+
+The possible concrete semantics for that can be specified with
+@samp{-fcontract-role} or @samp{-fcontract-semantic} are:
+
+@table @code
+@item ignore
+This contract has no effect.
+
+@item assume
+This contract is treated like C++23 @code{[[assume]]}.
+
+@item check_never_continue
+@itemx never
+@itemx abort
+This contract is checked.  If it fails, the violation handler is
+called.  If the handler returns, @code{std::terminate} is called.
+
+@item check_maybe_continue
+@itemx maybe
+This contract is checked.  If it fails, the violation handler is
+called.  If the handler returns, execution continues normally.
+@end table
+
 @item -fcoroutines
 @opindex fcoroutines
 Enable support for the C++ coroutines extension (experimental).
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 63a300ecd7c..f08d38d9127 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1674,6 +1674,47 @@ fconstexpr-ops-limit=
 C++ ObjC++ Joined RejectNegative Host_Wide_Int Var(constexpr_ops_limit) Init(33554432)
 -fconstexpr-ops-limit=<number>	Specify maximum number of constexpr operations during a single constexpr evaluation.
 
+fcontracts
+C++ ObjC++ Var(flag_contracts) Init(0)
+Enable certain features present drafts of C++ Contracts.
+
+Enum
+Name(on_off) Type(int) UnknownError(argument %qs must be either %<on%> or %<off%>)
+
+EnumValue
+Enum(on_off) String(off) Value(0)
+
+EnumValue
+Enum(on_off) String(on) Value(1)
+
+fcontract-assumption-mode=
+C++ Joined
+-fcontract-assumption-mode=[on|off]	Enable or disable treating axiom level contracts as assumptions (default on).
+
+fcontract-build-level=
+C++ Joined RejectNegative
+-fcontract-build-level=[off|default|audit]	Specify max contract level to generate runtime checks for
+
+fcontract-strict-declarations=
+C++ Var(flag_contract_strict_declarations) Enum(on_off) Joined Init(0) RejectNegative
+-fcontract-strict-declarations=[on|off]	Enable or disable warnings on generalized redeclaration of functions with contracts (default off).
+
+fcontract-mode=
+C++ Var(flag_contract_mode) Enum(on_off) Joined Init(1) RejectNegative
+-fcontract-mode=[on|off]	Enable or disable all contract facilities (default on).
+
+fcontract-continuation-mode=
+C++ Joined
+-fcontract-continuation-mode=[on|off]	Enable or disable contract continuation mode (default off).
+
+fcontract-role=
+C++ Joined
+-fcontract-role=<name>:<semantics>	Specify the semantics for all levels in a role (default, review), or a custom contract role with given semantics (ex: opt:assume,assume,assume)
+
+fcontract-semantic=
+C++ Joined
+-fcontract-semantic=<level>:<semantic>	Specify the concrete semantics for level
+
 fcoroutines
 C++ LTO Var(flag_coroutines)
 Enable C++ coroutines (experimental).
diff --git a/gcc/cp/contracts.h b/gcc/cp/contracts.h
new file mode 100644
index 00000000000..4050a38708b
--- /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-tree.h b/gcc/cp/cp-tree.h
index d13bb3d4c0e..ae3cdfb18ee 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
@@ -7454,8 +7473,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/parser.h b/gcc/cp/parser.h
index 3e95bfc131b..5737146dd42 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/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index cdb658f6ac9..f27dc2db794 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1087,6 +1087,12 @@ c_cpp_builtins (cpp_reader *pfile)
           else
             cpp_define (pfile, "__cpp_concepts=201507L");
         }
+      if (flag_contracts)
+	{
+	  cpp_define (pfile, "__cpp_contracts=201906L");
+	  cpp_define (pfile, "__cpp_contracts_literal_semantics=201906L");
+	  cpp_define (pfile, "__cpp_contracts_roles=201906L");
+	}
       if (flag_modules)
 	/* The std-defined value is 201907L, but I don't think we can
 	   claim victory yet.  201810 is the p1103 date. */
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 15b4f2c4a08..8a858be99f7 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)						\
@@ -1936,6 +1937,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.  */
@@ -1956,41 +2012,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:
@@ -7845,6 +7871,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
@@ -9819,6 +9863,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 00000000000..79374344207
--- /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, false);
+      --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 refor 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/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index cc8bfada5af..983f2a566a6 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 e4df30d9720..1a682abcc4f 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))
 	{
@@ -565,6 +569,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 ();
 }
 
@@ -577,6 +585,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/decl.cc b/gcc/cp/decl.cc
index 6e98ea35a39..f4d6ee5dede 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -2202,6 +2202,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.  */
@@ -2275,6 +2277,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);
 
@@ -2797,11 +2806,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
@@ -2845,7 +2866,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)
@@ -5456,6 +5508,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;
 }
 
@@ -5747,9 +5805,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.  */
@@ -10531,6 +10596,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))
     {
@@ -12731,7 +12799,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"))
@@ -12739,6 +12808,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)
@@ -12776,6 +12848,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.  */
@@ -13168,6 +13246,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:
 
@@ -14190,7 +14285,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"))
@@ -17410,6 +17508,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)
@@ -18130,6 +18230,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 eeb59eae64f..f95529a5c9a 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 da8c95c9b43..12b28e8ee5b 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 b63d8350ba1..257e49b7f3f 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 e97428e8f30..e363ef35b9f 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 0e9af318ba4..a1764354ba5 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 9523f73d9a3..e02b6229d90 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:
@@ -29317,10 +29432,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:
@@ -29328,7 +29794,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)
@@ -29340,10 +29814,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);
@@ -29372,6 +29863,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);
@@ -32396,6 +32888,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/pt.cc b/gcc/cp/pt.cc
index c3fc56a13ff..ecd6080d113 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
@@ -11576,6 +11591,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, false);
+  --processing_contract_condition;
+  if (auto_p)
+    --processing_template_decl;
+
+  /* And the comment.  */
+  CONTRACT_COMMENT (r)
+      = tsubst_expr (CONTRACT_COMMENT (r), args, complain, in_decl, false);
+
+  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.  */
 
@@ -11585,6 +11707,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.  */;
@@ -17131,7 +17257,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
@@ -18649,6 +18777,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;
@@ -26180,6 +26321,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))
@@ -26427,7 +26583,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 10863a40b11..0dbb3be1ee7 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 7c5f90b5127..c909a43fe52 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");
@@ -3010,6 +3020,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
@@ -3983,6 +3997,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)
@@ -4115,7 +4132,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;
@@ -12286,6 +12304,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 45348c58bb6..074ba3ab1ff 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;
     }
@@ -5034,6 +5086,8 @@ const struct attribute_spec std_attribute_table[] =
     handle_likeliness_attribute, attr_cold_hot_exclusions },
   { "noreturn", 0, 0, true, false, false, false,
     handle_noreturn_attribute, attr_noreturn_exclusions },
+  { "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 }
 };
 
@@ -5282,6 +5336,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 2e0fd8fbf17..50185a0fac8 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -11260,11 +11260,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;
 }
 
diff --git a/gcc/testsuite/g++.dg/contracts/backtrace_handler/assert_fail.cpp b/gcc/testsuite/g++.dg/contracts/backtrace_handler/assert_fail.cpp
new file mode 100644
index 00000000000..08c64bc2849
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/backtrace_handler/assert_fail.cpp
@@ -0,0 +1,23 @@
+void fun1() {
+  int x = 0;
+  [[ assert: x < 0 ]];
+}
+namespace tns {
+  void fun2() {
+    fun1();
+  }
+}
+template<typename T>
+void fun3(T a) {
+  tns::fun2();
+}
+void fun4() {
+  fun3(5);
+}
+int main(int, char**) {
+  void (*fp)() = nullptr;
+  fp = fun4;
+  fp();
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/backtrace_handler/handle_contract_violation.cpp b/gcc/testsuite/g++.dg/contracts/backtrace_handler/handle_contract_violation.cpp
new file mode 100644
index 00000000000..bfbb97ec02e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/backtrace_handler/handle_contract_violation.cpp
@@ -0,0 +1,26 @@
+#include <iostream>
+#include <contract>
+#include <execinfo.h>
+#include <unistd.h>
+
+static constexpr int MAX_BACKTRACE_DEPTH = 128;
+
+void handle_contract_violation(const std::contract_violation &violation) {
+  size_t _backtraceSize{0};
+  void *_backtrace[MAX_BACKTRACE_DEPTH]{};
+
+  _backtraceSize = backtrace(_backtrace, MAX_BACKTRACE_DEPTH);
+  if(_backtraceSize == MAX_BACKTRACE_DEPTH)
+    std::cerr << "warning: backtrace may have been truncated" << std::endl;
+
+  std::cerr << "contract violation: " << violation.file_name()
+    << ":" << violation.line_number()
+    << ": " << violation.comment() << " is false"
+    << " [with contract level=" << violation.assertion_level() << "]" << std::endl
+    << "violation occurs here:" << std::endl;
+  // skip the stack frame of handle_contract_violation and
+  // on_contract_violation to get to wherever the assert was
+  backtrace_symbols_fd(_backtrace + 2, _backtraceSize - 1, STDERR_FILENO);
+  std::cerr << "end of violation" << std::endl;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-access1.C b/gcc/testsuite/g++.dg/contracts/contracts-access1.C
new file mode 100644
index 00000000000..a3a29821017
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-access1.C
@@ -0,0 +1,128 @@
+// ensure that that preconditions can access public, protected, and private
+// members of the current and base classes
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+struct Base
+{
+  int pub{-1};
+
+  virtual int b()
+    [[ pre: pub > 0 ]]
+    [[ pre: pro > 0 ]]
+    [[ pre: pri > 0 ]]
+  {
+    return pub * pro * pri;
+  }
+
+  protected:
+    int pro{-1};
+    int pri{-1};
+};
+
+struct Child : Base
+{
+  int fun()
+    [[ pre: pub > 0 ]]
+    [[ pre: pro > 0 ]]
+    [[ pre: pri > 0 ]]
+  {
+    return pub * pro;
+  }
+};
+
+struct VChild : Base
+{
+  int b()
+    [[ pre: pub > 0 ]]
+    [[ pre: pro > 0 ]]
+    [[ pre: pri > 0 ]]
+  {
+    return pub * pro;
+  }
+};
+
+template<typename B>
+struct TChild : B
+{
+  int fun()
+    [[ pre: B::pub > 0 ]]
+    [[ pre: B::pro > 0 ]]
+    [[ pre: B::pri > 0 ]]
+  {
+    return B::pub * B::pro;
+  }
+};
+
+struct PubBase
+{
+  int pub{-1};
+  int pro{-1};
+  int pri{-1};
+};
+
+struct PubChild : PubBase
+{
+  int fun()
+    [[ pre: pub > 0 ]]
+    [[ pre: pro > 0 ]]
+    [[ pre: pri > 0 ]]
+  {
+    return pub * pro;
+  }
+};
+
+template<typename B>
+struct TPubChild : B
+{
+  int fun()
+    [[ pre: B::pub > 0 ]]
+    [[ pre: B::pro > 0 ]]
+    [[ pre: B::pri > 0 ]]
+  {
+    return B::pub * B::pro;
+  }
+};
+
+int main()
+{
+  Base base{};
+  base.b();
+
+  Child child{};
+  child.fun();
+
+  VChild vchild{};
+  vchild.b();
+
+  TChild<Base> tchild{};
+  tchild.fun();
+
+  PubChild pubchild{};
+  pubchild.fun();
+
+  TPubChild<PubBase> tpubchild;
+  tpubchild.fun();
+
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 11 Base::b .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 12 Base::b .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 13 Base::b .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 26 Child::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 27 Child::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 28 Child::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 37 VChild::b .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 38 VChild::b .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 39 VChild::b .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 49 TChild<Base>::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 50 TChild<Base>::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 51 TChild<Base>::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 67 PubChild::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 PubChild::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 69 PubChild::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 79 TPubChild<PubBase>::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 80 TPubChild<PubBase>::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 81 TPubChild<PubBase>::fun .*(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-assume1.C b/gcc/testsuite/g++.dg/contracts/contracts-assume1.C
new file mode 100644
index 00000000000..71388501ea7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-assume1.C
@@ -0,0 +1,30 @@
+// test that assumed contracts do instatiate templates
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+template<typename T>
+int f(T t)
+{
+  return -t;
+}
+
+int dummy()
+{
+  [[ assert assume: f(1.0) > 0 ]];
+  return -1;
+}
+
+template<>
+int f(double t) // { dg-error "specialization of.*after instantiation" }
+{
+  return -1.0;
+}
+
+int main()
+{
+  dummy();
+  f(1);
+  f(1.0);
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-assume2.C b/gcc/testsuite/g++.dg/contracts/contracts-assume2.C
new file mode 100644
index 00000000000..af163eddcf2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-assume2.C
@@ -0,0 +1,34 @@
+// ensure that assert contracts can be turned into compile time assumptions
+// and that they can be used for optimization.
+//
+// Even though x == -1, the assert contract tells the compiler that it is
+// safe to assume the x <= 0 branch is never taken fun can be transformed into
+// just
+//   printf("%d: test x>0\n", x);
+//   return 0;
+// we ensure this by matching on the output and expecting a 0 return code from
+// main -- unlike contracts-ignore2 which expects a failing return code
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-role=default:never,assume,ignore -O1" }
+#include <cstdio>
+
+int fun(int x) {
+  [[assert audit: x > 0]];
+  if(x <= 0)
+  {
+    printf("%d: test x<=0 opt out\n", x);
+    return -1;
+  }
+  else
+  {
+    printf("%d: test x>0\n", x);
+    return 0;
+  }
+}
+
+int main(int, char**) {
+  volatile int x = -1;
+  return fun(x);
+}
+
+// { dg-output "-1: test x>0(\n|\r\n|\r)*" }
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-assume3.C b/gcc/testsuite/g++.dg/contracts/contracts-assume3.C
new file mode 100644
index 00000000000..8dad6bb562e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-assume3.C
@@ -0,0 +1,19 @@
+// test that assumed contracts that reference undefined entities do not cause
+// a link failure
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts" }
+
+int f(int t);
+
+int dummy()
+{
+  [[ assert assume: f(1) > 0 ]];
+  return -1;
+}
+
+int main()
+{
+  dummy();
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-assume4.C b/gcc/testsuite/g++.dg/contracts/contracts-assume4.C
new file mode 100644
index 00000000000..7954f531612
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-assume4.C
@@ -0,0 +1,19 @@
+// test that assumed constexpr contracts that reference undefined entities do
+// not cause constexpr eval failure
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts" }
+
+constexpr int f(int t); // { dg-warning "used but never defined" }
+
+constexpr int dummy()
+{
+  [[ assert assume: f(1) > 0 ]];
+  return -1;
+}
+
+int main()
+{
+  constexpr int n = dummy();
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-assume5.C b/gcc/testsuite/g++.dg/contracts/contracts-assume5.C
new file mode 100644
index 00000000000..372c0deb13a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-assume5.C
@@ -0,0 +1,34 @@
+// test that assumed constexpr contracts that reference defined entities, or
+// undefined entities in unevaluated context, cause constexpr eval failure when
+// the predicate is constexpr false
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+constexpr int f(int t)
+{
+  return -1;
+}
+
+constexpr int dummy()
+{
+  [[ assert assume: f(1) > 0 ]];
+  return -1;
+}
+
+constexpr int undef(int t);
+
+constexpr int dummy2()
+{
+  [[ assert assume: sizeof(decltype(undef(1))) < 0 ]];
+  return -1;
+}
+
+int main()
+{
+  constexpr int n = dummy(); // { dg-message "in .constexpr. expansion" }
+  // { dg-error "contract predicate" "" { target *-*-* } 14 }
+  constexpr int m = dummy2(); // { dg-message "in .constexpr. expansion" }
+  // { dg-error "contract predicate" "" { target *-*-* } 22 }
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-assume6.C b/gcc/testsuite/g++.dg/contracts/contracts-assume6.C
new file mode 100644
index 00000000000..931c4d0c19c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-assume6.C
@@ -0,0 +1,61 @@
+// ensure that non-defined entities in assume contracts do not error
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+template<typename T>
+T id2(T n);
+
+int fun(int n)
+  [[ pre assume: id2(n) > 0 ]]
+  [[ pre: n > 0 ]]
+{
+  return -n;
+}
+
+template<typename T>
+T tfun(T n)
+  [[ pre assume: id2(n) > 0 ]]
+  [[ pre: n > 0 ]]
+{
+  return -n;
+}
+
+template<typename T>
+constexpr T id(T n); // { dg-warning "used but never defined" }
+
+template<typename T>
+constexpr T cfun(T n)
+  [[ pre assume: id(n) > 0 ]]
+  [[ pre: id(n) > 0 ]] // { dg-error "used before its definition" }
+{
+  return -n;
+}
+
+template<typename T>
+constexpr T id3(T n)
+{
+  return n;
+}
+
+template<typename T>
+constexpr T cfun2(T n)
+  [[ pre assume: id3(n) > 0 ]] // { dg-error "contract predicate" }
+{
+  return -n;
+}
+
+template<typename T>
+constexpr T cfun3(T n)
+  [[ pre: id3(n) > 0 ]] // { dg-error "contract predicate" }
+{
+  return -n;
+}
+
+int main() {
+  constexpr int n = cfun(-5);
+  constexpr int n2 = cfun2(-5);
+  constexpr int n3 = cfun3(-5);
+  fun(-5);
+  tfun(-5);
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-comdat1.C b/gcc/testsuite/g++.dg/contracts/contracts-comdat1.C
new file mode 100644
index 00000000000..3384ae6225e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-comdat1.C
@@ -0,0 +1,19 @@
+// Contract condition functions should be local symbols in a comdat group with
+// the guarded function.
+
+// { dg-do compile { target { c++20 && comdat_group } } }
+// { dg-additional-options -fcontracts }
+// { dg-final { scan-assembler-not "_Z1fi.pre,comdat" } }
+// { dg-final { scan-assembler-not {(weak|globl)[^\n]*_Z1fi.pre} } }
+
+inline int f(int i)
+  [[ pre: i > 0 ]]
+{
+  return i;
+}
+
+int main()
+{
+  if (f(42) != 42)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-config1.C b/gcc/testsuite/g++.dg/contracts/contracts-config1.C
new file mode 100644
index 00000000000..9e32bac535d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-config1.C
@@ -0,0 +1,36 @@
+// Small test to ensure that the level and role information printed by various
+// contract configurations is correct.
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-role=default:maybe,maybe,ignore" }
+
+int fun(int n)
+  [[ post default r: r > 0 ]]
+{
+  return -n;
+}
+
+int main(int, char **)
+{
+  [[ assert default: false ]];
+  [[ assert: false ]];
+  [[ assert audit: false ]];
+  [[ assert default %new_role: false ]];
+  [[ assert %new_role: false ]];
+  [[ assert audit %new_role: false ]];
+  [[ assert check_maybe_continue: false ]];
+  [[ assert %default: false ]];
+  [[ assert audit %default: false ]];
+  fun(5);
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*main false default default 1.*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*main false default default 1.*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*main false audit default 1.*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*main false default new_role 1.*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*main false default new_role 1.*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*main false audit new_role 1.*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*main false   1.*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*main false default default 1.*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*main false audit default 1.*(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-constexpr1.C b/gcc/testsuite/g++.dg/contracts/contracts-constexpr1.C
new file mode 100644
index 00000000000..4c111358d9b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-constexpr1.C
@@ -0,0 +1,74 @@
+// ensure that passing pre/post do not affect constexpr functions
+// ensure that failing pre/post generate an error at runtime in constexpr funcs
+// { dg-do run }
+// { dg-options "-std=c++20 -fcontracts -fcontract-continuation-mode=on" }
+
+constexpr int wfun(int a)
+  [[ pre: a > 0 ]]
+  [[ post r: r > 0 ]]
+{
+  return a;
+}
+
+constexpr int ffun(int a)
+  [[ pre: a > 0 ]]
+  [[ post r: r > 0 ]]
+{
+  return a;
+}
+
+template<typename T>
+constexpr int tfun(T a)
+  [[ pre: a > 0 ]]
+  [[ post r: r > 0 ]]
+{
+  return a;
+}
+
+template<typename T>
+constexpr int wtfun(T a)
+  [[ pre: a > 0 ]]
+  [[ post r: r > 0 ]]
+{
+  return a;
+}
+
+template<typename T>
+constexpr int ftfun(T a)
+  [[ pre: a > 0 ]]
+  [[ post r: r > 0 ]]
+{
+  return a;
+}
+
+constexpr int explicitfn(int a)
+  [[ pre ignore: a > 0 ]]
+  [[ pre check_maybe_continue: a > 0 ]]
+  [[ post ignore r: r > 0 ]]
+  [[ post check_maybe_continue r: r > 0 ]]
+{
+  return a;
+}
+
+int main(int, char **) {
+  constexpr int a = wfun(10);
+  int b = ffun(-10);
+  constexpr int c = wtfun(10);
+  int d = ftfun(-10);
+
+  int e = explicitfn(-10);
+
+  int z = ftfun(-10.0);
+
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 14 ffun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 15 ffun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 38 ftfun<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 39 ftfun<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 46 explicitfn .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 48 explicitfn .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 38 ftfun<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 39 ftfun<double> .*(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-constexpr2.C b/gcc/testsuite/g++.dg/contracts/contracts-constexpr2.C
new file mode 100644
index 00000000000..d0d41f05927
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-constexpr2.C
@@ -0,0 +1,58 @@
+// ensure that failing pre/post can fail at constexpr time
+// { dg-do compile }
+// { dg-options "-std=c++20 -fcontracts -fcontract-continuation-mode=on" }
+
+constexpr int ffun(int a)
+  [[ pre: a > 0 ]]
+  [[ post r: r > 10 ]]
+{
+  return a;
+}
+
+template<typename T>
+constexpr int ftfun(T a)
+  [[ pre: a > 0 ]]
+  [[ post r: r > 10 ]]
+{
+  return a;
+}
+
+constexpr int explicitfn(int a)
+  [[ pre ignore: a > 0 ]]
+  [[ pre check_maybe_continue: a > 0 ]]
+  [[ post ignore r: r > 10 ]]
+  [[ post check_maybe_continue r: r > 10 ]]
+{
+  return a;
+}
+
+template<typename T>
+constexpr int ftfun2(T a)
+  [[ pre: a > 0 ]]
+  [[ post r: r > 10 ]]
+{
+  return a;
+}
+
+int main(int, char **) {
+  constexpr int a = ffun(-10);
+  // { dg-error "contract predicate" "" { target *-*-* } 6 }
+  constexpr int b = ftfun(-10);
+  // { dg-error "contract predicate" "" { target *-*-* } 14 }
+  constexpr int c = explicitfn(-10);
+  // { dg-error "contract predicate" "" { target *-*-* } 22 }
+  constexpr int d = ftfun2(-10.0);
+  // { dg-error "contract predicate" "" { target *-*-* } 31 }
+
+  constexpr int e = ffun(5);
+  // { dg-error "contract predicate" "" { target *-*-* } 7 }
+  constexpr int f = ftfun(5);
+  // { dg-error "contract predicate" "" { target *-*-* } 15 }
+  constexpr int g = explicitfn(5);
+  // { dg-error "contract predicate" "" { target *-*-* } 24 }
+  constexpr int h = ftfun2(5.5);
+  // { dg-error "contract predicate" "" { target *-*-* } 32 }
+
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-constexpr3.C b/gcc/testsuite/g++.dg/contracts/contracts-constexpr3.C
new file mode 100644
index 00000000000..8826220ef91
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-constexpr3.C
@@ -0,0 +1,10 @@
+// An assumed contract shouldn't break constant evaluation.
+
+// { dg-do compile { target c++20 } }
+// { dg-additional-options -fcontracts }
+
+bool b;
+
+constexpr int f() [[ pre assume: b ]] { return 42; }
+
+static_assert (f() > 0);
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-conversion1.C b/gcc/testsuite/g++.dg/contracts/contracts-conversion1.C
new file mode 100644
index 00000000000..ff5e23f2f14
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-conversion1.C
@@ -0,0 +1,19 @@
+// Test to ensure that diagnostic location for condition conversion is in the
+// right place.
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+
+template<typename T>
+void fn()
+  [[ pre: T{} ]] // { dg-error "no match" }
+{
+}
+
+struct Z { };
+
+int main(int, char**) {
+  fn<int>();
+  fn<Z>();
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-ctor-dtor1.C b/gcc/testsuite/g++.dg/contracts/contracts-ctor-dtor1.C
new file mode 100644
index 00000000000..bcd6096b5a7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-ctor-dtor1.C
@@ -0,0 +1,177 @@
+// Tests to ensure that contracts are properly emitted for constructors,
+// destructors, and their intersection with templates.
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+bool pre_{false}, post_{false};
+bool delegate_{false};
+
+struct S
+{
+  S() [[ pre: pre_ ]] [[ post: post_ ]];
+  ~S() [[ pre: pre_ ]] [[ post: post_ ]];
+};
+
+S::S() { return; }
+S::~S() { return; }
+
+struct SInline
+{
+  SInline() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
+  ~SInline() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
+};
+
+struct SDelegate0
+{
+  SDelegate0(int) [[ pre: pre_ ]] [[ post: post_ ]] { return; }
+  SDelegate0() : SDelegate0(0) { return; }
+  ~SDelegate0() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
+};
+
+struct SDelegate1
+{
+  SDelegate1(int) { return; }
+  SDelegate1() [[ pre: pre_ ]] [[ post: post_ ]] : SDelegate1(0) { return; }
+  ~SDelegate1() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
+};
+
+struct SDelegate2
+{
+  SDelegate2(int) [[ pre: pre_ ]] [[ post: post_ ]] { return; }
+  SDelegate2() [[ pre: pre_ && delegate_ ]] [[ post: post_ && delegate_ ]] : SDelegate2(0) { return; }
+  ~SDelegate2() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
+};
+
+struct SDelegate3
+{
+  SDelegate3(int) [[ pre: pre_ ]] [[ post: post_ ]] { return; }
+  SDelegate3() [[ pre: pre_ && delegate_ ]] [[ post: post_ && delegate_ ]] : SDelegate3(0) { return; }
+  ~SDelegate3() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
+};
+
+template<typename T>
+struct S1
+{
+  S1() [[ pre: pre_ ]] [[ post: post_ ]] { }
+};
+
+struct S2
+{
+  template<typename T>
+  S2(T) [[ pre: pre_ ]] [[ post: post_ ]] { }
+};
+
+template<typename T>
+struct S3
+{
+  template<typename S>
+  S3(S) [[ pre: pre_ ]] [[ post: post_ ]] { }
+};
+
+struct G0
+{
+  G0() [[ post: x > 0 ]] {}
+  ~G0() [[ pre: x > 0 ]] {}
+  int x{-1};
+};
+
+struct G1
+{
+  G1() [[ post: this->x > 0 ]] {}
+  ~G1() [[ pre: this->x > 0 ]] {}
+  int x{-1};
+};
+
+int x{-1};
+
+struct G2
+{
+  G2() [[ pre: ::x > 0 ]] {}
+  ~G2() [[ post: ::x > 0 ]] {}
+  int x{1};
+};
+
+void test0()
+{
+  S s;
+  SInline si;
+  SDelegate0 sd0;
+  SDelegate1 sd1;
+  SDelegate2 sd2;
+  SDelegate3 sd3;
+  S1<int> s1_i;
+  S1<double> s1_d;
+  S2 s2_i{1};
+  S2 s2_d{.1};
+  S3<int> s3_i_i{1};
+  S3<int> s3_i_d{.1};
+  S3<double> s3_d_i{1};
+  S3<double> s3_d_d{.1};
+}
+
+void test1()
+{
+  G0 g0;
+  G1 g1;
+  G2 g2;
+}
+
+int main(int, char**)
+{
+  test0();
+  test1();
+  return 0;
+};
+
+// test0
+// { dg-output "default std::handle_contract_violation called: .*.C 11 S::S .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 11 S::S .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 20 SInline::SInline .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 20 SInline::SInline .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 26 SDelegate0::SDelegate0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 26 SDelegate0::SDelegate0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 34 SDelegate1::SDelegate1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 34 SDelegate1::SDelegate1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 41 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 40 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 40 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 41 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 48 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 47 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 47 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 48 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<int>::S1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<int>::S1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<double>::S1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<double>::S1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 49 SDelegate3::~SDelegate3 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 49 SDelegate3::~SDelegate3 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 42 SDelegate2::~SDelegate2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 42 SDelegate2::~SDelegate2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 35 SDelegate1::~SDelegate1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 35 SDelegate1::~SDelegate1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 28 SDelegate0::~SDelegate0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 28 SDelegate0::~SDelegate0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 21 SInline::~SInline .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 21 SInline::~SInline .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 12 S::~S .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 12 S::~S .*(\n|\r\n|\r)*" }
+
+// test1
+// { dg-output "default std::handle_contract_violation called: .*.C 73 G0::G0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 80 G1::G1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 81 G1::~G1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 74 G0::~G0 .*(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-ctor-dtor2.C b/gcc/testsuite/g++.dg/contracts/contracts-ctor-dtor2.C
new file mode 100644
index 00000000000..ba3b7678ef6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-ctor-dtor2.C
@@ -0,0 +1,35 @@
+// Tests to ensure that an invalid this parm cannot be used in pre on ctors or
+// in post on dtors.
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+struct F0
+{
+  F0() [[ pre: x > 0 ]]; // { dg-error "invalid use of member" }
+  ~F0() [[ post: x > 0 ]]; // { dg-error "invalid use of member" }
+  int x{-1};
+};
+
+struct F1
+{
+  F1() [[ pre: this->x > 0 ]]; // { dg-error "may not be used" }
+  ~F1() [[ post: this->x > 0 ]]; // { dg-error "may not be used" }
+  int x{-1};
+};
+
+struct F2
+{
+  F2()
+    [[ post ret: false ]] // { dg-error "does not return a value" }
+  {
+  }
+  ~F2()
+    [[ post r: false ]] // { dg-error "does not return a value" }
+  {
+  }
+  void f()
+    [[ post r: false ]] // { dg-error "does not return a value" }
+  {
+  }
+};
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-cv1.C b/gcc/testsuite/g++.dg/contracts/contracts-cv1.C
new file mode 100644
index 00000000000..8266b4fed8f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-cv1.C
@@ -0,0 +1,37 @@
+// Tests to ensure that contracts have a properly cv qualified this
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+struct S
+{
+  int g() const { return x_; }
+  int f() { return x_; }
+
+  void mem_c() const
+    [[ pre: f() ]] // { dg-error "discards qualifiers" }
+  {
+  }
+  void mem_nc()
+    [[ pre: f() ]]
+  {
+  }
+
+  void memc_c() const
+    [[ pre: g() ]]
+  {
+  }
+  void memc_nc()
+    [[ pre: g() ]]
+  {
+  }
+
+  private:
+    int x_{-10};
+};
+
+int main(int, char**)
+{
+  S s;
+  return 0;
+};
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-deduced1.C b/gcc/testsuite/g++.dg/contracts/contracts-deduced1.C
new file mode 100644
index 00000000000..200ec734800
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-deduced1.C
@@ -0,0 +1,108 @@
+// Tests to ensure that deduced return types work with postconditions using
+// the return value on defining declarations.
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts --param ggc-min-heapsize=0 --param ggc-min-expand=0" }
+
+auto undeduced(int z)
+{
+  if (!(sizeof(decltype(undeduced(5))) > 4)) // { dg-error "before deduction" }
+    return 5;
+  return 4;
+}
+
+// defining declaration, fine
+auto g0(int a) [[ pre: a < 0 ]] [[ post r: r > 0 ]]
+{
+  return -a * 1.2;
+}
+
+// non defining post using nondeduced identifier, fine
+int g1(int m) [[ post r: r == m ]];
+
+int g1(int n) [[ post s: s == n ]]
+{
+  return -n;
+}
+
+int g2(int z)
+  [[ pre: sizeof(decltype(g2(5))) > 4 ]]; // { dg-error "not declared" }
+
+int g3(int z)
+  [[ pre: sizeof(decltype(g2(5))) > 4 ]]
+{
+  return -z;
+}
+
+// deduced that doesn't use return, good
+auto g4(int m) [[ post: m ]];
+
+auto g4(int m) [[ post: m ]]
+{
+  return -m;
+}
+
+auto g5(int m) [[ pre: m ]];
+
+auto g5(int m) [[ pre: m ]]
+{
+  return -m;
+}
+
+template<typename T>
+auto g6(T t) [[ post r: r == t ]];
+
+template<typename S>
+auto g6(S s) [[ post q: q == s ]]
+{
+  return s;
+}
+
+template<typename T>
+T g7(T t) [[ post r: r == t ]];
+
+template<typename S>
+S g7(S s) [[ post q: q == s ]]
+{
+  return s;
+}
+
+template<typename T>
+auto g8(T t) [[ post r: r == t && sizeof(decltype(::g8(t))) > 2 ]]; // { dg-error "not been declared" }
+
+// This failure is related to the fact that we've invalidated the previous
+// contract. 
+template<typename S>
+auto g8(S s) [[ post q: q == s && sizeof(decltype(::g8(s))) > 2 ]] // { dg-error "mismatched" }
+{
+  return s;
+}
+
+// non defining pre, bad
+auto f0(int z)
+  [[ pre: sizeof(decltype(f0(5))) > 4 ]]; // { dg-error "not declared" }
+
+// defining pre, still ill formed
+auto f1(int z)
+  [[ pre: sizeof(decltype(f1(5))) > 4 ]] // { dg-error "not declared" }
+{
+  return '5';
+}
+
+// undeduced using postcon, OK
+auto f2(int m) [[ post r: r == m ]];
+
+auto f2(int n) [[ post s: s == n ]]
+{
+  return n;
+}
+
+template<typename T>
+void f3(T t) [[ post r: false ]] // { dg-error "function does not return a value" }
+{
+}
+
+int main(int, char**)
+{
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-deduced2.C b/gcc/testsuite/g++.dg/contracts/contracts-deduced2.C
new file mode 100644
index 00000000000..da9c019f10a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-deduced2.C
@@ -0,0 +1,84 @@
+// check that contracts work around deduced return types
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+auto g0(int a) [[ pre: a < 0 ]] [[ post r: r > 0 ]]
+{
+  return -a * 1.2;
+}
+
+int g1(int m) [[ post r: r == m ]];
+
+int g1(int n) [[ post s: s == n ]]
+{
+  return -n;
+}
+
+int g2(int z)
+{
+  return -z;
+}
+
+int g3(int z)
+  [[ pre: sizeof(decltype(g2(5))) > 4 ]]
+{
+  return -z;
+}
+
+auto g4(int m) [[ post: m ]];
+
+auto g4(int m) [[ post: m ]]
+{
+  return -m;
+}
+
+auto g5(int m) [[ pre: m ]];
+
+auto g5(int m) [[ pre: m ]]
+{
+  return -m;
+}
+
+template<typename T>
+auto g6(T t) [[ post r: r == t ]];
+
+template<typename S>
+auto g6(S s) [[ post q: q == s ]]
+{
+  return -s;
+}
+
+// template<typename T>
+// T g7(T t) [[ post r: r == t ]];
+
+template<typename S>
+S g7(S s) [[ post q: q == s ]]
+{
+  return -s;
+}
+
+int main(int, char**) {
+  g0(5);
+  g1(6);
+  g2(1);
+  g3(1);
+  g4(0);
+  g5(0);
+  g6(5);
+  g6(5.5);
+  g7(5);
+  g7(6.6);
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 5 g0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 5 g0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 12 g1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 23 g3 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 30 g4 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 37 g5 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 46 g6<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 46 g6<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 55 g7<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 55 g7<double> .*(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-friend1.C b/gcc/testsuite/g++.dg/contracts/contracts-friend1.C
new file mode 100644
index 00000000000..0ccfbe2c7c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-friend1.C
@@ -0,0 +1,40 @@
+// ensure contracts on friend declarations are a complete class context
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+struct X {
+  friend void fn0(X x) [[ pre: x.a > 0 ]] { }
+
+  friend void fn2(X x);
+  static void fns0(X x) [[ pre: x.a > 0 ]] { }
+  static void fns1(X x) [[ pre: x.a > 0 ]];
+  static void fns2(X x);
+
+  friend void fn(X &x) { x.a = -5; }
+
+private:
+  int a{10};
+};
+
+void fn2(X x) [[ pre: x.a > 0 ]] { }
+void X::fns1(X x) { }
+void X::fns2(X x) [[ pre: x.a > 0 ]] { }
+
+int main(int, char**) {
+  X x;
+  fn(x); // no contract
+
+  fn0(x);
+  fn2(x);
+
+  X::fns0(x);
+  X::fns1(x);
+  X::fns2(x);
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 6 fn0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 19 fn2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 9 X::fns0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 X::fns1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 21 X::fns2 .*(\n|\r\n|\r)*" }
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-ft1.C b/gcc/testsuite/g++.dg/contracts/contracts-ft1.C
new file mode 100644
index 00000000000..e5d5be7cb6a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-ft1.C
@@ -0,0 +1,14 @@
+// { dg-do compile }
+
+#ifdef __cpp_contracts
+static_assert (false);
+#endif
+
+#ifdef __cpp_contracts_literal_semantics
+static_assert (false);
+#endif
+
+#ifdef __cpp_contracts_roles
+static_assert (false);
+#endif
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-ignore1.C b/gcc/testsuite/g++.dg/contracts/contracts-ignore1.C
new file mode 100644
index 00000000000..c8ae6568166
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-ignore1.C
@@ -0,0 +1,30 @@
+// test that ignored contracts do instatiate templates
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+template<typename T>
+int f(T t)
+{
+  return -t;
+}
+
+int dummy()
+{
+  [[ assert ignore: f(1.0) > 0 ]];
+  return -1;
+}
+
+template<>
+int f(double t) // { dg-error "specialization of.*after instantiation" }
+{
+  return -1.0;
+}
+
+int main()
+{
+  dummy();
+  f(1);
+  f(1.0);
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-ignore2.C b/gcc/testsuite/g++.dg/contracts/contracts-ignore2.C
new file mode 100644
index 00000000000..5cf800a3559
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-ignore2.C
@@ -0,0 +1,26 @@
+// baseline for testing assert contracts being turned into compile time
+// assumptions; see contracts-assume2 for the assumed case
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts" }
+#include <cstdio>
+
+int fun(int x) {
+  [[assert audit: x > 0]];
+  if(x <= 0)
+  {
+    printf("%d: test x<=0 opt out\n", x);
+    return -1;
+  }
+  else
+  {
+    printf("%d: test x>0\n", x);
+    return 0;
+  }
+}
+
+int main(int, char**) {
+  volatile int x = -1;
+  return fun(x);
+}
+
+// { dg-shouldfail "" }
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-large-return.C b/gcc/testsuite/g++.dg/contracts/contracts-large-return.C
new file mode 100644
index 00000000000..ea8e73a387f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-large-return.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+struct Foo
+{
+  int x;
+  bool y;
+  long z[4];
+};
+
+Foo foo() [[ pre: true ]]
+{
+  return {};
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-multiline1.C b/gcc/testsuite/g++.dg/contracts/contracts-multiline1.C
new file mode 100644
index 00000000000..8145c61e827
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-multiline1.C
@@ -0,0 +1,19 @@
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+int main(int, char **)
+{
+  int x = 5;
+  int y = 10;
+  [[ assert:
+    x
+    <
+    10
+    &&
+    y
+    >
+    123
+  ]];
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 8 main x < 10 && y > 123.*(\n|\r\n|\r)*" }
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-multiple-inheritance1.C b/gcc/testsuite/g++.dg/contracts/contracts-multiple-inheritance1.C
new file mode 100644
index 00000000000..3f2f5edd6ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-multiple-inheritance1.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+struct BaseA {
+  virtual int fun(int n) [[ pre: n > 0 ]] { return -n; }
+};
+
+struct BaseB {
+  virtual int fun(int n) [[ pre: n > 0 ]] { return -n; }
+};
+
+struct Child : public BaseA, BaseB {
+  int fun(int n) [[ pre: n > 0 ]] { return -n; }
+};
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-multiple-inheritance2.C b/gcc/testsuite/g++.dg/contracts/contracts-multiple-inheritance2.C
new file mode 100644
index 00000000000..37bdac1d63f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-multiple-inheritance2.C
@@ -0,0 +1,33 @@
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+struct BaseA {
+  virtual int fun(int n) [[ pre: n > 0 ]] { return -n; }
+};
+
+struct BaseB {
+  virtual int fun(int n) [[ pre: n < 0 ]] { return -n; }
+};
+
+struct Child1 : public BaseA, BaseB {
+  int fun(int n) [[ pre: n > 0 ]] { return -n; } // { dg-error "mismatched" }
+};
+
+struct Child2 : public BaseA, BaseB {
+  int fun(int n) [[ pre: n < 0 ]] { return -n; } // { dg-error "mismatched" }
+};
+
+struct Child3 : public BaseA, BaseB {
+  int fun(int n) { return -n; }
+};
+
+struct Child4 : public BaseA {
+  int fun(int n);
+};
+
+int Child4::fun(int n)
+  [[ pre: n != 0 ]] // { dg-error "mismatched" }
+{
+  return -n;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-nested-class1.C b/gcc/testsuite/g++.dg/contracts/contracts-nested-class1.C
new file mode 100644
index 00000000000..9f91ff499d0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-nested-class1.C
@@ -0,0 +1,24 @@
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+void gfn3(int n) [[ pre: n > 0 ]];
+
+struct Outer {
+  struct Inner {
+    void fn(int n) [[ pre: n > 0 && bob > 1 ]];
+    void fn2(int n) [[ pre: n > 0 && bob > 1 ]];
+  };
+
+  void fn(int m) [[ pre: m > 1 ]];
+  friend void Inner::fn(int n) [[ pre: n > 0 && bob > 1 ]]; // { dg-error "not declared" }
+
+  friend void gfn(int p) [[ pre: p > 0 ]];
+  friend void gfn(int q) [[ pre: q > 1 ]]; // { dg-error "'q' was not declared" }
+
+  // This should be okay.
+  friend void gfn2(int q);
+  friend void gfn2(int p) [[ pre: p > 0 ]] { }
+
+  static int bob;
+};
+int Outer::bob{-1};
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-nested-class2.C b/gcc/testsuite/g++.dg/contracts/contracts-nested-class2.C
new file mode 100644
index 00000000000..43e75edbef8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-nested-class2.C
@@ -0,0 +1,40 @@
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+void gfn3(int n) [[ pre: n > 0 ]];
+
+struct Outer {
+  struct Inner {
+    void fn(int n) [[ pre: n > 0 && bob > 1 ]];
+  };
+
+  void fn(int m) [[ pre: m > 1 ]];
+
+  friend void gfn1(int q);
+  friend void gfn1(int p) [[ pre: p > 0 ]] { }
+
+  friend void gfn2(int q, Outer *);
+  friend void gfn2(int p, Outer *) [[ pre: p > 0 ]] { }
+
+  friend void gfn3(int n);
+
+  static int bob;
+};
+int Outer::bob{-1};
+
+void Outer::Inner::fn(int x) { }
+void Outer::fn(int y) { }
+
+void gfn3(int n) { }
+void gfn1(int q);
+
+int main(int, char **) {
+  Outer::Inner in;
+  in.fn(-5);
+  Outer out;
+  out.fn(-6);
+  gfn1(-7);
+  gfn2(-8, &out);
+  gfn3(-9);
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-nocopy1.C b/gcc/testsuite/g++.dg/contracts/contracts-nocopy1.C
new file mode 100644
index 00000000000..4608a4dc181
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-nocopy1.C
@@ -0,0 +1,24 @@
+// Contracts shouldn't introduce more copies.
+
+// { dg-do compile { target c++20 } }
+// { dg-additional-options -fcontracts }
+
+struct A
+{
+  int i;
+  A(int i): i(i) { }
+  A(const A&) = delete;
+};
+
+A f(A a)
+  [[ pre: a.i > 0 ]]
+  [[ post r: r.i > 0 ]]
+{
+  return {a.i};
+}
+
+int main()
+{
+  if (f({42}).i != 42)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-override.C b/gcc/testsuite/g++.dg/contracts/contracts-override.C
new file mode 100644
index 00000000000..f96aa988e0f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-override.C
@@ -0,0 +1,43 @@
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+struct Foo {
+  virtual int f0(int n) [[ pre: false ]] { return n; }
+  virtual int f1(int n) [[ pre: false ]] { return n; }
+  virtual int f2(int n) [[ pre: false ]] { return n; }
+  virtual int f3(int n) [[ pre: false ]] { return n; }
+  virtual int f4(int n) [[ pre: false ]] { return n; }
+  virtual int f5(int n) [[ pre: false ]] { return n; }
+  virtual int f6(int n) [[ pre: false ]] { return n; }
+  virtual int f7(int n) [[ pre: false ]] { return n; }
+  virtual int f8(int n) [[ pre: false ]] { return n; }
+  virtual int f9(int n) [[ pre: false ]] { return n; }
+  virtual int f10(int n) [[ pre: false ]] { return n; }
+  virtual int f11(int n) [[ pre: n > 0 ]] [[ pre: n > 1 ]] { return n; }
+  virtual int f12(int n) [[ pre: n > 0 ]] [[ pre: n > 1 ]] { return n; }
+};
+
+struct Bar : Foo {
+  [[ pre: n > -1 ]] int f0(int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
+  int [[ pre: n > -2 ]] f1(int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
+  int f2 [[ pre: n > -3 ]] (int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
+  int f4([[ pre: n > -4 ]] int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
+  int f5(int [[ pre: n > -5 ]] n = 0) override { return -n; } // { dg-error "contracts must appertain" }
+  int f6(int n [[ pre: n > -6 ]] = 0) override { return -n; } // { dg-error "contracts must appertain" }
+  int f7(int n = [[ pre: n > -7 ]] 0) override { return -n; }
+  // { dg-error "expected identifier" "" { target *-*-* } .-1 }
+  // { dg-error "expected .\{. before numeric" "" { target *-*-* } .-2 }
+  // { dg-error "invalid user-defined conversion" "" { target *-*-* } .-3 }
+  // { dg-error "expected .,." "" { target *-*-* } .-4 }
+  int f8(int n = 0 [[ pre: n > -8 ]]) override { return -n; }
+  // { dg-error "shall only introduce an attribute" "" { target *-*-* } .-1 }
+  int f9(int n = 0) [[ pre: n > -9 ]] override { return -n; } // { dg-error "mismatched contract" }
+
+  // The grammar doesn't appear to permit contracts after the virt-specifiers
+  // but the parser will happily add these to an attribute list that is not
+  // owned by the function declarator.
+  int f10(int n = 0) override [[ pre: n > -10 ]] { return -n; } // { dg-error "contracts must appertain" }
+  int f11(int n) [[ pre: n > 1 ]] override [[ pre: n > 0 ]] { return -n; } // { dg-error "contracts must appertain" }
+  int f12(int n) [[ pre: n > 0 ]] override [[ pre: n > 1 ]] { return -n; } // { dg-error "contracts must appertain" }
+};
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-post1.C b/gcc/testsuite/g++.dg/contracts/contracts-post1.C
new file mode 100644
index 00000000000..451760ea822
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-post1.C
@@ -0,0 +1,74 @@
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+int f1(int n)
+  [[pre: n >= 0]]
+  [[post r: r >= 0]]
+  [[post r: !(r < 0)]]
+{
+  return n;
+}
+
+int f2(int n)
+  [[post: true]]
+{
+  return 0;
+}
+
+int f3(int n)
+  [[post r: r >= n]]
+{
+  return n + 1;
+}
+
+int f4(int n)
+  [[post: x > 0]] // { dg-error "not declared" }
+{
+  return 0;
+}
+
+void f5()
+  [[post: true]]
+{ }
+
+void f6()
+  [[post r: true]] // { dg-error "function does not return a value" }
+{ }
+
+int f7(int n)
+  [[post: n > 0]]
+{
+  return x; // { dg-error "not declared" }
+}
+
+void f8(int n)
+  [[post: n > 0]]
+{
+  return;
+}
+
+void f9(int n)
+  [[post: n > 0]]
+{
+  return n; // { dg-error "return-statement with a value" }
+}
+
+int f10(int n)
+  [[post: n > 0]]
+{
+  return; // { dg-error "return-statement with no value" }
+}
+
+void f11()
+  [[post: true]]
+{
+  constexpr int n = 0;
+  return n; // { dg-error "return-statement with a value" }
+}
+
+int f12()
+  [[post: true]]
+{
+  return; // { dg-error "return-statement with no value" }
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-post2.C b/gcc/testsuite/g++.dg/contracts/contracts-post2.C
new file mode 100644
index 00000000000..7665f829107
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-post2.C
@@ -0,0 +1,13 @@
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts" }
+
+int f1(int n)
+  [[post r: r == n]]
+{
+  return n;
+}
+
+int main()
+{
+  f1(0);
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-post3.C b/gcc/testsuite/g++.dg/contracts/contracts-post3.C
new file mode 100644
index 00000000000..9f1dffd8f6a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-post3.C
@@ -0,0 +1,15 @@
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts" }
+// { dg-shouldfail "assert violation" }
+// { dg-output "default std::handle_contract_violation called" }
+
+int f1(int n)
+  [[post r: r > n]]
+{
+  return n;
+}
+
+int main()
+{
+  f1(0);
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-post4.C b/gcc/testsuite/g++.dg/contracts/contracts-post4.C
new file mode 100644
index 00000000000..af770c3dc94
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-post4.C
@@ -0,0 +1,36 @@
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+struct S
+{
+  S() [[post: n == 0]]
+    : n(0)
+  { }
+
+  ~S() [[post: true]]
+  { }
+
+  int f1()
+    [[post r: n == r]]
+  {
+    return n;
+  }
+
+  int f2()
+    [[post r: r == x]] // { dg-error "not declared" }
+  {
+    return n;
+  }
+
+  void f3()
+    [[post r: n]] // { dg-error "does not return a value" }
+  {
+  }
+
+  int n = 0;
+};
+
+int main()
+{
+  // f1(0);
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-post5.C b/gcc/testsuite/g++.dg/contracts/contracts-post5.C
new file mode 100644
index 00000000000..c9127fb2cc9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-post5.C
@@ -0,0 +1,19 @@
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+// Tests for function templates
+
+template<typename T>
+T f1(T n)
+  [[pre: n >= 0]]
+  [[post r: r >= 0]]
+  [[post r: !(r < 0)]]
+{
+  return n;
+}
+
+
+void driver()
+{
+  f1(0);
+}
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-post6.C b/gcc/testsuite/g++.dg/contracts/contracts-post6.C
new file mode 100644
index 00000000000..f8246fbc15f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-post6.C
@@ -0,0 +1,30 @@
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+// Test for class members
+
+template<typename T>
+struct S
+{
+  S(T n)
+    [[post: true]]
+    : n(n)
+  { }
+
+  T f1(T n)
+    [[pre: n >= 0]]
+    [[post r: r >= 0]]
+    [[post r: !(r < 0)]]
+  {
+    return n;
+  }
+
+  T n;
+};
+
+
+void driver()
+{
+  S<int> s1(0);
+  s1.f1(2);
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre1.C b/gcc/testsuite/g++.dg/contracts/contracts-pre1.C
new file mode 100644
index 00000000000..908103cde1e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre1.C
@@ -0,0 +1,36 @@
+// generic pre contract parsing checks
+//   check omitted, 'default', 'audit', and 'axiom' contract levels parse
+//   ensure that an invalid contrcat level 'off' errors
+//   ensure that a predicate referencing an undefined variable errors
+//   ensure that a missing colon after contract level errors
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+void f1(int x) [[ pre: x >= 0 ]] { }
+void f2(int x) [[ pre default: x >= 0 ]] { }
+void f3(int x) [[ pre audit: x >= 0 ]] { }
+void f4(int x) [[ pre axiom: x >= 0 ]] { }
+
+void finvalid(int x) [[ pre invalid: x >= 0 ]] { } // { dg-error "expected contract level" }
+void fundeclared() [[ pre: x >= 0 ]] { } // { dg-error ".x. was not declared in this scope" }
+void fmissingcolon(int x) [[ pre default x == 0]] { } // { dg-error "expected .:. before .x." }
+
+int Z;
+void (*fp1)(int x) [[ pre: Z > 0 ]]; // { dg-error "contracts must appertain" }
+void (*fp2 [[ pre: Z > 0 ]])(int x); // { dg-error "contracts must appertain" }
+typedef void (*fp3)(int x) [[ pre: Z > 0 ]]; // { dg-error "contracts must appertain" }
+typedef void (*fp4 [[ pre: Z > 0 ]])(int x); // { dg-error "contracts must appertain" }
+fp3 fn5(int a) [[ pre: a > 0 ]]; // { dg-bogus "contracts must appertain" }
+
+int xyz;
+[[ pre: xyz ]] struct Bar; // { dg-error "contracts must appertain" }
+// { dg-warning "attribute ignored" "" { target *-*-* } .-1 }
+struct [[ pre: xyz ]] Bar; // { dg-error "contracts must appertain" }
+struct Bar [[ pre: xyz ]]; // { dg-error "contracts must appertain" }
+struct Zoo {} x [[ pre: xyz ]]; // { dg-error "contracts must appertain" }
+
+void f6(int x) [[ pre: x > 0 ; // { dg-error "expected .]." }
+void f7(int x) [[ pre: x > 0 ]; // { dg-error "expected .]." }
+void f8(int x) [[ pre: x > 0 { }; // { dg-error "expected .]." }
+void f9(int x) [[ pre: x > 0 ] { }; // { dg-error "expected .]." }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre10.C b/gcc/testsuite/g++.dg/contracts/contracts-pre10.C
new file mode 100644
index 00000000000..877e9ada404
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre10.C
@@ -0,0 +1,190 @@
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+struct M
+{
+  template<typename T>
+  int f(int a) [[ pre: a > 0 ]];
+
+  template<typename T>
+  int g(int a) [[ pre: a > 0 ]]
+  {
+    return -a;
+  }
+
+  template<typename T>
+  int f_arg(T a) [[ pre: a > 0 ]];
+
+  template<typename T>
+  int g_arg(T a) [[ pre: a > 0 ]]
+  {
+    return (int)-a;
+  }
+
+  template<typename T>
+  T f_ret(int a) [[ pre: a > 0 ]];
+
+  template<typename T>
+  T g_ret(int a) [[ pre: a > 0 ]]
+  {
+    return -a * 1.5;
+  }
+};
+
+template<typename T>
+int M::f(int a)
+{
+  return -a;
+}
+
+template<typename T>
+int M::f_arg(T a)
+{
+  return (int)-a;
+}
+
+template<typename T>
+T M::f_ret(int a)
+{
+  return -a * (T)1.5;
+}
+
+template<typename T>
+struct S
+{
+  template<typename Q>
+  int f(int a) [[ pre: a > 0 ]];
+
+  template<typename Q>
+  int g(int a) [[ pre: a > 0 ]]
+  {
+    return -a;
+  }
+};
+
+template<typename T>
+template<typename Q>
+int S<T>::f(int a)
+{
+  return -a;
+}
+
+#include <cstdio>
+int main(int, char**)
+{
+  {
+    M m;
+    printf ("=================================\n");
+    printf ("m.f<int>(-10): %d\n", m.f<int>(-10));
+    printf ("m.f<double>(-11.5): %d\n", m.f<double>(-11.5));
+    printf ("m.f<int>(10): %d\n", m.f<int>(10));
+    printf ("m.f<double>(11.5): %d\n", m.f<double>(11.5));
+
+    printf ("=================================\n");
+    printf ("m.g<int>(-10): %d\n", m.g<int>(-10));
+    printf ("m.g<double>(-11.5): %d\n", m.g<double>(-11.5));
+    printf ("m.g<int>(10): %d\n", m.g<int>(10));
+    printf ("m.g<double>(11.5): %d\n", m.g<double>(11.5));
+
+    printf ("=================================\n");
+    printf ("m.f_arg(-10): %d\n", m.f_arg(-10));
+    printf ("m.f_arg(-11.5): %d\n", m.f_arg(-11.5));
+    printf ("m.f_arg(10): %d\n", m.f_arg(10));
+    printf ("m.f_arg(11.5): %d\n", m.f_arg(11.5));
+
+    printf ("=================================\n");
+    printf ("m.g_arg(-10): %d\n", m.g_arg(-10));
+    printf ("m.g_arg(-11.5): %d\n", m.g_arg(-11.5));
+    printf ("m.g_arg(10): %d\n", m.g_arg(10));
+    printf ("m.g_arg(11.5): %d\n", m.g_arg(11.5));
+
+    printf ("=================================\n");
+    printf ("m.f_ret<int>(-10): %d\n", m.f_ret<int>(-10));
+    printf ("m.f_ret<double>(-11.5): %f\n", m.f_ret<double>(-11.5));
+    printf ("m.f_ret<int>(10): %d\n", m.f_ret<int>(10));
+    printf ("m.f_ret<double>(11.5): %f\n", m.f_ret<double>(11.5));
+
+    printf ("=================================\n");
+    printf ("m.g_ret<int>(-10): %d\n", m.g_ret<int>(-10));
+    printf ("m.g_ret<double>(-11.5): %f\n", m.g_ret<double>(-11.5));
+    printf ("m.g_ret<int>(10): %d\n", m.g_ret<int>(10));
+    printf ("m.g_ret<double>(11.5): %f\n", m.g_ret<double>(11.5));
+  }
+
+  {
+    S<int> s;
+    printf ("=================================\n");
+    s.f<int>(-10);
+
+    s.f<double>(-10);
+
+    printf ("=================================\n");
+    s.g<int>(-10);
+
+    s.g<double>(-10);
+  }
+
+  {
+    S<double> s;
+    printf ("=================================\n");
+    s.f<int>(-10);
+
+    s.f<double>(-10);
+
+    printf ("=================================\n");
+    s.g<int>(-10);
+
+    s.g<double>(-10);
+  }
+
+  return 0;
+}
+
+// { dg-output "=================================(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 7 M::f<int> .*(\n|\r\n|\r)*" }
+// { dg-output "m.f<int>.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 7 M::f<double> .*(\n|\r\n|\r)*" }
+// { dg-output "m.f<double>.-11.5.: 11(\n|\r\n|\r)*" }
+// { dg-output "m.f<int>.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "m.f<double>.11.5.: -11(\n|\r\n|\r)*" }
+// { dg-output "=================================(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 M::g<int> .*(\n|\r\n|\r)*" }
+// { dg-output "m.g<int>.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 M::g<double> .*(\n|\r\n|\r)*" }
+// { dg-output "m.g<double>.-11.5.: 11(\n|\r\n|\r)*" }
+// { dg-output "m.g<int>.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "m.g<double>.11.5.: -11(\n|\r\n|\r)*" }
+// { dg-output "=================================(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 16 M::f_arg<int> .*(\n|\r\n|\r)*" }
+// { dg-output "m.f_arg.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 16 M::f_arg<double> .*(\n|\r\n|\r)*" }
+// { dg-output "m.f_arg.-11.5.: 11(\n|\r\n|\r)*" }
+// { dg-output "m.f_arg.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "m.f_arg.11.5.: -11(\n|\r\n|\r)*" }
+// { dg-output "=================================(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 19 M::g_arg<int> .*(\n|\r\n|\r)*" }
+// { dg-output "m.g_arg.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 19 M::g_arg<double> .*(\n|\r\n|\r)*" }
+// { dg-output "m.g_arg.-11.5.: 11(\n|\r\n|\r)*" }
+// { dg-output "m.g_arg.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "m.g_arg.11.5.: -11(\n|\r\n|\r)*" }
+// { dg-output "=================================(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 25 M::f_ret<int> .*(\n|\r\n|\r)*" }
+// { dg-output "m.f_ret<int>.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 25 M::f_ret<double> .*(\n|\r\n|\r)*" }
+// { dg-output "m.f_ret<double>.-11.5.: 16.500000(\n|\r\n|\r)*" }
+// { dg-output "m.f_ret<int>.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "m.f_ret<double>.11.5.: -16.500000(\n|\r\n|\r)*" }
+// { dg-output "=================================(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 28 M::g_ret<int> .*(\n|\r\n|\r)*" }
+// { dg-output "m.g_ret<int>.-10.: 15(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 28 M::g_ret<double> .*(\n|\r\n|\r)*" }
+// { dg-output "m.g_ret<double>.-11.5.: 16.500000(\n|\r\n|\r)*" }
+// { dg-output "m.g_ret<int>.10.: -15(\n|\r\n|\r)*" }
+// { dg-output "m.g_ret<double>.11.5.: -16.500000(\n|\r\n|\r)*" }
+// { dg-output "=================================(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 59 S<int>::g<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 59 S<int>::g<double> .*(\n|\r\n|\r)*" }
+// { dg-output "=================================(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 59 S<double>::g<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 59 S<double>::g<double> .*(\n|\r\n|\r)*" }
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre2.C b/gcc/testsuite/g++.dg/contracts/contracts-pre2.C
new file mode 100644
index 00000000000..4fe2f9d0192
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre2.C
@@ -0,0 +1,212 @@
+// basic test to ensure pre contracts work for free functions
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+
+namespace nullary
+{
+  int x = 10;
+  int y = 10;
+
+  void fun()
+    [[ pre: x < 0 ]]
+  {
+    printf("fun::x: %d\n", x);
+  }
+
+  void fun2()
+    [[ pre: x < 0 ]]
+    [[ pre: y < 0 ]]
+  {
+    printf("fun2::x: %d fun2::y: %d\n", x, y);
+  }
+
+  void funend()
+    [[ pre: x < 0 ]];
+}
+
+namespace nonvoid
+{
+  int x = 10;
+  double y = 10.5;
+
+  struct S
+  {
+    bool z;
+  };
+
+  void vfun()
+    [[ pre: x < 0 ]]
+  {
+    printf("vfun::x: %d\n", x);
+  }
+
+  int fun()
+    [[ pre: x < 0 ]]
+  {
+    printf("fun::x: %d\n", x);
+    return x;
+  }
+
+  double fun2()
+    [[ pre: x < 0 ]]
+    [[ pre: y < 0 ]]
+  {
+    printf("fun2::x: %d fun2::y: %f\n", x, y);
+    return y;
+  }
+
+  S funend()
+    [[ pre: x < 0 ]];
+}
+
+namespace nonnullary
+{
+  int x = 10;
+  double y = 10.5;
+
+  struct S
+  {
+    bool z;
+  };
+
+  void vfun(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]]
+  {
+    printf("vfun::x: %d\n", x);
+  }
+
+  int fun(int m, double n)
+    [[ pre: x < 0 ]]
+  {
+    printf("fun::x: %d\n", x);
+    return x;
+  }
+
+  double fun2(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: y < 0 ]]
+    [[ pre: m < 0 ]]
+    [[ pre: n < 0 ]]
+  {
+    printf("fun2::x: %d fun2::y: %f\n", x, y);
+    return y;
+  }
+
+  S funend(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]];
+}
+
+int main(int, char**) {
+  // nullary void
+  {
+    nullary::fun();
+    nullary::fun2();
+    nullary::funend();
+  }
+
+  // nullary non void
+  {
+    nonvoid::vfun();
+
+    int f = 13;
+    f = nonvoid::fun();
+    printf("main::f: %d\n", f);
+    double d = 13.37;
+    d = nonvoid::fun2();
+    printf("main::d: %f\n", d);
+    nonvoid::S s = nonvoid::funend();
+    printf("main::s.z: %d\n", s.z ? 1 : 0);
+  }
+
+  // non-nullary non-void
+  {
+    int x = 11;
+    double y = 11.5;
+
+    nonnullary::vfun(x, y);
+
+    int f = 13;
+    f = nonnullary::fun(x, y);
+    printf("main::f: %d\n", f);
+    double d = 13.37;
+    d = nonnullary::fun2(x, y);
+    printf("main::d: %f\n", d);
+    nonnullary::S s = nonnullary::funend(x, y);
+    printf("main::s.z: %d\n", s.z ? 1 : 0);
+  }
+  return 0;
+}
+
+namespace nullary
+{
+  void funend()
+    [[ pre: x < 0 ]]
+  {
+    printf("funend::x: %d\n", x);
+  }
+}
+
+namespace nonvoid
+{
+  S funend()
+    [[ pre: x < 0 ]]
+  {
+    printf("funend::x: %d\n", x);
+    S s;
+    s.z = true;
+    return s;
+  }
+}
+
+namespace nonnullary
+{
+  S funend(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]]
+  {
+    printf("funend::x: %d\n", x);
+    S s;
+    s.z = true;
+    return s;
+  }
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 12 nullary::fun .*(\n|\r\n|\r)*" }
+// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 18 nullary::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 19 nullary::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "fun2::x: 10 fun2::y: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 146 nullary::funend .*(\n|\r\n|\r)*" }
+// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 39 nonvoid::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 45 nonvoid::fun .*(\n|\r\n|\r)*" }
+// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::f: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 52 nonvoid::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 53 nonvoid::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 155 nonvoid::funend .*(\n|\r\n|\r)*" }
+// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 74 nonnullary::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 75 nonnullary::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 81 nonnullary::fun .*(\n|\r\n|\r)*" }
+// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::f: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 88 nonnullary::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 89 nonnullary::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 90 nonnullary::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 91 nonnullary::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 167 nonnullary::funend .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 168 nonnullary::funend .*(\n|\r\n|\r)*" }
+// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre2a1.C b/gcc/testsuite/g++.dg/contracts/contracts-pre2a1.C
new file mode 100644
index 00000000000..26167492b97
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre2a1.C
@@ -0,0 +1,33 @@
+// ensure the feature test macros are defined pre c++20 while we still support
+// -fcontracts independent of std version
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-fcontracts" }
+
+static_assert (__cpp_contracts >= 201906, "__cpp_contracts");
+static_assert (__cpp_contracts_literal_semantics >= 201906, "__cpp_contracts_literal_semantics");
+static_assert (__cpp_contracts_roles >= 201906, "__cpp_contracts_roles");
+
+int main()
+{
+  int x;
+
+  [[assert: x >= 0]];
+  [[assert default: x < 0]];
+  [[assert audit: x == 0]];
+  [[assert axiom: x == 1]];
+
+  [[assert: x > 0 ? true : false]];
+  [[assert: x < 0 ? true : false]];
+
+  [[assert ignore: x >= 0]];
+  [[assert assume: x >= 0]];
+  [[assert check_never_continue: x >= 0]];
+  [[assert check_maybe_continue: x >= 0]];
+
+  [[assert %default: x >= 0]];
+  [[assert default %default: x < 0]];
+  [[assert audit %default: x == 0]];
+  [[assert axiom %default: x == 1]];
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre2a2.C b/gcc/testsuite/g++.dg/contracts/contracts-pre2a2.C
new file mode 100644
index 00000000000..db9a0c37aa0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre2a2.C
@@ -0,0 +1,22 @@
+// basic test to ensure contracts work pre-c++2a
+// { dg-do run { target c++11 } }
+// { dg-additional-options "-fcontracts -fcontract-continuation-mode=on" }
+
+int f(int n)
+  [[ pre: n > 0 ]]
+  [[ post r: r < 0 ]]
+{
+  [[ assert: n > 0 ]];
+  return -n;
+}
+
+int main()
+{
+  f(-5);
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 6 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 9 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 7 .*(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre3.C b/gcc/testsuite/g++.dg/contracts/contracts-pre3.C
new file mode 100644
index 00000000000..1c4e3a98e63
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre3.C
@@ -0,0 +1,525 @@
+// tests to ensure pre contracts work on member functions
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+
+namespace member
+{
+  int x = 10;
+  double y = 10.5;
+
+  struct S
+  {
+    bool z;
+  };
+
+  struct T1
+  {
+    void vfun(int m, double n)
+      [[ pre: x < 0 ]]
+      [[ pre: m < 0 ]];
+
+    int fun(int m, double n)
+      [[ pre: x < 0 ]];
+
+    double fun2(int m, double n)
+      [[ pre: x < 0 ]]
+      [[ pre: y < 0 ]]
+      [[ pre: m < 0 ]]
+      [[ pre: n < 0 ]];
+
+    S funend(int m, double n)
+      [[ pre: x < 0 ]]
+      [[ pre: m < 0 ]];
+  };
+
+  void T1::vfun(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]]
+  {
+    printf("vfun::x: %d\n", x);
+  }
+
+
+  int T1::fun(int m, double n)
+    [[ pre: x < 0 ]]
+  {
+    printf("fun::x: %d\n", x);
+    return x;
+  }
+
+  double T1::fun2(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: y < 0 ]]
+    [[ pre: m < 0 ]]
+    [[ pre: n < 0 ]]
+  {
+    printf("fun2::x: %d fun2::y: %f\n", x, y);
+    return y;
+  }
+}
+
+namespace special
+{
+  struct T1
+  {
+    T1(int m, int n)
+      [[ pre: m < 0 ]]
+      [[ pre: n < 0 ]];
+
+    int operator+(int m)
+      [[ pre: m > 0 ]]
+      [[ pre: v > 0 ]];
+
+    int operator-(int m)
+      [[ pre: m > 0 ]]
+      [[ pre: v > 0 ]];
+
+    ~T1()
+      [[ pre: v > 0 ]];
+
+    int v{-10};
+  };
+
+  T1::T1(int m, int n)
+    [[ pre: m < 0 ]]
+    [[ pre: n < 0 ]]
+    : v{-m * n}
+  {
+  }
+
+  int T1::operator+(int m)
+    [[ pre: m > 0 ]]
+    [[ pre: v > 0 ]]
+  {
+    return v + m;
+  }
+
+  int T1::operator-(int m)
+    [[ pre: m > 0 ]]
+    [[ pre: v > 0 ]]
+  {
+    return v - m;
+  }
+
+  T1::~T1()
+    [[ pre: v > 0 ]]
+  {
+  }
+
+  struct T2
+  {
+    T2(int m, int n)
+      [[ pre: m < 0 ]]
+      [[ pre: n < 0 ]]
+      : v{-m * n}
+    {
+    }
+
+    int operator+(int m)
+      [[ pre: m > 0 ]]
+      [[ pre: v > 0 ]]
+    {
+      return v + m;
+    }
+
+    int operator-(int m)
+      [[ pre: m > 0 ]]
+      [[ pre: v > 0 ]]
+    {
+      return v - m;
+    }
+
+    ~T2()
+      [[ pre: v > 0 ]]
+    {
+    }
+
+    int v{-10};
+  };
+
+  struct TC : T1
+  {
+    TC(int m, int n)
+      [[ pre: m < -1 ]]
+      [[ pre: n < -1 ]]
+      : T1{m, n}
+    {
+    }
+
+    ~TC()
+      [[ pre: vc < 0 ]]
+    {
+    }
+
+    TC(int a)
+      [[ pre: a < 0 ]]
+      : TC{a, a}
+    {
+    }
+
+    int vc{10};
+  };
+
+  void test()
+  {
+    T1 t1{10, 20};
+    int a = t1 - -5;
+    int b = t1 + -5;
+    printf("==========\n");
+
+    T2 t2{10, 20};
+    int k = t2 - -5;
+    int j = t2 + -5;
+    printf("==========\n");
+
+    TC tc{10, 20};
+    printf("==========\n");
+
+    TC tc2{10};
+    printf("==========\n");
+  }
+}
+
+namespace virt
+{
+  struct T1
+  {
+    virtual int fun(int m, int n)
+      [[ pre: m > 0 ]]
+      [[ pre: n > 0 ]]
+      [[ pre: v > 0 ]];
+    int v{-10};
+  };
+
+  int T1::fun(int m, int n)
+    [[ pre: m > 0 ]]
+    [[ pre: n > 0 ]]
+    [[ pre: v > 0 ]]
+  {
+    printf("T1::fun::m: %d, T1::fun::n: %d, T1::v: %d\n", m, n, v);
+    return m * n * v;
+  }
+
+  struct T2 : T1
+  {
+  };
+
+  struct T3 : T2
+  {
+    virtual int fun(int m, int n)
+      [[ pre: m > 0 ]]
+      [[ pre: n > 0 ]]
+      [[ pre: v > 0 ]]
+      override
+    {
+      printf("T3::fun::m: %d, T3::fun::n: %d, T3::v: %d\n", m, n, v);
+      return m * n * v;
+    }
+  };
+
+  struct T3b : T2
+  {
+    virtual int fun(int m, int n)
+      [[ pre: m > 0 ]]
+      [[ pre: n > 0 ]]
+      [[ pre: v > 0 ]]
+      override;
+
+    int p(int a)
+      [[ pre: a > 0 ]]
+      [[ pre: v > 0 ]];
+
+    int u(int a)
+      [[ pre: a > 0 ]]
+      [[ pre: z > 0 ]];
+
+    int n(int a)
+      [[ pre: a > 0 ]];
+
+    static int Sn(int a)
+      [[ pre: a > 0 ]];
+
+    int z{-10};
+  };
+
+  int T3b::fun(int m, int n)
+    [[ pre: m > 0 ]]
+    [[ pre: n > 0 ]]
+    [[ pre: v > 0 ]]
+  {
+    printf("T3b::fun::m: %d, T3b::fun::n: %d, T3b::v: %d\n", m, n, v);
+    return m * n * v;
+  }
+
+  int T3b::p(int a)
+    [[ pre: a > 0 ]]
+    [[ pre: v > 0 ]]
+  {
+    printf("T3b::p: a: %d, v: %d\n", a, v);
+    return a * v;
+  }
+
+  int T3b::u(int a)
+    [[ pre: a > 0 ]]
+    [[ pre: z > 0 ]]
+  {
+    printf("T3b::u: a: %d, z: %d\n", a, z);
+    return a * z;
+  }
+
+  int T3b::n(int a)
+    [[ pre: a > 0 ]]
+  {
+    printf("T3b::n: a: %d\n", a);
+    return a;
+  }
+
+  int T3b::Sn(int a)
+    [[ pre: a > 0 ]]
+  {
+    printf("T3b::Sn: a: %d\n", a);
+    return a;
+  }
+
+  struct T3c : T2
+  {
+    int fun(int m, int n)
+      [[ pre: m > 0 ]]
+      [[ pre: n > 0 ]]
+      [[ pre: v > 0 ]]
+    {
+      printf("T3c::fun::m: %d, T3c::fun::n: %d, T3c::v: %d\n", m, n, v);
+      return m * n * v;
+    }
+
+    int p(int a)
+      [[ pre: a > 0 ]]
+      [[ pre: v > 0 ]]
+    {
+      printf("T3c::p: a: %d, v: %d\n", a, v);
+      return a * v;
+    }
+
+    int u(int a)
+      [[ pre: a > 0 ]]
+      [[ pre: z > 0 ]]
+    {
+      printf("T3c::u: a: %d, z: %d\n", a, z);
+      return a * z;
+    }
+
+    int n(int a)
+      [[ pre: a > 0 ]]
+    {
+      printf("T3c::n: a: %d\n", a);
+      return a;
+    }
+
+    static int Sn(int a)
+      [[ pre: a > 0 ]]
+    {
+      printf("T3c::Sn: a: %d\n", a);
+      return a;
+    }
+
+    int z{-10};
+  };
+
+  void t(const char *kind, T1 *t)
+  {
+    printf("=================\n%s:\n", kind);
+    t->fun(-1, -2);
+  }
+
+  void test()
+  {
+    T1 t1;
+    t1.fun(-10, -20);
+
+    T2 t2;
+    t2.fun(-10, -20);
+
+    T3 t3;
+    t3.fun(-10, -20);
+
+    T3b t3b;
+    t3b.fun(-10, -20);
+
+    T3c t3c;
+    t3c.fun(-10, -20);
+
+    t("T1", &t1);
+    t("T2", &t2);
+    t("T3", &t3);
+    t("T3b", &t3b);
+    t("T3c", &t3c);
+
+    printf("=============\n");
+    t3b.p(-3);
+    t3b.u(-3);
+    t3b.n(-3);
+    T3b::Sn(-3);
+
+    printf("=============\n");
+    t3c.p(-3);
+    t3c.u(-3);
+    t3c.n(-3);
+    T3c::Sn(-3);
+  }
+}
+
+int main(int, char**) 
+{
+  // ordinary member functions
+  {
+    int x = 11;
+    double y = 11.5;
+    member::T1 t1;
+    t1.vfun(x, y);
+
+    int f = 13;
+    f = t1.fun(x, y);
+    printf("main::f: %d\n", f);
+    double d = 13.37;
+    d = t1.fun2(x, y);
+    printf("main::d: %f\n", d);
+    member::S s = t1.funend(x, y);
+    printf("main::s.z: %d\n", s.z ? 1 : 0);
+  }
+
+  special::test();
+  virt::test();
+  return 0;
+}
+
+member::S member::T1::funend(int m, double n)
+  [[ pre: x < 0 ]]
+  [[ pre: m < 0 ]]
+{
+  printf("funend::x: %d\n", x);
+  S s;
+  s.z = true;
+  return s;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 37 member::T1::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 38 member::T1::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 45 member::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::f: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 52 member::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 53 member::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 54 member::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 55 member::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 397 member::T1::funend .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 398 member::T1::funend .*(\n|\r\n|\r)*" }
+// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 85 special::T1::T1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 86 special::T1::T1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 99 special::T1::operator- .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 100 special::T1::operator- .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 92 special::T1::operator. .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 93 special::T1::operator. .*(\n|\r\n|\r)*" }
+// { dg-output "==========(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 113 special::T2::T2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 114 special::T2::T2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 127 special::T2::operator- .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 128 special::T2::operator- .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 120 special::T2::operator. .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 121 special::T2::operator. .*(\n|\r\n|\r)*" }
+// { dg-output "==========(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 144 special::TC::TC .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 145 special::TC::TC .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 85 special::T1::T1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 86 special::T1::T1 .*(\n|\r\n|\r)*" }
+// { dg-output "==========(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 156 special::TC::TC .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 144 special::TC::TC .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 145 special::TC::TC .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 85 special::T1::T1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 86 special::T1::T1 .*(\n|\r\n|\r)*" }
+// { dg-output "==========(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 151 special::TC::~TC .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 106 special::T1::~T1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 151 special::TC::~TC .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 106 special::T1::~T1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 134 special::T2::~T2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 106 special::T1::~T1 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "T1::fun::m: -10, T1::fun::n: -20, T1::v: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "T1::fun::m: -10, T1::fun::n: -20, T1::v: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 211 virt::T3::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 212 virt::T3::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 213 virt::T3::fun .*(\n|\r\n|\r)*" }
+// { dg-output "T3::fun::m: -10, T3::fun::n: -20, T3::v: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 247 virt::T3b::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 248 virt::T3b::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 249 virt::T3b::fun .*(\n|\r\n|\r)*" }
+// { dg-output "T3b::fun::m: -10, T3b::fun::n: -20, T3b::v: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 288 virt::T3c::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 289 virt::T3c::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 290 virt::T3c::fun .*(\n|\r\n|\r)*" }
+// { dg-output "T3c::fun::m: -10, T3c::fun::n: -20, T3c::v: -10(\n|\r\n|\r)*" }
+// { dg-output "=================(\n|\r\n|\r)*" }
+// { dg-output "T1:(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "T1::fun::m: -1, T1::fun::n: -2, T1::v: -10(\n|\r\n|\r)*" }
+// { dg-output "=================(\n|\r\n|\r)*" }
+// { dg-output "T2:(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "T1::fun::m: -1, T1::fun::n: -2, T1::v: -10(\n|\r\n|\r)*" }
+// { dg-output "=================(\n|\r\n|\r)*" }
+// { dg-output "T3:(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 211 virt::T3::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 212 virt::T3::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 213 virt::T3::fun .*(\n|\r\n|\r)*" }
+// { dg-output "T3::fun::m: -1, T3::fun::n: -2, T3::v: -10(\n|\r\n|\r)*" }
+// { dg-output "=================(\n|\r\n|\r)*" }
+// { dg-output "T3b:(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 247 virt::T3b::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 248 virt::T3b::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 249 virt::T3b::fun .*(\n|\r\n|\r)*" }
+// { dg-output "T3b::fun::m: -1, T3b::fun::n: -2, T3b::v: -10(\n|\r\n|\r)*" }
+// { dg-output "=================(\n|\r\n|\r)*" }
+// { dg-output "T3c:(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 288 virt::T3c::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 289 virt::T3c::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 290 virt::T3c::fun .*(\n|\r\n|\r)*" }
+// { dg-output "T3c::fun::m: -1, T3c::fun::n: -2, T3c::v: -10(\n|\r\n|\r)*" }
+// { dg-output "=============(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 256 virt::T3b::p .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 257 virt::T3b::p .*(\n|\r\n|\r)*" }
+// { dg-output "T3b::p: a: -3, v: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 264 virt::T3b::u .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 265 virt::T3b::u .*(\n|\r\n|\r)*" }
+// { dg-output "T3b::u: a: -3, z: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 272 virt::T3b::n .*(\n|\r\n|\r)*" }
+// { dg-output "T3b::n: a: -3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 279 virt::T3b::Sn .*(\n|\r\n|\r)*" }
+// { dg-output "T3b::Sn: a: -3(\n|\r\n|\r)*" }
+// { dg-output "=============(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 297 virt::T3c::p .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 298 virt::T3c::p .*(\n|\r\n|\r)*" }
+// { dg-output "T3c::p: a: -3, v: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 305 virt::T3c::u .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 306 virt::T3c::u .*(\n|\r\n|\r)*" }
+// { dg-output "T3c::u: a: -3, z: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 313 virt::T3c::n .*(\n|\r\n|\r)*" }
+// { dg-output "T3c::n: a: -3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 320 virt::T3c::Sn .*(\n|\r\n|\r)*" }
+// { dg-output "T3c::Sn: a: -3(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre4.C b/gcc/testsuite/g++.dg/contracts/contracts-pre4.C
new file mode 100644
index 00000000000..16189cdce9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre4.C
@@ -0,0 +1,92 @@
+// test that contracts on overriding functions are found correctly
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+struct Base
+{
+  virtual int f(int a) [[ pre: a > 0 ]];
+};
+
+int Base::f(int a)
+{
+  return a + 10;
+}
+
+// inherits original
+struct Child0 : Base
+{
+};
+
+// defined out of line, explicit override
+struct Child1 : Base
+{
+  virtual int f(int a) override;
+};
+
+int Child1::f(int a)
+{
+  return a + 20;
+}
+
+// defined out of line
+struct Child2 : Base
+{
+  int f(int a);
+};
+
+int Child2::f(int a)
+{
+  return a + 30;
+}
+
+// defined inline, explicitly override
+struct Child3 : Base
+{
+  virtual int f(int a) override
+  {
+    return a + 40;
+  }
+};
+
+// defined inline
+struct Child4 : Base
+{
+  int f(int a)
+  {
+    return a + 50;
+  }
+};
+
+#include <cstdio>
+int main(int, char**)
+{
+  Base b;
+  Child0 c0;
+  Child1 c1;
+  Child2 c2;
+  Child3 c3;
+  Child4 c4;
+
+  printf("Base: %d\n", b.f(-10));
+  printf("Child0: %d\n", c0.f(-10));
+  printf("Child1: %d\n", c1.f(-10));
+  printf("Child2: %d\n", c2.f(-10));
+  printf("Child3: %d\n", c3.f(-10));
+  printf("Child4: %d\n", c4.f(-10));
+
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 7 Base::f .*(\n|\r\n|\r)*" }
+// { dg-output "Base: 0(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 7 Base::f .*(\n|\r\n|\r)*" }
+// { dg-output "Child0: 0(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 7 Child1::f .*(\n|\r\n|\r)*" }
+// { dg-output "Child1: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 7 Child2::f .*(\n|\r\n|\r)*" }
+// { dg-output "Child2: 20(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 7 Child3::f .*(\n|\r\n|\r)*" }
+// { dg-output "Child3: 30(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 7 Child4::f .*(\n|\r\n|\r)*" }
+// { dg-output "Child4: 40(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre5.C b/gcc/testsuite/g++.dg/contracts/contracts-pre5.C
new file mode 100644
index 00000000000..278a545055f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre5.C
@@ -0,0 +1,81 @@
+// basic test to ensure pre contracts work for free templates
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+
+template<typename T>
+int body(int a)
+  [[ pre: a > 0 ]]
+{
+  T t = a * 2.5;
+  return t;
+}
+
+template<typename T>
+int none(int a)
+  [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+template<typename T>
+int arg0(T t)
+  [[ pre: t > 0 ]]
+{
+  return -t - 10;
+}
+
+template<typename T>
+int arg1(int a, T t)
+  [[ pre: a > 0 ]]
+  [[ pre: t > 0 ]]
+{
+  return -t * a;
+}
+
+template<typename T>
+T ret(int a)
+  [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+int main(int, char**)
+{
+  printf("%d\n", body<int>(-1));
+  printf("%d\n", body<double>(-2));
+  printf("%d\n", none<int>(-1));
+  printf("%d\n", none<double>(-2));
+  printf("%d\n", arg0(-1));
+  printf("%d\n", arg0(-2.9));
+  printf("%d\n", arg1(-3, -1));
+  printf("%d\n", arg1(-4, -2.9));
+  printf("%d\n", (int)ret<int>(-3));
+  printf("%d\n", (int)ret<double>(-4.9));
+
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 8 body<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-2(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 8 body<double> .*(\n|\r\n|\r)*" }
+// { dg-output "-5(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 16 none<int> .*(\n|\r\n|\r)*" }
+// { dg-output "1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 16 none<double> .*(\n|\r\n|\r)*" }
+// { dg-output "2(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 23 arg0<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-9(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 23 arg0<double> .*(\n|\r\n|\r)*" }
+// { dg-output "-7(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 30 arg1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 31 arg1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 30 arg1<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 31 arg1<double> .*(\n|\r\n|\r)*" }
+// { dg-output "-11(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 38 ret<int> .*(\n|\r\n|\r)*" }
+// { dg-output "3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 38 ret<double> .*(\n|\r\n|\r)*" }
+// { dg-output "4(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre6.C b/gcc/testsuite/g++.dg/contracts/contracts-pre6.C
new file mode 100644
index 00000000000..44e8e264b54
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre6.C
@@ -0,0 +1,74 @@
+// ensure no errors are thrown when we have to insert a decl for the internal
+// unchecked function after leaving a (possibly nested) namespace
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+namespace ns0
+{
+  int f(int a) [[ pre: a > 0 ]];
+}
+
+int ns0::f(int a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+namespace ns0
+{
+  namespace ns1
+  {
+    int f(int a) [[ pre: a > 0 ]];
+  }
+}
+
+int ns0::ns1::f(int a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+namespace ns0
+{
+  namespace ns1
+  {
+    int f2(int a) [[ pre: a > 0 ]];
+    namespace ns2
+    {
+      int f(int a) [[ pre: a > 0 ]];
+    }
+  }
+  int ns1::f2(int a) [[ pre: a > 0 ]]
+  {
+    return -a;
+  }
+}
+
+int ns0::ns1::ns2::f(int a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+namespace ns0
+{
+  struct S
+  {
+    int f(int a) [[ pre: a > 0 ]];
+  };
+  namespace ns1
+  {
+    struct S2
+    {
+      int f(int a) [[ pre: a > 0 ]];
+    };
+  }
+}
+
+int ns0::S::f(int a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+int ns0::ns1::S2::f(int a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre7.C b/gcc/testsuite/g++.dg/contracts/contracts-pre7.C
new file mode 100644
index 00000000000..aeb8fc042e6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre7.C
@@ -0,0 +1,134 @@
+// ensure no errors are thrown when we have to insert a decl for the internal
+// unchecked function after leaving a (possibly nested) namespace
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+namespace ns0
+{
+  template<typename T>
+  int f(T a) [[ pre: a > 0 ]];
+}
+
+template<typename T>
+int ns0::f(T a) [[ pre: a > 0 ]]
+{
+  return (int)-a;
+}
+
+namespace ns0
+{
+  namespace ns1
+  {
+    template<typename T>
+    int f(T a) [[ pre: a > 0 ]];
+  }
+}
+
+template<typename T>
+int ns0::ns1::f(T a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+namespace ns0
+{
+  namespace ns1
+  {
+    template<typename T>
+    int f2(T a) [[ pre: a > 0 ]];
+    namespace ns2
+    {
+      template<typename T>
+      int f(T a) [[ pre: a > 0 ]];
+    }
+  }
+  template<typename T>
+  int ns1::f2(T a) [[ pre: a > 0 ]]
+  {
+    return -a;
+  }
+}
+
+template<typename T>
+int ns0::ns1::ns2::f(T a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+namespace ns0
+{
+  template<typename T>
+  struct S
+  {
+    int f(T a) [[ pre: a > 0 ]];
+  };
+  namespace ns1
+  {
+    template<typename T>
+    struct S2
+    {
+      int f(T a) [[ pre: a > 0 ]];
+    };
+  }
+}
+
+template<typename T>
+int ns0::S<T>::f(T a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+template<typename T>
+int ns0::ns1::S2<T>::f(T a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+#include <cstdio>
+int main(int, char**)
+{
+  printf ("%d\n", ns0::f(-1));
+  printf ("%d\n", ns0::ns1::f(-2));
+  printf ("%d\n", ns0::ns1::f2(-3));
+  printf ("%d\n", ns0::ns1::ns2::f(-4));
+  ns0::S<int> ns0_s;
+  printf ("%d\n", ns0_s.f(-5));
+  ns0::ns1::S2<int> ns0_ns1_s2;
+  printf ("%d\n", ns0_ns1_s2.f(-6));
+
+  printf ("%d\n", ns0::f(-7.5));
+  printf ("%d\n", ns0::ns1::f(-8.5));
+  printf ("%d\n", ns0::ns1::f2(-9.5));
+  printf ("%d\n", ns0::ns1::ns2::f(-10.5));
+  ns0::S<double> ns0_sd;
+  printf ("%d\n", ns0_sd.f(-11.5));
+  ns0::ns1::S2<double> ns0_ns1_s2d;
+  printf ("%d\n", ns0_ns1_s2d.f(-12.5));
+
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 13 ns0::f<int> .*(\n|\r\n|\r)*" }
+// { dg-output "1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 28 ns0::ns1::f<int> .*(\n|\r\n|\r)*" }
+// { dg-output "2(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 46 ns0::ns1::f2<int> .*(\n|\r\n|\r)*" }
+// { dg-output "3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 53 ns0::ns1::ns2::f<int> .*(\n|\r\n|\r)*" }
+// { dg-output "4(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 76 ns0::S<int>::f .*(\n|\r\n|\r)*" }
+// { dg-output "5(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 82 ns0::ns1::S2<int>::f .*(\n|\r\n|\r)*" }
+// { dg-output "6(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 13 ns0::f<double> .*(\n|\r\n|\r)*" }
+// { dg-output "7(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 28 ns0::ns1::f<double> .*(\n|\r\n|\r)*" }
+// { dg-output "8(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 46 ns0::ns1::f2<double> .*(\n|\r\n|\r)*" }
+// { dg-output "9(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 53 ns0::ns1::ns2::f<double> .*(\n|\r\n|\r)*" }
+// { dg-output "10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 76 ns0::S<double>::f .*(\n|\r\n|\r)*" }
+// { dg-output "11(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 82 ns0::ns1::S2<double>::f .*(\n|\r\n|\r)*" }
+// { dg-output "12(\n|\r\n|\r)*" }
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-pre9.C b/gcc/testsuite/g++.dg/contracts/contracts-pre9.C
new file mode 100644
index 00000000000..64c0cfa36df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-pre9.C
@@ -0,0 +1,146 @@
+// ensure no errors are thrown for various combinations of class templates
+// with guarded members
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+template<typename T>
+struct S
+{
+  int f(int a) [[ pre: a > 0 ]];
+  int g(int a) [[ pre: a > 0 ]];
+};
+
+template<typename T>
+int S<T>::f(int a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+template<typename T>
+int S<T>::g(int a) // Contract is inherited (error from line 10).
+{
+  return -a;
+}
+
+template<typename T>
+struct S_arg
+{
+  int f(T a) [[ pre: a > 0 ]];
+  int g(T a) [[ pre: a > 0 ]];
+};
+
+template<typename T>
+int S_arg<T>::f(T a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+template<typename T>
+int S_arg<T>::g(T a) // Contract is inherited (error from line 29).
+{
+  return -a;
+}
+
+template<typename T>
+struct S_ret
+{
+  T f(int a) [[ pre: a > 0 ]];
+  T g(int a) [[ pre: a > 0 ]];
+};
+
+template<typename T>
+T S_ret<T>::f(int a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+template<typename T>
+T S_ret<T>::g(int a) // Contract is inherited (error from line 48).
+{
+  return -a;
+}
+
+#include <cstdio>
+int main(int, char**)
+{
+  {
+    S<int> s_int;
+    printf ("s_int.f(-10): %d\n", s_int.f(-10));
+    printf ("s_int.g(-10): %d\n", s_int.g(-10));
+    printf ("s_int.f(10): %d\n", s_int.f(10));
+    printf ("s_int.g(10): %d\n", s_int.g(10));
+
+    S<double> s_double;
+    printf ("s_double.f(-10.5): %d\n", s_double.f(-10.5));
+    printf ("s_double.g(-10.5): %d\n", s_double.g(-10.5));
+    printf ("s_double.f(10.5): %d\n", s_double.f(10.5));
+    printf ("s_double.g(10.5): %d\n", s_double.g(10.5));
+  }
+
+  {
+    S_arg<int> s_arg_int;
+    printf ("s_arg_int.f(-10): %d\n", s_arg_int.f(-10));
+    printf ("s_arg_int.g(-10): %d\n", s_arg_int.g(-10));
+    printf ("s_arg_int.f(10): %d\n", s_arg_int.f(10));
+    printf ("s_arg_int.g(10): %d\n", s_arg_int.g(10));
+
+    S_arg<double> s_arg_double;
+    printf ("s_arg_double.f(-10): %d\n", s_arg_double.f(-10));
+    printf ("s_arg_double.g(-10): %d\n", s_arg_double.g(-10));
+    printf ("s_arg_double.f(10): %d\n", s_arg_double.f(10));
+    printf ("s_arg_double.g(10): %d\n", s_arg_double.g(10));
+  }
+
+  {
+    S_ret<int> s_ret_int;
+    printf ("s_ret_int.f(-10): %d\n", s_ret_int.f(-10));
+    printf ("s_ret_int.g(-10): %d\n", s_ret_int.g(-10));
+    printf ("s_ret_int.f(10): %d\n", s_ret_int.f(10));
+    printf ("s_ret_int.g(10): %d\n", s_ret_int.g(10));
+
+    S_ret<double> s_ret_double;
+    printf ("s_ret_double.f(-10): %f\n", s_ret_double.f(-10));
+    printf ("s_ret_double.g(-10): %f\n", s_ret_double.g(-10));
+    printf ("s_ret_double.f(10): %f\n", s_ret_double.f(10));
+    printf ("s_ret_double.g(10): %f\n", s_ret_double.g(10));
+  }
+
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 14 S<int>::f .*(\n|\r\n|\r)*" }
+// { dg-output "s_int.f.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 S<int>::g .*(\n|\r\n|\r)*" }
+// { dg-output "s_int.g.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "s_int.f.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "s_int.g.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 14 S<double>::f .*(\n|\r\n|\r)*" }
+// { dg-output "s_double.f.-10.5.: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 S<double>::g .*(\n|\r\n|\r)*" }
+// { dg-output "s_double.g.-10.5.: 10(\n|\r\n|\r)*" }
+// { dg-output "s_double.f.10.5.: -10(\n|\r\n|\r)*" }
+// { dg-output "s_double.g.10.5.: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 33 S_arg<int>::f .*(\n|\r\n|\r)*" }
+// { dg-output "s_arg_int.f.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 29 S_arg<int>::g .*(\n|\r\n|\r)*" }
+// { dg-output "s_arg_int.g.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "s_arg_int.f.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "s_arg_int.g.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 33 S_arg<double>::f .*(\n|\r\n|\r)*" }
+// { dg-output "s_arg_double.f.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 29 S_arg<double>::g .*(\n|\r\n|\r)*" }
+// { dg-output "s_arg_double.g.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "s_arg_double.f.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "s_arg_double.g.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 52 S_ret<int>::f .*(\n|\r\n|\r)*" }
+// { dg-output "s_ret_int.f.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 48 S_ret<int>::g .*(\n|\r\n|\r)*" }
+// { dg-output "s_ret_int.g.-10.: 10(\n|\r\n|\r)*" }
+// { dg-output "s_ret_int.f.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "s_ret_int.g.10.: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 52 S_ret<double>::f .*(\n|\r\n|\r)*" }
+// { dg-output "s_ret_double.f.-10.: 10.000000(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 48 S_ret<double>::g .*(\n|\r\n|\r)*" }
+// { dg-output "s_ret_double.g.-10.: 10.000000(\n|\r\n|\r)*" }
+// { dg-output "s_ret_double.f.10.: -10.000000(\n|\r\n|\r)*" }
+// { dg-output "s_ret_double.g.10.: -10.000000(\n|\r\n|\r)*" }
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-redecl1.C b/gcc/testsuite/g++.dg/contracts/contracts-redecl1.C
new file mode 100644
index 00000000000..58d0aafeff5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-redecl1.C
@@ -0,0 +1,149 @@
+// generic error tests for contract redecls with generalized redecl
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+// OK if equivalent -- even through renames.
+int g0(int a) [[ pre: a > 0 ]];
+int g0(int a) [[ pre: a > 0 ]];
+
+int g0b(int a) [[ pre: a > 0 ]];
+int g0b(int b) [[ pre: b > 0 ]];
+int g0b(int c) [[ pre: c > 0 ]]
+{
+  return 0;
+}
+
+// OK if specified before.
+int g1(int a) [[ pre: a > 0 ]];
+int g1(int a);
+
+// OK if specified after.
+int g2(int a);
+int g2(int a) [[ pre: a > 0 ]];
+
+int g2b(int a);
+int g2b(int b) [[ pre: b > 0 ]];
+
+// can add to non-virtual methods
+struct G0
+{
+  int f(int a);
+};
+
+// OK to add contracts at the point of definition.
+int G0::f(int a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+struct G1
+{
+  int f1(int a);
+};
+
+// OK to redeclare functions and add constraints...
+int G1::f1(int a) [[ pre: a > 0 ]];
+
+// ...and leave them off later.
+int G1::f1(int a)
+{
+  return -a;
+}
+
+int f0(int a) [[ pre: a > 0 ]];
+int f0(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]]; // { dg-error "different number of contracts" }
+
+int f1(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]];
+int f1(int a) [[ pre: a > 0 ]]; // { dg-error "different number of contracts" }
+
+int f2(int a) [[ pre: a > 0 ]];
+int f2(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
+
+int f3(int a) { return a; }
+int f3(int a) [[ pre: a < 0 ]]; // { dg-error "cannot add contracts" }
+
+struct Base
+{
+  virtual int f(int a) [[ pre: a > 0 ]];
+};
+
+struct Child : Base
+{
+  int f(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
+};
+
+struct S1
+{
+  virtual int f(int a); // contracts are inherited at the point of declarations
+};
+
+int S1::f(int a) [[ pre: a > 0 ]] // { dg-error "cannot add" }
+{
+  return -a;
+}
+
+struct S2
+{
+  int f() { return 0; }
+};
+
+int S2::f(); // OK?
+
+
+struct S3
+{
+  int f() { return 0; }
+};
+
+int S3::f() [[pre: true]]; // { dg-error "cannot add contracts" }
+
+
+// The initial decl of a guarded member must appear inside the class.
+struct S4
+{
+  int f(int a);
+};
+
+int S4::g(int a) [[ pre: a > 0 ]]; // { dg-error "no declaration matches" }
+
+
+struct S5
+{
+  template<typename T>
+  S5(T a);
+};
+
+template<typename T>
+S5::S5(T a) [[ pre: a > 0 ]]
+{
+}
+
+struct S6
+{
+  template<typename T>
+  S6(T a);
+};
+
+template<typename T>
+S6::S6(T a) [[ pre: a > 0 ]];
+
+template<typename T>
+S6::S6(T a)
+{
+}
+
+int p0(int n)
+  [[ post r: r > 0 && r == n ]]
+  [[ post r: r > 1 && r == n ]]
+  [[ post r: r > 2 && r == n ]]
+  [[ post r: r > 3 && r == n ]];
+
+int p0(int z)
+  [[ post r: r > 0 && r == z ]]
+  [[ post r1: r1 > 1 && r1 == z ]]
+  [[ post r2: r2 > 2 && r2 == z ]]
+  [[ post r3: r3 > 3 && r3 == z ]]
+{
+  return z;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-redecl2.C b/gcc/testsuite/g++.dg/contracts/contracts-redecl2.C
new file mode 100644
index 00000000000..70c9259049f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-redecl2.C
@@ -0,0 +1,149 @@
+// generic error tests for generalized contract redecls
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+// allowed to repeat contracts or omit them
+int g0(int a) [[ pre: a > 0 ]];
+int g0(int a) [[ pre: a > 0 ]];
+
+int g1(int a) [[ pre: a > 0 ]];
+int g1(int a);
+
+// allowed to add from none if generalized redecl is on (by default)
+int g2(int a);
+int g2(int a) [[ pre: a > 0 ]];
+
+// can add to non-virtual methods
+struct G0
+{
+  int f(int a);
+};
+
+int G0::f(int a) [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+struct G1
+{
+  int f(int a);
+};
+
+int G1::f(int a) [[ pre: a > 0 ]];
+
+int G1::f(int a)
+{
+  return -a;
+}
+
+// allowed to redeclare even without contracts
+struct G2
+{
+  int f(int a);
+};
+
+int G2::f(int a);
+
+
+int f0(int a) [[ pre: a > 0 ]];
+int f0(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]]; // { dg-error "different number of contracts" }
+
+int f1(int a) [[ pre: a > 0 ]];
+int f1(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
+
+int f2(int a) { return a; }
+int f2(int a) [[ pre: a < 0 ]]; // { dg-error "cannot add contracts after definition" }
+
+struct Base
+{
+  virtual int f(int a) [[ pre: a > 0 ]];
+};
+
+struct Child : Base
+{
+  int f(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
+};
+
+// the initial decl of a guarded member must appear inside the class
+struct F2
+{
+  int f(int a);
+};
+
+int F2::g(int a) [[ pre: a > 0 ]]; // { dg-error "no declaration matches" }
+// FIXME if we move F2 down then a different error makes F2 undeclared
+
+struct F0
+{
+  virtual int f(int a);
+};
+
+int F0::f(int a); // { dg-error "declaration.*is not definition" }
+
+struct F1
+{
+  virtual int f(int a);
+};
+
+int F1::f(int a) [[ pre: a > 0 ]] // { dg-error "cannot add" }
+{
+  return -a;
+}
+
+// cannot "re"declare members of a forward declared class
+struct F2;
+int F2::test(); // { dg-error "no declaration matches" }
+int F2::test2() [[ pre: true ]]; // { dg-error "no declaration matches" }
+
+// can only redeclare member functions
+struct F3
+{
+  int x;
+  typedef int my_int;
+
+  struct Inner0;
+  struct Inner1;
+  enum my_enum0; // { dg-error "use of enum.*without previous decl" }
+  enum my_enum1 { E1, E2 };
+
+  int test0();
+  int test1();
+  int test2();
+};
+
+int F3::x{-1}; // { dg-error "is not a static data member" }
+typedef double F3::my_int; // { dg-error "typedef name may not be a nested-name-specifier" }
+struct F3::Inner0; // { dg-warning "declaration.*does not declare anything" }
+
+struct F3::Inner1 { };
+
+enum F3::my_enum1 { E0, E1, END }; // { dg-error "multiple definition" }
+
+struct F4
+{
+  int test0();
+
+  int F3::test0() [[ pre: true ]]; // { dg-error "cannot declare member function" }
+  friend int F3::test1();
+  friend int F3::test2();
+};
+int F3::test2() [[ pre: true ]] { return -1; }
+
+void dummy0()
+{
+  int F4::test0() [[ pre: true ]]; // { dg-error "qualified-id in declaration" }
+}
+
+namespace ns0
+{
+  typedef int value;
+  struct X
+  {
+    int test1(value);
+    typedef double value;
+    int test2(value);
+  };
+  int X::test1(value); // { dg-error "no declaration matches" }
+  int X::test2(value);
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-redecl3.C b/gcc/testsuite/g++.dg/contracts/contracts-redecl3.C
new file mode 100644
index 00000000000..fdfca3a65ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-redecl3.C
@@ -0,0 +1,195 @@
+// basic test to ensure contracts generalized redecl works
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+
+namespace defining
+{
+  int x = 10;
+  double y = 10.5;
+
+  struct S
+  {
+    bool z;
+  };
+
+  struct T1
+  {
+    void vfun(int m, double n);
+    int fun(int m, double n);
+    double fun2(int m, double n);
+    S funend(int m, double n);
+  };
+
+  void T1::vfun(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]]
+  {
+    printf("vfun::x: %d\n", x);
+  }
+
+  int T1::fun(int m, double n)
+    [[ pre: x < 0 ]]
+  {
+    printf("fun::x: %d\n", x);
+    return x;
+  }
+
+  double T1::fun2(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: y < 0 ]]
+    [[ pre: m < 0 ]]
+    [[ pre: n < 0 ]]
+  {
+    printf("fun2::x: %d fun2::y: %f\n", x, y);
+    return y;
+  }
+}
+
+namespace nondefining
+{
+  int x = 10;
+  double y = 10.5;
+
+  struct S
+  {
+    bool z;
+  };
+
+  struct T1
+  {
+    void vfun(int m, double n);
+    int fun(int m, double n);
+    double fun2(int m, double n);
+    S funend(int m, double n);
+  };
+
+  void T1::vfun(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]];
+  int T1::fun(int m, double n)
+    [[ pre: x < 0 ]];
+  double T1::fun2(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: y < 0 ]]
+    [[ pre: m < 0 ]]
+    [[ pre: n < 0 ]];
+
+  void T1::vfun(int m, double n)
+  {
+    printf("vfun::x: %d\n", x);
+  }
+
+  int T1::fun(int m, double n)
+  {
+    printf("fun::x: %d\n", x);
+    return x;
+  }
+
+  double T1::fun2(int m, double n)
+  {
+    printf("fun2::x: %d fun2::y: %f\n", x, y);
+    return y;
+  }
+}
+
+int main(int, char**) {
+  // defining redecl
+  {
+    int x = 11;
+    double y = 11.5;
+
+    defining::T1 t1;
+    t1.vfun(x, y);
+
+    int f = 13;
+    f = t1.fun(x, y);
+    printf("main::f: %d\n", f);
+    double d = 13.37;
+    d = t1.fun2(x, y);
+    printf("main::d: %f\n", d);
+    defining::S s = t1.funend(x, y);
+    printf("main::s.z: %d\n", s.z ? 1 : 0);
+  }
+
+  // nondefining redecl
+  {
+    int x = 12;
+    double y = 12.5;
+
+    nondefining::T1 t1;
+    t1.vfun(x, y);
+
+    int f = 13;
+    f = t1.fun(x, y);
+    printf("main::f: %d\n", f);
+    double d = 13.37;
+    d = t1.fun2(x, y);
+    printf("main::d: %f\n", d);
+    nondefining::S s = t1.funend(x, y);
+    printf("main::s.z: %d\n", s.z ? 1 : 0);
+  }
+  return 0;
+}
+
+namespace defining
+{
+  S T1::funend(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]]
+  {
+    printf("funend::x: %d\n", x);
+    S s;
+    s.z = true;
+    return s;
+  }
+}
+
+namespace nondefining
+{
+  S T1::funend(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]];
+
+  S T1::funend(int m, double n)
+  {
+    printf("funend::x: %d\n", x);
+    S s;
+    s.z = true;
+    return s;
+  }
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 25 defining::T1::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 26 defining::T1::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 32 defining::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::f: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 39 defining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 40 defining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 41 defining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 42 defining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 138 defining::T1::funend .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 139 defining::T1::funend .*(\n|\r\n|\r)*" }
+// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 69 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 71 nondefining::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::f: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 73 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 74 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 75 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 76 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 151 nondefining::T1::funend .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 152 nondefining::T1::funend .*(\n|\r\n|\r)*" }
+// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-redecl4.C b/gcc/testsuite/g++.dg/contracts/contracts-redecl4.C
new file mode 100644
index 00000000000..c1e234226b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-redecl4.C
@@ -0,0 +1,56 @@
+// test that free functions can be redeclared with contracts without affecting
+// normal default parm handling
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+
+int f(int a, int, int c = 10);
+int f(int a, int b = 11, int);
+int f(int a, int b, int c)
+  [[ pre: a < 0 ]]
+  [[ pre: b < 0 ]]
+  [[ pre: c < 0 ]];
+
+int f(int, int, int);
+
+int f(int a, int b, int c)
+{
+  printf("f: a: %d, b: %d, c: %d\n", a, b, c);
+  return a * b - c;
+}
+
+int f(int a = 12, int, int);
+
+int main(int, char **)
+{
+  f(1,1,1);
+  printf("=====\n");
+  f(1,1);
+  printf("=====\n");
+  f(1);
+  printf("=====\n");
+  f();
+  printf("=====\n");
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
+// { dg-output "f: a: 1, b: 1, c: 1(\n|\r\n|\r)*" }
+// { dg-output "=====(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
+// { dg-output "f: a: 1, b: 1, c: 10(\n|\r\n|\r)*" }
+// { dg-output "=====(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
+// { dg-output "f: a: 1, b: 11, c: 10(\n|\r\n|\r)*" }
+// { dg-output "=====(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
+// { dg-output "f: a: 12, b: 11, c: 10(\n|\r\n|\r)*" }
+// { dg-output "=====(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-redecl5.C b/gcc/testsuite/g++.dg/contracts/contracts-redecl5.C
new file mode 100644
index 00000000000..ea4835f1899
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-redecl5.C
@@ -0,0 +1,101 @@
+// generic error tests for generalized contract redecls
+//   we also test for the warning diagnostic for strict redecl
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts -fcontract-strict-declarations=on" }
+
+// allowed to repeat contracts or omit them
+int g0(int a) [[ pre: a > 0 ]];
+int g0(int a) [[ pre: a > 0 ]];
+
+int g1(int a) [[ pre: a > 0 ]];
+int g1(int a);
+
+// allowed to add from none if generalized redecl is on (by default)
+int g2(int a);
+int g2(int a) [[ pre: a > 0 ]]; // { dg-warning "adds contracts" }
+int g2(int a) [[ pre: a > 0 ]]; // { dg-bogus "adds contracts" }
+
+// can add to non-virtual methods
+struct G0
+{
+  int f(int a);
+};
+
+int G0::f(int a) [[ pre: a > 0 ]] // { dg-warning "adds contracts" }
+{
+  return -a;
+}
+
+struct G1
+{
+  int f(int a);
+};
+
+int G1::f(int a) [[ pre: a > 0 ]]; // { dg-warning "adds contracts" }
+// { dg-warning "outside of class is not definition" "" { target *-*-* } .-1 }
+
+int G1::f(int a);
+// { dg-warning "outside of class is not definition" "" { target *-*-* } .-1 }
+
+int G1::f(int a) [[ pre: a > 0 ]];
+// { dg-warning "outside of class is not definition" "" { target *-*-* } .-1 }
+
+int G1::f(int a)
+{
+  return -a;
+}
+
+// allowed to redeclare even without contracts
+struct G2
+{
+  int f(int a);
+};
+
+int G2::f(int a); // { dg-warning "outside of class is not definition" }
+
+
+int f0(int a) [[ pre: a > 0 ]];
+int f0(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]]; // { dg-error "different number of contracts" }
+
+int f1(int a) [[ pre: a > 0 ]];
+int f1(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
+
+int f2(int a) { return a; }
+int f2(int a) [[ pre: a < 0 ]]; // { dg-error "cannot add contracts after definition" }
+
+struct Base
+{
+  virtual int f(int a) [[ pre: a > 0 ]];
+};
+
+struct Child : Base
+{
+  int f(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
+};
+
+// the initial decl of a guarded member must appear inside the class
+struct F2
+{
+  int f(int a);
+};
+
+int F2::g(int a) [[ pre: a > 0 ]]; // { dg-error "no declaration matches" }
+// FIXME if we move F2 down then a different error makes F2 undeclared
+
+struct F0
+{
+  virtual int f(int a);
+};
+
+int F0::f(int a); // { dg-error "declaration.*is not definition" }
+
+struct F1
+{
+  virtual int f(int a);
+};
+
+int F1::f(int a) [[ pre: a > 0 ]] // { dg-error "cannot add" }
+{
+  return -a;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-redecl6.C b/gcc/testsuite/g++.dg/contracts/contracts-redecl6.C
new file mode 100644
index 00000000000..e79a5aa38ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-redecl6.C
@@ -0,0 +1,195 @@
+// basic test to ensure contracts generalized redecl works
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+
+namespace defining
+{
+  int x = 10;
+  double y = 10.5;
+
+  struct S
+  {
+    bool z;
+  };
+
+  struct T1
+  {
+    void vfun(int m, double n) const;
+    int fun(int m, double n) volatile;
+    double fun2(int m, double n) const volatile;
+    static S funend(int m, double n);
+  };
+
+  void T1::vfun(int m, double n) const
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]]
+  {
+    printf("vfun::x: %d\n", x);
+  }
+
+  int T1::fun(int m, double n) volatile
+    [[ pre: x < 0 ]]
+  {
+    printf("fun::x: %d\n", x);
+    return x;
+  }
+
+  double T1::fun2(int m, double n) const volatile
+    [[ pre: x < 0 ]]
+    [[ pre: y < 0 ]]
+    [[ pre: m < 0 ]]
+    [[ pre: n < 0 ]]
+  {
+    printf("fun2::x: %d fun2::y: %f\n", x, y);
+    return y;
+  }
+}
+
+namespace nondefining
+{
+  int x = 10;
+  double y = 10.5;
+
+  struct S
+  {
+    bool z;
+  };
+
+  struct T1
+  {
+    void vfun(int m, double n) const;
+    int fun(int m, double n) volatile;
+    double fun2(int m, double n) const volatile;
+    static S funend(int m, double n);
+  };
+
+  void T1::vfun(int m, double n) const
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]];
+  int T1::fun(int m, double n) volatile
+    [[ pre: x < 0 ]];
+  double T1::fun2(int m, double n) const volatile
+    [[ pre: x < 0 ]]
+    [[ pre: y < 0 ]]
+    [[ pre: m < 0 ]]
+    [[ pre: n < 0 ]];
+
+  void T1::vfun(int m, double n) const
+  {
+    printf("vfun::x: %d\n", x);
+  }
+
+  int T1::fun(int m, double n) volatile
+  {
+    printf("fun::x: %d\n", x);
+    return x;
+  }
+
+  double T1::fun2(int m, double n) const volatile
+  {
+    printf("fun2::x: %d fun2::y: %f\n", x, y);
+    return y;
+  }
+}
+
+int main(int, char**) {
+  // defining redecl
+  {
+    int x = 11;
+    double y = 11.5;
+
+    defining::T1 t1;
+    t1.vfun(x, y);
+
+    int f = 13;
+    f = t1.fun(x, y);
+    printf("main::f: %d\n", f);
+    double d = 13.37;
+    d = t1.fun2(x, y);
+    printf("main::d: %f\n", d);
+    defining::S s = defining::T1::funend(x, y);
+    printf("main::s.z: %d\n", s.z ? 1 : 0);
+  }
+
+  // nondefining redecl
+  {
+    int x = 12;
+    double y = 12.5;
+
+    nondefining::T1 t1;
+    t1.vfun(x, y);
+
+    int f = 13;
+    f = t1.fun(x, y);
+    printf("main::f: %d\n", f);
+    double d = 13.37;
+    d = t1.fun2(x, y);
+    printf("main::d: %f\n", d);
+    nondefining::S s = nondefining::T1::funend(x, y);
+    printf("main::s.z: %d\n", s.z ? 1 : 0);
+  }
+  return 0;
+}
+
+namespace defining
+{
+  S T1::funend(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]]
+  {
+    printf("funend::x: %d\n", x);
+    S s;
+    s.z = true;
+    return s;
+  }
+}
+
+namespace nondefining
+{
+  S T1::funend(int m, double n)
+    [[ pre: x < 0 ]]
+    [[ pre: m < 0 ]];
+
+  S T1::funend(int m, double n)
+  {
+    printf("funend::x: %d\n", x);
+    S s;
+    s.z = true;
+    return s;
+  }
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 25 defining::T1::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 26 defining::T1::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 32 defining::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::f: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 39 defining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 40 defining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 41 defining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 42 defining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 138 defining::T1::funend .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 139 defining::T1::funend .*(\n|\r\n|\r)*" }
+// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 69 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
+// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 71 nondefining::T1::fun .*(\n|\r\n|\r)*" }
+// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::f: 10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 73 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 74 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 75 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 76 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 151 nondefining::T1::funend .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 152 nondefining::T1::funend .*(\n|\r\n|\r)*" }
+// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
+// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-redecl7.C b/gcc/testsuite/g++.dg/contracts/contracts-redecl7.C
new file mode 100644
index 00000000000..e3a57eea632
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-redecl7.C
@@ -0,0 +1,95 @@
+// test that contracts can be added during (defining) friend declarations
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+struct T;
+
+struct S
+{
+  int now(int a, T *t) [[ pre: a > 0 ]] [[ pre: x < 0 ]];
+
+
+  int x{-1};
+};
+
+int now(int a, T *t) [[ pre: a > 0 ]];
+int later(int a, T *t);
+int both(int a, T *t) [[ pre: a > 0 ]];
+
+struct T
+{
+  friend int now(int a, T *t);
+  friend int later(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]]
+  {
+    printf("later: a: %d, t->pri: %d\n", a, t->pri);
+    return -a * t->pri;
+  }
+  friend int both(int a, T *t) [[ pre: a > 0 ]]
+  {
+    printf("both: a: %d, t->pri: %d\n", a, t->pri);
+    return -a * t->pri;
+  }
+
+
+  friend int S::now(int a, T *t);
+
+  friend int hidden(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]]
+  {
+    printf("hidden: a: %d, t->pri: %d\n", a, t->pri);
+    return -a * t->pri;
+  }
+  friend int hidden2(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]]
+  {
+    printf("hidden2: a: %d, t->pri: %d\n", a, t->pri);
+    return -a * t->pri;
+  }
+
+  int x{1};
+  private:
+    int pri{-10};
+};
+
+int hidden2(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]];
+
+int S::now(int a, T *t)
+{
+  printf("S::now: a: %d, t->pri: %d\n", a, t->pri);
+  return -a * t->pri;
+}
+
+int now(int a, T *t)
+{
+  printf("now: a: %d, t->pri: %d\n", a, t->pri);
+  return -a * t->pri;
+}
+
+int main(int, char**)
+{
+  T t;
+  S s;
+  s.now(-10, &t);
+
+  now(-20, &t);
+  later(-21, &t);
+  both(-22, &t);
+  hidden(-23, &t);
+  hidden2(-24, &t);
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 9 S::now .*(\n|\r\n|\r)*" }
+// { dg-output "S::now: a: -10, t->pri: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 15 now .*(\n|\r\n|\r)*" }
+// { dg-output "now: a: -20, t->pri: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 22 later .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 22 later .*(\n|\r\n|\r)*" }
+// { dg-output "later: a: -21, t->pri: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 27 both .*(\n|\r\n|\r)*" }
+// { dg-output "both: a: -22, t->pri: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 36 hidden .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 36 hidden .*(\n|\r\n|\r)*" }
+// { dg-output "hidden: a: -23, t->pri: -10(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 41 hidden2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 41 hidden2 .*(\n|\r\n|\r)*" }
+// { dg-output "hidden2: a: -24, t->pri: -10(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-redecl8.C b/gcc/testsuite/g++.dg/contracts/contracts-redecl8.C
new file mode 100644
index 00000000000..933adce79f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-redecl8.C
@@ -0,0 +1,64 @@
+// test that contracts are matched on friend decls when the type is complete
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+struct T;
+
+int both(int x, T *t) [[ pre: x > 0 ]] { return 0; }
+int both2(int x, T *t) [[ pre: x > 0 ]];
+
+template<typename Z>
+int fn(int x, Z *z) [[ pre: x > 0 ]];
+
+template<typename Z>
+int fn2(int x, Z *z);
+
+template<typename Z>
+int fn3(int x, Z *z) [[ pre: x > 0 ]];
+
+template<>
+int fn3<T>(int x, T *z) [[ pre: x > 1 ]];
+
+struct T
+{
+  friend int both2(int x, T *t) [[ pre: x > 1 ]] // { dg-error "mismatched" }
+  {
+    return 0;
+  }
+
+  friend int hidden(int x, T *t)
+    [[ pre: x > 1 ]] [[ pre: t->pri > 0 ]]
+  {
+    return x;
+  }
+
+  /* cannot define friend spec, so we never get to matching contracts
+  friend int fn<T>(int x, T *t)
+    [[ pre: t->pri > 0 ]] { return 0; } // error defining explicit spec friend
+    */
+
+  // bad, general contracts must match general
+  template<typename Z>
+  friend int fn(int x, Z *z)
+    [[ pre: z->pri > 1 ]] { return 0; } // { dg-error "mismatched" }
+
+  // fine, can add contracts
+  template<typename Z>
+  friend int fn2(int x, Z *z)
+    [[ pre: z->pri > 1 ]] { return 0; } // { dg-bogus "mismatched" }
+
+  /* cannot declare without definition, so dup friend can't occur:
+  friend int dup(int x, T *t)
+    [[ pre: t->pri > 0 ]]; // error non-defining friend with contracts
+  friend int dup(int x, T *t)
+    [[ pre: t->pri > 1 ]]; // error non-defining friend with contracts
+    */
+
+  int x{1};
+  private:
+    int pri{-10};
+};
+
+int hidden(int x, T *t)
+  [[ pre: x > 0 ]] [[ pre: t->pri > 1 ]]; // { dg-error "mismatched" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-tmpl-attr1.C b/gcc/testsuite/g++.dg/contracts/contracts-tmpl-attr1.C
new file mode 100644
index 00000000000..c9e7b9cec8b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-tmpl-attr1.C
@@ -0,0 +1,19 @@
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+template<typename T>
+[[z]]
+[[nodiscard]]
+T fun(T n)
+  [[ pre: n > 0 ]]
+  [[ post r: r > 0 ]] // { dg-warning ".z. attribute.*ignored" }
+{
+  return n;
+}
+
+int main(int, char**) {
+  fun(-5); // { dg-warning "ignoring return value" }
+  fun(-5.3); // { dg-warning "ignoring return value" }
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec1.C b/gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec1.C
new file mode 100644
index 00000000000..57ba7653543
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec1.C
@@ -0,0 +1,121 @@
+// basic test to ensure pre contracts work for free template specializations
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+
+template<typename T>
+int body(int a)
+  [[ pre: a > 0 ]]
+{
+  T t = a * 2.5;
+  return t;
+}
+
+template<>
+int body<double>(int a)
+  [[ pre: a > 0 ]]
+{
+  double t = a * 3.3;
+  return t;
+}
+
+template<typename T>
+int none(int a)
+  [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+template<>
+int none<double>(int a)
+  [[ pre: a > 0 ]]
+{
+  return a - 100;
+}
+
+template<typename T>
+int arg0(T t)
+  [[ pre: t > 0 ]]
+{
+  return -t - 10;
+}
+
+template<>
+int arg0<double>(double t)
+  [[ pre: t > 0 ]]
+{
+  return -t + 10;
+}
+
+template<typename T>
+int arg1(int a, T t)
+  [[ pre: a > 0 ]]
+  [[ pre: t > 0 ]]
+{
+  return -t * a;
+}
+
+template<>
+int arg1<double>(int a, double t)
+  [[ pre: a > 0 ]]
+  [[ pre: t > 0 ]]
+{
+  return -t * a + 17;
+}
+
+template<typename T>
+T ret(int a)
+  [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+template<>
+double ret<double>(int a)
+  [[ pre: a > 0 ]]
+{
+  return -a * 3.3;
+}
+
+
+int main(int, char**)
+{
+  printf("%d\n", body<int>(-1));
+  printf("%d\n", body<double>(-1));
+  printf("%d\n", none<int>(-1));
+  printf("%d\n", none<double>(-1));
+  printf("%d\n", arg0(-1));
+  printf("%d\n", arg0(-1.0));
+  printf("%d\n", arg1(-3, -1));
+  printf("%d\n", arg1(-3, -1.0));
+  printf("%d\n", (int)ret<int>(-1));
+  printf("%d\n", (int)ret<double>(-1));
+  printf("%f\n", ret<double>(-1));
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 8 body<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-2(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 16 body<double> .*(\n|\r\n|\r)*" }
+// { dg-output "-3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 24 none<int> .*(\n|\r\n|\r)*" }
+// { dg-output "1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 31 none<double> .*(\n|\r\n|\r)*" }
+// { dg-output "-101(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 38 arg0<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-9(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 45 arg0<double> .*(\n|\r\n|\r)*" }
+// { dg-output "11(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 52 arg1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 53 arg1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 60 arg1<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 61 arg1<double> .*(\n|\r\n|\r)*" }
+// { dg-output "14(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 68 ret<int> .*(\n|\r\n|\r)*" }
+// { dg-output "1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 75 ret<double> .*(\n|\r\n|\r)*" }
+// { dg-output "3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 75 ret<double> .*(\n|\r\n|\r)*" }
+// { dg-output "3.300000(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec2.C b/gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec2.C
new file mode 100644
index 00000000000..25982dfc826
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec2.C
@@ -0,0 +1,395 @@
+// basic test to ensure contracts work for class and member specializations
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+
+// template specializations can have differing contracts
+template<typename T>
+int body(int a)
+  [[ pre: a > 0 ]]
+{
+  T t = a * 2.5;
+  return t;
+}
+
+template<>
+int body<double>(int a)
+  [[ pre: a > 1 ]]
+{
+  double t = a * 3.3;
+  return t;
+}
+
+template<typename T>
+int none(int a)
+  [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+template<>
+int none<double>(int a)
+  [[ pre: a > 1 ]]
+{
+  return a - 100;
+}
+
+template<typename T>
+int arg0(T t)
+  [[ pre: t > 0 ]]
+{
+  return -t - 10;
+}
+
+template<>
+int arg0<double>(double t)
+  [[ pre: t > 1 ]]
+{
+  return -t + 10;
+}
+
+template<typename T>
+int arg1(int a, T t)
+  [[ pre: a > 0 ]]
+  [[ pre: t > 0 ]]
+{
+  return -t * a;
+}
+
+template<>
+int arg1<double>(int a, double t)
+  [[ pre: a > 1 ]]
+  [[ pre: t > 1 ]]
+{
+  return -t * a + 17;
+}
+
+template<typename T>
+T ret(int a)
+  [[ pre: a > 0 ]]
+{
+  return -a;
+}
+
+template<>
+double ret<double>(int a)
+  [[ pre: a > 1 ]]
+{
+  return -a * 3.3;
+}
+
+// template specializations can have no contracts
+template<typename T>
+int g1(T t) [[ pre: t > 0 ]]
+{
+  return (int)t;
+}
+
+template<>
+int g1<double>(double t)
+{
+  return (int)t;
+}
+
+// template specializations can have no contracts in the first decl but add
+// them later
+template<typename T>
+int g2(T t) [[ pre: t > 0 ]]
+{
+  return (int)t;
+}
+
+template<>
+int g2<double>(double t);
+
+template<>
+int g2<double>(double t)
+  [[ pre: t < 0 ]]
+{
+  return (int)t;
+}
+
+template<>
+int g2<char>(char t)
+  [[ pre: t < 'c' ]]
+{
+  return (int)t;
+}
+
+// contracts can be different on the general template, partial and full specs
+template<typename T, typename S>
+struct G3
+{
+  void f(T t, S s)
+    [[ pre: t > 0 ]]
+    [[ pre: s > 0 ]]
+  {
+    printf ("G3 general T S\n");
+  }
+};
+
+template<typename S>
+struct G3<int, S>
+{
+  void f(int t, S s);
+};
+
+template<typename S>
+void G3<int, S>::f(int t, S s)
+  [[ pre: t > 1 ]]
+  [[ pre: s > 1 ]]
+{
+  printf ("G3 partial int S\n");
+}
+
+template<>
+void G3<int, double>::f(int t, double s)
+  [[ pre: t > 2 ]]
+  [[ pre: s > 2 ]]
+{
+  printf ("G3 full int double\n");
+}
+
+struct C
+{
+  bool operator>(int rhs) { return false; }
+};
+
+// deletes contracts
+template<>
+void G3<int, C>::f(int t, C s);
+
+template<>
+void G3<int, C>::f(int t, C s)
+{
+  printf ("G3 full int C\n");
+};
+
+// specialized ctors
+template<typename T, typename S>
+struct G4
+{
+  G4(T t, S s)
+    [[ pre: t > 0 ]]
+    [[ pre: s > 0 ]]
+    [[ post: x > 0 ]]
+  {
+    printf ("G4 general T S\n");
+    return;
+  }
+  int x{-1};
+};
+
+template<typename S>
+struct G4<char, S>
+{
+  G4(char t, S s)
+    [[ pre: t > 'c' ]]
+    [[ pre: s > 3 ]]
+    [[ post: x2 > 3 ]]
+  {
+    printf ("G4 partial char S\n");
+    return;
+  }
+  int x2{-1};
+};
+
+template<>
+G4<double, double>::G4(double, double)
+{
+  printf ("G4 full double double\n");
+  return;
+}
+
+template<>
+G4<double, char>::G4(double a, char b)
+  [[ pre: a > 0 ]]
+  [[ pre: b > 'b' ]]
+  [[ post: x > 1 ]]
+{
+  printf ("G4 full double char\n");
+  return;
+}
+
+// crossover of template classes and template members ok
+template<typename T, typename S>
+struct G5
+{
+  template<typename P>
+  void f(T t, S s, P r)
+    [[ pre: t > 0 ]]
+    [[ pre: s > 0 ]]
+    [[ pre: r > 0 ]]
+  {
+    printf ("G5 gen T S, f gen R\n");
+  }
+};
+
+template<typename S>
+struct G5<char, S>
+{
+  template<typename R>
+  void f(char x, S y, R z)
+    [[ pre: x > 'z' ]]
+    [[ pre: y > 1 ]]
+    [[ pre: z > 1 ]]
+  {
+    printf ("G5 partial char S, f gen R\n");
+  }
+};
+
+template<>
+template<typename Q>
+void G5<double, double>::f(double a, double b, Q c)
+  [[ pre: a > 2 ]]
+  [[ pre: b > 2 ]]
+  [[ pre: c > 2 ]]
+{
+  printf ("G5 full double double, f gen R\n");
+}
+
+int main(int, char**)
+{
+  printf("%d\n", body<int>(-1));
+  printf("%d\n", body<double>(-1));
+  printf("%d\n", none<int>(-1));
+  printf("%d\n", none<double>(-1));
+  printf("%d\n", arg0(-1));
+  printf("%d\n", arg0(-1.0));
+  printf("%d\n", arg1(-3, -1));
+  printf("%d\n", arg1(-3, -1.0));
+  printf("%d\n", (int)ret<int>(-1));
+  printf("%d\n", (int)ret<double>(-1));
+  printf("%f\n", ret<double>(-1));
+
+  printf("%d\n", g1(-1));
+  printf("%d\n", g1(-1.0));
+
+  printf("%d\n", g2(-1));
+  printf("%d\n", g2(1.0));
+  printf("%d\n", g2('d'));
+
+  G3<double, double> g3_gen;
+  G3<int, int> g3_partial;
+  G3<int, double> g3_full;
+  g3_gen.f(-1.0, -1.0); // general
+  g3_partial.f(-2, -2); // partial spec
+  g3_full.f(-3, -3.0); // full spec
+
+  G3<char, char> g3_gen2;
+  G3<int, char> g3_partial2;
+  g3_gen2.f((char)-1, (char)-1);
+  g3_partial2.f(-1, (char)-1);
+
+  G3<int, C> g3_full2;
+  g3_full2.f(5, C{});
+  g3_full2.f(-5, C{});
+
+  G4 g4_gen{-1, -1};
+  G4 g4_full1{-1.0, -1.0};
+  G4 g4_full2{-1.0, (char)'b'};
+  G4 g4_partial{(char)'c', -5};
+
+  G5<int, int> g5_gen;
+  g5_gen.f(-1, -1, -2);
+  g5_gen.f(-1, -1, -2.0);
+
+  G5<char, int> g5_part;
+  g5_part.f('a', -1, -2);
+  g5_part.f('a', -1, -2.1);
+
+  G5<double, double> g5_full;
+  g5_full.f(-1.0, -1.0, -2);
+  g5_full.f(-1.0, -1.0, -2.1);
+  return 0;
+}
+
+
+// { dg-output "default std::handle_contract_violation called: .*.C 9 body<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-2(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 17 body<double> .*(\n|\r\n|\r)*" }
+// { dg-output "-3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 25 none<int> .*(\n|\r\n|\r)*" }
+// { dg-output "1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 32 none<double> .*(\n|\r\n|\r)*" }
+// { dg-output "-101(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 39 arg0<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-9(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 46 arg0<double> .*(\n|\r\n|\r)*" }
+// { dg-output "11(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 53 arg1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 54 arg1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 61 arg1<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 62 arg1<double> .*(\n|\r\n|\r)*" }
+// { dg-output "14(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 69 ret<int> .*(\n|\r\n|\r)*" }
+// { dg-output "1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 76 ret<double> .*(\n|\r\n|\r)*" }
+// { dg-output "3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 76 ret<double> .*(\n|\r\n|\r)*" }
+// { dg-output "3.300000(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 83 g1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-1(\n|\r\n|\r)*" }
+// { dg-output "-1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 97 g2<int> .*(\n|\r\n|\r)*" }
+// { dg-output "-1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 107 g2<double> .*(\n|\r\n|\r)*" }
+// { dg-output "1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 114 g2<char> .*(\n|\r\n|\r)*" }
+// { dg-output "100(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 124 G3<double, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 125 G3<double, .*(\n|\r\n|\r)*" }
+// { dg-output "G3 general T S(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 139 G3<int, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 140 G3<int, .*(\n|\r\n|\r)*" }
+// { dg-output "G3 partial int S(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 147 G3<int, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 148 G3<int, .*(\n|\r\n|\r)*" }
+// { dg-output "G3 full int double(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 124 G3<char, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 125 G3<char, .*(\n|\r\n|\r)*" }
+// { dg-output "G3 general T S(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 139 G3<int, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 140 G3<int, .*(\n|\r\n|\r)*" }
+// { dg-output "G3 partial int S(\n|\r\n|\r)*" }
+// { dg-output "G3 full int C(\n|\r\n|\r)*" }
+// { dg-output "G3 full int C(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 173 G4<int, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 174 G4<int, .*(\n|\r\n|\r)*" }
+// { dg-output "G4 general T S(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 175 G4<int, .*(\n|\r\n|\r)*" }
+// { dg-output "G4 full double double(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 206 G4<double, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 207 G4<double, .*(\n|\r\n|\r)*" }
+// { dg-output "G4 full double char(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 208 G4<double, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 187 G4<char, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 188 G4<char, .*(\n|\r\n|\r)*" }
+// { dg-output "G4 partial char S(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 189 G4<char, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 220 G5<int, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 221 G5<int, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 222 G5<int, .*(\n|\r\n|\r)*" }
+// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 220 G5<int, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 221 G5<int, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 222 G5<int, .*(\n|\r\n|\r)*" }
+// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 233 G5<char, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 234 G5<char, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 235 G5<char, .*(\n|\r\n|\r)*" }
+// { dg-output "G5 partial char S, f gen R(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 233 G5<char, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 234 G5<char, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 235 G5<char, .*(\n|\r\n|\r)*" }
+// { dg-output "G5 partial char S, f gen R(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 244 G5<double, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 245 G5<double, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 246 G5<double, .*(\n|\r\n|\r)*" }
+// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 244 G5<double, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 245 G5<double, .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 246 G5<double, .*(\n|\r\n|\r)*" }
+// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec3.C b/gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec3.C
new file mode 100644
index 00000000000..44725893061
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec3.C
@@ -0,0 +1,45 @@
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+
+template<typename T, typename S>
+struct G5
+{
+  template<typename R>
+  void f(T t, S s, R r)
+    [[ pre: t > 0 ]] [[ pre: s > 0 ]] [[ pre: r > 0 ]]
+  {
+    printf ("G5 gen T S, f gen R\n");
+  }
+};
+
+// specializations can remove contracts
+template<>
+template<typename R>
+void G5<double, double>::f(double a, double b, R c)
+{
+  printf ("G5 full double double, f gen R\n");
+}
+
+int main(int, char**) {
+  G5<double, double> g5_full;
+  g5_full.f(-1.0, -1.0, -2);
+  g5_full.f(-1.0, -1.0, -2.1);
+
+  G5<int, double> g5_gen;
+  g5_gen.f(-1, -1.0, -2);
+  g5_gen.f(-1, -1.0, -2.1);
+  return 0;
+}
+
+// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
+// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* t > 0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* s > 0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* r > 0 .*(\n|\r\n|\r)*" }
+// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* t > 0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* s > 0 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* r > 0 .*(\n|\r\n|\r)*" }
+// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts1.C b/gcc/testsuite/g++.dg/contracts/contracts1.C
new file mode 100644
index 00000000000..6655e016221
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts1.C
@@ -0,0 +1,49 @@
+// generic assert contract parsing checks
+//   check omitted, 'default', 'audit', and 'axiom' contract levels parse
+//   check that all concrete semantics parse
+//   check omitted, '%default' contract roles parse
+//   ensure that an invalid contract level 'invalid' errors
+//   ensure that a predicate referencing an undefined variable errors
+//   ensure that a missing colon after contract level errors
+//   ensure that an invalid contract role 'invalid' errors
+//   ensure that a missing colon after contract role errors
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+static_assert (__cpp_contracts >= 201906);
+static_assert (__cpp_contracts_literal_semantics >= 201906);
+static_assert (__cpp_contracts_roles >= 201906);
+
+int main()
+{
+  int x;
+
+  [[assert: x >= 0]];
+  [[assert default: x < 0]];
+  [[assert audit: x == 0]];
+  [[assert axiom: x == 1]];
+
+  [[assert: x > 0 ? true : false]];
+  [[assert: x < 0 ? true : false]];
+
+  [[assert: x = 0]]; // { dg-error "expected .]. before .=. token" }
+
+  [[assert ignore: x >= 0]];
+  [[assert assume: x >= 0]];
+  [[assert check_never_continue: x >= 0]];
+  [[assert check_maybe_continue: x >= 0]];
+
+  [[assert %default: x >= 0]];
+  [[assert default %default: x < 0]];
+  [[assert audit %default: x == 0]];
+  [[assert axiom %default: x == 1]];
+
+  [[assert check_always_continue: x >= 0]]; // { dg-error "expected contract level" }
+  [[assert invalid: x == 0]]; // { dg-error "expected contract level" }
+  [[assert: y == 0]]; // { dg-error ".y. was not declared in this scope" }
+  [[assert default x == 0]]; // { dg-error "expected .:. before .x." }
+  [[assert %default x >= 0]]; // { dg-error "expected .:. before .x." }
+
+  [[assert %invalid: x >= 0]]; // TODO: optional warning?
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts10.C b/gcc/testsuite/g++.dg/contracts/contracts10.C
new file mode 100644
index 00000000000..ce0723c3db4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts10.C
@@ -0,0 +1,73 @@
+// general checks to ensure that contract violations are generated during
+// runtime when appropriate
+// each check also validates the expected file name, line number, function,
+// predicate, and contract level are included in the violation_info object
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-build-level=audit -fcontract-continuation-mode=on" }
+
+namespace tns
+{
+  int fun()
+  {
+    int x = 1;
+    [[ assert: x < 0 ]];
+    return 0;
+  }
+  int fun2();
+
+  struct TestType
+  {
+    static int fun();
+    static int fun2()
+    {
+      int x = 1;
+      [[ assert: x < 0 ]];
+      return 0;
+    }
+  };
+}
+
+int tns::fun2()
+{
+  int x = 1;
+  [[ assert: x < 0 ]];
+  return 0;
+}
+
+int tns::TestType::fun()
+{
+  int x = 1;
+  [[ assert: x < 0 ]];
+  return 0;
+}
+
+int main()
+{
+  int x = 100;
+  [[assert: x < 0]];
+  [[assert default: x < 1]];
+  [[assert audit: x < 2]];
+// contract_violation.line_number() may eventually come from
+// std::source_location which *is* affected by the #line macro; our current
+// implementation conforms to this so we've included it as a check
+#line 100
+  [[assert: x < 3]];
+  [[assert axiom: x < 4]];
+
+  tns::fun();
+  tns::fun2();
+
+  tns::TestType::fun();
+  tns::TestType::fun2();
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 47 main .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 48 main .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 49 main .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 100 main .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 13 tns::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 33 tns::fun2 .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 40 tns::TestType::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 24 tns::TestType::fun2 .*(\n|\r\n|\r)*" }
+ 
diff --git a/gcc/testsuite/g++.dg/contracts/contracts11.C b/gcc/testsuite/g++.dg/contracts/contracts11.C
new file mode 100644
index 00000000000..b41bc535e5d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts11.C
@@ -0,0 +1,103 @@
+// ensure that assert contract predicates that are not convertible to bool
+// generate an error
+// ensure the same for instatiated template functions
+// ensure the same for non-instatiated template functions when the predicate
+// is not dependent on the template parameters
+// ensure template parameter dependent, potentially non-boolean, contract
+// predicates do not generate an error if never instatiated
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+void fun()
+{
+  return;
+}
+
+template<typename T>
+void fun2(T a)
+{
+  [[assert: fun()]]; // { dg-error "could not convert|in argument" }
+}
+
+template<typename T>
+void fun3(T a)
+{
+  [[assert: fun()]]; // { dg-error "could not convert|in argument" }
+}
+
+template<typename T>
+void fun4(T a)
+{
+  [[assert: a.fun()]];
+}
+
+struct test
+{
+  void fun() { }
+  void fun2() { }
+};
+
+template<typename T>
+void fun5(T a)
+{
+  [[assert: a.fun2()]]; // { dg-error "could not convert" }
+}
+
+struct VoidFun
+{
+  void fun() { }
+};
+struct BoolFun
+{
+  bool fun() { return true; }
+};
+
+template<typename T>
+void fun6(T a)
+{
+  [[ assert: a.fun() ]]; // { dg-error "could not convert" }
+}
+
+template void fun6(VoidFun);
+
+template<typename T>
+void fun7(T a)
+{
+  [[ assert: a.fun() ]];
+}
+
+template void fun7(BoolFun);
+
+struct ImplicitBool
+{
+  operator bool() { return true; }
+};
+struct ExplicitBool
+{
+  explicit operator bool() { return true; }
+};
+
+template<typename T>
+void fun8(T a)
+{
+  [[ assert: T() ]];
+}
+
+template void fun8(ImplicitBool);
+template void fun8(ExplicitBool);
+
+void fun9()
+{
+  [[ assert: ImplicitBool() ]];
+  [[ assert: ExplicitBool() ]];
+}
+
+int main()
+{
+  [[assert: fun()]]; // { dg-error "could not convert" }
+  fun2(1);
+
+  test t;
+  fun5(t);
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts12.C b/gcc/testsuite/g++.dg/contracts/contracts12.C
new file mode 100644
index 00000000000..f888d51296d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts12.C
@@ -0,0 +1,15 @@
+// ensure that constants for contract levels are inserted into the binary when
+// used and omitted when the runtime check is not generated
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts -fcontract-build-level=default" }
+// { dg-final { scan-assembler-not "audit" } }
+// { dg-final { scan-assembler "default" } }
+
+int main()
+{
+  int x = 1;
+  [[assert: x < 0]];
+  [[assert default: x < 0]];
+  [[assert audit: x < 0]];
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts13.C b/gcc/testsuite/g++.dg/contracts/contracts13.C
new file mode 100644
index 00000000000..14ba0e96006
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts13.C
@@ -0,0 +1,51 @@
+// ensure that passing asserts do not affect constexpr functions
+// ensure that failing asserts generate an error in a constexpr function
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+constexpr int wfun(int a) {
+  [[assert: a > 0]];
+  return a;
+}
+
+constexpr int ffun(int a) {
+  [[assert: a > 0]];
+  return a;
+}
+
+template<typename T>
+constexpr int tfun(T a) {
+  [[assert: a > 0]];
+  return a;
+}
+
+template<typename T>
+constexpr int wtfun(T a) {
+  [[assert: a > 0]];
+  return a;
+}
+
+template<typename T>
+constexpr int ftfun(T a) {
+  [[assert: a > 0]];
+  return a;
+}
+
+constexpr int explicitfn(int a) {
+  [[assert ignore: a > 0]];
+  [[assert check_never_continue: a > 0]];
+  return a;
+}
+
+int main(int, char **) {
+  constexpr int a = wfun(10);
+  constexpr int b = ffun(-10); // { dg-message "in .constexpr. expansion" }
+  // { dg-error "contract predicate" "" { target *-*-* } 12 }
+  constexpr int c = wtfun(10);
+  constexpr int d = ftfun(-10);  // { dg-message "in .constexpr. expansion" }
+  // { dg-error "contract predicate" "" { target *-*-* } 30 }
+  constexpr int e = explicitfn(-10); // { dg-message "in .constexpr. expansion" }
+  // { dg-error "contract predicate" "" { target *-*-* } 36 }
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts14.C b/gcc/testsuite/g++.dg/contracts/contracts14.C
new file mode 100644
index 00000000000..55208dbc0a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts14.C
@@ -0,0 +1,58 @@
+// ensure that exceptions thrown inside a custom contract violation handler
+// are catchable up the call stack
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <iostream>
+#include <experimental/contract>
+
+void handle_contract_violation(const std::experimental::contract_violation &violation) {
+  std::cerr << "custom std::handle_contract_violation called:"
+    << " " << violation.line_number()
+    << " " << violation.file_name()
+    << std::endl;
+  throw -(int)violation.line_number();
+}
+
+int fun() {
+  int x = 0;
+  [[ assert: x < 0 ]];
+  return 0;
+}
+
+int fun3() {
+  fun();
+  return 2;
+}
+
+int main(int, char**) {
+  try {
+    int x = 0;
+    [[ assert: x < 0 ]];
+  } catch(int &ex) {
+    std::cerr << "synth caught direct: " << ex << std::endl;
+  }
+
+  try {
+    fun();
+  } catch(int &ex) {
+    std::cerr << "synth caught indirect: " << ex << std::endl;
+  }
+
+  try {
+    fun3();
+  } catch(int &ex) {
+    std::cerr << "synth caught double indirect: " << ex << std::endl;
+  }
+
+  std::cerr << "end main" << std::endl;
+  return 0;
+}
+
+// { dg-output "custom std::handle_contract_violation called: 30 .*/contracts14.C(\n|\r\n|\r)*" }
+// { dg-output "synth caught direct: -30(\n|\r\n|\r)*" }
+// { dg-output "custom std::handle_contract_violation called: 18 .*/contracts14.C(\n|\r\n|\r)*" }
+// { dg-output "synth caught indirect: -18(\n|\r\n|\r)*" }
+// { dg-output "custom std::handle_contract_violation called: 18 .*/contracts14.C(\n|\r\n|\r)*" }
+// { dg-output "synth caught double indirect: -18(\n|\r\n|\r)*" }
+// { dg-output "end main" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts15.C b/gcc/testsuite/g++.dg/contracts/contracts15.C
new file mode 100644
index 00000000000..d822f833916
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts15.C
@@ -0,0 +1,56 @@
+// ensure that exceptions thrown inside a custom contract violation handler
+// are not catchable up the call stack when failing in a noexcept function
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <iostream>
+#include <experimental/contract>
+
+void handle_contract_violation(const std::experimental::contract_violation &violation) {
+  std::cerr << "custom std::handle_contract_violation called:"
+    << " " << violation.line_number()
+    << " " << violation.file_name()
+    << std::endl;
+  throw -(int)violation.line_number();
+}
+
+int fun() noexcept {
+  int x = 0;
+  [[ assert: x < 0 ]];
+  return 0;
+}
+
+int fun3() {
+  fun();
+  return 2;
+}
+
+int main(int, char**) {
+  try {
+    int x = 0;
+    [[ assert: x < 0 ]];
+  } catch(int &ex) {
+    std::cerr << "synth caught direct: " << ex << std::endl;
+  }
+
+  try {
+    fun();
+  } catch(int &ex) {
+    std::cerr << "synth caught indirect: " << ex << std::endl;
+  }
+
+  try {
+    fun3();
+  } catch(int &ex) {
+    std::cerr << "synth caught double indirect: " << ex << std::endl;
+  }
+
+  std::cerr << "end main" << std::endl;
+  return 0;
+}
+
+// { dg-output "custom std::handle_contract_violation called: 30 .*/contracts15.C(\n|\r\n|\r)*" }
+// { dg-output "synth caught direct: -30(\n|\r\n|\r)*" }
+// { dg-output "custom std::handle_contract_violation called: 18 .*/contracts15.C(\n|\r\n|\r)*" }
+// { dg-output "terminate called after throwing an instance of .int.(\n|\r\n|\r)*" }
+// { dg-shouldfail "throwing in noexcept" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts16.C b/gcc/testsuite/g++.dg/contracts/contracts16.C
new file mode 100644
index 00000000000..1c7054507f2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts16.C
@@ -0,0 +1,34 @@
+// ensure that exceptions thrown inside a custom contract violation handler
+// are not catchable up the call stack even when continue mode is off
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts" }
+#include <iostream>
+#include <experimental/contract>
+
+void handle_contract_violation(const std::experimental::contract_violation &violation) {
+  std::cerr << "custom std::handle_contract_violation called:"
+    << " " << violation.line_number()
+    << " " << violation.file_name()
+    << std::endl;
+  throw -(int)violation.line_number();
+}
+
+int fun() {
+  int x = 0;
+  [[ assert: x < 0 ]];
+  return 0;
+}
+
+int main(int, char**) {
+  try {
+    fun();
+  } catch(int &ex) {
+    std::cerr << "synth caught indirect: " << ex << std::endl;
+  }
+
+  return 0;
+}
+
+// { dg-output "custom std::handle_contract_violation called: 18 .*/contracts16.C(\n|\r\n|\r)*" }
+// { dg-output "synth caught indirect: -18(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts17.C b/gcc/testsuite/g++.dg/contracts/contracts17.C
new file mode 100644
index 00000000000..d165bb05315
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts17.C
@@ -0,0 +1,35 @@
+// ensure that exceptions thrown inside a custom contract violation handler
+// are not catchable up the call stack when continue mode is off and the
+// assert fails in a noexcept function
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts" }
+#include <iostream>
+#include <experimental/contract>
+
+void handle_contract_violation(const std::experimental::contract_violation &violation) {
+  std::cerr << "custom std::handle_contract_violation called:"
+    << " " << violation.line_number()
+    << " " << violation.file_name()
+    << std::endl;
+  throw -violation.line_number();
+}
+
+int fun() noexcept {
+  int x = 0;
+  [[ assert: x < 0 ]];
+  return 0;
+}
+
+int main(int, char**) {
+  try {
+    fun();
+  } catch(int &ex) {
+    std::cerr << "synth caught indirect: " << ex << std::endl;
+  }
+
+  return 0;
+}
+
+// { dg-output "custom std::handle_contract_violation called: 19 .*/contracts17.C(\n|\r\n|\r)*" }
+// { dg-shouldfail "throwing in noexcept" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts18.C b/gcc/testsuite/g++.dg/contracts/contracts18.C
new file mode 100644
index 00000000000..e8163ba4ab2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts18.C
@@ -0,0 +1,15 @@
+// check that a valid program using assertions compiles and runs
+//   ensure an axiom with a failing predicate doesn't prevent a successful run
+//   (axiom level contracts are never checked at runtime)
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-role=custom:never,ignore,ignore" }
+
+int main()
+{
+  int x = 1;
+  [[assert axiom: x < 0]];
+  [[assert %custom: x > 0]];
+  [[assert audit %custom: x < 0]];
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts19.C b/gcc/testsuite/g++.dg/contracts/contracts19.C
new file mode 100644
index 00000000000..4a8b43a3186
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts19.C
@@ -0,0 +1,19 @@
+// check that a valid program using assertions compiles and runs
+//   ensure an axiom with a failing predicate doesn't prevent a successful run
+//   (axiom level contracts are never checked at runtime)
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-role=custom:maybe,maybe,ignore" }
+
+int main()
+{
+  int x = 10;
+  [[assert axiom: x < 0]];
+  [[assert %custom: x < 0]];
+  [[assert audit %custom: x < 1]];
+  [[assert axiom %custom: x < 1]];
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 11 main .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 12 main .*(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts2.C b/gcc/testsuite/g++.dg/contracts/contracts2.C
new file mode 100644
index 00000000000..9535e077d36
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts2.C
@@ -0,0 +1,13 @@
+// check that a valid program using assertions compiles and runs
+//   ensure an axiom with a failing predicate doesn't prevent a successful run
+//   (axiom level contracts are never checked at runtime)
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts" }
+
+int main()
+{
+  int x = 1;
+  [[assert axiom: x < 0]];
+  [[assert default: x > 0]];
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts20.C b/gcc/testsuite/g++.dg/contracts/contracts20.C
new file mode 100644
index 00000000000..adce5d70d40
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts20.C
@@ -0,0 +1,11 @@
+// test that contract attributes cause errors pre-c++2a
+// { dg-do compile { target c++17_only } }
+
+int fun(int a)
+  [[ pre: a > 0 ]] // { dg-error "contracts are only available with .-fcontracts." }
+  [[ post r: r < 0 ]] // { dg-error "contracts are only available with .-fcontracts." }
+{
+  [[ assert: a != 0 ]]; // { dg-error "contracts are only available with .-fcontracts." }
+  return -a;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts22.C b/gcc/testsuite/g++.dg/contracts/contracts22.C
new file mode 100644
index 00000000000..91e32b9d9b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts22.C
@@ -0,0 +1,32 @@
+// ensure a default level assert with a failing predicate does not generate an
+// error during runtime when the contracts mode is off
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-mode=off" }
+// { dg-output "returning from main" }
+#include <cstdio>
+
+int constexpr f()
+{
+  constexpr int x = 1;
+  [[assert default: x < 0]];
+  return x;
+}
+
+template<typename T> int k()
+{
+  int x = 1;
+  [[assert default: x < 0]];
+  return x;
+}
+
+
+int main()
+{
+  int x = 1;
+  [[assert default: x < 0]];
+  constexpr int x2 = f();
+  int x3 = k<int>();
+
+  printf ("returning from main\n");
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts24.C b/gcc/testsuite/g++.dg/contracts/contracts24.C
new file mode 100644
index 00000000000..70a54f95a93
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts24.C
@@ -0,0 +1,15 @@
+// check that a valid program using assertions compiles and runs
+//   ensure an axiom with a failing predicate doesn't prevent a successful run
+//   (axiom level contracts are never checked at runtime)
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-semantic=default:never -fcontract-semantic=audit:ignore -fcontract-semantic=axiom:ignore" }
+
+int main()
+{
+  int x = 1;
+  [[assert axiom: x < 0]];
+  [[assert: x > 0]];
+  [[assert audit: x < 0]];
+  return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts25.C b/gcc/testsuite/g++.dg/contracts/contracts25.C
new file mode 100644
index 00000000000..01217807bc1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts25.C
@@ -0,0 +1,57 @@
+// ensure that passing asserts do not affect constexpr functions
+// ensure that failing asserts generate an error at runtime in constexpr funcs
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+constexpr int wfun(int a) {
+  [[assert: a > 0]];
+  return a;
+}
+
+constexpr int ffun(int a) {
+  [[assert: a > 0]];
+  return a;
+}
+
+template<typename T>
+constexpr int tfun(T a) {
+  [[assert: a > 0]];
+  return a;
+}
+
+template<typename T>
+constexpr int wtfun(T a) {
+  [[assert: a > 0]];
+  return a;
+}
+
+template<typename T>
+constexpr int ftfun(T a) {
+  [[assert: a > 0]];
+  return a;
+}
+
+constexpr int explicitfn(int a) {
+  [[assert ignore: a > 0]];
+  [[assert check_maybe_continue: a > 0]];
+  return a;
+}
+
+int main(int, char **) {
+  int a = wfun(10);
+  int b = ffun(-10);
+  int c = wtfun(10);
+  int d = ftfun(-10);
+
+  int e = explicitfn(-10);
+
+  int z = ftfun(-10.0);
+
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 12 ffun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 30 ftfun<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 36 explicitfn .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 30 ftfun<double> .*(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts3.C b/gcc/testsuite/g++.dg/contracts/contracts3.C
new file mode 100644
index 00000000000..ecb9fdb1e65
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts3.C
@@ -0,0 +1,13 @@
+// ensure a default level assert with a failing predicate generates an error
+// during runtime when the contract build level is default
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts" }
+// { dg-shouldfail "assert violation" }
+// { dg-output "default std::handle_contract_violation called" }
+
+int main()
+{
+  int x = 1;
+  [[assert default: x < 0]];
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts35.C b/gcc/testsuite/g++.dg/contracts/contracts35.C
new file mode 100644
index 00000000000..ddd80025afc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts35.C
@@ -0,0 +1,47 @@
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+#include <cstdio>
+
+struct S
+{
+  template<typename T>
+  S(T a) [[ pre: a > 0 ]] [[ pre: a > 10 ]];
+};
+
+template<typename T>
+S::S(T a)
+{
+  printf ("S::S(T): %d\n", (int)a);
+}
+
+struct S1
+{
+  template<typename T>
+  S1(T a) [[ pre: a > 0 ]] [[ pre: a > 10 ]]
+  {
+    printf ("S1::S1(T): %d\n", (int)a);
+  }
+};
+
+int main(int, char **) {
+  S s{-1};
+  S s2{-2.5};
+
+  S1 s1_1{-3};
+  S1 s1_2{-4.5};
+  return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 8 S::S<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 8 S::S<int> .*(\n|\r\n|\r)*" }
+// { dg-output "S::S.T.: -1(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 8 S::S<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 8 S::S<double> .*(\n|\r\n|\r)*" }
+// { dg-output "S::S.T.: -2(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 20 S1::S1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 20 S1::S1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "S1::S1.T.: -3(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 20 S1::S1<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 20 S1::S1<double> .*(\n|\r\n|\r)*" }
+// { dg-output "S1::S1.T.: -4(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/contracts/contracts4.C b/gcc/testsuite/g++.dg/contracts/contracts4.C
new file mode 100644
index 00000000000..a43fb9f98e2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts4.C
@@ -0,0 +1,11 @@
+// ensure an audit level assert with a failing predicate does not generate an
+// error during runtime when the contract build level is default
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts" }
+
+int main()
+{
+  int x = 1;
+  [[assert audit: x < 0]];
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts5.C b/gcc/testsuite/g++.dg/contracts/contracts5.C
new file mode 100644
index 00000000000..0fa0ec83be5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts5.C
@@ -0,0 +1,13 @@
+// ensure an audit level assert with a failing predicate generates an error
+// during runtime when the contract build level is audit
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-build-level=audit" }
+// { dg-shouldfail "assert violation" }
+// { dg-output "default std::handle_contract_violation called" }
+
+int main()
+{
+  int x = 1;
+  [[assert audit: x < 0]];
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts6.C b/gcc/testsuite/g++.dg/contracts/contracts6.C
new file mode 100644
index 00000000000..59c010e5d39
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts6.C
@@ -0,0 +1,11 @@
+// ensure a default level assert with a failing predicate does not generate an
+// error during runtime when the contract build level is off
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-build-level=off" }
+
+int main()
+{
+  int x = 1;
+  [[assert default: x < 0]];
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts7.C b/gcc/testsuite/g++.dg/contracts/contracts7.C
new file mode 100644
index 00000000000..eaad8f0fc9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts7.C
@@ -0,0 +1,14 @@
+// ensure a default level assert with a failing predicate does generates an
+// error during runtime but lets the program continue and complete
+// successfully when the contract build level is default but continuation on
+// contract failure is switched on
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+// { dg-output "default std::handle_contract_violation called" }
+
+int main()
+{
+  int x = 1;
+  [[assert default: x < 0]];
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts8.C b/gcc/testsuite/g++.dg/contracts/contracts8.C
new file mode 100644
index 00000000000..9c8a6b5d8c4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts8.C
@@ -0,0 +1,43 @@
+// generic assert contract parsing checks
+//   ensure that existing generalized attribute parsing is not intefered with
+//   ensure that an assert contract cannot chain into an empty attribute list
+//   ensure that an attribute list cannot chain into an assert contract
+// { dg-do compile }
+// { dg-options "-std=c++2a -fcontracts" }
+
+constexpr bool fun(int x) {
+  return x < 0;
+}
+
+namespace tns {
+  constexpr bool f(int x) {
+    return x < 0;
+  }
+}
+
+bool assert(int x) {
+  return x < 0;
+}
+
+int main()
+{
+  constexpr int x = 1;
+  [[fun(x)]]; // { dg-warning "attributes at the beginning of statement are ignored" }
+  [[fun(x), assert(x)]]; // { dg-warning "attributes at the beginning of statement are ignored" }
+
+  [[assert default: fun(x), ]]; // { dg-error "expected ']'" }
+  [[assert default: fun(x) ]];
+
+  [[fun(x), assert default: fun(x)]]; // { dg-error "expected .]. before .default." }
+  // { dg-warning "attributes at the beginning of statement are ignored" "" { target *-*-* } .-1 }
+  [[fun(x), assert: fun(x)]]; // { dg-error "expected .]. before .:. token" }
+  // { dg-warning "attributes at the beginning of statement are ignored" "" { target *-*-* } .-1 }
+  [[fun(x), assert fun(x)]]; // { dg-error "expected .]. before .fun." }
+  // { dg-warning "attributes at the beginning of statement are ignored" "" { target *-*-* } .-1 }
+  [[ using tns: f(x) ]]; // { dg-warning "attributes at the beginning of statement are ignored" }
+  [[ using tns: f(x), assert default: fun(x) ]]; // { dg-error "expected .]. before .default." }
+  // { dg-warning "attributes at the beginning of statement are ignored" "" { target *-*-* } .-1 }
+  [[ using tns: f(x), , default: fun(x) ]]; // { dg-error "expected .]. before .:." }
+  // { dg-warning "attributes at the beginning of statement are ignored" "" { target *-*-* } .-1 }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/contracts/contracts9.C b/gcc/testsuite/g++.dg/contracts/contracts9.C
new file mode 100644
index 00000000000..f566628ec44
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/contracts9.C
@@ -0,0 +1,45 @@
+// ensure that dependent and non-dependent asserts inside templated
+// functions parse without error whether the function is instatiated or not
+// ensure that assert contract checks are generated inside called templates
+// ensure that template functions can be used as assert predicates
+// { dg-do run }
+// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
+
+template<typename T>
+int fun1(int a, T b)
+{
+  [[ assert: a > 0 ]];
+  [[ assert: (long long)b > 0 ]];
+  return a > 0;
+}
+
+template<typename T>
+struct test
+{
+  static int fun(int a, T b) {
+    [[ assert: a > 0 ]];
+    [[ assert: b > 0 ]];
+    return a > 0;
+  }
+};
+
+int main()
+{
+  fun1(1, -1);
+  fun1(-1, 1.0);
+  fun1(-1, "test");
+
+  [[ assert: fun1(-1, -5) ]];
+  [[ assert: test<int>::fun(10, -6) ]];
+  [[ assert: test<double>::fun(10.0, -7) ]];
+  // return 0;
+}
+
+// { dg-output "default std::handle_contract_violation called: .*.C 12 fun1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 11 fun1<double> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 11 fun1<const char.> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 11 fun1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 12 fun1<int> .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 32 main .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 21 test<int>::fun .*(\n|\r\n|\r)*" }
+// { dg-output "default std::handle_contract_violation called: .*.C 21 test<double>::fun .*(\n|\r\n|\r)*" }
diff --git a/gcc/testsuite/g++.dg/contracts/except_preload_handler/assert_fail.cpp b/gcc/testsuite/g++.dg/contracts/except_preload_handler/assert_fail.cpp
new file mode 100644
index 00000000000..3dda7ac84d5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/except_preload_handler/assert_fail.cpp
@@ -0,0 +1,20 @@
+#include <iostream>
+#include <contract>
+
+int fun() {
+	int x = 0;
+	[[ assert: x < 0 ]];
+
+	return 0;
+}
+
+int main(int argc, char**) {
+	try {
+		fun();
+	} catch(int &ex) {
+		std::cerr << "synth caught indirect: " << ex << std::endl;
+	}
+
+	return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/except_preload_handler/handle_contract_violation.cpp b/gcc/testsuite/g++.dg/contracts/except_preload_handler/handle_contract_violation.cpp
new file mode 100644
index 00000000000..ec051b39e5f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/except_preload_handler/handle_contract_violation.cpp
@@ -0,0 +1,14 @@
+#include <iostream>
+#include <contract>
+
+void handle_contract_violation(const std::contract_violation &violation) {
+  std::cerr << "custom handle_contract_violation: " << std::endl
+    << " line_number: " << violation.line_number() << std::endl
+    << " file_name: " << violation.file_name() << std::endl
+    << " function_name: " << violation.function_name() << std::endl
+    << " comment: " << violation.comment() << std::endl
+    << " assertion_level: " << violation.assertion_level() << std::endl
+    << std::endl;
+  throw -1;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/assert_fail.cpp b/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/assert_fail.cpp
new file mode 100644
index 00000000000..8ae98fbe668
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/assert_fail.cpp
@@ -0,0 +1,20 @@
+#include <iostream>
+#include <contract>
+
+int fun() noexcept {
+	int x = 0;
+	[[ assert: x < 0 ]];
+
+	return 0;
+}
+
+int main(int argc, char**) {
+	try {
+		fun();
+	} catch(int &ex) {
+		std::cerr << "synth caught indirect: " << ex << std::endl;
+	}
+
+	return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/handle_contract_violation.cpp b/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/handle_contract_violation.cpp
new file mode 100644
index 00000000000..ec051b39e5f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/handle_contract_violation.cpp
@@ -0,0 +1,14 @@
+#include <iostream>
+#include <contract>
+
+void handle_contract_violation(const std::contract_violation &violation) {
+  std::cerr << "custom handle_contract_violation: " << std::endl
+    << " line_number: " << violation.line_number() << std::endl
+    << " file_name: " << violation.file_name() << std::endl
+    << " function_name: " << violation.function_name() << std::endl
+    << " comment: " << violation.comment() << std::endl
+    << " assertion_level: " << violation.assertion_level() << std::endl
+    << std::endl;
+  throw -1;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/preload_handler/assert_fail.cpp b/gcc/testsuite/g++.dg/contracts/preload_handler/assert_fail.cpp
new file mode 100644
index 00000000000..0f807db0628
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/preload_handler/assert_fail.cpp
@@ -0,0 +1,7 @@
+int main(int, char**) {
+	int x = 0;
+	[[ assert: x < 0 ]];
+
+	return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/preload_handler/handle_contract_violation.cpp b/gcc/testsuite/g++.dg/contracts/preload_handler/handle_contract_violation.cpp
new file mode 100644
index 00000000000..6029875ee6c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/preload_handler/handle_contract_violation.cpp
@@ -0,0 +1,15 @@
+#include <iostream>
+#include <contract>
+
+void handle_contract_violation(const std::contract_violation &violation) {
+  std::cerr << "custom handle_contract_violation: " << std::endl
+    << " line_number: " << violation.line_number() << std::endl
+    << " file_name: " << violation.file_name() << std::endl
+    << " function_name: " << violation.function_name() << std::endl
+    << " comment: " << violation.comment() << std::endl
+    << " assertion_level: " << violation.assertion_level() << std::endl
+    << " assertion_role: " << violation.assertion_role() << std::endl
+    << " continuation_mode: " << (int)violation.continuation_mode() << std::endl
+    << std::endl;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/assert_fail.cpp b/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/assert_fail.cpp
new file mode 100644
index 00000000000..a17a6239bb7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/assert_fail.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+
+int main(int, char**) {
+	int x = 0;
+	[[ assert: x < 0 ]];
+
+	std::cout << "returning from main" << std::endl;
+	return 0;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/handle_contract_violation.cpp b/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/handle_contract_violation.cpp
new file mode 100644
index 00000000000..8499483c86b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/handle_contract_violation.cpp
@@ -0,0 +1,13 @@
+#include <iostream>
+#include <contract>
+
+void handle_contract_violation(const std::contract_violation &violation) {
+  std::cerr << "custom handle_contract_violation: " << std::endl
+    << " line_number: " << violation.line_number() << std::endl
+    << " file_name: " << violation.file_name() << std::endl
+    << " function_name: " << violation.function_name() << std::endl
+    << " comment: " << violation.comment() << std::endl
+    << " assertion_level: " << violation.assertion_level() << std::endl
+    << std::endl;
+}
+
diff --git a/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/nocontinue.cpp b/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/nocontinue.cpp
new file mode 100644
index 00000000000..2a7d53e353d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/nocontinue.cpp
@@ -0,0 +1,19 @@
+#include <exception>
+#include <contract>
+#include <dlfcn.h>
+
+using handler = void (*)(const std::contract_violation &);
+constexpr const char *mangledHandlerName = "_Z25handle_contract_violationRKSt18contract_violation";
+void handle_contract_violation(const std::contract_violation &violation) {
+  try {
+    handler original_handle_contract_violation;
+    original_handle_contract_violation =
+      (handler)dlsym(RTLD_NEXT, mangledHandlerName);
+    (*original_handle_contract_violation)(violation);
+  }
+  catch(...) {
+    ; // squash
+  }
+  std::terminate();
+}
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-1_a.C b/gcc/testsuite/g++.dg/modules/contracts-1_a.C
new file mode 100644
index 00000000000..f991ef8644e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-1_a.C
@@ -0,0 +1,46 @@
+// Basic test to ensure that guarded templates correctly serialize and
+// deserialize their contracts through the CMI.
+// { dg-additional-options "-fmodules-ts -fcontracts -fcontract-continuation-mode=on" }
+module;
+#include <cstdio>
+#include <experimental/contract>
+export module foo;
+// { dg-module-cmi foo }
+
+export int violation_count{0};
+extern "C++" export void handle_contract_violation(const std::experimental::contract_violation &violation)
+{
+  violation_count++;
+  printf("violation_count: %d\n", violation_count);
+}
+
+export int nontemplate(int n)
+  [[ pre: n > 0 ]]
+  [[ post r: r > 0 ]]
+{
+  return -n;
+}
+
+export
+template<typename T>
+T fn(T n)
+  [[ pre: n > 0 ]]
+  [[ post r: r > 0 ]]
+{
+  printf("%s(%d)\n", __FUNCTION__, n);
+  return n;
+}
+
+export
+template<typename T>
+void void_fn(T n)
+  [[ pre: n < 0 ]]
+{
+  printf("%s(%d)\n", __FUNCTION__, n);
+}
+
+export void foo_fn()
+{
+  fn(5);
+}
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-1_b.C b/gcc/testsuite/g++.dg/modules/contracts-1_b.C
new file mode 100644
index 00000000000..30c15f6928b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-1_b.C
@@ -0,0 +1,33 @@
+// { dg-module-do run }
+// { dg-additional-options "-fmodules-ts -fcontracts -fcontract-continuation-mode=on" }
+module;
+#include <cstdio>
+export module bar;
+// { dg-module-cmi bar }
+import foo;
+
+template<typename T>
+bool bar_fn_pre(T n) { printf("bar fn pre(%d)\n", n); return true; }
+
+export
+template<typename T>
+T bar_fn(T n)
+  [[ pre: bar_fn_pre(n) && n > 0 ]]
+{
+  printf("%s(%d)\n", __FUNCTION__, n);
+  return n;
+}
+
+int main(int, char**)
+{
+  nontemplate(5);
+  nontemplate(-5);
+  fn(5);
+  fn(-5);
+  void_fn(5);
+  void_fn(-5);
+  bar_fn(5);
+  bar_fn(-5);
+  return violation_count == 6 ? 0 : -1;
+}
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-2_a.C b/gcc/testsuite/g++.dg/modules/contracts-2_a.C
new file mode 100644
index 00000000000..828d680d2a0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-2_a.C
@@ -0,0 +1,49 @@
+// Basic test to ensure that guarded function contracts are correctly
+// serialized and deserialized through the CMI.
+// This also tries to ensure that entities referenced in a function's
+// contracts are correctly marked as a dependency of the function itself and
+// serialized in the correct order.
+// { dg-additional-options "-fmodules-ts -fcontracts -fcontract-role=default:maybe,maybe,ignore" }
+module;
+#include <cstdio>
+#include <experimental/contract>
+export module foo;
+// { dg-module-cmi foo }
+
+export int violation_count{0};
+export int violation_line_sum{0};
+extern "C++" export void handle_contract_violation(const std::experimental::contract_violation &violation)
+{
+  violation_count++;
+  violation_line_sum += violation.line_number () * violation_count;
+  printf("violation: %d %d\n", violation_count, violation_line_sum);
+}
+
+export int fn2(int n);
+export int fn_in2(int n);
+export int pre_print(int n) { printf("pre_print(%d)\n", n); return n; }
+
+export int fn_in1(int n) [[ pre: pre_print(n) > 0 ]]
+{
+  printf("%s blah (%d)\n", __FUNCTION__, n);
+  return n;
+}
+export int fn_in2(int x) [[ pre: pre_print(x) > 0 ]]
+{
+  printf("%s(%d)\n", __FUNCTION__, x);
+  return x;
+}
+
+export int fn_iso(int n);
+
+export int fn1(int n)
+  [[ pre: pre_print(n) > 0 ]];
+
+export int fn2(int n)
+  [[ pre: pre_print(n) > 0 ]];
+
+export int pre_print2(int n);
+
+export int fn3(int n)
+  [[ pre: pre_print2(n) > 0 ]];
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-2_b.C b/gcc/testsuite/g++.dg/modules/contracts-2_b.C
new file mode 100644
index 00000000000..01939aeb947
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-2_b.C
@@ -0,0 +1,35 @@
+// { dg-additional-options "-fmodules-ts -fcontracts -fcontract-role=default:maybe,maybe,ignore" }
+module;
+#include <cstdio>
+module foo;
+
+int fn1(int x)
+{
+  printf("%s(%d)\n", __FUNCTION__, x);
+  return x;
+}
+
+int fn_iso(int n) [[ pre: pre_print(n) > 0 ]]
+{
+  printf("%s(%d)\n", __FUNCTION__, n);
+  return n;
+}
+
+int pre_print2(int n)
+{
+  printf("pre_print(%d)\n", n);
+  return n;
+}
+
+int fn2(int x)
+{
+  printf("%s(%d)\n", __FUNCTION__, x);
+  return x;
+}
+
+int fn3(int x)
+{
+  printf("%s(%d)\n", __FUNCTION__, x);
+  return x;
+}
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-2_c.C b/gcc/testsuite/g++.dg/modules/contracts-2_c.C
new file mode 100644
index 00000000000..58938e0e6de
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-2_c.C
@@ -0,0 +1,22 @@
+// { dg-module-do run }
+// { dg-additional-options "-fmodules-ts -fcontracts -fcontract-role=default:maybe,maybe,ignore" }
+import foo;
+
+int main(int, char**)
+{
+  int x = -1;
+
+  fn_iso(x--);
+  fn1(x--);
+  fn2(x--);
+  fn3(x--);
+  fn_in1(x--);
+  fn_in2(x--);
+  return (violation_count == 6 && violation_line_sum == 729) ? 0 : -1;
+}
+
+// TODO does any of this actually get verified?
+// { dg-output "pre_print.-1.(\n|\r\n|\r)*" }
+// { dg-output "violation: 1 12(\n|\r\n|\r)*" }
+// { dg-output "fn_iso.-1.(\n|\r\n|\r)*" }
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-3_a.C b/gcc/testsuite/g++.dg/modules/contracts-3_a.C
new file mode 100644
index 00000000000..a4f03d35842
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-3_a.C
@@ -0,0 +1,41 @@
+// Basic test to ensure that guarded templates correctly serialize and
+// deserialize their contracts through the CMI.
+// { dg-additional-options "-fmodules-ts -fcontracts -fcontract-continuation-mode=on" }
+module;
+#include <cstdio>
+#include <experimental/contract>
+export module foo;
+// { dg-module-cmi foo }
+
+export int violation_count{0};
+extern "C++" export void handle_contract_violation(const std::experimental::contract_violation &violation)
+{
+  violation_count++;
+  printf("violation_count: %d\n", violation_count);
+}
+
+export int nontemplate(int n)
+  [[ pre: n > 0 ]]
+  [[ post r: r > 0 ]]
+{
+  return -n;
+}
+
+export
+template<typename T>
+T fn(T n)
+  [[ pre: n > 0 ]]
+  [[ post r: r > 0 ]]
+{
+  printf("%s(%d)\n", __FUNCTION__, n);
+  return n;
+}
+
+export
+template<typename T>
+void void_fn(T n)
+  [[ pre: n < 0 ]]
+{
+  printf("%s(%d)\n", __FUNCTION__, n);
+}
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-3_b.C b/gcc/testsuite/g++.dg/modules/contracts-3_b.C
new file mode 100644
index 00000000000..b1d6375391b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-3_b.C
@@ -0,0 +1,35 @@
+// { dg-module-do run }
+// { dg-additional-options "-fmodules-ts -fcontracts -fcontract-role=default:ignore,ignore,ignore" }
+module;
+#include <cstdio>
+export module bar;
+// { dg-module-cmi bar }
+import foo;
+
+template<typename T>
+bool bar_fn_pre(T n) { printf("bar fn pre(%d)\n", n); return true; }
+
+export
+template<typename T>
+T bar_fn(T n)
+  [[ pre: bar_fn_pre(n) && n > 0 ]]
+{
+  printf("%s(%d)\n", __FUNCTION__, n);
+  return n;
+}
+
+int main(int, char**)
+{
+  nontemplate(5);
+  nontemplate(-5);
+  fn(5);
+  fn(-5);
+  fn(5.3);
+  fn(-5.3);
+  void_fn(5);
+  void_fn(-5);
+  bar_fn(5);
+  bar_fn(-5);
+  return violation_count == 7 ? 0 : -1;
+}
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-4_a.C b/gcc/testsuite/g++.dg/modules/contracts-4_a.C
new file mode 100644
index 00000000000..f269e6c2078
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-4_a.C
@@ -0,0 +1,28 @@
+// Test that template contracts are not reintpreted when the reinterpret
+// contracts flag is not set, regardless of the current TU's contract
+// configuration.
+// { dg-additional-options "-fmodules-ts -fcontracts -fcontract-role=default:maybe,maybe,ignore" }
+module;
+#include <cstdio>
+#include <experimental/contract>
+export module foo;
+// { dg-module-cmi foo }
+
+export int violation_count{0};
+extern "C++" export void handle_contract_violation(const std::experimental::contract_violation &violation)
+{
+  violation_count++;
+  printf("violation_count: %d\n", violation_count);
+}
+
+export template<typename T>
+T fn_t(T t)
+  [[ pre: t > 0 ]]
+  [[ pre audit %custom: t > 0 ]]
+{
+  printf("%s(%d)\n", __FUNCTION__, t);
+  return t;
+}
+
+export int fn_int(int n);
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-4_b.C b/gcc/testsuite/g++.dg/modules/contracts-4_b.C
new file mode 100644
index 00000000000..65b9287b211
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-4_b.C
@@ -0,0 +1,8 @@
+// { dg-additional-options "-fmodules-ts -fcontracts -fcontract-role=default:ignore,ignore,ignore" }
+module foo;
+
+int fn_int(int n)
+{
+  return fn_t(n);
+}
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-4_c.C b/gcc/testsuite/g++.dg/modules/contracts-4_c.C
new file mode 100644
index 00000000000..84fcb616314
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-4_c.C
@@ -0,0 +1,9 @@
+// { dg-additional-options "-fmodules-ts -fcontracts -fcontract-role=default:ignore,ignore,ignore" }
+export module bar;
+import foo;
+
+export int bar_fn_int(int n)
+{
+  return fn_t(n);
+}
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-4_d.C b/gcc/testsuite/g++.dg/modules/contracts-4_d.C
new file mode 100644
index 00000000000..dc56251d1d8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-4_d.C
@@ -0,0 +1,22 @@
+// { dg-module-do run }
+// { dg-additional-options "-fmodules-ts -fcontracts" }
+module;
+#include <cstdio>
+export module baz;
+import foo;
+import bar;
+
+int main(int, char**)
+{
+  int x = -1;
+
+  printf("calling fn_int\n");
+  fn_int(x--);
+  printf("calling bar_fn_int\n");
+  bar_fn_int(x--);
+
+  return violation_count - 4;
+}
+
+// TODO verify dg-output as well once the testsuite supports it
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-tpl-friend-1_a.C b/gcc/testsuite/g++.dg/modules/contracts-tpl-friend-1_a.C
new file mode 100644
index 00000000000..5e6d848aaa4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-tpl-friend-1_a.C
@@ -0,0 +1,17 @@
+// { dg-additional-options "-fmodules-ts -fcontracts" }
+
+export module foo;
+// { dg-module-cmi foo }
+
+void foo (int, void *);
+void foo (float, void *);
+
+template <typename T> class TPL
+{
+  friend void foo (T, void *); // { dg-warning "non-template function" }
+
+  T member;
+};
+
+template class TPL<float>;  // instantiate
+
diff --git a/gcc/testsuite/g++.dg/modules/contracts-tpl-friend-1_b.C b/gcc/testsuite/g++.dg/modules/contracts-tpl-friend-1_b.C
new file mode 100644
index 00000000000..f872c9248d9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/contracts-tpl-friend-1_b.C
@@ -0,0 +1,19 @@
+// { dg-additional-options "-fmodules-ts -fcontracts" }
+
+module foo;
+
+void foo (int x, void *p)
+  [[ pre: x > 0 ]]
+{
+  auto *obj = reinterpret_cast<TPL<int> *> (p);
+
+  obj->member = x;
+}
+
+void foo (float x, void *p)
+  [[ pre: x > 0 ]]
+{
+  auto *obj = reinterpret_cast<TPL<float> *> (p);
+
+  obj->member = x;
+}
diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in
index 291835d326e..af25bdc044a 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 9ac3dee72f4..f9e5f322fbf 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/cp-tree.def b/gcc/cp/cp-tree.def
index f83b4c54d43..0e07fedc8e1 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -587,6 +587,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/testsuite/g++.dg/contracts/backtrace_handler/Makefile b/gcc/testsuite/g++.dg/contracts/backtrace_handler/Makefile
new file mode 100644
index 00000000000..4f01a9506a7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/backtrace_handler/Makefile
@@ -0,0 +1,13 @@
+CXXFLAGS=--std=c++17 -g
+
+default: assert_fail libhandle_contract_violation.so
+
+run: default
+	LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
+
+./libhandle_contract_violation.so: ./handle_contract_violation.cpp
+	${CXX} ${CXXFLAGS} -shared -fPIC -o $@ $<
+
+clean:
+	rm -fr ./libhandle_contract_violation.so ./assert_fail
+
diff --git a/gcc/testsuite/g++.dg/contracts/backtrace_handler/README b/gcc/testsuite/g++.dg/contracts/backtrace_handler/README
new file mode 100644
index 00000000000..df729f0d609
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/backtrace_handler/README
@@ -0,0 +1,12 @@
+build and install gcc to $prefix, then to see the raw backtrace info run:
+LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make run
+
+for a filtered view using addr2line, see ./prettytrace.sh:
+LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make run |& ./prettytrace.sh
+
+prettytrace.sh relies on addr2line and c++filt to lookup and demangle names,
+and misc coreutils
+
+example_out.txt has an example of the raw output while example_pretty.txt
+shows the corresponding prettified output
+
diff --git a/gcc/testsuite/g++.dg/contracts/backtrace_handler/example_out.txt b/gcc/testsuite/g++.dg/contracts/backtrace_handler/example_out.txt
new file mode 100644
index 00000000000..903ef22f2f5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/backtrace_handler/example_out.txt
@@ -0,0 +1,12 @@
+LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
+contract violation: assert_fail.cpp:3: fun1::x < 0 is false [with contract level=default]
+violation occurs here:
+./assert_fail[0x4011ad]
+./assert_fail[0x4011e0]
+./assert_fail[0x401230]
+./assert_fail[0x4011f1]
+./assert_fail[0x401219]
+/usr/lib/libc.so.6(__libc_start_main+0xf3)[0x7f26e4fa9223]
+./assert_fail[0x4010be]
+[0x0]
+end of violation
diff --git a/gcc/testsuite/g++.dg/contracts/backtrace_handler/example_pretty.txt b/gcc/testsuite/g++.dg/contracts/backtrace_handler/example_pretty.txt
new file mode 100644
index 00000000000..9d5d481a9ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/backtrace_handler/example_pretty.txt
@@ -0,0 +1,8 @@
+LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
+contract violation: assert_fail.cpp:3: fun1::x < 0 is false [with contract level=default]
+violation occurs here:
+    assert_fail.cpp:fun1():4
+    assert_fail.cpp:tns::fun2():8
+    assert_fail.cpp:void fun3<int>(int):13
+    assert_fail.cpp:fun4():16
+    assert_fail.cpp:main:21
diff --git a/gcc/testsuite/g++.dg/contracts/backtrace_handler/prettytrace.sh b/gcc/testsuite/g++.dg/contracts/backtrace_handler/prettytrace.sh
new file mode 100755
index 00000000000..1978cd1c164
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/backtrace_handler/prettytrace.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+inViolation="false"
+sed 's/^/:/' /dev/stdin | while read -r line; do
+  line="$(echo "$line" | sed 's/^://')"
+
+  if [[ "${inViolation}" == "false" ]]; then
+    echo "$line"
+    if [[ -n "$(echo "$line" | grep 'violation occurs here:')" ]]; then
+      inViolation="true"
+    fi
+    continue
+  fi
+
+  if [[ -n "$(echo "$line" | grep 'end of violation')" ]]; then
+    inViolation="false"
+    continue
+  fi
+
+  addr="$(echo "$line" | sed -r 's/.*\[0x([a-f0-9]+)\]$/\1/')"
+  bin="$(echo "$line" | sed -r 's/^([^([]*).*/\1/')"
+  [[ -n "${bin}" ]] || continue
+  t="$(addr2line -e "$bin" "$addr" -f)"
+  file="$(echo "$t" | tail -1 | tr ':' '\n' | head -1)"
+  file="$(echo "$file" | sed -r "s:^$(pwd)/?::")"
+  line="$(echo "$t" | tail -1 | tr ':' '\n' | tail -1 | cut -d' ' -f1)"
+  func="$(echo "$t" | head -1 | c++filt)"
+  [[ $file != "??" ]] && echo "    $file:$func:$line"
+done
+
diff --git a/gcc/testsuite/g++.dg/contracts/except_preload_handler/Makefile b/gcc/testsuite/g++.dg/contracts/except_preload_handler/Makefile
new file mode 100644
index 00000000000..8fcc5b2368a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/except_preload_handler/Makefile
@@ -0,0 +1,13 @@
+CXXFLAGS=--std=c++17 -fcontract-continuation-mode=on
+
+default: assert_fail libhandle_contract_violation.so
+
+run: default
+	LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
+
+./libhandle_contract_violation.so: ./handle_contract_violation.cpp
+	${CXX} ${CXXFLAGS} -shared -fPIC -o $@ $<
+
+clean:
+	rm -fr ./*.o ./libhandle_contract_violation.so ./assert_fail
+
diff --git a/gcc/testsuite/g++.dg/contracts/except_preload_handler/README b/gcc/testsuite/g++.dg/contracts/except_preload_handler/README
new file mode 100644
index 00000000000..cbfe48bd33f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/except_preload_handler/README
@@ -0,0 +1,13 @@
+build and install gcc to $prefix, then run:
+LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make run
+
+this test has a custom handle_contract_violation that throws an exception
+this test is built with -fcontract-continuation-mode=on
+
+since 1) our fun() is not marked noexcept and 2) the continue mode is set to
+on, we expect the exception thrown within the contract violation handler to
+propagate back up into the catch block located in main()
+
+expected output therefore ends in:
+	synth caught indirect: -1
+
diff --git a/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/Makefile b/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/Makefile
new file mode 100644
index 00000000000..8fcc5b2368a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/Makefile
@@ -0,0 +1,13 @@
+CXXFLAGS=--std=c++17 -fcontract-continuation-mode=on
+
+default: assert_fail libhandle_contract_violation.so
+
+run: default
+	LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
+
+./libhandle_contract_violation.so: ./handle_contract_violation.cpp
+	${CXX} ${CXXFLAGS} -shared -fPIC -o $@ $<
+
+clean:
+	rm -fr ./*.o ./libhandle_contract_violation.so ./assert_fail
+
diff --git a/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/README b/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/README
new file mode 100644
index 00000000000..aa7c6dd2df6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/noexcept_preload_handler/README
@@ -0,0 +1,15 @@
+build and install gcc to $prefix, then run:
+LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make run
+
+this test has a custom handle_contract_violation that throws an exception
+this test is built with -fcontract-continuation-mode=on
+
+since 1) our fun() *IS* marked noexcept even though 2) the continue mode is
+set to on, we expect the exception thrown within the contract violation
+handler to quashed and have std::terminate run.
+
+expected output therefore ends in:
+	terminate called after throwing an instance of 'int'
+
+despite there being a catch(int &) handler in main
+
diff --git a/gcc/testsuite/g++.dg/contracts/preload_handler/Makefile b/gcc/testsuite/g++.dg/contracts/preload_handler/Makefile
new file mode 100644
index 00000000000..6ff083f4b5c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/preload_handler/Makefile
@@ -0,0 +1,13 @@
+CXXFLAGS=--std=c++17
+
+default: assert_fail libhandle_contract_violation.so
+
+run: default
+	LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
+
+./libhandle_contract_violation.so: ./handle_contract_violation.cpp
+	${CXX} ${CXXFLAGS} -shared -fPIC -o $@ $<
+
+clean:
+	rm -fr ./libhandle_contract_violation.so ./assert_fail
+
diff --git a/gcc/testsuite/g++.dg/contracts/preload_handler/README b/gcc/testsuite/g++.dg/contracts/preload_handler/README
new file mode 100644
index 00000000000..cc913fe8188
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/preload_handler/README
@@ -0,0 +1,2 @@
+build and install gcc to $prefix, then run:
+LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make run
diff --git a/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/Makefile b/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/Makefile
new file mode 100644
index 00000000000..c8263285471
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/Makefile
@@ -0,0 +1,23 @@
+CXXFLAGS=--std=c++17 -fcontract-continuation-mode=on
+LDFLAGS=-ldl
+
+default: assert_fail libhandle_contract_violation.so libnocontinue.so
+
+run: default
+	./assert_fail
+
+runno: default
+	LD_PRELOAD="./libnocontinue.so ./libhandle_contract_violation.so" ./assert_fail
+
+runnostd: default
+	LD_PRELOAD=./libnocontinue.so ./assert_fail
+
+./libhandle_contract_violation.so: ./handle_contract_violation.cpp
+	${CXX} ${CXXFLAGS} -shared -fPIC -o $@ $<
+
+./libnocontinue.so: ./nocontinue.cpp
+	${CXX} ${CXXFLAGS} -shared -fPIC -o $@ $<
+
+clean:
+	rm -fr ./libhandle_contract_violation.so ./libnocontinue.so ./assert_fail
+
diff --git a/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/README b/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/README
new file mode 100644
index 00000000000..5c931918759
--- /dev/null
+++ b/gcc/testsuite/g++.dg/contracts/preload_nocontinue_handler/README
@@ -0,0 +1,23 @@
+build and install gcc to $prefix, then run:
+
+LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make run
+  Build and run with continuation mode on; will print violation info from the
+  standard handler and then continue to print "returning from main"
+
+LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make runnostd
+  Build and run with continuation mode on, using the default violation handler
+  while preloading the 'nocontinue' hook. This uses LD_PRELOAD to turn all
+  continuing contract violations into non-continuing versions.
+
+  Will print violation info from the standard handler and then terminate -- it
+  will not print "returning from main"
+
+LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make runno
+  Build and run with continuation mode on, using a custom violation handler
+  while preloading the 'nocontinue' hook. This uses LD_PRELOAD to turn all
+  continuing contract violations into non-continuing versions and to install a
+  custom violation handler.
+
+  Will print violation info from the custom handler and then terminate -- it
+  will not print "returning from main"
+
diff --git a/gcc/testsuite/g++.dg/modules/modules.exp b/gcc/testsuite/g++.dg/modules/modules.exp
index afb323d0efd..45ff74c6aa9 100644
--- a/gcc/testsuite/g++.dg/modules/modules.exp
+++ b/gcc/testsuite/g++.dg/modules/modules.exp
@@ -187,8 +187,9 @@ proc module_do_it { do_what testcase std asm_list } {
 	lappend options "additional_flags=$std"
 	set ident "$ident $std"
     }
-    if { [llength $do_what] > 3 } {
-	lappend options "additional_flags=[lindex $do_what 3]"
+    global extra_tool_flags
+    if { [llength $extra_tool_flags] } {
+	lappend options "additional_flags=$extra_tool_flags"
     }
 
     set execname "./[file tail $testcase].exe"
@@ -250,6 +251,8 @@ proc module-init { src } {
     set option_list {}
     set have_std 0
     set std_prefix "-std=c++"
+    global extra_tool_flags
+    set extra_tool_flags {}
 
     foreach op $tmp {
 	switch [lindex $op 0] {
@@ -258,11 +261,13 @@ proc module-init { src } {
 		if { [string match "*-std=*" [lindex $op 2]] } {
 		    set have_std 1
 		}
+		eval lappend extra_tool_flags [lindex $op 2]
 	    }
 	    "dg-additional-options" {
 		if { [string match "*-std=*" [lindex $op 2]] } {
 		    set have_std 1
 		}
+		eval lappend extra_tool_flags [lindex $op 2]
 	    }
 	}
     }
diff --git a/gcc/testsuite/lib/g++.exp b/gcc/testsuite/lib/g++.exp
index 16e61fb4ad4..4e10b614f07 100644
--- a/gcc/testsuite/lib/g++.exp
+++ b/gcc/testsuite/lib/g++.exp
@@ -142,6 +142,10 @@ proc g++_link_flags { paths } {
 	  append flags " -L${gccpath}/libstdc++-v3/src/.libs "
 	  append ld_library_path ":${gccpath}/libstdc++-v3/src/.libs"
       }
+      if [file exists "${gccpath}/libstdc++-v3/src/experimental/.libs/libstdc++exp.a"] {
+	  append flags " -L${gccpath}/libstdc++-v3/src/experimental/.libs "
+	  append ld_library_path ":${gccpath}/libstdc++-v3/src/experimental/.libs"
+      }
 
       if [file exists "${gccpath}/libiberty/libiberty.a"] {
           append flags "-L${gccpath}/libiberty "
