Comparing friend contracts to a previous declaration was awkward
because at the point of duplicate_decls we haven't parsed the new ones yet,
and we're about to throw away one of the decls. But conveniently, there's
already defer_guarded_contract_match to handle this.
This reverts commit
9c0d8bfebc32d5e2c35ed61753440fb175a87dcf.
gcc/cp/ChangeLog:
* contracts.cc (check_for_mismatched_contracts):
Only check new_contract for deferred.
(match_deferred_contracts): Set processing_template_decl.
(duplicate_contracts): Call defer_guarded_contract_match
for friend decl. Handle templates.
* decl.cc (duplicate_decls): Use it for templates.
* parser.h (struct cp_parser): Remove declaring_friend_p.
* parser.cc (cp_parser_new): Don't clear it.
(cp_parser_direct_declarator): Don't set it.
(cp_parser_contract_attribute_spec): Don't check it.
* contracts.h (match_contract_conditions): Remove.
gcc/testsuite/ChangeLog:
* g++.dg/contracts/contracts-friend1.C: Revert.
* g++.dg/contracts/contracts-nested-class1.C: Revert.
* g++.dg/contracts/contracts-redecl7.C: Revert.
* g++.dg/contracts/contracts-redecl8.C: Revert.
return true;
}
- /* Two deferred contracts tentatively match. */
- if (CONTRACT_CONDITION_DEFERRED_P (old_contract)
- && CONTRACT_CONDITION_DEFERRED_P (new_contract))
+ /* 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
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))
{
}
/* A subroutine of duplicate_decls. Diagnose issues in the redeclaration of
- guarded functions. Note that attributes on new friend declarations have not
- been processed yet, so we take those from the global above. */
+ 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);
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
extern tree get_precondition_function (tree);
extern tree get_postcondition_function (tree);
extern void duplicate_contracts (tree, tree);
-extern bool match_contract_conditions (location_t, tree, location_t, tree, contract_matching_context);
extern void match_deferred_contracts (tree);
extern void defer_guarded_contract_match (tree, tree, tree);
extern bool diagnose_misapplied_contracts (tree);
gcc_assert (!DECL_TEMPLATE_SPECIALIZATIONS (newdecl));
/* Make sure the contracts are equivalent. */
- tree old_contracts = DECL_CONTRACTS (old_result);
- tree new_contracts = DECL_CONTRACTS (new_result);
- if (old_contracts && new_contracts)
- {
- match_contract_conditions (DECL_SOURCE_LOCATION (old_result),
- old_contracts,
- DECL_SOURCE_LOCATION (new_result),
- new_contracts,
- cmc_declaration);
- }
+ duplicate_contracts (newdecl, olddecl);
/* Remove contracts from old_result so they aren't appended to
old_result by the merge function. */
/* We are not processing a declarator. */
parser->in_declarator_p = false;
- /* We are not parsing a friend declaration. */
- parser->declaring_friend_p = false;
-
/* We are not processing a template-argument-list. */
parser->in_template_argument_list_p = false;
= cp_parser_exception_specification_opt (parser,
flags);
- bool saved_declaring_friend_p = parser->declaring_friend_p;
- parser->declaring_friend_p = friend_p;
attrs = cp_parser_std_attribute_spec_seq (parser);
- parser->declaring_friend_p = saved_declaring_friend_p;
cp_omp_declare_simd_data odsd;
if ((flag_openmp || flag_openmp_simd)
cp_parser_require (parser, CPP_COLON, RT_COLON);
- /* Defer the parsing of pre/post contracts inside class definitions.
- Note that friends are not member functions and thus not in the complete
- class context. */
+ /* Defer the parsing of pre/post contracts inside class definitions. */
tree contract;
- if (!assertion_p
- && current_class_type
- && TYPE_BEING_DEFINED (current_class_type)
- && !parser->declaring_friend_p)
+ 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
direct-declarator. */
bool in_declarator_p;
- /* TRUE if the decl-specifier-seq preceding a declarator includes
- the 'friend' specifier. This prevents attributes on friend function
- declarations from being parsed in the complete class context. */
- /* ??? But they should be; maybe use defer_guarded_contract_match? */
- bool declaring_friend_p;
-
/* TRUE if we are presently parsing a template-argument-list. */
bool in_template_argument_list_p;
// { 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 ]];
X x;
fn(x); // no contract
+ fn0(x);
fn2(x);
X::fns0(x);
return 0;
}
-// { dg-output "default std::handle_contract_violation called: .*.C 17 fn2 .*(\n|\r\n|\r)*" }
-// { dg-output "default std::handle_contract_violation called: .*.C 7 X::fns0 .*(\n|\r\n|\r)*" }
-// { dg-output "default std::handle_contract_violation called: .*.C 8 X::fns1 .*(\n|\r\n|\r)*" }
-// { dg-output "default std::handle_contract_violation called: .*.C 19 X::fns2 .*(\n|\r\n|\r)*" }
+// { 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)*" }
// error about 'p' not being declared because the contracts haven't been
// unified or remapped.
friend void gfn(int p) [[ pre: p > 0 ]];
- friend void gfn(int q) [[ pre: q > 1 ]]; // { dg-error "mismatched contract" }
+ friend void gfn(int q) [[ pre: q > 1 ]]; // { dg-error "'q' was not declared" }
// This should be okay.
friend void gfn2(int q);
static int bob;
};
-int Outer::bob{-1};
\ No newline at end of file
+int Outer::bob{-1};
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);
s.now(-10, &t);
now(-20, &t);
+ later(-21, &t);
both(-22, &t);
+ hidden(-23, &t);
+ hidden2(-24, &t);
return 0;
}
// { 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 both .*(\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)*" }
return 0;
}
- // friends are not members and thus not in the complete class context.
friend int hidden(int x, T *t)
- [[ pre: x > 1 ]] [[ pre: t->pri > 0 ]] // { dg-error "has no member" }
+ [[ pre: x > 1 ]] [[ pre: t->pri > 0 ]]
{
return x;
}