aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/cp/cp-tree.h3
-rw-r--r--gcc/cp/decl.cc49
-rw-r--r--gcc/cp/mangle.cc10
-rw-r--r--gcc/cp/pt.cc14
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-friend11.C26
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C15
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C22
-rw-r--r--include/demangle.h2
-rw-r--r--libiberty/cp-demangle.c17
-rw-r--r--libiberty/testsuite/demangle-expected3
10 files changed, 145 insertions, 16 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d051ee8..356d7ff 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 62c34bf9..bea0ee92 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -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)
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index bef0fda..bb0e9d3 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -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))
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index a4809f0..f4e77d1 100644
--- a/gcc/cp/pt.cc
+++ b/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
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend11.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend11.C
index 0350ac3..93cb1f0 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-friend11.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend11.C
@@ -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
+
+// { dg-final { scan-assembler "_ZN1SILi1EEF3fooER4Base" } }
+int x = foo(s1); // OK, selects #1
+// { dg-final { scan-assembler "_ZN1SILi2EEF3fooER4Base" } }
+int y = foo(s2); // OK, selects #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 "_ZN1SILi1EEF3barIiEEiR4Base" } }
+int x2 = bar<int>(s1); // OK, selects #1
+// { dg-final { scan-assembler "_ZN1SILi2EEF3barIiEEiR4Base" } }
+int y2 = bar<int>(s2); // OK, selects #2
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C
new file mode 100644
index 0000000..f3481b6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C
@@ -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" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C
new file mode 100644
index 0000000..c37d547
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C
@@ -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>>);
diff --git a/include/demangle.h b/include/demangle.h
index 769137e..f062d77 100644
--- a/include/demangle.h
+++ b/include/demangle.h
@@ -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,
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 3bd303a..2ce984f 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -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, '<');
diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected
index 0acd2d6..01ca222 100644
--- a/libiberty/testsuite/demangle-expected
+++ b/libiberty/testsuite/demangle-expected
@@ -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&)