From: Ronan Desplanques <desplanq...@adacore.com> Before this patch, confirming Stream_Size aspect specifications on elementary types were incorrectly rejected when the stream size was 128, and the error messages emitted for Stream_Size aspect errors gave incorrect possible values.
This patch fixes this. The most significant part of the fix is a new subprogram in Exp_Strm, Get_Primitives, that makes it possible to retrieve a precise list of supported stream sizes, but also to select the right runtime streaming primitives for a given type. Using the latter, this patch factorizes code that was present in both Build_Elementary_Input_Call and Build_Elementary_Write_Call. gcc/ada/ChangeLog: * exp_strm.ads (Get_Primitives): New function. * exp_strm.adb (Get_Primitives): Likewise. (Build_Elementary_Input_Call, Build_Elementary_Write_Call): use Get_Primitives. (Has_Stream_Standard_Rep): Add formal parameter and rename to... (Is_Stream_Standard_Rep): New function. * sem_ch13.adb (Analyze_Attribute_Definition_Clause): Fix error emission. Tested on x86_64-pc-linux-gnu, committed on master. --- gcc/ada/exp_strm.adb | 447 +++++++++++++++---------------------------- gcc/ada/exp_strm.ads | 30 +++ gcc/ada/sem_ch13.adb | 74 ++++--- 3 files changed, 234 insertions(+), 317 deletions(-) diff --git a/gcc/ada/exp_strm.adb b/gcc/ada/exp_strm.adb index 5e1c9134fb57..3bb6966dc1c2 100644 --- a/gcc/ada/exp_strm.adb +++ b/gcc/ada/exp_strm.adb @@ -33,7 +33,6 @@ with Mutably_Tagged; use Mutably_Tagged; with Namet; use Namet; with Nlists; use Nlists; with Nmake; use Nmake; -with Rtsfind; use Rtsfind; with Sem_Aux; use Sem_Aux; with Sem_Util; use Sem_Util; with Sinfo; use Sinfo; @@ -43,7 +42,6 @@ with Snames; use Snames; with Stand; use Stand; with Tbuild; use Tbuild; with Ttypes; use Ttypes; -with Uintp; use Uintp; package body Exp_Strm is @@ -82,13 +80,13 @@ package body Exp_Strm is -- Decls and Stms are the declarations and statements for the body and -- The parameter Fnam is the name of the constructed function. - function Has_Stream_Standard_Rep (U_Type : Entity_Id) return Boolean; - -- This function is used to test the type U_Type, to determine if it has - -- a standard representation from a streaming point of view. Standard means - -- that it has a standard representation (e.g. no enumeration rep clause), - -- and the size of the root type is the same as the streaming size (which - -- is defined as value specified by a Stream_Size clause if present, or - -- the Esize of U_Type if not). + function Is_Stream_Standard_Rep + (U_Type : Entity_Id; S_Size : Uint) return Boolean; + -- This function is used to test the type U_Type, to determine whether it + -- would have a standard representation from a streaming point of view if + -- its Stream_Size attribute was set to S_Size. Standard means that it has + -- a standard representation (e.g. no enumeration rep clause), and the size + -- of the root type is the same as the stream size. function Make_Stream_Subprogram_Name (Loc : Source_Ptr; @@ -436,51 +434,39 @@ package body Exp_Strm is Build_Array_Read_Write_Procedure (Typ, Decl, Pnam, Name_Write); end Build_Array_Write_Procedure; - --------------------------------- - -- Build_Elementary_Input_Call -- - --------------------------------- + function Get_Primitives + (P_Type : Entity_Id; P_Size : Uint) return Primitive_Result + is - function Build_Elementary_Input_Call (N : Node_Id) return Node_Id is - Loc : constant Source_Ptr := Sloc (N); - P_Type : constant Entity_Id := Entity (Prefix (N)); - U_Type : constant Entity_Id := Underlying_Type (P_Type); - Rt_Type : constant Entity_Id := Root_Type (U_Type); - FST : constant Entity_Id := First_Subtype (U_Type); - Strm : constant Node_Id := First (Expressions (N)); - Targ : constant Node_Id := Next (Strm); - P_Size : constant Uint := Get_Stream_Size (FST); - Res : Node_Id; - Lib_RE : RE_Id; + function Prims (Input, Write : RE_Id) return Primitive_Result; + function Prims (Input, Write : RE_Id) return Primitive_Result is + begin + return (Primitives, 0, Input, Write); + end Prims; + function PSizes (L : Sizes) return Primitive_Result; + function PSizes (L : Sizes) return Primitive_Result is + begin + return (Possible_Sizes, L'Length, L); + end PSizes; + + U_Type : constant Entity_Id := Underlying_Type (P_Type); + FST : constant Entity_Id := First_Subtype (U_Type); + Rt_Type : constant Entity_Id := Root_Type (U_Type); + + Rep_Is_Standard : constant Boolean := + Known_RM_Size (U_Type) + and then Is_Stream_Standard_Rep (U_Type, P_Size); begin - -- Check first for Boolean and Character. These are enumeration types, - -- but we treat them specially, since they may require special handling - -- in the transfer protocol. However, this special handling only applies - -- if they have standard representation, otherwise they are treated like - -- any other enumeration type. - - if Rt_Type = Standard_Boolean - and then Has_Stream_Standard_Rep (U_Type) + if Rt_Type = Standard_Boolean and then Rep_Is_Standard then + return Prims (RE_I_B, RE_W_B); + elsif Rt_Type = Standard_Character and then Rep_Is_Standard then + return Prims (RE_I_C, RE_W_C); + elsif Rt_Type = Standard_Wide_Character and then Rep_Is_Standard then + return Prims (RE_I_WC, RE_W_WC); + elsif Rt_Type = Standard_Wide_Wide_Character and then Rep_Is_Standard then - Lib_RE := RE_I_B; - - elsif Rt_Type = Standard_Character - and then Has_Stream_Standard_Rep (U_Type) - then - Lib_RE := RE_I_C; - - elsif Rt_Type = Standard_Wide_Character - and then Has_Stream_Standard_Rep (U_Type) - then - Lib_RE := RE_I_WC; - - elsif Rt_Type = Standard_Wide_Wide_Character - and then Has_Stream_Standard_Rep (U_Type) - then - Lib_RE := RE_I_WWC; - - -- Floating point types - + return Prims (RE_I_WWC, RE_W_WWC); elsif Is_Floating_Point_Type (U_Type) then -- Question: should we use P_Size or Rt_Type to distinguish between @@ -500,23 +486,30 @@ package body Exp_Strm is -- To deal with these two requirements we add the special checks -- on equal sizes and use the root type to distinguish. - if P_Size <= Standard_Short_Float_Size + if P_Size = Standard_Short_Float_Size and then (Standard_Short_Float_Size /= Standard_Float_Size or else Rt_Type = Standard_Short_Float) then - Lib_RE := RE_I_SF; + return Prims (RE_I_SF, RE_W_SF); - elsif P_Size <= Standard_Float_Size then - Lib_RE := RE_I_F; + elsif P_Size = Standard_Float_Size then + return Prims (RE_I_F, RE_W_F); - elsif P_Size <= Standard_Long_Float_Size + elsif P_Size = Standard_Long_Float_Size and then (Standard_Long_Float_Size /= Standard_Long_Long_Float_Size - or else Rt_Type = Standard_Long_Float) + or else Rt_Type = Standard_Long_Float) then - Lib_RE := RE_I_LF; + return Prims (RE_I_LF, RE_W_LF); + elsif P_Size = Standard_Long_Long_Float_Size then + return Prims (RE_I_LLF, RE_W_LLF); else - Lib_RE := RE_I_LLF; + return + PSizes + ((Standard_Short_Float_Size, + Standard_Float_Size, + Standard_Long_Float_Size, + Standard_Long_Long_Float_Size)); end if; -- Signed integer types. Also includes signed fixed-point types and @@ -548,35 +541,42 @@ package body Exp_Strm is -- The following set of tests gets repeated many times, we should -- have an abstraction defined ??? - and then - (Is_Fixed_Point_Type (U_Type) - or else - Is_Enumeration_Type (U_Type) - or else - (Is_Signed_Integer_Type (U_Type) - and then not Has_Biased_Representation (FST))) + and then (Is_Fixed_Point_Type (U_Type) + or else Is_Enumeration_Type (U_Type) + or else (Is_Signed_Integer_Type (U_Type) + and then not Has_Biased_Representation (FST))) then - if P_Size <= Standard_Short_Short_Integer_Size then - Lib_RE := RE_I_SSI; + if P_Size = Standard_Short_Short_Integer_Size then + return Prims (RE_I_SSI, RE_W_SSI); - elsif P_Size <= Standard_Short_Integer_Size then - Lib_RE := RE_I_SI; + elsif P_Size = Standard_Short_Integer_Size then + return Prims (RE_I_SI, RE_W_SI); elsif P_Size = 24 then - Lib_RE := RE_I_I24; + return Prims (RE_I_I24, RE_W_I24); - elsif P_Size <= Standard_Integer_Size then - Lib_RE := RE_I_I; + elsif P_Size = Standard_Integer_Size then + return Prims (RE_I_I, RE_W_I); - elsif P_Size <= Standard_Long_Integer_Size then - Lib_RE := RE_I_LI; + elsif P_Size = Standard_Long_Integer_Size then + return Prims (RE_I_LI, RE_W_LI); - elsif P_Size <= Standard_Long_Long_Integer_Size then - Lib_RE := RE_I_LLI; + elsif P_Size = Standard_Long_Long_Integer_Size then + return Prims (RE_I_LLI, RE_W_LLI); + elsif P_Size = Standard_Long_Long_Long_Integer_Size then + return Prims (RE_I_LLLI, RE_W_LLLI); else - Lib_RE := RE_I_LLLI; + return + PSizes + ((Standard_Short_Short_Integer_Size, + Standard_Short_Integer_Size, + 24, + Standard_Integer_Size, + Standard_Long_Integer_Size, + Standard_Long_Long_Integer_Size, + Standard_Long_Long_Long_Integer_Size)); end if; -- Unsigned integer types, also includes unsigned fixed-point types @@ -586,41 +586,74 @@ package body Exp_Strm is -- Also includes signed integer types that are unsigned in the sense -- that they do not include negative numbers. See above for details. - elsif Is_Modular_Integer_Type (U_Type) - or else Is_Fixed_Point_Type (U_Type) - or else Is_Enumeration_Type (U_Type) + elsif Is_Modular_Integer_Type (U_Type) + or else Is_Fixed_Point_Type (U_Type) + or else Is_Enumeration_Type (U_Type) or else Is_Signed_Integer_Type (U_Type) then - if P_Size <= Standard_Short_Short_Integer_Size then - Lib_RE := RE_I_SSU; + if P_Size = Standard_Short_Short_Integer_Size then + return Prims (RE_I_SSU, RE_W_SSU); - elsif P_Size <= Standard_Short_Integer_Size then - Lib_RE := RE_I_SU; + elsif P_Size = Standard_Short_Integer_Size then + return Prims (RE_I_SU, RE_W_SU); elsif P_Size = 24 then - Lib_RE := RE_I_U24; + return Prims (RE_I_U24, RE_W_U24); - elsif P_Size <= Standard_Integer_Size then - Lib_RE := RE_I_U; + elsif P_Size = Standard_Integer_Size then + return Prims (RE_I_U, RE_W_U); - elsif P_Size <= Standard_Long_Integer_Size then - Lib_RE := RE_I_LU; + elsif P_Size = Standard_Long_Integer_Size then + return Prims (RE_I_LU, RE_W_LU); - elsif P_Size <= Standard_Long_Long_Integer_Size then - Lib_RE := RE_I_LLU; + elsif P_Size = Standard_Long_Long_Integer_Size then + return Prims (RE_I_LLU, RE_W_LLU); + + elsif P_Size = Standard_Long_Long_Long_Integer_Size then + return Prims (RE_I_LLLU, RE_W_LLLU); else - Lib_RE := RE_I_LLLU; + return + PSizes + ((Standard_Short_Short_Integer_Size, + Standard_Short_Integer_Size, + 24, + Standard_Integer_Size, + Standard_Long_Integer_Size, + Standard_Long_Long_Integer_Size, + Standard_Long_Long_Long_Integer_Size)); end if; else pragma Assert (Is_Access_Type (U_Type)); if Present (P_Size) and then P_Size > System_Address_Size then - Lib_RE := RE_I_AD; + return Prims (RE_I_AD, RE_W_AD); else - Lib_RE := RE_I_AS; + return Prims (RE_I_AS, RE_W_AS); end if; end if; + end Get_Primitives; + --------------------------------- + -- Build_Elementary_Input_Call -- + --------------------------------- + + function Build_Elementary_Input_Call (N : Node_Id) return Node_Id is + Loc : constant Source_Ptr := Sloc (N); + P_Type : constant Entity_Id := Entity (Prefix (N)); + U_Type : constant Entity_Id := Underlying_Type (P_Type); + FST : constant Entity_Id := First_Subtype (U_Type); + Strm : constant Node_Id := First (Expressions (N)); + Targ : constant Node_Id := Next (Strm); + P_Size : constant Uint := Get_Stream_Size (FST); + Res : Node_Id; + + Prims : constant Primitive_Result := Get_Primitives (P_Type, P_Size); + + Lib_RE : constant RE_Id := + (case Prims.S is + when Primitives => Prims.Input, + when others => raise Program_Error); + begin -- Call the function, and do an unchecked conversion of the result -- to the actual type of the prefix. If the target is a discriminant, -- and we are in the body of the default implementation of a 'Read @@ -679,191 +712,22 @@ package body Exp_Strm is function Build_Elementary_Write_Call (N : Node_Id) return Node_Id is Loc : constant Source_Ptr := Sloc (N); - P_Type : constant Entity_Id := Entity (Prefix (N)); - U_Type : constant Entity_Id := Underlying_Type (P_Type); - Rt_Type : constant Entity_Id := Root_Type (U_Type); - FST : constant Entity_Id := First_Subtype (U_Type); - Strm : constant Node_Id := First (Expressions (N)); - Item : constant Node_Id := Next (Strm); - P_Size : Uint; - Lib_RE : RE_Id; + P_Type : constant Entity_Id := Entity (Prefix (N)); + U_Type : constant Entity_Id := Underlying_Type (P_Type); + FST : constant Entity_Id := First_Subtype (U_Type); + Strm : constant Node_Id := First (Expressions (N)); + Item : constant Node_Id := Next (Strm); + P_Size : constant Uint := Get_Stream_Size (FST); Libent : Entity_Id; + Prims : constant Primitive_Result := Get_Primitives (P_Type, P_Size); + + Lib_RE : constant RE_Id := + (case Prims.S is + when Primitives => Prims.Write, + when others => raise Program_Error); begin - -- Compute the size of the stream element. This is either the size of - -- the first subtype or if given the size of the Stream_Size attribute. - - if Has_Stream_Size_Clause (FST) then - P_Size := Static_Integer (Expression (Stream_Size_Clause (FST))); - else - P_Size := Esize (FST); - end if; - - -- Find the routine to be called - - -- Check for First Boolean and Character. These are enumeration types, - -- but we treat them specially, since they may require special handling - -- in the transfer protocol. However, this special handling only applies - -- if they have standard representation, otherwise they are treated like - -- any other enumeration type. - - if Rt_Type = Standard_Boolean - and then Has_Stream_Standard_Rep (U_Type) - then - Lib_RE := RE_W_B; - - elsif Rt_Type = Standard_Character - and then Has_Stream_Standard_Rep (U_Type) - then - Lib_RE := RE_W_C; - - elsif Rt_Type = Standard_Wide_Character - and then Has_Stream_Standard_Rep (U_Type) - then - Lib_RE := RE_W_WC; - - elsif Rt_Type = Standard_Wide_Wide_Character - and then Has_Stream_Standard_Rep (U_Type) - then - Lib_RE := RE_W_WWC; - - -- Floating point types - - elsif Is_Floating_Point_Type (U_Type) then - - -- Question: should we use P_Size or Rt_Type to distinguish between - -- possible floating point types? If a non-standard size or a stream - -- size is specified, then we should certainly use the size. But if - -- we have two types the same (notably Short_Float_Size = Float_Size - -- which is close to universally true, and Long_Long_Float_Size = - -- Long_Float_Size, true on most targets except the x86), then we - -- would really rather use the root type, so that if people want to - -- fiddle with System.Stream_Attributes to get inter-target portable - -- streams, they get the size they expect. Consider in particular the - -- case of a stream written on an x86, with 96-bit Long_Long_Float - -- being read into a non-x86 target with 64 bit Long_Long_Float. A - -- special version of System.Stream_Attributes can deal with this - -- provided the proper type is always used. - - -- To deal with these two requirements we add the special checks - -- on equal sizes and use the root type to distinguish. - - if P_Size <= Standard_Short_Float_Size - and then (Standard_Short_Float_Size /= Standard_Float_Size - or else Rt_Type = Standard_Short_Float) - then - Lib_RE := RE_W_SF; - - elsif P_Size <= Standard_Float_Size then - Lib_RE := RE_W_F; - - elsif P_Size <= Standard_Long_Float_Size - and then (Standard_Long_Float_Size /= Standard_Long_Long_Float_Size - or else Rt_Type = Standard_Long_Float) - then - Lib_RE := RE_W_LF; - - else - Lib_RE := RE_W_LLF; - end if; - - -- Signed integer types. Also includes signed fixed-point types and - -- signed enumeration types share this circuitry. - - -- Note on signed integer types. We do not consider types as signed for - -- this purpose if they have no negative numbers, or if they have biased - -- representation. The reason is that the value in either case basically - -- represents an unsigned value. - - -- For example, consider: - - -- type W is range 0 .. 2**32 - 1; - -- for W'Size use 32; - - -- This is a signed type, but the representation is unsigned, and may - -- be outside the range of a 32-bit signed integer, so this must be - -- treated as 32-bit unsigned. - - -- Similarly, the representation is also unsigned if we have: - - -- type W is range -1 .. +254; - -- for W'Size use 8; - - -- forcing a biased and unsigned representation - - elsif not Is_Unsigned_Type (FST) - and then - (Is_Fixed_Point_Type (U_Type) - or else - Is_Enumeration_Type (U_Type) - or else - (Is_Signed_Integer_Type (U_Type) - and then not Has_Biased_Representation (FST))) - then - if P_Size <= Standard_Short_Short_Integer_Size then - Lib_RE := RE_W_SSI; - - elsif P_Size <= Standard_Short_Integer_Size then - Lib_RE := RE_W_SI; - - elsif P_Size = 24 then - Lib_RE := RE_W_I24; - - elsif P_Size <= Standard_Integer_Size then - Lib_RE := RE_W_I; - - elsif P_Size <= Standard_Long_Integer_Size then - Lib_RE := RE_W_LI; - - elsif P_Size <= Standard_Long_Long_Integer_Size then - Lib_RE := RE_W_LLI; - - else - Lib_RE := RE_W_LLLI; - end if; - - -- Unsigned integer types, also includes unsigned fixed-point types - -- and unsigned enumeration types (note we know they are unsigned - -- because we already tested for signed above). - - -- Also includes signed integer types that are unsigned in the sense - -- that they do not include negative numbers. See above for details. - - elsif Is_Modular_Integer_Type (U_Type) - or else Is_Fixed_Point_Type (U_Type) - or else Is_Enumeration_Type (U_Type) - or else Is_Signed_Integer_Type (U_Type) - then - if P_Size <= Standard_Short_Short_Integer_Size then - Lib_RE := RE_W_SSU; - - elsif P_Size <= Standard_Short_Integer_Size then - Lib_RE := RE_W_SU; - - elsif P_Size = 24 then - Lib_RE := RE_W_U24; - - elsif P_Size <= Standard_Integer_Size then - Lib_RE := RE_W_U; - - elsif P_Size <= Standard_Long_Integer_Size then - Lib_RE := RE_W_LU; - - elsif P_Size <= Standard_Long_Long_Integer_Size then - Lib_RE := RE_W_LLU; - - else - Lib_RE := RE_W_LLLU; - end if; - - else pragma Assert (Is_Access_Type (U_Type)); - - if Present (P_Size) and then P_Size > System_Address_Size then - Lib_RE := RE_W_AD; - else - Lib_RE := RE_W_AS; - end if; - end if; + pragma Assert (Prims.S = Primitives); -- Unchecked-convert parameter to the required type (i.e. the type of -- the corresponding parameter, and call the appropriate routine. @@ -871,12 +735,15 @@ package body Exp_Strm is Libent := RTE (Lib_RE); return - Make_Procedure_Call_Statement (Loc, - Name => New_Occurrence_Of (Libent, Loc), - Parameter_Associations => New_List ( - Relocate_Node (Strm), - Unchecked_Convert_To (Etype (Next_Formal (First_Formal (Libent))), - Relocate_Node (Item)))); + Make_Procedure_Call_Statement + (Loc, + Name => New_Occurrence_Of (Libent, Loc), + Parameter_Associations => + New_List + (Relocate_Node (Strm), + Unchecked_Convert_To + (Etype (Next_Formal (First_Formal (Libent))), + Relocate_Node (Item)))); end Build_Elementary_Write_Call; ----------------------------------------- @@ -1766,22 +1633,15 @@ package body Exp_Strm is -- Has_Stream_Standard_Rep -- ----------------------------- - function Has_Stream_Standard_Rep (U_Type : Entity_Id) return Boolean is - Siz : Uint; - + function Is_Stream_Standard_Rep + (U_Type : Entity_Id; S_Size : Uint) return Boolean is begin if Has_Non_Standard_Rep (U_Type) then return False; end if; - if Has_Stream_Size_Clause (U_Type) then - Siz := Static_Integer (Expression (Stream_Size_Clause (U_Type))); - else - Siz := Esize (First_Subtype (U_Type)); - end if; - - return Siz = Esize (Root_Type (U_Type)); - end Has_Stream_Standard_Rep; + return S_Size = Esize (Root_Type (U_Type)); + end Is_Stream_Standard_Rep; --------------------------------- -- Make_Stream_Subprogram_Name -- @@ -1827,5 +1687,4 @@ package body Exp_Strm is return Base_Type (E); end if; end Stream_Base_Type; - end Exp_Strm; diff --git a/gcc/ada/exp_strm.ads b/gcc/ada/exp_strm.ads index 733cf9c0dd19..54eded5d61df 100644 --- a/gcc/ada/exp_strm.ads +++ b/gcc/ada/exp_strm.ads @@ -26,7 +26,9 @@ -- Routines to build stream subprograms for composite types with Exp_Tss; use Exp_Tss; +with Rtsfind; use Rtsfind; with Types; use Types; +with Uintp; use Uintp; package Exp_Strm is @@ -138,4 +140,32 @@ package Exp_Strm is -- always null), and Pnam is the name of the constructed procedure. -- Used by Exp_Dist to generate stream-oriented attributes for RACWs. + type Status is (Primitives, Possible_Sizes); + + type Sizes is array (Positive range <>) of Nat; + + type Primitive_Result + (S : Status; + Len : Natural) + is record + case S is + when Primitives => + Input : RE_Id; + Write : RE_Id; + + when Possible_Sizes => + List : Sizes (1 .. Len); + end case; + end record; + + -------------------- + -- Get_Primitives -- + -------------------- + + function Get_Primitives + (P_Type : Entity_Id; P_Size : Uint) return Primitive_Result; + -- If P_Type supports a stream size of P_Size, returns the corresponding + -- input and write primitives. Otherwise, returns a list of the stream + -- sizes P_Type supports, in nondecreasing order and with possible + -- duplicates. end Exp_Strm; diff --git a/gcc/ada/sem_ch13.adb b/gcc/ada/sem_ch13.adb index 2166eb318d75..22fea0d02907 100644 --- a/gcc/ada/sem_ch13.adb +++ b/gcc/ada/sem_ch13.adb @@ -37,6 +37,7 @@ with Errid; use Errid; with Errout; use Errout; with Exp_Ch3; use Exp_Ch3; with Exp_Disp; use Exp_Disp; +with Exp_Strm; use Exp_Strm; with Exp_Tss; use Exp_Tss; with Exp_Util; use Exp_Util; with Expander; use Expander; @@ -8366,32 +8367,59 @@ package body Sem_Ch13 is if Duplicate_Clause then null; - + elsif No (Size) then + Error_Msg_N ("invalid argument for Stream_Size aspect", Nam); elsif Is_Elementary_Type (U_Ent) then - -- Size will be empty if we already detected an error - -- (e.g. Expr is of the wrong type); we might as well - -- give the useful hint below even in that case. - - if No (Size) or else - (Size /= System_Storage_Unit - and then Size /= System_Storage_Unit * 2 - and then Size /= System_Storage_Unit * 3 - and then Size /= System_Storage_Unit * 4 - and then Size /= System_Storage_Unit * 8) - then - Error_Msg_N - ("stream size for elementary type must be 8, 16, 24, " & - "32 or 64", N); - - elsif Known_RM_Size (U_Ent) and then RM_Size (U_Ent) > Size then - Error_Msg_Uint_1 := RM_Size (U_Ent); - Error_Msg_N - ("stream size for elementary type must be 8, 16, 24, " & - "32 or 64 and at least ^", N); - end if; - Set_Has_Stream_Size_Clause (U_Ent); + declare + Minimum_Size : constant Uint := + (if Known_RM_Size (U_Ent) + then RM_Size (U_Ent) + else Uint_0); + + Size_Or_Zero : constant Uint := + (if Size < Minimum_Size then Uint_0 else Size); + -- If the requested size is smaller than the RM size of the + -- type, we pass zero to Get_Primitives. That will always + -- give us the list of supported sizes we need to report an + -- error. + + P : constant Primitive_Result := + Get_Primitives (U_Ent, Size_Or_Zero); + + Error_Text : Bounded_String; + + In_First_Iteration : Boolean := True; + Previous_Value : Nat := 0; + begin + case P.S is + when Possible_Sizes => + Error_Msg_N ("unsupported stream size", N); + + Append + (Error_Text, + "\supported stream sizes for this type: "); + for Sz of P.List loop + if Minimum_Size <= Sz and then Sz /= Previous_Value + then + if In_First_Iteration then + In_First_Iteration := False; + else + Append (Error_Text, ", "); + end if; + + Append (Error_Text, Sz); + + Previous_Value := Sz; + end if; + end loop; + Error_Msg_N (To_String (Error_Text), N); + + when others => + null; + end case; + end; else Error_Msg_N ("Stream_Size cannot be given for &", Nam); end if; -- 2.43.0