mirror of
https://github.com/gcc-mirror/gcc.git
synced 2024-11-21 13:40:47 +00:00
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.
This commit is contained in:
parent
3571cc9351
commit
810bcc0015
@ -6859,6 +6859,7 @@ extern void note_break_stmt (void);
|
||||
extern bool note_iteration_stmt_body_start (void);
|
||||
extern void note_iteration_stmt_body_end (bool);
|
||||
extern void determine_local_discriminator (tree);
|
||||
extern bool member_like_constrained_friend_p (tree);
|
||||
extern bool fns_correspond (tree, tree);
|
||||
extern int decls_match (tree, tree, bool = true);
|
||||
extern bool maybe_version_functions (tree, tree, bool);
|
||||
@ -7385,7 +7386,7 @@ extern tree lookup_template_function (tree, tree);
|
||||
extern tree lookup_template_variable (tree, tree, tsubst_flags_t);
|
||||
extern bool uses_template_parms (tree);
|
||||
extern bool uses_template_parms_level (tree, int);
|
||||
extern bool uses_outer_template_parms_in_constraints (tree);
|
||||
extern bool uses_outer_template_parms_in_constraints (tree, tree = NULL_TREE);
|
||||
extern bool need_generic_capture (void);
|
||||
extern tree instantiate_class_template (tree);
|
||||
extern tree instantiate_template (tree, tree, tsubst_flags_t);
|
||||
|
@ -951,6 +951,30 @@ determine_local_discriminator (tree decl)
|
||||
}
|
||||
|
||||
|
||||
/* True if DECL is a constrained hidden friend as per [temp.friend]/9:
|
||||
|
||||
A non-template friend declaration with a requires-clause shall be a
|
||||
definition. A friend function template with a constraint that depends on a
|
||||
template parameter from an enclosing template shall be a definition. Such a
|
||||
constrained friend function or function template declaration does not
|
||||
declare the same function or function template as a declaration in any other
|
||||
scope.
|
||||
|
||||
The ABI calls this a "member-like constrained friend" and mangles it like a
|
||||
member function to avoid collisions. */
|
||||
|
||||
bool
|
||||
member_like_constrained_friend_p (tree decl)
|
||||
{
|
||||
return (TREE_CODE (decl) == FUNCTION_DECL
|
||||
&& DECL_UNIQUE_FRIEND_P (decl)
|
||||
&& DECL_FRIEND_CONTEXT (decl)
|
||||
&& get_constraints (decl)
|
||||
&& (!DECL_TEMPLATE_INFO (decl)
|
||||
|| !PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
|
||||
|| (uses_outer_template_parms_in_constraints
|
||||
(most_general_template (decl)))));
|
||||
}
|
||||
|
||||
/* Returns true if functions FN1 and FN2 have equivalent trailing
|
||||
requires clauses. */
|
||||
@ -968,6 +992,13 @@ function_requirements_equivalent_p (tree newfn, tree oldfn)
|
||||
return cp_tree_equal (req1, req2);
|
||||
}
|
||||
|
||||
/* [temp.friend]/9 "Such a constrained friend function does not declare the
|
||||
same function as a declaration in any other scope." So no need to
|
||||
actually compare the requirements. */
|
||||
if (member_like_constrained_friend_p (newfn)
|
||||
|| member_like_constrained_friend_p (oldfn))
|
||||
return false;
|
||||
|
||||
/* Compare only trailing requirements. */
|
||||
tree reqs1 = get_trailing_function_requirements (newfn);
|
||||
tree reqs2 = get_trailing_function_requirements (oldfn);
|
||||
@ -1936,6 +1967,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
|
||||
are not ambiguous. */
|
||||
else if ((!DECL_FUNCTION_VERSIONED (newdecl)
|
||||
&& !DECL_FUNCTION_VERSIONED (olddecl))
|
||||
/* Let constrained hidden friends coexist for now, we'll
|
||||
check satisfaction later. */
|
||||
&& !member_like_constrained_friend_p (newdecl)
|
||||
&& !member_like_constrained_friend_p (olddecl)
|
||||
// The functions have the same parameter types.
|
||||
&& compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)),
|
||||
TYPE_ARG_TYPES (TREE_TYPE (olddecl)))
|
||||
@ -10305,16 +10340,28 @@ grokfndecl (tree ctype,
|
||||
ci = NULL_TREE;
|
||||
}
|
||||
/* C++20 CA378: Remove non-templated constrained functions. */
|
||||
/* [temp.friend]/9 A non-template friend declaration with a
|
||||
requires-clause shall be a definition. A friend function template with
|
||||
a constraint that depends on a template parameter from an enclosing
|
||||
template shall be a definition. */
|
||||
if (ci
|
||||
&& (block_local
|
||||
|| (!flag_concepts_ts
|
||||
&& (!processing_template_decl
|
||||
|| (friendp && !memtmpl && !funcdef_flag)))))
|
||||
{
|
||||
error_at (location, "constraints on a non-templated function");
|
||||
if (!friendp || !processing_template_decl)
|
||||
error_at (location, "constraints on a non-templated function");
|
||||
else
|
||||
error_at (location, "constrained non-template friend declaration"
|
||||
" must be a definition");
|
||||
ci = NULL_TREE;
|
||||
}
|
||||
set_constraints (decl, ci);
|
||||
if (ci && friendp && memtmpl && !funcdef_flag
|
||||
&& uses_outer_template_parms_in_constraints (decl, ctx))
|
||||
error_at (location, "friend function template with constraints that "
|
||||
"depend on outer template parameters must be a definition");
|
||||
}
|
||||
|
||||
if (TREE_CODE (type) == METHOD_TYPE)
|
||||
|
@ -963,6 +963,9 @@ decl_mangling_context (tree decl)
|
||||
|
||||
tcontext = CP_DECL_CONTEXT (decl);
|
||||
|
||||
if (member_like_constrained_friend_p (decl))
|
||||
tcontext = DECL_FRIEND_CONTEXT (decl);
|
||||
|
||||
/* Ignore the artificial declare reduction functions. */
|
||||
if (tcontext
|
||||
&& TREE_CODE (tcontext) == FUNCTION_DECL
|
||||
@ -1419,6 +1422,7 @@ anon_aggr_naming_decl (tree type)
|
||||
::= [<module-name>] <source-name>
|
||||
::= [<module-name>] <unnamed-type-name>
|
||||
::= <local-source-name>
|
||||
::= F <source-name> # member-like constrained friend
|
||||
|
||||
<local-source-name> ::= L <source-name> <discriminator> */
|
||||
|
||||
@ -1476,6 +1480,12 @@ write_unqualified_name (tree decl)
|
||||
else if (DECL_DECLARES_FUNCTION_P (decl))
|
||||
{
|
||||
found = true;
|
||||
|
||||
/* A constrained hidden friend is mangled like a member function, with
|
||||
the name prefixed by 'F'. */
|
||||
if (member_like_constrained_friend_p (decl))
|
||||
write_char ('F');
|
||||
|
||||
if (DECL_CONSTRUCTOR_P (decl))
|
||||
write_special_name_constructor (decl);
|
||||
else if (DECL_DESTRUCTOR_P (decl))
|
||||
|
14
gcc/cp/pt.cc
14
gcc/cp/pt.cc
@ -11049,14 +11049,21 @@ uses_outer_template_parms (tree decl)
|
||||
from its enclosing scope. */
|
||||
|
||||
bool
|
||||
uses_outer_template_parms_in_constraints (tree decl)
|
||||
uses_outer_template_parms_in_constraints (tree decl, tree ctx/*=NULL_TREE*/)
|
||||
{
|
||||
tree ci = get_constraints (decl);
|
||||
if (ci)
|
||||
ci = CI_ASSOCIATED_CONSTRAINTS (ci);
|
||||
if (!ci)
|
||||
return false;
|
||||
int depth = template_class_depth (CP_DECL_CONTEXT (decl));
|
||||
if (!ctx)
|
||||
{
|
||||
if (tree fc = DECL_FRIEND_CONTEXT (decl))
|
||||
ctx = fc;
|
||||
else
|
||||
ctx = CP_DECL_CONTEXT (decl);
|
||||
}
|
||||
int depth = template_class_depth (ctx);
|
||||
if (depth == 0)
|
||||
return false;
|
||||
return for_each_template_parm (ci, template_parm_outer_level,
|
||||
@ -11393,9 +11400,6 @@ tsubst_friend_function (tree decl, tree args)
|
||||
not_tmpl = DECL_TEMPLATE_RESULT (new_friend);
|
||||
new_friend_result_template_info = DECL_TEMPLATE_INFO (not_tmpl);
|
||||
}
|
||||
else if (!constraints_satisfied_p (new_friend))
|
||||
/* Only define a constrained hidden friend when satisfied. */
|
||||
return error_mark_node;
|
||||
|
||||
/* Inside pushdecl_namespace_level, we will push into the
|
||||
current namespace. However, the friend function should go
|
||||
|
@ -1,21 +1,29 @@
|
||||
// CWG2596
|
||||
// { dg-do compile { target c++20 } }
|
||||
// { dg-additional-options -fno-implicit-constexpr }
|
||||
|
||||
struct Base {};
|
||||
|
||||
int foo(Base&) { return 0; } // #0
|
||||
|
||||
template<int N>
|
||||
struct S : Base {
|
||||
friend int foo(Base&) requires (N == 1) { return 1; } // #1
|
||||
// friend int foo(Base&) requires (N == 2) { return 3; } // #2
|
||||
friend int foo(Base&) requires (N == 2) { return 3; } // #2
|
||||
|
||||
template <class T>
|
||||
friend int bar(Base&) requires (N == 1) { return 1; }
|
||||
template <class T>
|
||||
friend int bar(Base&) requires (N == 2) { return 3; }
|
||||
};
|
||||
|
||||
S<1> s1;
|
||||
S<2> s2; // OK, no conflict between #1 and #0
|
||||
int x = foo(s1); // { dg-error "ambiguous" }
|
||||
int y = foo(s2); // OK, selects #0
|
||||
S<2> s2; // OK, no conflict between #1 and #2
|
||||
|
||||
// ??? currently the foos all mangle the same, so comment out #2
|
||||
// and only test that #1 isn't multiply defined and overloads with #0.
|
||||
// The 2596 example does not include #0 and expects both calls to work.
|
||||
// { dg-final { scan-assembler "_ZN1SILi1EEF3fooER4Base" } }
|
||||
int x = foo(s1); // OK, selects #1
|
||||
// { dg-final { scan-assembler "_ZN1SILi2EEF3fooER4Base" } }
|
||||
int y = foo(s2); // OK, selects #2
|
||||
|
||||
// { dg-final { scan-assembler "_ZN1SILi1EEF3barIiEEiR4Base" } }
|
||||
int x2 = bar<int>(s1); // OK, selects #1
|
||||
// { dg-final { scan-assembler "_ZN1SILi2EEF3barIiEEiR4Base" } }
|
||||
int y2 = bar<int>(s2); // OK, selects #2
|
||||
|
15
gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C
Normal file
15
gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C
Normal file
@ -0,0 +1,15 @@
|
||||
// CWG2596
|
||||
// { dg-do compile { target c++20 } }
|
||||
|
||||
struct Base {};
|
||||
|
||||
template<int N>
|
||||
struct S : Base {
|
||||
friend int foo(Base&) requires (N == 1); // { dg-error "must be a definition" }
|
||||
friend int foo(Base&) requires (N == 2); // { dg-error "must be a definition" }
|
||||
|
||||
template <class T>
|
||||
friend int bar(Base&) requires (N == 1); // { dg-error "must be a definition" }
|
||||
template <class T>
|
||||
friend int bar(Base&) requires (N == 2); // { dg-error "must be a definition" }
|
||||
};
|
22
gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C
Normal file
22
gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C
Normal file
@ -0,0 +1,22 @@
|
||||
// PR c++/109751
|
||||
// { dg-do compile { target c++20 } }
|
||||
|
||||
template<typename _Tp> concept cmpeq
|
||||
= requires(_Tp __t, _Tp __u) { { __u != __t } ; };
|
||||
|
||||
template<typename D>
|
||||
struct iterator_interface
|
||||
{
|
||||
friend constexpr bool operator>=(D lhs, D rhs)
|
||||
requires cmpeq<D> { return true; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct iterator : iterator_interface<iterator<T>>
|
||||
{
|
||||
bool operator==(iterator) const;
|
||||
iterator &operator++();
|
||||
iterator &operator++(int);
|
||||
};
|
||||
|
||||
static_assert(cmpeq<iterator<int>>);
|
@ -448,6 +448,8 @@ enum demangle_component_type
|
||||
DEMANGLE_COMPONENT_TRANSACTION_SAFE,
|
||||
/* A cloned function. */
|
||||
DEMANGLE_COMPONENT_CLONE,
|
||||
/* A member-like friend function. */
|
||||
DEMANGLE_COMPONENT_FRIEND,
|
||||
DEMANGLE_COMPONENT_NOEXCEPT,
|
||||
DEMANGLE_COMPONENT_THROW_SPEC,
|
||||
|
||||
|
@ -1036,6 +1036,7 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
|
||||
case DEMANGLE_COMPONENT_TEMPLATE_NON_TYPE_PARM:
|
||||
case DEMANGLE_COMPONENT_TEMPLATE_TEMPLATE_PARM:
|
||||
case DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM:
|
||||
case DEMANGLE_COMPONENT_FRIEND:
|
||||
if (left == NULL)
|
||||
return NULL;
|
||||
break;
|
||||
@ -1681,6 +1682,7 @@ d_maybe_module_name (struct d_info *di, struct demangle_component **name)
|
||||
/* <unqualified-name> ::= [<module-name>] <operator-name> [<abi-tags>]
|
||||
::= [<module-name>] <ctor-dtor-name> [<abi-tags>]
|
||||
::= [<module-name>] <source-name> [<abi-tags>]
|
||||
::= [<module-name>] F <source-name> [<abi-tags>]
|
||||
::= [<module-name>] <local-source-name> [<abi-tags>]
|
||||
::= [<module-name>] DC <source-name>+ E [<abi-tags>]
|
||||
<local-source-name> ::= L <source-name> <discriminator> [<abi-tags>]
|
||||
@ -1692,11 +1694,18 @@ d_unqualified_name (struct d_info *di, struct demangle_component *scope,
|
||||
{
|
||||
struct demangle_component *ret;
|
||||
char peek;
|
||||
int member_like_friend = 0;
|
||||
|
||||
if (!d_maybe_module_name (di, &module))
|
||||
return NULL;
|
||||
|
||||
peek = d_peek_char (di);
|
||||
if (peek == 'F')
|
||||
{
|
||||
member_like_friend = 1;
|
||||
d_advance (di, 1);
|
||||
peek = d_peek_char (di);
|
||||
}
|
||||
if (IS_DIGIT (peek))
|
||||
ret = d_source_name (di);
|
||||
else if (IS_LOWER (peek))
|
||||
@ -1773,6 +1782,8 @@ d_unqualified_name (struct d_info *di, struct demangle_component *scope,
|
||||
ret = d_make_comp (di, DEMANGLE_COMPONENT_MODULE_ENTITY, ret, module);
|
||||
if (d_peek_char (di) == 'B')
|
||||
ret = d_abi_tags (di, ret);
|
||||
if (member_like_friend)
|
||||
ret = d_make_comp (di, DEMANGLE_COMPONENT_FRIEND, ret, NULL);
|
||||
if (scope)
|
||||
ret = d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, scope, ret);
|
||||
|
||||
@ -4459,6 +4470,7 @@ d_count_templates_scopes (struct d_print_info *dpi,
|
||||
case DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS:
|
||||
case DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS:
|
||||
case DEMANGLE_COMPONENT_MODULE_ENTITY:
|
||||
case DEMANGLE_COMPONENT_FRIEND:
|
||||
d_count_templates_scopes (dpi, d_left (dc));
|
||||
break;
|
||||
|
||||
@ -6197,6 +6209,11 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
|
||||
d_append_char (dpi, ']');
|
||||
return;
|
||||
|
||||
case DEMANGLE_COMPONENT_FRIEND:
|
||||
d_print_comp (dpi, options, d_left (dc));
|
||||
d_append_string (dpi, "[friend]");
|
||||
return;
|
||||
|
||||
case DEMANGLE_COMPONENT_TEMPLATE_HEAD:
|
||||
{
|
||||
d_append_char (dpi, '<');
|
||||
|
@ -1689,3 +1689,6 @@ X::operator Z<int><int>()::y
|
||||
|
||||
_ZZN1XIfEcv1ZIT_EIiEEvE1y
|
||||
X<float>::operator Z<int><int>()::y
|
||||
|
||||
_ZN1SILi1EEF3barIiEEiR4Base
|
||||
int S<1>::bar[friend]<int>(Base&)
|
||||
|
Loading…
Reference in New Issue
Block a user