Patchwork C++ PATCH for c++/57550 (wrong access error with templates)

login
register
mail settings
Submitter Jason Merrill
Date July 9, 2013, 3:08 a.m.
Message ID <51DB7EA7.9080109@redhat.com>
Download mbox | patch
Permalink /patch/257631/
State New
Headers show

Comments

Jason Merrill - July 9, 2013, 3:08 a.m.
Well, this has taken a lot of revisions to get right.  In this testcase, in

     MakeHandler(Wrapper<Append<T> >);

we need to check the access for Append in the context of the expression. 
  But we don't know which Append we want until we do template argument 
deduction.  In other testcases, we need to check the access of things 
mentioned in default template arguments in the context of their 
templates.  But we need to instantiate default arguments during template 
argument deduction.  So during deduction we need to check access in both 
contexts.  In this patch I'm dealing with that by staying in the 
expression context most of the time, and doing substitution in deferred 
access context so that we can do those checks in the template context 
later.  For default arguments, I collect up any checks and pass them 
back to the caller to handle.

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

Patch

commit 2baa70278509769abf4f4ed3c9796962fee44319
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Jul 8 17:03:05 2013 -0400

    	PR c++/57550
    	* pt.c (fn_type_unification): Only defer during substitution.
    	(type_unification_real): Defer during defarg substitution,
    	add checks parm to pass back deferred checks.
    	(unify, do_auto_deduction): Adjust.
    	* semantics.c (reopen_deferring_access_checks): New.
    	* cp-tree.h: Declare it.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1b0b243..1971bc5 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5637,6 +5637,7 @@  extern void resume_deferring_access_checks	(void);
 extern void stop_deferring_access_checks	(void);
 extern void pop_deferring_access_checks		(void);
 extern vec<deferred_access_check, va_gc> *get_deferred_access_checks (void);
+extern void reopen_deferring_access_checks (vec<deferred_access_check, va_gc> *);
 extern void pop_to_parent_deferring_access_checks (void);
 extern bool perform_access_checks (vec<deferred_access_check, va_gc> *,
 				   tsubst_flags_t);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 3847a1d..26d5a1c 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -138,6 +138,7 @@  static int maybe_adjust_types_for_deduction (unification_kind_t, tree*, tree*,
 					     tree);
 static int type_unification_real (tree, tree, tree, const tree *,
 				  unsigned int, int, unification_kind_t, int,
+				  vec<deferred_access_check, va_gc> **,
 				  bool);
 static void note_template_header (int);
 static tree convert_nontype_argument_function (tree, tree);
@@ -15052,7 +15053,6 @@  fn_type_unification (tree fn,
     return error_mark_node;
   tinst = build_tree_list (fn, NULL_TREE);
   ++deduction_depth;
-  push_deferring_access_checks (dk_deferred);
 
   gcc_assert (TREE_CODE (fn) == TEMPLATE_DECL);
 
@@ -15144,8 +15144,13 @@  fn_type_unification (tree fn,
 	}
       processing_template_decl += incomplete;
       input_location = DECL_SOURCE_LOCATION (fn);
+      /* Ignore any access checks; we'll see them again in
+	 instantiate_template and they might have the wrong
+	 access path at this point.  */
+      push_deferring_access_checks (dk_deferred);
       fntype = tsubst (TREE_TYPE (fn), explicit_targs,
 		       complain | tf_partial, NULL_TREE);
+      pop_deferring_access_checks ();
       input_location = loc;
       processing_template_decl -= incomplete;
       pop_tinst_level ();
@@ -15153,12 +15158,6 @@  fn_type_unification (tree fn,
       if (fntype == error_mark_node)
 	goto fail;
 
-      /* Throw away these access checks; we'll see them again in
-	 instantiate_template and they might have the wrong
-	 access path at this point.  */
-      pop_deferring_access_checks ();
-      push_deferring_access_checks (dk_deferred);
-
       /* Place the explicitly specified arguments in TARGS.  */
       for (i = NUM_TMPL_ARGS (explicit_targs); i--;)
 	TREE_VEC_ELT (targs, i) = TREE_VEC_ELT (explicit_targs, i);
@@ -15194,9 +15193,15 @@  fn_type_unification (tree fn,
       excessive_deduction_depth = true;
       goto fail;
     }
+
+  /* type_unification_real will pass back any access checks from default
+     template argument substitution.  */
+  vec<deferred_access_check, va_gc> *checks;
+  checks = NULL;
+
   ok = !type_unification_real (DECL_INNERMOST_TEMPLATE_PARMS (fn),
 			       targs, parms, args, nargs, /*subr=*/0,
-			       strict, flags, explain_p);
+			       strict, flags, &checks, explain_p);
   if (!explain_p)
     pop_tinst_level ();
   if (!ok)
@@ -15245,16 +15250,23 @@  fn_type_unification (tree fn,
       excessive_deduction_depth = true;
       goto fail;
     }
+
+  /* Also collect access checks from the instantiation.  */
+  reopen_deferring_access_checks (checks);
+
   decl = instantiate_template (fn, targs, complain);
+
+  checks = get_deferred_access_checks ();
+  pop_deferring_access_checks ();
+
   pop_tinst_level ();
 
   if (decl == error_mark_node)
     goto fail;
 
-  /* Now perform any access checks encountered during deduction, such as
-     for default template arguments.  */
+  /* Now perform any access checks encountered during substitution.  */
   push_access_scope (decl);
-  ok = perform_deferred_access_checks (complain);
+  ok = perform_access_checks (checks, complain);
   pop_access_scope (decl);
   if (!ok)
     goto fail;
@@ -15283,7 +15295,6 @@  fn_type_unification (tree fn,
   r = decl;
 
  fail:
-  pop_deferring_access_checks ();
   --deduction_depth;
   if (excessive_deduction_depth)
     {
@@ -15684,7 +15695,10 @@  unify_one_argument (tree tparms, tree targs, tree parm, tree arg,
 
    If SUBR is 1, we're being called recursively (to unify the
    arguments of a function or method parameter of a function
-   template). */
+   template).
+
+   CHECKS is a pointer to a vector of access checks encountered while
+   substituting default template arguments.  */
 
 static int
 type_unification_real (tree tparms,
@@ -15695,6 +15709,7 @@  type_unification_real (tree tparms,
 		       int subr,
 		       unification_kind_t strict,
 		       int flags,
+		       vec<deferred_access_check, va_gc> **checks,
 		       bool explain_p)
 {
   tree parm, arg;
@@ -15834,6 +15849,7 @@  type_unification_real (tree tparms,
 	    {
 	      tree parm = TREE_VALUE (TREE_VEC_ELT (tparms, i));
 	      tree arg = TREE_PURPOSE (TREE_VEC_ELT (tparms, i));
+	      reopen_deferring_access_checks (*checks);
 	      location_t save_loc = input_location;
 	      if (DECL_P (parm))
 		input_location = DECL_SOURCE_LOCATION (parm);
@@ -15841,6 +15857,8 @@  type_unification_real (tree tparms,
 	      arg = convert_template_argument (parm, arg, targs, complain,
 					       i, NULL_TREE);
 	      input_location = save_loc;
+	      *checks = get_deferred_access_checks ();
+	      pop_deferring_access_checks ();
 	      if (arg == error_mark_node)
 		return 1;
 	      else
@@ -17307,7 +17325,7 @@  unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 
 	return type_unification_real (tparms, targs, TYPE_ARG_TYPES (parm),
 				      args, nargs, 1, DEDUCE_EXACT,
-				      LOOKUP_NORMAL, explain_p);
+				      LOOKUP_NORMAL, NULL, explain_p);
       }
 
     case OFFSET_TYPE:
@@ -20914,7 +20932,7 @@  do_auto_deduction (tree type, tree init, tree auto_node)
 	= build_tree_list (NULL_TREE, TYPE_NAME (auto_node));
       val = type_unification_real (tparms, targs, parms, &init, 1, 0,
 				   DEDUCE_CALL, LOOKUP_NORMAL,
-				   /*explain_p=*/false);
+				   NULL, /*explain_p=*/false);
       if (val > 0)
 	{
 	  if (processing_template_decl)
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index f821754..0a6c775 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -155,6 +155,17 @@  push_deferring_access_checks (deferring_kind deferring)
     }
 }
 
+/* Save the current deferred access states and start deferred access
+   checking, continuing the set of deferred checks in CHECKS.  */
+
+void
+reopen_deferring_access_checks (vec<deferred_access_check, va_gc> * checks)
+{
+  push_deferring_access_checks (dk_deferred);
+  if (!deferred_access_no_check)
+    deferred_access_stack->last().deferred_access_checks = checks;
+}
+
 /* Resume deferring access checks again after we stopped doing
    this previously.  */
 
diff --git a/gcc/testsuite/g++.dg/template/access27.C b/gcc/testsuite/g++.dg/template/access27.C
new file mode 100644
index 0000000..967e10f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access27.C
@@ -0,0 +1,17 @@ 
+// PR c++/57550
+
+template <bool (double)> bool Wrapper(double);
+template <class T> void MakeHandler(bool (T));
+
+class Handler
+{
+public:
+  template <typename T> static void SetPrimitiveHandlers()
+  {
+    MakeHandler(Wrapper<Append<T> >);
+  }
+private :
+  template <typename T> static bool Append(T);
+};
+
+template void Handler::SetPrimitiveHandlers<double>();