Patchwork PR c++/47346 - access control for nested type is ignored in template

login
register
mail settings
Submitter Dodji Seketeli
Date Aug. 17, 2011, 3:22 p.m.
Message ID <m3r54kcbba.fsf@redhat.com>
Download mbox | patch
Permalink /patch/110332/
State New
Headers show

Comments

Dodji Seketeli - Aug. 17, 2011, 3:22 p.m.
Hello,

Consider this code snippet:

    class C
    {
      struct Private { };
    };

    template<typename T>
    struct exploit1
    {
      C::Private type;
    };

    exploit1<int> x1;   //#1

At the instantiation point of exploit1<int> in #1, the compiler should
issue an error because C::Private is private in that context.

The infrastructure I have added a while back to do access checking of
references to types like this was limited to typedefs only.  This bug
seems to suggest that the access checking of references to types, at
template instantiation should be done for references to all types, not
just typedefs.

But then, consider this other example:


    class C
    {
      struct Private { };
    };

    template<typename T>
    struct exploit2 : C::Private
    {
    };

    exploit2<int> x2;

g++ should warn here too, because access to C::Private is private in
this context.  The problem is when
add_typedef_to_current_template_for_access_check is called (from
check_accessibility_of_qualified_id), during the parsing of the class
header, the "current template" representing exploit2 is not yet
created.  So the reference to C::Private cannot be added to that
not-yet created template.

The patch below loosens the existing typedefs access checking
mechanisms to make it work for all type references, and defers the
scheduling of that access checking when the relevant template is not
yet created, until it becomes available.

It also adjusts two test cases that were using similar invalid
constructs that are now caught by the compiler.

Tested on x86_64-unknown-linux-gnu against trunk.

gcc/cp/

	* cp-tree.h (struct qualified_type_usage_s):  Renamed struct
	qualified_typedef_usage_s into this.
	(qualified_type_usage_t): Renamed qualified_typedef_usage_t into
	this.
	(struct tree_template_info::type_needing_access_checking):
	Renamed typedefs_needing_access_checking into this.
	(TI_TYPES_NEEDING_ACCESS_CHECKING): Renamed
	TI_TYPEDEFS_NEEDING_ACCESS_CHECKING into this.
	(add_type_decl_to_current_template_for_access_check): Renamed
	add_typedef_to_current_template_for_access_check into this.
	(cp_parsing_class_head, cp_no_type_access_check_p): Declare new public
	global variables.
	(defer_type_access_check_for_cur_template)
	(schedule_deferred_access_check_types_for_template): New
	functions.
	* semantics.c
	(add_type_decl_to_current_template_for_access_check): Renamed
	add_typedef_to_current_template_for_access_check into this.  Make
	it accept potentially accesses to all types, not just typedefs.
	Make it defer the adding of the type access to the relevant
	template when said template is not yet available.
	(check_accessibility_of_qualified_id): Adjust.
	* decl.c (make_typename_type): Adjust.
	* parser.c (cp_parsing_class_head, cp_no_type_access_check_p):
	Define new public global variables.
	(cp_parser_elaborated_type_specifier): Don't schedule access
	checks for qualified type names of friend declaration, when in a
	template context.
	(cp_parser_class_specifier_1): Schedule qualified type names
	access check for type names access that happened while parsing the
	class head of a class template.
	(cp_parser_class_head): Notify the world when we are parsing a
	class head.
	* pt.c (cur_template_deferred_types_to_access_check): Define new
	global static variable.
	(perform_types_access_check): Renamed
	perform_typedefs_access_check into this and adjust the body.
	(instantiate_class_template_1, get_types_needing_access_check)
	(append_type_to_template_for_access_check_1)
	(append_type_to_template_for_access_check): Adjust.
	(defer_type_access_check_for_cur_template)
	(schedule_deferred_access_check_types_for_template): Define new
	functions.

gcc/testsuite/

	* g++.dg/template/access23.C: New test case.
	* g++.dg/lto/20081219_1.C: Adjust.
	* g++.dg/lto/20091002-1_0.C: Likewise.
