diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 36dedc7c50ca..2e26c7dc152a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2005-09-12 Ian Lance Taylor + + 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 PR middle-end/23237 diff --git a/gcc/c.opt b/gcc/c.opt index 908efd61f9c1..d650f111168e 100644 --- a/gcc/c.opt +++ b/gcc/c.opt @@ -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++ diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 4ea174c8fe77..b0f70bc07dde 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,54 @@ +2005-09-12 Ian Lance Taylor + + 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 * decl2.c (build_anon_union_vars): Copy attributes from the base addr. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index fab01fc95f76..e8f93c4daf80 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -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); diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 98a88004d40f..7fdeb873ce1c 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -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; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index cf241290ca44..9e74d37dcac4 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index f33276144bfb..99072920f565 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -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) diff --git a/gcc/cp/friend.c b/gcc/cp/friend.c index 4e1a20982217..c71b90f83ef5 100644 --- a/gcc/cp/friend.c +++ b/gcc/cp/friend.c @@ -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); } diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index cba939335880..9afde87016fa 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -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); diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 2a0c4c4d2589..fa2a760bd94e 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -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); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 85e8cb83b868..3f749554e675 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -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); } diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 0e926bf1d269..c7392b034ce3 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -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) { diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 7dfa3a4693d6..55cf558d3abc 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -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 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index f8ba4db4392e..cdea80a8ccd3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,18 @@ +2005-09-12 Ian Lance Taylor + + 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 PR c++/23691 diff --git a/gcc/testsuite/g++.dg/lookup/friend7.C b/gcc/testsuite/g++.dg/lookup/friend7.C new file mode 100644 index 000000000000..dd19c72f9a65 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/friend7.C @@ -0,0 +1,19 @@ +// { dg-do compile } +// PR c++/7874: Don't inject friend functions into global name space. + +namespace N { template struct A { friend void f(A) { }; }; } +int main() +{ + N::A 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); +} diff --git a/gcc/testsuite/g++.dg/lookup/friend8.C b/gcc/testsuite/g++.dg/lookup/friend8.C new file mode 100644 index 000000000000..e9992cdadf41 --- /dev/null +++ b/gcc/testsuite/g++.dg/lookup/friend8.C @@ -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'); } diff --git a/gcc/testsuite/g++.dg/parse/defarg4.C b/gcc/testsuite/g++.dg/parse/defarg4.C index 39d0a89904ff..bafdadb9429f 100644 --- a/gcc/testsuite/g++.dg/parse/defarg4.C +++ b/gcc/testsuite/g++.dg/parse/defarg4.C @@ -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 { diff --git a/gcc/testsuite/g++.old-deja/g++.brendan/crash56.C b/gcc/testsuite/g++.old-deja/g++.brendan/crash56.C index 57c7c11480bd..d1cae6d198a1 100644 --- a/gcc/testsuite/g++.old-deja/g++.brendan/crash56.C +++ b/gcc/testsuite/g++.old-deja/g++.brendan/crash56.C @@ -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; } diff --git a/gcc/testsuite/g++.old-deja/g++.jason/friend.C b/gcc/testsuite/g++.old-deja/g++.jason/friend.C index e2cf7fe7cc41..9bcc81488b59 100644 --- a/gcc/testsuite/g++.old-deja/g++.jason/friend.C +++ b/gcc/testsuite/g++.old-deja/g++.jason/friend.C @@ -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); } diff --git a/gcc/testsuite/g++.old-deja/g++.jason/scoping15.C b/gcc/testsuite/g++.old-deja/g++.jason/scoping15.C index 5485b29b32a3..cc34c5f5785e 100644 --- a/gcc/testsuite/g++.old-deja/g++.jason/scoping15.C +++ b/gcc/testsuite/g++.old-deja/g++.jason/scoping15.C @@ -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. diff --git a/gcc/testsuite/g++.old-deja/g++.mike/net43.C b/gcc/testsuite/g++.old-deja/g++.mike/net43.C index 234295fed67b..aadd03de92f3 100644 --- a/gcc/testsuite/g++.old-deja/g++.mike/net43.C +++ b/gcc/testsuite/g++.old-deja/g++.mike/net43.C @@ -1,4 +1,5 @@ // { dg-do assemble } +// { dg-options "-ffriend-injection" } class foo { public: