re PR c++/7874 (g++ finds friend functions defined in class-definition but not declared in the enclosing namespace)

./	PR g++/7874
	* c.opt (ffriend-injection): New C++ option.
	* doc/invoke.texi (Option Summary): Mention -ffriend-injection.
	(C++ Dialect Options): Document -ffriend-injection.
cp/
	PR g++/7874
	* cp-tree.h (struct lang_decl_flags): Add hidden_friend_p
	bitfield.  Make dummy bitfield one bit smaller.
	(DECL_HIDDEN_FRIEND_P): Define.
	(pushdecl_maybe_friend): Declare.
	(pushdecl_top_level_maybe_friend): Declare.
	* decl.c (duplicate_decls): Add newdecl_is_friend parameter.
	Change prototype and all callers.  Add assertion that a
	DECL_ARTIFICIAL FUNCTION_DECL is not DECL_HIDDEN_FRIEND_P.  Set
	DECL_ANTICIPATED and DECL_HIDDEN_FRIEND_P in duplicated decl if
	appropriate.
	* name-lookup.c (supplement_binding): Don't ignore a
	DECL_HIDDEN_FRIEND_P.
	(pushdecl_maybe_friend): Break out contents of pushdecl.  Add
	is_friend parameter.  Set DECL_ANTICIPATED and
	DECL_HIDDEN_FRIEND_P for a friend function.
	(pushdecl): Just call pushdecl_maybe_friend.
	(pushdecl_with_scope): Add is_friend parameter.  Change prototype
	and all callers.
	(pushdecl_namespace_level): Likewise.
	(push_overloaded_decl): Likewise.  Check DECL_HIDDEN_FRIEND_P as
	well as DECL_ANTICIPATED when checking for a builtin.
	(do_nonmember_using_decl): Check DECL_HIDDEN_FRIEND_P as well as
	DECL_ANTICIPATED when checking for a builtin.
	(do_nonmember_using_decl): Likewise.
	(pushdecl_top_level_1): Add is_friend parameter.  Change all
	callers.
	(pushdecl_top_level_maybe_friend): New function.
	(remove_hidden_names): New function.
	(struct arg_lookup): Add args field.
	(friend_of_associated_class_p): New static function.
	(arg_assoc_namespace): Ignore hidden functions which are not
	friends of an associated class of some argument.
	(lookup_arg_dependent): Remove hidden functions from list passed
	in.  Initialize k.args.
	* name-lookup.h (remove_hidden_names): Declare.
	* friend.c (do_friend): Call pushdecl_maybe_friend instead of
	pushdecl.
	* call.c (add_function_candidate): Change DECL_ANTICIPATED test to
	an assertion, with a check for DECL_HIDDEN_FRIEND_P.
	(build_new_function_call): Add koenig_p parameter.  Change
	prototype and callers.
	* pt.c (register_specialization): Add is_friend parameter.  Change
	all callers.
	(push_template_decl_real): Change is_friend parameter to bool.
	Change prototype and all callers.
	(tsubst_friend_class): Call pushdecl_top_level_maybe_friend
	instead of pushdecl_top_level.
testsuite/
	PR g++/7874
	* g++.dg/lookup/friend7.C: New test.
	* g++.dg/lookup/friend8.C: New test.
	* g++.dg/parse/defarg4.C: Add a parameter to the friend function,
	so that it will be found via argument dependent lookup.
	* g++.old-deja/g++.brendan/crash56.C: Don't expect errors for
	friend functions which will no longer be found.
	* g++.old-deja/g++.jason/friend.C: Add a parameter to the friend
	function g, so that it will be found via argument dependent
	lookup.
	* g++.old-deja/g++.jason/scoping15.C: Use -ffriend-injection.
	* g++.old-deja/g++.mike/net43.C: Likewise.

From-SVN: r104188
This commit is contained in:
Ian Lance Taylor 2005-09-12 19:54:23 +00:00 committed by Ian Lance Taylor
parent dbc564aea3
commit d63d5d0c32
21 changed files with 403 additions and 90 deletions

View File

@ -1,3 +1,10 @@
2005-09-12 Ian Lance Taylor <ian@airs.com>
PR g++/7874
* c.opt (ffriend-injection): New C++ option.
* doc/invoke.texi (Option Summary): Mention -ffriend-injection.
(C++ Dialect Options): Document -ffriend-injection.
2005-09-12 Josh Conner <jconner@apple.com>
PR middle-end/23237

View File

@ -554,6 +554,10 @@ fimplicit-templates
C++ ObjC++
Emit implicit instantiations of templates
ffriend-injection
C++ Var(flag_friend_injection)
Inject friend functions into enclosing namespace
flabels-ok
C++ ObjC++

View File

