Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Keith Seitz <keiths@redhat.com>
To: Jan Kratochvil <jan.kratochvil@redhat.com>
Cc: gdb-patches@sourceware.org
Subject: Re: [RFA] c++/12266 (again) [cp_canonicalize_no_typedefs-4.patch]
Date: Tue, 09 Aug 2011 20:48:00 -0000	[thread overview]
Message-ID: <4E419D17.9020404@redhat.com> (raw)
In-Reply-To: <20110802203654.GA8670@host1.jankratochvil.net>

[-- Attachment #1: Type: text/plain, Size: 4261 bytes --]

On 08/02/2011 01:36 PM, Jan Kratochvil wrote:
>> +/* Merge the two parse trees given by DEST and SRC.  The parse tree
>> +   in SRC is attached to DEST at the node represented by TARGET.
>> +   SRC is then freed.  */
>
> It would need to state that DEST will then still possibly reference the
> original string of SRC.  Commented more at `struct demangle_parse_info'.

I have updated the comments.

> Why not just:
>    *target = *src->tree;

Changed.

> You can calculate LAST only if it gets used - move the code more down.

That block got moved around based on Tom's comments. And obstacks 
cleaned a lot of it up, too.

> In some cases type == otype here, such as if there was only one typedef of
> anonymous type or of typedef failed to be resolved.  Here could be also just
> return 0.

Done.

> double-free, NAME is already in FREE_LIST.
> LEN is not set here for NAME.  And CANON is leaked.

Yup. Cleaned up with the move to an obstack.

> ui_file_rewind should happen even if N is NULL, somehow probably to rather
> abort the operation on NULL N.

Agreed.

> IMO here is missing:
> 	ret_comp->type = DEMANGLE_COMPONENT_NAME;

Indeed!

> If it returns NULL I would prefer some sort of abort.  It risks now to quietly
> corrupt the name.  The later operations will probably fail not modifying
> anything but still it is a bit fragile.

Done.

>> +	case DEMANGLE_COMPONENT_TYPED_NAME:
>> +	  {
>> +	    struct demangle_component *comp = d_right (ret_comp);
>> +
>> +	    while (comp != NULL
>> +		&&  (comp->type == DEMANGLE_COMPONENT_VOLATILE
>> +		       || comp->type == DEMANGLE_COMPONENT_RESTRICT
>> +		       || comp->type == DEMANGLE_COMPONENT_CONST
>> +		       || comp->type == DEMANGLE_COMPONENT_VOLATILE_THIS
>> +		       || comp->type == DEMANGLE_COMPONENT_RESTRICT_THIS
>> +		       || comp->type == DEMANGLE_COMPONENT_CONST_THIS))
>> +	      comp = d_left (comp);
>> +
>> +	    if (d_left (ret_comp)->type != DEMANGLE_COMPONENT_NAME
>> +		|| comp->type != DEMANGLE_COMPONENT_FUNCTION_TYPE)
>> +	      replace_typedefs (info, d_left (ret_comp), free_list);
>
> I admit I do not understand the goal of the COMP computation and the
> conditional.  replace_typedefs unconditionally does not break
> gdb.cp/meth-typedefs.exp.  At least a comment of the purpose would be nice.

I've removed this to unconditionally call replace_typedefs. IIRC, that 
was needed for some reason that escapes me at the moment. The real need 
for it is being hidden by the minsym fallback. I'll follow-up on this 
after this patchset is finalized. [That entails a lot of fixes to 
c-typeprint.c and whatnot called by dwarf2_physname when physnames are 
computed.]

> You are now using cp_canonicalize_string_no_typedefs at the consumers but it
> is not used at the producer - at dwarf2_canonicalize_name.  It is not needed
> there as long as GDB depends on DW_AT_MIPS_linkage_name.  But do you plan to
> use it at dwarf2_canonicalize_name?  That is it would fix
> the `set debug check-physname yes' warnings.

When physnames are computed, no typedefs should appear in them (although 
apparently exceptions should exist for std::string and others that are 
excepted in libiberty). This is a bunch of bugs which I will address 
immediately after this is finalized.

Thank you for reviewing this (again)!

New patch attached.

Keith

ChangeLog
2011-08-09  Keith Seitz  <keiths@redhat.com>

	* cp-support.h (cp_canonicalize_string_no_typedefs): Declare.
	(cp_merge_demangle_parse_infos): Declare.
	* cp-support.c (ignore_typedefs): New file global.
	(copy_string_to_obstack): New function.
	(inspect_type): New function.
	(replace_typedefs): New function.
	(replace_typedefs_qualified_name): New function.
	(cp_canonicalize_string_no_typedefs): New function.
	* cp-name-parser.y (cp_merge_demangle_parse_infos): New function.
	(cp_new_demangle__parse_info): Allocate and initialize the obstack.
	* linespec.c (find_methods): Use cp_canonicalize_string_no_typedefs
	instead of cp_canonicalize_string.
	(find_method): Likewise.
	(decode_compound): Before looking up the name, call
	cp_canonicalize_string_no_typedefs.
	(decode_variable): Likewise.

2011-08-09  Keith Seitz  <keiths@redhat.com>

	* gdb.cp/meth-typedefs.cc: New file.
	* gdb.cp/meth-typedefs.exp: New file.


[-- Attachment #2: cp_canonicalize_no_typedefs-5.patch --]
[-- Type: text/plain, Size: 27468 bytes --]

diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y
index 6d23c9d..e73017c 100644
--- a/gdb/cp-name-parser.y
+++ b/gdb/cp-name-parser.y
@@ -41,6 +41,8 @@
 #include "libiberty.h"
 #include "demangle.h"
 #include "cp-support.h"
+#include "gdb_obstack.h"
+#include "gdb_assert.h"
 
 /* Bison does not make it easy to create a parser without global
    state, unfortunately.  Here are all the global variables used
@@ -1970,6 +1972,9 @@ cp_new_demangle_parse_info (void)
   info = malloc (sizeof (struct demangle_parse_info));
   info->info = NULL;
   info->tree = NULL;
+  info->obstack = malloc (sizeof (struct obstack));
+  obstack_init (info->obstack);
+
   return info;
 }
 
@@ -1989,10 +1994,51 @@ cp_demangled_name_parse_free (struct demangle_parse_info *parse_info)
       info = next;
     }
 
+  /* Free any memory allocated during typedef replacement.  */
+  obstack_free (parse_info->obstack, NULL);
+  free (parse_info->obstack);
+
   /* Free the parser info.  */
   free (parse_info);
 }
 
+/* Merge the two parse trees given by DEST and SRC.  The parse tree
+   in SRC is attached to DEST at the node represented by TARGET.
+   SRC is then freed.
+
+   NOTE 1: Since there is no API to merge obstacks, this function does
+   even attempt to try it.  Fortunately, we do not (yet?) need this ability.
+   The code will assert if SRC->obstack is not empty.
+
+   NOTE 2: The string from which SRC was parsed must not be freed, since
+   this function will place pointers to that string into DEST.  */
+
+void
+cp_merge_demangle_parse_infos (struct demangle_parse_info *dest,
+			       struct demangle_component *target,
+			       struct demangle_parse_info *src)
+
+{
+  struct demangle_info *di;
+
+  /* Copy the SRC's parse data into DEST.  */
+  *target = *src->tree;
+  di = dest->info;
+  while (di->next != NULL)
+    di = di->next;
+  di->next = src->info;
+
+  /* Clear the (pointer to) SRC's parse data so that it is not freed when
+     cp_demangled_parse_info_free is called.  */
+  src->info = NULL;
+
+  /* Assert if the SRC obstack is not empty.  */
+  gdb_assert (obstack_empty_p (src->obstack));
+
+  /* Free SRC.  */
+  cp_demangled_name_parse_free (src);
+}
+
 /* Convert a demangled name to a demangle_component tree.  On success,
    a structure containing the root of the new tree is returned; it must
    be freed by calling cp_demangled_name_parse_free. On error, NULL is
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index 8cda2b4..2eff1d8 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -86,6 +86,32 @@ static const char *operator_tokens[] =
     /* new[] and delete[] require special whitespace handling */
   };
 
