aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/class.cc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2022-02-25 15:07:15 -0400
committerJason Merrill <jason@redhat.com>2022-03-18 14:07:58 -0400
commit47da5198766256be658b4c321cecfd6039b0b91b (patch)
treecf04525e4d62c0579f27fdaba87433023f92400d /gcc/cp/class.cc
parent32ca611c42658948f1b8883994796f35e8b4e74d (diff)
downloadgcc-47da5198766256be658b4c321cecfd6039b0b91b.zip
gcc-47da5198766256be658b4c321cecfd6039b0b91b.tar.gz
gcc-47da5198766256be658b4c321cecfd6039b0b91b.tar.bz2
c++: using lookup within class defn [PR104476]
The problem in both PR92918 and PR104476 is overloading of base member functions brought in by 'using' with direct member functions during parsing of the class body. To this point they've had a troublesome coexistence which was resolved by set_class_bindings when the class is complete, but we also need to handle lookup within the class body, such as in a trailing return type. The problem was that push_class_level_binding would either clobber the using-decl with the direct members or vice-versa. In older versions of GCC we only pushed dependent usings, and preferring the dependent using made sense, as it expresses a type-dependent overload set that we can't do anything useful with. But when we started keeping non-dependent usings around, push_class_level_binding in particular wasn't adjusted accordingly. This patch makes that adjustment, and pushes the functions imported by a non-dependent using immediately from finish_member_declaration. This made diagnosing redundant using-decls a bit awkward, since we no longer push the using-decl itself; I handle that by noticing when we try to add the same function again and searching TYPE_FIELDS for the previous using-decl. PR c++/92918 PR c++/104476 gcc/cp/ChangeLog: * class.cc (add_method): Avoid adding the same used function twice. (handle_using_decl): Don't add_method. (finish_struct): Don't using op= if we have one already. (maybe_push_used_methods): New. * semantics.cc (finish_member_declaration): Call it. * name-lookup.cc (diagnose_name_conflict): No longer static. (push_class_level_binding): Revert 92918 patch, limit to dependent using. * cp-tree.h: Adjust. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/pr85070.C: Remove expected error. * g++.dg/lookup/using66a.C: New test. * g++.dg/lookup/using67.C: New test.
Diffstat (limited to 'gcc/cp/class.cc')
-rw-r--r--gcc/cp/class.cc136
1 files changed, 81 insertions, 55 deletions
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6961557..40e1714 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -1049,7 +1049,12 @@ add_method (tree type, tree method, bool via_using)
if (via_using && iter.using_p ()
/* Except handle inherited constructors specially. */
&& ! DECL_CONSTRUCTOR_P (fn))
- continue;
+ {
+ if (fn == method)
+ /* Don't add the same one twice. */
+ return false;
+ continue;
+ }
/* [over.load] Member function declarations with the
same name and the same parameter types cannot be
@@ -1212,10 +1217,11 @@ add_method (tree type, tree method, bool via_using)
if (via_using)
/* Defer to the local function. */
return false;
- else if (flag_new_inheriting_ctors
- && DECL_INHERITED_CTOR (fn))
+ else if (iter.using_p ()
+ || (flag_new_inheriting_ctors
+ && DECL_INHERITED_CTOR (fn)))
{
- /* Remove the inherited constructor. */
+ /* Remove the inherited function. */
current_fns = iter.remove_node (current_fns);
continue;
}
@@ -1299,21 +1305,65 @@ declared_access (tree decl)
: access_public_node);
}
+/* If DECL is a non-dependent using of non-ctor function members, push them
+ and return true, otherwise return false. Called from
+ finish_member_declaration. */
+
+bool
+maybe_push_used_methods (tree decl)
+{
+ if (TREE_CODE (decl) != USING_DECL)
+ return false;
+ tree used = strip_using_decl (decl);
+ if (!used || !is_overloaded_fn (used))
+ return false;
+
+ /* Add the functions to CLASSTYPE_MEMBER_VEC so that overload resolution
+ works within the class body. */
+ for (tree f : ovl_range (used))
+ {
+ if (DECL_CONSTRUCTOR_P (f))
+ /* Inheriting constructors are handled separately. */
+ return false;
+
+ bool added = add_method (current_class_type, f, true);
+
+ if (added)
+ alter_access (current_class_type, f, current_access_specifier);
+
+ /* If add_method returns false because f was already declared, look
+ for a duplicate using-declaration. */
+ else
+ for (tree d = TYPE_FIELDS (current_class_type); d; d = DECL_CHAIN (d))
+ if (TREE_CODE (d) == USING_DECL
+ && DECL_NAME (d) == DECL_NAME (decl)
+ && same_type_p (USING_DECL_SCOPE (d), USING_DECL_SCOPE (decl)))
+ {
+ diagnose_name_conflict (decl, d);
+ break;
+ }
+ }
+ return true;
+}
+
/* Process the USING_DECL, which is a member of T. */
static void
handle_using_decl (tree using_decl, tree t)
{
tree decl = USING_DECL_DECLS (using_decl);
- tree name = DECL_NAME (using_decl);
- tree access = declared_access (using_decl);
- tree flist = NULL_TREE;
- tree old_value;
gcc_assert (!processing_template_decl && decl);
- old_value = lookup_member (t, name, /*protect=*/0, /*want_type=*/false,
- tf_warning_or_error);
+ cp_emit_debug_info_for_using (decl, t);
+
+ if (is_overloaded_fn (decl))
+ /* Handled in maybe_push_used_methods. */
+ return;
+
+ tree name = DECL_NAME (using_decl);
+ tree old_value = lookup_member (t, name, /*protect=*/0, /*want_type=*/false,
+ tf_warning_or_error);
if (old_value)
{
old_value = OVL_FIRST (old_value);
@@ -1324,27 +1374,16 @@ handle_using_decl (tree using_decl, tree t)
old_value = NULL_TREE;
}
- cp_emit_debug_info_for_using (decl, t);
-
- if (is_overloaded_fn (decl))
- flist = decl;
-
if (! old_value)
;
else if (is_overloaded_fn (old_value))
{
- if (flist)
- /* It's OK to use functions from a base when there are functions with
- the same name already present in the current class. */;
- else
- {
- error_at (DECL_SOURCE_LOCATION (using_decl), "%qD invalid in %q#T "
- "because of local method %q#D with same name",
- using_decl, t, old_value);
- inform (DECL_SOURCE_LOCATION (old_value),
- "local method %q#D declared here", old_value);
- return;
- }
+ error_at (DECL_SOURCE_LOCATION (using_decl), "%qD invalid in %q#T "
+ "because of local method %q#D with same name",
+ using_decl, t, old_value);
+ inform (DECL_SOURCE_LOCATION (old_value),
+ "local method %q#D declared here", old_value);
+ return;
}
else if (!DECL_ARTIFICIAL (old_value))
{
@@ -1357,23 +1396,17 @@ handle_using_decl (tree using_decl, tree t)
}
iloc_sentinel ils (DECL_SOURCE_LOCATION (using_decl));
+ tree access = declared_access (using_decl);
/* Make type T see field decl FDECL with access ACCESS. */
- if (flist)
- for (tree f : ovl_range (flist))
- {
- add_method (t, f, true);
- alter_access (t, f, access);
- }
- else if (USING_DECL_UNRELATED_P (using_decl))
+ if (USING_DECL_UNRELATED_P (using_decl))
{
/* C++20 using enum can import non-inherited enumerators into class
scope. We implement that by making a copy of the CONST_DECL for which
CONST_DECL_USING_P is true. */
gcc_assert (TREE_CODE (decl) == CONST_DECL);
- auto cas = make_temp_override (current_access_specifier);
- set_current_access_from_decl (using_decl);
+ auto cas = make_temp_override (current_access_specifier, access);
tree copy = copy_decl (decl);
DECL_CONTEXT (copy) = t;
DECL_ARTIFICIAL (copy) = true;
@@ -7672,18 +7705,8 @@ finish_struct (tree t, tree attributes)
{
tree x;
- /* We need to add the target functions of USING_DECLS, so that
- they can be found when the using declaration is not
- instantiated yet. */
for (x = TYPE_FIELDS (t); x; x = DECL_CHAIN (x))
- if (TREE_CODE (x) == USING_DECL)
- {
- tree fn = strip_using_decl (x);
- if (OVL_P (fn))
- for (lkp_iterator iter (fn); iter; ++iter)
- add_method (t, *iter, true);
- }
- else if (DECL_DECLARES_FUNCTION_P (x))
+ if (DECL_DECLARES_FUNCTION_P (x))
{
DECL_IN_AGGR_P (x) = false;
if (DECL_VIRTUAL_P (x))
@@ -7700,14 +7723,17 @@ finish_struct (tree t, tree attributes)
lookup not to fail or recurse into bases. This isn't added
to the template decl list so we drop this at instantiation
time. */
- tree ass_op = build_lang_decl (USING_DECL, assign_op_identifier,
- NULL_TREE);
- DECL_CONTEXT (ass_op) = t;
- USING_DECL_SCOPE (ass_op) = t;
- DECL_DEPENDENT_P (ass_op) = true;
- DECL_ARTIFICIAL (ass_op) = true;
- DECL_CHAIN (ass_op) = TYPE_FIELDS (t);
- TYPE_FIELDS (t) = ass_op;
+ if (!get_class_binding_direct (t, assign_op_identifier, false))
+ {
+ tree ass_op = build_lang_decl (USING_DECL, assign_op_identifier,
+ NULL_TREE);
+ DECL_CONTEXT (ass_op) = t;
+ USING_DECL_SCOPE (ass_op) = t;
+ DECL_DEPENDENT_P (ass_op) = true;
+ DECL_ARTIFICIAL (ass_op) = true;
+ DECL_CHAIN (ass_op) = TYPE_FIELDS (t);
+ TYPE_FIELDS (t) = ass_op;
+ }
TYPE_SIZE (t) = bitsize_zero_node;
TYPE_SIZE_UNIT (t) = size_zero_node;