+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
C++ ObjC++
Emit implicit instantiations of templates
+ffriend-injection
+C++ Var(flag_friend_injection)
+Inject friend functions into enclosing namespace
+
flabels-ok
C++ ObjC++
+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.
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. */
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;
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);
one, or vice versa. */
continue;
- /* Ignore anticipated decls of undeclared builtins. */
+ /* Ignore functions which haven't been explicitly
+ declared. */
if (DECL_ANTICIPATED (fn))
continue;
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
#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. */
#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)
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);
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 *);
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);
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);
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)
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;
}
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
= 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;
}
}
/* 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);
/* 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);
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
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. */
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
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;
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;
}
}
> 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;
}
/* 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)
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
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
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);
}
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,
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))
{
&& 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
/* 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;
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)
}
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. */
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 ())
}
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);
}
}
+ 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 ())
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. */
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;
{
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;
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;
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);
}
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)
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. */
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
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);
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. */
*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 ();
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
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
}
/* 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)
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
struct arg_lookup
{
tree name;
+ tree args;
tree namespaces;
tree classes;
tree functions;
}
}
+/* 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. */
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;
}
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;
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);
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);
}
/* 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;
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. */
/* 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);
}
}
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;
&& 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
register_specialization (new_tmpl,
most_general_template (tmpl),
- args);
+ args,
+ is_friend);
return decl;
}
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;
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
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)
= 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)
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;
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
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);
}
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)
{
@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
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
+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
--- /dev/null
+// { 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);
+}
--- /dev/null
+// 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'); }
// 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
{
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; }
struct B {
static void f () { exit (0); }
- friend void g () { f (); }
+ friend void g (B) { f (); }
};
int main ()
{
- g ();
+ B b;
+ g (b);
}
// { 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.
// { dg-do assemble }
+// { dg-options "-ffriend-injection" }
class foo {
public: