]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Extend the PR96204 fix to variable templates too
authorPatrick Palka <ppalka@redhat.com>
Thu, 1 Jul 2021 00:21:16 +0000 (20:21 -0400)
committerPatrick Palka <ppalka@redhat.com>
Thu, 1 Jul 2021 00:21:16 +0000 (20:21 -0400)
r12-1829 corrected the access scope during partial specialization
matching of class templates, but overlooked the variable template case.
This patch moves the access scope adjustment to within
most_specialized_partial_spec so that all callers can benefit.

This patch also adjusts a couple of these callers to avoid always
passing the most general template of a variable template specialization,
since that'd cause us to push the wrong access scope for e.g. the second
testcase below (we'd push A<T> instead of A<int>/A<char>).  We ought to
be passing the partially instantiated template instead.

PR c++/96204

gcc/cp/ChangeLog:

* pt.c (finish_template_variable): Pass the partially
instantiated template and its args to instantiate_template.
(instantiate_class_template_1): No need to call
push_nested_class and pop_nested_class around the call to
most_specialized_partial_spec.
(instantiate_template_1): Pass the partially instantiated
template to lookup_template_variable.
(most_specialized_partial_spec):  Use push_access_scope_guard
to set the access scope appropriately.  Use
deferring_access_check_sentinel to force access to get checked
immediately.
(instantiate_decl): Just pass the VAR_DECL to
most_specialized_partial_spec.

gcc/testsuite/ChangeLog:

* g++.dg/template/access41.C: New test.
* g++.dg/template/access41a.C: New test.

gcc/cp/pt.c
gcc/testsuite/g++.dg/template/access41.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/access41a.C [new file with mode: 0644]

index f8f7616bf2a106f275b323602a94bc6eae91a3d6..dda6c9e940d436b07f11dbb58337d82cfdf6e6a0 100644 (file)
@@ -10268,10 +10268,6 @@ finish_template_variable (tree var, tsubst_flags_t complain)
   tree templ = TREE_OPERAND (var, 0);
   tree arglist = TREE_OPERAND (var, 1);
 
-  tree tmpl_args = DECL_TI_ARGS (DECL_TEMPLATE_RESULT (templ));
-  arglist = add_outermost_template_args (tmpl_args, arglist);
-
-  templ = most_general_template (templ);
   tree parms = DECL_TEMPLATE_PARMS (templ);
   arglist = coerce_innermost_template_parms (parms, arglist, templ, complain,
                                             /*req_all*/true,
@@ -11774,11 +11770,8 @@ instantiate_class_template_1 (tree type)
   deferring_access_check_sentinel acs (dk_no_deferred);
 
   /* Determine what specialization of the original template to
-     instantiate; do this relative to the scope of the class for
-     sake of access checking.  */
-  push_nested_class (type);
+     instantiate.  */
   t = most_specialized_partial_spec (type, tf_warning_or_error);
-  pop_nested_class ();
   if (t == error_mark_node)
     return error_mark_node;
   else if (t)
@@ -21134,7 +21127,7 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
       /* We need to determine if we're using a partial or explicit
         specialization now, because the type of the variable could be
         different.  */
-      tree tid = lookup_template_variable (gen_tmpl, targ_ptr);
+      tree tid = lookup_template_variable (tmpl, targ_ptr);
       tree elt = most_specialized_partial_spec (tid, complain);
       if (elt == error_mark_node)
        pattern = error_mark_node;
@@ -24987,26 +24980,33 @@ most_specialized_partial_spec (tree target, tsubst_flags_t complain)
   tree outer_args = NULL_TREE;
   tree tmpl, args;
 
+  tree decl;
   if (TYPE_P (target))
     {
       tree tinfo = CLASSTYPE_TEMPLATE_INFO (target);
       tmpl = TI_TEMPLATE (tinfo);
       args = TI_ARGS (tinfo);
+      decl = TYPE_NAME (target);
     }
   else if (TREE_CODE (target) == TEMPLATE_ID_EXPR)
     {
       tmpl = TREE_OPERAND (target, 0);
       args = TREE_OPERAND (target, 1);
+      decl = DECL_TEMPLATE_RESULT (tmpl);
     }
   else if (VAR_P (target))
     {
       tree tinfo = DECL_TEMPLATE_INFO (target);
       tmpl = TI_TEMPLATE (tinfo);
       args = TI_ARGS (tinfo);
+      decl = target;
     }
   else
     gcc_unreachable ();
 
+  push_access_scope_guard pas (decl);
+  deferring_access_check_sentinel acs (dk_no_deferred);
+
   tree main_tmpl = most_general_template (tmpl);
 
   /* For determining which partial specialization to use, only the
@@ -26011,8 +26011,7 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
   if (variable_template_specialization_p (d))
     {
       /* Look up an explicit specialization, if any.  */
-      tree tid = lookup_template_variable (gen_tmpl, gen_args);
-      tree elt = most_specialized_partial_spec (tid, tf_warning_or_error);
+      tree elt = most_specialized_partial_spec (d, tf_warning_or_error);
       if (elt && elt != error_mark_node)
        {
          td = TREE_VALUE (elt);
diff --git a/gcc/testsuite/g++.dg/template/access41.C b/gcc/testsuite/g++.dg/template/access41.C
new file mode 100644 (file)
index 0000000..1ab9a1a
--- /dev/null
@@ -0,0 +1,24 @@
+// PR c++/96204
+// { dg-do compile { target c++14 } }
+// A variant of access40.C where has_type_member is a variable template instead
+// of a class template.
+
+template<class, class = void>
+constexpr bool has_type_member = false;
+
+template<class T>
+constexpr bool has_type_member<T, typename T::type> = true;
+
+struct Parent;
+
+struct Child {
+private:
+  friend struct Parent;
+  typedef void type;
+};
+
+struct Parent {
+  // The partial specialization does not match despite Child::type
+  // being accessible from the current scope.
+  static_assert(!has_type_member<Child>, "");
+};
diff --git a/gcc/testsuite/g++.dg/template/access41a.C b/gcc/testsuite/g++.dg/template/access41a.C
new file mode 100644 (file)
index 0000000..e108049
--- /dev/null
@@ -0,0 +1,29 @@
+// PR c++/96204
+// { dg-do compile { target c++14 } }
+// A variant of access40a.C where has_type_member is a variable template instead
+// of a class template.
+
+template<class T>
+struct A {
+  template<class, class = void>
+  static constexpr bool has_type_member = false;
+};
+
+template<class T>
+template<class U>
+constexpr int A<T>::has_type_member<U, typename U::type> = true;
+
+struct Child {
+private:
+  friend struct A<int>;
+  typedef void type;
+};
+
+// The partial specialization matches because A<int> is a friend of Child.
+static_assert(A<int>::has_type_member<Child>, "");
+using type1 = const int;
+using type1 = decltype(A<int>::has_type_member<Child>);
+
+static_assert(!A<char>::has_type_member<Child>, "");
+using type2 = const bool;
+using type2 = decltype(A<char>::has_type_member<Child>);