@ -1,3 +1,54 @@
2005-09-12 Ian Lance Taylor <ian@airs.com>
PR g++/7874
* cp-tree.h (struct lang_decl_flags): Add hidden_friend_p
bitfield. Make dummy bitfield one bit smaller.
(DECL_HIDDEN_FRIEND_P): Define.
(pushdecl_maybe_friend): Declare.
(pushdecl_top_level_maybe_friend): Declare.
* decl.c (duplicate_decls): Add newdecl_is_friend parameter.
Change prototype and all callers. Add assertion that a
DECL_ARTIFICIAL FUNCTION_DECL is not DECL_HIDDEN_FRIEND_P. Set
DECL_ANTICIPATED and DECL_HIDDEN_FRIEND_P in duplicated decl if
appropriate.
* name-lookup.c (supplement_binding): Don't ignore a
DECL_HIDDEN_FRIEND_P.
(pushdecl_maybe_friend): Break out contents of pushdecl. Add
is_friend parameter. Set DECL_ANTICIPATED and
DECL_HIDDEN_FRIEND_P for a friend function.
(pushdecl): Just call pushdecl_maybe_friend.
(pushdecl_with_scope): Add is_friend parameter. Change prototype
and all callers.
(pushdecl_namespace_level): Likewise.
(push_overloaded_decl): Likewise. Check DECL_HIDDEN_FRIEND_P as
well as DECL_ANTICIPATED when checking for a builtin.
(do_nonmember_using_decl): Check DECL_HIDDEN_FRIEND_P as well as
DECL_ANTICIPATED when checking for a builtin.
(do_nonmember_using_decl): Likewise.
(pushdecl_top_level_1): Add is_friend parameter. Change all
callers.
(pushdecl_top_level_maybe_friend): New function.
(remove_hidden_names): New function.
(struct arg_lookup): Add args field.
(friend_of_associated_class_p): New static function.
(arg_assoc_namespace): Ignore hidden functions which are not
friends of an associated class of some argument.
(lookup_arg_dependent): Remove hidden functions from list passed
in. Initialize k.args.
* name-lookup.h (remove_hidden_names): Declare.
* friend.c (do_friend): Call pushdecl_maybe_friend instead of
pushdecl.
* call.c (add_function_candidate): Change DECL_ANTICIPATED test to
an assertion, with a check for DECL_HIDDEN_FRIEND_P.
(build_new_function_call): Add koenig_p parameter. Change
prototype and callers.
* pt.c (register_specialization): Add is_friend parameter. Change
all callers.
(push_template_decl_real): Change is_friend parameter to bool.
Change prototype and all callers.
(tsubst_friend_class): Call pushdecl_top_level_maybe_friend
instead of pushdecl_top_level.
2005-09-11 Richard Henderson <rth@redhat.com>
* decl2.c (build_anon_union_vars): Copy attributes from the base addr.

View File

@ -1309,10 +1309,10 @@ add_function_candidate (struct z_candidate **candidates,
tree orig_arglist;
int viable = 1;
/* Built-in functions that haven't been declared don't really
exist. */
if (DECL_ANTICIPATED (fn))
return NULL;
/* At this point we should not see any functions which haven't been
explicitly declared, except for friend functions which will have
been found using argument dependent lookup. */
gcc_assert (!DECL_ANTICIPATED (fn) || DECL_HIDDEN_FRIEND_P (fn));
/* The `this', `in_chrg' and VTT arguments to constructors are not
considered in overload resolution. */
@ -2758,7 +2758,7 @@ perform_overload_resolution (tree fn,
or a static member function) with the ARGS. */
tree
build_new_function_call (tree fn, tree args)
build_new_function_call (tree fn, tree args, bool koenig_p)
{
struct z_candidate *candidates, *cand;
bool any_viable_p;
@ -2769,6 +2769,22 @@ build_new_function_call (tree fn, tree args)
if (args == error_mark_node)
return error_mark_node;
/* If this function was found without using argument dependent
lookup, then we want to ignore any undeclared friend
functions. */
if (!koenig_p)
{
tree orig_fn = fn;
fn = remove_hidden_names (fn);
if (!fn)
{
error ("no matching function for call to %<%D(%A)%>",
DECL_NAME (OVL_CURRENT (orig_fn)), args);
return error_mark_node;
}
}
/* Get the high-water mark for the CONVERSION_OBSTACK. */
p = conversion_obstack_alloc (0);

View File

@ -5645,7 +5645,8 @@ resolve_address_of_overloaded_function (tree target_type,
one, or vice versa. */
continue;
/* Ignore anticipated decls of undeclared builtins. */
/* Ignore functions which haven't been explicitly
declared. */
if (DECL_ANTICIPATED (fn))
continue;

View File

@ -1512,7 +1512,8 @@ struct lang_decl_flags GTY(())
unsigned thunk_p : 1;
unsigned this_thunk_p : 1;
unsigned repo_available_p : 1;
unsigned dummy : 3;
unsigned hidden_friend_p : 1;
unsigned dummy : 2;
union lang_decl_u {
/* In a FUNCTION_DECL for which DECL_THUNK_P holds, this is
@ -1814,9 +1815,8 @@ struct lang_decl GTY(())
#define DECL_INITIALIZED_IN_CLASS_P(DECL) \
(DECL_LANG_SPECIFIC (DECL)->decl_flags.initialized_in_class)
/* Nonzero for FUNCTION_DECL means that this decl is just a
friend declaration, and should not be added to the list of
member functions for this class. */
/* Nonzero for DECL means that this decl is just a friend declaration,
and should not be added to the list of members for this class. */
#define DECL_FRIEND_P(NODE) (DECL_LANG_SPECIFIC (NODE)->decl_flags.friend_attr)
/* A TREE_LIST of the types which have befriended this FUNCTION_DECL. */
@ -2321,11 +2321,19 @@ extern void decl_shadowed_for_var_insert (tree, tree);
#define DECL_LOCAL_FUNCTION_P(NODE) \
DECL_LANG_FLAG_0 (FUNCTION_DECL_CHECK (NODE))
/* Nonzero if NODE is a FUNCTION_DECL for a built-in function, and we have
not yet seen a prototype for that function. */
/* Nonzero if NODE is a DECL which we know about but which has not
been explicitly declared, such as a built-in function or a friend
declared inside a class. In the latter case DECL_HIDDEN_FRIEND_P
will be set. */
#define DECL_ANTICIPATED(NODE) \
(DECL_LANG_SPECIFIC (DECL_COMMON_CHECK (NODE))->decl_flags.anticipated_p)
/* Nonzero if NODE is a FUNCTION_DECL which was declared as a friend
within a class but has not been declared in the surrounding scope.
The function is invisible except via argument dependent lookup. */
#define DECL_HIDDEN_FRIEND_P(NODE) \
(DECL_LANG_SPECIFIC (DECL_COMMON_CHECK (NODE))->decl_flags.hidden_friend_p)
/* Record whether a typedef for type `int' was actually `signed int'. */
#define C_TYPEDEF_EXPLICITLY_SIGNED(EXP) DECL_LANG_FLAG_1 (EXP)
@ -3644,7 +3652,7 @@ extern bool null_ptr_cst_p (tree);
extern bool sufficient_parms_p (tree);
extern tree type_decays_to (tree);
extern tree build_user_type_conversion (tree, tree, int);
extern tree build_new_function_call (tree, tree);
extern tree build_new_function_call (tree, tree, bool);
extern tree build_operator_new_call (tree, tree, tree *, tree *);
extern tree build_new_method_call (tree, tree, tree, tree, int);
extern tree build_special_member_call (tree, tree, tree, tree, int);
@ -3743,6 +3751,7 @@ extern void adjust_clone_args (tree);
extern tree poplevel (int, int, int);
extern void insert_block (tree);
extern tree pushdecl (tree);
extern tree pushdecl_maybe_friend (tree, bool);
extern void cxx_init_decl_processing (void);
enum cp_tree_node_structure_enum cp_tree_node_structure
(union lang_tree_node *);
@ -3756,8 +3765,9 @@ extern void pop_switch (void);
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);
extern tree duplicate_decls (tree, tree, bool);
extern tree pushdecl_top_level (tree);
extern tree pushdecl_top_level_maybe_friend (tree, bool);
extern tree pushdecl_top_level_and_finish (tree, tree);
extern tree push_using_decl (tree, tree);
extern tree declare_local_label (tree);
@ -3983,7 +3993,7 @@ extern tree end_template_parm_list (tree);
extern void end_template_decl (void);
extern tree current_template_args (void);
extern tree push_template_decl (tree);
extern tree push_template_decl_real (tree, int);
extern tree push_template_decl_real (tree, bool);
extern void redeclare_class_template (tree, tree);
extern tree lookup_template_class (tree, tree, tree, tree,
int, tsubst_flags_t);

View File

@ -1009,13 +1009,15 @@ warn_extern_redeclared_static (tree newdecl, tree olddecl)
error_mark_node is returned. Otherwise, OLDDECL is returned.
If NEWDECL is not a redeclaration of OLDDECL, NULL_TREE is
returned. */
returned.
NEWDECL_IS_FRIEND is true if NEWDECL was declared as a friend. */
tree
duplicate_decls (tree newdecl, tree olddecl)
duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
{
unsigned olddecl_uid = DECL_UID (olddecl);
int olddecl_friend = 0, types_match = 0;
int olddecl_friend = 0, types_match = 0, hidden_friend = 0;
int new_defines_function = 0;
if (newdecl == olddecl)
@ -1069,9 +1071,11 @@ duplicate_decls (tree newdecl, tree olddecl)
if (TREE_CODE (olddecl) == FUNCTION_DECL
&& DECL_ARTIFICIAL (olddecl))
{
gcc_assert (!DECL_HIDDEN_FRIEND_P (olddecl));
if (TREE_CODE (newdecl) != FUNCTION_DECL)
{
/* Avoid warnings redeclaring anticipated built-ins. */
/* Avoid warnings redeclaring built-ins which have not been
explicitly declared. */
if (DECL_ANTICIPATED (olddecl))
return NULL_TREE;
@ -1102,7 +1106,8 @@ duplicate_decls (tree newdecl, tree olddecl)
}
else if (!types_match)
{
/* Avoid warnings redeclaring anticipated built-ins. */
/* Avoid warnings redeclaring built-ins which have not been
explicitly declared. */
if (DECL_ANTICIPATED (olddecl))
{
/* Deal with fileptr_type_node. FILE type is not known
@ -1131,7 +1136,8 @@ duplicate_decls (tree newdecl, tree olddecl)
= TYPE_ARG_TYPES (TREE_TYPE (newdecl));
types_match = decls_match (newdecl, olddecl);
if (types_match)
return duplicate_decls (newdecl, olddecl);
return duplicate_decls (newdecl, olddecl,
newdecl_is_friend);
TYPE_ARG_TYPES (TREE_TYPE (olddecl)) = oldargs;
}
}
@ -1163,8 +1169,9 @@ duplicate_decls (tree newdecl, tree olddecl)
/* Replace the old RTL to avoid problems with inlining. */
COPY_DECL_RTL (newdecl, olddecl);
}
/* Even if the types match, prefer the new declarations type
for anticipated built-ins, for exception lists, etc... */
/* Even if the types match, prefer the new declarations type for
built-ins which have not been explicitly declared, for
exception lists, etc... */
else if (DECL_ANTICIPATED (olddecl))
{
tree type = TREE_TYPE (newdecl);
@ -1460,7 +1467,7 @@ duplicate_decls (tree newdecl, tree olddecl)
/* Don't warn about extern decl followed by definition. */
&& !(DECL_EXTERNAL (olddecl) && ! DECL_EXTERNAL (newdecl))
/* Don't warn about friends, let add_friend take care of it. */
&& ! (DECL_FRIEND_P (newdecl) || DECL_FRIEND_P (olddecl)))
&& ! (newdecl_is_friend || DECL_FRIEND_P (olddecl)))
{
warning (0, "redundant redeclaration of %qD in same scope", newdecl);
warning (0, "previous declaration of %q+D", olddecl);
@ -1685,6 +1692,9 @@ duplicate_decls (tree newdecl, tree olddecl)
DECL_INITIALIZED_IN_CLASS_P (newdecl)
|= DECL_INITIALIZED_IN_CLASS_P (olddecl);
olddecl_friend = DECL_FRIEND_P (olddecl);
hidden_friend = (DECL_ANTICIPATED (olddecl)
&& DECL_HIDDEN_FRIEND_P (olddecl)
&& newdecl_is_friend);
/* Only functions have DECL_BEFRIENDING_CLASSES. */
if (TREE_CODE (newdecl) == FUNCTION_DECL
@ -1898,6 +1908,11 @@ duplicate_decls (tree newdecl, tree olddecl)
DECL_UID (olddecl) = olddecl_uid;
if (olddecl_friend)
DECL_FRIEND_P (olddecl) = 1;
if (hidden_friend)
{
DECL_ANTICIPATED (olddecl) = 1;
DECL_HIDDEN_FRIEND_P (olddecl) = 1;
}
/* NEWDECL contains the merged attribute lists.
Update OLDDECL to be the same. */
@ -3142,7 +3157,7 @@ cp_make_fname_decl (tree id, int type_dep)
struct cp_binding_level *b = current_binding_level;
while (b->level_chain->kind != sk_function_parms)
b = b->level_chain;
pushdecl_with_scope (decl, b);
pushdecl_with_scope (decl, b, /*is_friend=*/false);
cp_finish_decl (decl, init, NULL_TREE, LOOKUP_ONLYCONVERTING);
}
else
@ -3185,8 +3200,9 @@ builtin_function_1 (const char* name,
if (libname)
SET_DECL_ASSEMBLER_NAME (decl, get_identifier (libname));
/* Warn if a function in the namespace for users
is used without an occasion to consider it declared. */
/* A function in the user's namespace should have an explicit
declaration before it is used. Mark the built-in function as
anticipated but not actually declared. */
if (name[0] != '_' || name[1] != '_')
DECL_ANTICIPATED (decl) = 1;
@ -3720,7 +3736,7 @@ start_decl (const cp_declarator *declarator,
if (DECL_INITIAL (decl)
&& DECL_INITIALIZED_IN_CLASS_P (field))
error ("duplicate initialization of %qD", decl);
if (duplicate_decls (decl, field))
if (duplicate_decls (decl, field, /*newdecl_is_friend=*/false))
decl = field;
}
}
@ -3731,7 +3747,8 @@ start_decl (const cp_declarator *declarator,
> template_class_depth (context))
? current_template_parms
: NULL_TREE);
if (field && duplicate_decls (decl, field))
if (field && duplicate_decls (decl, field,
/*newdecl_is_friend=*/false))
decl = field;
}
@ -5871,7 +5888,7 @@ grokfndecl (tree ctype,
/* Attempt to merge the declarations. This can fail, in
the case of some invalid specialization declarations. */
pushed_scope = push_scope (ctype);
ok = duplicate_decls (decl, old_decl);
ok = duplicate_decls (decl, old_decl, friendp);
if (pushed_scope)
pop_scope (pushed_scope);
if (!ok)

View File

@ -480,7 +480,7 @@ do_friend (tree ctype, tree declarator, tree decl,
else if (class_template_depth)
/* We rely on tsubst_friend_function to check the
validity of the declaration later. */
decl = push_template_decl_real (decl, /*is_friend=*/1);
decl = push_template_decl_real (decl, /*is_friend=*/true);
else
decl = check_classfn (ctype, decl,
template_member_p
@ -527,13 +527,13 @@ do_friend (tree ctype, tree declarator, tree decl,
general, such a declaration depends on template
parameters. Instead, we call pushdecl when the class
is instantiated. */
decl = push_template_decl_real (decl, /*is_friend=*/1);
decl = push_template_decl_real (decl, /*is_friend=*/true);
else if (current_function_decl)
/* This must be a local class, so pushdecl will be ok, and
insert an unqualified friend into the local scope
(rather than the containing namespace scope, which the
next choice will do). */
decl = pushdecl (decl);
decl = pushdecl_maybe_friend (decl, /*is_friend=*/true);
else
{
/* We can't use pushdecl, as we might be in a template
@ -543,7 +543,7 @@ do_friend (tree ctype, tree declarator, tree decl,
tree ns = decl_namespace_context (decl);
push_nested_namespace (ns);
decl = pushdecl_namespace_level (decl);
decl = pushdecl_namespace_level (decl, /*is_friend=*/true);
pop_nested_namespace (ns);
}

View File

@ -44,7 +44,7 @@ static cxx_scope *innermost_nonclass_level (void);
static tree select_decl (const struct scope_binding *, int);
static cxx_binding *binding_for_name (cxx_scope *, tree);
static tree lookup_name_innermost_nonclass_level (tree);
static tree push_overloaded_decl (tree, int);
static tree push_overloaded_decl (tree, int, bool);
static bool lookup_using_namespace (tree, struct scope_binding *, tree,
tree, int);
static bool qualified_lookup_using_namespace (tree, tree,
@ -437,10 +437,11 @@ supplement_binding (cxx_binding *binding, tree decl)
error recovery purpose, pretend this was the intended
declaration for that name. */
|| bval == error_mark_node
/* If BVAL is a built-in that has not yet been declared,
/* If BVAL is anticipated but has not yet been declared,
pretend it is not there at all. */
|| (TREE_CODE (bval) == FUNCTION_DECL
&& DECL_ANTICIPATED (bval)))
&& DECL_ANTICIPATED (bval)
&& !DECL_HIDDEN_FRIEND_P (bval)))
binding->value = decl;
else if (TREE_CODE (bval) == TYPE_DECL && DECL_ARTIFICIAL (bval))
{
@ -487,7 +488,7 @@ supplement_binding (cxx_binding *binding, tree decl)
&& DECL_EXTERNAL (decl) && DECL_EXTERNAL (bval)
&& !DECL_CLASS_SCOPE_P (decl))
{
duplicate_decls (decl, binding->value);
duplicate_decls (decl, binding->value, /*newdecl_is_friend=*/false);
ok = false;
}
else if (TREE_CODE (decl) == NAMESPACE_DECL
@ -551,14 +552,15 @@ add_decl_to_level (tree decl, cxx_scope *b)
/* Record a decl-node X as belonging to the current lexical scope.
Check for errors (such as an incompatible declaration for the same
name already seen in the same scope).
name already seen in the same scope). IS_FRIEND is true if X is
declared as a friend.
Returns either X or an old decl for the same name.
If an old decl is returned, it may have been smashed
to agree with what X says. */
tree
pushdecl (tree x)
pushdecl_maybe_friend (tree x, bool is_friend)
{
tree t;
tree name;
@ -679,7 +681,7 @@ pushdecl (tree x)
gcc_assert (DECL_CONTEXT (t));
/* Check for duplicate params. */
if (duplicate_decls (x, t))
if (duplicate_decls (x, t, is_friend))
POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
}
else if ((DECL_EXTERN_C_FUNCTION_P (x)
@ -697,7 +699,7 @@ pushdecl (tree x)
}
else
{
tree olddecl = duplicate_decls (x, t);
tree olddecl = duplicate_decls (x, t, is_friend);
/* If the redeclaration failed, we can stop at this
point. */
@ -742,7 +744,7 @@ pushdecl (tree x)
if (DECL_NON_THUNK_FUNCTION_P (x) && ! DECL_FUNCTION_MEMBER_P (x))
{
t = push_overloaded_decl (x, PUSH_LOCAL);
t = push_overloaded_decl (x, PUSH_LOCAL, is_friend);
if (t != x)
POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
if (!namespace_bindings_p ())
@ -753,7 +755,7 @@ pushdecl (tree x)
}
else if (DECL_FUNCTION_TEMPLATE_P (x) && DECL_NAMESPACE_SCOPE_P (x))
{
t = push_overloaded_decl (x, PUSH_GLOBAL);
t = push_overloaded_decl (x, PUSH_GLOBAL, is_friend);
if (t == x)
add_decl_to_level (x, NAMESPACE_LEVEL (CP_DECL_CONTEXT (t)));
POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, t);
@ -815,6 +817,16 @@ pushdecl (tree x)
}
}
if (TREE_CODE (x) == FUNCTION_DECL
&& is_friend
&& !flag_friend_injection)
{
/* This is a new declaration of a friend function, so hide
it from ordinary function lookup. */
DECL_ANTICIPATED (x) = 1;
DECL_HIDDEN_FRIEND_P (x) = 1;
}
/* This name is new in its binding level.
Install the new declaration and return it. */
if (namespace_bindings_p ())
@ -997,6 +1009,14 @@ pushdecl (tree x)
POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, x);
}
/* Record a decl-node X as belonging to the current lexical scope. */
tree
pushdecl (tree x)
{
return pushdecl_maybe_friend (x, false);
}
/* Enter DECL into the symbol table, if that's appropriate. Returns
DECL, or a modified version thereof. */
@ -1795,7 +1815,7 @@ push_using_decl (tree scope, tree name)
caller to set DECL_CONTEXT properly. */
tree
pushdecl_with_scope (tree x, cxx_scope *level)
pushdecl_with_scope (tree x, cxx_scope *level, bool is_friend)
{
struct cp_binding_level *b;
tree function_decl = current_function_decl;
@ -1813,7 +1833,7 @@ pushdecl_with_scope (tree x, cxx_scope *level)
{
b = current_binding_level;
current_binding_level = level;
x = pushdecl (x);
x = pushdecl_maybe_friend (x, is_friend);
current_binding_level = b;
}
current_function_decl = function_decl;
@ -1835,12 +1855,14 @@ pushdecl_with_scope (tree x, cxx_scope *level)
PUSH_USING: DECL is being pushed as the result of a using
declaration.
IS_FRIEND is true if this is a friend declaration.
The value returned may be a previous declaration if we guessed wrong
about what language DECL should belong to (C or C++). Otherwise,
it's always DECL (and never something that's not a _DECL). */
static tree
push_overloaded_decl (tree decl, int flags)
push_overloaded_decl (tree decl, int flags, bool is_friend)
{
tree name = DECL_NAME (decl);
tree old;
@ -1880,7 +1902,7 @@ push_overloaded_decl (tree decl, int flags)
error ("%q#D conflicts with previous using declaration %q#D",
decl, fn);
if (duplicate_decls (decl, fn) == fn)
if (duplicate_decls (decl, fn, is_friend) == fn)
POP_TIMEVAR_AND_RETURN (TV_NAME_LOOKUP, fn);
}
@ -1888,7 +1910,8 @@ push_overloaded_decl (tree decl, int flags)
may fail to merge the decls if the new decl is e.g. a
template function. */
if (TREE_CODE (old) == FUNCTION_DECL
&& DECL_ANTICIPATED (old))
&& DECL_ANTICIPATED (old)
&& !DECL_HIDDEN_FRIEND_P (old))
old = NULL;
}
else if (old == error_mark_node)
@ -2037,7 +2060,8 @@ do_nonmember_using_decl (tree scope, tree name, tree oldval, tree oldtype,
is a built-in, then we can just pretend it isn't there. */
if (oldval
&& TREE_CODE (oldval) == FUNCTION_DECL
&& DECL_ANTICIPATED (oldval))
&& DECL_ANTICIPATED (oldval)
&& !DECL_HIDDEN_FRIEND_P (oldval))
oldval = NULL_TREE;
/* Check for using functions. */
@ -2075,7 +2099,8 @@ do_nonmember_using_decl (tree scope, tree name, tree oldval, tree oldtype,
else if (compparms (TYPE_ARG_TYPES (TREE_TYPE (new_fn)),
TYPE_ARG_TYPES (TREE_TYPE (old_fn))))
{
gcc_assert (!DECL_ANTICIPATED (old_fn));
gcc_assert (!DECL_ANTICIPATED (old_fn)
|| DECL_HIDDEN_FRIEND_P (old_fn));
/* There was already a non-using declaration in
this scope with the same parameter types. If both
@ -2168,7 +2193,8 @@ do_local_using_decl (tree decl, tree scope, tree name)
for (fn = newval; fn && OVL_CURRENT (fn) != term;
fn = OVL_NEXT (fn))
push_overloaded_decl (OVL_CURRENT (fn),
PUSH_LOCAL | PUSH_USING);
PUSH_LOCAL | PUSH_USING,
false);
}
else
push_local_binding (name, newval, PUSH_USING);
@ -3058,13 +3084,13 @@ do_namespace_alias (tree alias, tree namespace)
if appropriate. */
tree
pushdecl_namespace_level (tree x)
pushdecl_namespace_level (tree x, bool is_friend)
{
struct cp_binding_level *b = current_binding_level;
tree t;
timevar_push (TV_NAME_LOOKUP);
t = pushdecl_with_scope (x, NAMESPACE_LEVEL (current_namespace));
t = pushdecl_with_scope (x, NAMESPACE_LEVEL (current_namespace), is_friend);
/* Now, the type_shadowed stack may screw us. Munge it so it does
what we want. */
@ -3247,11 +3273,11 @@ parse_using_directive (tree namespace, tree attribs)
*INIT, if INIT is non-NULL. */
static tree
pushdecl_top_level_1 (tree x, tree *init)
pushdecl_top_level_1 (tree x, tree *init, bool is_friend)
{
timevar_push (TV_NAME_LOOKUP);
push_to_top_level ();
x = pushdecl_namespace_level (x);
x = pushdecl_namespace_level (x, is_friend);
if (init)
cp_finish_decl (x, *init, NULL_TREE, 0);
pop_from_top_level ();
@ -3263,7 +3289,15 @@ pushdecl_top_level_1 (tree x, tree *init)
tree
pushdecl_top_level (tree x)
{
return pushdecl_top_level_1 (x, NULL);
return pushdecl_top_level_1 (x, NULL, false);
}
/* Like pushdecl_top_level, but adding the IS_FRIEND parameter. */
tree
pushdecl_top_level_maybe_friend (tree x, bool is_friend)
{
return pushdecl_top_level_1 (x, NULL, is_friend);
}
/* Like pushdecl, only it places X in the global scope if
@ -3273,7 +3307,7 @@ pushdecl_top_level (tree x)
tree
pushdecl_top_level_and_finish (tree x, tree init)
{
return pushdecl_top_level_1 (x, &init);
return pushdecl_top_level_1 (x, &init, false);
}
/* Combines two sets of overloaded functions into an OVERLOAD chain, removing
@ -3440,7 +3474,7 @@ qualify_lookup (tree val, int flags)
}
/* Given a lookup that returned VAL, decide if we want to ignore it or
not based on DECL_ANTICIPATED_P. */
not based on DECL_ANTICIPATED. */
bool
hidden_name_p (tree val)
@ -3452,6 +3486,38 @@ hidden_name_p (tree val)
return false;
}
/* Remove any hidden friend functions from a possibly overloaded set
of functions. */
tree
remove_hidden_names (tree fns)
{
if (!fns)
return fns;
if (TREE_CODE (fns) == FUNCTION_DECL && hidden_name_p (fns))
fns = NULL_TREE;
else if (TREE_CODE (fns) == OVERLOAD)
{
tree o;
for (o = fns; o; o = OVL_NEXT (o))
if (hidden_name_p (OVL_CURRENT (o)))
break;
if (o)
{
tree n = NULL_TREE;
for (o = fns; o; o = OVL_NEXT (o))
if (!hidden_name_p (OVL_CURRENT (o)))
n = build_overload (OVL_CURRENT (o), n);
fns = n;
}
}
return fns;
}
/* Look up NAME in the NAMESPACE. */
tree
@ -4112,6 +4178,7 @@ lookup_type_current_level (tree name)
struct arg_lookup
{
tree name;
tree args;
tree namespaces;
tree classes;
tree functions;
@ -4190,6 +4257,53 @@ is_associated_namespace (tree current, tree scope)
}
}
/* Return whether FN is a friend of an associated class of ARG. */
static bool
friend_of_associated_class_p (tree arg, tree fn)
{
tree type;
if (TYPE_P (arg))
type = arg;
else if (type_unknown_p (arg))
return false;
else
type = TREE_TYPE (arg);
/* If TYPE is a class, the class itself and all base classes are
associated classes. */
if (CLASS_TYPE_P (type))
{
if (is_friend (type, fn))
return true;
if (TYPE_BINFO (type))
{
tree binfo, base_binfo;
int i;
for (binfo = TYPE_BINFO (type), i = 0;
BINFO_BASE_ITERATE (binfo, i, base_binfo);
i++)
if (is_friend (BINFO_TYPE (base_binfo), fn))
return true;
}
}
/* If TYPE is a class member, the class of which it is a member is
an associated class. */
if ((CLASS_TYPE_P (type)
|| TREE_CODE (type) == UNION_TYPE
|| TREE_CODE (type) == ENUMERAL_TYPE)
&& TYPE_CONTEXT (type)
&& CLASS_TYPE_P (TYPE_CONTEXT (type))
&& is_friend (TYPE_CONTEXT (type), fn))
return true;
return false;
}
/* Add functions of a namespace to the lookup structure.
Returns true on error. */
@ -4213,8 +4327,25 @@ arg_assoc_namespace (struct arg_lookup *k, tree scope)
return false;
for (; value; value = OVL_NEXT (value))
if (add_function (k, OVL_CURRENT (value)))
return true;
{
/* We don't want to find arbitrary hidden functions via argument
dependent lookup. We only want to find friends of associated
classes. */
if (hidden_name_p (OVL_CURRENT (value)))
{
tree args;
for (args = k->args; args; args = TREE_CHAIN (args))
if (friend_of_associated_class_p (TREE_VALUE (args),
OVL_CURRENT (value)))
break;
if (!args)
continue;
}
if (add_function (k, OVL_CURRENT (value)))
return true;
}
return false;
}
@ -4485,7 +4616,14 @@ lookup_arg_dependent (tree name, tree fns, tree args)
struct arg_lookup k;
timevar_push (TV_NAME_LOOKUP);
/* Remove any hidden friend functions from the list of functions
found so far. They will be added back by arg_assoc_class as
appropriate. */
fns = remove_hidden_names (fns);
k.name = name;
k.args = args;
k.functions = fns;
k.classes = NULL_TREE;
@ -4699,7 +4837,7 @@ pushtag (tree name, tree type, tag_scope scope)
pushdecl_class_level (decl);
}
else if (b->kind != sk_template_parms)
decl = pushdecl_with_scope (decl, b);
decl = pushdecl_with_scope (decl, b, /*is_friend=*/false);
TYPE_CONTEXT (type) = DECL_CONTEXT (decl);

View File

@ -311,20 +311,21 @@ extern void push_nested_namespace (tree);
extern void pop_nested_namespace (tree);
extern void pushlevel_class (void);
extern void poplevel_class (void);
extern tree pushdecl_with_scope (tree, cxx_scope *);
extern tree pushdecl_with_scope (tree, cxx_scope *, bool);
extern tree lookup_name (tree, int);
extern tree lookup_name_real (tree, int, int, bool, int, int);
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 remove_hidden_names (tree);
extern tree lookup_namespace_name (tree, tree);
extern tree lookup_qualified_name (tree, tree, bool, bool);
extern tree lookup_name_nonclass (tree);
extern tree lookup_function_nonclass (tree, tree, bool);
extern void push_local_binding (tree, tree, int);
extern bool pushdecl_class_level (tree);
extern tree pushdecl_namespace_level (tree);
extern tree pushdecl_namespace_level (tree, bool);
extern bool push_class_level_binding (tree, tree);
extern tree getdecls (void);
extern tree cp_namespace_decls (tree);

View File

@ -1114,11 +1114,12 @@ is_specialization_of_friend (tree decl, tree friend)
}
/* Register the specialization SPEC as a specialization of TMPL with
the indicated ARGS. Returns SPEC, or an equivalent prior
declaration, if available. */
the indicated ARGS. IS_FRIEND indicates whether the specialization
is actually just a friend declaration. Returns SPEC, or an
equivalent prior declaration, if available. */
static tree
register_specialization (tree spec, tree tmpl, tree args)
register_specialization (tree spec, tree tmpl, tree args, bool is_friend)
{
tree fn;
@ -1185,14 +1186,14 @@ register_specialization (tree spec, tree tmpl, tree args)
for the specialization, we want this to look as if
there were no definition, and vice versa. */
DECL_INITIAL (fn) = NULL_TREE;
duplicate_decls (spec, fn);
duplicate_decls (spec, fn, is_friend);
return fn;
}
}
else if (DECL_TEMPLATE_SPECIALIZATION (fn))
{
if (!duplicate_decls (spec, fn) && DECL_INITIAL (spec))
if (!duplicate_decls (spec, fn, is_friend) && DECL_INITIAL (spec))
/* Dup decl failed, but this is a new definition. Set the
line number so any errors match this new
definition. */
@ -2108,7 +2109,7 @@ check_explicit_specialization (tree declarator,
/* Register this specialization so that we can find it
again. */
decl = register_specialization (decl, gen_tmpl, targs);
decl = register_specialization (decl, gen_tmpl, targs, is_friend);
}
}
@ -2882,10 +2883,10 @@ template_parm_this_level_p (tree t, void* data)
previously existing one, if appropriate. Returns the DECL, or an
equivalent one, if it is replaced via a call to duplicate_decls.
If IS_FRIEND is nonzero, DECL is a friend declaration. */
If IS_FRIEND is true, DECL is a friend declaration. */
tree
push_template_decl_real (tree decl, int is_friend)
push_template_decl_real (tree decl, bool is_friend)
{
tree tmpl;
tree args;
@ -2906,7 +2907,8 @@ push_template_decl_real (tree decl, int is_friend)
&& TREE_CODE (TREE_TYPE (decl)) != ENUMERAL_TYPE
&& CLASSTYPE_TEMPLATE_SPECIALIZATION (TREE_TYPE (decl)));
is_friend |= (TREE_CODE (decl) == FUNCTION_DECL && DECL_FRIEND_P (decl));
if (TREE_CODE (decl) == FUNCTION_DECL && DECL_FRIEND_P (decl))
is_friend = true;
if (is_friend)
/* For a friend, we want the context of the friend function, not
@ -3081,7 +3083,8 @@ push_template_decl_real (tree decl, int is_friend)
register_specialization (new_tmpl,
most_general_template (tmpl),
args);
args,
is_friend);
return decl;
}
@ -3132,7 +3135,7 @@ push_template_decl_real (tree decl, int is_friend)
if (new_template_p && !ctx
&& !(is_friend && template_class_depth (current_class_type) > 0))
{
tmpl = pushdecl_namespace_level (tmpl);
tmpl = pushdecl_namespace_level (tmpl, is_friend);
if (tmpl == error_mark_node)
return error_mark_node;
@ -3187,7 +3190,7 @@ push_template_decl_real (tree decl, int is_friend)
tree
push_template_decl (tree decl)
{
return push_template_decl_real (decl, 0);
return push_template_decl_real (decl, false);
}
/* Called when a class template TYPE is redeclared with the indicated
@ -5175,7 +5178,7 @@ tsubst_friend_function (tree decl, tree args)
into the namespace of the template. */
ns = decl_namespace_context (new_friend);
push_nested_namespace (ns);
old_decl = pushdecl_namespace_level (new_friend);
old_decl = pushdecl_namespace_level (new_friend, /*is_friend=*/true);
pop_nested_namespace (ns);
if (old_decl != new_friend)
@ -5387,7 +5390,7 @@ tsubst_friend_class (tree friend_tmpl, tree args)
= INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (TREE_TYPE (tmpl)));
/* Inject this template into the global scope. */
friend_type = TREE_TYPE (pushdecl_top_level (tmpl));
friend_type = TREE_TYPE (pushdecl_top_level_maybe_friend (tmpl, true));
}
if (context)
@ -6302,7 +6305,8 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
if (TREE_CODE (decl) != TYPE_DECL)
/* Record this non-type partial instantiation. */
register_specialization (r, t,
DECL_TI_ARGS (DECL_TEMPLATE_RESULT (r)));
DECL_TI_ARGS (DECL_TEMPLATE_RESULT (r)),
false);
}
break;
@ -6477,7 +6481,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
DECL_TEMPLATE_INFO (r)
= tree_cons (gen_tmpl, argvec, NULL_TREE);
SET_DECL_IMPLICIT_INSTANTIATION (r);
register_specialization (r, gen_tmpl, argvec);
register_specialization (r, gen_tmpl, argvec, false);
/* We're not supposed to instantiate default arguments
until they are called, for a template. But, for a
@ -6706,7 +6710,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
processing here. */
DECL_EXTERNAL (r) = 1;
register_specialization (r, gen_tmpl, argvec);
register_specialization (r, gen_tmpl, argvec, false);
DECL_TEMPLATE_INFO (r) = tree_cons (tmpl, argvec, NULL_TREE);
SET_DECL_IMPLICIT_INSTANTIATION (r);
}

View File

@ -1822,7 +1822,7 @@ finish_call_expr (tree fn, tree args, bool disallow_virtual, bool koenig_p)
if (!result)
/* A call to a namespace-scope function. */
result = build_new_function_call (fn, args);
result = build_new_function_call (fn, args, koenig_p);
}
else if (TREE_CODE (fn) == PSEUDO_DTOR_EXPR)
{

View File

@ -171,7 +171,7 @@ in the following sections.
@item C++ Language Options
@xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
@gccoptlist{-fabi-version=@var{n} -fno-access-control -fcheck-new @gol
-fconserve-space -fno-const-strings @gol
-fconserve-space -ffriend-injection -fno-const-strings @gol
-fno-elide-constructors @gol
-fno-enforce-eh-specs @gol
-ffor-scope -fno-for-scope -fno-gnu-keywords @gol
@ -1425,6 +1425,20 @@ two definitions were merged.
This option is no longer useful on most targets, now that support has
been added for putting variables into BSS without making them common.
@item -ffriend-injection
@opindex ffriend-injection
Inject friend functions into the enclosing namespace, so that they are
visible outside the scope of the class in which they are declared.
Friend functions were documented to work this way in the old Annotated
C++ Reference Manual, and versions of G++ before 4.1 always worked
that way. However, in ISO C++ a friend function which is not declared
in an enclosing scope can only be found using argument dependent
lookup. This option causes friends to be injected as they were in
earlier releases.
This option is for compatibility, and may be removed in a future
release of G++.
@item -fno-const-strings
@opindex fno-const-strings
Give string constants type @code{char *} instead of type @code{const

View File

@ -1,3 +1,18 @@
2005-09-12 Ian Lance Taylor <ian@airs.com>
PR g++/7874
* g++.dg/lookup/friend7.C: New test.
* g++.dg/lookup/friend8.C: New test.
* g++.dg/parse/defarg4.C: Add a parameter to the friend function,
so that it will be found via argument dependent lookup.
* g++.old-deja/g++.brendan/crash56.C: Don't expect errors for
friend functions which will no longer be found.
* g++.old-deja/g++.jason/friend.C: Add a parameter to the friend
function g, so that it will be found via argument dependent
lookup.
* g++.old-deja/g++.jason/scoping15.C: Use -ffriend-injection.
* g++.old-deja/g++.mike/net43.C: Likewise.
2005-09-12 Mark Mitchell <mark@codesourcery.com>
PR c++/23691

View File

@ -0,0 +1,19 @@
// { dg-do compile }
// PR c++/7874: Don't inject friend functions into global name space.
namespace N { template<typename T> struct A { friend void f(A) { }; }; }
int main()
{
N::A<int> a;
N::f(a); // { dg-error "not a member" }
}
struct S { friend void g(); friend void h(S); };
struct T { friend void g(); friend void h(T); };
void i() {
g(); // { dg-error "not declared" }
S s;
h(s);
T t;
h(t);
}

View File

@ -0,0 +1,12 @@
// Test that we look up a friend declared at top level ahead of an
// undeclared friend found by argument dependent lookup.
// { dg-do run }
int f(int) { return 0; }
struct S {
friend int f(char) { return 1; }
};
int main () { return f('a'); }

View File

@ -6,9 +6,10 @@
// PR c++ 9162. default args got left unprocessed
struct S {
friend int foo (int = 100);
friend int foo (const S&, int = 100);
};
int i = foo ();
S s;
int i = foo (s);
struct R
{

View File

@ -20,15 +20,15 @@ public:
class Vix {
public:
Vix();
friend int operator==(void *v, const Vix& x) // { dg-error "operator==" }
friend int operator==(void *v, const Vix& x)
{ return v == x.item; }
friend int operator==(const Vix& x, void *v) // { dg-error "operator==" }
friend int operator==(const Vix& x, void *v)
{ return v == x.item; }
friend int operator!=(void *v, const Vix& x)
{ return v != x.item; }
friend int operator!=(const Vix& x, void *v)
{ return v != x.item; }
friend int operator==(const Vix& x1, const Vix& x2) // { dg-error "operator==" }
friend int operator==(const Vix& x1, const Vix& x2)
{ return x1.owner == x2.owner && x1.item == x2.item; }
friend int operator!=(const Vix& x1, const Vix& x2)
{ return x1.owner != x2.owner || x1.item != x2.item; }

View File

@ -10,10 +10,11 @@ struct A {
struct B {
static void f () { exit (0); }
friend void g () { f (); }
friend void g (B) { f (); }
};
int main ()
{
g ();
B b;
g (b);
}

View File

@ -1,4 +1,5 @@
// { dg-do assemble }
// { dg-options "-ffriend-injection" }
// Bug: g++ ignores the :: qualification and dies trying to treat an integer
// variable as a list of functions.

View File

@ -1,4 +1,5 @@
// { dg-do assemble }
// { dg-options "-ffriend-injection" }
class foo {
public: