| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- E X P _ P U T _ I M A G E -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2020-2021, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Atree; use Atree; |
| with Einfo; use Einfo; |
| with Einfo.Entities; use Einfo.Entities; |
| with Einfo.Utils; use Einfo.Utils; |
| with Exp_Tss; use Exp_Tss; |
| with Exp_Util; |
| with Debug; use Debug; |
| with Lib; use Lib; |
| with Namet; use Namet; |
| with Nlists; use Nlists; |
| with Nmake; use Nmake; |
| with Opt; use Opt; |
| with Rtsfind; use Rtsfind; |
| with Sem_Aux; use Sem_Aux; |
| with Sem_Util; use Sem_Util; |
| with Sinfo; use Sinfo; |
| with Sinfo.Nodes; use Sinfo.Nodes; |
| with Sinfo.Utils; use Sinfo.Utils; |
| with Snames; use Snames; |
| with Stand; |
| with Tbuild; use Tbuild; |
| with Ttypes; use Ttypes; |
| with Uintp; use Uintp; |
| |
| package body Exp_Put_Image is |
| |
| Tagged_Put_Image_Enabled : Boolean renames Debug_Flag_Underscore_Z; |
| -- ???Set True to enable Put_Image for at least some tagged types |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| procedure Build_Put_Image_Proc |
| (Loc : Source_Ptr; |
| Typ : Entity_Id; |
| Decl : out Node_Id; |
| Pnam : Entity_Id; |
| Stms : List_Id); |
| -- Build an array or record Put_Image procedure. Stms is the list of |
| -- statements for the body and Pnam is the name of the constructed |
| -- procedure. (The declaration list is always null.) |
| |
| function Make_Put_Image_Name |
| (Loc : Source_Ptr; Typ : Entity_Id) return Entity_Id; |
| -- Return the entity that identifies the Put_Image subprogram for Typ. This |
| -- procedure deals with the difference between tagged types (where a single |
| -- subprogram associated with the type is generated) and all other cases |
| -- (where a subprogram is generated at the point of the attribute |
| -- reference). The Loc parameter is used as the Sloc of the created entity. |
| |
| function Put_Image_Base_Type (E : Entity_Id) return Entity_Id; |
| -- Returns the base type, except for an array type whose whose first |
| -- subtype is constrained, in which case it returns the first subtype. |
| |
| ------------------------------------- |
| -- Build_Array_Put_Image_Procedure -- |
| ------------------------------------- |
| |
| procedure Build_Array_Put_Image_Procedure |
| (Nod : Node_Id; |
| Typ : Entity_Id; |
| Decl : out Node_Id; |
| Pnam : out Entity_Id) |
| is |
| Loc : constant Source_Ptr := Sloc (Nod); |
| |
| function Wrap_In_Loop |
| (Stms : List_Id; |
| Dim : Pos; |
| Index_Subtype : Entity_Id; |
| Between_Proc : RE_Id) return Node_Id; |
| -- Wrap Stms in a loop and if statement of the form: |
| -- |
| -- if V'First (Dim) <= V'Last (Dim) then -- nonempty range? |
| -- declare |
| -- LDim : Index_Type_For_Dim := V'First (Dim); |
| -- begin |
| -- loop |
| -- Stms; |
| -- exit when LDim = V'Last (Dim); |
| -- Between_Proc (S); |
| -- LDim := Index_Type_For_Dim'Succ (LDim); |
| -- end loop; |
| -- end; |
| -- end if; |
| -- |
| -- This is called once per dimension, from inner to outer. |
| |
| function Wrap_In_Loop |
| (Stms : List_Id; |
| Dim : Pos; |
| Index_Subtype : Entity_Id; |
| Between_Proc : RE_Id) return Node_Id |
| is |
| Index : constant Entity_Id := |
| Make_Defining_Identifier |
| (Loc, Chars => New_External_Name ('L', Dim)); |
| Decl : constant Node_Id := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Index, |
| Object_Definition => |
| New_Occurrence_Of (Index_Subtype, Loc), |
| Expression => |
| Make_Attribute_Reference (Loc, |
| Prefix => Make_Identifier (Loc, Name_V), |
| Attribute_Name => Name_First, |
| Expressions => New_List ( |
| Make_Integer_Literal (Loc, Dim)))); |
| Loop_Stm : constant Node_Id := |
| Make_Implicit_Loop_Statement (Nod, Statements => Stms); |
| Exit_Stm : constant Node_Id := |
| Make_Exit_Statement (Loc, |
| Condition => |
| Make_Op_Eq (Loc, |
| Left_Opnd => New_Occurrence_Of (Index, Loc), |
| Right_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => |
| Make_Identifier (Loc, Name_V), |
| Attribute_Name => Name_Last, |
| Expressions => New_List ( |
| Make_Integer_Literal (Loc, Dim))))); |
| Increment : constant Node_Id := |
| Make_Increment (Loc, Index, Index_Subtype); |
| Between : constant Node_Id := |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (Between_Proc), Loc), |
| Parameter_Associations => New_List |
| (Make_Identifier (Loc, Name_S))); |
| Block : constant Node_Id := |
| Make_Block_Statement (Loc, |
| Declarations => New_List (Decl), |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => New_List (Loop_Stm))); |
| begin |
| Append_To (Stms, Exit_Stm); |
| Append_To (Stms, Between); |
| Append_To (Stms, Increment); |
| -- Note that we're appending to the Stms list passed in |
| |
| return |
| Make_If_Statement (Loc, |
| Condition => |
| Make_Op_Le (Loc, |
| Left_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => Make_Identifier (Loc, Name_V), |
| Attribute_Name => Name_First, |
| Expressions => New_List ( |
| Make_Integer_Literal (Loc, Dim))), |
| Right_Opnd => |
| Make_Attribute_Reference (Loc, |
| Prefix => Make_Identifier (Loc, Name_V), |
| Attribute_Name => Name_Last, |
| Expressions => New_List ( |
| Make_Integer_Literal (Loc, Dim)))), |
| Then_Statements => New_List (Block)); |
| end Wrap_In_Loop; |
| |
| Ndim : constant Pos := Number_Dimensions (Typ); |
| Ctyp : constant Entity_Id := Component_Type (Typ); |
| |
| Stm : Node_Id; |
| Exl : constant List_Id := New_List; |
| PI_Entity : Entity_Id; |
| |
| Indices : array (1 .. Ndim) of Entity_Id; |
| |
| -- Start of processing for Build_Array_Put_Image_Procedure |
| |
| begin |
| Pnam := |
| Make_Defining_Identifier (Loc, |
| Chars => Make_TSS_Name_Local (Typ, TSS_Put_Image)); |
| |
| -- Get the Indices |
| |
| declare |
| Index_Subtype : Node_Id := First_Index (Typ); |
| begin |
| for Dim in 1 .. Ndim loop |
| Indices (Dim) := Etype (Index_Subtype); |
| Next_Index (Index_Subtype); |
| end loop; |
| pragma Assert (No (Index_Subtype)); |
| end; |
| |
| -- Build the inner attribute call |
| |
| for Dim in 1 .. Ndim loop |
| Append_To (Exl, Make_Identifier (Loc, New_External_Name ('L', Dim))); |
| end loop; |
| |
| Stm := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Put_Image_Base_Type (Ctyp), Loc), |
| Attribute_Name => Name_Put_Image, |
| Expressions => New_List ( |
| Make_Identifier (Loc, Name_S), |
| Make_Indexed_Component (Loc, |
| Prefix => Make_Identifier (Loc, Name_V), |
| Expressions => Exl))); |
| |
| -- The corresponding attribute for the component type of the array might |
| -- be user-defined, and frozen after the array type. In that case, |
| -- freeze the Put_Image attribute of the component type, whose |
| -- declaration could not generate any additional freezing actions in any |
| -- case. |
| |
| PI_Entity := TSS (Base_Type (Ctyp), TSS_Put_Image); |
| |
| if Present (PI_Entity) and then not Is_Frozen (PI_Entity) then |
| Set_Is_Frozen (PI_Entity); |
| end if; |
| |
| -- Loop through the dimensions, innermost first, generating a loop for |
| -- each dimension. |
| |
| declare |
| Stms : List_Id := New_List (Stm); |
| begin |
| for Dim in reverse 1 .. Ndim loop |
| declare |
| New_Stms : constant List_Id := New_List; |
| Between_Proc : RE_Id; |
| begin |
| -- For a one-dimensional array of elementary type, use |
| -- RE_Simple_Array_Between. The same applies to the last |
| -- dimension of a multidimensional array. |
| |
| if Is_Elementary_Type (Ctyp) and then Dim = Ndim then |
| Between_Proc := RE_Simple_Array_Between; |
| else |
| Between_Proc := RE_Array_Between; |
| end if; |
| |
| Append_To (New_Stms, |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (RTE (RE_Array_Before), Loc), |
| Parameter_Associations => New_List |
| (Make_Identifier (Loc, Name_S)))); |
| |
| Append_To |
| (New_Stms, |
| Wrap_In_Loop (Stms, Dim, Indices (Dim), Between_Proc)); |
| |
| Append_To (New_Stms, |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (RTE (RE_Array_After), Loc), |
| Parameter_Associations => New_List |
| (Make_Identifier (Loc, Name_S)))); |
| |
| Stms := New_Stms; |
| end; |
| end loop; |
| |
| Build_Put_Image_Proc (Loc, Typ, Decl, Pnam, Stms); |
| end; |
| end Build_Array_Put_Image_Procedure; |
| |
| ------------------------------------- |
| -- Build_Elementary_Put_Image_Call -- |
| ------------------------------------- |
| |
| function Build_Elementary_Put_Image_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); |
| Sink : constant Node_Id := First (Expressions (N)); |
| Item : constant Node_Id := Next (Sink); |
| P_Size : constant Uint := Esize (FST); |
| Lib_RE : RE_Id; |
| |
| begin |
| if Is_Signed_Integer_Type (U_Type) then |
| if P_Size <= Standard_Integer_Size then |
| Lib_RE := RE_Put_Image_Integer; |
| elsif P_Size <= Standard_Long_Long_Integer_Size then |
| Lib_RE := RE_Put_Image_Long_Long_Integer; |
| else |
| pragma Assert (P_Size <= Standard_Long_Long_Long_Integer_Size); |
| Lib_RE := RE_Put_Image_Long_Long_Long_Integer; |
| end if; |
| |
| elsif Is_Modular_Integer_Type (U_Type) then |
| if P_Size <= Standard_Integer_Size then -- Yes, Integer |
| Lib_RE := RE_Put_Image_Unsigned; |
| elsif P_Size <= Standard_Long_Long_Integer_Size then |
| Lib_RE := RE_Put_Image_Long_Long_Unsigned; |
| else |
| pragma Assert (P_Size <= Standard_Long_Long_Long_Integer_Size); |
| Lib_RE := RE_Put_Image_Long_Long_Long_Unsigned; |
| end if; |
| |
| elsif Is_Access_Type (U_Type) then |
| if Is_Access_Protected_Subprogram_Type (Base_Type (U_Type)) then |
| Lib_RE := RE_Put_Image_Access_Prot_Subp; |
| elsif Is_Access_Subprogram_Type (Base_Type (U_Type)) then |
| Lib_RE := RE_Put_Image_Access_Subp; |
| elsif P_Size = System_Address_Size then |
| Lib_RE := RE_Put_Image_Thin_Pointer; |
| else |
| pragma Assert (P_Size = 2 * System_Address_Size); |
| Lib_RE := RE_Put_Image_Fat_Pointer; |
| end if; |
| |
| else |
| pragma Assert |
| (Is_Enumeration_Type (U_Type) or else Is_Real_Type (U_Type)); |
| |
| -- For other elementary types, generate: |
| -- |
| -- Put_Wide_Wide_String (Sink, U_Type'Wide_Wide_Image (Item)); |
| -- |
| -- It would be more elegant to do it the other way around (define |
| -- '[[Wide_]Wide_]Image in terms of 'Put_Image). But this is easier |
| -- to implement, because we already have support for |
| -- 'Wide_Wide_Image. Furthermore, we don't want to remove the |
| -- existing support for '[[Wide_]Wide_]Image, because we don't |
| -- currently plan to support 'Put_Image on restricted runtimes. |
| |
| -- We can't do this: |
| -- |
| -- Put_UTF_8 (Sink, U_Type'Image (Item)); |
| -- |
| -- because we need to generate UTF-8, but 'Image for enumeration |
| -- types uses the character encoding of the source file. |
| -- |
| -- Note that this is putting a leading space for reals. |
| |
| declare |
| Image : constant Node_Id := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (U_Type, Loc), |
| Attribute_Name => Name_Wide_Wide_Image, |
| Expressions => New_List (Relocate_Node (Item))); |
| Put_Call : constant Node_Id := |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Put_Wide_Wide_String), Loc), |
| Parameter_Associations => New_List |
| (Relocate_Node (Sink), Image)); |
| begin |
| return Put_Call; |
| end; |
| end if; |
| |
| -- Unchecked-convert parameter to the required type (i.e. the type of |
| -- the corresponding parameter), and call the appropriate routine. |
| -- We could use a normal type conversion for scalars, but the |
| -- "unchecked" is needed for access and private types. |
| |
| declare |
| Libent : constant Entity_Id := RTE (Lib_RE); |
| begin |
| return |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Libent, Loc), |
| Parameter_Associations => New_List ( |
| Relocate_Node (Sink), |
| Unchecked_Convert_To |
| (Etype (Next_Formal (First_Formal (Libent))), |
| Relocate_Node (Item)))); |
| end; |
| end Build_Elementary_Put_Image_Call; |
| |
| ------------------------------------- |
| -- Build_String_Put_Image_Call -- |
| ------------------------------------- |
| |
| function Build_String_Put_Image_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); |
| R : constant Entity_Id := Root_Type (U_Type); |
| Sink : constant Node_Id := First (Expressions (N)); |
| Item : constant Node_Id := Next (Sink); |
| Lib_RE : RE_Id; |
| use Stand; |
| begin |
| if R = Standard_String then |
| Lib_RE := RE_Put_Image_String; |
| elsif R = Standard_Wide_String then |
| Lib_RE := RE_Put_Image_Wide_String; |
| elsif R = Standard_Wide_Wide_String then |
| Lib_RE := RE_Put_Image_Wide_Wide_String; |
| else |
| raise Program_Error; |
| end if; |
| |
| -- Convert parameter to the required type (i.e. the type of the |
| -- corresponding parameter), and call the appropriate routine. |
| -- We set the Conversion_OK flag in case the type is private. |
| |
| declare |
| Libent : constant Entity_Id := RTE (Lib_RE); |
| Conv : constant Node_Id := |
| OK_Convert_To |
| (Etype (Next_Formal (First_Formal (Libent))), |
| Relocate_Node (Item)); |
| begin |
| return |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Libent, Loc), |
| Parameter_Associations => New_List ( |
| Relocate_Node (Sink), |
| Conv)); |
| end; |
| end Build_String_Put_Image_Call; |
| |
| ------------------------------------ |
| -- Build_Protected_Put_Image_Call -- |
| ------------------------------------ |
| |
| -- For "Protected_Type'Put_Image (S, Protected_Object)", build: |
| -- |
| -- Put_Image_Protected (S); |
| -- |
| -- The protected object is not passed. |
| |
| function Build_Protected_Put_Image_Call (N : Node_Id) return Node_Id is |
| Loc : constant Source_Ptr := Sloc (N); |
| Sink : constant Node_Id := First (Expressions (N)); |
| Lib_RE : constant RE_Id := RE_Put_Image_Protected; |
| Libent : constant Entity_Id := RTE (Lib_RE); |
| begin |
| return |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Libent, Loc), |
| Parameter_Associations => New_List ( |
| Relocate_Node (Sink))); |
| end Build_Protected_Put_Image_Call; |
| |
| ------------------------------------ |
| -- Build_Task_Put_Image_Call -- |
| ------------------------------------ |
| |
| -- For "Task_Type'Put_Image (S, Task_Object)", build: |
| -- |
| -- Put_Image_Task (S, Task_Object'Identity); |
| -- |
| -- The task object is not passed; its Task_Id is. |
| |
| function Build_Task_Put_Image_Call (N : Node_Id) return Node_Id is |
| Loc : constant Source_Ptr := Sloc (N); |
| Sink : constant Node_Id := First (Expressions (N)); |
| Item : constant Node_Id := Next (Sink); |
| Lib_RE : constant RE_Id := RE_Put_Image_Task; |
| Libent : constant Entity_Id := RTE (Lib_RE); |
| |
| Task_Id : constant Node_Id := |
| Make_Attribute_Reference (Loc, |
| Prefix => Relocate_Node (Item), |
| Attribute_Name => Name_Identity, |
| Expressions => No_List); |
| |
| begin |
| return |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Libent, Loc), |
| Parameter_Associations => New_List ( |
| Relocate_Node (Sink), |
| Task_Id)); |
| end Build_Task_Put_Image_Call; |
| |
| -------------------------------------- |
| -- Build_Record_Put_Image_Procedure -- |
| -------------------------------------- |
| |
| -- The form of the record Put_Image procedure is as shown by the |
| -- following example: |
| |
| -- procedure Put_Image (S : in out Sink'Class; V : Typ) is |
| -- begin |
| -- Component_Type'Put_Image (S, V.component); |
| -- Component_Type'Put_Image (S, V.component); |
| -- ... |
| -- Component_Type'Put_Image (S, V.component); |
| -- |
| -- case V.discriminant is |
| -- when choices => |
| -- Component_Type'Put_Image (S, V.component); |
| -- Component_Type'Put_Image (S, V.component); |
| -- ... |
| -- Component_Type'Put_Image (S, V.component); |
| -- |
| -- when choices => |
| -- Component_Type'Put_Image (S, V.component); |
| -- Component_Type'Put_Image (S, V.component); |
| -- ... |
| -- Component_Type'Put_Image (S, V.component); |
| -- ... |
| -- end case; |
| -- end Put_Image; |
| |
| procedure Build_Record_Put_Image_Procedure |
| (Loc : Source_Ptr; |
| Typ : Entity_Id; |
| Decl : out Node_Id; |
| Pnam : out Entity_Id) |
| is |
| Btyp : constant Entity_Id := Base_Type (Typ); |
| pragma Assert (not Is_Unchecked_Union (Btyp)); |
| |
| First_Time : Boolean := True; |
| |
| function Make_Component_List_Attributes (CL : Node_Id) return List_Id; |
| -- Returns a sequence of Component_Type'Put_Image attribute_references |
| -- to process the components that are referenced in the given component |
| -- list. Called for the main component list, and then recursively for |
| -- variants. |
| |
| function Make_Component_Attributes (Clist : List_Id) return List_Id; |
| -- Given Clist, a component items list, construct series of |
| -- Component_Type'Put_Image attribute_references for componentwise |
| -- processing of the corresponding components. Called for the |
| -- discriminants, and then from Make_Component_List_Attributes for each |
| -- list (including in variants). |
| |
| procedure Append_Component_Attr (Clist : List_Id; C : Entity_Id); |
| -- Given C, the entity for a discriminant or component, build a call to |
| -- Component_Type'Put_Image for the corresponding component value, and |
| -- append it onto Clist. Called from Make_Component_Attributes. |
| |
| function Make_Component_Name (C : Entity_Id) return Node_Id; |
| -- Create a call that prints "Comp_Name => " |
| |
| ------------------------------------ |
| -- Make_Component_List_Attributes -- |
| ------------------------------------ |
| |
| function Make_Component_List_Attributes (CL : Node_Id) return List_Id is |
| CI : constant List_Id := Component_Items (CL); |
| VP : constant Node_Id := Variant_Part (CL); |
| |
| Result : List_Id; |
| Alts : List_Id; |
| V : Node_Id; |
| DC : Node_Id; |
| DCH : List_Id; |
| D_Ref : Node_Id; |
| |
| begin |
| Result := Make_Component_Attributes (CI); |
| |
| if Present (VP) then |
| Alts := New_List; |
| |
| V := First_Non_Pragma (Variants (VP)); |
| while Present (V) loop |
| DCH := New_List; |
| |
| DC := First (Discrete_Choices (V)); |
| while Present (DC) loop |
| Append_To (DCH, New_Copy_Tree (DC)); |
| Next (DC); |
| end loop; |
| |
| Append_To (Alts, |
| Make_Case_Statement_Alternative (Loc, |
| Discrete_Choices => DCH, |
| Statements => |
| Make_Component_List_Attributes (Component_List (V)))); |
| Next_Non_Pragma (V); |
| end loop; |
| |
| -- Note: in the following, we use New_Occurrence_Of for the |
| -- selector, since there are cases in which we make a reference |
| -- to a hidden discriminant that is not visible. |
| |
| D_Ref := |
| Make_Selected_Component (Loc, |
| Prefix => Make_Identifier (Loc, Name_V), |
| Selector_Name => |
| New_Occurrence_Of (Entity (Name (VP)), Loc)); |
| |
| Append_To (Result, |
| Make_Case_Statement (Loc, |
| Expression => D_Ref, |
| Alternatives => Alts)); |
| end if; |
| |
| return Result; |
| end Make_Component_List_Attributes; |
| |
| -------------------------------- |
| -- Append_Component_Attr -- |
| -------------------------------- |
| |
| procedure Append_Component_Attr (Clist : List_Id; C : Entity_Id) is |
| Component_Typ : constant Entity_Id := Put_Image_Base_Type (Etype (C)); |
| begin |
| if Ekind (C) /= E_Void then |
| Append_To (Clist, |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (Component_Typ, Loc), |
| Attribute_Name => Name_Put_Image, |
| Expressions => New_List ( |
| Make_Identifier (Loc, Name_S), |
| Make_Selected_Component (Loc, |
| Prefix => Make_Identifier (Loc, Name_V), |
| Selector_Name => New_Occurrence_Of (C, Loc))))); |
| end if; |
| end Append_Component_Attr; |
| |
| ------------------------------- |
| -- Make_Component_Attributes -- |
| ------------------------------- |
| |
| function Make_Component_Attributes (Clist : List_Id) return List_Id is |
| Item : Node_Id; |
| Result : List_Id; |
| |
| begin |
| Result := New_List; |
| |
| if Present (Clist) then |
| Item := First (Clist); |
| |
| -- Loop through components, skipping all internal components, |
| -- which are not part of the value (e.g. _Tag), except that we |
| -- don't skip the _Parent, since we do want to process that |
| -- recursively. If _Parent is an interface type, being abstract |
| -- with no components there is no need to handle it. |
| |
| while Present (Item) loop |
| if Nkind (Item) in |
| N_Component_Declaration | N_Discriminant_Specification |
| and then |
| ((Chars (Defining_Identifier (Item)) = Name_uParent |
| and then not Is_Interface |
| (Etype (Defining_Identifier (Item)))) |
| or else |
| not Is_Internal_Name (Chars (Defining_Identifier (Item)))) |
| then |
| if First_Time then |
| First_Time := False; |
| else |
| Append_To (Result, |
| Make_Procedure_Call_Statement (Loc, |
| Name => |
| New_Occurrence_Of (RTE (RE_Record_Between), Loc), |
| Parameter_Associations => New_List |
| (Make_Identifier (Loc, Name_S)))); |
| end if; |
| |
| Append_To (Result, Make_Component_Name (Item)); |
| Append_Component_Attr (Result, Defining_Identifier (Item)); |
| end if; |
| |
| Next (Item); |
| end loop; |
| end if; |
| |
| return Result; |
| end Make_Component_Attributes; |
| |
| ------------------------- |
| -- Make_Component_Name -- |
| ------------------------- |
| |
| function Make_Component_Name (C : Entity_Id) return Node_Id is |
| Name : constant Name_Id := Chars (Defining_Identifier (C)); |
| begin |
| return |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (RTE (RE_Put_UTF_8), Loc), |
| Parameter_Associations => New_List |
| (Make_Identifier (Loc, Name_S), |
| Make_String_Literal (Loc, Get_Name_String (Name) & " => "))); |
| end Make_Component_Name; |
| |
| Stms : constant List_Id := New_List; |
| Rdef : Node_Id; |
| Type_Decl : constant Node_Id := |
| Declaration_Node (Base_Type (Underlying_Type (Btyp))); |
| |
| -- Start of processing for Build_Record_Put_Image_Procedure |
| |
| begin |
| Append_To (Stms, |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (RTE (RE_Record_Before), Loc), |
| Parameter_Associations => New_List |
| (Make_Identifier (Loc, Name_S)))); |
| |
| -- Generate Put_Images for the discriminants of the type |
| |
| Append_List_To (Stms, |
| Make_Component_Attributes (Discriminant_Specifications (Type_Decl))); |
| |
| Rdef := Type_Definition (Type_Decl); |
| |
| -- In the record extension case, the components we want, including the |
| -- _Parent component representing the parent type, are to be found in |
| -- the extension. We will process the _Parent component using the type |
| -- of the parent. |
| |
| if Nkind (Rdef) = N_Derived_Type_Definition then |
| Rdef := Record_Extension_Part (Rdef); |
| end if; |
| |
| if Present (Component_List (Rdef)) then |
| Append_List_To (Stms, |
| Make_Component_List_Attributes (Component_List (Rdef))); |
| end if; |
| |
| Append_To (Stms, |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (RTE (RE_Record_After), Loc), |
| Parameter_Associations => New_List |
| (Make_Identifier (Loc, Name_S)))); |
| |
| Pnam := Make_Put_Image_Name (Loc, Btyp); |
| Build_Put_Image_Proc (Loc, Btyp, Decl, Pnam, Stms); |
| end Build_Record_Put_Image_Procedure; |
| |
| ------------------------------- |
| -- Build_Put_Image_Profile -- |
| ------------------------------- |
| |
| function Build_Put_Image_Profile |
| (Loc : Source_Ptr; Typ : Entity_Id) return List_Id |
| is |
| begin |
| return New_List ( |
| Make_Parameter_Specification (Loc, |
| Defining_Identifier => Make_Defining_Identifier (Loc, Name_S), |
| In_Present => True, |
| Out_Present => True, |
| Parameter_Type => |
| New_Occurrence_Of (Class_Wide_Type (RTE (RE_Sink)), Loc)), |
| |
| Make_Parameter_Specification (Loc, |
| Defining_Identifier => Make_Defining_Identifier (Loc, Name_V), |
| Parameter_Type => New_Occurrence_Of (Typ, Loc))); |
| end Build_Put_Image_Profile; |
| |
| -------------------------- |
| -- Build_Put_Image_Proc -- |
| -------------------------- |
| |
| procedure Build_Put_Image_Proc |
| (Loc : Source_Ptr; |
| Typ : Entity_Id; |
| Decl : out Node_Id; |
| Pnam : Entity_Id; |
| Stms : List_Id) |
| is |
| Spec : constant Node_Id := |
| Make_Procedure_Specification (Loc, |
| Defining_Unit_Name => Pnam, |
| Parameter_Specifications => Build_Put_Image_Profile (Loc, Typ)); |
| begin |
| Decl := |
| Make_Subprogram_Body (Loc, |
| Specification => Spec, |
| Declarations => Empty_List, |
| Handled_Statement_Sequence => |
| Make_Handled_Sequence_Of_Statements (Loc, |
| Statements => Stms)); |
| end Build_Put_Image_Proc; |
| |
| ------------------------------------ |
| -- Build_Unknown_Put_Image_Call -- |
| ------------------------------------ |
| |
| function Build_Unknown_Put_Image_Call (N : Node_Id) return Node_Id is |
| Loc : constant Source_Ptr := Sloc (N); |
| Sink : constant Node_Id := First (Expressions (N)); |
| Lib_RE : constant RE_Id := RE_Put_Image_Unknown; |
| Libent : constant Entity_Id := RTE (Lib_RE); |
| begin |
| return |
| Make_Procedure_Call_Statement (Loc, |
| Name => New_Occurrence_Of (Libent, Loc), |
| Parameter_Associations => New_List ( |
| Relocate_Node (Sink), |
| Make_String_Literal (Loc, |
| Exp_Util.Fully_Qualified_Name_String ( |
| Entity (Prefix (N)), Append_NUL => False)))); |
| end Build_Unknown_Put_Image_Call; |
| |
| ---------------------- |
| -- Enable_Put_Image -- |
| ---------------------- |
| |
| function Enable_Put_Image (Typ : Entity_Id) return Boolean is |
| begin |
| -- There's a bit of a chicken&egg problem. The compiler is likely to |
| -- have trouble if we refer to the Put_Image of Sink itself, because |
| -- Sink is part of the parameter profile: |
| -- |
| -- function Sink'Put_Image (S : in out Sink'Class; V : T); |
| -- |
| -- Likewise, the Ada.Strings.Text_Output package, where Sink is |
| -- declared, depends on various other packages, so if we refer to |
| -- Put_Image of types declared in those other packages, we could create |
| -- cyclic dependencies. Therefore, we disable Put_Image for some |
| -- types. It's not clear exactly what types should be disabled. Scalar |
| -- types are OK, even if predefined, because calls to Put_Image of |
| -- scalar types are expanded inline. We certainly want to be able to use |
| -- Integer'Put_Image, for example. |
| |
| -- ???Temporarily disable to work around bugs: |
| -- |
| -- Put_Image does not work for Remote_Types. We check the containing |
| -- package, rather than the type itself, because we want to include |
| -- types in the private part of a Remote_Types package. |
| -- |
| -- Put_Image on tagged types triggers some bugs. |
| |
| if Is_Remote_Types (Scope (Typ)) |
| or else (Is_Tagged_Type (Typ) and then In_Predefined_Unit (Typ)) |
| or else (Is_Tagged_Type (Typ) and then not Tagged_Put_Image_Enabled) |
| then |
| return False; |
| end if; |
| |
| -- End of workarounds. |
| |
| -- No sense in generating code for Put_Image if there are errors. This |
| -- avoids certain cascade errors. |
| |
| if Total_Errors_Detected > 0 then |
| return False; |
| end if; |
| |
| -- If type Sink is unavailable in this runtime, disable Put_Image |
| -- altogether. |
| |
| if No_Run_Time_Mode or else not RTE_Available (RE_Sink) then |
| return False; |
| end if; |
| |
| -- ???Disable Put_Image on type Sink declared in |
| -- Ada.Strings.Text_Output. Note that we can't call Is_RTU on |
| -- Ada_Strings_Text_Output, because it's not known yet (we might be |
| -- compiling it). But this is insufficient to allow support for tagged |
| -- predefined types. |
| |
| declare |
| Parent_Scope : constant Entity_Id := Scope (Scope (Typ)); |
| begin |
| if Present (Parent_Scope) |
| and then Is_RTU (Parent_Scope, Ada_Strings) |
| and then Chars (Scope (Typ)) = Name_Find ("text_output") |
| then |
| return False; |
| end if; |
| end; |
| |
| -- Disable for CPP types, because the components are unavailable on the |
| -- Ada side. |
| |
| if Is_Tagged_Type (Typ) |
| and then Convention (Typ) = Convention_CPP |
| and then Is_CPP_Class (Root_Type (Typ)) |
| then |
| return False; |
| end if; |
| |
| -- Disable for unchecked unions, because there is no way to know the |
| -- discriminant value, and therefore no way to know which components |
| -- should be printed. |
| |
| if Is_Unchecked_Union (Typ) then |
| return False; |
| end if; |
| |
| return True; |
| end Enable_Put_Image; |
| |
| --------------------------------- |
| -- Make_Put_Image_Name -- |
| --------------------------------- |
| |
| function Make_Put_Image_Name |
| (Loc : Source_Ptr; Typ : Entity_Id) return Entity_Id |
| is |
| Sname : Name_Id; |
| begin |
| -- For tagged types, we are dealing with a TSS associated with the |
| -- declaration, so we use the standard primitive function name. For |
| -- other types, generate a local TSS name since we are generating |
| -- the subprogram at the point of use. |
| |
| if Is_Tagged_Type (Typ) then |
| Sname := Make_TSS_Name (Typ, TSS_Put_Image); |
| else |
| Sname := Make_TSS_Name_Local (Typ, TSS_Put_Image); |
| end if; |
| |
| return Make_Defining_Identifier (Loc, Sname); |
| end Make_Put_Image_Name; |
| |
| function Image_Should_Call_Put_Image (N : Node_Id) return Boolean is |
| begin |
| if Ada_Version < Ada_2020 then |
| return False; |
| end if; |
| |
| -- In Ada 2020, T'Image calls T'Put_Image if there is an explicit |
| -- aspect_specification for Put_Image, or if U_Type'Image is illegal |
| -- in pre-2020 versions of Ada. |
| |
| declare |
| U_Type : constant Entity_Id := Underlying_Type (Entity (Prefix (N))); |
| begin |
| if Present (TSS (U_Type, TSS_Put_Image)) then |
| return True; |
| end if; |
| |
| return not Is_Scalar_Type (U_Type); |
| end; |
| end Image_Should_Call_Put_Image; |
| |
| function Build_Image_Call (N : Node_Id) return Node_Id is |
| -- For T'Image (X) Generate an Expression_With_Actions node: |
| -- |
| -- do |
| -- S : Buffer := New_Buffer; |
| -- U_Type'Put_Image (S, X); |
| -- Result : constant String := Get (S); |
| -- Destroy (S); |
| -- in Result end |
| -- |
| -- where U_Type is the underlying type, as needed to bypass privacy. |
| |
| Loc : constant Source_Ptr := Sloc (N); |
| U_Type : constant Entity_Id := Underlying_Type (Entity (Prefix (N))); |
| Sink_Entity : constant Entity_Id := |
| Make_Defining_Identifier (Loc, Chars => New_Internal_Name ('S')); |
| Sink_Decl : constant Node_Id := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Sink_Entity, |
| Object_Definition => |
| New_Occurrence_Of (RTE (RE_Buffer), Loc), |
| Expression => |
| Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (RTE (RE_New_Buffer), Loc), |
| Parameter_Associations => Empty_List)); |
| Put_Im : constant Node_Id := |
| Make_Attribute_Reference (Loc, |
| Prefix => New_Occurrence_Of (U_Type, Loc), |
| Attribute_Name => Name_Put_Image, |
| Expressions => New_List ( |
| New_Occurrence_Of (Sink_Entity, Loc), |
| New_Copy_Tree (First (Expressions (N))))); |
| Result_Entity : constant Entity_Id := |
| Make_Defining_Identifier (Loc, Chars => New_Internal_Name ('R')); |
| Result_Decl : constant Node_Id := |
| Make_Object_Declaration (Loc, |
| Defining_Identifier => Result_Entity, |
| Object_Definition => |
| New_Occurrence_Of (Stand.Standard_String, Loc), |
| Expression => |
| Make_Function_Call (Loc, |
| Name => New_Occurrence_Of (RTE (RE_Get), Loc), |
| Parameter_Associations => New_List ( |
| New_Occurrence_Of (Sink_Entity, Loc)))); |
| Image : constant Node_Id := |
| Make_Expression_With_Actions (Loc, |
| Actions => New_List (Sink_Decl, Put_Im, Result_Decl), |
| Expression => New_Occurrence_Of (Result_Entity, Loc)); |
| begin |
| return Image; |
| end Build_Image_Call; |
| |
| ------------------ |
| -- Preload_Sink -- |
| ------------------ |
| |
| procedure Preload_Sink (Compilation_Unit : Node_Id) is |
| begin |
| -- We can't call RTE (RE_Sink) for at least some predefined units, |
| -- because it would introduce cyclic dependences. The package where Sink |
| -- is declared, for example, and things it depends on. |
| -- |
| -- It's only needed for tagged types, so don't do it unless Put_Image is |
| -- enabled for tagged types, and we've seen a tagged type. Note that |
| -- Tagged_Seen is set True by the parser if the "tagged" reserved word |
| -- is seen; this flag tells us whether we have any tagged types. |
| -- It's unfortunate to have this Tagged_Seen processing so scattered |
| -- about, but we need to know if there are tagged types where this is |
| -- called in Analyze_Compilation_Unit, before we have analyzed any type |
| -- declarations. This mechanism also prevents doing RTE (RE_Sink) when |
| -- compiling the compiler itself. Packages Ada.Strings.Text_Output and |
| -- friends are not included in the compiler. |
| -- |
| -- Don't do it if type Sink is unavailable in the runtime. |
| |
| if not In_Predefined_Unit (Compilation_Unit) |
| and then Tagged_Put_Image_Enabled |
| and then Tagged_Seen |
| and then not No_Run_Time_Mode |
| and then RTE_Available (RE_Sink) |
| then |
| declare |
| Ignore : constant Entity_Id := RTE (RE_Sink); |
| begin |
| null; |
| end; |
| end if; |
| end Preload_Sink; |
| |
| ------------------------- |
| -- Put_Image_Base_Type -- |
| ------------------------- |
| |
| function Put_Image_Base_Type (E : Entity_Id) return Entity_Id is |
| begin |
| if Is_Array_Type (E) and then Is_First_Subtype (E) then |
| return E; |
| else |
| return Base_Type (E); |
| end if; |
| end Put_Image_Base_Type; |
| |
| end Exp_Put_Image; |