Skip to content

Commit 810bcc0

Browse files
committed
c++: constrained hidden friends [PR109751]
r13-4035 avoided a problem with overloading of constrained hidden friends by checking satisfaction, but checking satisfaction early is inconsistent with the usual late checking and can lead to hard errors, so let's not do that after all. We were wrongly treating the different instantiations of the same friend template as the same function because maybe_substitute_reqs_for was failing to actually substitute in the case of a non-template friend. But we don't actually need to do the substitution anyway, because [temp.friend] says that such a friend can't be the same as any other declaration. After fixing that, instead of a redefinition error we got an ambiguous overload error, fixed by allowing constrained hidden friends to coexist until overload resolution, at which point they probably won't be in the same ADL overload set anyway. And we avoid mangling collisions by following the proposed mangling for these friends as a member function with an extra 'F' before the name. I demangle this by just adding [friend] to the name of the function because it's not feasible to reconstruct the actual scope of the function since the mangling ABI doesn't distinguish between class and namespace scopes. PR c++/109751 gcc/cp/ChangeLog: * cp-tree.h (member_like_constrained_friend_p): Declare. * decl.cc (member_like_constrained_friend_p): New. (function_requirements_equivalent_p): Check it. (duplicate_decls): Check it. (grokfndecl): Check friend template constraints. * mangle.cc (decl_mangling_context): Check it. (write_unqualified_name): Check it. * pt.cc (uses_outer_template_parms_in_constraints): Fix for friends. (tsubst_friend_function): Don't check satisfaction. include/ChangeLog: * demangle.h (enum demangle_component_type): Add DEMANGLE_COMPONENT_FRIEND. libiberty/ChangeLog: * cp-demangle.c (d_make_comp): Handle DEMANGLE_COMPONENT_FRIEND. (d_count_templates_scopes): Likewise. (d_print_comp_inner): Likewise. (d_unqualified_name): Handle member-like friend mangling. * testsuite/demangle-expected: Add test. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-friend11.C: Now works. Add template. * g++.dg/cpp2a/concepts-friend15.C: New test.
1 parent 3571cc9 commit 810bcc0

File tree

10 files changed

+145
-16
lines changed

10 files changed

+145
-16
lines changed

gcc/cp/cp-tree.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6859,6 +6859,7 @@ extern void note_break_stmt (void);
68596859
extern bool note_iteration_stmt_body_start (void);
68606860
extern void note_iteration_stmt_body_end (bool);
68616861
extern void determine_local_discriminator (tree);
6862+
extern bool member_like_constrained_friend_p (tree);
68626863
extern bool fns_correspond (tree, tree);
68636864
extern int decls_match (tree, tree, bool = true);
68646865
extern bool maybe_version_functions (tree, tree, bool);
@@ -7385,7 +7386,7 @@ extern tree lookup_template_function (tree, tree);
73857386
extern tree lookup_template_variable (tree, tree, tsubst_flags_t);
73867387
extern bool uses_template_parms (tree);
73877388
extern bool uses_template_parms_level (tree, int);
7388-
extern bool uses_outer_template_parms_in_constraints (tree);
7389+
extern bool uses_outer_template_parms_in_constraints (tree, tree = NULL_TREE);
73897390
extern bool need_generic_capture (void);
73907391
extern tree instantiate_class_template (tree);
73917392
extern tree instantiate_template (tree, tree, tsubst_flags_t);

gcc/cp/decl.cc

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,30 @@ determine_local_discriminator (tree decl)
951951
}
952952

953953

954+
/* True if DECL is a constrained hidden friend as per [temp.friend]/9:
955+
956+
A non-template friend declaration with a requires-clause shall be a
957+
definition. A friend function template with a constraint that depends on a
958+
template parameter from an enclosing template shall be a definition. Such a
959+
constrained friend function or function template declaration does not
960+
declare the same function or function template as a declaration in any other
961+
scope.
962+
963+
The ABI calls this a "member-like constrained friend" and mangles it like a
964+
member function to avoid collisions. */
965+
966+
bool
967+
member_like_constrained_friend_p (tree decl)
968+
{
969+
return (TREE_CODE (decl) == FUNCTION_DECL
970+
&& DECL_UNIQUE_FRIEND_P (decl)
971+
&& DECL_FRIEND_CONTEXT (decl)
972+
&& get_constraints (decl)
973+
&& (!DECL_TEMPLATE_INFO (decl)
974+
|| !PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
975+
|| (uses_outer_template_parms_in_constraints
976+
(most_general_template (decl)))));
977+
}
954978

955979
/* Returns true if functions FN1 and FN2 have equivalent trailing
956980
requires clauses. */
@@ -968,6 +992,13 @@ function_requirements_equivalent_p (tree newfn, tree oldfn)
968992
return cp_tree_equal (req1, req2);
969993
}
970994

995+
/* [temp.friend]/9 "Such a constrained friend function does not declare the
996+
same function as a declaration in any other scope." So no need to
997+
actually compare the requirements. */
998+
if (member_like_constrained_friend_p (newfn)
999+
|| member_like_constrained_friend_p (oldfn))
1000+
return false;
1001+
9711002
/* Compare only trailing requirements. */
9721003
tree reqs1 = get_trailing_function_requirements (newfn);
9731004
tree reqs2 = get_trailing_function_requirements (oldfn);
@@ -1936,6 +1967,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
19361967
are not ambiguous. */
19371968
else if ((!DECL_FUNCTION_VERSIONED (newdecl)
19381969
&& !DECL_FUNCTION_VERSIONED (olddecl))
1970+
/* Let constrained hidden friends coexist for now, we'll
1971+
check satisfaction later. */
1972+
&& !member_like_constrained_friend_p (newdecl)
1973+
&& !member_like_constrained_friend_p (olddecl)
19391974
// The functions have the same parameter types.
19401975
&& compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)),
19411976
TYPE_ARG_TYPES (TREE_TYPE (olddecl)))
@@ -10305,16 +10340,28 @@ grokfndecl (tree ctype,
1030510340
ci = NULL_TREE;
1030610341
}
1030710342
/* C++20 CA378: Remove non-templated constrained functions. */
10343+
/* [temp.friend]/9 A non-template friend declaration with a
10344+
requires-clause shall be a definition. A friend function template with
10345+
a constraint that depends on a template parameter from an enclosing
10346+
template shall be a definition. */
1030810347
if (ci
1030910348
&& (block_local
1031010349
|| (!flag_concepts_ts
1031110350
&& (!processing_template_decl
1031210351
|| (friendp && !memtmpl && !funcdef_flag)))))
1031310352
{
10314-
error_at (location, "constraints on a non-templated function");
10353+
if (!friendp || !processing_template_decl)
10354+
error_at (location, "constraints on a non-templated function");
10355+
else
10356+
error_at (location, "constrained non-template friend declaration"
10357+
" must be a definition");
1031510358
ci = NULL_TREE;
1031610359
}
1031710360
set_constraints (decl, ci);
10361+
if (ci && friendp && memtmpl && !funcdef_flag
10362+
&& uses_outer_template_parms_in_constraints (decl, ctx))
10363+
error_at (location, "friend function template with constraints that "
10364+
"depend on outer template parameters must be a definition");
1031810365
}
1031910366

1032010367
if (TREE_CODE (type) == METHOD_TYPE)

gcc/cp/mangle.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,9 @@ decl_mangling_context (tree decl)
963963

964964
tcontext = CP_DECL_CONTEXT (decl);
965965

966+
if (member_like_constrained_friend_p (decl))
967+
tcontext = DECL_FRIEND_CONTEXT (decl);
968+
966969
/* Ignore the artificial declare reduction functions. */
967970
if (tcontext
968971
&& TREE_CODE (tcontext) == FUNCTION_DECL
@@ -1419,6 +1422,7 @@ anon_aggr_naming_decl (tree type)
14191422
::= [<module-name>] <source-name>
14201423
::= [<module-name>] <unnamed-type-name>
14211424
::= <local-source-name>
1425+
::= F <source-name> # member-like constrained friend
14221426
14231427
<local-source-name> ::= L <source-name> <discriminator> */
14241428

@@ -1476,6 +1480,12 @@ write_unqualified_name (tree decl)
14761480
else if (DECL_DECLARES_FUNCTION_P (decl))
14771481
{
14781482
found = true;
1483+
1484+
/* A constrained hidden friend is mangled like a member function, with
1485+
the name prefixed by 'F'. */
1486+
if (member_like_constrained_friend_p (decl))
1487+
write_char ('F');
1488+
14791489
if (DECL_CONSTRUCTOR_P (decl))
14801490
write_special_name_constructor (decl);
14811491
else if (DECL_DESTRUCTOR_P (decl))

gcc/cp/pt.cc

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11049,14 +11049,21 @@ uses_outer_template_parms (tree decl)
1104911049
from its enclosing scope. */
1105011050

1105111051
bool
11052-
uses_outer_template_parms_in_constraints (tree decl)
11052+
uses_outer_template_parms_in_constraints (tree decl, tree ctx/*=NULL_TREE*/)
1105311053
{
1105411054
tree ci = get_constraints (decl);
1105511055
if (ci)
1105611056
ci = CI_ASSOCIATED_CONSTRAINTS (ci);
1105711057
if (!ci)
1105811058
return false;
11059-
int depth = template_class_depth (CP_DECL_CONTEXT (decl));
11059+
if (!ctx)
11060+
{
11061+
if (tree fc = DECL_FRIEND_CONTEXT (decl))
11062+
ctx = fc;
11063+
else
11064+
ctx = CP_DECL_CONTEXT (decl);
11065+
}
11066+
int depth = template_class_depth (ctx);
1106011067
if (depth == 0)
1106111068
return false;
1106211069
return for_each_template_parm (ci, template_parm_outer_level,
@@ -11393,9 +11400,6 @@ tsubst_friend_function (tree decl, tree args)
1139311400
not_tmpl = DECL_TEMPLATE_RESULT (new_friend);
1139411401
new_friend_result_template_info = DECL_TEMPLATE_INFO (not_tmpl);
1139511402
}
11396-
else if (!constraints_satisfied_p (new_friend))
11397-
/* Only define a constrained hidden friend when satisfied. */
11398-
return error_mark_node;
1139911403

1140011404
/* Inside pushdecl_namespace_level, we will push into the
1140111405
current namespace. However, the friend function should go
Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
// CWG2596
22
// { dg-do compile { target c++20 } }
3+
// { dg-additional-options -fno-implicit-constexpr }
34

45
struct Base {};
56

6-
int foo(Base&) { return 0; } // #0
7-
87
template<int N>
98
struct S : Base {
109
friend int foo(Base&) requires (N == 1) { return 1; } // #1
11-
// friend int foo(Base&) requires (N == 2) { return 3; } // #2
10+
friend int foo(Base&) requires (N == 2) { return 3; } // #2
11+
12+
template <class T>
13+
friend int bar(Base&) requires (N == 1) { return 1; }
14+
template <class T>
15+
friend int bar(Base&) requires (N == 2) { return 3; }
1216
};
1317

1418
S<1> s1;
15-
S<2> s2; // OK, no conflict between #1 and #0
16-
int x = foo(s1); // { dg-error "ambiguous" }
17-
int y = foo(s2); // OK, selects #0
19+
S<2> s2; // OK, no conflict between #1 and #2
20+
21+
// { dg-final { scan-assembler "_ZN1SILi1EEF3fooER4Base" } }
22+
int x = foo(s1); // OK, selects #1
23+
// { dg-final { scan-assembler "_ZN1SILi2EEF3fooER4Base" } }
24+
int y = foo(s2); // OK, selects #2
1825

19-
// ??? currently the foos all mangle the same, so comment out #2
20-
// and only test that #1 isn't multiply defined and overloads with #0.
21-
// The 2596 example does not include #0 and expects both calls to work.
26+
// { dg-final { scan-assembler "_ZN1SILi1EEF3barIiEEiR4Base" } }
27+
int x2 = bar<int>(s1); // OK, selects #1
28+
// { dg-final { scan-assembler "_ZN1SILi2EEF3barIiEEiR4Base" } }
29+
int y2 = bar<int>(s2); // OK, selects #2
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// CWG2596
2+
// { dg-do compile { target c++20 } }
3+
4+
struct Base {};
5+
6+
template<int N>
7+
struct S : Base {
8+
friend int foo(Base&) requires (N == 1); // { dg-error "must be a definition" }
9+
friend int foo(Base&) requires (N == 2); // { dg-error "must be a definition" }
10+
11+
template <class T>
12+
friend int bar(Base&) requires (N == 1); // { dg-error "must be a definition" }
13+
template <class T>
14+
friend int bar(Base&) requires (N == 2); // { dg-error "must be a definition" }
15+
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// PR c++/109751
2+
// { dg-do compile { target c++20 } }
3+
4+
template<typename _Tp> concept cmpeq
5+
= requires(_Tp __t, _Tp __u) { { __u != __t } ; };
6+
7+
template<typename D>
8+
struct iterator_interface
9+
{
10+
friend constexpr bool operator>=(D lhs, D rhs)
11+
requires cmpeq<D> { return true; }
12+
};
13+
14+
template<typename T>
15+
struct iterator : iterator_interface<iterator<T>>
16+
{
17+
bool operator==(iterator) const;
18+
iterator &operator++();
19+
iterator &operator++(int);
20+
};
21+
22+
static_assert(cmpeq<iterator<int>>);

include/demangle.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,8 @@ enum demangle_component_type
448448
DEMANGLE_COMPONENT_TRANSACTION_SAFE,
449449
/* A cloned function. */
450450
DEMANGLE_COMPONENT_CLONE,
451+
/* A member-like friend function. */
452+
DEMANGLE_COMPONENT_FRIEND,
451453
DEMANGLE_COMPONENT_NOEXCEPT,
452454
DEMANGLE_COMPONENT_THROW_SPEC,
453455

libiberty/cp-demangle.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,7 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
10361036
case DEMANGLE_COMPONENT_TEMPLATE_NON_TYPE_PARM:
10371037
case DEMANGLE_COMPONENT_TEMPLATE_TEMPLATE_PARM:
10381038
case DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM:
1039+
case DEMANGLE_COMPONENT_FRIEND:
10391040
if (left == NULL)
10401041
return NULL;
10411042
break;
@@ -1681,6 +1682,7 @@ d_maybe_module_name (struct d_info *di, struct demangle_component **name)
16811682
/* <unqualified-name> ::= [<module-name>] <operator-name> [<abi-tags>]
16821683
::= [<module-name>] <ctor-dtor-name> [<abi-tags>]
16831684
::= [<module-name>] <source-name> [<abi-tags>]
1685+
::= [<module-name>] F <source-name> [<abi-tags>]
16841686
::= [<module-name>] <local-source-name> [<abi-tags>]
16851687
::= [<module-name>] DC <source-name>+ E [<abi-tags>]
16861688
<local-source-name> ::= L <source-name> <discriminator> [<abi-tags>]
@@ -1692,11 +1694,18 @@ d_unqualified_name (struct d_info *di, struct demangle_component *scope,
16921694
{
16931695
struct demangle_component *ret;
16941696
char peek;
1697+
int member_like_friend = 0;
16951698

16961699
if (!d_maybe_module_name (di, &module))
16971700
return NULL;
16981701

16991702
peek = d_peek_char (di);
1703+
if (peek == 'F')
1704+
{
1705+
member_like_friend = 1;
1706+
d_advance (di, 1);
1707+
peek = d_peek_char (di);
1708+
}
17001709
if (IS_DIGIT (peek))
17011710
ret = d_source_name (di);
17021711
else if (IS_LOWER (peek))
@@ -1773,6 +1782,8 @@ d_unqualified_name (struct d_info *di, struct demangle_component *scope,
17731782
ret = d_make_comp (di, DEMANGLE_COMPONENT_MODULE_ENTITY, ret, module);
17741783
if (d_peek_char (di) == 'B')
17751784
ret = d_abi_tags (di, ret);
1785+
if (member_like_friend)
1786+
ret = d_make_comp (di, DEMANGLE_COMPONENT_FRIEND, ret, NULL);
17761787
if (scope)
17771788
ret = d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, scope, ret);
17781789

@@ -4459,6 +4470,7 @@ d_count_templates_scopes (struct d_print_info *dpi,
44594470
case DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS:
44604471
case DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS:
44614472
case DEMANGLE_COMPONENT_MODULE_ENTITY:
4473+
case DEMANGLE_COMPONENT_FRIEND:
44624474
d_count_templates_scopes (dpi, d_left (dc));
44634475
break;
44644476

@@ -6197,6 +6209,11 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
61976209
d_append_char (dpi, ']');
61986210
return;
61996211

6212+
case DEMANGLE_COMPONENT_FRIEND:
6213+
d_print_comp (dpi, options, d_left (dc));
6214+
d_append_string (dpi, "[friend]");
6215+
return;
6216+
62006217
case DEMANGLE_COMPONENT_TEMPLATE_HEAD:
62016218
{
62026219
d_append_char (dpi, '<');

libiberty/testsuite/demangle-expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,3 +1689,6 @@ X::operator Z<int><int>()::y
16891689

16901690
_ZZN1XIfEcv1ZIT_EIiEEvE1y
16911691
X<float>::operator Z<int><int>()::y
1692+
1693+
_ZN1SILi1EEF3barIiEEiR4Base
1694+
int S<1>::bar[friend]<int>(Base&)

0 commit comments

Comments
 (0)