This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PR59319] output friends in debug info


This is not a finished patch.  There are two issues I'd like feedback
on before a final submission.  See them below.  First, a general
description.

Handling non-template friends is kind of easy, but it required a bit
of infrastructure in dwarf2out to avoid (i) forcing debug info for
unused types or functions: DW_TAG_friend DIEs are only emitted if
their DW_AT_friend DIE is emitted, and (ii) creating DIEs for such
types or functions just to have them discarded at the end.  To this
end, I introduced a list (vec, actually) of types with friends,
processed at the end of the translation unit, and a list of
DW_TAG_friend DIEs that, when we're pruning unused types, reference
DIEs that are still not known to be used, revisited after we finish
deciding all other DIEs, so that we prune DIEs that would have
referenced pruned types or functions.

Handlig template friends turned out to be trickier: there's no
representation in DWARF for templates.  I decided to give debuggers as
much information as possible, enumerating all specializations of
friend templates and outputting DW_TAG_friend DIEs referencing them as
well, but marking them as DW_AT_artificial to indicate they're not
explicitly stated in the source code.  This attribute is not valid for
DW_TAG_friend, so it's only emitted in non-strict mode.  The greatest
challenge was to enumerate all specializations of a template.  It
looked trivial at first, given DECL_TEMPLATE_INSTANTIATIONS, but in
some of the testcases, cases it wouldn't list any specializations, and
in others it would list only some of them.  I couldn't figure out the
logic behind that, and it seemed clear from the documentation of this
macro that at least in some cases it wouldn't hold the list, so I
ended up writing code to look for specializations in the hashtables of
decl or type specializations.  That worked fine, but it's not exactly
an efficient way to obtain the desired information, at least in some
cases.



- should we output specializations of friend templates as friends even
  in strict mode?  Currently we output them with DW_AT_artificial in
  non-strict mode, and without the artificial mark in strict mode.

- is there any way we can use DECL_TEMPLATE_INSTANTIATIONS reliably to
  enumerate the specializations of a friend template, or at least tell
  when it can be used?

- I haven't used local_specializations, should I?  I was a bit
  confused about the apparently unused local_specialization_stack,
  too.

I haven't covered partial and explicit specializations in the
testcases yet.


for gcc/ChangeLog

	PR debug/59319
	* dwarf2out.c (class_types_with_friends): New.
	(gen_friend_tags_for_type, gen_friend_tags): New.
	(gen_member_die): Record class types with friends.
	(deferred_marks): New.
	(prune_unused_types_defer_undecided_mark_p): New.
	(prune_unused_types_defer_mark): New.
	(prune_unused_types_deferred_walk): New.
	(prune_unused_types_walk): Defer DW_TAG_friend.
	(prune_unused_types): Check deferred marks is empty on entry,
	empty it after processing.
	(dwarf2out_finish): Generate friend tags.
	* langhooks-def.h (LANG_HOOKS_GET_FRIENDS): New.
	(LANG_HOOKS_FOR_TYPES_INITIALIZER): Add it.
	* langhooks.h (lang_hooks_for_types): Add get_friends.

for gcc/cp/ChangeLog

	PR debug/59319
	* cp-objcp-common.c (cp_get_friends): New.
	* cp-objcp-common.h (cp_get_friends): Declare.
	(LANG_HOOKS_GET_FRIENDS): Override.
	* cp-tree.h (enumerate_friend_specializations): Declare.
	* pt.c (enumerate_friend_specializations): New.

for gcc/testsuite/ChangeLog

	PR debug/59319
	* g++.dg/debug/dwarf2/friend-1.C: New.
	* g++.dg/debug/dwarf2/friend-2.C: New.
	* g++.dg/debug/dwarf2/friend-3.C: New.
	* g++.dg/debug/dwarf2/friend-4.C: New.
	* g++.dg/debug/dwarf2/friend-5.C: New.
	* g++.dg/debug/dwarf2/friend-6.C: New.
	* g++.dg/debug/dwarf2/friend-7.C: New.
	* g++.dg/debug/dwarf2/friend-8.C: New.
	* g++.dg/debug/dwarf2/friend-9.C: New.
	* g++.dg/debug/dwarf2/friend-10.C: New.
	* g++.dg/debug/dwarf2/friend-11.C: New.
	* g++.dg/debug/dwarf2/friend-12.C: New.
	* g++.dg/debug/dwarf2/friend-13.C: New.