+/* A list of typedefs which should not be substituted by replace_typedefs.  */
+static const char * const ignore_typedefs[] =
+  {
+    "std::istream", "std::iostream", "std::ostream", "std::string"
+  };
+
+static void
+  replace_typedefs (struct demangle_parse_info *info,
+		    struct demangle_component *ret_comp);
+
+/* A convenience function to copy STRING into OBSTACK, returning a pointer
+   to the newly allocated string and saving the number of bytes saved in LEN.
+
+   It does not copy the terminating '\0' byte!  */
+
+static char *
+copy_string_to_obstack (struct obstack *obstack, const char *string,
+			size_t *len)
+{
+  char *s;
+
+  *len = strlen (string);
+  s = obstack_alloc (obstack, *len);
+  memcpy (s, string, *len);
+  return s;
+}
 
 /* A cleanup wrapper for cp_demangled_name_parse_free.  */
 
@@ -136,6 +162,341 @@ cp_already_canonical (const char *string)
     return 0;
 }
 
+/* Inspect the given RET_COMP for its type.  If it is a typedef,
+   replace the node with the typedef's tree.
+
+   Returns 1 if any typedef substitutions were made, 0 otherwise.  */
+
+static int
+inspect_type (struct demangle_parse_info *info,
+	      struct demangle_component *ret_comp)
+{
+  int i;
+  char *name;
+  struct symbol *sym;
+
+  /* Copy the symbol's name from RET_COMP and look it up
+     in the symbol table.  */
+  name = (char *) alloca (ret_comp->u.s_name.len + 1);
+  memcpy (name, ret_comp->u.s_name.s, ret_comp->u.s_name.len);
+  name[ret_comp->u.s_name.len] = '\0';
+
+  /* Ignore any typedefs that should not be substituted.  */
+  for (i = 0; i < ARRAY_SIZE (ignore_typedefs); ++i)
+    {
+      if (strcmp (name, ignore_typedefs[i]) == 0)
+	return 0;
+    }
+
+  sym = lookup_symbol (name, 0, VAR_DOMAIN, 0);
+  if (sym != NULL)
+    {
+      struct type *otype = SYMBOL_TYPE (sym);
+
+      /* If the type is a typedef, replace it.  */
+      if (TYPE_CODE (otype) == TYPE_CODE_TYPEDEF)
+	{
+	  long len;
+	  int is_anon;
+	  struct type *type;
+	  struct demangle_parse_info *i;
+	  struct ui_file *buf;
+	  struct cleanup *cleanup;
+
+	  /* Get the real type of the typedef.  */
+	  type = check_typedef (otype);
+
+	  is_anon = (TYPE_TAG_NAME (type) == NULL
+		     && (TYPE_CODE (type) == TYPE_CODE_ENUM
+			 || TYPE_CODE (type) == TYPE_CODE_STRUCT
+			 || TYPE_CODE (type) == TYPE_CODE_UNION));
+	  if (is_anon)
+	    {
+	      struct type *last = otype;
+
+	      /* Find the last typedef for the type.  */
+	      while (TYPE_TARGET_TYPE (last) != NULL
+		     && (TYPE_CODE (TYPE_TARGET_TYPE (last))
+			 == TYPE_CODE_TYPEDEF))
+		last = TYPE_TARGET_TYPE (last);
+
+	      /* If there is only one typedef for this anonymous type,
+		 do not substitute it.  */
+	      if (type == otype)
+		return 0;
+	      else
+		/* Use the last typedef seen as the type for this
+		   anonymous type.  */
+		type = last;
+	    }
+
+
+	  buf = mem_fileopen ();
+	  cleanup = make_cleanup_ui_file_delete (buf);
+
+	  type_print (type, "", buf, -1);
+	  name = ui_file_obsavestring (buf, info->obstack, &len);
+	  do_cleanups (cleanup);
+
+	  /* Turn the result into a new tree.  Note that this
+	     tree will contain pointers into NAME, so NAME cannot
+	     be free'd until all typedef conversion is done and
+	     the final result is converted into a string.  */
+	  i = cp_demangled_name_to_comp (name, NULL);
+	  if (i != NULL)
+	    {
+	      /* Merge the two trees.  */
+	      cp_merge_demangle_parse_infos (info, ret_comp, i);
+
+	      /* Replace any newly introduced typedefs -- but not
+		 if the type is anonymous (that would lead to infinite
+		 looping).  */
+	      if (!is_anon)
+		replace_typedefs (info, ret_comp);
+	    }
+	  else
+	    {
+	      /* This shouldn't happen unless the type printer has
+		 output something that the name parser cannot grok.
+		 Nonetheless, an ounce of prevention...
+
+		 Canonicalize the name again, and store it in the
+		 current node (RET_COMP).  */
+	      char *canon = cp_canonicalize_string_no_typedefs (name);
+
+	      if (canon != NULL)
+		{
+		  /* Copy the canonicalization into the obstack and
+		     free CANON.  */
+		  name = copy_string_to_obstack (info->obstack, canon, &len);
+		  xfree (canon);
+		}
+
+	      ret_comp->u.s_name.s = name;
+	      ret_comp->u.s_name.len = len;
+	    }
+
+	  return 1;
+	}
+    }
+
+  return 0;
+}
+
+/* Replace any typedefs appearing in the qualified name
+   (DEMANGLE_COMPONENT_QUAL_NAME) represented in RET_COMP for the name parse
+   given in INFO.  */
+
+static void
+replace_typedefs_qualified_name (struct demangle_parse_info *info,
+				 struct demangle_component *ret_comp)
+{
+  long len;
+  char *name;
+  struct ui_file *buf = mem_fileopen ();
+  struct demangle_component *comp = ret_comp;
+
+  /* Walk each node of the qualified name, reconstructing the name of
+     this element.  With every node, check for any typedef substitutions.
+     If a substitution has occurred, replace the qualified name node
+     with a DEMANGLE_COMPONENT_NAME node representing the new, typedef-
+     substituted name.  */
+  while (comp->type == DEMANGLE_COMPONENT_QUAL_NAME)
+    {
+      if (d_left (comp)->type == DEMANGLE_COMPONENT_NAME)
+	{
+	  struct demangle_component new;
+
+	  ui_file_write (buf, d_left (comp)->u.s_name.s,
+			 d_left (comp)->u.s_name.len);
+	  name = ui_file_obsavestring (buf, info->obstack, &len);
+	  new.type = DEMANGLE_COMPONENT_NAME;
+	  new.u.s_name.s = name;
+	  new.u.s_name.len = len;
+	  if (inspect_type (info, &new))
+	    {
+	      char *n, *s;
+	      size_t slen;
+
+	      /* A typedef was substituted in NEW.  Convert it to a
+		 string and replace the top DEMANGLE_COMPONENT_QUAL_NAME
+		 node.  */
+
+	      ui_file_rewind (buf);
+	      n = cp_comp_to_string (&new, 100);
+	      if (n == NULL)
+		{
+		  /* If something went astray, abort typedef substitutions.  */
+		  ui_file_delete (buf);
+		  return;
+		}
+
+	      s = copy_string_to_obstack (info->obstack, n, &slen);
+	      xfree (n);
+
+	      ret_comp->type = DEMANGLE_COMPONENT_NAME;
+	      d_left (ret_comp)->u.s_name.s = s;
+	      d_left (ret_comp)->u.s_name.len = slen;
+	      d_right (ret_comp) = d_right (comp);
+	      comp = ret_comp;
+	      continue;
+	    }
+	}
+      else
+	{
+	  /* The current node is not a name, so simply replace any
+	     typedefs in it.  Then print it to the stream to continue
+	     checking for more typedefs in the tree.  */
+	  replace_typedefs (info, d_left (comp));
+	  name = cp_comp_to_string (d_left (comp), 100);
+	  if (name == NULL)
+	    {
+	      /* If something went astray, abort typedef substitutions.  */
+	      ui_file_delete (buf);
+	      return;
+	    }
+	  fputs_unfiltered (name, buf);
+	  xfree (name);
+	}
+      ui_file_write (buf, "::", 2);
+      comp = d_right (comp);
+    }
+
+  /* If the next component is DEMANGLE_COMPONENT_NAME, save the qualified
+     name assembled above and append the name given by COMP.  Then use this
+     reassembled name to check for a typedef.  */
+
+  if (comp->type == DEMANGLE_COMPONENT_NAME)
+    {
+      ui_file_write (buf, comp->u.s_name.s, comp->u.s_name.len);
+      name = ui_file_obsavestring (buf, info->obstack, &len);
+
+      /* Replace the top (DEMANGLE_COMPONENT_QUAL_NAME) node
+	 with a DEMANGLE_COMPONENT_NAME node containing the whole
+	 name.  */
+      ret_comp->type = DEMANGLE_COMPONENT_NAME;
+      ret_comp->u.s_name.s = name;
+      ret_comp->u.s_name.len = len;
+      inspect_type (info, ret_comp);
+    }
+  else
+    replace_typedefs (info, comp);
+
+  ui_file_delete (buf);
+}
+
+
+/* A function to check const and volatile qualifiers for argument types.
+
+   "Parameter declarations that differ only in the presence
+   or absence of `const' and/or `volatile' are equivalent."
+   C++ Standard N3290, clause 13.1.3 #4.  */
+
+static void
+check_cv_qualifiers (struct demangle_component *ret_comp)
+{
+  while (d_left (ret_comp) != NULL
+	 && (d_left (ret_comp)->type == DEMANGLE_COMPONENT_CONST
+	     || d_left (ret_comp)->type == DEMANGLE_COMPONENT_VOLATILE))
+    {
+      d_left (ret_comp) = d_left (d_left (ret_comp));
+    }
+}
+
+/* Walk the parse tree given by RET_COMP, replacing any typedefs with
+   their basic types.  */
+
+static void
+replace_typedefs (struct demangle_parse_info *info,
+		  struct demangle_component *ret_comp)
+{
+  if (ret_comp)
+    {
+      switch (ret_comp->type)
+	{
+	case DEMANGLE_COMPONENT_ARGLIST:
+	  check_cv_qualifiers (ret_comp);
+	  /* Fall through */
+
+	case DEMANGLE_COMPONENT_FUNCTION_TYPE:
+	case DEMANGLE_COMPONENT_TEMPLATE:
+	case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST:
+	case DEMANGLE_COMPONENT_TYPED_NAME:
+	  replace_typedefs (info, d_left (ret_comp));
+	  replace_typedefs (info, d_right (ret_comp));
+	  break;
+
+	case DEMANGLE_COMPONENT_NAME:
+	  inspect_type (info, ret_comp);
+	  break;
+
+	case DEMANGLE_COMPONENT_QUAL_NAME:
+	  replace_typedefs_qualified_name (info, ret_comp);
+	  break;
+
+	case DEMANGLE_COMPONENT_LOCAL_NAME:
+	case DEMANGLE_COMPONENT_CTOR:
+	case DEMANGLE_COMPONENT_ARRAY_TYPE:
+	case DEMANGLE_COMPONENT_PTRMEM_TYPE:
+	  replace_typedefs (info, d_right (ret_comp));
+	  break;
+
+	case DEMANGLE_COMPONENT_CONST:
+	case DEMANGLE_COMPONENT_RESTRICT:
+	case DEMANGLE_COMPONENT_VOLATILE:
+	case DEMANGLE_COMPONENT_VOLATILE_THIS:
+	case DEMANGLE_COMPONENT_CONST_THIS:
+	case DEMANGLE_COMPONENT_RESTRICT_THIS:
+	case DEMANGLE_COMPONENT_POINTER:
+	case DEMANGLE_COMPONENT_REFERENCE:
+	  replace_typedefs (info, d_left (ret_comp));
+	  break;
+
+	default:
+	  break;
+	}
+    }
+}
+
+/* Parse STRING and convert it to canonical form, resolving any typedefs.
+   If parsing fails, or if STRING is already canonical, return NULL.
+   Otherwise return the canonical form.  The return value is allocated via
+   xmalloc.  */
+
+char *
+cp_canonicalize_string_no_typedefs (const char *string)
+{
+  char *ret;
+  unsigned int estimated_len;
+  struct demangle_parse_info *info;
+
+  ret = NULL;
+  estimated_len = strlen (string) * 2;
+  info = cp_demangled_name_to_comp (string, NULL);
+  if (info != NULL)
+    {
+      /* Replace all the typedefs in the tree.  */
+      replace_typedefs (info, info->tree);
+
+      /* Convert the tree back into a string.  */
+      ret = cp_comp_to_string (info->tree, estimated_len);
+      gdb_assert (ret != NULL);
+
+      /* Free the parse information.  */
+      cp_demangled_name_parse_free (info);
+
+      /* Finally, compare the original string with the computed
+	 name, returning NULL if they are the same.  */
+      if (strcmp (string, ret) == 0)
+	{
+	  xfree (ret);
+	  return NULL;
+	}
+    }
+
+  return ret;
+}
+
 /* Parse STRING and convert it to canonical form.  If parsing fails,
    or if STRING is already canonical, return NULL.  Otherwise return
    the canonical form.  The return value is allocated via xmalloc.  */
