PR c++/1016
* cp-tree.h (pushtag): Adjust declaration.
* decl.c (lookup_and_check_tag): Call lookup_type_scope if
lookup_name fails.
(xref_tag): Adjust call to pushtag. Make hidden class visible.
(start_enum): Adjust call to pushtag.
* name-lookup.c (ambiguous_decl): Ignore hidden names.
(qualify_lookup): Change return type to bool.
(hidden_name_p): New function.
(lookup_namespace_name, unqualified_namespace_lookup,
lookup_name_real): Use it.
(lookup_type_scope): Update comments.
(maybe_process_template_type_declaration): Change parameter name
from globalize to is_friend.
(pushtag): Change globalize parameter of type int to tag_scope.
Hide name if introduced by friend declaration.
* name-lookup.h (hidden_name_p): Add declaration.
* parser.c (cp_parser_lookup_name): Don't deal with hidden name
here.
* pt.c (push_template_decl_real): Make hidden class template
visible.
(lookup_template_class, instantiate_class_template): Adjust call
to pushtag.
* semantics.c (begin_class_definition): Likewise.
* rtti.c (init_rtti_processing, build_dynamic_cast_1,
tinfo_base_init, emit_support_tinfos): Use ts_current instead of
ts_global.
* g++.dg/lookup/hidden-class1.C: New test.
* g++.dg/lookup/hidden-class2.C: Likewise.
* g++.dg/lookup/hidden-class3.C: Likewise.
* g++.dg/lookup/hidden-class4.C: Likewise.
* g++.dg/lookup/hidden-class5.C: Likewise.
* g++.dg/lookup/hidden-class6.C: Likewise.
* g++.dg/lookup/hidden-class7.C: Likewise.
* g++.dg/lookup/hidden-class8.C: Likewise.
* g++.dg/lookup/hidden-class9.C: Likewise.
* g++.dg/lookup/hidden-temp-class1.C: Likewise.
* g++.dg/lookup/hidden-temp-class2.C: Likewise.
* g++.dg/lookup/hidden-temp-class3.C: Likewise.
* g++.dg/lookup/hidden-temp-class4.C: Likewise.
* g++.dg/lookup/hidden-temp-class5.C: Likewise.
* g++.dg/lookup/hidden-temp-class6.C: Likewise.
* g++.dg/lookup/hidden-temp-class7.C: Likewise.
* g++.dg/lookup/hidden-temp-class8.C: Likewise.
* g++.dg/lookup/hidden-temp-class9.C: Likewise.
* g++.dg/lookup/hidden-temp-class10.C: Likewise.
* g++.dg/lookup/hidden-temp-class11.C: Likewise.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@96430
138bc75d-0d04-0410-961f-
82ee72b054a4
+2005-03-14 Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+
+ Friend class name lookup 5/n
+ PR c++/1016
+ * cp-tree.h (pushtag): Adjust declaration.
+ * decl.c (lookup_and_check_tag): Call lookup_type_scope if
+ lookup_name fails.
+ (xref_tag): Adjust call to pushtag. Make hidden class visible.
+ (start_enum): Adjust call to pushtag.
+ * name-lookup.c (ambiguous_decl): Ignore hidden names.
+ (qualify_lookup): Change return type to bool.
+ (hidden_name_p): New function.
+ (lookup_namespace_name, unqualified_namespace_lookup,
+ lookup_name_real): Use it.
+ (lookup_type_scope): Update comments.
+ (maybe_process_template_type_declaration): Change parameter name
+ from globalize to is_friend.
+ (pushtag): Change globalize parameter of type int to tag_scope.
+ Hide name if introduced by friend declaration.
+ * name-lookup.h (hidden_name_p): Add declaration.
+ * parser.c (cp_parser_lookup_name): Don't deal with hidden name
+ here.
+ * pt.c (push_template_decl_real): Make hidden class template
+ visible.
+ (lookup_template_class, instantiate_class_template): Adjust call
+ to pushtag.
+ * semantics.c (begin_class_definition): Likewise.
+ * rtti.c (init_rtti_processing, build_dynamic_cast_1,
+ tinfo_base_init, emit_support_tinfos): Use ts_current instead of
+ ts_global.
+
2005-03-13 Mark Mitchell <mark@codesourcery.com>
PR c++/20157
extern void finish_scope (void);
extern void push_switch (tree);
extern void pop_switch (void);
-extern tree pushtag (tree, tree, int);
+extern tree pushtag (tree, tree, tag_scope);
extern tree make_anon_name (void);
extern int decls_match (tree, tree);
extern tree duplicate_decls (tree, tree);
void f(class C); // No template header here
then the required template argument is missing. */
-
error ("template argument required for %<%s %T%>",
tag_name (tag_code),
DECL_NAME (CLASSTYPE_TI_TEMPLATE (type)));
tree t;
tree decl;
if (scope == ts_global)
- decl = lookup_name (name, 2);
+ {
+ /* First try ordinary name lookup, ignoring hidden class name
+ injected via friend declaration. */
+ decl = lookup_name (name, 2);
+ /* If that fails, the name will be placed in the smallest
+ non-class, non-function-prototype scope according to 3.3.1/5.
+ We may already have a hidden name declared as friend in this
+ scope. So lookup again but not ignoring hidden name.
+ If we find one, that name will be made visible rather than
+ creating a new tag. */
+ if (!decl)
+ decl = lookup_type_scope (name, ts_within_enclosing_non_class);
+ }
else
decl = lookup_type_scope (name, scope);
{
t = make_aggr_type (code);
TYPE_CONTEXT (t) = context;
- /* pushtag only cares whether SCOPE is zero or not. */
- t = pushtag (name, t, scope != ts_current);
+ t = pushtag (name, t, scope);
}
}
else
error ("redeclaration of %qT as a non-template", t);
t = error_mark_node;
}
+
+ /* Make injected friend class visible. */
+ if (scope != ts_within_enclosing_non_class
+ && hidden_name_p (TYPE_NAME (t)))
+ {
+ DECL_ANTICIPATED (TYPE_NAME (t)) = 0;
+ DECL_FRIEND_P (TYPE_NAME (t)) = 0;
+
+ if (TYPE_TEMPLATE_INFO (t))
+ {
+ DECL_ANTICIPATED (TYPE_TI_TEMPLATE (t)) = 0;
+ DECL_FRIEND_P (TYPE_TI_TEMPLATE (t)) = 0;
+ }
+ }
}
POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
name = make_anon_name ();
enumtype = make_node (ENUMERAL_TYPE);
- enumtype = pushtag (name, enumtype, 0);
+ enumtype = pushtag (name, enumtype, /*tag_scope=*/ts_current);
}
return enumtype;
case TEMPLATE_DECL:
/* If we expect types or namespaces, and not templates,
or this is not a template class. */
- if (LOOKUP_QUALIFIERS_ONLY (flags)
- && !DECL_CLASS_TEMPLATE_P (val))
+ if ((LOOKUP_QUALIFIERS_ONLY (flags)
+ && !DECL_CLASS_TEMPLATE_P (val))
+ || hidden_name_p (val))
val = NULL_TREE;
break;
case TYPE_DECL:
- if (LOOKUP_NAMESPACES_ONLY (flags))
+ if (LOOKUP_NAMESPACES_ONLY (flags) || hidden_name_p (val))
val = NULL_TREE;
break;
case NAMESPACE_DECL:
break;
case FUNCTION_DECL:
/* Ignore built-in functions that are still anticipated. */
- if (LOOKUP_QUALIFIERS_ONLY (flags) || DECL_ANTICIPATED (val))
+ if (LOOKUP_QUALIFIERS_ONLY (flags) || hidden_name_p (val))
val = NULL_TREE;
break;
default:
}
/* Given a lookup that returned VAL, use FLAGS to decide if we want to
- ignore it or not. Subroutine of lookup_name_real. */
+ ignore it or not. Subroutine of lookup_name_real and
+ lookup_type_scope. */
-static tree
+static bool
qualify_lookup (tree val, int flags)
{
if (val == NULL_TREE)
- return val;
+ return false;
if ((flags & LOOKUP_PREFER_NAMESPACES) && TREE_CODE (val) == NAMESPACE_DECL)
- return val;
+ return true;
if ((flags & LOOKUP_PREFER_TYPES)
&& (TREE_CODE (val) == TYPE_DECL || TREE_CODE (val) == TEMPLATE_DECL))
- return val;
+ return true;
if (flags & (LOOKUP_PREFER_NAMESPACES | LOOKUP_PREFER_TYPES))
- return NULL_TREE;
- return val;
+ return false;
+ return true;
+}
+
+/* Given a lookup that returned VAL, decide if we want to ignore it or
+ not based on DECL_ANTICIPATED_P. */
+
+bool
+hidden_name_p (tree val)
+{
+ if (DECL_P (val)
+ && DECL_LANG_SPECIFIC (val)
+ && DECL_ANTICIPATED (val))
+ return true;
+ return false;
}
/* Look up NAME in the NAMESPACE. */
if (TREE_CODE (val) == OVERLOAD && ! really_overloaded_fn (val))
val = OVL_FUNCTION (val);
- /* Ignore built-in functions that haven't been prototyped yet. */
- if (!val || !DECL_P(val)
- || !DECL_LANG_SPECIFIC(val)
- || !DECL_ANTICIPATED (val))
+ /* Ignore built-in functions and friends that haven't been declared
+ yet. */
+ if (!val || !hidden_name_p (val))
POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, val);
}
if (b)
{
- if (b->value && DECL_P (b->value)
- && DECL_LANG_SPECIFIC (b->value)
- && DECL_ANTICIPATED (b->value))
- /* Ignore anticipated built-in functions. */
+ if (b->value && hidden_name_p (b->value))
+ /* Ignore anticipated built-in functions and friends. */
;
else
binding.value = b->value;
node of some kind representing its definition if there is only one
such declaration, or return a TREE_LIST with all the overloaded
definitions if there are many, or return 0 if it is undefined.
+ Hidden name, either friend declaration or built-in function, are
+ not ignored.
If PREFER_TYPE is > 0, we prefer TYPE_DECLs or namespaces.
If PREFER_TYPE is > 1, we reject non-type decls (e.g. namespaces).
continue;
/* If this is the kind of thing we're looking for, we're done. */
- if (qualify_lookup (iter->value, flags))
+ if (qualify_lookup (iter->value, flags)
+ && !hidden_name_p (iter->value))
binding = iter->value;
else if ((flags & LOOKUP_PREFER_TYPES)
- && qualify_lookup (iter->type, flags))
+ && qualify_lookup (iter->type, flags)
+ && !hidden_name_p (iter->type))
binding = iter->type;
else
binding = NULL_TREE;
Unlike lookup_name_real, we make sure that NAME is actually
declared in the desired scope, not from inheritance, nor using
directive. For using declaration, there is DR138 still waiting
- to be resolved.
+ to be resolved. Hidden name coming from earlier an friend
+ declaration is also returned.
A TYPE_DECL best matching the NAME is returned. Catching error
and issuing diagnostics are caller's responsibility. */
if (iter)
{
- /* If this is the kind of thing we're looking for, we're done.
- Ignore names found via using declaration. See DR138 for
- current status. */
+ /* If this is the kind of thing we're looking for, we're done. */
if (qualify_lookup (iter->type, LOOKUP_PREFER_TYPES))
val = iter->type;
else if (qualify_lookup (iter->value, LOOKUP_PREFER_TYPES))
processing. */
static tree
-maybe_process_template_type_declaration (tree type, int globalize,
+maybe_process_template_type_declaration (tree type, int is_friend,
cxx_scope *b)
{
tree decl = TYPE_NAME (type);
push_template_decl_real, but we want the original value. */
tree name = DECL_NAME (decl);
- decl = push_template_decl_real (decl, globalize);
+ decl = push_template_decl_real (decl, is_friend);
/* If the current binding level is the binding level for the
template parameters (see the comment in
begin_template_parm_list) and the enclosing level is a class
friend case, push_template_decl will already have put the
friend into global scope, if appropriate. */
if (TREE_CODE (type) != ENUMERAL_TYPE
- && !globalize && b->kind == sk_template_parms
+ && !is_friend && b->kind == sk_template_parms
&& b->level_chain->kind == sk_class)
{
finish_member_declaration (CLASSTYPE_TI_TEMPLATE (type));
Returns TYPE upon success and ERROR_MARK_NODE otherwise. */
tree
-pushtag (tree name, tree type, int globalize)
+pushtag (tree name, tree type, tag_scope scope)
{
struct cp_binding_level *b;
template is instantiated. */
|| (b->kind == sk_template_parms && b->explicit_spec_p)
|| (b->kind == sk_class
- && (globalize
+ && (scope != ts_current
/* We may be defining a new type in the initializer
of a static member variable. We allow this when
not pedantic, and it is particularly useful for
{
tree cs = current_scope ();
- if (! globalize)
+ if (scope == ts_current)
context = cs;
else if (cs != NULL_TREE && TYPE_P (cs))
/* When declaring a friend class of a local class, we want
d = create_implicit_typedef (name, type);
DECL_CONTEXT (d) = FROB_CONTEXT (context);
+ if (scope == ts_within_enclosing_non_class)
+ {
+ /* This is a friend. Make this TYPE_DECL node hidden from
+ ordinary name lookup. Its corresponding TEMPLATE_DECL
+ will be marked in push_template_decl_real. */
+ retrofit_lang_decl (d);
+ DECL_ANTICIPATED (d) = 1;
+ DECL_FRIEND_P (d) = 1;
+ }
+
if (! in_class)
set_identifier_type_value_with_scope (name, d, b);
- d = maybe_process_template_type_declaration (type,
- globalize, b);
+ d = maybe_process_template_type_declaration
+ (type, scope == ts_within_enclosing_non_class, b);
if (d == error_mark_node)
POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, error_mark_node);
extern tree lookup_type_scope (tree, tag_scope);
extern tree namespace_binding (tree, tree);
extern void set_namespace_binding (tree, tree, tree);
+extern bool hidden_name_p (tree);
extern tree lookup_namespace_name (tree, tree);
extern tree lookup_qualified_name (tree, tree, bool, bool);
extern tree lookup_name_nonclass (tree);
}
/* If the lookup failed, let our caller know. */
- if (!decl
- || decl == error_mark_node
- || (TREE_CODE (decl) == FUNCTION_DECL
- && DECL_ANTICIPATED (decl)))
+ if (!decl || decl == error_mark_node)
return error_mark_node;
/* If it's a TREE_LIST, the result of the lookup was ambiguous. */
tmpl = pushdecl_namespace_level (tmpl);
if (tmpl == error_mark_node)
return error_mark_node;
+
+ /* Hide template friend classes that haven't been declared yet. */
+ if (is_friend && TREE_CODE (decl) == TYPE_DECL)
+ {
+ DECL_ANTICIPATED (tmpl) = 1;
+ DECL_FRIEND_P (tmpl) = 1;
+ }
}
if (primary)
/* A local class. Make sure the decl gets registered properly. */
if (context == current_function_decl)
- pushtag (DECL_NAME (template), t, 0);
+ pushtag (DECL_NAME (template), t, /*tag_scope=*/ts_current);
}
/* If we called start_enum or pushtag above, this information
tsubst_enum. */
if (name)
SET_IDENTIFIER_TYPE_VALUE (name, newtag);
- pushtag (name, newtag, /*globalize=*/0);
+ pushtag (name, newtag, /*tag_scope=*/ts_current);
}
}
else if (TREE_CODE (t) == FUNCTION_DECL
push_namespace (std_identifier);
type_info_type = xref_tag (class_type, get_identifier ("type_info"),
- /*tag_scope=*/ts_global, false);
+ /*tag_scope=*/ts_current, false);
pop_namespace ();
const_type_info_type_node
= build_qualified_type (type_info_type, TYPE_QUAL_CONST);
push_nested_namespace (ns);
tinfo_ptr = xref_tag (class_type,
get_identifier ("__class_type_info"),
- /*tag_scope=*/ts_global, false);
+ /*tag_scope=*/ts_current, false);
tinfo_ptr = build_pointer_type
(build_qualified_type
push_nested_namespace (abi_node);
real_type = xref_tag (class_type, TINFO_REAL_NAME (desc),
- /*tag_scope=*/ts_global, false);
+ /*tag_scope=*/ts_current, false);
pop_nested_namespace (abi_node);
if (!COMPLETE_TYPE_P (real_type))
push_nested_namespace (abi_node);
bltn_type = xref_tag (class_type,
get_identifier ("__fundamental_type_info"),
- /*tag_scope=*/ts_global, false);
+ /*tag_scope=*/ts_current, false);
pop_nested_namespace (abi_node);
if (!COMPLETE_TYPE_P (bltn_type))
return;
if (t == error_mark_node || ! IS_AGGR_TYPE (t))
{
t = make_aggr_type (RECORD_TYPE);
- pushtag (make_anon_name (), t, 0);
+ pushtag (make_anon_name (), t, /*tag_scope=*/ts_current);
}
/* Update the location of the decl. */
if (TYPE_BEING_DEFINED (t))
{
t = make_aggr_type (TREE_CODE (t));
- pushtag (TYPE_IDENTIFIER (t), t, 0);
+ pushtag (TYPE_IDENTIFIER (t), t, /*tag_scope=*/ts_current);
}
maybe_process_partial_specialization (t);
pushclass (t);
+2005-03-14 Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+
+ Friend class name lookup 5/n
+ PR c++/1016
+ * g++.dg/lookup/hidden-class1.C: New test.
+ * g++.dg/lookup/hidden-class2.C: Likewise.
+ * g++.dg/lookup/hidden-class3.C: Likewise.
+ * g++.dg/lookup/hidden-class4.C: Likewise.
+ * g++.dg/lookup/hidden-class5.C: Likewise.
+ * g++.dg/lookup/hidden-class6.C: Likewise.
+ * g++.dg/lookup/hidden-class7.C: Likewise.
+ * g++.dg/lookup/hidden-class8.C: Likewise.
+ * g++.dg/lookup/hidden-class9.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class1.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class2.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class3.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class4.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class5.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class6.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class7.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class8.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class9.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class10.C: Likewise.
+ * g++.dg/lookup/hidden-temp-class11.C: Likewise.
+
2005-03-13 Mark Mitchell <mark@codesourcery.com>
PR c++/20157
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ friend class B;
+ B *b; // { dg-error "no type|expected" }
+};
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ friend class B;
+};
+
+class B* b;
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ friend class B;
+
+ class B;
+ B *b;
+};
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ friend class B;
+};
+
+class B *b;
+B *c;
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ friend class B;
+};
+
+B* b; // { dg-error "expected" }
--- /dev/null
+// { dg-do compile }
+
+// Origin: Jay Cox <jaycox@gimp.org>
+
+// PR c++/1016: Name lookup for injected friend class
+
+class B;
+
+namespace N {
+ class A {
+ friend class B;
+ B* b;
+ };
+}
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ friend class B;
+};
+
+class C {
+ friend class B;
+};
+
+B *b; // { dg-error "expected" }
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+namespace N {
+ class A {
+ friend class B;
+ };
+}
+
+class N::B { // { dg-error "not name a class" }
+};
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+namespace N {
+ class A {
+ friend class B;
+ };
+}
+
+using N::B; // { dg-error "declared" }
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ template <class T> friend class B;
+ B<int> *b; // { dg-error "no type|expected" }
+};
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+namespace N {
+ class A {
+ template <class T> friend class B;
+ };
+}
+
+template <class T> class N::B { // { dg-error "not name a class" }
+};
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+namespace N {
+ class A {
+ template <class T> friend class B;
+ };
+}
+
+using N::B; // { dg-error "declared" }
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ template <class T> friend class B;
+};
+
+class B* b; // { dg-error "argument required|invalid" }
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ template <class T> friend class B;
+ template <class T> class B;
+ B<int> *b;
+};
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ template <class T> friend class B;
+};
+
+B<int> *b; // { dg-error "expected" }
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ template <class T> friend class B;
+};
+
+template <class T> class B;
+B<int>* b;
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class B;
+namespace N {
+ class A {
+ template <class T> friend class B;
+ B* b;
+ };
+}
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ template <class T> friend class B;
+};
+
+class C {
+ template <class T> friend class B;
+};
+
+B<int> *b; // { dg-error "expected" }
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ template <class T> friend class B;
+};
+
+class C {
+ friend class B; // { dg-error "argument required|friend" }
+};
--- /dev/null
+// Copyright (C) 2005 Free Software Foundation
+// Contributed by Kriang Lerdsuwanakij <lerdsuwa@users.sourceforge.net>
+// { dg-do compile }
+
+class A {
+ friend class B;
+};
+
+class C {
+ template <class T> friend class B; // { dg-error "not a template" }
+};