Here, when determining whether the partial specialization matches the specialization has_set_attr_method<Child>, we do so from the scope of where the template-id appears rather than from the scope of the specialization, and this causes us to select the partial specialization (since Child::type is accessible from Parent). When we later instantiate this partial specialization, we've entered the scope of the specialization and so substitution into e.g. the DECL_CONTEXT for 'value' yields access errors for Child::type since the friend declaration no longer applies.
It seems the appropriate access scope from which to perform partial specialization matching is the specialization itself (similar to how we check access of base-clauses), which is what this patch implements. There's implementation divergence however: Clang accepts both testcases below whereas MSVC and ICC reject both (indicating that Clang performs partial spec matching from the scope of the specialization and MSVC/ICC performs it from whatever scope the template-id appears). Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk? PR c++/96204 gcc/cp/ChangeLog: * pt.c (instantiate_class_template_1): Enter the scope of the type before calling most_specialized_partial_spec. gcc/testsuite/ChangeLog: * g++.dg/template/access40.C: New test. * g++.dg/template/access40a.C: New test. --- gcc/cp/pt.c | 6 ++++- gcc/testsuite/g++.dg/template/access40.C | 30 +++++++++++++++++++++++ gcc/testsuite/g++.dg/template/access40a.C | 30 +++++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/template/access40.C create mode 100644 gcc/testsuite/g++.dg/template/access40a.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index f4e0abe5c1e..5107bfbf9d1 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -11774,8 +11774,12 @@ instantiate_class_template_1 (tree type) deferring_access_check_sentinel acs (dk_no_deferred); /* Determine what specialization of the original template to - instantiate. */ + instantiate; do this relative to the scope of the type. */ + push_access_scope (TYPE_NAME (type)); + pushclass (type); t = most_specialized_partial_spec (type, tf_warning_or_error); + popclass (); + pop_access_scope (TYPE_NAME (type)); if (t == error_mark_node) return error_mark_node; else if (t) diff --git a/gcc/testsuite/g++.dg/template/access40.C b/gcc/testsuite/g++.dg/template/access40.C new file mode 100644 index 00000000000..e0d30779377 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/access40.C @@ -0,0 +1,30 @@ +// PR c++/96204 + +template<bool> struct bool_constant; + +template<class, class = void> +struct has_type_member { + static const bool value = false; +}; + +template<class T> +struct has_type_member<T, typename T::type> { + static const bool value = true; +}; + +struct Parent; + +struct Child { +private: + friend struct Parent; + typedef void type; +}; + +struct Parent { + static void f() { + // The partial specialization of has_type_member does not match + // despite Child::type being accessible from the current scope. + typedef bool_constant<has_type_member<Child>::value> type; + typedef bool_constant<false> type; + } +}; diff --git a/gcc/testsuite/g++.dg/template/access40a.C b/gcc/testsuite/g++.dg/template/access40a.C new file mode 100644 index 00000000000..85138c9e570 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/access40a.C @@ -0,0 +1,30 @@ +// PR c++/96204 + +template<bool> struct bool_constant; + +template<class, class = void> +struct has_type_member { + static const bool value = false; +}; + +template<class T> +struct has_type_member<T, typename T::type> { + static const bool value = true; +}; + +struct Parent; + +struct Child { +private: + friend struct has_type_member<Child>; + typedef void type; +}; + +struct Parent { + static void f() { + // The partial specialization matches because Child::type is + // accessible from has_type_member<Child>. + typedef bool_constant<has_type_member<Child>::value> type; + typedef bool_constant<true> type; + } +}; -- 2.32.0.93.g670b81a890