Convert Rust to use discriminated unions
[deliverable/binutils-gdb.git] / gdb / dwarf2read.c
index ff808be828bf60cd9f0fb4ca0988774d07c51f9c..dab5cfb0dc058248d8fad98407969c0b158fe867 100644 (file)
@@ -86,6 +86,7 @@
 #include <cmath>
 #include <set>
 #include <forward_list>
+#include "rust-lang.h"
 
 /* When == 1, print basic high level tracing messages.
    When > 1, be more verbose.
@@ -762,6 +763,14 @@ struct dwarf2_cu
      whether the DW_AT_ranges attribute came from the skeleton or DWO.  */
   ULONGEST ranges_base = 0;
 
+  /* When reading debug info generated by older versions of rustc, we
+     have to rewrite some union types to be struct types with a
+     variant part.  This rewriting must be done after the CU is fully
+     read in, because otherwise at the point of rewriting some struct
+     type might not have been fully processed.  So, we keep a list of
+     all such types here and process them after expansion.  */
+  std::vector<struct type *> rust_unions;
+
   /* Mark used when releasing cached dies.  */
   unsigned int mark : 1;
 
@@ -10318,6 +10327,302 @@ fixup_go_packaging (struct dwarf2_cu *cu)
     }
 }
 
+/* Allocate a fully-qualified name consisting of the two parts on the
+   obstack.  */
+
+static const char *
+rust_fully_qualify (struct obstack *obstack, const char *p1, const char *p2)
+{
+  return obconcat (obstack, p1, "::", p2, (char *) NULL);
+}
+
+/* A helper that allocates a struct discriminant_info to attach to a
+   union type.  */
+
+static struct discriminant_info *
+alloc_discriminant_info (struct type *type, int discriminant_index,
+                        int default_index)
+{
+  gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION);
+  gdb_assert (default_index == -1
+             || (default_index > 0 && default_index < TYPE_NFIELDS (type)));
+
+  TYPE_FLAG_DISCRIMINATED_UNION (type) = 1;
+
+  struct discriminant_info *disc
+    = ((struct discriminant_info *)
+       TYPE_ZALLOC (type,
+                   offsetof (struct discriminant_info, discriminants)
+                   + TYPE_NFIELDS (type) * sizeof (disc->discriminants[0])));
+  disc->default_index = default_index;
+  disc->discriminant_index = discriminant_index;
+
+  struct dynamic_prop prop;
+  prop.kind = PROP_UNDEFINED;
+  prop.data.baton = disc;
+
+  add_dyn_prop (DYN_PROP_DISCRIMINATED, prop, type);
+
+  return disc;
+}
+
+/* Some versions of rustc emitted enums in an unusual way.
+
+   Ordinary enums were emitted as unions.  The first element of each
+   structure in the union was named "RUST$ENUM$DISR".  This element
+   held the discriminant.
+
+   These versions of Rust also implemented the "non-zero"
+   optimization.  When the enum had two values, and one is empty and
+   the other holds a pointer that cannot be zero, the pointer is used
+   as the discriminant, with a zero value meaning the empty variant.
+   Here, the union's first member is of the form
+   RUST$ENCODED$ENUM$<fieldno>$<fieldno>$...$<variantname>
+   where the fieldnos are the indices of the fields that should be
+   traversed in order to find the field (which may be several fields deep)
+   and the variantname is the name of the variant of the case when the
+   field is zero.
+
+   This function recognizes whether TYPE is of one of these forms,
+   and, if so, smashes it to be a variant type.  */
+
+static void
+quirk_rust_enum (struct type *type, struct objfile *objfile)
+{
+  gdb_assert (TYPE_CODE (type) == TYPE_CODE_UNION);
+
+  /* We don't need to deal with empty enums.  */
+  if (TYPE_NFIELDS (type) == 0)
+    return;
+
+#define RUST_ENUM_PREFIX "RUST$ENCODED$ENUM$"
+  if (TYPE_NFIELDS (type) == 1
+      && startswith (TYPE_FIELD_NAME (type, 0), RUST_ENUM_PREFIX))
+    {
+      const char *name = TYPE_FIELD_NAME (type, 0) + strlen (RUST_ENUM_PREFIX);
+
+      /* Decode the field name to find the offset of the
+        discriminant.  */
+      ULONGEST bit_offset = 0;
+      struct type *field_type = TYPE_FIELD_TYPE (type, 0);
+      while (name[0] >= '0' && name[0] <= '9')
+       {
+         char *tail;
+         unsigned long index = strtoul (name, &tail, 10);
+         name = tail;
+         if (*name != '$'
+             || index >= TYPE_NFIELDS (field_type)
+             || (TYPE_FIELD_LOC_KIND (field_type, index)
+                 != FIELD_LOC_KIND_BITPOS))
+           {
+             complaint (&symfile_complaints,
+                        _("Could not parse Rust enum encoding string \"%s\""
+                          "[in module %s]"),
+                        TYPE_FIELD_NAME (type, 0),
+                        objfile_name (objfile));
+             return;
+           }
+         ++name;
+
+         bit_offset += TYPE_FIELD_BITPOS (field_type, index);
+         field_type = TYPE_FIELD_TYPE (field_type, index);
+       }
+
+      /* Make a union to hold the variants.  */
+      struct type *union_type = alloc_type (objfile);
+      TYPE_CODE (union_type) = TYPE_CODE_UNION;
+      TYPE_NFIELDS (union_type) = 3;
+      TYPE_FIELDS (union_type)
+       = (struct field *) TYPE_ZALLOC (type, 3 * sizeof (struct field));
+      TYPE_LENGTH (union_type) = TYPE_LENGTH (type);
+
+      /* Put the discriminant must at index 0.  */
+      TYPE_FIELD_TYPE (union_type, 0) = field_type;
+      TYPE_FIELD_ARTIFICIAL (union_type, 0) = 1;
+      TYPE_FIELD_NAME (union_type, 0) = "<<discriminant>>";
+      SET_FIELD_BITPOS (TYPE_FIELD (union_type, 0), bit_offset);
+
+      /* The order of fields doesn't really matter, so put the real
+        field at index 1 and the data-less field at index 2.  */
+      struct discriminant_info *disc
+       = alloc_discriminant_info (union_type, 0, 1);
+      TYPE_FIELD (union_type, 1) = TYPE_FIELD (type, 0);
+      TYPE_FIELD_NAME (union_type, 1)
+       = rust_last_path_segment (TYPE_NAME (TYPE_FIELD_TYPE (union_type, 1)));
+      TYPE_NAME (TYPE_FIELD_TYPE (union_type, 1))
+       = rust_fully_qualify (&objfile->objfile_obstack, TYPE_NAME (type),
+                             TYPE_FIELD_NAME (union_type, 1));
+
+      const char *dataless_name
+       = rust_fully_qualify (&objfile->objfile_obstack, TYPE_NAME (type),
+                             name);
+      struct type *dataless_type = init_type (objfile, TYPE_CODE_VOID, 0,
+                                             dataless_name);
+      TYPE_FIELD_TYPE (union_type, 2) = dataless_type;
+      /* NAME points into the original discriminant name, which
+        already has the correct lifetime.  */
+      TYPE_FIELD_NAME (union_type, 2) = name;
+      SET_FIELD_BITPOS (TYPE_FIELD (union_type, 2), 0);
+      disc->discriminants[2] = 0;
+
+      /* Smash this type to be a structure type.  We have to do this
+        because the type has already been recorded.  */
+      TYPE_CODE (type) = TYPE_CODE_STRUCT;
+      TYPE_NFIELDS (type) = 1;
+      TYPE_FIELDS (type)
+       = (struct field *) TYPE_ZALLOC (type, sizeof (struct field));
+
+      /* Install the variant part.  */
+      TYPE_FIELD_TYPE (type, 0) = union_type;
+      SET_FIELD_BITPOS (TYPE_FIELD (type, 0), 0);
+      TYPE_FIELD_NAME (type, 0) = "<<variants>>";
+    }
+  else if (TYPE_NFIELDS (type) == 1)
+    {
+      /* We assume that a union with a single field is a univariant
+        enum.  */
+      /* Smash this type to be a structure type.  We have to do this
+        because the type has already been recorded.  */
+      TYPE_CODE (type) = TYPE_CODE_STRUCT;
+
+      /* Make a union to hold the variants.  */
+      struct type *union_type = alloc_type (objfile);
+      TYPE_CODE (union_type) = TYPE_CODE_UNION;
+      TYPE_NFIELDS (union_type) = TYPE_NFIELDS (type);
+      TYPE_LENGTH (union_type) = TYPE_LENGTH (type);
+      TYPE_FIELDS (union_type) = TYPE_FIELDS (type);
+
+      struct type *field_type = TYPE_FIELD_TYPE (union_type, 0);
+      const char *variant_name
+       = rust_last_path_segment (TYPE_NAME (field_type));
+      TYPE_FIELD_NAME (union_type, 0) = variant_name;
+      TYPE_NAME (field_type)
+       = rust_fully_qualify (&objfile->objfile_obstack,
+                             TYPE_NAME (field_type), variant_name);
+
+      /* Install the union in the outer struct type.  */
+      TYPE_NFIELDS (type) = 1;
+      TYPE_FIELDS (type)
+       = (struct field *) TYPE_ZALLOC (union_type, sizeof (struct field));
+      TYPE_FIELD_TYPE (type, 0) = union_type;
+      TYPE_FIELD_NAME (type, 0) = "<<variants>>";
+      SET_FIELD_BITPOS (TYPE_FIELD (type, 0), 0);
+
+      alloc_discriminant_info (union_type, -1, 0);
+    }
+  else
+    {
+      struct type *disr_type = nullptr;
+      for (int i = 0; i < TYPE_NFIELDS (type); ++i)
+       {
+         disr_type = TYPE_FIELD_TYPE (type, i);
+
+         if (TYPE_NFIELDS (disr_type) == 0)
+           {
+             /* Could be data-less variant, so keep going.  */
+           }
+         else if (strcmp (TYPE_FIELD_NAME (disr_type, 0),
+                          "RUST$ENUM$DISR") != 0)
+           {
+             /* Not a Rust enum.  */
+             return;
+           }
+         else
+           {
+             /* Found one.  */
+             break;
+           }
+       }
+
+      /* If we got here without a discriminant, then it's probably
+        just a union.  */
+      if (disr_type == nullptr)
+       return;
+
+      /* Smash this type to be a structure type.  We have to do this
+        because the type has already been recorded.  */
+      TYPE_CODE (type) = TYPE_CODE_STRUCT;
+
+      /* Make a union to hold the variants.  */
+      struct field *disr_field = &TYPE_FIELD (disr_type, 0);
+      struct type *union_type = alloc_type (objfile);
+      TYPE_CODE (union_type) = TYPE_CODE_UNION;
+      TYPE_NFIELDS (union_type) = 1 + TYPE_NFIELDS (type);
+      TYPE_LENGTH (union_type) = TYPE_LENGTH (type);
+      TYPE_FIELDS (union_type)
+       = (struct field *) TYPE_ZALLOC (union_type,
+                                       (TYPE_NFIELDS (union_type)
+                                        * sizeof (struct field)));
+
+      memcpy (TYPE_FIELDS (union_type) + 1, TYPE_FIELDS (type),
+             TYPE_NFIELDS (type) * sizeof (struct field));
+
+      /* Install the discriminant at index 0 in the union.  */
+      TYPE_FIELD (union_type, 0) = *disr_field;
+      TYPE_FIELD_ARTIFICIAL (union_type, 0) = 1;
+      TYPE_FIELD_NAME (union_type, 0) = "<<discriminant>>";
+
+      /* Install the union in the outer struct type.  */
+      TYPE_FIELD_TYPE (type, 0) = union_type;
+      TYPE_FIELD_NAME (type, 0) = "<<variants>>";
+      TYPE_NFIELDS (type) = 1;
+
+      /* Set the size and offset of the union type.  */
+      SET_FIELD_BITPOS (TYPE_FIELD (type, 0), 0);
+
+      /* We need a way to find the correct discriminant given a
+        variant name.  For convenience we build a map here.  */
+      struct type *enum_type = FIELD_TYPE (*disr_field);
+      std::unordered_map<std::string, ULONGEST> discriminant_map;
+      for (int i = 0; i < TYPE_NFIELDS (enum_type); ++i)
+       {
+         if (TYPE_FIELD_LOC_KIND (enum_type, i) == FIELD_LOC_KIND_ENUMVAL)
+           {
+             const char *name
+               = rust_last_path_segment (TYPE_FIELD_NAME (enum_type, i));
+             discriminant_map[name] = TYPE_FIELD_ENUMVAL (enum_type, i);
+           }
+       }
+
+      int n_fields = TYPE_NFIELDS (union_type);
+      struct discriminant_info *disc
+       = alloc_discriminant_info (union_type, 0, -1);
+      /* Skip the discriminant here.  */
+      for (int i = 1; i < n_fields; ++i)
+       {
+         /* Find the final word in the name of this variant's type.
+            That name can be used to look up the correct
+            discriminant.  */
+         const char *variant_name
+           = rust_last_path_segment (TYPE_NAME (TYPE_FIELD_TYPE (union_type,
+                                                                 i)));
+
+         auto iter = discriminant_map.find (variant_name);
+         if (iter != discriminant_map.end ())
+           disc->discriminants[i] = iter->second;
+
+         /* Remove the discriminant field.  */
+         struct type *sub_type = TYPE_FIELD_TYPE (union_type, i);
+         --TYPE_NFIELDS (sub_type);
+         ++TYPE_FIELDS (sub_type);
+         TYPE_FIELD_NAME (union_type, i) = variant_name;
+         TYPE_NAME (sub_type)
+           = rust_fully_qualify (&objfile->objfile_obstack,
+                                 TYPE_NAME (type), variant_name);
+       }
+    }
+}
+
+/* Rewrite some Rust unions to be structures with variants parts.  */
+
+static void
+rust_union_quirks (struct dwarf2_cu *cu)
+{
+  gdb_assert (cu->language == language_rust);
+  for (struct type *type : cu->rust_unions)
+    quirk_rust_enum (type, cu->per_cu->dwarf2_per_objfile->objfile);
+}
+
 /* Return the symtab for PER_CU.  This works properly regardless of
    whether we're using the index or psymtabs.  */
 
@@ -10502,6 +10807,9 @@ process_full_comp_unit (struct dwarf2_per_cu_data *per_cu,
      physnames.  */
   compute_delayed_physnames (cu);
 
+  if (cu->language == language_rust)
+    rust_union_quirks (cu);
+
   /* Some compilers don't define a DW_AT_high_pc attribute for the
      compilation unit.  If the DW_AT_high_pc is missing, synthesize
      it, by scanning the DIE's below the compilation unit.  */
@@ -10604,6 +10912,9 @@ process_full_type_unit (struct dwarf2_per_cu_data *per_cu,
      physnames.  */
   compute_delayed_physnames (cu);
 
+  if (cu->language == language_rust)
+    rust_union_quirks (cu);
+
   /* TUs share symbol tables.
      If this is the first TU to use this symtab, complete the construction
      of it with end_expandable_symtab.  Otherwise, complete the addition of
@@ -16184,6 +16495,8 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu)
     }
 
   quirk_gcc_member_function_pointer (type, objfile);
+  if (cu->language == language_rust && die->tag == DW_TAG_union_type)
+    cu->rust_unions.push_back (type);
 
   /* NOTE: carlton/2004-03-16: GCC 3.4 (or at least one of its
      snapshots) has been known to create a die giving a declaration
This page took 0.036753 seconds and 4 git commands to generate.