---
 gcc/cp/cp-objcp-common.c                      |  103 ++++++++++++++++
 gcc/cp/cp-objcp-common.h                      |    3 
 gcc/cp/cp-tree.h                              |    1 
 gcc/cp/pt.c                                   |   73 +++++++++++
 gcc/dwarf2out.c                               |  161 +++++++++++++++++++++++++
 gcc/langhooks-def.h                           |    4 -
 gcc/langhooks.h                               |   19 +++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C  |   10 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C |   13 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C |   13 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C |   15 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C  |   11 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C  |    9 +
 gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C  |   12 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C  |   10 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C  |   11 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C  |   11 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C  |   13 ++
 gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C  |   13 ++
 19 files changed, 503 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C
 create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C

diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c
index e9f9a63..d35632c 100644
--- a/gcc/cp/cp-objcp-common.c
+++ b/gcc/cp/cp-objcp-common.c
@@ -167,6 +167,109 @@ cp_get_ptrmemfn_type (const_tree type, int selector)
     }
 }
 
+/* At DETAIL level 0, returns non-NULL if the named class TYPE has any
+   friends, NULL otherwise.  At higher detail levels, return a tree
+   list with the friends of the named class type.  Each TREE_VALUE
+   contains one friend type or function decl.  For non-template
+   friends, TREE_PURPOSE is NULL.  For template friend declarations,
+   the returned entries depend on the DETAIL level.  At level 1, and
+   only at level 1, an entry with NULL TREE_VALUE and non-NULL
+   TREE_PURPOSE will START the returned list to indicate the named
+   class TYPE has at least one template friend.  At level 2, each
+   template friend will be in an entry with NULL TREE_VALUE, and with
+   the TEMPLATE_DECL in TREE_PURPOSE.  At level 3, instead of a NULL
+   TREE_VALUE, we add one entry for each instantiation or
+   specialization of the template that fits the template friend
+   declaration, as long as there is at least one instantiation or
+   specialization; if there isn't any, an entry with NULL TREE_VALUE
+   is created.  A negative detail level will omit non-template friends
+   from the returned list.  */
+
+tree
+cp_get_friends (const_tree type, int detail)
+{
+  tree list = NULL_TREE;
+  tree typedecl = TYPE_MAIN_DECL (type);
+  bool has_templates = false;
+  bool non_templates = true;
+
+  if (detail == 0)
+    {
+      if (DECL_FRIENDLIST (typedecl)
+	  || CLASSTYPE_FRIEND_CLASSES (TREE_TYPE (typedecl)))
+	return integer_one_node;
+      else
+	return NULL_TREE;
+    }
+  else if (detail < 0)
+    {
+      detail = -detail;
+      non_templates = false;
+    }
+
+  gcc_assert (detail <= 3);
+
+  for (tree fnlist = DECL_FRIENDLIST (typedecl); fnlist;
+       fnlist = TREE_CHAIN (fnlist))
+    for (tree fns = FRIEND_DECLS (fnlist); fns; fns = TREE_CHAIN (fns))
+      {
+	tree fn = TREE_VALUE (fns);
+	if (TREE_CODE (fn) == FUNCTION_DECL
+	    && (!DECL_TEMPLATE_INFO (fn) || DECL_USE_TEMPLATE (fn)))
+	  {
+	    if (non_templates)
+	      list = tree_cons (NULL_TREE, fn, list);
+	    continue;
+	  }
+
+	has_templates = true;
+
+	if (detail == 2)
+	  list = tree_cons (fn, NULL_TREE, list);
+
+	if (detail <= 2)
+	  continue;
+
+	tree new_list = enumerate_friend_specializations (fn);
+	if (new_list)
+	  list = chainon (new_list, list);
+	else
+	  list = tree_cons (fn, NULL_TREE, list);
+      }
+
+  for (tree cllist = CLASSTYPE_FRIEND_CLASSES (TREE_TYPE (typedecl));
+       cllist; cllist = TREE_CHAIN (cllist))
+    {
+      tree cl = TREE_VALUE (cllist);
+
+      if (TREE_CODE (cl) == RECORD_TYPE)
+	{
+	  if (non_templates)
+	    list = tree_cons (NULL_TREE, cl, list);
+	  continue;
+	}
+
+      has_templates = true;
+
+      if (detail == 2)
+	list = tree_cons (cl, NULL_TREE, list);
+
+      if (detail <= 2)
+	continue;
+
+      tree new_list = enumerate_friend_specializations (cl);
+      if (new_list)
+	list = chainon (new_list, list);
+      else
+	list = tree_cons (cl, NULL_TREE, list);
+    }
+
+  if (has_templates && detail == 1)
+    list = tree_cons (integer_one_node, NULL_TREE, list);
+
+  return list;
+}
+
 /* Return true if DECL is explicit member function.  */
 
 bool
diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h
index 00780c7..d85d357 100644
--- a/gcc/cp/cp-objcp-common.h
+++ b/gcc/cp/cp-objcp-common.h
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 
 extern int cp_get_ref_qualifier (const_tree);
 extern tree cp_get_ptrmemfn_type (const_tree, int);
+extern tree cp_get_friends (const_tree, int);
 
 extern tree objcp_tsubst_copy_and_build (tree, tree, tsubst_flags_t,
 					 tree, bool);
@@ -134,6 +135,8 @@ extern void cp_common_init_ts (void);
 #define LANG_HOOKS_GET_REF_QUALIFIER cp_get_ref_qualifier
 #undef LANG_HOOKS_GET_PTRMEMFN_TYPE
 #define LANG_HOOKS_GET_PTRMEMFN_TYPE cp_get_ptrmemfn_type
+#undef LANG_HOOKS_GET_FRIENDS
+#define LANG_HOOKS_GET_FRIENDS cp_get_friends
 #undef LANG_HOOKS_TO_TARGET_CHARSET
 #define LANG_HOOKS_TO_TARGET_CHARSET c_common_to_target_charset
 #undef LANG_HOOKS_GIMPLIFY_EXPR
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 8a32f17..66106b5 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6114,6 +6114,7 @@ extern vec<qualified_typedef_usage_t, va_gc> *get_types_needing_access_check (tr
 extern int template_class_depth			(tree);
 extern int is_specialization_of			(tree, tree);
 extern bool is_specialization_of_friend		(tree, tree);
+extern tree enumerate_friend_specializations	(tree);
 extern tree get_pattern_parm			(tree, tree);
 extern int comp_template_args			(tree, tree, tree * = NULL,
 						 tree * = NULL);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 1ee5fd4..f0bd40b 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1469,6 +1469,79 @@ is_specialization_of_friend (tree decl, tree friend_decl)
   return false;
 }
 
+/* Return a list of instantiations/specializations that match
+   FRIEND_DECL.  */
+
+tree
+enumerate_friend_specializations (tree friend_decl)
+{
+  if (TREE_CODE (friend_decl) != TEMPLATE_DECL)
+    friend_decl = DECL_TI_TEMPLATE (friend_decl);
+
+  tree opt_decl = friend_decl;
+  while (optimize_specialization_lookup_p (opt_decl))
+    opt_decl = CLASSTYPE_TI_TEMPLATE (DECL_CONTEXT (opt_decl));
+
+  gcc_assert (TREE_CODE (opt_decl) == TEMPLATE_DECL);
+
+  /* FIXME: This would be much preferred, but it doesn't always list
+     all specializations.  */
+  if (0 && DECL_TEMPLATE_INSTANTIATIONS (opt_decl))
+    {
+      tree list = NULL_TREE;
+      for (tree speclist = DECL_TEMPLATE_INSTANTIATIONS (opt_decl);
+	   speclist; speclist = TREE_CHAIN (speclist))
+	{
+	  tree spec = TREE_VALUE (speclist);
+	  if (opt_decl != friend_decl)
+	    spec = retrieve_specialization
+	      (friend_decl, CLASSTYPE_TI_ARGS (spec), 0);
+	  if (TREE_CODE (spec) == TYPE_DECL)
+	    spec = TREE_TYPE (spec);
+	  list = tree_cons (friend_decl, spec, list);
+	}
+      return list;
+    }
+
+  typedef hash_table<spec_hasher> specs_t;
+  specs_t *specializations;
+  tree_code code;
+
+  if (DECL_CLASS_TEMPLATE_P (opt_decl))
+    {
+      specializations = type_specializations;
+      code = RECORD_TYPE;
+    }
+  else
+    {
+      specializations = decl_specializations;
+      code = FUNCTION_DECL;
+    }
+
+  tree list = NULL_TREE;
+
+  for (specs_t::iterator iter = specializations->begin(),
+	 end = specializations->end();
+       iter != end; ++iter)
+    {
+      tree spec = (*iter)->spec;
+      if (TREE_CODE (spec) != code)
+	continue;
+      if (TREE_CODE (spec) == RECORD_TYPE)
+	spec = TYPE_NAME (spec);
+      if (is_specialization_of_friend (spec, opt_decl))
+	{
+	  if (opt_decl != friend_decl)
+	    spec = retrieve_specialization (friend_decl, (*iter)->args, 0);
+	  if (TREE_CODE (spec) == TYPE_DECL)
+	    spec = TREE_TYPE (spec);
+	  list = tree_cons (friend_decl, spec, list);
+	}
+    }
+
+  return list;
+}
+
 /* Register the specialization SPEC as a specialization of TMPL with
    the indicated ARGS.  IS_FRIEND indicates whether the specialization
    is actually just a friend declaration.  Returns SPEC, or an
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index f40f759..43aa5a0 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -22474,6 +22474,57 @@ gen_variant_part (tree variant_part_decl, struct vlr_context *vlr_ctx,
   free (discr_lists);
 }
 
+/* Types that have friends have to be revisited, because we want to
+   emit friend attributes for them once we know what types and decls
+   have DIEs, and we want to emit friend tags for specializations of
+   template friends.  We could create DIEs in limbo ourselves, but we
+   can't know the specializations before we've seen the entire
+   translation unit.  */
+
+static vec<tree> class_types_with_friends;
+
+/* Add any friend tags corresponding to the named TYPE.  */
+
+static void
+gen_friend_tags_for_type (tree type)
+{
+  dw_die_ref context_die = lookup_type_die (type);
+  gcc_assert (context_die);
+
+  for (tree friends = lang_hooks.types.get_friends (type, 3); friends;
+       friends = TREE_CHAIN (friends))
+    {
+      tree t = TREE_VALUE (friends);
+      dw_die_ref die = NULL;
+      if (!t)
+	/* If it's a friend template without any specializations, we
+	   can't refer to it in debug information.  */
+	continue;
+      else if (TYPE_P (t))
+	die = lookup_type_die (t);
+      else if (DECL_P (t))
+	die = lookup_decl_die (t);
+      else
+	gcc_unreachable ();
+      if (!die)
+	continue;
+      dw_die_ref child = new_die (DW_TAG_friend, context_die, type);
+      add_AT_die_ref (child, DW_AT_friend, die);
+      if (!dwarf_strict && TREE_PURPOSE (friends))
+	add_AT_flag (child, DW_AT_artificial, 1);
+    }
+}
+
+/* Add any friend tags corresponding to class TYPEs that were found to
+   have friend declarations.  */
+
+static void
+gen_friend_tags ()
+{
+  while (!class_types_with_friends.is_empty ())
+    gen_friend_tags_for_type (class_types_with_friends.pop ());
+}
+
 /* Generate a DIE for a class member.  */
 
 static void
