Hi,

This works around a limitation of the DWARF description, which requires the
DW_AT_discr attribute of a DW_TAG_variant_part DIE to be the reference to a
DW_TAG_member DIE of the same record type.  Now, for a derived discriminated
tagged record type in Ada, the discriminant may be a component of the _Parent
component, in other words a sub-component of the record type.

Tested on x86-64/Linux, OK for the mainline?


2026-10-08  Eric Botcazou  <[email protected]>

        * dwarf2out.cc (gen_field_die): Change return type to DW_DIE_REF
        and add ANONYMOUS parameter.  Adjust implementation accordingly.
        (analyze_discr_in_predicate): Rename into...
        (extract_discr_in_expr): ...this.  Also return a sub-field of the
        record type when the containing fields are at offset 0.
        (analyze_variants_discr): Adjust to above renaming.
        (gen_variant_part): If there is no DIE for the discriminant, create
        an anonymous DIE for it in the non-variant part of the record type.

-- 
Eric Botcazou
diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index 4db0600a2ea..9736f7f3ed7 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -3957,7 +3957,8 @@ static void gen_const_die (tree, dw_die_ref);
 static void gen_label_die (tree, dw_die_ref);
 static void gen_lexical_block_die (tree, dw_die_ref);
 static void gen_inlined_subroutine_die (tree, dw_die_ref);
-static void gen_field_die (tree, struct vlr_context *, dw_die_ref);
+static dw_die_ref gen_field_die (tree, struct vlr_context *, dw_die_ref,
+				 bool = false);
 static void gen_ptr_to_mbr_type_die (tree, dw_die_ref);
 static dw_die_ref gen_compile_unit_die (const char *);
 static void gen_inheritance_die (tree, tree, tree, dw_die_ref);
@@ -25563,18 +25564,21 @@ gen_inlined_subroutine_die (tree stmt, dw_die_ref context_die)
 }
 
 /* Generate a DIE for a field in a record, or structure.  CTX is required: see
-   the comment for VLR_CONTEXT.  */
+   the comment for VLR_CONTEXT.  If ANONYMOUS is true, create an anonymous DIE
+   and do not equate it to the field.  */
 
-static void
-gen_field_die (tree decl, struct vlr_context *ctx, dw_die_ref context_die)
+static dw_die_ref
+gen_field_die (tree decl, struct vlr_context *ctx, dw_die_ref context_die,
+	       bool anonymous)
 {
   dw_die_ref decl_die;
 
   if (TREE_TYPE (decl) == error_mark_node)
-    return;
+    return NULL;
 
   decl_die = new_die (DW_TAG_member, context_die, decl);
-  add_name_and_src_coords_attributes (decl_die, decl);
+  if (!anonymous)
+    add_name_and_src_coords_attributes (decl_die, decl);
   add_type_attribute (decl_die, member_declared_type (decl), decl_quals (decl),
 		      TYPE_REVERSE_STORAGE_ORDER (DECL_FIELD_CONTEXT (decl)),
 		      context_die);
@@ -25608,7 +25612,9 @@ gen_field_die (tree decl, struct vlr_context *ctx, dw_die_ref context_die)
 	}
 
   /* Equate decl number to die, so that we can look up this decl later on.  */
-  equate_decl_number_to_die (decl, decl_die);
+  if (!anonymous)
+    equate_decl_number_to_die (decl, decl_die);
+  return decl_die;
 }
 
 /* Generate a DIE for a pointer to a member type.  TYPE can be an
@@ -25932,21 +25938,36 @@ is_variant_part (tree decl)
 	  && TREE_CODE (TREE_TYPE (decl)) == QUAL_UNION_TYPE);
 }
 
-/* Check that OPERAND is a reference to a field in STRUCT_TYPE.  If it is,
-   return the FIELD_DECL.  Return NULL_TREE otherwise.  */
+/* Check that OPERAND is a reference to a field or sub-field of STRUCT_TYPE
+   whose prefix is a PLACEHOLDER_EXPR and, in the case of a sub-field, check
+   that the containing fields are at offset 0.  If it is, return the field or
+   sub-field, or else return NULL_TREE.  */
 
 static tree
-analyze_discr_in_predicate (tree operand, tree struct_type)
+extract_discr_in_expr (tree operand, tree struct_type)
 {
   while (CONVERT_EXPR_P (operand))
     operand = TREE_OPERAND (operand, 0);
 
-  /* Match field access to members of struct_type only.  */
-  if (TREE_CODE (operand) == COMPONENT_REF
-      && TREE_CODE (TREE_OPERAND (operand, 0)) == PLACEHOLDER_EXPR
-      && TREE_TYPE (TREE_OPERAND (operand, 0)) == struct_type
-      && TREE_CODE (TREE_OPERAND (operand, 1)) == FIELD_DECL)
-    return TREE_OPERAND (operand, 1);
+  if (TREE_CODE (operand) != COMPONENT_REF)
+    return NULL_TREE;
+
+  tree op0 = TREE_OPERAND (operand, 0);
+  tree op1 = TREE_OPERAND (operand, 1);
+  tree field;
+
+  /* The direct case of a field of STRUCT_TYPE.  */
+  if (TREE_CODE (op0) == PLACEHOLDER_EXPR
+      && TREE_TYPE (op0) == struct_type
+      && TREE_CODE (op1) == FIELD_DECL)
+    return op1;
+
+  /* The case of a sub-field of STRUCT_TYPE with containing field at 0.  */
+  else if ((field = extract_discr_in_expr (op0, struct_type))
+	   && integer_zerop (DECL_FIELD_OFFSET (field)))
+    return op1;
+
+  /* Otherwise, neither field nor sub-field.  */
   else
     return NULL_TREE;
 }
@@ -26081,8 +26102,8 @@ analyze_variants_discr (tree variant_part_decl,
 	      tree integer_cst = TREE_OPERAND (match_expr, 1);
 
 	      candidate_discr
-	       = analyze_discr_in_predicate (TREE_OPERAND (match_expr, 0),
-					     struct_type);
+		= extract_discr_in_expr (TREE_OPERAND (match_expr, 0),
+					 struct_type);
 
 	      new_node = ggc_cleared_alloc<dw_discr_list_node> ();
 	      if (!get_discr_value (integer_cst,
@@ -26126,11 +26147,11 @@ analyze_variants_discr (tree variant_part_decl,
 		 is consistent with the same analysis in the second
 		 operand.  */
 	      candidate_discr_1
-	        = analyze_discr_in_predicate (TREE_OPERAND (lower_op, 0),
-					      struct_type);
+		= extract_discr_in_expr (TREE_OPERAND (lower_op, 0),
+					 struct_type);
 	      candidate_discr_2
-	        = analyze_discr_in_predicate (TREE_OPERAND (upper_op, 0),
-					      struct_type);
+		= extract_discr_in_expr (TREE_OPERAND (upper_op, 0),
+					 struct_type);
 	      if (candidate_discr_1 == candidate_discr_2)
 		candidate_discr = candidate_discr_1;
 	      else
@@ -26160,7 +26181,7 @@ analyze_variants_discr (tree variant_part_decl,
 	    }
 
 	  else if ((candidate_discr
-		      = analyze_discr_in_predicate (match_expr, struct_type))
+		      = extract_discr_in_expr (match_expr, struct_type))
 		   && (TREE_TYPE (candidate_discr) == boolean_type_node
 		       || TREE_TYPE (TREE_TYPE (candidate_discr))
 			  == boolean_type_node))
@@ -26207,9 +26228,9 @@ abort:
   *discr_decl = NULL_TREE;
 }
 
-/* Generate a DIE to represent VARIANT_PART_DECL, a variant part that is part
-   of STRUCT_TYPE, a record type.  This new DIE is emitted as the next child
-   under CONTEXT_DIE.
+/* Generate a DIE to represent VARIANT_PART_DECL, a variant part of the record
+   type VLR_CTX->STRUCT_TYPE.  This new DIE is emitted as the next child under
+   CONTEXT_DIE.
 
    Variant parts are supposed to be implemented as a FIELD_DECL whose type is a
    QUAL_UNION_TYPE: this is the VARIANT_PART_DECL parameter.  The members for
@@ -26251,12 +26272,20 @@ gen_variant_part (tree variant_part_decl, struct vlr_context *vlr_ctx,
     {
       dw_die_ref discr_die = lookup_decl_die (discr_decl);
 
-      if (discr_die)
-	add_AT_die_ref (variant_part_die, DW_AT_discr, discr_die);
-      else
-	/* We have no DIE for the discriminant, so just discard all
-	   discrimimant information in the output.  */
-	discr_decl = NULL_TREE;
+      /* If we have no DIE for the discriminant, this may be a sub-field of
+	 VLR_CTX->STRUCT_TYPE.  Generate an anonymous DIE for the sub-field
+	 in the non-variant part of VLR_CTX->STRUCT_TYPE.  This is necessary
+	 to work around a limitation of the DWARF description, which requires
+	 the DW_AT_discr attribute of the DW_TAG_variant_part DIE to be the
+	 reference to a DW_TAG_member DIE of the same record type.  */
+      if (!discr_die)
+	{
+	  vlr_ctx->variant_part_offset = NULL_TREE;
+	  discr_die = gen_field_die (discr_decl, vlr_ctx, context_die, true);
+	  vlr_ctx->variant_part_offset = variant_part_offset;
+	}
+
+       add_AT_die_ref (variant_part_die, DW_AT_discr, discr_die);
     }
 
   /* If the offset for this variant part is more complex than a constant,

Reply via email to