[pushed] c++: also note name used in enclosing class

Message ID 20220502195247.1484300-1-jason@redhat.com
State Committed
Commit dcb4bd0789d13dd4d07428bff712d01d3ea71ebe
Headers
Series [pushed] c++: also note name used in enclosing class |

Commit Message

Jason Merrill May 2, 2022, 7:52 p.m. UTC
  While looking at PR96645 I noticed that while we were diagnosing names
changing meaning in the full class context, we weren't doing this for
lookups in nested class bodies.

Note that this breaks current range-v3; I've submitted a pull request to fix
its violation of the rule.

Tested x86_64-pc-linux-gnu, applying to trunk.

gcc/cp/ChangeLog:

	* class.cc (maybe_note_name_used_in_class): Note in all enclosing
	classes.  Remember location of use.
	(note_name_declared_in_class): Adjust.

gcc/testsuite/ChangeLog:

	* g++.dg/lookup/name-clash13.C: New test.
	* g++.dg/lookup/name-clash14.C: New test.
	* g++.dg/lookup/name-clash15.C: New test.
	* g++.dg/lookup/name-clash16.C: New test.
---
 gcc/cp/class.cc                            | 61 +++++++++++++++-------
 gcc/testsuite/g++.dg/lookup/name-clash13.C |  7 +++
 gcc/testsuite/g++.dg/lookup/name-clash14.C |  9 ++++
 gcc/testsuite/g++.dg/lookup/name-clash15.C | 14 +++++
 gcc/testsuite/g++.dg/lookup/name-clash16.C | 13 +++++
 5 files changed, 86 insertions(+), 18 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/lookup/name-clash13.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/name-clash14.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/name-clash15.C
 create mode 100644 gcc/testsuite/g++.dg/lookup/name-clash16.C


base-commit: 7e1f30d7edd433ca0fc28ae9ac1ce9b522790baf
  

Patch

diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index bfda0065bf4..bc94ed45e17 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -8931,32 +8931,53 @@  is_really_empty_class (tree type, bool ignore_vptr)
 void
 maybe_note_name_used_in_class (tree name, tree decl)
 {
-  splay_tree names_used;
-
   /* If we're not defining a class, there's nothing to do.  */
   if (!(innermost_scope_kind() == sk_class
 	&& TYPE_BEING_DEFINED (current_class_type)
 	&& !LAMBDA_TYPE_P (current_class_type)))
     return;
 
-  /* If there's already a binding for this NAME, then we don't have
-     anything to worry about.  */
-  if (lookup_member (current_class_type, name,
-		     /*protect=*/0, /*want_type=*/false, tf_warning_or_error))
-    return;
+  const cp_binding_level *blev = nullptr;
+  if (const cxx_binding *binding = IDENTIFIER_BINDING (name))
+    blev = binding->scope;
+  const cp_binding_level *lev = current_binding_level;
 
-  if (!current_class_stack[current_class_depth - 1].names_used)
-    current_class_stack[current_class_depth - 1].names_used
-      = splay_tree_new (splay_tree_compare_pointers, 0, 0);
-  names_used = current_class_stack[current_class_depth - 1].names_used;
+  /* Record the binding in the names_used tables for classes inside blev.  */
+  for (int i = current_class_depth; i > 0; --i)
+    {
+      tree type = (i == current_class_depth
+		   ? current_class_type
+		   : current_class_stack[i].type);
 
-  splay_tree_insert (names_used,
-		     (splay_tree_key) name,
-		     (splay_tree_value) decl);
+      for (; lev; lev = lev->level_chain)
+	{
+	  if (lev == blev)
+	    /* We found the declaration.  */
+	    return;
+	  if (lev->kind == sk_class && lev->this_entity == type)
+	    /* This class is inside the declaration scope.  */
+	    break;
+	}
+
+      auto &names_used = current_class_stack[i-1].names_used;
+      if (!names_used)
+	names_used = splay_tree_new (splay_tree_compare_pointers, 0, 0);
+
+      tree use = build1_loc (input_location, VIEW_CONVERT_EXPR,
+			     TREE_TYPE (decl), decl);
+      EXPR_LOCATION_WRAPPER_P (use) = 1;
+      splay_tree_insert (names_used,
+			 (splay_tree_key) name,
+			 (splay_tree_value) use);
+    }
 }
 
 /* Note that NAME was declared (as DECL) in the current class.  Check
-   to see that the declaration is valid.  */
+   to see that the declaration is valid under [class.member.lookup]:
+
+   If [the result of a search in T for N at point P] differs from the result of
+   a search in T for N from immediately after the class-specifier of T, the
+   program is ill-formed, no diagnostic required.  */
 
 void
 note_name_declared_in_class (tree name, tree decl)
@@ -8979,6 +9000,9 @@  note_name_declared_in_class (tree name, tree decl)
   n = splay_tree_lookup (names_used, (splay_tree_key) name);
   if (n)
     {
+      tree use = (tree) n->value;
+      location_t loc = EXPR_LOCATION (use);
+      tree olddecl = OVL_FIRST (TREE_OPERAND (use, 0));
       /* [basic.scope.class]
 
 	 A name N used in a class S shall refer to the same declaration
@@ -8987,9 +9011,10 @@  note_name_declared_in_class (tree name, tree decl)
       if (permerror (location_of (decl),
 		     "declaration of %q#D changes meaning of %qD",
 		     decl, OVL_NAME (decl)))
-	inform (location_of ((tree) n->value),
-		"%qD declared here as %q#D",
-		OVL_NAME (decl), (tree) n->value);
+	{
+	  inform (loc, "used here to mean %q#D", olddecl);
+	  inform (location_of (olddecl), "declared here" );
+	}
     }
 }
 
diff --git a/gcc/testsuite/g++.dg/lookup/name-clash13.C b/gcc/testsuite/g++.dg/lookup/name-clash13.C
new file mode 100644
index 00000000000..ce43435f011
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/name-clash13.C
@@ -0,0 +1,7 @@ 
+typedef int T;
+struct A {
+  struct B {
+    static T t;
+  };
+  typedef float T;		// { dg-error "changes meaning" }
+};
diff --git a/gcc/testsuite/g++.dg/lookup/name-clash14.C b/gcc/testsuite/g++.dg/lookup/name-clash14.C
new file mode 100644
index 00000000000..71305ba082c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/name-clash14.C
@@ -0,0 +1,9 @@ 
+const int x = 24;
+struct A
+{
+  struct B
+  {
+    enum { E = x };
+  };
+  static const int x = 42;	// { dg-error "changes meaning" }
+};
diff --git a/gcc/testsuite/g++.dg/lookup/name-clash15.C b/gcc/testsuite/g++.dg/lookup/name-clash15.C
new file mode 100644
index 00000000000..5c123bf7005
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/name-clash15.C
@@ -0,0 +1,14 @@ 
+struct C {
+  static const int x = 24;
+};
+
+struct A
+{
+  struct B: C
+  {
+    enum { E = x };
+  };
+
+  // OK, earlier x was found in a base, lookup didn't pass through A.
+  static const int x = 42;
+};
diff --git a/gcc/testsuite/g++.dg/lookup/name-clash16.C b/gcc/testsuite/g++.dg/lookup/name-clash16.C
new file mode 100644
index 00000000000..250d6147c73
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/name-clash16.C
@@ -0,0 +1,13 @@ 
+typedef int T;
+
+struct A
+{
+  template <class T>
+  struct B
+  {
+    T t;
+  };
+
+  // OK, earlier T was found in template header, didn't look in A.
+  typedef float T;
+};