@@ -22559,6 +22610,9 @@ gen_member_die (tree type, dw_die_ref context_die)
 	else
 	  gen_decl_die (member, NULL, NULL, context_die);
       }
+
+  if (lang_hooks.types.get_friends (type, 0))
+    class_types_with_friends.safe_push (type);
 }
 
 /* Generate a DIE for a structure or union type.  If TYPE_DECL_SUPPRESS_DEBUG
@@ -26031,6 +26085,97 @@ prune_unused_types_walk_local_classes (dw_die_ref die)
   FOR_EACH_CHILD (die, c, prune_unused_types_walk_local_classes (c));
 }
 
+/* Nodes to revisit after marking everything else, to decide whether
+   or not they can/should be emitted.  */
+
+static vec<dw_die_ref> deferred_marks;
+
+/* Return true if the mark is already decided, false otherwise.  */
+
+static bool
+prune_unused_types_defer_undecided_mark_p (dw_die_ref die)
+{
+  gcc_assert (!die->die_mark);
+
+  dw_attr_node *a;
+  unsigned ix;
+  bool can_mark_now = true;
+
+  FOR_EACH_VEC_SAFE_ELT (die->die_attr, ix, a)
+    switch (AT_class (a))
+      {
+      case dw_val_class_loc:
+      case dw_val_class_loc_list:
+	/* We don't support attributes of this type now.  Deferred
+	   walking of the location expressions might mark DIEs that
+	   we've already decided not to output, and we may have
+	   already based other decisions on it.  This is not
+	   insurmountable, but we don't need to tackle that right
+	   away.  */
+	gcc_unreachable ();
+
+      case dw_val_class_die_ref:
+	if (!a->dw_attr_val.v.val_die_ref.die->die_mark)
+	  can_mark_now = false;
+	break;
+
+      case dw_val_class_str:
+      default:
+	break;
+      }
+
+  return !can_mark_now;
+}
+
+/* Return true if we've deferred the decision on whether to mark DIE.
+   It must not have children or attributes with location expressions
+   or lists.  Attributes with strings and other DIEs are ok.  If any
+   of the DIEs referenced by attributes is not marked, we defer the
+   decision to give it a chance to be marked so that we output the
+   present DIE too.  In this case, we return TRUE, to indicate the
+   decision was deferred.  If they are all marked already, then we
+   know we can output this one as well, so we return FALSE to indicate
+   it was NOT deferred.  */
+
+static bool
+prune_unused_types_defer_mark (dw_die_ref die)
+{
+  gcc_assert (die->die_parent->die_mark);
+
+  /* We use this for friend DIEs only, and they have no children, so
+     don't make things more complicated than needed.  */
+  gcc_assert (!die->die_child);
+
+  if (die->die_mark || !prune_unused_types_defer_undecided_mark_p (die))
+    return false;
+
+  deferred_marks.safe_push (die);
+
+  return true;
+}
+
+/* This function revisits a deferred DIE, and marks it iff each DIE
+   its attributes reference is also marked.  */
+
+static void
+prune_unused_types_deferred_walk (dw_die_ref die)
+{
+  /* If we're marked, we're done.  Otherwise, if referenced DIEs
+     remain unmarked, then we don't mark this one either.  */
+  if (die->die_mark
+      || prune_unused_types_defer_undecided_mark_p (die))
+    return;
+
+  gcc_assert (!die->die_mark);
+  die->die_mark = 1;
+  /* This should do no more than resetting the refcount of
+     strings.  */
+  prune_unused_types_walk_attribs (die);
+  die->die_mark = 2;
+
+  /* We don't mark children because we know we have none.  */
+}
+
 /* Walk the tree DIE and mark types that we actually use.  */
 
 static void
@@ -26066,6 +26211,13 @@ prune_unused_types_walk (dw_die_ref die)
       /* It's a type node --- don't mark it.  */
       return;
 