---
 gcc/cp/cp-tree.h                         |   27 +++++----
 gcc/cp/decl.c                            |    4 +-
 gcc/cp/parser.c                          |   27 ++++++++-
 gcc/cp/pt.c                              |   92 +++++++++++++++++++++++-------
 gcc/cp/semantics.c                       |   59 ++++++++++++-------
 gcc/testsuite/g++.dg/lto/20081219_1.C    |    1 +
 gcc/testsuite/g++.dg/lto/20091002-1_0.C  |    1 +
 gcc/testsuite/g++.dg/template/access23.C |   33 +++++++++++
 8 files changed, 187 insertions(+), 57 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/access23.C

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ff5509e..3b01573 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -696,18 +696,18 @@  struct GTY (()) tree_lambda_expr
 
    In bar, the triplet will be (myint, foo, #1).
    */
-struct GTY(()) qualified_typedef_usage_s {
-  tree typedef_decl;
+struct GTY(()) qualified_type_usage_s {
+  tree type_decl;
   tree context;
   location_t locus;
 };
-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);
+typedef struct qualified_type_usage_s qualified_type_usage_t;
+DEF_VEC_O (qualified_type_usage_t);
+DEF_VEC_ALLOC_O (qualified_type_usage_t,gc);
 
 struct GTY(()) tree_template_info {
   struct tree_common common;
-  VEC(qualified_typedef_usage_t,gc) *typedefs_needing_access_checking;
+  VEC(qualified_type_usage_t,gc) *types_needing_access_checking;
 };
 
 enum cp_tree_node_structure_enum {
@@ -2609,9 +2609,9 @@  extern void decl_shadowed_for_var_insert (tree, tree);
 #endif
 /* The list of typedefs - used in the template - that need
    access checking at template instantiation time.  */
-#define TI_TYPEDEFS_NEEDING_ACCESS_CHECKING(NODE) \
+#define TI_TYPES_NEEDING_ACCESS_CHECKING(NODE) \
   ((struct tree_template_info*)TEMPLATE_INFO_CHECK \
-     (NODE))->typedefs_needing_access_checking
+     (NODE))->types_needing_access_checking
 
 /* We use TREE_VECs to hold template arguments.  If there is only one
    level of template arguments, then the TREE_VEC contains the
@@ -4093,6 +4093,9 @@  extern int function_depth;
    sizeof can be nested.  */
 
 extern int cp_unevaluated_operand;
+extern int cp_parsing_class_head;
+extern bool cp_no_type_access_check_p;
+
 extern tree cp_convert_range_for (tree, tree, tree);
 
 /* in pt.c  */
@@ -5142,6 +5145,8 @@  extern tree do_auto_deduction			(tree, tree, tree);
 extern tree type_uses_auto			(tree);
 extern void append_type_to_template_for_access_check (tree, tree, tree,
 						      location_t);
+extern void defer_type_access_check_for_cur_template (tree, tree, location_t);
+extern void schedule_deferred_access_check_types_for_template (tree);
 extern tree splice_late_return_type		(tree, tree);
 extern bool is_auto				(const_tree);
 extern tree process_template_parm		(tree, location_t, tree, 
@@ -5181,7 +5186,7 @@  extern tree make_pack_expansion                 (tree);
 extern bool check_for_bare_parameter_packs      (tree);
 extern tree build_template_info			(tree, tree);
 extern tree get_template_info			(const_tree);
-extern VEC(qualified_typedef_usage_t,gc)* get_types_needing_access_check (tree);
+extern VEC(qualified_type_usage_t,gc)* get_types_needing_access_check (tree);
 extern int template_class_depth			(tree);
 extern int is_specialization_of			(tree, tree);
 extern bool is_specialization_of_friend		(tree, tree);
@@ -5436,8 +5441,8 @@  extern void finish_mem_initializers		(tree);
 extern tree check_template_template_default_arg (tree);
 extern bool expand_or_defer_fn_1		(tree);
 extern void expand_or_defer_fn			(tree);
-extern void add_typedef_to_current_template_for_access_check (tree, tree,
-							      location_t);
+extern void add_type_decl_to_current_template_for_access_check (tree, tree,
+								location_t);
 extern void check_accessibility_of_qualified_id (tree, tree, tree);
 extern tree finish_qualified_id_expr		(tree, tree, bool, bool,
 						 bool, bool);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index c125f05..eef727b 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -3251,10 +3251,10 @@  make_typename_type (tree context, tree name, enum tag_types tag_type,
   if (complain & tf_error)
     perform_or_defer_access_check (TYPE_BINFO (context), t, t);
 
-  /* If we are currently parsing a template and if T is a typedef accessed
+  /* If we are currently parsing a template and if T is accessed
      through CONTEXT then we need to remember and check access of T at
      template instantiation time.  */
-  add_typedef_to_current_template_for_access_check (t, context, input_location);
+  add_type_decl_to_current_template_for_access_check (t, context, input_location);
 
   if (want_template)
     return lookup_template_class (t, TREE_OPERAND (fullname, 1),
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 84b8c60..91ab816 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -254,6 +254,9 @@  int cp_unevaluated_operand;
 /* Dump up to NUM tokens in BUFFER to FILE.  If NUM is 0, dump all the
    tokens.  */
 
+int cp_parsing_class_head = 0;
+bool cp_no_type_access_check_p = false;
+
 void
 cp_lexer_dump_tokens (FILE *file, VEC(cp_token,gc) *buffer, unsigned num)
 {
@@ -13243,7 +13246,14 @@  cp_parser_elaborated_type_specifier (cp_parser* parser,
 	{
 	  tree decl;
 	  tree ambiguous_decls;
-
+	  bool saved_cp_no_type_access_check_p = cp_no_type_access_check_p;
+
+	  /* So if we are parsing a friend template declaration, then
+	     we should make sure that looking up the name of the
+	     declaration doesn't schedule it for type access checking
+	     at the time of instantiation of the current containing
+	     template class, if any.  */
+	  cp_no_type_access_check_p = is_friend;
 	  decl = cp_parser_lookup_name (parser, identifier,
 					tag_type,
 					/*is_template=*/false,
@@ -13251,6 +13261,7 @@  cp_parser_elaborated_type_specifier (cp_parser* parser,
 					/*check_dependency=*/true,
 					&ambiguous_decls,
 					token->location);
+	  cp_no_type_access_check_p = saved_cp_no_type_access_check_p;
 
 	  /* If the lookup was ambiguous, an error will already have been
 	     issued.  */
@@ -17071,6 +17082,13 @@  cp_parser_class_specifier_1 (cp_parser* parser)
 			       &nested_name_specifier_p,
 			       &attributes,
 			       &bases);
+  /* We have parsed the class head, so now we have the resulting TYPE.
+     If TYPE is a template, then we can stuff if with the type
+     references that need access checking at its instantiation time.
+     These access checks were deferred in cp_parser_class_head because
+     TYPE was not available yet.  */
+  schedule_deferred_access_check_types_for_template (type);
+
   /* If the class-head was a semantic disaster, skip the entire body
      of the class.  */
   if (!type)
@@ -17389,13 +17407,17 @@  cp_parser_class_head (cp_parser* parser,
      type.  */
   num_templates = 0;
   parser->colon_corrects_to_scope_p = false;
+  cp_parsing_class_head++;
 
   *bases = NULL_TREE;
 
   /* Look for the class-key.  */
   class_key = cp_parser_class_key (parser);
   if (class_key == none_type)
-    return error_mark_node;
+    {
+      type = error_mark_node;
+      goto out;
+    }
 
   /* Parse the attributes.  */
   attributes = cp_parser_attributes_opt (parser);
@@ -17764,6 +17786,7 @@  cp_parser_class_head (cp_parser* parser,
   if (type && (virt_specifiers & VIRT_SPEC_FINAL))
     CLASSTYPE_FINAL (type) = 1;
  out:
+  cp_parsing_class_head--;
   parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
   return type;
 }
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 9ab110a..fb4ab24 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -94,6 +94,10 @@  static GTY ((param_is (spec_entry)))
 static GTY ((param_is (spec_entry)))
   htab_t type_specializations;
 
+/*  A vector of types that needs to be scheduled for access check when
+    the current template being parsed becomes available.  */
+static GTY (()) VEC(qualified_type_usage_t,gc) *cur_template_deferred_types_to_access_check;
+
 /* Contains canonical template parameter types. The vector is indexed by
    the TEMPLATE_TYPE_IDX of the template parameter. Each element is a
    TREE_LIST, whose TREE_VALUEs contain the canonical template
@@ -197,7 +201,7 @@  static tree tsubst_expr	(tree, tree, tsubst_flags_t, tree, bool);
 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 void perform_types_access_check (tree tmpl, tree targs);
 static void append_type_to_template_for_access_check_1 (tree, tree, tree,
 							location_t);
 static tree listify (tree);
@@ -8376,18 +8380,18 @@  apply_late_template_attributes (tree *decl_p, tree attributes, int attr_flags,
     }
 }
 
-/* Perform (or defer) access check for typedefs that were referenced
+/* Perform (or defer) access check for types that were referenced
    from within the template TMPL code.
    This is a subroutine of instantiate_template and instantiate_class_template.
    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)
+perform_types_access_check (tree tmpl, tree targs)
 {
   location_t saved_location;
   int i;
-  qualified_typedef_usage_t *iter;
+  qualified_type_usage_t *iter;
 
   if (!tmpl
       || (!CLASS_TYPE_P (tmpl)
@@ -8395,11 +8399,11 @@  perform_typedefs_access_check (tree tmpl, tree targs)
     return;
 
   saved_location = input_location;
-  FOR_EACH_VEC_ELT (qualified_typedef_usage_t,
+  FOR_EACH_VEC_ELT (qualified_type_usage_t,
 		    get_types_needing_access_check (tmpl),
 		    i, iter)
     {
-      tree type_decl = iter->typedef_decl;
+      tree type_decl = iter->type_decl;
       tree type_scope = iter->context;
 
       if (!type_decl || !type_scope || !CLASS_TYPE_P (type_scope))
@@ -8905,7 +8909,7 @@  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_types_access_check (pattern, args);
   perform_deferred_access_checks ();
   pop_nested_class ();
   maximum_field_alignment = saved_maximum_field_alignment;
@@ -14159,7 +14163,7 @@  instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
      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_types_access_check (DECL_TEMPLATE_RESULT (tmpl), targ_ptr);
   perform_deferred_access_checks ();
   pop_access_scope (fndecl);
   pop_deferring_access_checks ();
@@ -19912,11 +19916,11 @@  type_uses_auto (tree type)
    Those typedefs were added to T by the function
    append_type_to_template_for_access_check.  */
 
-VEC(qualified_typedef_usage_t,gc)*
+VEC(qualified_type_usage_t,gc)*
 get_types_needing_access_check (tree t)
 {
   tree ti;
-  VEC(qualified_typedef_usage_t,gc) *result = NULL;
+  VEC(qualified_type_usage_t,gc) *result = NULL;
 
   if (!t || t == error_mark_node)
     return NULL;
@@ -19930,7 +19934,7 @@  get_types_needing_access_check (tree t)
       if (!TI_TEMPLATE (ti))
 	return NULL;
 
-      result = TI_TYPEDEFS_NEEDING_ACCESS_CHECKING (ti);
+      result = TI_TYPES_NEEDING_ACCESS_CHECKING (ti);
     }
 
   return result;
@@ -19953,7 +19957,7 @@  append_type_to_template_for_access_check_1 (tree t,
 					    tree scope,
 					    location_t location)
 {
-  qualified_typedef_usage_t typedef_usage;
+  qualified_type_usage_t type_usage;
   tree ti;
 
   if (!t || t == error_mark_node)
@@ -19970,13 +19974,13 @@  append_type_to_template_for_access_check_1 (tree t,
 
   gcc_assert (TI_TEMPLATE (ti));
 
-  typedef_usage.typedef_decl = type_decl;
-  typedef_usage.context = scope;
-  typedef_usage.locus = location;
+  type_usage.type_decl = type_decl;
+  type_usage.context = scope;
+  type_usage.locus = location;
 
-  VEC_safe_push (qualified_typedef_usage_t, gc,
-		 TI_TYPEDEFS_NEEDING_ACCESS_CHECKING (ti),
-		 &typedef_usage);
+  VEC_safe_push (qualified_type_usage_t, gc,
+		 TI_TYPES_NEEDING_ACCESS_CHECKING (ti),
+		 &type_usage);
 }
 
 /* Append TYPE_DECL to the template TEMPL.
@@ -20014,16 +20018,16 @@  append_type_to_template_for_access_check (tree templ,
 					  tree scope,
 					  location_t location)
 {
-  qualified_typedef_usage_t *iter;
+  qualified_type_usage_t *iter;
   int i;
 
   gcc_assert (type_decl && (TREE_CODE (type_decl) == TYPE_DECL));
 
   /* Make sure we don't append the type to the template twice.  */
-  FOR_EACH_VEC_ELT (qualified_typedef_usage_t,
+  FOR_EACH_VEC_ELT (qualified_type_usage_t,
 		    get_types_needing_access_check (templ),
 		    i, iter)
-    if (iter->typedef_decl == type_decl && scope == iter->context)
+    if (iter->type_decl == type_decl && scope == iter->context)
       return;
 
   append_type_to_template_for_access_check_1 (templ, type_decl,
@@ -20060,4 +20064,50 @@  print_template_statistics (void)
 	   htab_collisions (type_specializations));
 }
 
+/* Save the type access check of T through SCOPE, so that it can be
+   scheduled later when
+   schedule_deferred_access_check_types_for_template is called.  */
+
+void
+defer_type_access_check_for_cur_template (tree t, tree scope, location_t location)
+{
+  qualified_type_usage_t type_usage;
+
+  if (t == NULL_TREE || TREE_CODE (t) != TYPE_DECL)
+    return;
+
+  type_usage.type_decl = t;
+  type_usage.context = scope;
+  type_usage.locus = location;
+
+  VEC_safe_push (qualified_type_usage_t, gc,
+		 cur_template_deferred_types_to_access_check,
+		 &type_usage);
+}
+
+/* Add the type access checks stashed away by
+   defer_type_access_check_for_cur_template to the list of type
+   access checks to be done at the instantiation time of TEMPL.  */
+
+void
+schedule_deferred_access_check_types_for_template (tree templ)
+{
+  int i;
+  qualified_type_usage_t *type_usage;
+
+  FOR_EACH_VEC_ELT (qualified_type_usage_t,
+		    cur_template_deferred_types_to_access_check,
+		    i, type_usage)
+    append_type_to_template_for_access_check (templ, type_usage->type_decl,
+					      type_usage->context,
+					      type_usage->locus);
+
+  if (cur_template_deferred_types_to_access_check != NULL)
+    {
+      VEC_free (qualified_type_usage_t, gc,
+		cur_template_deferred_types_to_access_check);
+      cur_template_deferred_types_to_access_check = NULL;
+    }
+}
+
 #include "gt-cp-pt.h"
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 59b25e5..99f5e83 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1606,35 +1606,52 @@  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
-   performed for each typedef.
-   LOCATION is the location of the usage point of TYPEDEF_DECL.  */
+/* If we are currently parsing a template and we encountered a type
+   decl DECL that is being accessed though CONTEXT, this function adds
+   that DECL to a list tied to the current template.  At template
+   instantiatin time, that list is walked and access check performed
+   for each DECL.
+
+   Note that if we are parsing a class head or a list
+   of template parameters, there is no "current template" defined yet.
+   In that case, the function stashes DECL away, until the "current
+   template" is built and
+   schedule_deferred_access_check_types_for_template is called.
+
+   LOCATION is the location of the usage point of DECL.  */
 
 void
-add_typedef_to_current_template_for_access_check (tree typedef_decl,
-                                                  tree context,
-						  location_t location)
+add_type_decl_to_current_template_for_access_check (tree decl,
+						    tree context,
+						    location_t location)
 {
     tree template_info = NULL;
     tree cs = current_scope ();
 
-    if (!is_typedef_decl (typedef_decl)
+    if (decl == NULL_TREE
+	|| TREE_CODE (decl) != TYPE_DECL
 	|| !context
 	|| !CLASS_TYPE_P (context)
-	|| !cs)
+	|| cp_no_type_access_check_p
+	|| (at_namespace_scope_p ()
+	    && !cp_parsing_class_head)
+	|| currently_open_class (context)
+	|| currently_open_class (TREE_TYPE (decl)))
       return;
 
+    if ((cp_parsing_class_head && processing_template_decl)
+	|| processing_template_parmlist)
+      {
+	defer_type_access_check_for_cur_template (decl, context, location);
+	return;
+      }
+
     if (CLASS_TYPE_P (cs) || TREE_CODE (cs) == FUNCTION_DECL)
       template_info = get_template_info (cs);
 
     if (template_info
-	&& TI_TEMPLATE (template_info)
-	&& !currently_open_class (context))
-      append_type_to_template_for_access_check (cs, typedef_decl,
-						context, location);
+	&& TI_TEMPLATE (template_info))
+      append_type_to_template_for_access_check (cs, decl, context, location);
 }
 
 /* DECL was the declaration to which a qualified-id resolved.  Issue
@@ -1651,15 +1668,15 @@  check_accessibility_of_qualified_id (tree decl,
   tree scope;
   tree qualifying_type = NULL_TREE;
 
-  /* If we are parsing a template declaration and if decl is a typedef,
+  /* If we are parsing a template declaration and if decl is a TYPE_DECL,
      add it to a list tied to the template.
      At template instantiation time, that list will be walked and
      access check performed.  */
-  add_typedef_to_current_template_for_access_check (decl,
-						    nested_name_specifier
-						    ? nested_name_specifier
-						    : DECL_CONTEXT (decl),
-						    input_location);
+  add_type_decl_to_current_template_for_access_check (decl,
+						      nested_name_specifier
+						      ? nested_name_specifier
+						      : DECL_CONTEXT (decl),
+						      input_location);
 
   /* If we're not checking, return immediately.  */
   if (deferred_access_no_check)
diff --git a/gcc/testsuite/g++.dg/lto/20081219_1.C b/gcc/testsuite/g++.dg/lto/20081219_1.C
index 1bb96ef..9e80836 100644
--- a/gcc/testsuite/g++.dg/lto/20081219_1.C
+++ b/gcc/testsuite/g++.dg/lto/20081219_1.C
@@ -9,6 +9,7 @@  namespace std __attribute__ ((__visibility__ ("default")))
   typedef int *__c_locale;
   class locale
   {
+  public:
     class facet;
   };
   class locale::facet
diff --git a/gcc/testsuite/g++.dg/lto/20091002-1_0.C b/gcc/testsuite/g++.dg/lto/20091002-1_0.C
index c63b079..025939d 100644
--- a/gcc/testsuite/g++.dg/lto/20091002-1_0.C
+++ b/gcc/testsuite/g++.dg/lto/20091002-1_0.C
@@ -14,6 +14,7 @@  namespace std __attribute__ ((__visibility__ ("default")))
   template<typename _CharT, typename _InIter = istreambuf_iterator<_CharT> >
       class num_get;
   class locale   {
+  public:
       class facet;
   };
   class locale::facet   {
diff --git a/gcc/testsuite/g++.dg/template/access23.C b/gcc/testsuite/g++.dg/template/access23.C
new file mode 100644
index 0000000..b9c9d49
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/access23.C
@@ -0,0 +1,33 @@ 
+class C
+{
+  struct Private { }; // { dg-error "is private" }
+};
+
+template<typename T>
+struct exploit1
+{
+  typedef C::Private type; // { dg-error "in this context" }
+};
+
+template<typename T>
+struct exploit2 : C::Private  // { dg-error "in this context" }
+{
+};
+
+template<typename T>
+struct exploit3
+{
+  C::Private a; // { dg-error "in this context" }
+};
+
+template<typename T>
+struct exploit4
+{
+  template<class U = C::Private> // { dg-error "in this context" }
+  struct E {};
+};
+
+exploit1<int>::type x1;
+exploit2<int> x2;
+exploit3<int> x3;
+exploit4<int>::E<> x4;