diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index bfda0065bf467686fac7aa7c3b7a8662fca59697..bc94ed45e17e26be1e24c7d511642129cc70fe87 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 0000000000000000000000000000000000000000..ce43435f0114f7dfb47942c6b8781986deec0d2c --- /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 0000000000000000000000000000000000000000..71305ba082c7c2a3b70f056a515345664d569bbd --- /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 0000000000000000000000000000000000000000..5c123bf7005ecbfac9003c16941194014759ce37 --- /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 0000000000000000000000000000000000000000..250d6147c73c8698fbfd62a7634e2339f1bd3fe5 --- /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; +};