On 6/4/21 5:08 PM, Patrick Palka wrote:
Here, when resolving the destructor named by Inner<int>::~Inner<int>
(which is valid only before C++20) we end up in cp_parser_lookup_name to
look up the name Inner relative to the scope Inner<int>. The lookup
naturally finds the injected-class-name Inner<int>, and because
is_template is true, we adjust this lookup result to the TEMPLATE_DECL
Inner, and then check access of this adjusted lookup result. But this
access check fails because the scope is Inner<int> and not Outer, and
the context_for_name_lookup of the TEMPLATE_DECL is Outer.
The simplest fix seems to be to perform the access check on the original
lookup result (the injected-class-name) instead of the TEMPLATE_DECL.
So this patch moves the access check in cp_parser_lookup_name to before
the injected-class-name adjustment.
Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?
OK.
PR c++/100918
gcc/cp/ChangeLog:
* parser.c (cp_parser_lookup_name): Check access of the lookup
result before we potentially adjust an injected-class-name to
its TEMPLATE_DECL.
gcc/testsuite/ChangeLog:
* g++.dg/template/access38.C: New test.
---
gcc/cp/parser.c | 24 +++++++++++++-----------
gcc/testsuite/g++.dg/template/access38.C | 15 +++++++++++++++
2 files changed, 28 insertions(+), 11 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/template/access38.C
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 4a46828e162..829a94b2928 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -29505,6 +29505,19 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
if (!decl || decl == error_mark_node)
return error_mark_node;
+ /* If we have resolved the name of a member declaration, check to
+ see if the declaration is accessible. When the name resolves to
+ set of overloaded functions, accessibility is checked when
+ overload resolution is done. If we have a TREE_LIST, then the lookup
+ is either ambiguous or it found multiple injected-class-names, the
+ accessibility of which is trivially satisfied.
+
+ During an explicit instantiation, access is not checked at all,
+ as per [temp.explicit]. */
+ if (DECL_P (decl))
+ check_accessibility_of_qualified_id (decl, object_type, parser->scope,
+ tf_warning_or_error);
+
/* Pull out the template from an injected-class-name (or multiple). */
if (is_template)
decl = maybe_get_template_decl_from_type_decl (decl);
@@ -29531,17 +29544,6 @@ cp_parser_lookup_name (cp_parser *parser, tree name,
|| TREE_CODE (decl) == UNBOUND_CLASS_TEMPLATE
|| BASELINK_P (decl));
- /* If we have resolved the name of a member declaration, check to
- see if the declaration is accessible. When the name resolves to
- set of overloaded functions, accessibility is checked when
- overload resolution is done.
-
- During an explicit instantiation, access is not checked at all,
- as per [temp.explicit]. */
- if (DECL_P (decl))
- check_accessibility_of_qualified_id (decl, object_type, parser->scope,
- tf_warning_or_error);
-
maybe_record_typedef_use (decl);
return cp_expr (decl, name_location);
diff --git a/gcc/testsuite/g++.dg/template/access38.C
b/gcc/testsuite/g++.dg/template/access38.C
new file mode 100644
index 00000000000..488f8650c97
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access38.C
@@ -0,0 +1,15 @@
+// PR c++/100918
+
+struct Outer {
+ template<class T>
+ struct Inner { ~Inner(); };
+};
+
+template<>
+Outer::Inner<int>::~Inner<int>() { } // { dg-error "template-id" "" { target
c++20 } }
+
+template<class T>
+Outer::Inner<T>::~Inner<T>() { } // { dg-error "template-id" "" { target c++20
} }
+
+Outer::Inner<int> x;
+Outer::Inner<char> y;