Fix corner case issue with discriminated record type
authorEric Botcazou <ebotcazou@adacore.com>
Mon, 7 Dec 2020 09:30:05 +0000 (10:30 +0100)
committerEric Botcazou <ebotcazou@adacore.com>
Mon, 7 Dec 2020 09:30:05 +0000 (10:30 +0100)
The compiler generates code that writes too much data into a component
of a record subject to a representation clause, when the source of the
assignment is a call to a function that returns a discriminated record
type with default discriminants, variable size and a statically known
upper bound for this size, and the size of the component given by the
representation clause is lower than the value of this bound rounded up
to the alignment.

gcc/ada/ChangeLog:
* gcc-interface/trans.c (Call_to_gnu): Also create a temporary for
the return value if the LHS is a bit-field and the return type is
a type padding a self-referential type.
(gnat_to_gnu): Do not remove the padding on the result if it is too
small with regard to the natural padding size.

gcc/ada/gcc-interface/trans.c

index 0eec1788e05db87ddf1b1d04914c07f0182bba48..07e5a285b2b0ead1cbcc4bba354707cdfcc1b691 100644 (file)
@@ -4513,7 +4513,11 @@ Call_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, tree gnu_target,
          and the return type has variable size, because the gimplifier
          doesn't handle these cases.
 
-       4. There is no target and we have misaligned In Out or Out parameters
+       4. There is a target which is a bit-field and the function returns an
+         unconstrained record type with default discriminant, because the
+         return may copy more data than the bit-field can contain.
+
+       5. There is no target and we have misaligned In Out or Out parameters
          passed by reference, because we need to preserve the return value
          before copying back the parameters.  However, in this case, we'll
          defer creating the temporary, see below.
@@ -4536,7 +4540,11 @@ Call_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, tree gnu_target,
                  || (TREE_CODE (TREE_TYPE (gnu_target)) == ARRAY_TYPE
                      && TREE_CODE (TYPE_SIZE (TREE_TYPE (gnu_target)))
                         == INTEGER_CST))
-             && TREE_CODE (TYPE_SIZE (gnu_result_type)) != INTEGER_CST)))
+             && TREE_CODE (TYPE_SIZE (gnu_result_type)) != INTEGER_CST)
+         || (gnu_target
+             && TREE_CODE (gnu_target) == COMPONENT_REF
+             && DECL_BIT_FIELD (TREE_OPERAND (gnu_target, 1))
+             && type_is_padding_self_referential (gnu_result_type))))
     {
       gnu_retval = create_temporary ("R", gnu_result_type);
       DECL_RETURN_VALUE_P (gnu_retval) = 1;
@@ -8249,8 +8257,10 @@ gnat_to_gnu (Node_Id gnat_node)
       /* Remove padding only if the inner object is of self-referential
         size: in that case it must be an object of unconstrained type
         with a default discriminant and we want to avoid copying too
-        much data.  */
-      if (type_is_padding_self_referential (TREE_TYPE (gnu_result)))
+        much data.  But do not remove it if it is already too small.  */
+      if (type_is_padding_self_referential (TREE_TYPE (gnu_result))
+         && !(TREE_CODE (gnu_result) == COMPONENT_REF
+              && DECL_BIT_FIELD (TREE_OPERAND (gnu_result, 1))))
        gnu_result = convert (TREE_TYPE (TYPE_FIELDS (TREE_TYPE (gnu_result))),
                              gnu_result);
     }