diff --git a/gdb/cp-support.h b/gdb/cp-support.h
index f333ffa..5c839fc 100644
--- a/gdb/cp-support.h
+++ b/gdb/cp-support.h
@@ -55,6 +55,9 @@ struct demangle_parse_info
 
   /* The result of the parse.  */
   struct demangle_component *tree;
+
+  /* Any temporary memory used during typedef replacement.  */
+  struct obstack *obstack;
 };
 
 /* This struct is designed to store data from using directives.  It
@@ -145,6 +148,8 @@ struct using_direct
 
 extern char *cp_canonicalize_string (const char *string);
 
+extern char *cp_canonicalize_string_no_typedefs (const char *string);
+
 extern char *cp_class_name_from_physname (const char *physname);
 
 extern char *method_name_from_physname (const char *physname);
@@ -234,6 +239,9 @@ extern char *cp_comp_to_string (struct demangle_component *result,
 extern void cp_demangled_name_parse_free (struct demangle_parse_info *);
 extern struct cleanup *make_cleanup_cp_demangled_name_parse_free
      (struct demangle_parse_info *);
+extern void cp_merge_demangle_parse_infos (struct demangle_parse_info *,
+					   struct demangle_component *,
+					   struct demangle_parse_info *);
 
 extern struct demangle_parse_info *cp_new_demangle_parse_info (void);
 
diff --git a/gdb/linespec.c b/gdb/linespec.c
index 137ef9c..b96c79f 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -245,7 +245,7 @@ find_methods (struct type *t, char *name, enum language language,
 
   /* NAME is typed by the user: it needs to be canonicalized before
      passing to lookup_symbol.  */
-  canon = cp_canonicalize_string (name);
+  canon = cp_canonicalize_string_no_typedefs (name);
   if (canon != NULL)
     {
       name = canon;
@@ -1365,7 +1365,7 @@ decode_compound (char **argptr, int funfirstline,
 		 char *the_real_saved_arg, char *p)
 {
   struct symtabs_and_lines values;
-  char *p2;
+  char *p2, *name, *canon;
   char *saved_arg2 = *argptr;
   char *temp_end;
   struct symbol *sym;
@@ -1373,6 +1373,7 @@ decode_compound (char **argptr, int funfirstline,
   struct symbol *sym_class;
   struct type *t;
   char *saved_arg;
+  struct cleanup *cleanup;
 
   /* If the user specified any completer quote characters in the input,
      strip them.  They are superfluous.  */
@@ -1597,7 +1598,18 @@ decode_compound (char **argptr, int funfirstline,
   *argptr = (*p == '\'') ? p + 1 : p;
 
   /* Look up entire name.  */
-  sym = lookup_symbol (copy, get_selected_block (0), VAR_DOMAIN, 0);
+  name = copy;
+
+  cleanup = make_cleanup (null_cleanup, NULL);
+  canon = cp_canonicalize_string_no_typedefs (copy);
+  if (canon != NULL)
+    {
+      name = canon;
+      make_cleanup (xfree, name);
+    }
+
+  sym = lookup_symbol (name, get_selected_block (0), VAR_DOMAIN, 0);
+  do_cleanups (cleanup);
   if (sym)
     return symbol_found (funfirstline, canonical, copy, sym, NULL, NULL);
   else
@@ -1743,7 +1755,7 @@ find_method (int funfirstline, struct linespec_result *canonical,
 	  strcpy (name, SYMBOL_NATURAL_NAME (sym_class));
 	  strcat (name, "::");
 	  strcat (name, copy);
-	  canon = cp_canonicalize_string (name);
+	  canon = cp_canonicalize_string_no_typedefs (name);
 	  if (canon != NULL)
 	    {
 	      xfree (name);
@@ -2085,16 +2097,31 @@ decode_variable (char *copy, int funfirstline,
 		 struct linespec_result *canonical,
 		 struct symtab *file_symtab)
 {
+  char *name, *canon;
   struct symbol *sym;
+  struct cleanup *cleanup;
   struct minimal_symbol *msymbol;
 
-  sym = lookup_symbol (copy, get_search_block (file_symtab),
-		       VAR_DOMAIN, 0);
+  name = copy;
+  cleanup = make_cleanup (null_cleanup, NULL);
+  canon = cp_canonicalize_string_no_typedefs (copy);
+  if (canon != NULL)
+    {
+      name = canon;
+      make_cleanup (xfree, name);
+    }
+
+  sym = lookup_symbol (name, get_search_block (file_symtab), VAR_DOMAIN, 0);
 
   if (sym != NULL)
-    return symbol_found (funfirstline, canonical, copy, sym, file_symtab, NULL);
+    {
+      do_cleanups (cleanup);
+      return symbol_found (funfirstline, canonical, copy, sym,
+			   file_symtab, NULL);
+    }
 
-  msymbol = lookup_minimal_symbol (copy, NULL, NULL);
+  msymbol = lookup_minimal_symbol (name, NULL, NULL);
+  do_cleanups (cleanup);
 
   if (msymbol != NULL)
     return minsym_found (funfirstline, msymbol);
diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.cc b/gdb/testsuite/gdb.cp/meth-typedefs.cc
new file mode 100644
index 0000000..b3c68cc
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/meth-typedefs.cc
@@ -0,0 +1,149 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+   Contributed by Red Hat, originally written by Keith Seitz.  */
+
+#include <stdlib.h>
+
+typedef const char* const* my_type;
+typedef int my_type_2;
+typedef my_type my_other_type;
+typedef my_type_2 my_other_type_2;
+typedef unsigned long CORE_ADDR;
+typedef enum {E_A, E_B, E_C} anon_enum;
+typedef struct {int a; char b;} anon_struct;
+typedef union {int a; char b;} anon_union;
+typedef anon_enum aenum;
+typedef anon_struct astruct;
+typedef anon_union aunion;
+
+typedef void (*fptr1) (my_other_type);
+typedef void (*fptr2) (fptr1, my_other_type_2);
+typedef void (*fptr3) (fptr2, my_other_type);
+typedef void (*fptr4) (anon_enum a, anon_struct const& b, anon_union const*** c);
+
+namespace A
+{
+  class foo
+  {
+  public:
+    foo (void) { }
+    foo (my_other_type a) { } // A::foo::foo(my_other_type)
+    foo (my_other_type_2 a) { } // A::foo::foo(my_other_type_2)
+    foo (my_other_type_2 a, const my_other_type b) { } // A::foo::foo(my_other_type_2, const my_other_type)
+    foo (fptr3) { } // A::foo::foo(fptr3)
+    foo (fptr1* a) { } // A::foo::foo(fptr1*)
+    foo (CORE_ADDR (*) [10]) { } // A::foo::foo(CORE_ADDR (*) [10])
+    foo (aenum a, astruct const& b, aunion const*** c) { } // A::foo::foo(aenum, astruct const&, aunion const***)
+
+    void test (my_other_type a) { } // A::foo::test(my_other_type)
+    void test (my_other_type_2 a) { } // A::foo::test(my_other_type_2)
+    void test (my_other_type_2 a, const my_other_type b) { } // A::foo::test(my_other_type_2, const my_other_type)
+    void test (fptr3 a) { } // A::foo::test(fptr3)
+    void test (fptr1* a) { } // A::foo::test(fptr1*)
+    void test (CORE_ADDR (*) [10]) { } // A::foo::test(CORE_ADDR (*) [10])
+    void test (aenum a, astruct const& b, aunion const*** c) { }; // A::foo::test(aenum, astruct const&, aunion const***)
+  };
+};
+
+namespace B
+{
+  void
+  test (my_other_type foo) { } // B::test(my_other_type)
+
+  void
+  test (aenum a, astruct const& b, aunion const*** c) { } // B::test(aenum, astruct const&, aunion const***)
+
+  template <typename T1, typename T2>
+  void test (T1 a, T2 b) { } // B::test (T1, T2)
+
+  template <>
+  void test (my_other_type foo, my_other_type_2) { } // B::test<my_other_type, my_other_type_2>(my_other_type, my_other_type_2)
+};
+
+namespace a
+{
+  namespace b
+  {
+    namespace c
+    {
+      namespace d
+      {
+	class bar { };
+      }
+    }
+
+    typedef c::d::bar BAR;
+  }
+}
+
+typedef a::b::BAR _BAR_;
+
+template <typename T1, typename T2>
+void test (T1 a, T2 b) {} // test (T1, T2)
+
+template <>
+void test (my_other_type foo, my_other_type_2) { } // test<my_other_type, my_other_type_2>(my_other_type, my_other_type_2)
+
+void
+test (my_other_type foo) { } // test(my_other_type)
+
+void
+test (_BAR_ &b) { } // test(_BAR_&)
+
+void
+test (aenum a, astruct const& b, aunion const*** c) { } // test(aenum, astruct const&, aunion const***)
+
+int
+main (void)
+{
+  A::foo my_foo;
+  fptr1 fptr;
+  astruct as = { 0, 0 };
+  aunion const au = { 0 };
+  aunion const* aup = &au;
+  aunion const** aupp = &aup;
+  aunion const*** auppp = &aupp;
+
+  my_foo.test (static_cast<my_other_type> (NULL));
+  my_foo.test (0);
+  my_foo.test (0, static_cast<my_type> (NULL));
+  my_foo.test (static_cast<fptr3> (NULL));
+  my_foo.test (&fptr);
+  my_foo.test (static_cast<CORE_ADDR (*) [10]> (0));
+  my_foo.test (E_A, as, auppp);
+
+  B::test (static_cast<my_other_type> (NULL));
+  B::test (static_cast<my_other_type> (NULL), 0);
+  B::test (E_A, as, auppp);
+
+  test (static_cast<my_other_type> (NULL));
+  test<my_other_type, my_other_type_2> (static_cast<my_other_type> (NULL), 0);
+  test (E_A, as, auppp);
+
+  A::foo a (static_cast<my_other_type> (NULL));
+  A::foo b (0);
+  A::foo c (0, static_cast<my_other_type> (NULL));
+  A::foo d (static_cast<fptr3> (NULL));
+  A::foo e (&fptr);
+  A::foo f (static_cast<CORE_ADDR (*) [10]> (0));
+  A::foo g (E_A, as, auppp);
+
+  fptr4 f4;
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/meth-typedefs.exp b/gdb/testsuite/gdb.cp/meth-typedefs.exp
new file mode 100644
index 0000000..851ec02
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/meth-typedefs.exp
@@ -0,0 +1,160 @@
+# Copyright 2011 Free Software Foundation, Inc.
+#
+# Contributed by Red Hat, originally written by Keith Seitz.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the gdb testsuite.
+
+proc add {var name params expected {kind {func}}} {
+    upvar $var result
+
+    if {[string compare $kind "template"] == 0} {
+	set method_name "${name}<$expected>"
+    } else {
+	set method_name "$name"
+    }
+
+    set expect ".*// ${method_name}\\($expected\\)"
+    lappend result [list "${method_name}($params)" $expect]
+}
+
+if {[skip_cplus_tests]} { continue }
+
+# Tests for c++/12266 et al
+set testfile "meth-typedefs"
+set srcfile $testfile.cc
+
+if {[prepare_for_testing $testfile $testfile $srcfile {c++ debug}]} {
+    return -1
+}
+
+if {![runto_main]} {
+    perror "couldn't run to breakpoint"
+    continue
+}
+
+array set typedefs {
+    "my_other_type" {"my_other_type" "my_type" "const char* const*"}
+    "my_other_type_2" {"my_other_type_2" "my_type_2" "int"}
+    "CORE_ADDR" { "CORE_ADDR" "unsigned long" }
+    "_BAR_" { "_BAR_" "a::b::BAR" "a::b::c::d::bar" }
+    "aenum" { "aenum" "anon_enum" }
+    "astruct" { "astruct" "anon_struct" }
+    "aunion" { "aunion" "anon_union" }
+}
+
+set methods {}
+
+# Add the simple, one-parameter methods
+foreach meth {A::foo::test A::foo::foo} {
+    foreach type {my_other_type my_other_type_2} {
+	foreach t $typedefs($type)  {
+	    add methods $meth $t $type
+	}
+    }
+}
+
+# Add two-parameter methods
+foreach meth {A::foo::test A::foo::foo} {
+    set type "my_other_type_2, const my_other_type"
+    foreach t1 $typedefs(my_other_type_2) {
+	foreach t2 $typedefs(my_other_type) {
+	    add methods $meth "$t1, const $t2" $type
+	    add methods $meth "$t1, $t2" $type
+	}
+    }
+}
+
+# Add three-parameter methods/functions
+foreach meth {A::foo::test A::foo::foo B::test test} {
+    set type "aenum, astruct const&, aunion const\\*\\*\\*"
+    foreach t1 $typedefs(aenum) {
+	foreach t2 $typedefs(astruct) {
+	    foreach t3 $typedefs(aunion) {
+		add methods $meth "$t1, $t2 const&, $t3 const***" $type
+	    }
+	}
+    }
+}
+
+# Add the array-of-function pointer methods
+set type "fptr1\\*"
+foreach meth {A::foo::test A::foo::foo} {
+    add methods $meth "fptr1*" $type
+    foreach t $typedefs(my_other_type) {
+	add methods $meth "void (**) ($t)" $type
+    }
+}
+
+# Add the function pointer methods
+set type "fptr3"
+foreach meth {A::foo::test A::foo::foo} {
+    add methods $meth "fptr3" $type
+
+    foreach t1 $typedefs(my_other_type) {
+	add methods $meth "void (*)(fptr2, $t1)" $type
+	foreach t2 $typedefs(my_other_type_2) {
+	    add methods $meth "void (*)(void (*)(fptr1, $t2), $t1)" $type
+	    foreach t3 $typedefs(my_other_type) {
+		add methods $meth \
+		    "void (*)(void (*)(void (*) ($t3), $t2), $t1)" $type
+	    }
+	}
+    }
+}
+
+set type1 "my_other_type"
+set type2 "my_other_type, my_other_type_2"
+foreach meth {"test" "B::test"} {
+    foreach t1 $typedefs(my_other_type) {
+	add methods $meth $t1 $type1
+	foreach t2 $typedefs(my_other_type_2) {
+	    add methods $meth "$t1, $t2" $type2 template
+	}
+    }
+}
+
+# Miscellaneous tests
+set type {CORE_ADDR \(\*\) \[10\]}
+foreach meth {A::foo::foo A::foo::test} {
+    foreach t $typedefs(CORE_ADDR) {
+	add methods $meth "$t (*) \[10\]" $type
+    }
+}
+
+foreach t $typedefs(_BAR_) {
+    add methods "test" "$t&" {_BAR_&}
+}
+
+gdb_test_no_output "set listsize 1" ""
+
+# Finally, for each method in the list METHODS, check whether
+# the user can "list" it and "break" on it (both quoted and unquoted).
+foreach test $methods {
+    set func [lindex $test 0]
+    set result [lindex $test 1]
+
+    gdb_test "list $func" $result
+    gdb_test "list '$func'" $result
+    if {[gdb_breakpoint $func]} {
+      pass "break $func"
+    }
+    if {[gdb_breakpoint '$func']} {
+      pass "break '$func'"
+    }
+}
+
+gdb_exit
+return 0

  reply	other threads:[~2011-08-09 20:48 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-07-29 13:16 [RFA] c++/12266 (again) Keith Seitz
2011-08-01 20:38 ` Tom Tromey
2011-08-02 16:08 ` Tom Tromey
2011-08-02 17:58   ` Keith Seitz
2011-08-02 20:28 ` [RFA] c++/12266 (again) [cp_demangled_name_parse_free-4.patch] Jan Kratochvil
2011-08-09 20:23   ` Keith Seitz
2011-08-13 16:51     ` Jan Kratochvil
2011-08-18 16:22       ` Keith Seitz
2011-08-02 20:37 ` [RFA] c++/12266 (again) [cp_canonicalize_no_typedefs-4.patch] Jan Kratochvil
2011-08-09 20:48   ` Keith Seitz [this message]
2011-08-13 16:50     ` Jan Kratochvil
2011-08-18 16:25       ` Keith Seitz
2011-08-18 16:35         ` Keith Seitz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4E419D17.9020404@redhat.com \
    --to=keiths@redhat.com \
    --cc=gdb-patches@sourceware.org \
    --cc=jan.kratochvil@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox