diff mbox

[C++,RFC,/] PR 51213 ("access control under SFINAE")

Message ID 50055E76.8030500@oracle.com
State New
Headers show

Commit Message

Paolo Carlini July 17, 2012, 12:45 p.m. UTC
Hi,

On 07/16/2012 07:39 PM, Jason Merrill wrote:
> On 07/14/2012 05:54 AM, Paolo Carlini wrote:
>> The above change of yours appear to imply that, at variance with what I
>> had in my first draft, perform_typedefs_access_check shouldn't really
>> gain a tsubst_flags_t argument, because now it's called by
>> instantiate_decl and instantiate_class_template_1 (from which I was
>> passing a true / tf_error). Makes sense?
> That makes sense to me.
Excellent. Then the below (which incorporates your access7 patch) 
bootstraps and regtests fine on x86_64-linux. As you can see, I'm 
consistently using tsubst_flags_t parameters and, in enforce_access, I 
make sure that in C++98 mode we emit hard errors even in SFINAE contexts.

Let me know how you want proceed, if you want now a ChangeLog entry for 
my bits or whatelse...

Thanks!
Paolo.

///////////////////////////

Comments

Jason Merrill July 17, 2012, 2:10 p.m. UTC | #1
On 07/17/2012 08:45 AM, Paolo Carlini wrote:
> -check_default_argument (tree decl, tree arg)
> +check_default_argument (tree decl, tree arg, tsubst_flags_t complain)

Hmm, I don't think substitution of default arguments can cause deduction 
failure; it happens after we've chosen which function to call.  What was 
the motivation for the default argument changes?

> +    tmp = error_mark_node;

Let's use a more informative name than "tmp" for these flags.

> -void
> -perform_deferred_access_checks (void)
> +bool
> +perform_deferred_access_checks (tsubst_flags_t complain)

Need to document what the return value means.

> -  if (complain & tf_error)
> -    perform_or_defer_access_check (TYPE_BINFO (context), t, t);
>
> +  if (!perform_or_defer_access_check (TYPE_BINFO (context), t, t, complain))
> +    return error_mark_node;

These changes along with the enforce_access handling of cxx_dialect 
break C++03 code that currently works, such as

template <class T>
class A
{
   typedef T I;
};

template <class T>
void f(typename T::I);

template <class T>
void f(int);

int main()
{
   f<A<float> > (1);
}

Under the C++03 rules, we don't get access errors when generating 
overload candidates, we get them when the function is instantiated (i.e. 
in instantiate_decl).

Hmm, now that I look at the code in instantiate_decl for re-substituting 
to get additional errors, I guess I should have factored that code out 
into a separate function and used it in the access7 patch rather than 
add handling of FNDECL_RECHECK_ACCESS_P in tsubst_decl.

Jason
diff mbox

Patch

Index: libstdc++-v3/testsuite/20_util/pair/noncopyable.cc
===================================================================
--- libstdc++-v3/testsuite/20_util/pair/noncopyable.cc	(revision 0)
+++ libstdc++-v3/testsuite/20_util/pair/noncopyable.cc	(revision 0)
@@ -0,0 +1,39 @@ 
+// { dg-do compile }
+// { dg-options "-std=gnu++11" }
+
+// Copyright (C) 2012 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <utility>
+
+// PR c++/51213
+class Uncopyable
+{
+  Uncopyable(const Uncopyable&);
+ public:
+  Uncopyable() = default;
+};
+
+struct ContainsUncopyable
+{
+  std::pair<Uncopyable, int> pv;
+};
+
+void foo()
+{
+  ContainsUncopyable c;
+}
Index: gcc/testsuite/g++.dg/cpp0x/sfinae37.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/sfinae37.C	(revision 0)
+++ gcc/testsuite/g++.dg/cpp0x/sfinae37.C	(revision 0)
@@ -0,0 +1,22 @@ 
+// PR c++/51213
+// { dg-do compile { target c++11 } }
+
+class C {
+  typedef int type;
+};
+
+template<class T, class = typename T::type>
+auto f(int) -> char;
+
+template<class>
+auto f(...) -> char (&)[2];
+
+static_assert(sizeof(f<C>(0)) == 2, "Ouch");
+
+template<class T>
+auto g(int) -> decltype(typename T::type(), char());
+
+template<class>
+auto g(...) -> char (&)[2];
+
+static_assert(sizeof(g<C>(0)) == 2, "Ouch");
Index: gcc/testsuite/g++.dg/template/access7.C
===================================================================
--- gcc/testsuite/g++.dg/template/access7.C	(revision 189572)
+++ gcc/testsuite/g++.dg/template/access7.C	(working copy)
@@ -14,5 +14,5 @@  typename A::T* f (A) {			// { dg-error "this conte
 }
 
 void g () {
-  f (S<int> ());			// { dg-message "required" }
+  f (S<int> ());			// { dg-message "required|no match" }
 }
Index: gcc/testsuite/g++.dg/template/sfinae10.C
===================================================================
--- gcc/testsuite/g++.dg/template/sfinae10.C	(revision 189572)
+++ gcc/testsuite/g++.dg/template/sfinae10.C	(working copy)
@@ -81,19 +81,19 @@  struct Y { };
 
 struct Z {
 private:
-  Z operator+(); // { dg-error "is private" }
-  Z operator-(); // { dg-error "is private" }
-  int operator*(); // { dg-error "is private" }
-  Z operator~(); // { dg-error "is private" } 
-  bool operator!(); // { dg-error "is private" }  
-  Z& operator++(); // { dg-error "is private" }  
-  Z& operator--(); // { dg-error "is private" }  
-  Z& operator++(int); // { dg-error "is private" }  
-  Z& operator--(int); // { dg-error "is private" }  
+  Z operator+(); // { dg-error "is private" "" { target c++98 } }
+  Z operator-(); // { dg-error "is private" "" { target c++98 } }
+  int operator*(); // { dg-error "is private" "" { target c++98 } }
+  Z operator~(); // { dg-error "is private" "" { target c++98 } } 
+  bool operator!(); // { dg-error "is private" "" { target c++98 } }  
+  Z& operator++(); // { dg-error "is private" "" { target c++98 } }  
+  Z& operator--(); // { dg-error "is private" "" { target c++98 } }  
+  Z& operator++(int); // { dg-error "is private" "" { target c++98 } }  
+  Z& operator--(int); // { dg-error "is private" "" { target c++98 } }  
 };
 
 // has_unary_plus
-DEFINE_PREFIX_UNARY_TRAIT(has_unary_plus, +); // { dg-error "within this context" }
+DEFINE_PREFIX_UNARY_TRAIT(has_unary_plus, +); // { dg-error "within this context" "" { target c++98 } }
 STATIC_ASSERT((has_unary_plus<int>::value));
 STATIC_ASSERT((!has_unary_plus<int X::*>::value));
 STATIC_ASSERT((has_unary_plus<W>::value));
@@ -101,7 +101,7 @@  STATIC_ASSERT((has_unary_plus<X>::value));
 STATIC_ASSERT((!has_unary_plus<Y>::value));
 
 // is_negatable
-DEFINE_PREFIX_UNARY_TRAIT(is_negatable, -); // { dg-error "within this context" }
+DEFINE_PREFIX_UNARY_TRAIT(is_negatable, -); // { dg-error "within this context" "" { target c++98 } }
 STATIC_ASSERT((is_negatable<int>::value));
 STATIC_ASSERT((!is_negatable<int X::*>::value));
 STATIC_ASSERT((is_negatable<W>::value));
@@ -109,7 +109,7 @@  STATIC_ASSERT((is_negatable<X>::value));
 STATIC_ASSERT((!is_negatable<Y>::value));
 
 // is_dereferenceable
-DEFINE_PREFIX_UNARY_TRAIT(is_dereferenceable, *); // { dg-error "within this context" }
+DEFINE_PREFIX_UNARY_TRAIT(is_dereferenceable, *); // { dg-error "within this context" "" { target c++98 } }
 STATIC_ASSERT((!is_dereferenceable<int>::value));
 STATIC_ASSERT((is_dereferenceable<int*>::value));
 STATIC_ASSERT((is_dereferenceable<W>::value));
@@ -117,7 +117,7 @@  STATIC_ASSERT((is_dereferenceable<X>::value));
 STATIC_ASSERT((!is_dereferenceable<Y>::value));
 
 // has_bitwise_not
-DEFINE_PREFIX_UNARY_TRAIT(has_bitwise_not, ~); // { dg-error "within this context" }
+DEFINE_PREFIX_UNARY_TRAIT(has_bitwise_not, ~); // { dg-error "within this context" "" { target c++98 } }
 STATIC_ASSERT((has_bitwise_not<int>::value));
 STATIC_ASSERT((!has_bitwise_not<int*>::value));
 STATIC_ASSERT((has_bitwise_not<W>::value));
@@ -125,7 +125,7 @@  STATIC_ASSERT((has_bitwise_not<X>::value));
 STATIC_ASSERT((!has_bitwise_not<Y>::value));
 
 // has_truth_not
-DEFINE_PREFIX_UNARY_TRAIT(has_truth_not, !); // { dg-error "within this context" }
+DEFINE_PREFIX_UNARY_TRAIT(has_truth_not, !); // { dg-error "within this context" "" { target c++98 } }
 STATIC_ASSERT((has_truth_not<int>::value));
 STATIC_ASSERT((has_truth_not<int*>::value));
 STATIC_ASSERT((has_truth_not<W>::value));
@@ -133,7 +133,7 @@  STATIC_ASSERT((has_truth_not<X>::value));
 STATIC_ASSERT((!has_truth_not<Y>::value));
 
 // has_preincrement
-DEFINE_PREFIX_UNARY_TRAIT(has_preincrement, ++); // { dg-error "within this context" }
+DEFINE_PREFIX_UNARY_TRAIT(has_preincrement, ++); // { dg-error "within this context" "" { target c++98 } }
 STATIC_ASSERT((has_preincrement<int>::value));
 STATIC_ASSERT((has_preincrement<int*>::value));
 STATIC_ASSERT((!has_preincrement<int X::*>::value));
@@ -142,7 +142,7 @@  STATIC_ASSERT((has_preincrement<X>::value));
 STATIC_ASSERT((!has_preincrement<Y>::value));
 
 // has_predecrement
-DEFINE_PREFIX_UNARY_TRAIT(has_predecrement, --); // { dg-error "within this context" }
+DEFINE_PREFIX_UNARY_TRAIT(has_predecrement, --); // { dg-error "within this context" "" { target c++98 } }
 STATIC_ASSERT((has_predecrement<int>::value));
 STATIC_ASSERT((has_predecrement<int*>::value));
 STATIC_ASSERT((!has_predecrement<int X::*>::value));
@@ -151,7 +151,7 @@  STATIC_ASSERT((has_predecrement<X>::value));
 STATIC_ASSERT((!has_predecrement<Y>::value));
 
 // has_postincrement
-DEFINE_POSTFIX_UNARY_TRAIT(has_postincrement, ++); // { dg-error "within this context" }
+DEFINE_POSTFIX_UNARY_TRAIT(has_postincrement, ++); // { dg-error "within this context" "" { target c++98 } }
 STATIC_ASSERT((has_postincrement<int>::value));
 STATIC_ASSERT((has_postincrement<int*>::value));
 STATIC_ASSERT((!has_postincrement<int X::*>::value));
@@ -160,7 +160,7 @@  STATIC_ASSERT((has_postincrement<X>::value));
 STATIC_ASSERT((!has_postincrement<Y>::value));
 
 // has_postdecrement
-DEFINE_POSTFIX_UNARY_TRAIT(has_postdecrement, --); // { dg-error "within this context" }
+DEFINE_POSTFIX_UNARY_TRAIT(has_postdecrement, --); // { dg-error "within this context" "" { target c++98 } }
 STATIC_ASSERT((has_postdecrement<int>::value));
 STATIC_ASSERT((has_postdecrement<int*>::value));
 STATIC_ASSERT((!has_postdecrement<int X::*>::value));
@@ -169,13 +169,12 @@  STATIC_ASSERT((has_postdecrement<X>::value));
 STATIC_ASSERT((!has_postdecrement<Y>::value));
 
 // Check for private members
-STATIC_ASSERT((has_unary_plus<Z>::value)); // { dg-message "required from here" }
-STATIC_ASSERT((is_negatable<Z>::value)); // { dg-message "required from here" }
-STATIC_ASSERT((is_dereferenceable<Z>::value)); // { dg-message "required from here" }
-STATIC_ASSERT((has_bitwise_not<Z>::value)); // { dg-message "required from here" }
-STATIC_ASSERT((has_truth_not<Z>::value)); // { dg-message "required from here" }
-STATIC_ASSERT((has_preincrement<Z>::value)); // { dg-message "required from here" }
-STATIC_ASSERT((has_predecrement<Z>::value)); // { dg-message "required from here" }
-STATIC_ASSERT((has_postincrement<Z>::value)); // { dg-message "required from here" }
-STATIC_ASSERT((has_postdecrement<Z>::value)); // { dg-message "required from here" }
-
+STATIC_ASSERT((!has_unary_plus<Z>::value));
+STATIC_ASSERT((!is_negatable<Z>::value));
+STATIC_ASSERT((!is_dereferenceable<Z>::value));
+STATIC_ASSERT((!has_bitwise_not<Z>::value));
+STATIC_ASSERT((!has_truth_not<Z>::value));
+STATIC_ASSERT((!has_preincrement<Z>::value));
+STATIC_ASSERT((!has_predecrement<Z>::value));
+STATIC_ASSERT((!has_postincrement<Z>::value));
+STATIC_ASSERT((!has_postdecrement<Z>::value));
Index: gcc/testsuite/g++.dg/template/sfinae6_neg.C
===================================================================
--- gcc/testsuite/g++.dg/template/sfinae6_neg.C	(revision 189572)
+++ gcc/testsuite/g++.dg/template/sfinae6_neg.C	(working copy)
@@ -14,7 +14,7 @@  template<typename T> struct enable_if<false, T> {
 template<typename F, typename T1, typename T2>
   typename enable_if<sizeof(create_a<F>()(create_a<T1>(), create_a<T2>()), 1),
 		     yes_type>::type
-  check_is_callable2(type<F>, type<T1>, type<T2>); // { dg-error "within this context" }
+  check_is_callable2(type<F>, type<T1>, type<T2>); // { dg-error "within this context" "" { target c++98 } }
 
 no_type check_is_callable2(...);
 
@@ -52,7 +52,7 @@  struct F {
   void operator()(A, A);
 
 private:
-  void operator()(B, B); // { dg-error "is private" }
+  void operator()(B, B); // { dg-error "is private" "" { target c++98 } }
 };
 
-STATIC_ASSERT((is_callable2<F, B, B>::value));
+STATIC_ASSERT((!is_callable2<F, B, B>::value));
Index: gcc/cp/init.c
===================================================================
--- gcc/cp/init.c	(revision 189561)
+++ gcc/cp/init.c	(working copy)
@@ -1876,9 +1876,9 @@  build_offset_ref (tree type, tree member, bool add
 	       (or any class derived from that class).  */
 	  if (address_p && DECL_P (t)
 	      && DECL_NONSTATIC_MEMBER_P (t))
-	    perform_or_defer_access_check (TYPE_BINFO (type), t, t);
+	    perform_or_defer_access_check (TYPE_BINFO (type), t, t, tf_error);
 	  else
-	    perform_or_defer_access_check (basebinfo, t, t);
+	    perform_or_defer_access_check (basebinfo, t, t, tf_error);
 
 	  if (DECL_STATIC_FUNCTION_P (t))
 	    return t;
@@ -1891,7 +1891,7 @@  build_offset_ref (tree type, tree member, bool add
     /* We need additional test besides the one in
        check_accessibility_of_qualified_id in case it is
        a pointer to non-static member.  */
-    perform_or_defer_access_check (TYPE_BINFO (type), member, member);
+    perform_or_defer_access_check (TYPE_BINFO (type), member, member, tf_error);
 
   if (!address_p)
     {
Index: gcc/cp/class.c
===================================================================
--- gcc/cp/class.c	(revision 189561)
+++ gcc/cp/class.c	(working copy)
@@ -1189,7 +1189,7 @@  alter_access (tree t, tree fdecl, tree access)
     }
   else
     {
-      perform_or_defer_access_check (TYPE_BINFO (t), fdecl, fdecl);
+      perform_or_defer_access_check (TYPE_BINFO (t), fdecl, fdecl, tf_error);
       DECL_ACCESS (fdecl) = tree_cons (t, access, DECL_ACCESS (fdecl));
       return 1;
     }
@@ -7138,7 +7138,7 @@  resolve_address_of_overloaded_function (tree targe
       && DECL_FUNCTION_MEMBER_P (fn))
     {
       gcc_assert (access_path);
-      perform_or_defer_access_check (access_path, fn, fn);
+      perform_or_defer_access_check (access_path, fn, fn, tf_error);
     }
 
   if (TYPE_PTRFN_P (target_type) || TYPE_PTRMEMFUNC_P (target_type))
Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 189561)
+++ gcc/cp/decl.c	(working copy)
@@ -3306,10 +3306,10 @@  make_typename_type (tree context, tree name, enum
 	       context, name, t);
       return error_mark_node;
     }
-  
-  if (complain & tf_error)
-    perform_or_defer_access_check (TYPE_BINFO (context), t, t);
 
+  if (!perform_or_defer_access_check (TYPE_BINFO (context), t, t, complain))
+    return error_mark_node;
+
   /* If we are currently parsing a template and if T is a typedef accessed
      through CONTEXT then we need to remember and check access of T at
      template instantiation time.  */
@@ -3378,8 +3378,9 @@  make_unbound_class_template (tree context, tree na
 	  return error_mark_node;
 	}
 
-      if (complain & tf_error)
-	perform_or_defer_access_check (TYPE_BINFO (context), tmpl, tmpl);
+      if (!perform_or_defer_access_check (TYPE_BINFO (context), tmpl, tmpl,
+					  complain))
+	return error_mark_node;
 
       return tmpl;
     }
@@ -6647,7 +6648,8 @@  register_dtor_fn (tree decl)
       gcc_assert (idx >= 0);
       cleanup = VEC_index (tree, CLASSTYPE_METHOD_VEC (type), idx);
       /* Make sure it is accessible.  */
-      perform_or_defer_access_check (TYPE_BINFO (type), cleanup, cleanup);
+      perform_or_defer_access_check (TYPE_BINFO (type), cleanup, cleanup,
+				     tf_error);
     }
   else
     {
@@ -10569,7 +10571,7 @@  local_variable_p_walkfn (tree *tp, int *walk_subtr
    DECL, if there is no DECL available.  */
 
 tree
-check_default_argument (tree decl, tree arg)
+check_default_argument (tree decl, tree arg, tsubst_flags_t complain)
 {
   tree var;
   tree decl_type;
@@ -10602,15 +10604,17 @@  tree
      parameter type.  */
   if (!TREE_TYPE (arg)
       || !can_convert_arg (decl_type, TREE_TYPE (arg), arg, LOOKUP_NORMAL,
-			   tf_warning_or_error))
+			   complain))
     {
-      if (decl)
-	error ("default argument for %q#D has type %qT",
-	       decl, TREE_TYPE (arg));
-      else
-	error ("default argument for parameter of type %qT has type %qT",
-	       decl_type, TREE_TYPE (arg));
-
+      if (complain & tf_error)
+	{
+	  if (decl)
+	    error ("default argument for %q#D has type %qT",
+		   decl, TREE_TYPE (arg));
+	  else
+	    error ("default argument for parameter of type %qT has type %qT",
+		   decl_type, TREE_TYPE (arg));
+	}
       return error_mark_node;
     }
 
@@ -10620,8 +10624,9 @@  tree
       && null_ptr_cst_p (arg)
       && !NULLPTR_TYPE_P (TREE_TYPE (arg)))
     {
-      warning (OPT_Wzero_as_null_pointer_constant,
-	       "zero as null pointer constant");
+      if (complain & tf_warning)
+	warning (OPT_Wzero_as_null_pointer_constant,
+		 "zero as null pointer constant");
       return nullptr_node;
     }
 
@@ -10635,10 +10640,14 @@  tree
   var = cp_walk_tree_without_duplicates (&arg, local_variable_p_walkfn, NULL);
   if (var)
     {
-      if (DECL_NAME (var) == this_identifier)
-	permerror (input_location, "default argument %qE uses %qD", arg, var);
-      else
-	error ("default argument %qE uses local variable %qD", arg, var);
+      if (complain & tf_error)
+	{
+	  if (DECL_NAME (var) == this_identifier)
+	    permerror (input_location, "default argument %qE uses %qD",
+		       arg, var);
+	  else
+	    error ("default argument %qE uses local variable %qD", arg, var);
+	}
       return error_mark_node;
     }
 
@@ -10789,7 +10798,7 @@  grokparms (tree parmlist, tree *parms)
 	  if (any_error)
 	    init = NULL_TREE;
 	  else if (init && !processing_template_decl)
-	    init = check_default_argument (decl, init);
+	    init = check_default_argument (decl, init, tf_warning_or_error);
 	}
 
       DECL_CHAIN (decl) = decls;
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 189561)
+++ gcc/cp/pt.c	(working copy)
@@ -181,7 +181,7 @@  static int coerce_template_template_parms (tree, t
 					   tree, tree);
 static bool template_template_parm_bindings_ok_p (tree, tree);
 static int template_args_equal (tree, tree);
-static void tsubst_default_arguments (tree);
+static void tsubst_default_arguments (tree, tsubst_flags_t);
 static tree for_each_template_parm_r (tree *, int *, void *);
 static tree copy_default_args_to_explicit_spec_1 (tree, tree);
 static void copy_default_args_to_explicit_spec (tree);
@@ -8370,7 +8370,7 @@  perform_typedefs_access_check (tree tmpl, tree tar
          of the use of the typedef.  */
       input_location = iter->locus;
       perform_or_defer_access_check (TYPE_BINFO (type_scope),
-				     type_decl, type_decl);
+				     type_decl, type_decl, tf_error);
     }
     input_location = saved_location;
 }
@@ -8877,7 +8877,7 @@  instantiate_class_template_1 (tree type)
      added to the template at parsing time. Let's get those and perform
      the access checks then.  */
   perform_typedefs_access_check (pattern, args);
-  perform_deferred_access_checks ();
+  perform_deferred_access_checks (tf_error);
   pop_nested_class ();
   maximum_field_alignment = saved_maximum_field_alignment;
   if (!fn_context)
@@ -9596,7 +9596,8 @@  tsubst_aggr_type (tree t,
    FN), which has the indicated TYPE.  */
 
 tree
-tsubst_default_argument (tree fn, tree type, tree arg)
+tsubst_default_argument (tree fn, tree type, tree arg,
+			 tsubst_flags_t complain)
 {
   tree saved_class_ptr = NULL_TREE;
   tree saved_class_ref = NULL_TREE;
@@ -9635,7 +9636,7 @@  tree
      stack.  */
   ++function_depth;
   arg = tsubst_expr (arg, DECL_TI_ARGS (fn),
-		     tf_warning_or_error, NULL_TREE,
+		     complain, NULL_TREE,
 		     /*integral_constant_expression_p=*/false);
   --function_depth;
   pop_deferring_access_checks();
@@ -9648,7 +9649,7 @@  tree
     }
 
   /* Make sure the default argument is reasonable.  */
-  arg = check_default_argument (type, arg);
+  arg = check_default_argument (type, arg, complain);
 
   pop_access_scope (fn);
 
@@ -9658,7 +9659,7 @@  tree
 /* Substitute into all the default arguments for FN.  */
 
 static void
-tsubst_default_arguments (tree fn)
+tsubst_default_arguments (tree fn, tsubst_flags_t complain)
 {
   tree arg;
   tree tmpl_args;
@@ -9679,7 +9680,8 @@  static void
     if (TREE_PURPOSE (arg))
       TREE_PURPOSE (arg) = tsubst_default_argument (fn,
 						    TREE_VALUE (arg),
-						    TREE_PURPOSE (arg));
+						    TREE_PURPOSE (arg),
+						    complain);
 }
 
 /* Substitute the ARGS into the T, which is a _DECL.  Return the
@@ -9870,10 +9872,13 @@  tsubst_decl (tree t, tree args, tsubst_flags_t com
 	    hash = hash_tmpl_and_args (gen_tmpl, argvec);
 	    spec = retrieve_specialization (gen_tmpl, argvec, hash);
 
+	    r = spec;
 	    if (spec)
 	      {
-		r = spec;
-		break;
+		if (FNDECL_RECHECK_ACCESS_P (spec) && (complain & tf_error))
+		  /* Reinstantiate to get access errors.  */;
+		else
+		  break;
 	      }
 
 	    /* We can see more levels of arguments than parameters if
@@ -9949,6 +9954,13 @@  tsubst_decl (tree t, tree args, tsubst_flags_t com
 	if (type == error_mark_node)
 	  RETURN (error_mark_node);
 
+	if (r)
+	  {
+	    /* We're done reinstantiating for access errors.  */
+	    gcc_assert (FNDECL_RECHECK_ACCESS_P (r));
+	    break;
+	  }
+
 	/* We do NOT check for matching decls pushed separately at this
 	   point, as they may not represent instantiations of this
 	   template, and in any case are considered separate under the
@@ -10021,7 +10033,7 @@  tsubst_decl (tree t, tree args, tsubst_flags_t com
 	    if (!member
 		&& !PRIMARY_TEMPLATE_P (gen_tmpl)
 		&& !uses_template_parms (argvec))
-	      tsubst_default_arguments (r);
+	      tsubst_default_arguments (r, complain);
 	  }
 	else
 	  DECL_TEMPLATE_INFO (r) = NULL_TREE;
@@ -14294,6 +14306,7 @@  instantiate_template_1 (tree tmpl, tree orig_args,
   tree fndecl;
   tree gen_tmpl;
   tree spec;
+  tree tmp = NULL_TREE;
 
   if (tmpl == error_mark_node)
     return error_mark_node;
@@ -14341,7 +14354,13 @@  instantiate_template_1 (tree tmpl, tree orig_args,
 	      || fndecl == NULL_TREE);
 
   if (spec != NULL_TREE)
-    return spec;
+    {
+      if (FNDECL_RECHECK_ACCESS_P (spec)
+	  && (complain & tf_error))
+	/* Do the instantiation again, we're out of SFINAE context.  */;
+      else
+	return spec;
+    }
 
   if (check_instantiated_args (gen_tmpl, INNERMOST_TEMPLATE_ARGS (targ_ptr),
 			       complain))
@@ -14380,7 +14399,8 @@  instantiate_template_1 (tree tmpl, tree orig_args,
   /* Now we know the specialization, compute access previously
      deferred.  */
   push_access_scope (fndecl);
-  perform_deferred_access_checks ();
+  if (!perform_deferred_access_checks (complain))
+    tmp = error_mark_node;
   pop_access_scope (fndecl);
   pop_deferring_access_checks ();
 
@@ -14391,6 +14411,16 @@  instantiate_template_1 (tree tmpl, tree orig_args,
   if (DECL_CHAIN (gen_tmpl) && DECL_CLONED_FUNCTION_P (DECL_CHAIN (gen_tmpl)))
     clone_function_decl (fndecl, /*update_method_vec_p=*/0);
 
+  if (tmp)
+    {
+      if (!(complain & tf_error))
+	{
+	  /* Remember to reinstantiate when we're out of SFINAE so the user
+	     can see the errors.  */
+	  FNDECL_RECHECK_ACCESS_P (fndecl) = true;
+	}
+      return error_mark_node;
+    }
   return fndecl;
 }
 
Index: gcc/cp/semantics.c
===================================================================
--- gcc/cp/semantics.c	(revision 189561)
+++ gcc/cp/semantics.c	(working copy)
@@ -223,7 +223,7 @@  pop_to_parent_deferring_access_checks (void)
       if (ptr->deferring_access_checks_kind == dk_no_deferred)
 	{
 	  /* Check access.  */
-	  perform_access_checks (checks);
+	  perform_access_checks (checks, tf_error);
 	}
       else
 	{
@@ -254,23 +254,26 @@  pop_to_parent_deferring_access_checks (void)
    is the BINFO indicating the qualifying scope used to access the
    DECL node stored in the TREE_VALUE of the node.  */
 
-void
-perform_access_checks (VEC (deferred_access_check,gc)* checks)
+bool
+perform_access_checks (VEC (deferred_access_check,gc)* checks,
+		       tsubst_flags_t complain)
 {
   int i;
   deferred_access_check *chk;
   location_t loc = input_location;
+  bool tmp = true;
 
   if (!checks)
-    return;
+    return true;
 
   FOR_EACH_VEC_ELT (deferred_access_check, checks, i, chk)
     {
       input_location = chk->loc;
-      enforce_access (chk->binfo, chk->decl, chk->diag_decl);
+      tmp &= enforce_access (chk->binfo, chk->decl, chk->diag_decl, complain);
     }
 
   input_location = loc;
+  return (complain & tf_error) ? true : tmp;
 }
 
 /* Perform the deferred access checks.
@@ -289,17 +292,18 @@  pop_to_parent_deferring_access_checks (void)
    We have to perform deferred access of `A::X', first with `A::a',
    next with `x'.  */
 
-void
-perform_deferred_access_checks (void)
+bool
+perform_deferred_access_checks (tsubst_flags_t complain)
 {
-  perform_access_checks (get_deferred_access_checks ());
+  return perform_access_checks (get_deferred_access_checks (), complain);
 }
 
 /* Defer checking the accessibility of DECL, when looked up in
    BINFO. DIAG_DECL is the declaration to use to print diagnostics.  */
 
-void
-perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl)
+bool
+perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl,
+			       tsubst_flags_t complain)
 {
   int i;
   deferred_access *ptr;
@@ -310,7 +314,7 @@  pop_to_parent_deferring_access_checks (void)
   /* Exit if we are in a context that no access checking is performed.
      */
   if (deferred_access_no_check)
-    return;
+    return true;
 
   gcc_assert (TREE_CODE (binfo) == TREE_BINFO);
 
@@ -319,8 +323,8 @@  pop_to_parent_deferring_access_checks (void)
   /* If we are not supposed to defer access checks, just check now.  */
   if (ptr->deferring_access_checks_kind == dk_no_deferred)
     {
-      enforce_access (binfo, decl, diag_decl);
-      return;
+      bool tmp = enforce_access (binfo, decl, diag_decl, complain);
+      return (complain & tf_error) ? true : tmp;
     }
 
   /* See if we are already going to perform this check.  */
@@ -330,7 +334,7 @@  pop_to_parent_deferring_access_checks (void)
       if (chk->decl == decl && chk->binfo == binfo &&
 	  chk->diag_decl == diag_decl)
 	{
-	  return;
+	  return true;
 	}
     }
   /* If not, record the check.  */
@@ -341,6 +345,8 @@  pop_to_parent_deferring_access_checks (void)
   new_access->decl = decl;
   new_access->diag_decl = diag_decl;
   new_access->loc = input_location;
+
+  return true;
 }
 
 /* Used by build_over_call in LOOKUP_SPECULATIVE mode: return whether DECL
@@ -349,7 +355,7 @@  pop_to_parent_deferring_access_checks (void)
 
 bool
 speculative_access_check (tree binfo, tree decl, tree diag_decl,
-			  bool complain)
+			  tsubst_flags_t complain)
 {
   if (deferred_access_no_check)
     return true;
@@ -359,8 +365,8 @@  speculative_access_check (tree binfo, tree decl, t
   if (!accessible_p (binfo, decl, true))
     {
       /* Unless we're under maybe_explain_implicit_delete.  */
-      if (complain)
-	enforce_access (binfo, decl, diag_decl);
+      if (complain & tf_error)
+	enforce_access (binfo, decl, diag_decl, tf_error);
       return false;
     }
 
@@ -1611,7 +1617,7 @@  finish_non_static_data_member (tree decl, tree obj
       tree access_type = TREE_TYPE (object);
 
       perform_or_defer_access_check (TYPE_BINFO (access_type), decl,
-				     decl);
+				     decl, tf_error);
 
       /* If the data member was named `C::M', convert `*this' to `C'
 	 first.  */
@@ -1733,7 +1739,7 @@  check_accessibility_of_qualified_id (tree decl,
       && CLASS_TYPE_P (qualifying_type)
       && !dependent_type_p (qualifying_type))
     perform_or_defer_access_check (TYPE_BINFO (qualifying_type), decl,
-				   decl);
+				   decl, tf_error);
 }
 
 /* EXPR is the result of a qualified-id.  The QUALIFYING_CLASS was the
@@ -3336,7 +3342,7 @@  finish_id_expression (tree id_expression,
 		{
 		  tree path = currently_open_derived_class (context);
 		  perform_or_defer_access_check (TYPE_BINFO (path),
-						 decl, decl);
+						 decl, decl, tf_error);
 		}
 	    }
 
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 189561)
+++ gcc/cp/parser.c	(working copy)
@@ -10515,7 +10515,7 @@  cp_parser_simple_declaration (cp_parser* parser,
       if (cp_parser_declares_only_class_p (parser))
 	shadow_tag (&decl_specifiers);
       /* Perform any deferred access checks.  */
-      perform_deferred_access_checks ();
+      perform_deferred_access_checks (tf_error);
     }
 
   /* Consume the `;'.  */
@@ -12416,7 +12416,7 @@  cp_parser_template_id (cp_parser *parser,
 	  FOR_EACH_VEC_ELT (deferred_access_check, access_check, i, chk)
 	    perform_or_defer_access_check (chk->binfo,
 					   chk->decl,
-					   chk->diag_decl);
+					   chk->diag_decl, tf_error);
 	}
       /* Return the stored value.  */
       return check_value->value;
@@ -15751,7 +15751,7 @@  cp_parser_init_declarator (cp_parser* parser,
 
       /* Perform the access control checks for the declarator and the
 	 decl-specifiers.  */
-      perform_deferred_access_checks ();
+      perform_deferred_access_checks (true);
 
       /* Restore the saved value.  */
       if (TREE_CODE (decl) == FUNCTION_DECL)
@@ -21017,7 +21017,7 @@  cp_parser_function_definition_from_specifiers_and_
      did not check, check them now.  We must wait until we are in the
      scope of the function to perform the checks, since the function
      might be a friend.  */
-  perform_deferred_access_checks ();
+  perform_deferred_access_checks (tf_error);
 
   if (!success_p)
     {
@@ -21311,7 +21311,7 @@  static void
 cp_parser_perform_template_parameter_access_checks (VEC (deferred_access_check,gc)* checks)
 {
   ++processing_template_parmlist;
-  perform_access_checks (checks);
+  perform_access_checks (checks, tf_error);
   --processing_template_parmlist;
 }
 
@@ -21838,7 +21838,8 @@  cp_parser_late_parse_one_default_arg (cp_parser *p
       /* In a non-template class, check conversions now.  In a template,
 	 we'll wait and instantiate these as needed.  */
       if (TREE_CODE (decl) == PARM_DECL)
-	parsed_arg = check_default_argument (parmtype, parsed_arg);
+	parsed_arg = check_default_argument (parmtype, parsed_arg,
+					     tf_warning_or_error);
       else
 	{
 	  int flags = LOOKUP_IMPLICIT;
@@ -22760,7 +22761,7 @@  cp_parser_pre_parsed_nested_name_specifier (cp_par
       FOR_EACH_VEC_ELT (deferred_access_check, checks, i, chk)
 	perform_or_defer_access_check (chk->binfo,
 				       chk->decl,
-				       chk->diag_decl);
+				       chk->diag_decl, tf_error);
     }
   /* Set the scope from the stored value.  */
   parser->scope = check_value->value;
@@ -24018,7 +24019,7 @@  cp_parser_objc_method_definition_list (cp_parser*
 	  if (!(ptk->type == CPP_PLUS || ptk->type == CPP_MINUS 
 		|| ptk->type == CPP_EOF || ptk->keyword == RID_AT_END))
 	    {
-	      perform_deferred_access_checks ();
+	      perform_deferred_access_checks (tf_error);
 	      stop_deferring_access_checks ();
 	      meth = cp_parser_function_definition_after_declarator (parser,
 								     false);
Index: gcc/cp/call.c
===================================================================
--- gcc/cp/call.c	(revision 189561)
+++ gcc/cp/call.c	(working copy)
@@ -5515,7 +5515,8 @@  build_op_delete_call (enum tree_code code, tree ad
       /* If the FN is a member function, make sure that it is
 	 accessible.  */
       if (BASELINK_P (fns))
-	perform_or_defer_access_check (BASELINK_BINFO (fns), fn, fn);
+	perform_or_defer_access_check (BASELINK_BINFO (fns), fn, fn,
+				       complain);
 
       /* Core issue 901: It's ok to new a type with deleted delete.  */
       if (DECL_DELETED_FN (fn) && alloc_fn)
@@ -5573,19 +5574,23 @@  build_op_delete_call (enum tree_code code, tree ad
    the declaration to use in the error diagnostic.  */
 
 bool
-enforce_access (tree basetype_path, tree decl, tree diag_decl)
+enforce_access (tree basetype_path, tree decl, tree diag_decl,
+		tsubst_flags_t complain)
 {
   gcc_assert (TREE_CODE (basetype_path) == TREE_BINFO);
 
   if (!accessible_p (basetype_path, decl, true))
     {
-      if (TREE_PRIVATE (decl))
-	error ("%q+#D is private", diag_decl);
-      else if (TREE_PROTECTED (decl))
-	error ("%q+#D is protected", diag_decl);
-      else
-	error ("%q+#D is inaccessible", diag_decl);
-      error ("within this context");
+      if (cxx_dialect < cxx0x || complain & tf_error)
+	{
+	  if (TREE_PRIVATE (decl))
+	    error ("%q+#D is private", diag_decl);
+	  else if (TREE_PROTECTED (decl))
+	    error ("%q+#D is protected", diag_decl);
+	  else
+	    error ("%q+#D is inaccessible", diag_decl);
+	  error ("within this context");
+	}
       return false;
     }
 
@@ -6267,7 +6272,7 @@  convert_default_arg (tree type, tree arg, tree fn,
   push_defarg_context (fn);
 
   if (fn && DECL_TEMPLATE_INFO (fn))
-    arg = tsubst_default_argument (fn, type, arg);
+    arg = tsubst_default_argument (fn, type, arg, complain);
 
   /* Due to:
 
@@ -6513,11 +6518,12 @@  build_over_call (struct z_candidate *cand, int fla
       if (flags & LOOKUP_SPECULATIVE)
 	{
 	  if (!speculative_access_check (cand->access_path, access_fn, fn,
-					 complain & tf_error))
+					 complain))
 	    return error_mark_node;
 	}
-      else
-	perform_or_defer_access_check (cand->access_path, access_fn, fn);
+      else if (!perform_or_defer_access_check (cand->access_path, access_fn,
+					       fn, complain))
+	return error_mark_node;
     }
 
   /* If we're checking for implicit delete, don't bother with argument
Index: gcc/cp/cp-tree.h
===================================================================
--- gcc/cp/cp-tree.h	(revision 189561)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -78,6 +78,7 @@  c-common.h, not after.
       CONVERT_EXPR_VBASE_PATH (in CONVERT_EXPR)
       OVL_ARG_DEPENDENT (in OVERLOAD)
       PACK_EXPANSION_LOCAL_P (in *_PACK_EXPANSION)
+      TINFO_RECHECK_ACCESS_P (in TEMPLATE_INFO)
    1: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
       TI_PENDING_TEMPLATE_FLAG.
       TEMPLATE_PARMS_FOR_INLINE.
@@ -725,6 +726,14 @@  typedef struct qualified_typedef_usage_s qualified
 DEF_VEC_O (qualified_typedef_usage_t);
 DEF_VEC_ALLOC_O (qualified_typedef_usage_t,gc);
 
+/* Non-zero if this template specialization has access violations that
+   should be rechecked when the function is instantiated outside argument
+   deduction.  */
+#define TINFO_RECHECK_ACCESS_P(NODE) \
+  (TREE_LANG_FLAG_0 (TEMPLATE_INFO_CHECK (NODE)))
+#define FNDECL_RECHECK_ACCESS_P(NODE) \
+  (TINFO_RECHECK_ACCESS_P (DECL_TEMPLATE_INFO (NODE)))
+
 struct GTY(()) tree_template_info {
   struct tree_common common;
   VEC(qualified_typedef_usage_t,gc) *typedefs_needing_access_checking;
@@ -4901,7 +4910,8 @@  extern bool can_convert_arg			(tree, tree, tree, i
 						 tsubst_flags_t);
 extern bool can_convert_arg_bad			(tree, tree, tree, int,
 						 tsubst_flags_t);
-extern bool enforce_access			(tree, tree, tree);
+extern bool enforce_access			(tree, tree, tree,
+						 tsubst_flags_t);
 extern void push_defarg_context			(tree);
 extern void pop_defarg_context			(void);
 extern tree convert_default_arg			(tree, tree, tree, int,
@@ -5097,7 +5107,7 @@  extern tree static_fn_type			(tree);
 extern void revert_static_member_fn		(tree);
 extern void fixup_anonymous_aggr		(tree);
 extern tree compute_array_index_type		(tree, tree, tsubst_flags_t);
-extern tree check_default_argument		(tree, tree);
+extern tree check_default_argument		(tree, tree, tsubst_flags_t);
 typedef int (*walk_namespaces_fn)		(tree, void *);
 extern int walk_namespaces			(walk_namespaces_fn,
 						 void *);
@@ -5364,7 +5374,8 @@  extern tree maybe_process_partial_specialization (
 extern tree most_specialized_instantiation	(tree);
 extern void print_candidates			(tree);
 extern void instantiate_pending_templates	(int);
-extern tree tsubst_default_argument		(tree, tree, tree);
+extern tree tsubst_default_argument		(tree, tree, tree,
+						 tsubst_flags_t);
 extern tree tsubst (tree, tree, tsubst_flags_t, tree);
 extern tree tsubst_copy_and_build		(tree, tree, tsubst_flags_t,
 						 tree, bool, bool);
@@ -5497,10 +5508,13 @@  extern void stop_deferring_access_checks	(void);
 extern void pop_deferring_access_checks		(void);
 extern VEC (deferred_access_check,gc)* get_deferred_access_checks		(void);
 extern void pop_to_parent_deferring_access_checks (void);
-extern void perform_access_checks		(VEC (deferred_access_check,gc)*);
-extern void perform_deferred_access_checks	(void);
-extern void perform_or_defer_access_check	(tree, tree, tree);
-extern bool speculative_access_check		(tree, tree, tree, bool);
+extern bool perform_access_checks (VEC (deferred_access_check,gc)*,
+				   tsubst_flags_t);
+extern bool perform_deferred_access_checks	(tsubst_flags_t);
+extern bool perform_or_defer_access_check	(tree, tree, tree,
+						 tsubst_flags_t);
+extern bool speculative_access_check		(tree, tree, tree,
+						 tsubst_flags_t);
 extern int stmts_are_full_exprs_p		(void);
 extern void init_cp_semantics			(void);
 extern tree do_poplevel				(tree);
Index: gcc/cp/search.c
===================================================================
--- gcc/cp/search.c	(revision 189561)
+++ gcc/cp/search.c	(working copy)
@@ -1254,8 +1254,10 @@  lookup_member (tree xbasetype, tree name, int prot
       && !really_overloaded_fn (rval))
     {
       tree decl = is_overloaded_fn (rval) ? get_first_fn (rval) : rval;
-      if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
-	perform_or_defer_access_check (basetype_path, decl, decl);
+      if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)
+	  && !perform_or_defer_access_check (basetype_path, decl, decl,
+					     complain))
+	rval = error_mark_node;
     }
 
   if (errstr && protect)
Index: gcc/cp/friend.c
===================================================================
--- gcc/cp/friend.c	(revision 189561)
+++ gcc/cp/friend.c	(working copy)
@@ -166,7 +166,7 @@  add_friend (tree type, tree decl, bool complain)
 
   ctx = DECL_CONTEXT (decl);
   if (ctx && CLASS_TYPE_P (ctx) && !uses_template_parms (ctx))
-    perform_or_defer_access_check (TYPE_BINFO (ctx), decl, decl);
+    perform_or_defer_access_check (TYPE_BINFO (ctx), decl, decl, tf_error);
 
   maybe_add_class_template_decl_list (type, decl, /*friend_p=*/1);