diff options
-rw-r--r-- | gcc/cp/cp-tree.h | 3 | ||||
-rw-r--r-- | gcc/cp/decl.cc | 49 | ||||
-rw-r--r-- | gcc/cp/mangle.cc | 10 | ||||
-rw-r--r-- | gcc/cp/pt.cc | 14 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/concepts-friend11.C | 26 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/concepts-friend11a.C | 15 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/concepts-friend15.C | 22 | ||||
-rw-r--r-- | include/demangle.h | 2 | ||||
-rw-r--r-- | libiberty/cp-demangle.c | 17 | ||||
-rw-r--r-- | libiberty/testsuite/demangle-expected | 3 |
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&) |