...
 
Commits (5)
......@@ -7293,8 +7293,7 @@ extern void remove_contract_attributes (tree);
extern bool contract_active_p (tree);
extern bool contract_any_active_p (tree);
extern bool contract_any_deferred_p (tree);
extern tree build_pre_fn (tree);
extern tree build_post_fn (tree, tree);
extern void build_contract_function_decls (tree);
extern tree start_postcondition_statement ();
extern void finish_postcondition_statement (tree);
extern tree start_contract (location_t, tree, tree, tree);
......@@ -7327,7 +7326,7 @@ set_decl_contracts (tree decl, tree contract_attrs)
}
/* in decl.c */
extern hash_map<tree_decl_hash, hash_set<tree, true> *> pending_guarded_decls;
extern hash_map<tree_decl_hash, tree> pending_guarded_decls;
extern void defer_guarded_contract_match (tree, tree, tree);
/* RAII sentinel to ensures that deferred access checks are popped before
......
......@@ -1149,19 +1149,30 @@ merge_contracts (tree decl, const cp_declarator *fn)
defer_guarded_contract_match (decl, NULL_TREE, fn->contracts);
}
/* Map from FUNCTION_DECL to a set of tree lists of contracts that have not
/* Map from FUNCTION_DECL to a tree list of contracts that have not
been matched or diagnosed yet. The TREE_PURPOSE is the basefn we're
overriding or NULL_TREE if this is a redecl, and the TREE_VALUE is the list
of contract attrs. */
hash_map<tree_decl_hash, hash_set<tree, true> *> pending_guarded_decls;
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, new hash_set<tree, true>());
hash_set<tree, true> *bases = *pending_guarded_decls.get (fndecl);
bases->add (build_tree_list (fn, contracts));
{
pending_guarded_decls.put (fndecl, build_tree_list (fn, contracts));
return;
}
for (tree pending = *pending_guarded_decls.get (fndecl);
pending;
pending = TREE_CHAIN (pending))
{
/* FIXME don't try to queue these in the first place... */
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
......@@ -1177,20 +1188,18 @@ match_deferred_contracts (tree decl)
if (contract_any_deferred_p (DECL_CONTRACTS (decl)))
return;
hash_set<tree, true> *decls = *pending_guarded_decls.get (decl);
tree old_contracts = DECL_CONTRACTS (decl);
location_t oldloc = CONTRACT_SOURCE_LOCATION (old_contracts);
/* Do late contract matching. */
for (hash_set<tree, true>::iterator it = decls->begin();
it != decls->end();
++it)
for (tree pending = *pending_guarded_decls.get (decl);
pending;
pending = TREE_CHAIN (pending))
{
tree base = TREE_PURPOSE (*it);
tree contracts = TREE_VALUE (*it);
tree base = TREE_PURPOSE (pending);
tree contracts = TREE_VALUE (pending);
if (contracts == old_contracts)
continue; // FIXME we shouldn't queue these to begin with
continue; // FIXME we shouldn't queue these to begin with
if (base)
match_contract_conditions (CONTRACT_SOURCE_LOCATION (contracts),
contracts,
......@@ -1721,6 +1730,20 @@ duplicate_function_template_decls (tree newdecl, tree olddecl)
return false;
}
/* Replace the argument names in the FUNCTION_DECL TO with the names of the
arguments from the FUNCTION_DECL FROM. */
static void
copy_argument_names (tree from, tree to)
{
/* Save new argument names for use in contracts parsing. */
for (tree to_arg = DECL_ARGUMENTS (to),
from_arg = DECL_ARGUMENTS (from);
to_arg && from_arg;
to_arg = TREE_CHAIN (to_arg), from_arg = TREE_CHAIN (from_arg))
DECL_NAME (to_arg) = DECL_NAME (from_arg);
}
/* If NEWDECL is a redeclaration of OLDDECL, merge the declarations.
If the redeclaration is invalid, a diagnostic is issued, and the
error_mark_node is returned. Otherwise, OLDDECL is returned.
......@@ -2354,6 +2377,11 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
= DECL_SEEN_WITHOUT_CONTRACTS_P (olddecl);
set_pre_fn (newdecl, DECL_PRE_FN (olddecl));
set_post_fn (newdecl, DECL_POST_FN (olddecl));
/* Save new argument names for use in contracts parsing. */
if (DECL_PRE_FN (newdecl))
copy_argument_names (newdecl, DECL_PRE_FN (newdecl));
if (DECL_POST_FN (newdecl))
copy_argument_names (newdecl, DECL_POST_FN (newdecl));
/* Optionally warn about more than one declaration for the same
name, but don't warn about a function declaration followed by a
......@@ -2972,11 +3000,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
if (DECL_ARGUMENTS (olddecl))
{
/* Save new argument names for use in contracts parsing. */
for (tree o = DECL_ARGUMENTS (olddecl),
n = DECL_ARGUMENTS (newdecl);
o && n;
o = TREE_CHAIN (o), n = TREE_CHAIN (n))
DECL_NAME (o) = DECL_NAME (n);
copy_argument_names (newdecl, olddecl);
DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl);
}
......@@ -16805,8 +16829,10 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
&& contract_any_active_p (DECL_CONTRACTS (decl1))
&& !DECL_CONSTRUCTOR_P (decl1)
&& !DECL_DESTRUCTOR_P (decl1);
/* 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 (starting_guarded_p && !DECL_PRE_FN (decl1))
build_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. */
......@@ -16815,11 +16841,13 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
{
vec<tree, va_gc> *args = build_arg_list (decl1);
push_deferring_access_checks (dk_no_check);
tree call = finish_call_expr (DECL_PRE_FN (decl1), &args,
/*disallow_virtual=*/true,
/*koenig_p=*/false,
/*complain=*/tf_warning_or_error);
gcc_assert (call != error_mark_node);
pop_deferring_access_checks ();
/* Just add the call expression. */
finish_expr_stmt (call);
......@@ -17611,7 +17639,8 @@ finish_function (bool inline_p)
invoke_plugin_callbacks (PLUGIN_FINISH_PARSE_FUNCTION, fndecl);
// FIXME finish_function_contracts here?
if (finishing_guarded_p)
finish_function_contracts (fndecl, 0);
return fndecl;
}
......@@ -17628,6 +17657,7 @@ finish_function_contracts (tree fndecl, bool is_inline)
return;
for (tree ca = DECL_CONTRACTS (fndecl); ca; ca = CONTRACT_CHAIN (ca))
if (!CONTRACT_CONDITION (TREE_VALUE (ca))
|| CONTRACT_CONDITION_DEFERRED_P (TREE_VALUE (ca))
|| CONTRACT_CONDITION (TREE_VALUE (ca)) == error_mark_node)
return;
......
This diff is collapsed.
......@@ -13998,7 +13998,6 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
{
set_pre_fn (r, tsubst_decl (DECL_PRE_FN (t), args,
tf_warning_or_error));
DECL_CONTEXT (DECL_PRE_FN (r)) = DECL_CONTEXT (r);
DECL_ORIGINAL_FN (DECL_PRE_FN (r)) = r;
}
if (DECL_POST_FN (t))
......@@ -20772,6 +20771,16 @@ tsubst_contract_conditions (tree t, tree args, tsubst_flags_t complain,
tree in_decl)
{
if (!DECL_HAS_CONTRACTS_P (t)) return;
local_specialization_stack lss (lss_copy);
/* If we're not a cdtor then the contracts are actually parsed in terms of
the pre and post function arguments, not our own. */
if (!DECL_CONSTRUCTOR_P (t) && !DECL_DESTRUCTOR_P (t))
{
register_parameter_specializations (DECL_PRE_FN (in_decl),
DECL_PRE_FN (t));
register_parameter_specializations (DECL_POST_FN (in_decl),
DECL_POST_FN (t));
}
tree contract_attrs = DECL_CONTRACTS (t);
contract_attrs = tsubst_contract_conditions_r (contract_attrs, args,
complain, in_decl);
......@@ -20936,6 +20945,22 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
if (DECL_P (chain) && DECL_CLONED_FUNCTION_P (chain))
clone_function_decl (fndecl, /*update_methods=*/false);
if (DECL_DECLARES_FUNCTION_P (fndecl)
&& DECL_TEMPLATE_SPECIALIZATION (tmpl))
{
gcc_assert (DECL_CONTRACTS (DECL_TEMPLATE_RESULT (tmpl)));
set_decl_contracts (fndecl, DECL_CONTRACTS (DECL_TEMPLATE_RESULT (tmpl)));
}
if (processing_specialization
&& DECL_DECLARES_FUNCTION_P (fndecl)
&& DECL_CONTRACTS (fndecl))
{
/* Specializations have completely independent contracts. */
remove_contract_attributes (fndecl);
set_pre_fn (fndecl, NULL_TREE);
set_post_fn (fndecl, NULL_TREE);
}
if (!access_ok)
{
if (!(complain & tf_error))
......@@ -25202,7 +25227,7 @@ regenerate_decl_from_template (tree decl, tree tmpl, tree args)
&& !DECL_DECLARED_INLINE_P (decl))
DECL_DECLARED_INLINE_P (decl) = 1;
set_decl_contracts (decl, DECL_CONTRACTS (code_pattern));
//set_decl_contracts (decl, DECL_CONTRACTS (code_pattern));
/* FIXME DECL_PRE_FN/DECL_POST_FN/DECL_ORIGINAL_FN? */
maybe_instantiate_noexcept (decl, tf_error);
......@@ -25833,7 +25858,7 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
For templates with deduced return types this must be done *after*
substituting the body so we have the deduced return type. */
if (DECL_CONSTRUCTOR_P (d))
tsubst_contract_conditions (d, args, tf_warning_or_error, tmpl);
tsubst_contract_conditions (d, args, tf_warning_or_error, code_pattern);
/* Substitute into the body of the function. */
if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern))
......@@ -25856,9 +25881,8 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
}
/* Instantiate pending dependent contract conditions if we didn't already. */
//if (!DECL_CONSTRUCTOR_P (d))
if (DECL_DESTRUCTOR_P (d))
tsubst_contract_conditions (d, args, tf_warning_or_error, tmpl);
if (!DECL_CONSTRUCTOR_P (d))
tsubst_contract_conditions (d, args, tf_warning_or_error, code_pattern);
/* Finish the function. */
if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)
......@@ -25868,8 +25892,6 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
{
d = finish_function (/*inline_p=*/false);
expand_or_defer_fn (d);
// FIXME: finish_function_contracts (d); ?
//finish_function_contracts (d, false);
}
if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern))
......
......@@ -2057,12 +2057,13 @@ check_final_overrider (tree overrider, tree basefn)
}
else if (DECL_HAS_CONTRACTS_P (basefn) && !DECL_HAS_CONTRACTS_P (overrider))
{
/* FIXME this works semantically but currently prints the base
* function's name in diagnostics. Should we actually build a separate
* copy, or pass it in as a pointer to a static string maybe? */
#if 0
/* FIXME this results in an almost exact duplicate of the final pre/post
fns, with just the name of the caller changing. It may be beneficial
to collapse these ourselves. */
/* We're inheriting basefn's contracts; create a copy of them but
replace references to their parms to our parms. */
if(!DECL_PRE_FN (overrider))
build_contract_function_decls (overrider);
tree last = NULL_TREE, contract_attrs = NULL_TREE;
for (tree a = DECL_CONTRACTS (basefn);
a != NULL_TREE;
......@@ -2070,7 +2071,14 @@ check_final_overrider (tree overrider, tree basefn)
{
tree c = copy_node (a);
TREE_VALUE (c) = copy_node (TREE_VALUE (c));
remap_contract (basefn, overrider, TREE_VALUE (c));
tree src = DECL_PRE_FN (basefn);
tree dst = DECL_PRE_FN (overrider);
if (TREE_CODE (TREE_VALUE (c)) == POSTCONDITION_STMT)
{
src = DECL_POST_FN (basefn);
dst = DECL_POST_FN (overrider);
}
remap_contract (src, dst, TREE_VALUE (c));
CONTRACT_COMMENT (TREE_VALUE (c)) =
copy_node (CONTRACT_COMMENT (TREE_VALUE (c)));
......@@ -2080,9 +2088,6 @@ check_final_overrider (tree overrider, tree basefn)
contract_attrs = c;
}
set_decl_contracts (overrider, contract_attrs);
#endif
set_pre_fn (overrider, DECL_PRE_FN (basefn));
set_post_fn (overrider, DECL_POST_FN (basefn));
}
else if (DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
{
......
......@@ -875,7 +875,7 @@ compute_contract_concrete_semantic (tree contract)
}
static tree
build_contract_functor_declaration (tree fndecl, bool pre, tree ret_type)
build_contract_functor_declaration (tree fndecl, bool pre)
{
if (TREE_TYPE (fndecl) == error_mark_node)
return error_mark_node;
......@@ -886,7 +886,7 @@ build_contract_functor_declaration (tree fndecl, bool pre, tree ret_type)
/* Create and rename unchecked function and give an internal name. */
tree fn = copy_fn_decl (fndecl);
DECL_RESULT (fn) = NULL_TREE;
tree value_type = ret_type ? ret_type : TREE_TYPE (TREE_TYPE (fn));
tree value_type = pre ? void_type_node : TREE_TYPE (TREE_TYPE (fn));
// FIXME will this already have `this`?
tree arg_types = NULL_TREE;
......@@ -923,8 +923,6 @@ build_contract_functor_declaration (tree fndecl, bool pre, tree ret_type)
TREE_CHAIN (*last) = void_list_node;
}
if (pre) // make void
value_type = void_type_node;
TREE_TYPE (fn) = build_function_type (value_type, arg_types);
if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fndecl))
......@@ -943,6 +941,11 @@ build_contract_functor_declaration (tree fndecl, bool pre, tree ret_type)
IDENTIFIER_VIRTUAL_P (DECL_NAME (fn)) = false;
DECL_VIRTUAL_P (fn) = false;
/* Update various inline related declaration properties. */
DECL_DECLARED_INLINE_P (fn) = true;
DECL_DISREGARD_INLINE_LIMITS (fn) = true;
TREE_NO_WARNING (fn) = 1;
if (!DECL_TEMPLATE_INFO (fn))
return fn;
......@@ -956,57 +959,23 @@ build_contract_functor_declaration (tree fndecl, bool pre, tree ret_type)
return fn;
}
tree
build_pre_fn (tree fndecl)
{
if (DECL_PRE_FN (fndecl)) gcc_assert (false);
/* Build the unchecked function declaration. */
tree pre = build_contract_functor_declaration (fndecl, true, void_type_node);
if (pre == error_mark_node) return pre;
/* Update various pre declaration properties. */
DECL_DECLARED_INLINE_P (pre) = true;
DECL_DISREGARD_INLINE_LIMITS (pre) = true;
TREE_NO_WARNING (pre) = 1;
/* We parse contracts using this new function's args, so we don't need to
remap them later.
We build the actual definition after finishing the guarded function. */
set_pre_fn (fndecl, pre);
return pre;
}
/* FIXME this can largely be merged with build_pre_fn */
tree
build_post_fn (tree fndecl, tree ret_type)
void
build_contract_function_decls (tree fndecl)
{
/* Do not build until we have the deduced return type. */
if (undeduced_auto_decl (fndecl) && !DECL_TEMPLATE_INFO (fndecl)
&& ret_type == NULL_TREE)
return NULL_TREE;
/* Build the unchecked function declaration. */
tree post = build_contract_functor_declaration (fndecl, false,
ret_type ? ret_type : TREE_TYPE (TREE_TYPE (fndecl)));
if (post == error_mark_node) return post;
/* Update various post declaration properties. */
DECL_DECLARED_INLINE_P (post) = true;
DECL_DISREGARD_INLINE_LIMITS (post) = true;
TREE_NO_WARNING (post) = 1;
/* Constructors and destructors have their contracts inserted inline. */
if (DECL_CONSTRUCTOR_P (fndecl) || DECL_DESTRUCTOR_P (fndecl))
return;
/* We parse contracts using this new function's args, so we don't need to
remap them later.
We build the actual definition after finishing the guarded function. */
set_post_fn (fndecl, post);
return post;
if (!DECL_PRE_FN (fndecl))
set_pre_fn (fndecl, build_contract_functor_declaration (fndecl, true));
if (!DECL_POST_FN (fndecl))
set_post_fn (fndecl, build_contract_functor_declaration (fndecl, false));
}
/* Begin a new scope for the postcondition. */
tree
......@@ -1687,6 +1656,43 @@ finish_do_stmt (tree cond, tree do_stmt, bool ivdep, unsigned short unroll)
DO_COND (do_stmt) = cond;
}
/* Rewrite the post function decl of FNDECL, replacing the original undeduced
return type with RETURN_TYPE. */
static void
apply_post_deduced_return_type (tree fndecl, tree return_type)
{
tree post_fn = DECL_POST_FN (fndecl);
tree fntype = TREE_TYPE (post_fn);
/* Replace the type of the final parameter (the result of FNDECL) with the
actual type. */
tree arg_types = TYPE_ARG_TYPES (TREE_TYPE (post_fn));
for (tree arg_type = arg_types; arg_type; arg_type = TREE_CHAIN (arg_type))
if (TREE_CHAIN (arg_type) == void_list_node)
TREE_VALUE (arg_type) = return_type;
tree newtype;
if (TREE_CODE (fntype) == FUNCTION_TYPE)
{
newtype = build_function_type (return_type, arg_types);
newtype = apply_memfn_quals (newtype,
type_memfn_quals (fntype));
}
else
newtype = build_method_type_directly
(class_of_this_parm (fntype), return_type, TREE_CHAIN (arg_types));
if (tree attrs = TYPE_ATTRIBUTES (fntype))
newtype = cp_build_type_attribute_variant (newtype, attrs);
newtype = cxx_copy_lang_qualifiers (newtype, fntype);
TREE_TYPE (post_fn) = newtype;
/* Fix return value parameter type. */
TREE_TYPE (DECL_UNCHECKED_RESULT (fndecl)) = return_type;
}
/* Finish a return-statement. The EXPRESSION returned, if any, is as
indicated. */
......@@ -1703,18 +1709,22 @@ finish_return_stmt (tree expr)
&& !DECL_CONSTRUCTOR_P (current_function_decl)
&& !DECL_DESTRUCTOR_P (current_function_decl)
&& contract_any_active_p (DECL_CONTRACTS (current_function_decl));
if (needs_post && !DECL_POST_FN (current_function_decl))
build_post_fn (current_function_decl, TREE_TYPE (expr));
if (needs_post && DECL_POST_FN (current_function_decl) != error_mark_node)
{
vec<tree, va_gc> *args = build_arg_list (current_function_decl);
vec_safe_push (args, expr); // FIXME do we need forward_parm or similar?
if (undeduced_auto_decl (DECL_POST_FN (current_function_decl)))
apply_post_deduced_return_type (current_function_decl,
TREE_TYPE (expr));
push_deferring_access_checks (dk_no_check);
tree call = finish_call_expr (DECL_POST_FN (current_function_decl), &args,
/*disallow_virtual=*/true,
/*koenig_p=*/false,
/*complain=*/tf_warning_or_error);
gcc_assert (call != error_mark_node);
pop_deferring_access_checks ();
/* Replace returned expression with call to post function. */
expr = call;
......
// 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)
[[ pre: sizeof(decltype(g2(5))) > 4 ]];
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;
}
template<typename T>
auto g8(T t) [[ post r: r == t && sizeof(decltype(::g8(t))) > 2 ]];
template<typename S>
auto g8(S s) [[ post q: q == s && sizeof(decltype(::g8(s))) > 2 ]]
{
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);
g8(7);
g8('a');
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 10 g1 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 18 g2 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 26 g3 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 31 g4 .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 38 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)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 64 g8<int> .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 64 g8<char> .*(\n|\r\n|\r)*" }
// { 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; }
};
// { 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; }
// FIXME this diagnostic is in a weird spot
// { dg-error "mismatched" "" { target *-*-* } 5 }
};
struct Child4 : public BaseA {
int fun(int n);
};
int Child4::fun(int n)
[[ pre: n != 0 ]] // { dg-error "mismatched" }
{
return -n;
}
......@@ -2,10 +2,17 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts" }
// OK if equivalent.
// 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);
......@@ -14,6 +21,9 @@ int g1(int a);
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
{
......