...
 
Commits (2)
......@@ -2845,8 +2845,6 @@ struct GTY(()) lang_decl_fn {
struct cp_token_cache * GTY ((tag ("1"))) pending_inline_info;
tree GTY ((tag ("0"))) saved_auto_return_type;
} GTY ((desc ("%1.pending_inline_p"))) u;
tree contracts_original_fn; // FIXME move out of here
};
/* DECL_LANG_SPECIFIC for namespaces. */
......@@ -3209,9 +3207,6 @@ struct GTY(()) lang_decl {
#define DECL_BEFRIENDING_CLASSES(NODE) \
(LANG_DECL_FN_CHECK (NODE)->befriending_classes)
#define DECL_ORIGINAL_FN(NODE) \
(LANG_DECL_FN_CHECK (NODE)->contracts_original_fn)
/* Nonzero for FUNCTION_DECL means that this decl is a static
member function. */
#define DECL_STATIC_FUNCTION_P(NODE) \
......@@ -3550,12 +3545,21 @@ find_contract (tree attrs)
#define DECL_UNCHECKED_RESULT(NODE) \
(get_unchecked_result (NODE))
/* 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_pre_fn ((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_post_fn ((NODE)))
/* For a FUNCTION_DECL of a pre/post function, this points back to the
original guarded function. */
#define DECL_ORIGINAL_FN(NODE) \
(get_contracts_original_fn (NODE))
/* True the FUNCTION_DECL NODE was initially declared without contracts. */
#define DECL_SEEN_WITHOUT_CONTRACTS_P(NODE) \
(LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->seen_without_contracts_p)
......@@ -7294,17 +7298,18 @@ extern bool contract_active_p (tree);
extern bool contract_any_active_p (tree);
extern bool contract_any_deferred_p (tree);
extern void build_contract_function_decls (tree);
extern void set_contract_functions (tree, tree, tree);
extern void set_contracts_original_fn (tree, tree);
extern tree start_postcondition_statement ();
extern void finish_postcondition_statement (tree);
extern tree start_contract (location_t, tree, tree, tree);
extern tree finish_contract (tree, tree, tree);
extern tree build_contract_check (tree);
extern vec<tree, va_gc> *build_arg_list (tree);
extern vec<tree, va_gc> *build_arg_list (tree);
extern tree get_unchecked_result (tree);
extern tree get_pre_fn (tree);
extern tree get_post_fn (tree);
extern void set_pre_fn (tree, tree);
extern void set_post_fn (tree, tree);
extern tree get_pre_fn (tree);
extern tree get_post_fn (tree);
extern tree get_contracts_original_fn (tree);
extern void emit_assertion (tree);
extern void emit_preconditions (tree);
......
......@@ -1085,8 +1085,7 @@ merge_contracts (tree decl, const cp_declarator *fn)
&& DECL_USE_TEMPLATE (decl) && !DECL_USE_TEMPLATE (orig))
{
remove_contract_attributes (decl);
set_pre_fn (decl, NULL_TREE);
set_post_fn (decl, NULL_TREE);
set_contract_functions (decl, NULL_TREE, NULL_TREE);
}
}
......@@ -1099,7 +1098,7 @@ merge_contracts (tree decl, const cp_declarator *fn)
}
if (fn->contracts == DECL_CONTRACTS (decl))
return; //gcc_assert (false); //return;
return;
bool any_uses_return = false;
for (tree ca = fn->contracts; ca; ca = TREE_CHAIN (ca))
......@@ -1167,7 +1166,6 @@ defer_guarded_contract_match (tree fndecl, tree fn, tree contracts)
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)
......@@ -1198,8 +1196,6 @@ match_deferred_contracts (tree decl)
{
tree base = TREE_PURPOSE (pending);
tree contracts = TREE_VALUE (pending);
if (contracts == old_contracts)
continue; // FIXME we shouldn't queue these to begin with
if (base)
match_contract_conditions (CONTRACT_SOURCE_LOCATION (contracts),
contracts,
......@@ -2375,8 +2371,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
set_decl_contracts (newdecl, DECL_CONTRACTS (olddecl));
DECL_SEEN_WITHOUT_CONTRACTS_P (newdecl)
= DECL_SEEN_WITHOUT_CONTRACTS_P (olddecl);
set_pre_fn (newdecl, DECL_PRE_FN (olddecl));
set_post_fn (newdecl, DECL_POST_FN (olddecl));
set_contract_functions (newdecl,
DECL_PRE_FN (olddecl),
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));
......@@ -2465,9 +2462,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
specializations. */
gcc_assert (!DECL_TEMPLATE_SPECIALIZATIONS (newdecl));
//tree new_contracts = DECL_CONTRACTS (new_result);
/* Remove contracts from new_result so they aren't appended to
old_result by the merge function. */
old_result by the merge function. */
remove_contract_attributes (new_result);
DECL_ATTRIBUTES (old_result)
= (*targetm.merge_decl_attributes) (old_result, new_result);
......@@ -3258,8 +3254,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
if (flag_contracts && TREE_CODE (newdecl) == FUNCTION_DECL)
{
remove_contract_attributes (newdecl);
set_pre_fn (newdecl, NULL_TREE);
set_post_fn (newdecl, NULL_TREE);
set_contract_functions (newdecl, NULL_TREE, NULL_TREE);
}
ggc_free (newdecl);
......@@ -17374,9 +17369,9 @@ finish_function (bool inline_p)
{
/* Save our function name for diagnostics generated by our contracts. */
if (DECL_PRE_FN (fndecl) && DECL_PRE_FN (fndecl) != error_mark_node)
DECL_ORIGINAL_FN (DECL_PRE_FN (fndecl)) = fndecl;
set_contracts_original_fn (DECL_PRE_FN (fndecl), fndecl);
if (DECL_POST_FN (fndecl) && DECL_POST_FN (fndecl) != error_mark_node)
DECL_ORIGINAL_FN (DECL_POST_FN (fndecl)) = fndecl;
set_contracts_original_fn (DECL_POST_FN (fndecl), fndecl);
}
/* If we're saving up tree structure, tie off the function now. */
......@@ -17665,6 +17660,8 @@ finish_function_contracts (tree fndecl, bool is_inline)
if (is_inline)
flags = SF_PRE_PARSED | SF_INCLASS_INLINE;
tree finished_pre = NULL_TREE, finished_post = NULL_TREE;
if (DECL_PRE_FN (fndecl) && DECL_INITIAL (fndecl) != error_mark_node
&& DECL_PRE_FN (fndecl) != error_mark_node)
{
......@@ -17673,8 +17670,8 @@ finish_function_contracts (tree fndecl, bool is_inline)
DECL_ATTRIBUTES (DECL_PRE_FN (fndecl)),
flags);
emit_preconditions (DECL_CONTRACTS (fndecl));
set_pre_fn (fndecl, finish_function (false));
expand_or_defer_fn (DECL_PRE_FN (fndecl));
finished_pre = finish_function (false);
expand_or_defer_fn (finished_pre);
}
if (DECL_POST_FN (fndecl) && DECL_INITIAL (fndecl) != error_mark_node
&& DECL_POST_FN (fndecl) != error_mark_node)
......@@ -17688,9 +17685,11 @@ finish_function_contracts (tree fndecl, bool is_inline)
tree res = DECL_UNCHECKED_RESULT (fndecl);
if (res)
finish_return_stmt (res);
set_post_fn (fndecl, finish_function (false));
expand_or_defer_fn (DECL_POST_FN (fndecl));
finished_post = finish_function (false);
expand_or_defer_fn (finished_post);
}
set_contract_functions (fndecl, finished_pre, finished_post);
}
......
......@@ -2554,7 +2554,7 @@ static tree cp_parser_nested_requirement
/* Contracts Extensions */
static void cp_parser_contracts
(cp_parser *, tree, const cp_declarator *, bool);
(cp_parser *, tree, const cp_declarator *);
static tree cp_parser_contract_condition
(cp_parser *, tree, tree);
static tree cp_parser_cache_contract_condition
......@@ -15412,8 +15412,7 @@ cp_parser_contract_condition (cp_parser *parser, tree contract,
DECL. */
static void
cp_parser_contracts (cp_parser *parser, tree decl,
const cp_declarator *declarator, bool defer_p = false)
cp_parser_contracts (cp_parser *parser, tree decl, const cp_declarator *declarator)
{
if (!flag_contracts) return;
if (!decl || decl == error_mark_node || !declarator) return;
......@@ -15422,22 +15421,7 @@ cp_parser_contracts (cp_parser *parser, tree decl,
return;
merge_contracts (decl, fn);
/* We need to be told to defer in the case that this is a friend decl inside
* befriending class T where the friend is a member function of another class:
* struct T;
* struct S {
* int f(int n, T *t);
* };
* struct T {
* friend int S::f(int n, T *t)
* [[ pre: t->x > 0 ]];
* int x;
* };
* FIXME is there some way to query this so we can handle it inside
* cp_parser_late_parsing_for_contracts?
*/
if (!defer_p)
cp_parser_late_parsing_for_contracts (parser, decl);
cp_parser_late_parsing_for_contracts (parser, decl);
}
/* Parse an (optional) ctor-initializer.
......@@ -25654,8 +25638,7 @@ cp_parser_member_declaration (cp_parser* parser)
initializer, /*init_const_expr_p=*/true,
asm_specification, attributes);
cp_parser_contracts (parser, decl, declarator,
/*defer_p=*/friend_p);
cp_parser_contracts (parser, decl, declarator);
/* If we've declared a member function with contracts, ensure we
do late parsing for the contracts even if we have no function
body to parse at that time. */
......@@ -30193,7 +30176,7 @@ cp_parser_save_member_function_body (cp_parser* parser,
/* Create the FUNCTION_DECL. */
fn = grokmethod (decl_specifiers, declarator, attributes);
cp_parser_contracts (parser, fn, declarator, /*defer_p=*/true);
cp_parser_contracts (parser, fn, declarator);
cp_finalize_omp_declare_simd (parser, fn);
cp_finalize_oacc_routine (parser, fn, true);
/* If something went badly wrong, bail out now. */
......@@ -30570,11 +30553,11 @@ cp_parser_late_parsing_for_contracts (cp_parser *parser, tree function)
&& TYPE_BEING_DEFINED (DECL_CONTEXT (function)))
return; /* Defer further. */
/* FIXME we need to check that this works correctly for nested classes... */
/* Similar to member functions, we cannot parse the contracts friend
functions when we're inside the befriending type or otherwise
incomplete. This is partially handled by our caller. */
if (DECL_FRIEND_P (function) && current_class_type
&& TYPE_BEING_DEFINED (current_class_type))
incomplete. */
if (current_class_type && TYPE_BEING_DEFINED (current_class_type))
return;
build_contract_function_decls (function);
......@@ -13996,15 +13996,11 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
if (DECL_PRE_FN (t))
{
set_pre_fn (r, tsubst_decl (DECL_PRE_FN (t), args,
tf_warning_or_error));
DECL_ORIGINAL_FN (DECL_PRE_FN (r)) = r;
}
if (DECL_POST_FN (t))
{
set_post_fn (r, tsubst_decl (DECL_POST_FN (t), args,
tf_warning_or_error));
DECL_ORIGINAL_FN (DECL_POST_FN (r)) = r;
tree r_pre = tsubst_decl (DECL_PRE_FN (t), args,
tf_warning_or_error);
tree r_post = tsubst_decl (DECL_POST_FN (t), args,
tf_warning_or_error);
set_contract_functions (r, r_pre, r_post);
}
return r;
}
......@@ -20945,22 +20941,6 @@ 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))
......@@ -25227,8 +25207,11 @@ 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));
/* FIXME DECL_PRE_FN/DECL_POST_FN/DECL_ORIGINAL_FN? */
set_decl_contracts (decl, DECL_CONTRACTS (code_pattern));
/* If a specialiaztion removed contracts, ensure we're not generating
* calls to pre/post functions that will never be generated. */
if (!DECL_PRE_FN (code_pattern))
set_contract_functions (decl, NULL_TREE, NULL_TREE);
maybe_instantiate_noexcept (decl, tf_error);
}
......
......@@ -587,51 +587,68 @@ remove_contract_attributes (tree fndecl)
DECL_ATTRIBUTES (fndecl) = list;
}
/* Map from FUNCTION_DECL to a VAR_DECL used to capture the result of the
unchecked function inside the checked function. This is also used to parse
postcondition conditions that refer to the return value. */
/* 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;
static GTY(()) hash_map<tree, tree> *decl_original_fn;
/* FIXME gc issues: doesn't look through tree list?
static GTY(()) hash_map<tree, tree> *decl_contract_fns; */
/* Lookup and return the PRE_FN of FNDECL, or NULL_TREE if it does not exist. */
tree
get_pre_fn (tree fndecl)
{
if (!fndecl || fndecl == error_mark_node) return NULL_TREE;
hash_map_maybe_create<hm_ggc> (decl_pre_fn);
tree *result = decl_pre_fn->get (fndecl);
return result ? *result : NULL_TREE;
}
void
set_pre_fn (tree fndecl, tree pre)
{
if (!fndecl || fndecl == error_mark_node)
return;
hash_map_maybe_create<hm_ggc> (decl_pre_fn)->remove (fndecl);
if (pre)
decl_pre_fn->put (fndecl, pre);
}
/* Lookup and return the POST_FN of FNDECL, or NULL_TREE if it does not exist. */
tree
get_post_fn (tree fndecl)
{
if (!fndecl || fndecl == error_mark_node) return NULL_TREE;
hash_map_maybe_create<hm_ggc> (decl_post_fn);
tree *result = decl_post_fn->get (fndecl);
return result ? *result : NULL_TREE;
}
/* Set the PRE_FN and POST_FN for FNDECL. */
void
set_post_fn (tree fndecl, tree post)
set_contract_functions (tree fndecl, tree pre_fn, tree post_fn)
{
if (!fndecl || fndecl == error_mark_node)
return;
hash_map_maybe_create<hm_ggc> (decl_pre_fn)->remove (fndecl);
if (pre_fn)
decl_pre_fn->put (fndecl, pre_fn);
hash_map_maybe_create<hm_ggc> (decl_post_fn)->remove (fndecl);
if (post)
decl_post_fn->put (fndecl, post);
if (post_fn)
decl_post_fn->put (fndecl, post_fn);
}
/* Lookup and return the original guarded function of a contract function
FNDECL, or NULL_TREE if it does not exist. */
tree
get_contracts_original_fn (tree fndecl)
{
hash_map_maybe_create<hm_ggc> (decl_original_fn);
tree *result = decl_original_fn->get (fndecl);
return result ? *result : NULL_TREE;
}
/* Set the original fn for a contract function FNDECL. */
void
set_contracts_original_fn (tree fndecl, tree original_fn)
{
hash_map_maybe_create<hm_ggc> (decl_original_fn)->remove (fndecl);
if (original_fn)
decl_original_fn->put (fndecl, original_fn);
}
/* Lookup and return the unchecked result of the guarded FUNCTION_DECL FNDECL,
or NULL_TREE if it doesn't (yet) exist. */
......@@ -966,14 +983,15 @@ build_contract_function_decls (tree fndecl)
if (DECL_CONSTRUCTOR_P (fndecl) || DECL_DESTRUCTOR_P (fndecl))
return;
if (DECL_PRE_FN (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. */
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));
set_contract_functions (fndecl,
build_contract_functor_declaration (fndecl, /*pre=*/true),
build_contract_functor_declaration (fndecl, /*pre=*/false));
}
/* Begin a new scope for the postcondition. */
......
// { 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 t, double s, R r)
{
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, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 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 10 G5<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .*(\n|\r\n|\r)*" }
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .*(\n|\r\n|\r)*" }
// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }