public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
@ 2012-06-15 14:57 Paolo Carlini
  2012-06-28 10:38 ` Paolo Carlini
                   ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: Paolo Carlini @ 2012-06-15 14:57 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jason Merrill

[-- Attachment #1: Type: text/plain, Size: 3021 bytes --]

Hi,

as I mentioned a few days ago, I'm working on implementing this feature, 
which I personally consider rather high priority, from the library point 
of view too (eg, <type_traits>).

I have been making some progress - I'm attaching below what I have so 
far in my local tree - but I also think it's time to get feedback both 
about the general approach and about more specific issues with the 
testsuite. The attached leads to unexpected fails for 4 testcases in 
g++.dg, which I would bundle in 2 pairs: I (we) should obviously do 
something about both but the second one seems more subtle (and indeed 
I'm looking for guidance!). The first pair is:

FAIL: g++.dg/template/access7.C -std=c++98 (test for errors, line 8)
FAIL: g++.dg/template/access7.C -std=c++98 (test for errors, line 12)
FAIL: g++.dg/template/access7.C -std=c++98 (test for warnings, line 17)
FAIL: g++.dg/template/access7.C -std=c++98 (test for excess errors)
FAIL: g++.dg/template/access7.C -std=c++11 (test for excess errors)
FAIL: g++.dg/template/typedef11.C -std=c++98 (test for errors, line 8)
FAIL: g++.dg/template/typedef11.C -std=c++98 (test for errors, line 14)
FAIL: g++.dg/template/typedef11.C -std=c++98 (test for errors, line 25)
FAIL: g++.dg/template/typedef11.C -std=c++98 (test for excess errors)
FAIL: g++.dg/template/typedef11.C -std=c++11 (test for errors, line 8)
FAIL: g++.dg/template/typedef11.C -std=c++11 (test for errors, line 14)
FAIL: g++.dg/template/typedef11.C -std=c++11 (test for errors, line 25)
FAIL: g++.dg/template/typedef11.C -std=c++11 (test for excess errors)

The problem is that with the patch applied we get errors of the "no 
matching function" type, instead of access control errors. For example, 
for access7.C:

access7.C: In function ‘void g()’:
access7.C:17:15: error: no matching function for call to ‘f(S<int>)’
f (S<int> ()); // { dg-message "required" }
^
access7.C:17:15: note: candidate is:
access7.C:12:16: note: template<class A> typename A::T* f(A)
typename A::T* f (A) { // { dg-error "this context" }
^

I suppose this is expected, right?!? (we may have more in old-deja, have 
to re-check)

The second pair is more subtle:

FAIL: g++.dg/template/friend13.C -std=c++11 (test for excess errors)
FAIL: g++.dg/template/friend15.C -std=c++98 (test for excess errors)
FAIL: g++.dg/template/friend15.C -std=c++11 (test for excess errors)

That is, we have that the first one fails only in c++11 mode, rather 
subtle per se. And in general friend* cases seem quite unexpected to me.

Now, something rather interesting is that, clang 3.0, which I tried 
online, rejects friend13 and friend15 as patched GCC does, thus 
disagreeing with current mainline.

For example, for friend13.C, it emits an access control error for struct 
Inner in the context of line 8, like patched GCC. And, for friend15.C, 
an error @ line 18, for undeclared 'foo', whereas patched GCC gives "no 
matching function for call to ‘foo()’" for the same line.

Thanks for any help!
Paolo.

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



[-- Attachment #2: patch_51213_2 --]
[-- Type: text/plain, Size: 33724 bytes --]

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-options -std=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/sfinae10.C
===================================================================
--- gcc/testsuite/g++.dg/template/sfinae10.C	(revision 188652)
+++ 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+();
+  Z operator-();
+  int operator*();
+  Z operator~();
+  bool operator!();
+  Z& operator++();
+  Z& operator--();
+  Z& operator++(int);
+  Z& operator--(int);
 };
 
 // has_unary_plus
-DEFINE_PREFIX_UNARY_TRAIT(has_unary_plus, +); // { dg-error "within this context" }
+DEFINE_PREFIX_UNARY_TRAIT(has_unary_plus, +);
 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, -);
 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, *);
 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, ~);
 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, !);
 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, ++);
 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, --);
 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, ++);
 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, --);
 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 188652)
+++ gcc/testsuite/g++.dg/template/sfinae6_neg.C	(working copy)
@@ -14,14 +14,14 @@ 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" "" { target c++11 } }
+  check_is_callable2(type<F>, type<T1>, type<T2>);
 
 no_type check_is_callable2(...);
 
 template<typename F, typename T1, typename T2 = T1>
 struct is_callable2
 {
-  static const bool value = // { dg-error "within this context" }
+  static const bool value =
     (sizeof(check_is_callable2(type<F>(), type<T1>(), type<T2>()))
      == sizeof(yes_type));
 };
@@ -52,7 +52,7 @@ struct F {
   void operator()(A, A);
 
 private:
-  void operator()(B, B); // { dg-error "is private" }
+  void operator()(B, B);
 };
 
-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 188652)
+++ 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, true);
 	  else
-	    perform_or_defer_access_check (basebinfo, t, t);
+	    perform_or_defer_access_check (basebinfo, t, t, true);
 
 	  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, true);
 
   if (!address_p)
     {
Index: gcc/cp/class.c
===================================================================
--- gcc/cp/class.c	(revision 188652)
+++ gcc/cp/class.c	(working copy)
@@ -1190,7 +1190,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, true);
       DECL_ACCESS (fdecl) = tree_cons (t, access, DECL_ACCESS (fdecl));
       return 1;
     }
@@ -7135,7 +7135,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, true);
     }
 
   if (TYPE_PTRFN_P (target_type) || TYPE_PTRMEMFUNC_P (target_type))
Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(revision 188652)
+++ gcc/cp/decl.c	(working copy)
@@ -3305,10 +3305,11 @@ 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 & tf_error))
+    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.  */
@@ -3377,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 & tf_error))
+	return error_mark_node;
 
       return tmpl;
     }
@@ -6646,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,
+				     true);
     }
   else
     {
@@ -10568,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;
@@ -10601,15 +10604,15 @@ 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;
     }
 
@@ -10619,8 +10622,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;
     }
 
@@ -10634,10 +10638,11 @@ 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;
     }
 
@@ -10788,7 +10793,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 188652)
+++ gcc/cp/pt.c	(working copy)
@@ -183,7 +183,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);
@@ -195,7 +195,7 @@ static tree tsubst_expr	(tree, tree, tsubst_flags_
 static tree tsubst_copy	(tree, tree, tsubst_flags_t, tree);
 static tree tsubst_pack_expansion (tree, tree, tsubst_flags_t, tree);
 static tree tsubst_decl (tree, tree, tsubst_flags_t);
-static void perform_typedefs_access_check (tree tmpl, tree targs);
+static bool perform_typedefs_access_check (tree, tree, bool);
 static void append_type_to_template_for_access_check_1 (tree, tree, tree,
 							location_t);
 static tree listify (tree);
@@ -8652,17 +8652,18 @@ apply_late_template_attributes (tree *decl_p, tree
    TMPL is the template to consider and TARGS is the list of arguments of
    that template.  */
 
-static void
-perform_typedefs_access_check (tree tmpl, tree targs)
+static bool
+perform_typedefs_access_check (tree tmpl, tree targs, bool complain)
 {
   location_t saved_location;
   int i;
   qualified_typedef_usage_t *iter;
+  bool ret = true;
 
   if (!tmpl
       || (!CLASS_TYPE_P (tmpl)
 	  && TREE_CODE (tmpl) != FUNCTION_DECL))
-    return;
+    return true;
 
   saved_location = input_location;
   FOR_EACH_VEC_ELT (qualified_typedef_usage_t,
@@ -8683,10 +8684,12 @@ apply_late_template_attributes (tree *decl_p, tree
       /* Make access check error messages point to the location
          of the use of the typedef.  */
       input_location = iter->locus;
-      perform_or_defer_access_check (TYPE_BINFO (type_scope),
-				     type_decl, type_decl);
+      ret &= perform_or_defer_access_check (TYPE_BINFO (type_scope),
+					    type_decl, type_decl, complain);
     }
-    input_location = saved_location;
+
+  input_location = saved_location;
+  return ret;
 }
 
 static tree
@@ -9190,8 +9193,8 @@ instantiate_class_template_1 (tree type)
      checked at template instantiation time, i.e now. These types were
      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_typedefs_access_check (pattern, args, true);
+  perform_deferred_access_checks (true);
   pop_nested_class ();
   maximum_field_alignment = saved_maximum_field_alignment;
   if (!fn_context)
@@ -9910,7 +9913,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;
@@ -9949,7 +9953,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();
@@ -9962,7 +9966,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);
 
@@ -9972,7 +9976,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;
@@ -9993,7 +9997,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
@@ -10335,7 +10340,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;
@@ -14639,6 +14644,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;
@@ -14726,8 +14732,11 @@ instantiate_template_1 (tree tmpl, tree orig_args,
      checked at template instantiation time, i.e now. These types were
      added to the template at parsing time. Let's get those and perfom
      the acces checks then.  */
-  perform_typedefs_access_check (DECL_TEMPLATE_RESULT (tmpl), targ_ptr);
-  perform_deferred_access_checks ();
+  if (!perform_typedefs_access_check (DECL_TEMPLATE_RESULT (tmpl), targ_ptr,
+				      complain & tf_error))
+    tmp = error_mark_node;
+  if (!perform_deferred_access_checks (complain & tf_error))
+    tmp = error_mark_node;
   pop_access_scope (fndecl);
   pop_deferring_access_checks ();
 
@@ -14742,7 +14751,7 @@ 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);
 
-  return fndecl;
+  return tmp ? error_mark_node : fndecl;
 }
 
 /* Wrapper for instantiate_template_1.  */
Index: gcc/cp/semantics.c
===================================================================
--- gcc/cp/semantics.c	(revision 188652)
+++ 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, true);
 	}
       else
 	{
@@ -254,17 +254,22 @@ 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,
+		       bool complain)
 {
   int i;
   deferred_access_check *chk;
+  bool tmp = true;
 
   if (!checks)
-    return;
+    return true;
 
   FOR_EACH_VEC_ELT (deferred_access_check, checks, i, chk)
-    enforce_access (chk->binfo, chk->decl, chk->diag_decl);
+    tmp &= enforce_access (chk->binfo, chk->decl, chk->diag_decl,
+			   complain);
+
+  return complain ? true : tmp;
 }
 
 /* Perform the deferred access checks.
@@ -283,28 +288,28 @@ 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 (bool 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,
+			       bool complain)
 {
   int i;
   deferred_access *ptr;
   deferred_access_check *chk;
   deferred_access_check *new_access;
 
-
   /* 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);
 
@@ -313,8 +318,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 ? true : tmp;
     }
 
   /* See if we are already going to perform this check.  */
@@ -324,7 +329,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.  */
@@ -334,6 +339,8 @@ pop_to_parent_deferring_access_checks (void)
   new_access->binfo = binfo;
   new_access->decl = decl;
   new_access->diag_decl = diag_decl;
+
+  return true;
 }
 
 /* Used by build_over_call in LOOKUP_SPECULATIVE mode: return whether DECL
@@ -353,7 +360,7 @@ speculative_access_check (tree binfo, tree decl, t
     {
       /* Unless we're under maybe_explain_implicit_delete.  */
       if (complain)
-	enforce_access (binfo, decl, diag_decl);
+	enforce_access (binfo, decl, diag_decl, true);
       return false;
     }
 
@@ -1601,7 +1608,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, true);
 
       /* If the data member was named `C::M', convert `*this' to `C'
 	 first.  */
@@ -1723,7 +1730,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, true);
 }
 
 /* EXPR is the result of a qualified-id.  The QUALIFYING_CLASS was the
@@ -3331,7 +3338,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, true);
 		}
 	    }
 
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 188652)
+++ gcc/cp/parser.c	(working copy)
@@ -10472,7 +10472,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 (true);
     }
 
   /* Consume the `;'.  */
@@ -12373,7 +12373,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, true);
 	}
       /* Return the stored value.  */
       return check_value->value;
@@ -15700,7 +15700,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)
@@ -20962,7 +20962,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 (true);
 
   if (!success_p)
     {
@@ -21257,7 +21257,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, true);
   --processing_template_parmlist;
 }
 
@@ -21784,7 +21784,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;
@@ -22706,7 +22707,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, true);
     }
   /* Set the scope from the stored value.  */
   parser->scope = check_value->value;
@@ -23964,7 +23965,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 (true);
 	      stop_deferring_access_checks ();
 	      meth = cp_parser_function_definition_after_declarator (parser,
 								     false);
Index: gcc/cp/call.c
===================================================================
--- gcc/cp/call.c	(revision 188652)
+++ gcc/cp/call.c	(working copy)
@@ -5503,7 +5503,7 @@ 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, true);
 
       /* Core issue 901: It's ok to new a type with deleted delete.  */
       if (DECL_DELETED_FN (fn) && alloc_fn)
@@ -5561,19 +5561,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,
+		bool 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 (complain)
+	{
+	  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;
     }
 
@@ -6255,7 +6259,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:
 
@@ -6504,8 +6508,9 @@ build_over_call (struct z_candidate *cand, int fla
 					 complain & tf_error))
 	    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 & tf_error))
+	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 188652)
+++ gcc/cp/cp-tree.h	(working copy)
@@ -4899,7 +4899,7 @@ 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, bool);
 extern void push_defarg_context			(tree);
 extern void pop_defarg_context			(void);
 extern tree convert_default_arg			(tree, tree, tree, int,
@@ -5095,7 +5095,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 *);
@@ -5362,7 +5362,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);
@@ -5493,9 +5494,9 @@ 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 perform_access_checks (VEC (deferred_access_check,gc)*, bool);
+extern bool perform_deferred_access_checks	(bool);
+extern bool perform_or_defer_access_check	(tree, tree, tree, bool);
 extern bool speculative_access_check		(tree, tree, tree, bool);
 extern int stmts_are_full_exprs_p		(void);
 extern void init_cp_semantics			(void);
Index: gcc/cp/search.c
===================================================================
--- gcc/cp/search.c	(revision 188652)
+++ gcc/cp/search.c	(working copy)
@@ -1255,8 +1255,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 & tf_error))
+	rval = error_mark_node;
     }
 
   if (errstr && protect)
Index: gcc/cp/friend.c
===================================================================
--- gcc/cp/friend.c	(revision 188652)
+++ 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, true);
 
   maybe_add_class_template_decl_list (type, decl, /*friend_p=*/1);
 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-06-15 14:57 [C++ RFC / Patch] PR 51213 ("access control under SFINAE") Paolo Carlini
@ 2012-06-28 10:38 ` Paolo Carlini
  2012-07-02 19:48 ` Jason Merrill
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 16+ messages in thread
From: Paolo Carlini @ 2012-06-28 10:38 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jason Merrill

On 06/15/2012 04:27 PM, Paolo Carlini wrote:
> Hi,
>
> as I mentioned a few days ago, I'm working on implementing this
> feature, which I personally consider rather high priority, from the
> library point of view too (eg, <type_traits>).
>
> I have been making some progress - I'm attaching below what I have so
> far in my local tree - but I also think it's time to get feedback both
> about the general approach and about more specific issues with the
> testsuite.
... any comments on this?

Thanks!
Paolo.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-06-15 14:57 [C++ RFC / Patch] PR 51213 ("access control under SFINAE") Paolo Carlini
  2012-06-28 10:38 ` Paolo Carlini
@ 2012-07-02 19:48 ` Jason Merrill
  2012-07-09 16:05   ` Jason Merrill
  2012-07-12 15:26 ` Jason Merrill
  2012-07-12 23:54 ` Jason Merrill
  3 siblings, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2012-07-02 19:48 UTC (permalink / raw)
  To: Paolo Carlini; +Cc: gcc-patches

On 06/15/2012 10:27 AM, Paolo Carlini wrote:
> The problem is that with the patch applied we get errors of the "no
> matching function" type, instead of access control errors.
>
> I suppose this is expected, right?!?

Yes, but when we're printing the candidates we need to give an access 
control error to explain why the candidate isn't viable.

> The second pair is more subtle:
>
> FAIL: g++.dg/template/friend13.C -std=c++11 (test for excess errors)
> FAIL: g++.dg/template/friend15.C -std=c++98 (test for excess errors)
> FAIL: g++.dg/template/friend15.C -std=c++11 (test for excess errors)

friend13 seems well-formed to me.

friend15 seems ill-formed since friends are no longer injected into the 
enclosing namespace, only found by argument-dependent lookup.

I don't see anything offhand that would cause these changes.  I'll look 
deeper.

>       added to the template at parsing time. Let's get those and perfom
>       the acces checks then.  */

"perform the access"

> +enforce_access (tree basetype_path, tree decl, tree diag_decl,
> +		bool complain)

Let's use tsubst_flags_t for complain parms even if all we really need 
is a boolean value, to help with readability.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-02 19:48 ` Jason Merrill
@ 2012-07-09 16:05   ` Jason Merrill
  0 siblings, 0 replies; 16+ messages in thread
From: Jason Merrill @ 2012-07-09 16:05 UTC (permalink / raw)
  To: Paolo Carlini; +Cc: gcc-patches

On 07/02/2012 09:47 PM, Jason Merrill wrote:
>> FAIL: g++.dg/template/friend13.C -std=c++11 (test for excess errors)

OK, the problem here is that we need to look up Outer::Inner in order to 
generate the declaration of foo<Outer>, and we can't check whether 
foo<Outer> is a friend of Outer until after we've completed the 
declaration.  deduction_tsubst_fntype tries to deal with this by pushing 
into the scope of the function template, but that doesn't help if only a 
specialization (or, in friend15, partial instantiation) is a friend.

I'll work on this some more.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-06-15 14:57 [C++ RFC / Patch] PR 51213 ("access control under SFINAE") Paolo Carlini
  2012-06-28 10:38 ` Paolo Carlini
  2012-07-02 19:48 ` Jason Merrill
@ 2012-07-12 15:26 ` Jason Merrill
  2012-07-12 23:54 ` Jason Merrill
  3 siblings, 0 replies; 16+ messages in thread
From: Jason Merrill @ 2012-07-12 15:26 UTC (permalink / raw)
  To: Paolo Carlini; +Cc: gcc-patches

On 06/15/2012 04:27 PM, Paolo Carlini wrote:
> FAIL: g++.dg/template/access7.C -std=c++98 (test for errors, line 8)
> FAIL: g++.dg/template/access7.C -std=c++98 (test for errors, line 12)
> FAIL: g++.dg/template/access7.C -std=c++98 (test for warnings, line 17)
> FAIL: g++.dg/template/access7.C -std=c++98 (test for excess errors)
> FAIL: g++.dg/template/access7.C -std=c++11 (test for excess errors)
> FAIL: g++.dg/template/typedef11.C -std=c++98 (test for errors, line 8)
> FAIL: g++.dg/template/typedef11.C -std=c++98 (test for errors, line 14)
> FAIL: g++.dg/template/typedef11.C -std=c++98 (test for errors, line 25)
> FAIL: g++.dg/template/typedef11.C -std=c++98 (test for excess errors)
> FAIL: g++.dg/template/typedef11.C -std=c++11 (test for errors, line 8)
> FAIL: g++.dg/template/typedef11.C -std=c++11 (test for errors, line 14)
> FAIL: g++.dg/template/typedef11.C -std=c++11 (test for errors, line 25)
> FAIL: g++.dg/template/typedef11.C -std=c++11 (test for excess errors)

I'm fixing these, too.  They turn out to be more subtle issues.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-12 23:54 ` Jason Merrill
@ 2012-07-12 23:06   ` Jason Merrill
  2012-07-19  1:36     ` Jason Merrill
  2012-07-14  9:57   ` Paolo Carlini
  1 sibling, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2012-07-12 23:06 UTC (permalink / raw)
  To: Paolo Carlini; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 262 bytes --]

And here's a fix for the access7 failure, to be applied on top of your 
patch.

I notice that your patch changes the behavior of C++98/03 mode as well, 
which seems wrong to me; I think this is a big enough change that we 
should limit it to C++11 mode.

Jason


[-- Attachment #2: access7.patch --]
[-- Type: text/x-patch, Size: 3824 bytes --]

commit f60b940ab6bcbad60632ba085fa0a5cff2e963e3
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Jul 12 17:16:35 2012 +0200

    access7

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8ce0f2a..12c688a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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_typedef_usage_t;
 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;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 6a1780b..7eca5c7 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -9875,10 +9875,13 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
 	    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
@@ -9954,6 +9957,13 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
 	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
@@ -14347,7 +14357,13 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
 	      || 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))
@@ -14398,7 +14414,17 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
   if (DECL_CHAIN (gen_tmpl) && DECL_CLONED_FUNCTION_P (DECL_CHAIN (gen_tmpl)))
     clone_function_decl (fndecl, /*update_method_vec_p=*/0);
 
-  return tmp ? error_mark_node : fndecl;
+  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;
 }
 
 /* Wrapper for instantiate_template_1.  */
diff --git a/gcc/testsuite/g++.dg/template/access7.C b/gcc/testsuite/g++.dg/template/access7.C
index bd38e4e..7d18127 100644
--- a/gcc/testsuite/g++.dg/template/access7.C
+++ b/gcc/testsuite/g++.dg/template/access7.C
@@ -14,5 +14,5 @@ typename A::T* f (A) {			// { dg-error "this context" }
 }
 
 void g () {
-  f (S<int> ());			// { dg-message "required" }
+  f (S<int> ());			// { dg-message "required|no match" }
 }

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-06-15 14:57 [C++ RFC / Patch] PR 51213 ("access control under SFINAE") Paolo Carlini
                   ` (2 preceding siblings ...)
  2012-07-12 15:26 ` Jason Merrill
@ 2012-07-12 23:54 ` Jason Merrill
  2012-07-12 23:06   ` Jason Merrill
  2012-07-14  9:57   ` Paolo Carlini
  3 siblings, 2 replies; 16+ messages in thread
