]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Fix C++ canonicalization of hex literals
authorTom Tromey <tom@tromey.com>
Thu, 11 Apr 2024 18:43:06 +0000 (12:43 -0600)
committerTom Tromey <tom@tromey.com>
Tue, 14 May 2024 19:28:40 +0000 (13:28 -0600)
Currently names like "x::y::z<1>" and "x::y::z<0x01>" canonicalize to
different things.  I think it's nicer for them to be the same.
Differences between types can be done using suffixes like "ll" and "u"
-- it's not really possible to implement C++ rules in the
canoncalizer, because no gdbarch is available.  Possibly gdb should
even drop the type here and just represent all integers the same way
in names.

Approved-By: John Baldwin <jhb@FreeBSD.org>
gdb/cp-name-parser.y

index b254d63bfec70336825a8a1d29bd894ac17c1fe8..a84051a1b050fef132626879135560820df848be 100644 (file)
@@ -1336,8 +1336,35 @@ cpname_state::parse_number (const char *p, int len, int parsed_float,
       return FLOAT;
     }
 
-  /* This treats 0x1 and 1 as different literals.  We also do not
-     automatically generate unsigned types.  */
+  /* Note that we do not automatically generate unsigned types.  This
+     can't be done because we don't have access to the gdbarch
+     here.  */
+
+  int base = 10;
+  if (len > 1 && p[0] == '0')
+    {
+      if (p[1] == 'x' || p[1] == 'X')
+       {
+         base = 16;
+         p += 2;
+         len -= 2;
+       }
+      else if (p[1] == 'b' || p[1] == 'B')
+       {
+         base = 2;
+         p += 2;
+         len -= 2;
+       }
+      else if (p[1] == 'd' || p[1] == 'D' || p[1] == 't' || p[1] == 'T')
+       {
+         /* Apparently gdb extensions.  */
+         base = 10;
+         p += 2;
+         len -= 2;
+       }
+      else
+       base = 8;
+    }
 
   long_p = 0;
   unsigned_p = 0;
@@ -1358,6 +1385,24 @@ cpname_state::parse_number (const char *p, int len, int parsed_float,
       break;
     }
 
+  /* Use gdb_mpz here in case a 128-bit value appears.  */
+  gdb_mpz value (0);
+  for (int off = 0; off < len; ++off)
+    {
+      int dig;
+      if (ISDIGIT (p[off]))
+       dig = p[off] - '0';
+      else
+       dig = TOLOWER (p[off]) - 'a' + 10;
+      if (dig >= base)
+       return ERROR;
+      value *= base;
+      value += dig;
+    }
+
+  std::string printed = value.str ();
+  const char *copy = obstack_strdup (&demangle_info->obstack, printed);
+
   if (long_p == 0)
     {
       if (unsigned_p)
@@ -1380,10 +1425,10 @@ cpname_state::parse_number (const char *p, int len, int parsed_float,
        type = make_builtin_type ("long long");
     }
 
-   name = make_name (p, len);
-   lvalp->comp = fill_comp (literal_type, type, name);
+  name = make_name (copy, strlen (copy));
+  lvalp->comp = fill_comp (literal_type, type, name);
 
-   return INT;
+  return INT;
 }
 
 static const char backslashable[] = "abefnrtv";
@@ -1990,6 +2035,12 @@ canonicalize_tests ()
   should_be_the_same ("int short", "short");
 
   should_be_the_same ("C<(char) 1>::m()", "C<(char) '\\001'>::m()");
+  should_be_the_same ("x::y::z<1>", "x::y::z<0x01>");
+  should_be_the_same ("x::y::z<1>", "x::y::z<01>");
+  should_be_the_same ("x::y::z<(unsigned long long) 1>", "x::y::z<01ull>");
+  should_be_the_same ("x::y::z<0b111>", "x::y::z<7>");
+  should_be_the_same ("x::y::z<0b111>", "x::y::z<0t7>");
+  should_be_the_same ("x::y::z<0b111>", "x::y::z<0D7>");
 }
 
 #endif