This is a regression present on all active branches: the compiler enters an 
infinite recursion when it is generating the initialization procedure of an 
unchecked union type with a representation clause, because the internal layout 
it has done for it is problematic.  This comes from a kludge added to support 
aggregates for such types, so the fix is to further tweak this kludge.

Tested on x86-64/Linux, applied on all the active branches.


2026-02-28  Eric Botcazou  <[email protected]>

        PR ada/124285
        * gcc-interface/decl.cc (components_to_record): Force a packed
        layout for the innermost variant of an unchecked union type with
        fixed part and full representation clause.


2026-02-28  Eric Botcazou  <[email protected]>

        * gnat.dg/specs/unchecked_union3.ads: New test.

-- 
Eric Botcazou
diff --git a/gcc/ada/gcc-interface/decl.cc b/gcc/ada/gcc-interface/decl.cc
index 028fa0e5ea3..4ccc8d7a0e1 100644
--- a/gcc/ada/gcc-interface/decl.cc
+++ b/gcc/ada/gcc-interface/decl.cc
@@ -8513,7 +8513,7 @@ typedef struct vinfo
 
    DEBUG_INFO is true if we need to write debug information about the type.
 
-   IN_VARIANT is true if the componennt list is that of a variant.
+   IN_VARIANT is true if the component list is that of a variant.
 
    FIRST_FREE_POS, if nonzero, is the first (lowest) free field position in
    the outer record type down to this variant level.  It is nonzero only if
@@ -8630,7 +8630,7 @@ components_to_record (Node_Id gnat_component_list, Entity_Id gnat_record_type,
       tree gnu_union_type;
       tree this_first_free_pos, gnu_variant_list = NULL_TREE;
       bool union_field_needs_strict_alignment = false;
-      bool innermost_variant_level = true;
+      bool innermost_of_unchecked_union = false;
       auto_vec <vinfo_t, 16> variant_types;
       vinfo_t *gnu_variant;
       unsigned int variants_align = 0;
@@ -8679,15 +8679,19 @@ components_to_record (Node_Id gnat_component_list, Entity_Id gnat_record_type,
       /* For an unchecked union with a fixed part, we need to compute whether
 	 we are at the innermost level of the variant part.  */
       if (unchecked_union && gnu_field_list)
-	for (variant = First_Non_Pragma (Variants (gnat_variant_part));
-	     Present (variant);
-	     variant = Next_Non_Pragma (variant))
-	  if (Present (Component_List (variant))
-	      && Present (Variant_Part (Component_List (variant))))
-	    {
-	      innermost_variant_level = false;
-	      break;
-	    }
+	{
+	  innermost_of_unchecked_union = true;
+
+	  for (variant = First_Non_Pragma (Variants (gnat_variant_part));
+	       Present (variant);
+	       variant = Next_Non_Pragma (variant))
+	    if (Present (Component_List (variant))
+		&& Present (Variant_Part (Component_List (variant))))
+	      {
+		innermost_of_unchecked_union = false;
+		break;
+	      }
+	}
 
       /* We build the variants in two passes.  The bulk of the work is done in
 	 the first pass, that is to say translating the GNAT nodes, building
@@ -8734,17 +8738,17 @@ components_to_record (Node_Id gnat_component_list, Entity_Id gnat_record_type,
 	     the outer variant, so as to flatten the rep-ed layout as much as
 	     possible, the reason being that we cannot do any flattening when
 	     a subtype statically selects a variant later on, for example for
-	     an aggregate.  */
+	     an aggregate; in that case, we force a packed layout because the
+	     moved fields may overlap with packed bit-fields.  */
 	  has_rep
 	    = components_to_record (Component_List (variant), gnat_record_type,
-				    NULL_TREE, gnu_variant_type, packed,
+				    NULL_TREE, gnu_variant_type, packed ||
+				    (all_rep && innermost_of_unchecked_union),
 				    definition, !all_rep_and_size, all_rep,
 				    unchecked_union, true, needs_xv_encodings,
 				    true, this_first_free_pos,
 				    (all_rep || this_first_free_pos)
-				    && !(unchecked_union
-				         && gnu_field_list
-					 && innermost_variant_level)
+				    && !innermost_of_unchecked_union
 				    ? NULL : &gnu_rep_list);
 
 	  /* Translate the qualifier and annotate the GNAT node.  */
-- { dg-do compile }

with Interfaces;

package Unchecked_Union3 is

   type T_CAN_ID is mod 2**29 with
      Default_Value => 0,
      Size          => 29;

   type T_Node_ID is mod 2**7 with
      Size          => 7,
      Default_Value => 0;

   type T_Message_Type_ID is new Interfaces.Unsigned_16;

   type T_Service_Type_ID is new Interfaces.Unsigned_8;

   type T_Priority is mod 2**5 with
      Size          => 5,
      Default_Value => 0;

   type T_Format_Selector is
     (CAN_Fields,
      DroneCAN_Fields);

   type T_Frame_Selector is
     (Message_Frame,
      Anonymous_Frame,
      Service_Frame);

   type Unsigned_2 is mod 2**2 with
      Size          => 2,
      Default_Value => 0;

   type Unsigned_14 is mod 2**14 with
      Size          => 14,
      Default_Value => 0;

   type T_Header (Format_Selector : T_Format_Selector := DroneCAN_Fields;
      Frame_Selector              : T_Frame_Selector  := Message_Frame) is record
      case Format_Selector is
         when CAN_Fields =>
            CAN_ID : T_CAN_ID;

         when DroneCAN_Fields =>
            Source_Node_ID      : T_Node_ID;
            Service_Not_Message : Boolean := False;
            Priority            : T_Priority;

            case Frame_Selector is
               when Message_Frame =>
                  Message_Type_ID : T_Message_Type_ID;
               when Anonymous_Frame =>
                  Message_Type_ID_Lower_Bits : Unsigned_2;
                  Discriminator              : Unsigned_14;
               when Service_Frame =>
                  Destination_Node_ID  : T_Node_ID;
                  Request_Not_Response : Boolean := False;
                  Service_Type_ID      : T_Service_Type_ID;
            end case;
      end case;
   end record with
      Unchecked_Union,
      Size => 29;

   for T_Header use record
      CAN_ID                     at 0 range  0 .. 28;
      Source_Node_ID             at 0 range  0 ..  6;
      Service_Not_Message        at 0 range  7 ..  7;
      Priority                   at 0 range 24 .. 28;
      Message_Type_ID            at 0 range  8 .. 23;
      Message_Type_ID_Lower_Bits at 0 range  8 ..  9;
      Discriminator              at 0 range 10 .. 23;
      Destination_Node_ID        at 0 range  8 .. 14;
      Request_Not_Response       at 0 range 15 .. 15;
      Service_Type_ID            at 0 range 16 .. 23;
   end record;

end Unchecked_Union3;

Reply via email to