From: Jason Merrill @ 2012-07-12 23:54 UTC (permalink / raw)
  To: Paolo Carlini; +Cc: gcc-patches, Dodji Seketeli

[-- Attachment #1: Type: text/plain, Size: 576 bytes --]

These patches fix the typedef11.C and friend13/15.C failures.  Each 
fixes a latent bug in the compiler.

1) We shouldn't do access control checking for typedefs used in the body 
of a function when the declaration is instantiated; we should wait until 
the body is instantiated.

2) We shouldn't try to do any access checking when deducing template 
arguments, that can wait until we call instantiate_template later in 
add_template_candidate_real, when we actually have a FUNCTION_DECL to 
compare to any friend declarations.

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

[-- Attachment #2: typedef11.patch --]
[-- Type: text/x-patch, Size: 3355 bytes --]

commit 37c9b524e42df0b5843af5ceab1f958b9e17dfe4
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Jul 12 17:17:12 2012 +0200

    	* pt.c (instantiate_decl): Check typedefs access here.
    	(instantiate_template_1): Not here.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index fec88eb..95c6464 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8334,7 +8334,7 @@ apply_late_template_attributes (tree *decl_p, tree attributes, int attr_flags,
 
 /* Perform (or defer) access check for typedefs that were referenced
    from within the template TMPL code.
-   This is a subroutine of instantiate_template and instantiate_class_template.
+   This is a subroutine of instantiate_decl and instantiate_class_template.
    TMPL is the template to consider and TARGS is the list of arguments of
    that template.  */
 
@@ -14380,12 +14380,6 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
   /* Now we know the specialization, compute access previously
      deferred.  */
   push_access_scope (fndecl);
-
-  /* Some typedefs referenced from within the template code need to be access
-     checked at template instantiation time, i.e now. These types were
-     added to the template at parsing time. Let's get those and perfom
-     the acces checks then.  */
-  perform_typedefs_access_check (DECL_TEMPLATE_RESULT (tmpl), targ_ptr);
   perform_deferred_access_checks ();
   pop_access_scope (fndecl);
   pop_deferring_access_checks ();
@@ -18395,6 +18389,13 @@ instantiate_decl (tree d, int defer_ok,
       /* Set up context.  */
       start_preparsed_function (d, NULL_TREE, SF_PRE_PARSED);
 
+      /* Some typedefs referenced from within the template code need to be
+	 access checked at template instantiation time, i.e now. These
+	 types were added to the template at parsing time. Let's get those
+	 and perform the access checks then.  */
+      perform_typedefs_access_check (DECL_TEMPLATE_RESULT (gen_tmpl),
+				     gen_args);
+
       /* Create substitution entries for the parameters.  */
       subst_decl = DECL_TEMPLATE_RESULT (template_for_substitution (d));
       tmpl_parm = DECL_ARGUMENTS (subst_decl);
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 0cacf74..a32e48a 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1625,7 +1625,7 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope)
 /* If we are currently parsing a template and we encountered a typedef
    TYPEDEF_DECL that is being accessed though CONTEXT, this function
    adds the typedef to a list tied to the current template.
-   At tempate instantiatin time, that list is walked and access check
+   At template instantiation time, that list is walked and access check
    performed for each typedef.
    LOCATION is the location of the usage point of TYPEDEF_DECL.  */
 
diff --git a/gcc/testsuite/g++.dg/template/typedef11.C b/gcc/testsuite/g++.dg/template/typedef11.C
index c7c7c98..a08d7b3 100644
--- a/gcc/testsuite/g++.dg/template/typedef11.C
+++ b/gcc/testsuite/g++.dg/template/typedef11.C
@@ -18,8 +18,8 @@ template <int>
 int
 bar ()
 {
-  Beta<0>::Y i = 0;
-  return Alpha::X ();
+  Beta<0>::Y i = 0;		// { dg-error "within this context" }
+  return Alpha::X ();		// { dg-error "within this context" }
 }
 
-int i = bar<0> (); // { dg-error "within this context" }
+int i = bar<0> ();

[-- Attachment #3: friend13.patch --]
[-- Type: text/x-patch, Size: 3979 bytes --]

commit 56a4aeb321d97093da7ac7b0cc4a4783adc83494
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Jul 12 12:20:36 2012 +0200

    	* pt.c (deduction_tsubst_fntype): Just suppress access checking.
    	(instantiate_template_1): Set DECL_TI_TEMPLATE before access checking.
    	(push_deduction_access_scope, pop_deduction_access_scope): Remove.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 72b0c4f..fec88eb 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -116,8 +116,6 @@ enum template_base_result {
 
 static void push_access_scope (tree);
 static void pop_access_scope (tree);
-static void push_deduction_access_scope (tree);
-static void pop_deduction_access_scope (tree);
 static bool resolve_overloaded_unification (tree, tree, tree, tree,
 					    unification_kind_t, int,
 					    bool);
@@ -14263,9 +14261,10 @@ deduction_tsubst_fntype (tree fn, tree targs, tsubst_flags_t complain)
 
   input_location = DECL_SOURCE_LOCATION (fn);
   ++deduction_depth;
-  push_deduction_access_scope (fn);
+  /* We will do access checks in instantiate_template.  */
+  push_deferring_access_checks (dk_deferred);
   r = tsubst (fntype, targs, complain, NULL_TREE);
-  pop_deduction_access_scope (fn);
+  pop_deferring_access_checks ();
   --deduction_depth;
 
   if (excessive_deduction_depth)
@@ -14374,6 +14373,10 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
   if (fndecl == error_mark_node)
     return error_mark_node;
 
+  /* The DECL_TI_TEMPLATE should always be the immediate parent
+     template, not the most general template.  */
+  DECL_TI_TEMPLATE (fndecl) = tmpl;
+
   /* Now we know the specialization, compute access previously
      deferred.  */
   push_access_scope (fndecl);
@@ -14387,10 +14390,6 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
   pop_access_scope (fndecl);
   pop_deferring_access_checks ();
 
-  /* The DECL_TI_TEMPLATE should always be the immediate parent
-     template, not the most general template.  */
-  DECL_TI_TEMPLATE (fndecl) = tmpl;
-
   /* If we've just instantiated the main entry point for a function,
      instantiate all the alternate entry points as well.  We do this
      by cloning the instantiation of the main entry point, not by
@@ -14413,36 +14412,6 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain)
   return ret;
 }
 
-/* We're going to do deduction substitution on the type of TMPL, a function
-   template.  In C++11 mode, push into that access scope.  In C++03 mode,
-   disable access checking.  */
-
-static void
-push_deduction_access_scope (tree tmpl)
-{
-  if (cxx_dialect >= cxx0x)
-    {
-      int ptd = processing_template_decl;
-      push_access_scope (DECL_TEMPLATE_RESULT (tmpl));
-      /* Preserve processing_template_decl across push_to_top_level.  */
-      if (ptd && !processing_template_decl)
-	++processing_template_decl;
-    }
-  else
-    push_deferring_access_checks (dk_no_check);
-}
-
-/* And pop back out.  */
-
-static void
-pop_deduction_access_scope (tree tmpl)
-{
-  if (cxx_dialect >= cxx0x)
-    pop_access_scope (DECL_TEMPLATE_RESULT (tmpl));
-  else
-    pop_deferring_access_checks ();
-}
-
 /* PARM is a template parameter pack for FN.  Returns true iff
    PARM is used in a deducible way in the argument list of FN.  */
 
diff --git a/gcc/testsuite/g++.dg/template/sfinae6_neg.C b/gcc/testsuite/g++.dg/template/sfinae6_neg.C
index 9b7bdfd1..c238222 100644
--- a/gcc/testsuite/g++.dg/template/sfinae6_neg.C
+++ b/gcc/testsuite/g++.dg/template/sfinae6_neg.C
@@ -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" "" { target c++11 } }
+  check_is_callable2(type<F>, type<T1>, type<T2>);
 
 no_type check_is_callable2(...);
 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-12 23:54 ` Jason Merrill
  2012-07-12 23:06   ` Jason Merrill
@ 2012-07-14  9:57   ` Paolo Carlini
  2012-07-16 18:53     ` Jason Merrill
  1 sibling, 1 reply; 16+ messages in thread
From: Paolo Carlini @ 2012-07-14  9:57 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Dodji Seketeli

Hi,

and, first, thanks a lot for all the nice work and help on these issues!

On 07/13/2012 12:55 AM, Jason Merrill wrote:
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index fec88eb..95c6464 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -8334,7 +8334,7 @@ apply_late_template_attributes (tree *decl_p, tree attributes, int attr_flags,
>   
>   /* Perform (or defer) access check for typedefs that were referenced
>      from within the template TMPL code.
> -   This is a subroutine of instantiate_template and instantiate_class_template.
> +   This is a subroutine of instantiate_decl and instantiate_class_template.
>      TMPL is the template to consider and TARGS is the list of arguments of
>      that template.  */
>   
> @@ -14380,12 +14380,6 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
>     /* Now we know the specialization, compute access previously
>        deferred.  */
>     push_access_scope (fndecl);
> -
> -  /* Some typedefs referenced from within the template code need to be access
> -     checked at template instantiation time, i.e now. These types were
> -     added to the template at parsing time. Let's get those and perfom
> -     the acces checks then.  */
> -  perform_typedefs_access_check (DECL_TEMPLATE_RESULT (tmpl), targ_ptr);
>     perform_deferred_access_checks ();
>     pop_access_scope (fndecl);
>     pop_deferring_access_checks ();
> @@ -18395,6 +18389,13 @@ instantiate_decl (tree d, int defer_ok,
>         /* Set up context.  */
>         start_preparsed_function (d, NULL_TREE, SF_PRE_PARSED);
>   
> +      /* Some typedefs referenced from within the template code need to be
> +	 access checked at template instantiation time, i.e now. These
> +	 types were added to the template at parsing time. Let's get those
> +	 and perform the access checks then.  */
> +      perform_typedefs_access_check (DECL_TEMPLATE_RESULT (gen_tmpl),
> +				     gen_args);
> +
I'm working on integrating again my work.

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?

Thanks!
Paolo.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-14  9:57   ` Paolo Carlini
@ 2012-07-16 18:53     ` Jason Merrill
  2012-07-17 12:46       ` Paolo Carlini
  0 siblings, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2012-07-16 18:53 UTC (permalink / raw)
  To: Paolo Carlini; +Cc: gcc-patches, Dodji Seketeli

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.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-16 18:53     ` Jason Merrill
@ 2012-07-17 12:46       ` Paolo Carlini
  2012-07-17 14:11         ` Jason Merrill
  0 siblings, 1 reply; 16+ messages in thread
From: Paolo Carlini @ 2012-07-17 12:46 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Dodji Seketeli

[-- Attachment #1: Type: text/plain, Size: 870 bytes --]

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.

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

[-- Attachment #2: patch_51213_6 --]
[-- Type: text/plain, Size: 36649 bytes --]

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);
 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-17 12:46       ` Paolo Carlini
@ 2012-07-17 14:11         ` Jason Merrill
  2012-07-17 16:14           ` Paolo Carlini
  0 siblings, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2012-07-17 14:11 UTC (permalink / raw)
  To: Paolo Carlini; +Cc: gcc-patches, Dodji Seketeli

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

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-17 14:11         ` Jason Merrill
@ 2012-07-17 16:14           ` Paolo Carlini
  2012-07-18  1:20             ` Paolo Carlini
  0 siblings, 1 reply; 16+ messages in thread
From: Paolo Carlini @ 2012-07-17 16:14 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Dodji Seketeli

[-- Attachment #1: Type: text/plain, Size: 2321 bytes --]

Hi,

On 07/17/2012 04:10 PM, Jason Merrill wrote:
> 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?
Eh, thanks for noticing. Honestly, I have no idea where that cruft was 
coming from, I removed it and everything seems fine.
>
>> +    tmp = error_mark_node;
>
> Let's use a more informative name than "tmp" for these flags.
Ok.
>
>> -void
>> -perform_deferred_access_checks (void)
>> +bool
>> +perform_deferred_access_checks (tsubst_flags_t complain)
>
> Need to document what the return value means.
Indeed.
>
>
>> -  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).
Ok, thanks. In fact today I added the enforce_access change very late, 
without really checking that it was consistent with the callers etc. The 
below variant makes more sense to me, afaics reduces correctly in the 
various C++ modes either to what we used to have before this work or to 
what I had in my first draft.
> 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.
I see. Could you take care of this improvement? Otherwise the below 
passes testing, tries to implement all the changes you indicated.

Thanks!
Paolo.

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

[-- Attachment #2: patch_51213_8 --]
[-- Type: text/plain, Size: 31112 bytes --]

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,11 @@ 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 ((cxx_dialect >= cxx0x || complain & tf_error)
+      && !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 +3379,10 @@ 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 ((cxx_dialect >= cxx0x || complain & tf_error)
+	  && !perform_or_defer_access_check (TYPE_BINFO (context), tmpl, tmpl,
+					     complain))
+	return error_mark_node;
 
       return tmpl;
     }
@@ -6647,7 +6650,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
     {
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 189561)
+++ gcc/cp/pt.c	(working copy)
@@ -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)
@@ -9870,10 +9870,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 +9952,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
@@ -14294,6 +14304,7 @@ instantiate_template_1 (tree tmpl, tree orig_args,
   tree fndecl;
   tree gen_tmpl;
   tree spec;
+  bool access_ok = true;
 
   if (tmpl == error_mark_node)
     return error_mark_node;
@@ -14341,7 +14352,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 +14397,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))
+    access_ok = false;
   pop_access_scope (fndecl);
   pop_deferring_access_checks ();
 
@@ -14391,6 +14409,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 (!access_ok)
+    {
+      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
 	{
@@ -252,25 +252,30 @@ pop_to_parent_deferring_access_checks (void)
 
 /* Perform the access checks in CHECKS.  The TREE_PURPOSE of each node
    is the BINFO indicating the qualifying scope used to access the
-   DECL node stored in the TREE_VALUE of the node.  */
+   DECL node stored in the TREE_VALUE of the node.  If CHECKS is empty
+   or we aren't in SFINAE context or all the checks succeed return TRUE,
+   otherwise FALSE.  */
 
-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 ok = 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);
+      ok &= enforce_access (chk->binfo, chk->decl, chk->diag_decl, complain);
     }
 
   input_location = loc;
+  return (complain & tf_error) ? true : ok;
 }
 
 /* Perform the deferred access checks.
@@ -287,19 +292,21 @@ pop_to_parent_deferring_access_checks (void)
      A::X A::a, x;	// No error for `A::a', error for `x'
 
    We have to perform deferred access of `A::X', first with `A::a',
-   next with `x'.  */
+   next with `x'.  Return value like perform_access_checks above.  */
 
-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.  */
+   BINFO. DIAG_DECL is the declaration to use to print diagnostics.
+   Return value like perform_access_checks above.  */
 
-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 +317,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 +326,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 ok = enforce_access (binfo, decl, diag_decl, complain);
+      return (complain & tf_error) ? true : ok;
     }
 
   /* See if we are already going to perform this check.  */
@@ -330,7 +337,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 +348,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 +358,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 +368,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 +1620,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 +1742,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 +3345,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;
 }
 
@@ -22760,7 +22760,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 +24018,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;
     }
 
@@ -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,
@@ -5497,10 +5507,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);
 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-17 16:14           ` Paolo Carlini
@ 2012-07-18  1:20             ` Paolo Carlini
  2012-07-18  2:47               ` Jason Merrill
  0 siblings, 1 reply; 16+ messages in thread
From: Paolo Carlini @ 2012-07-18  1:20 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches, Dodji Seketeli

[-- Attachment #1: Type: text/plain, Size: 655 bytes --]

Hi again,
>> 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.
> I see. Could you take care of this improvement? Otherwise the below 
> passes testing, tries to implement all the changes you indicated.
The below variant adds a fndecl_recheck_access_p and uses it in two 
places, in tsubst_decl and instantiate_template_1. Did I understand 
correctly?

Tested x86_64-linux.

Thanks,
Paolo.

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

[-- Attachment #2: patch_51213_9 --]
[-- Type: text/plain, Size: 31519 bytes --]

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,11 @@ 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 ((cxx_dialect >= cxx0x || complain & tf_error)
+      && !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 +3379,10 @@ 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 ((cxx_dialect >= cxx0x || complain & tf_error)
+	  && !perform_or_defer_access_check (TYPE_BINFO (context), tmpl, tmpl,
+					     complain))
+	return error_mark_node;
 
       return tmpl;
     }
@@ -6647,7 +6650,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
     {
Index: gcc/cp/pt.c
===================================================================
--- gcc/cp/pt.c	(revision 189561)
+++ gcc/cp/pt.c	(working copy)
@@ -7853,6 +7853,21 @@ outermost_tinst_level (void)
   return level;
 }
 
+/* Return TRUE if either SPEC is NULL_TREE or FNDECL_RECHECK_ACCESS_P
+   is TRUE and we are out of SFINAE context, FALSE otherwise.  */
+
+static bool
+fndecl_recheck_access_p (tree spec, tsubst_flags_t complain)
+{
+  if (!spec)
+    return true;
+
+  if (FNDECL_RECHECK_ACCESS_P (spec) && (complain & tf_error))
+    return true;
+
+  return false;
+}
+
 /* DECL is a friend FUNCTION_DECL or TEMPLATE_DECL.  ARGS is the
    vector of template arguments, as for tsubst.
 
@@ -8370,7 +8385,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 +8892,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)
@@ -9870,11 +9885,9 @@ 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);
 
-	    if (spec)
-	      {
-		r = spec;
-		break;
-	      }
+	    r = spec;
+	    if (!fndecl_recheck_access_p (spec, complain))
+	      break;
 
 	    /* We can see more levels of arguments than parameters if
 	       there was a specialization of a member template, like
@@ -9949,6 +9962,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
@@ -14294,6 +14314,7 @@ instantiate_template_1 (tree tmpl, tree orig_args,
   tree fndecl;
   tree gen_tmpl;
   tree spec;
+  bool access_ok = true;
 
   if (tmpl == error_mark_node)
     return error_mark_node;
@@ -14340,7 +14361,7 @@ instantiate_template_1 (tree tmpl, tree orig_args,
 		  == spec)
 	      || fndecl == NULL_TREE);
 
-  if (spec != NULL_TREE)
+  if (!fndecl_recheck_access_p (spec, complain))
     return spec;
 
   if (check_instantiated_args (gen_tmpl, INNERMOST_TEMPLATE_ARGS (targ_ptr),
@@ -14380,7 +14401,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))
+    access_ok = false;
   pop_access_scope (fndecl);
   pop_deferring_access_checks ();
 
@@ -14391,6 +14413,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 (!access_ok)
+    {
+      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
 	{
@@ -252,25 +252,30 @@ pop_to_parent_deferring_access_checks (void)
 
 /* Perform the access checks in CHECKS.  The TREE_PURPOSE of each node
    is the BINFO indicating the qualifying scope used to access the
-   DECL node stored in the TREE_VALUE of the node.  */
+   DECL node stored in the TREE_VALUE of the node.  If CHECKS is empty
+   or we aren't in SFINAE context or all the checks succeed return TRUE,
+   otherwise FALSE.  */
 
-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 ok = 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);
+      ok &= enforce_access (chk->binfo, chk->decl, chk->diag_decl, complain);
     }
 
   input_location = loc;
+  return (complain & tf_error) ? true : ok;
 }
 
 /* Perform the deferred access checks.
@@ -287,19 +292,21 @@ pop_to_parent_deferring_access_checks (void)
      A::X A::a, x;	// No error for `A::a', error for `x'
 
    We have to perform deferred access of `A::X', first with `A::a',
-   next with `x'.  */
+   next with `x'.  Return value like perform_access_checks above.  */
 
-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.  */
+   BINFO. DIAG_DECL is the declaration to use to print diagnostics.
+   Return value like perform_access_checks above.  */
 
-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 +317,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 +326,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 ok = enforce_access (binfo, decl, diag_decl, complain);
+      return (complain & tf_error) ? true : ok;
     }
 
   /* See if we are already going to perform this check.  */
@@ -330,7 +337,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 +348,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 +358,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 +368,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 +1620,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 +1742,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 +3345,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;
 }
 
@@ -22760,7 +22760,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 +24018,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;
     }
 
@@ -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,
@@ -5497,10 +5507,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);
 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-18  1:20             ` Paolo Carlini
@ 2012-07-18  2:47               ` Jason Merrill
  0 siblings, 0 replies; 16+ messages in thread
From: Jason Merrill @ 2012-07-18  2:47 UTC (permalink / raw)
  To: Paolo Carlini; +Cc: gcc-patches, Dodji Seketeli

On 07/17/2012 09:20 PM, Paolo Carlini wrote:
> The below variant adds a fndecl_recheck_access_p and uses it in two
> places, in tsubst_decl and instantiate_template_1. Did I understand
> correctly?

I had something else in mind, but I'll take care of it.

Jason

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-12 23:06   ` Jason Merrill
@ 2012-07-19  1:36     ` Jason Merrill
  2012-07-19  9:45       ` Paolo Carlini
  0 siblings, 1 reply; 16+ messages in thread
From: Jason Merrill @ 2012-07-19  1:36 UTC (permalink / raw)
  To: Paolo Carlini; +Cc: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1126 bytes --]

On 07/12/2012 07:06 PM, Jason Merrill wrote:
> I notice that your patch changes the behavior of C++98/03 mode as well,
> which seems wrong to me; I think this is a big enough change that we
> should limit it to C++11 mode.

...except that I can't figure out what the semantics before this DR were 
really supposed to be; G++, EDG and Clang all handle this stuff 
differently.  After poking at it for a while I think the only sensible 
thing is to have C++03 work the same as C++11.  I've done that, and I'm 
going to check in the full patch.

The first attachment is the full patch; the second is the changes 
relative to your earlier patch.  I changed tf_error to 
tf_warning_or_error, went back to treating C++03 and C++11 the same, 
made the FNDECL_RECHECK_ACCESS_P change I mentioned, and streamlined 
LOOKUP_SPECULATIVE handling.

The third attachment is to fix another problem with my earlier DR 1402 
patch.  I'm applying this one to 4.7 as well.

Finally, the fourth attachment is to remove the substitution checking 
from instantiate_decl, as it seems to be redundant.

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

[-- Attachment #2: dr1170.patch --]
[-- Type: text/x-patch, Size: 32759 bytes --]

commit 32570a0651e12c51bdc6a976b2f981792ea8e4c8
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jul 17 14:10:31 2012 -0400

    	DR 1170
    	PR c++/51213
    	* semantics.c (perform_access_checks): Add complain parm, return bool.
    	(perform_deferred_access_checks): Likewise.
    	(perform_or_defer_access_check): Likewise.
    	(speculative_access_check): Remove.
    	* call.c (enforce_access): Add complain parm, return bool.
    	* decl.c, friend.c, class.c, init.c, parser.c: Adjust callers.
    	* search.c: Adjust callers.
    	* cp-tree.h (TINFO_RECHECK_ACCESS_P): New macro.
    	(FNDECL_RECHECK_ACCESS_P): New macro.
    	* method.c (synthesized_method_walk): Stop deferring access checks.
    	* pt.c (recheck_decl_substitution): New.
    	(instantiate_template_1): Set and check FNDECL_RECHECK_ACCESS_P.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 72394f4..5b3245f 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5515,7 +5515,8 @@ build_op_delete_call (enum tree_code code, tree addr, tree size,
       /* 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 addr, tree size,
    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 (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;
     }
 
@@ -6510,14 +6515,9 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	access_fn = DECL_TI_TEMPLATE (fn);
       else
 	access_fn = fn;
-      if (flags & LOOKUP_SPECULATIVE)
-	{
-	  if (!speculative_access_check (cand->access_path, access_fn, fn,
-					 complain & tf_error))
-	    return error_mark_node;
-	}
-      else
-	perform_or_defer_access_check (cand->access_path, access_fn, fn);
+      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
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 82c28fa..96a7420 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -1189,7 +1189,8 @@ 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_warning_or_error);
       DECL_ACCESS (fdecl) = tree_cons (t, access, DECL_ACCESS (fdecl));
       return 1;
     }
@@ -7147,7 +7148,8 @@ resolve_address_of_overloaded_function (tree target_type,
       && 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_warning_or_error);
     }
 
   if (TYPE_PTRFN_P (target_type) || TYPE_PTRMEMFUNC_P (target_type))
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 59104e7..f1a4b32 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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_typedef_usage_t;
 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;
@@ -4424,9 +4433,7 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
    Used by sythesized_method_walk to determine which functions will
    be called to initialize subobjects, in order to determine exception
    specification and possible implicit delete.
-   This is kind of a hack, but since access control doesn't respect SFINAE
-   we can't just use tf_none to avoid access control errors, we need
-   another mechanism.  Exiting early also avoids problems with trying
+   This is kind of a hack, but exiting early avoids problems with trying
    to perform argument conversions when the class isn't complete yet.  */
 #define LOOKUP_SPECULATIVE (LOOKUP_LIST_ONLY << 1)
 /* Used by calls from defaulted functions to limit the overload set to avoid
@@ -4901,7 +4908,8 @@ extern bool can_convert_arg			(tree, tree, tree, int,
 						 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,
@@ -5497,10 +5505,11 @@ 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 int stmts_are_full_exprs_p		(void);
 extern void init_cp_semantics			(void);
 extern tree do_poplevel				(tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 8a3163a..605058d 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -3306,9 +3306,9 @@ make_typename_type (tree context, tree name, enum tag_types tag_type,
 	       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
@@ -3378,8 +3378,9 @@ make_unbound_class_template (tree context, tree name, tree parm_list,
 	  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_warning_or_error);
     }
   else
     {
diff --git a/gcc/cp/friend.c b/gcc/cp/friend.c
index e68af3d..d0cbaed 100644
--- a/gcc/cp/friend.c
+++ b/gcc/cp/friend.c
@@ -166,7 +166,8 @@ 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_warning_or_error);
 
   maybe_add_class_template_decl_list (type, decl, /*friend_p=*/1);
 
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 5a81643..a725a0c 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -1876,9 +1876,11 @@ build_offset_ref (tree type, tree member, bool address_p)
 	       (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_warning_or_error);
 	  else
-	    perform_or_defer_access_check (basebinfo, t, t);
+	    perform_or_defer_access_check (basebinfo, t, t,
+					   tf_warning_or_error);
 
 	  if (DECL_STATIC_FUNCTION_P (t))
 	    return t;
@@ -1891,7 +1893,8 @@ build_offset_ref (tree type, tree member, bool address_p)
     /* 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_warning_or_error);
 
   if (!address_p)
     {
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 20a6c87..c21ae15 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1225,6 +1225,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
 
   ++cp_unevaluated_operand;
   ++c_inhibit_evaluation_warnings;
+  push_deferring_access_checks (dk_no_deferred);
 
   scope = push_scope (ctype);
 
@@ -1342,6 +1343,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
 
   pop_scope (scope);
 
+  pop_deferring_access_checks ();
   --cp_unevaluated_operand;
   --c_inhibit_evaluation_warnings;
 }
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index df23299..70d6dac 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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_warning_or_error);
     }
 
   /* Consume the `;'.  */
@@ -12416,7 +12416,8 @@ 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_warning_or_error);
 	}
       /* Return the stored value.  */
       return check_value->value;
@@ -15751,7 +15752,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 (tf_warning_or_error);
 
       /* Restore the saved value.  */
       if (TREE_CODE (decl) == FUNCTION_DECL)
@@ -21009,7 +21010,7 @@ cp_parser_function_definition_from_specifiers_and_declarator
      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_warning_or_error);
 
   if (!success_p)
     {
@@ -21303,7 +21304,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_warning_or_error);
   --processing_template_parmlist;
 }
 
@@ -22752,7 +22753,7 @@ cp_parser_pre_parsed_nested_name_specifier (cp_parser *parser)
       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_warning_or_error);
     }
   /* Set the scope from the stored value.  */
   parser->scope = check_value->value;
@@ -24010,7 +24011,7 @@ cp_parser_objc_method_definition_list (cp_parser* 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_warning_or_error);
 	      stop_deferring_access_checks ();
 	      meth = cp_parser_function_definition_after_declarator (parser,
 								     false);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 542f57a..65eb2cc 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8370,7 +8370,8 @@ perform_typedefs_access_check (tree tmpl, tree targs)
          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_warning_or_error);
     }
     input_location = saved_location;
 }
@@ -8877,7 +8878,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_warning_or_error);
   pop_nested_class ();
   maximum_field_alignment = saved_maximum_field_alignment;
   if (!fn_context)
@@ -14288,6 +14289,23 @@ deduction_tsubst_fntype (tree fn, tree targs, tsubst_flags_t complain)
   return r;
 }
 
+/* We're out of SFINAE context now, so generate diagnostics for the access
+   errors we saw earlier when instantiating D from TMPL and ARGS.  */
+
+static void
+recheck_decl_substitution (tree d, tree tmpl, tree args)
+{
+  tree pattern = DECL_TEMPLATE_RESULT (tmpl);
+  tree type = TREE_TYPE (pattern);
+  location_t loc = input_location;
+
+  push_access_scope (d);
+  input_location = DECL_SOURCE_LOCATION (pattern);
+  tsubst (type, args, tf_warning_or_error, d);
+  input_location = loc;
+  pop_access_scope (d);
+}
+
 /* Instantiate the indicated variable or function template TMPL with
    the template arguments in TARG_PTR.  */
 