+    case DW_TAG_friend:
+      if (die->die_perennial_p
+	  || !prune_unused_types_defer_mark (die))
+	break;
+
+      return;
+
     case DW_TAG_const_type:
     case DW_TAG_packed_type:
     case DW_TAG_pointer_type:
@@ -26075,7 +26227,6 @@ prune_unused_types_walk (dw_die_ref die)
     case DW_TAG_typedef:
     case DW_TAG_array_type:
     case DW_TAG_interface_type:
-    case DW_TAG_friend:
     case DW_TAG_enumeration_type:
     case DW_TAG_subroutine_type:
     case DW_TAG_string_type:
@@ -26191,6 +26342,8 @@ prune_unused_types (void)
   pubname_entry *pub;
   dw_die_ref base_type;
 
+  gcc_assert (deferred_marks.is_empty ());
+
 #if ENABLE_ASSERT_CHECKING
   /* All the marks should already be clear.  */
   verify_marks_clear (comp_unit_die ());
@@ -26223,6 +26376,10 @@ prune_unused_types (void)
   for (i = 0; base_types.iterate (i, &base_type); i++)
     prune_unused_types_mark (base_type, 1);
 
+  while (!deferred_marks.is_empty ())
+    prune_unused_types_deferred_walk (deferred_marks.pop ());
+  deferred_marks.release ();
+
   if (debug_str_hash)
     debug_str_hash->empty ();
   if (skeleton_debug_str_hash)
@@ -27569,6 +27726,8 @@ dwarf2out_finish (const char *filename)
 
   gen_remaining_tmpl_value_param_die_attribute ();
 
+  gen_friend_tags ();
+
   /* Add the name for the main input file now.  We delayed this from
      dwarf2out_init to avoid complications with PCH.
      For LTO produced units use a fixed artificial name to avoid
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index 5961c42..b11384f 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -182,6 +182,7 @@ extern tree lhd_make_node (enum tree_code);
 #define LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO NULL
 #define LANG_HOOKS_GET_REF_QUALIFIER	hook_int_const_tree_0
 #define LANG_HOOKS_GET_PTRMEMFN_TYPE    hook_tree_const_tree_int_null
+#define LANG_HOOKS_GET_FRIENDS		hook_tree_const_tree_int_null
 
 #define LANG_HOOKS_FOR_TYPES_INITIALIZER { \
   LANG_HOOKS_MAKE_TYPE, \
@@ -206,7 +207,8 @@ extern tree lhd_make_node (enum tree_code);
   LANG_HOOKS_GET_DEBUG_TYPE, \
   LANG_HOOKS_GET_FIXED_POINT_TYPE_INFO, \
   LANG_HOOKS_GET_REF_QUALIFIER, \
-  LANG_HOOKS_GET_PTRMEMFN_TYPE \
+  LANG_HOOKS_GET_PTRMEMFN_TYPE, \
+  LANG_HOOKS_GET_FRIENDS \
 }
 
 /* Declaration hooks.  */
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index cf8b550..73b7bb8 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -170,6 +170,25 @@ struct lang_hooks_for_types
      Otherwise, return the class type when the selector is 0, or the
      member function type when the selector is 1.  */
   tree (*get_ptrmemfn_type) (const_tree, int);
+
+  /* At DETAIL level 0, returns non-NULL if the named class TYPE has
+     any friends, NULL otherwise.  At higher detail levels, return a
+     tree list with the friends of the named class type.  Each
+     TREE_VALUE contains one friend type or function decl.  For
+     non-template friends, TREE_PURPOSE is NULL.  For template friend
+     declarations, the returned entries depend on the DETAIL level.
+     At level 1, and only at level 1, an entry with NULL TREE_VALUE
+     and non-NULL TREE_PURPOSE will START the returned list to
+     indicate the named class TYPE has at least one template friend.
+     At level 2, each template friend will be in an entry with NULL
+     TREE_VALUE, and with the TEMPLATE_DECL in TREE_PURPOSE.  At level
+     3, instead of a NULL TREE_VALUE, we add one entry for each
+     instantiation or specialization of the template that fits the
+     template friend declaration, as long as there is at least one
+     instantiation or specialization; if there isn't any, an entry
+     with NULL TREE_VALUE is created.  A negative detail level will
+     omit non-template friends from the returned list.  */
+  tree (*get_friends) (const_tree, int);
 };
 
 /* Language hooks related to decls and the symbol table.  */
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C
new file mode 100644
index 0000000..df06f6f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-1.C
@@ -0,0 +1,10 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+class foo {};
+class bar {
+  friend class foo;
+};
+bar t;
+foo l;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C
new file mode 100644
index 0000000..dcff721
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-10.C
@@ -0,0 +1,13 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename> struct foo {
+  template <typename> void f () {}
+};
+class bar {
+  template <typename T> template <typename> friend void foo<T>::f ();
+};
+bar t;
+template void foo<int>::f<bar> ();
+template void foo<bar>::f<int> ();
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C
new file mode 100644
index 0000000..24382c8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-11.C
@@ -0,0 +1,13 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename> struct foo {
+  struct f {};
+};
+class bar {
+  template <typename T> friend struct foo<T>::f;
+};
+bar t;
+foo<int>::f i;
+foo<bar>::f b;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C
new file mode 100644
index 0000000..0c172a7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-12.C
@@ -0,0 +1,15 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 5 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename> struct foo {
+  template <typename> struct f {};
+};
+class bar {
+  template <typename T> template <typename> friend struct foo<T>::f;
+};
+bar t;
+foo<int>::f<int> i;
+foo<bar>::f<bar> b;
+foo<int>::f<bar> ib;
+foo<bar>::f<int> bi;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C
new file mode 100644
index 0000000..b4cd04d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-2.C
@@ -0,0 +1,11 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-not " DW_AT_friend" { xfail { powerpc-ibm-aix* } } } }
+// class foo is unused, so we do NOT output the friend tag.
+
+class foo {};
+class bar {
+  friend class foo;
+};
+bar t;
+// foo l; 
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C
new file mode 100644
index 0000000..05cdde1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-3.C
@@ -0,0 +1,9 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+int f() {}
+class bar {
+  friend int f();
+};
+bar t;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C
new file mode 100644
index 0000000..4a67516
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-4.C
@@ -0,0 +1,12 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+struct foo {
+  int f();
+};
+class bar {
+  friend int foo::f();
+};
+int foo::f() {}
+bar t;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C
new file mode 100644
index 0000000..3ccfd6d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-5.C
@@ -0,0 +1,10 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 2 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename> class foo {};
+class bar {
+  friend class foo<int>;
+};
+bar t;
+foo<int> l;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C
new file mode 100644
index 0000000..34dd458
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-6.C
@@ -0,0 +1,11 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename> class foo {};
+class bar {
+  template <typename> friend class foo;
+};
+bar t;
+foo<int> l;
+foo<bar> b;
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C
new file mode 100644
index 0000000..3a6554c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-7.C
@@ -0,0 +1,11 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename> void f () {}
+class bar {
+  template <typename> friend void f ();
+};
+bar t;
+template void f<int> ();
+template void f<bar> ();
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C
new file mode 100644
index 0000000..4ede43c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-8.C
@@ -0,0 +1,13 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } }
+
+struct foo {
+  template <typename> void f () {}
+};
+class bar {
+  template <typename> friend void foo::f ();
+};
+bar t;
+template void foo::f<int> ();
+template void foo::f<bar> ();
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C b/gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C
new file mode 100644
index 0000000..fe6b0ac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/friend-9.C
@@ -0,0 +1,13 @@
+// { dg-do compile }
+// { dg-options "-O -g -dA" }
+// { dg-final { scan-assembler-times " DW_AT_friend" 3 { xfail { powerpc-ibm-aix* } } } }
+
+template <typename> struct foo {
+  void f () {}
+};
+class bar {
+  template <typename T> friend void foo<T>::f ();
+};
+bar t;
+template void foo<int>::f ();
+template void foo<bar>::f ();

-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]