@@ -14298,6 +14316,7 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
   tree fndecl;
   tree gen_tmpl;
   tree spec;
+  bool access_ok = true;
 
   if (tmpl == error_mark_node)
     return error_mark_node;
@@ -14345,7 +14364,11 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
 	      || fndecl == NULL_TREE);
 
   if (spec != NULL_TREE)
-    return spec;
+    {
+      if (FNDECL_RECHECK_ACCESS_P (spec) && (complain & tf_error))
+	recheck_decl_substitution (spec, gen_tmpl, targ_ptr);
+      return spec;
+    }
 
   if (check_instantiated_args (gen_tmpl, INNERMOST_TEMPLATE_ARGS (targ_ptr),
 			       complain))
@@ -14375,7 +14398,10 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
   pop_from_top_level ();
 
   if (fndecl == error_mark_node)
-    return error_mark_node;
+    {
+      pop_deferring_access_checks ();
+      return error_mark_node;
+    }
 
   /* The DECL_TI_TEMPLATE should always be the immediate parent
      template, not the most general template.  */
@@ -14384,7 +14410,8 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
   /* Now we know the specialization, compute access previously
      deferred.  */
   push_access_scope (fndecl);
-  perform_deferred_access_checks ();
+  if (!perform_deferred_access_checks (complain))
+    access_ok = false;
   pop_access_scope (fndecl);
   pop_deferring_access_checks ();
 
@@ -14395,6 +14422,16 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
   if (DECL_CHAIN (gen_tmpl) && DECL_CLONED_FUNCTION_P (DECL_CHAIN (gen_tmpl)))
     clone_function_decl (fndecl, /*update_method_vec_p=*/0);
 
+  if (!access_ok)
+    {
+      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;
 }
 
diff --git a/gcc/cp/search.c b/gcc/cp/search.c
index d112c05..048fdf3 100644
--- a/gcc/cp/search.c
+++ b/gcc/cp/search.c
@@ -1254,8 +1254,10 @@ lookup_member (tree xbasetype, tree name, int protect, bool want_type,
       && !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)
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 8110295..6381949 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -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_warning_or_error);
 	}
       else
 	{
@@ -252,25 +252,30 @@ pop_to_parent_deferring_access_checks (void)
 
 /* Perform the access checks in CHECKS.  The TREE_PURPOSE of each node
    is the BINFO indicating the qualifying scope used to access the
-   DECL node stored in the TREE_VALUE of the node.  */
+   DECL node stored in the TREE_VALUE of the node.  If CHECKS is empty
+   or we aren't in SFINAE context or all the checks succeed return TRUE,
+   otherwise FALSE.  */
 
-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 ok = 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);
+      ok &= enforce_access (chk->binfo, chk->decl, chk->diag_decl, complain);
     }
 
   input_location = loc;
+  return (complain & tf_error) ? true : ok;
 }
 
 /* Perform the deferred access checks.
@@ -287,19 +292,21 @@ perform_access_checks (VEC (deferred_access_check,gc)* checks)
      A::X A::a, x;	// No error for `A::a', error for `x'
 
    We have to perform deferred access of `A::X', first with `A::a',
-   next with `x'.  */
+   next with `x'.  Return value like perform_access_checks above.  */
 
-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.  */
+   BINFO. DIAG_DECL is the declaration to use to print diagnostics.
+   Return value like perform_access_checks above.  */
 
-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 +317,7 @@ perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl)
   /* 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 +326,8 @@ perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl)
   /* 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 ok = enforce_access (binfo, decl, diag_decl, complain);
+      return (complain & tf_error) ? true : ok;
     }
 
   /* See if we are already going to perform this check.  */
@@ -330,7 +337,7 @@ perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl)
       if (chk->decl == decl && chk->binfo == binfo &&
 	  chk->diag_decl == diag_decl)
 	{
-	  return;
+	  return true;
 	}
     }
   /* If not, record the check.  */
@@ -341,28 +348,6 @@ perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl)
   new_access->decl = decl;
   new_access->diag_decl = diag_decl;
   new_access->loc = input_location;
-}
-
-/* Used by build_over_call in LOOKUP_SPECULATIVE mode: return whether DECL
-   is accessible in BINFO, and possibly complain if not.  If we're not
-   checking access, everything is accessible.  */
-
-bool
-speculative_access_check (tree binfo, tree decl, tree diag_decl,
-			  bool complain)
-{
-  if (deferred_access_no_check)
-    return true;
-
-  /* If we're checking for implicit delete, we don't want access
-     control errors.  */
-  if (!accessible_p (binfo, decl, true))
-    {
-      /* Unless we're under maybe_explain_implicit_delete.  */
-      if (complain)
-	enforce_access (binfo, decl, diag_decl);
-      return false;
-    }
 
   return true;
 }
@@ -1611,7 +1596,7 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope)
       tree access_type = TREE_TYPE (object);
 
       perform_or_defer_access_check (TYPE_BINFO (access_type), decl,
-				     decl);
+				     decl, tf_warning_or_error);
 
       /* If the data member was named `C::M', convert `*this' to `C'
 	 first.  */
@@ -1733,7 +1718,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_warning_or_error);
 }
 
 /* EXPR is the result of a qualified-id.  The QUALIFYING_CLASS was the
@@ -3336,7 +3321,8 @@ 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_warning_or_error);
 		}
 	    }
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/sfinae37.C b/gcc/testsuite/g++.dg/cpp0x/sfinae37.C
new file mode 100644
index 0000000..071923e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/sfinae37.C
@@ -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");
diff --git a/gcc/testsuite/g++.dg/template/access23.C b/gcc/testsuite/g++.dg/template/access23.C
new file mode 100644
index 0000000..054cf92
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access23.C
@@ -0,0 +1,16 @@
+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);
+}
diff --git a/gcc/testsuite/g++.dg/template/access7.C b/gcc/testsuite/g++.dg/template/access7.C
index bd38e4e..7d18127 100644
--- a/gcc/testsuite/g++.dg/template/access7.C
+++ b/gcc/testsuite/g++.dg/template/access7.C
@@ -14,5 +14,5 @@ typename A::T* f (A) {			// { dg-error "this context" }
 }
 
 void g () {
-  f (S<int> ());			// { dg-message "required" }
+  f (S<int> ());			// { dg-message "required|no match" }
 }
diff --git a/gcc/testsuite/g++.dg/template/sfinae10.C b/gcc/testsuite/g++.dg/template/sfinae10.C
index 3b1d26b..574fedd 100644
--- a/gcc/testsuite/g++.dg/template/sfinae10.C
+++ b/gcc/testsuite/g++.dg/template/sfinae10.C
@@ -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+();
+  Z operator-();
+  int operator*();
+  Z operator~();
+  bool operator!();
+  Z& operator++();
+  Z& operator--();
+  Z& operator++(int);
+  Z& operator--(int);
 };
 
 // has_unary_plus
-DEFINE_PREFIX_UNARY_TRAIT(has_unary_plus, +); // { dg-error "within this context" }
+DEFINE_PREFIX_UNARY_TRAIT(has_unary_plus, +);
 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, -);
 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, *);
 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, ~);
 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, !);
 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, ++);
 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, --);
 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, ++);
 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, --);
 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));
diff --git a/gcc/testsuite/g++.dg/template/sfinae6_neg.C b/gcc/testsuite/g++.dg/template/sfinae6_neg.C
index 28adf73..6ed3d22 100644
--- a/gcc/testsuite/g++.dg/template/sfinae6_neg.C
+++ b/gcc/testsuite/g++.dg/template/sfinae6_neg.C
@@ -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>);
 
 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);
 };
 
-STATIC_ASSERT((is_callable2<F, B, B>::value));
+STATIC_ASSERT((!is_callable2<F, B, B>::value));
diff --git a/libstdc++-v3/testsuite/20_util/pair/noncopyable.cc b/libstdc++-v3/testsuite/20_util/pair/noncopyable.cc
new file mode 100644
index 0000000..731e7ee
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/noncopyable.cc
@@ -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;
+}

[-- Attachment #3: sfinae-jason.patch --]
[-- Type: text/x-patch, Size: 16295 bytes --]

commit 1e81dada2d956ef98865385dcc75186baf6accdd
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Jul 17 14:32:35 2012 -0400

    	* semantics.c (perform_access_checks): Add complain parm, return bool.
    	(perform_deferred_access_checks): Likewise.
    	(perform_or_defer_access_check): Likewise.
    	(speculative_access_check): Remove.
    	* call.c (enforce_access): Add complain parm, return bool.
    	* decl.c, friend.c, class.c, init.c, parser.c: Adjust callers.
    	* search.c: Adjust callers.
    	* cp-tree.h (TINFO_RECHECK_ACCESS_P): New macro.
    	(FNDECL_RECHECK_ACCESS_P): New macro.
    	* method.c (synthesized_method_walk): Stop deferring access checks.
    	* pt.c (recheck_decl_substitution): New.
    	(instantiate_template_1): Set and check FNDECL_RECHECK_ACCESS_P.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 4be5424..5b3245f 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5581,7 +5581,7 @@ enforce_access (tree basetype_path, tree decl, tree diag_decl,
 
   if (!accessible_p (basetype_path, decl, true))
     {
-      if (cxx_dialect < cxx0x || complain & tf_error)
+      if (complain & tf_error)
 	{
 	  if (TREE_PRIVATE (decl))
 	    error ("%q+#D is private", diag_decl);
@@ -6515,14 +6515,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	access_fn = DECL_TI_TEMPLATE (fn);
       else
 	access_fn = fn;
-      if (flags & LOOKUP_SPECULATIVE)
-	{
-	  if (!speculative_access_check (cand->access_path, access_fn, fn,
-					 complain))
-	    return error_mark_node;
-	}
-      else if (!perform_or_defer_access_check (cand->access_path, access_fn,
-					       fn, complain))
+      if (!perform_or_defer_access_check (cand->access_path, access_fn,
+					  fn, complain))
 	return error_mark_node;
     }
 
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 16a5b4b..96a7420 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -1189,7 +1189,8 @@ alter_access (tree t, tree fdecl, tree access)
     }
   else
     {
-      perform_or_defer_access_check (TYPE_BINFO (t), fdecl, fdecl, tf_error);
+      perform_or_defer_access_check (TYPE_BINFO (t), fdecl, fdecl,
+				     tf_warning_or_error);
       DECL_ACCESS (fdecl) = tree_cons (t, access, DECL_ACCESS (fdecl));
       return 1;
     }
@@ -7147,7 +7148,8 @@ resolve_address_of_overloaded_function (tree target_type,
       && DECL_FUNCTION_MEMBER_P (fn))
     {
       gcc_assert (access_path);
-      perform_or_defer_access_check (access_path, fn, fn, tf_error);
+      perform_or_defer_access_check (access_path, fn, fn,
+				     tf_warning_or_error);
     }
 
   if (TYPE_PTRFN_P (target_type) || TYPE_PTRMEMFUNC_P (target_type))
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5a5bec7..f1a4b32 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4433,9 +4433,7 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
    Used by sythesized_method_walk to determine which functions will
    be called to initialize subobjects, in order to determine exception
    specification and possible implicit delete.
-   This is kind of a hack, but since access control doesn't respect SFINAE
-   we can't just use tf_none to avoid access control errors, we need
-   another mechanism.  Exiting early also avoids problems with trying
+   This is kind of a hack, but exiting early avoids problems with trying
    to perform argument conversions when the class isn't complete yet.  */
 #define LOOKUP_SPECULATIVE (LOOKUP_LIST_ONLY << 1)
 /* Used by calls from defaulted functions to limit the overload set to avoid
@@ -5512,8 +5510,6 @@ extern bool perform_access_checks (VEC (deferred_access_check,gc)*,
 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);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 32f64ab..605058d 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -3307,8 +3307,7 @@ make_typename_type (tree context, tree name, enum tag_types tag_type,
       return error_mark_node;
     }
 
-  if ((cxx_dialect >= cxx0x || complain & tf_error)
-      && !perform_or_defer_access_check (TYPE_BINFO (context), t, t, complain))
+  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
@@ -3379,9 +3378,8 @@ make_unbound_class_template (tree context, tree name, tree parm_list,
 	  return error_mark_node;
 	}
 
-      if ((cxx_dialect >= cxx0x || complain & tf_error)
-	  && !perform_or_defer_access_check (TYPE_BINFO (context), tmpl, tmpl,
-					     complain))
+      if (!perform_or_defer_access_check (TYPE_BINFO (context), tmpl, tmpl,
+					  complain))
 	return error_mark_node;
 
       return tmpl;
@@ -6651,7 +6649,7 @@ register_dtor_fn (tree decl)
       cleanup = VEC_index (tree, CLASSTYPE_METHOD_VEC (type), idx);
       /* Make sure it is accessible.  */
       perform_or_defer_access_check (TYPE_BINFO (type), cleanup, cleanup,
-				     tf_error);
+				     tf_warning_or_error);
     }
   else
     {
diff --git a/gcc/cp/friend.c b/gcc/cp/friend.c
index 9f20e6e..d0cbaed 100644
--- a/gcc/cp/friend.c
+++ b/gcc/cp/friend.c
@@ -166,7 +166,8 @@ 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, tf_error);
+    perform_or_defer_access_check (TYPE_BINFO (ctx), decl, decl,
+				   tf_warning_or_error);
 
   maybe_add_class_template_decl_list (type, decl, /*friend_p=*/1);
 
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index aadbea8..a725a0c 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -1876,9 +1876,11 @@ build_offset_ref (tree type, tree member, bool address_p)
 	       (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, tf_error);
+	    perform_or_defer_access_check (TYPE_BINFO (type), t, t,
+					   tf_warning_or_error);
 	  else
-	    perform_or_defer_access_check (basebinfo, t, t, tf_error);
+	    perform_or_defer_access_check (basebinfo, t, t,
+					   tf_warning_or_error);
 
 	  if (DECL_STATIC_FUNCTION_P (t))
 	    return t;
@@ -1891,7 +1893,8 @@ build_offset_ref (tree type, tree member, bool address_p)
     /* 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, tf_error);
+    perform_or_defer_access_check (TYPE_BINFO (type), member, member,
+				   tf_warning_or_error);
 
   if (!address_p)
     {
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 20a6c87..c21ae15 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -1225,6 +1225,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
 
   ++cp_unevaluated_operand;
   ++c_inhibit_evaluation_warnings;
+  push_deferring_access_checks (dk_no_deferred);
 
   scope = push_scope (ctype);
 
@@ -1342,6 +1343,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p,
 
   pop_scope (scope);
 
+  pop_deferring_access_checks ();
   --cp_unevaluated_operand;
   --c_inhibit_evaluation_warnings;
 }
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 010d7fc..70d6dac 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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 (tf_error);
+      perform_deferred_access_checks (tf_warning_or_error);
     }
 
   /* Consume the `;'.  */
@@ -12416,7 +12416,8 @@ 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, tf_error);
+					   chk->diag_decl,
+					   tf_warning_or_error);
 	}
       /* Return the stored value.  */
       return check_value->value;
@@ -15751,7 +15752,7 @@ cp_parser_init_declarator (cp_parser* parser,
 
       /* Perform the access control checks for the declarator and the
 	 decl-specifiers.  */
-      perform_deferred_access_checks (true);
+      perform_deferred_access_checks (tf_warning_or_error);
 
       /* Restore the saved value.  */
       if (TREE_CODE (decl) == FUNCTION_DECL)
@@ -21009,7 +21010,7 @@ cp_parser_function_definition_from_specifiers_and_declarator
      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 (tf_error);
+  perform_deferred_access_checks (tf_warning_or_error);
 
   if (!success_p)
     {
@@ -21303,7 +21304,7 @@ static void
 cp_parser_perform_template_parameter_access_checks (VEC (deferred_access_check,gc)* checks)
 {
   ++processing_template_parmlist;
-  perform_access_checks (checks, tf_error);
+  perform_access_checks (checks, tf_warning_or_error);
   --processing_template_parmlist;
 }
 
@@ -22752,7 +22753,7 @@ cp_parser_pre_parsed_nested_name_specifier (cp_parser *parser)
       FOR_EACH_VEC_ELT (deferred_access_check, checks, i, chk)
 	perform_or_defer_access_check (chk->binfo,
 				       chk->decl,
-				       chk->diag_decl, tf_error);
+				       chk->diag_decl, tf_warning_or_error);
     }
   /* Set the scope from the stored value.  */
   parser->scope = check_value->value;
@@ -24010,7 +24011,7 @@ cp_parser_objc_method_definition_list (cp_parser* parser)
 	  if (!(ptk->type == CPP_PLUS || ptk->type == CPP_MINUS 
 		|| ptk->type == CPP_EOF || ptk->keyword == RID_AT_END))
 	    {
-	      perform_deferred_access_checks (tf_error);
+	      perform_deferred_access_checks (tf_warning_or_error);
 	      stop_deferring_access_checks ();
 	      meth = cp_parser_function_definition_after_declarator (parser,
 								     false);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 26e3be2..776f5e6 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8370,7 +8370,8 @@ perform_typedefs_access_check (tree tmpl, tree targs)
          of the use of the typedef.  */
       input_location = iter->locus;
       perform_or_defer_access_check (TYPE_BINFO (type_scope),
-				     type_decl, type_decl, tf_error);
+				     type_decl, type_decl,
+				     tf_warning_or_error);
     }
     input_location = saved_location;
 }
@@ -8877,7 +8878,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 (tf_error);
+  perform_deferred_access_checks (tf_warning_or_error);
   pop_nested_class ();
   maximum_field_alignment = saved_maximum_field_alignment;
   if (!fn_context)
@@ -9870,13 +9871,10 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
 	    hash = hash_tmpl_and_args (gen_tmpl, argvec);
 	    spec = retrieve_specialization (gen_tmpl, argvec, hash);
 
-	    r = spec;
 	    if (spec)
 	      {
-		if (FNDECL_RECHECK_ACCESS_P (spec) && (complain & tf_error))
-		  /* Reinstantiate to get access errors.  */;
-		else
-		  break;
+		r = spec;
+		break;
 	      }
 
 	    /* We can see more levels of arguments than parameters if
@@ -9952,13 +9950,6 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
 	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
@@ -14298,6 +14289,23 @@ deduction_tsubst_fntype (tree fn, tree targs, tsubst_flags_t complain)
   return r;
 }
 
+/* We're out of SFINAE context now, so generate diagnostics for the access
+   errors we saw earlier when instantiating D from TMPL and ARGS.  */
+
+static void
+recheck_decl_substitution (tree d, tree tmpl, tree args)
+{
+  tree pattern = DECL_TEMPLATE_RESULT (tmpl);
+  tree type = TREE_TYPE (pattern);
+  location_t loc = input_location;
+
+  push_access_scope (d);
+  input_location = DECL_SOURCE_LOCATION (pattern);
+  tsubst (type, args, tf_warning_or_error, d);
+  input_location = loc;
+  pop_access_scope (d);
+}
+
 /* Instantiate the indicated variable or function template TMPL with
    the template arguments in TARG_PTR.  */
 
@@ -14357,11 +14365,9 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
 
   if (spec != NULL_TREE)
     {
-      if (FNDECL_RECHECK_ACCESS_P (spec)
-	  && (complain & tf_error))
-	/* Do the instantiation again, we're out of SFINAE context.  */;
-      else
-	return spec;
+      if (FNDECL_RECHECK_ACCESS_P (spec) && (complain & tf_error))
+	recheck_decl_substitution (spec, gen_tmpl, targ_ptr);
+      return spec;
     }
 
   if (check_instantiated_args (gen_tmpl, INNERMOST_TEMPLATE_ARGS (targ_ptr),
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 612b054..6381949 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -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, tf_error);
+	  perform_access_checks (checks, tf_warning_or_error);
 	}
       else
 	{
@@ -352,30 +352,6 @@ perform_or_defer_access_check (tree binfo, tree decl, tree diag_decl,
   return true;
 }
 
-/* Used by build_over_call in LOOKUP_SPECULATIVE mode: return whether DECL
-   is accessible in BINFO, and possibly complain if not.  If we're not
-   checking access, everything is accessible.  */
-
-bool
-speculative_access_check (tree binfo, tree decl, tree diag_decl,
-			  tsubst_flags_t complain)
-{
-  if (deferred_access_no_check)
-    return true;
-
-  /* If we're checking for implicit delete, we don't want access
-     control errors.  */
-  if (!accessible_p (binfo, decl, true))
-    {
-      /* Unless we're under maybe_explain_implicit_delete.  */
-      if (complain & tf_error)
-	enforce_access (binfo, decl, diag_decl, tf_error);
-      return false;
-    }
-
-  return true;
-}
-
 /* Returns nonzero if the current statement is a full expression,
    i.e. temporaries created during that statement should be destroyed
    at the end of the statement.  */
@@ -1620,7 +1596,7 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope)
       tree access_type = TREE_TYPE (object);
 
       perform_or_defer_access_check (TYPE_BINFO (access_type), decl,
-				     decl, tf_error);
+				     decl, tf_warning_or_error);
 
       /* If the data member was named `C::M', convert `*this' to `C'
 	 first.  */
@@ -1742,7 +1718,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, tf_error);
+				   decl, tf_warning_or_error);
 }
 
 /* EXPR is the result of a qualified-id.  The QUALIFYING_CLASS was the
@@ -3345,7 +3321,8 @@ finish_id_expression (tree id_expression,
 		{
 		  tree path = currently_open_derived_class (context);
 		  perform_or_defer_access_check (TYPE_BINFO (path),
-						 decl, decl, tf_error);
+						 decl, decl,
+						 tf_warning_or_error);
 		}
 	    }
 
diff --git a/gcc/testsuite/g++.dg/template/access23.C b/gcc/testsuite/g++.dg/template/access23.C
new file mode 100644
index 0000000..054cf92
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access23.C
@@ -0,0 +1,16 @@
+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);
+}

[-- Attachment #4: no-imp.patch --]
[-- Type: text/x-patch, Size: 777 bytes --]

commit ea9415a98877ad49f9443910f4b44e732aed0ac8
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Jul 18 17:39:21 2012 -0400

    	* method.c (process_subob_fn): Make sure no_implicit_p is non-null
    	before trying to store through it.

diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index b0e9ece..20a6c87 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -949,7 +949,7 @@ process_subob_fn (tree fn, bool move_p, tree *spec_p, bool *trivial_p,
 
   /* Core 1402: A non-trivial copy op suppresses the implicit
      declaration of the move ctor/op=.  */
-  if (move_p && !move_fn_p (fn) && !trivial_fn_p (fn))
+  if (no_implicit_p && move_p && !move_fn_p (fn) && !trivial_fn_p (fn))
     *no_implicit_p = true;
 
   if (constexpr_p && !DECL_DECLARED_CONSTEXPR_P (fn))

[-- Attachment #5: recheck-subst.patch --]
[-- Type: text/x-patch, Size: 1765 bytes --]

commit c871e18c31fd94e9ee7ee983bedf50047dedfe60
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Jul 18 18:37:17 2012 -0400

    	* pt.c (instantiate_decl): Don't recheck substitutions.

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 65eb2cc..0d25398 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -18248,35 +18248,6 @@ instantiate_decl (tree d, int defer_ok,
   if (TREE_CODE (d) == FUNCTION_DECL)
     maybe_instantiate_noexcept (d);
 
-  /* Recheck the substitutions to obtain any warning messages
-     about ignoring cv qualifiers.  Don't do this for artificial decls,
-     as it breaks the context-sensitive substitution for lambda op(). */
-  if (!defer_ok && !DECL_ARTIFICIAL (d))
-    {
-      tree gen = DECL_TEMPLATE_RESULT (gen_tmpl);
-      tree type = TREE_TYPE (gen);
-
-      /* Make sure that we can see identifiers, and compute access
-	 correctly.  D is already the target FUNCTION_DECL with the
-	 right context.  */
-      push_access_scope (d);
-
-      if (TREE_CODE (gen) == FUNCTION_DECL)
-	{
-	  tsubst (DECL_ARGUMENTS (gen), gen_args, tf_warning_or_error, d);
-          tsubst_exception_specification (type, gen_args, tf_warning_or_error,
-                                          d, /*defer_ok*/true);
-	  /* Don't simply tsubst the function type, as that will give
-	     duplicate warnings about poor parameter qualifications.
-	     The function arguments are the same as the decl_arguments
-	     without the top level cv qualifiers.  */
-	  type = TREE_TYPE (type);
-	}
-      tsubst (type, gen_args, tf_warning_or_error, d);
-
-      pop_access_scope (d);
-    }
-
   /* Defer all other templates, unless we have been explicitly
      forbidden from doing so.  */
   if (/* If there is no definition, we cannot instantiate the

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [C++ RFC / Patch] PR 51213 ("access control under SFINAE")
  2012-07-19  1:36     ` Jason Merrill
@ 2012-07-19  9:45       ` Paolo Carlini
  0 siblings, 0 replies; 16+ messages in thread
From: Paolo Carlini @ 2012-07-19  9:45 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On 07/19/2012 03:35 AM, Jason Merrill wrote:
> On 07/12/2012 07:06 PM, Jason Merrill wrote:
>> I notice that your patch changes the behavior of C++98/03 mode as well,
>> which seems wrong to me; I think this is a big enough change that we
>> should limit it to C++11 mode.
>
> ...except that I can't figure out what the semantics before this DR 
> were really supposed to be; G++, EDG and Clang all handle this stuff 
> differently.  After poking at it for a while I think the only sensible 
> thing is to have C++03 work the same as C++11.  I've done that, and 
> I'm going to check in the full patch.
>
> The first attachment is the full patch; the second is the changes 
> relative to your earlier patch.  I changed tf_error to 
> tf_warning_or_error, went back to treating C++03 and C++11 the same, 
> made the FNDECL_RECHECK_ACCESS_P change I mentioned, and streamlined 
> LOOKUP_SPECULATIVE handling.
Thanks A LOT!

Cheers,
Paolo.

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2012-07-19  9:45 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-06-15 14:57 [C++ RFC / Patch] PR 51213 ("access control under SFINAE") Paolo Carlini
2012-06-28 10:38 ` Paolo Carlini
2012-07-02 19:48 ` Jason Merrill
2012-07-09 16:05   ` Jason Merrill
2012-07-12 15:26 ` Jason Merrill
2012-07-12 23:54 ` Jason Merrill
2012-07-12 23:06   ` Jason Merrill
2012-07-19  1:36     ` Jason Merrill
2012-07-19  9:45       ` Paolo Carlini
2012-07-14  9:57   ` Paolo Carlini
2012-07-16 18:53     ` Jason Merrill
2012-07-17 12:46       ` Paolo Carlini
2012-07-17 14:11         ` Jason Merrill
2012-07-17 16:14           ` Paolo Carlini
2012-07-18  1:20             ` Paolo Carlini
2012-07-18  2:47               ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).