1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 1997-2023, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 ------------------------------------------------------------------------------
26 with Atree
; use Atree
;
27 with Einfo
; use Einfo
;
28 with Einfo
.Entities
; use Einfo
.Entities
;
29 with Einfo
.Utils
; use Einfo
.Utils
;
30 with Errout
; use Errout
;
32 with Namet
; use Namet
;
33 with Nlists
; use Nlists
;
36 with Sem_Aux
; use Sem_Aux
;
37 with Sem_Prag
; use Sem_Prag
;
38 with Sem_Util
; use Sem_Util
;
39 with Sinput
; use Sinput
;
40 with Sinfo
; use Sinfo
;
41 with Sinfo
.Nodes
; use Sinfo
.Nodes
;
42 with Snames
; use Snames
;
43 with Stand
; use Stand
;
44 with Stringt
; use Stringt
;
47 with GNAT
.HTable
; use GNAT
.HTable
;
49 package body Sem_Elim
is
51 No_Elimination
: Boolean;
52 -- Set True if no Eliminate pragmas active
58 -- A single pragma Eliminate is represented by the following record
61 type Access_Elim_Data
is access Elim_Data
;
63 type Names
is array (Nat
range <>) of Name_Id
;
64 -- Type used to represent set of names. Used for names in Unit_Name
65 -- and also the set of names in Argument_Types.
67 type Access_Names
is access Names
;
69 type Elim_Data
is record
71 Unit_Name
: Access_Names
;
72 -- Unit name, broken down into a set of names (e.g. A.B.C is
73 -- represented as Name_Id values for A, B, C in sequence).
75 Entity_Name
: Name_Id
;
76 -- Entity name if Entity parameter if present. If no Entity parameter
77 -- was supplied, then Entity_Node is set to Empty, and the Entity_Name
78 -- field contains the last identifier name in the Unit_Name.
80 Entity_Scope
: Access_Names
;
81 -- Static scope of the entity within the compilation unit represented by
84 Entity_Node
: Node_Id
;
85 -- Save node of entity argument, for posting error messages. Set
86 -- to Empty if there is no entity argument.
88 Parameter_Types
: Access_Names
;
89 -- Set to set of names given for parameter types. If no parameter
90 -- types argument is present, this argument is set to null.
92 Result_Type
: Name_Id
;
93 -- Result type name if Result_Types parameter present, No_Name if not
95 Source_Location
: Name_Id
;
96 -- String describing the source location of subprogram defining name if
97 -- Source_Location parameter present, No_Name if not
99 Hash_Link
: Access_Elim_Data
;
100 -- Link for hash table use
102 Homonym
: Access_Elim_Data
;
103 -- Pointer to next entry with same key
106 -- Node_Id for Eliminate pragma
114 -- Setup hash table using the Entity_Name field as the hash key
116 subtype Element
is Elim_Data
;
117 subtype Elmt_Ptr
is Access_Elim_Data
;
119 subtype Key
is Name_Id
;
121 type Header_Num
is range 0 .. 1023;
123 Null_Ptr
: constant Elmt_Ptr
:= null;
125 ----------------------
126 -- Hash_Subprograms --
127 ----------------------
129 package Hash_Subprograms
is
131 function Equal
(F1
, F2
: Key
) return Boolean;
132 pragma Inline
(Equal
);
134 function Get_Key
(E
: Elmt_Ptr
) return Key
;
135 pragma Inline
(Get_Key
);
137 function Hash
(F
: Key
) return Header_Num
;
138 pragma Inline
(Hash
);
140 function Next
(E
: Elmt_Ptr
) return Elmt_Ptr
;
141 pragma Inline
(Next
);
143 procedure Set_Next
(E
: Elmt_Ptr
; Next
: Elmt_Ptr
);
144 pragma Inline
(Set_Next
);
146 end Hash_Subprograms
;
148 package body Hash_Subprograms
is
154 function Equal
(F1
, F2
: Key
) return Boolean is
163 function Get_Key
(E
: Elmt_Ptr
) return Key
is
165 return E
.Entity_Name
;
172 function Hash
(F
: Key
) return Header_Num
is
174 return Header_Num
(Int
(F
) mod 1024);
181 function Next
(E
: Elmt_Ptr
) return Elmt_Ptr
is
190 procedure Set_Next
(E
: Elmt_Ptr
; Next
: Elmt_Ptr
) is
194 end Hash_Subprograms
;
200 -- The following table records the data for each pragma, using the
201 -- entity name as the hash key for retrieval. Entries in this table
202 -- are set by Process_Eliminate_Pragma and read by Check_Eliminated.
204 package Elim_Hash_Table
is new Static_HTable
(
205 Header_Num
=> Header_Num
,
207 Elmt_Ptr
=> Elmt_Ptr
,
208 Null_Ptr
=> Null_Ptr
,
209 Set_Next
=> Hash_Subprograms
.Set_Next
,
210 Next
=> Hash_Subprograms
.Next
,
212 Get_Key
=> Hash_Subprograms
.Get_Key
,
213 Hash
=> Hash_Subprograms
.Hash
,
214 Equal
=> Hash_Subprograms
.Equal
);
216 -- The following table records entities for subprograms that are
217 -- eliminated, and corresponding eliminate pragmas that caused the
218 -- elimination. Entries in this table are set by Check_Eliminated
219 -- and read by Eliminate_Error_Msg.
221 type Elim_Entity_Entry
is record
226 package Elim_Entities
is new Table
.Table
(
227 Table_Component_Type
=> Elim_Entity_Entry
,
228 Table_Index_Type
=> Name_Id
'Base,
229 Table_Low_Bound
=> First_Name_Id
,
231 Table_Increment
=> 200,
232 Table_Name
=> "Elim_Entries");
234 ----------------------
235 -- Check_Eliminated --
236 ----------------------
238 procedure Check_Eliminated
(E
: Entity_Id
) is
239 Elmt
: Access_Elim_Data
;
245 if No_Elimination
then
248 -- Elimination of objects and types is not implemented yet
250 elsif not Is_Subprogram
(E
) then
254 -- Loop through homonyms for this key
256 Elmt
:= Elim_Hash_Table
.Get
(Chars
(E
));
257 while Elmt
/= null loop
258 Check_Homonyms
: declare
259 procedure Set_Eliminated
;
260 -- Set current subprogram entity as eliminated
266 procedure Set_Eliminated
is
267 Overridden
: Entity_Id
;
270 if Is_Dispatching_Operation
(E
) then
272 -- If an overriding dispatching primitive is eliminated then
273 -- its parent must have been eliminated. If the parent is an
274 -- inherited operation, check the operation that it renames,
275 -- because flag Eliminated is only set on source operations.
277 Overridden
:= Overridden_Operation
(E
);
279 if Present
(Overridden
)
280 and then not Comes_From_Source
(Overridden
)
281 and then Present
(Alias
(Overridden
))
283 Overridden
:= Alias
(Overridden
);
286 if Present
(Overridden
)
287 and then not Is_Eliminated
(Overridden
)
288 and then not Is_Abstract_Subprogram
(Overridden
)
290 Error_Msg_Name_1
:= Chars
(E
);
291 Error_Msg_N
("cannot eliminate subprogram %", E
);
296 Set_Is_Eliminated
(E
);
297 Elim_Entities
.Append
((Prag
=> Elmt
.Prag
, Subp
=> E
));
300 -- Start of processing for Check_Homonyms
303 -- First we check that the name of the entity matches
305 if Elmt
.Entity_Name
/= Chars
(E
) then
309 -- Find enclosing unit, and verify that its name and those of its
312 Scop
:= Cunit_Entity
(Current_Sem_Unit
);
314 -- Now see if compilation unit matches
316 Up
:= Elmt
.Unit_Name
'Last;
318 -- If we are within a subunit, the name in the pragma has been
319 -- parsed as a child unit, but the current compilation unit is in
320 -- fact the parent in which the subunit is embedded. We must skip
321 -- the first name which is that of the subunit to match the pragma
322 -- specification. Body may be that of a package or subprogram.
329 while Present
(Par
) loop
330 if Nkind
(Par
) = N_Subunit
then
331 if Chars
(Defining_Entity
(Proper_Body
(Par
))) =
346 for J
in reverse Elmt
.Unit_Name
'First .. Up
loop
347 if Elmt
.Unit_Name
(J
) /= Chars
(Scop
) then
351 Scop
:= Scope
(Scop
);
353 if Scop
/= Standard_Standard
and then J
= 1 then
358 if Scop
/= Standard_Standard
then
362 if Present
(Elmt
.Entity_Node
)
363 and then Elmt
.Entity_Scope
/= null
365 -- Check that names of enclosing scopes match. Skip blocks and
366 -- wrapper package of subprogram instances, which do not appear
371 for J
in reverse Elmt
.Entity_Scope
'Range loop
372 while Ekind
(Scop
) = E_Block
374 (Ekind
(Scop
) = E_Package
375 and then Is_Wrapper_Package
(Scop
))
377 Scop
:= Scope
(Scop
);
380 if Elmt
.Entity_Scope
(J
) /= Chars
(Scop
) then
381 if Ekind
(Scop
) /= E_Protected_Type
382 or else Comes_From_Source
(Scop
)
386 -- For simple protected declarations, retrieve the source
387 -- name of the object, which appeared in the Eliminate
392 Decl
: constant Node_Id
:=
393 Original_Node
(Parent
(Scop
));
396 if Elmt
.Entity_Scope
(J
) /=
397 Chars
(Defining_Identifier
(Decl
))
409 Scop
:= Scope
(Scop
);
413 -- If given entity is a library level subprogram and pragma had a
414 -- single parameter, a match.
416 if Is_Compilation_Unit
(E
)
417 and then Is_Subprogram
(E
)
418 and then No
(Elmt
.Entity_Node
)
423 -- Check for case of type or object with two parameter case
425 elsif (Is_Type
(E
) or else Is_Object
(E
))
426 and then Elmt
.Result_Type
= No_Name
427 and then Elmt
.Parameter_Types
= null
432 -- Check for case of subprogram
434 elsif Ekind
(E
) in E_Function | E_Procedure
then
436 -- If Source_Location present, then see if it matches
438 if Elmt
.Source_Location
/= No_Name
then
439 Get_Name_String
(Elmt
.Source_Location
);
442 Sloc_Trace
: constant String :=
443 Name_Buffer
(1 .. Name_Len
);
445 Idx
: Natural := Sloc_Trace
'First;
446 -- Index in Sloc_Trace, if equals to 0, then we have
447 -- completely traversed Sloc_Trace
449 Last
: constant Natural := Sloc_Trace
'Last;
452 Sindex
: Source_File_Index
;
454 function File_Name_Match
return Boolean;
455 -- This function is supposed to be called when Idx points
456 -- to the beginning of the new file name, and Name_Buffer
457 -- is set to contain the name of the proper source file
458 -- from the chain corresponding to the Sloc of E. First
459 -- it checks that these two files have the same name. If
460 -- this check is successful, moves Idx to point to the
461 -- beginning of the column number.
463 function Line_Num_Match
return Boolean;
464 -- This function is supposed to be called when Idx points
465 -- to the beginning of the column number, and P is
466 -- set to point to the proper Sloc the chain
467 -- corresponding to the Sloc of E. First it checks that
468 -- the line number Idx points on and the line number
469 -- corresponding to P are the same. If this check is
470 -- successful, moves Idx to point to the beginning of
471 -- the next file name in Sloc_Trace. If there is no file
472 -- name any more, Idx is set to 0.
474 function Different_Trace_Lengths
return Boolean;
475 -- From Idx and P, defines if there are in both traces
476 -- more element(s) in the instantiation chains. Returns
477 -- False if one trace contains more element(s), but
478 -- another does not. If both traces contains more
479 -- elements (that is, the function returns False), moves
480 -- P ahead in the chain corresponding to E, recomputes
481 -- Sindex and sets the name of the corresponding file in
484 function Skip_Spaces
return Natural;
485 -- If Sloc_Trace (Idx) is not space character, returns
486 -- Idx. Otherwise returns the index of the nearest
487 -- non-space character in Sloc_Trace to the right of Idx.
488 -- Returns 0 if there is no such character.
490 -----------------------------
491 -- Different_Trace_Lengths --
492 -----------------------------
494 function Different_Trace_Lengths
return Boolean is
496 P
:= Instantiation
(Sindex
);
498 if (P
= No_Location
and then Idx
/= 0)
500 (P
/= No_Location
and then Idx
= 0)
505 if P
/= No_Location
then
506 Sindex
:= Get_Source_File_Index
(P
);
507 Get_Name_String
(File_Name
(Sindex
));
512 end Different_Trace_Lengths
;
514 ---------------------
515 -- File_Name_Match --
516 ---------------------
518 function File_Name_Match
return Boolean is
527 -- Find first colon. If no colon, then return False.
528 -- If there is a colon, Tmp_Idx is set to point just
533 if Tmp_Idx
>= Last
then
535 elsif Sloc_Trace
(Tmp_Idx
+ 1) = ':' then
538 Tmp_Idx
:= Tmp_Idx
+ 1;
542 -- Find last non-space before this colon. If there is
543 -- no space character before this colon, then return
544 -- False. Otherwise, End_Idx is set to point to this
545 -- non-space character.
549 if End_Idx
< Idx
then
552 elsif Sloc_Trace
(End_Idx
) /= ' ' then
556 End_Idx
:= End_Idx
- 1;
560 -- Now see if file name matches what is in Name_Buffer
561 -- and if so, step Idx past it and return True. If the
562 -- name does not match, return False.
564 if Sloc_Trace
(Idx
.. End_Idx
) =
565 Name_Buffer
(1 .. Name_Len
)
579 function Line_Num_Match
return Boolean is
588 and then Sloc_Trace
(Idx
) in '0' .. '9'
591 (Character'Pos (Sloc_Trace
(Idx
)) -
592 Character'Pos ('0'));
596 if Get_Physical_Line_Number
(P
) =
597 Physical_Line_Number
(N
)
599 while Idx
<= Last
and then
600 Sloc_Trace
(Idx
) /= '['
606 pragma Assert
(Sloc_Trace
(Idx
) = '[');
624 function Skip_Spaces
return Natural is
629 while Sloc_Trace
(Res
) = ' ' loop
643 Sindex
:= Get_Source_File_Index
(P
);
644 Get_Name_String
(File_Name
(Sindex
));
648 if not File_Name_Match
then
650 elsif not Line_Num_Match
then
654 if Different_Trace_Lengths
then
661 -- If we have a Result_Type, then we must have a function with
662 -- the proper result type.
664 if Elmt
.Result_Type
/= No_Name
then
665 if Ekind
(E
) /= E_Function
666 or else Chars
(Etype
(E
)) /= Elmt
.Result_Type
672 -- If we have Parameter_Types, they must match
674 if Elmt
.Parameter_Types
/= null then
675 Form
:= First_Formal
(E
);
678 and then Elmt
.Parameter_Types
'Length = 1
679 and then Elmt
.Parameter_Types
(1) = No_Name
681 -- Parameterless procedure matches
685 elsif Elmt
.Parameter_Types
= null then
689 for J
in Elmt
.Parameter_Types
'Range loop
692 Chars
(Etype
(Form
)) /= Elmt
.Parameter_Types
(J
)
700 if Present
(Form
) then
706 -- If we fall through, this is match
714 Elmt
:= Elmt
.Homonym
;
718 end Check_Eliminated
;
720 -------------------------------------
721 -- Check_For_Eliminated_Subprogram --
722 -------------------------------------
724 procedure Check_For_Eliminated_Subprogram
(N
: Node_Id
; S
: Entity_Id
) is
725 Ultimate_Subp
: constant Entity_Id
:= Ultimate_Alias
(S
);
726 Enclosing_Subp
: Entity_Id
;
729 -- No check needed within a default expression for a formal, since this
730 -- is not really a use, and the expression (a call or attribute) may
731 -- never be used if the enclosing subprogram is itself eliminated.
733 if In_Spec_Expression
then
737 if Is_Eliminated
(Ultimate_Subp
)
738 and then not Inside_A_Generic
739 and then not Is_Generic_Unit
(Cunit_Entity
(Current_Sem_Unit
))
741 Enclosing_Subp
:= Current_Subprogram
;
742 while Present
(Enclosing_Subp
) loop
743 if Is_Eliminated
(Enclosing_Subp
) then
747 Enclosing_Subp
:= Enclosing_Subprogram
(Enclosing_Subp
);
750 -- Emit error, unless we are within an instance body and the expander
751 -- is disabled, indicating an instance within an enclosing generic.
752 -- In an instance, the ultimate alias is an internal entity, so place
753 -- the message on the original subprogram.
755 if In_Instance_Body
and then not Expander_Active
then
758 elsif Comes_From_Source
(Ultimate_Subp
) then
759 Eliminate_Error_Msg
(N
, Ultimate_Subp
);
762 Eliminate_Error_Msg
(N
, S
);
765 end Check_For_Eliminated_Subprogram
;
767 -------------------------
768 -- Eliminate_Error_Msg --
769 -------------------------
771 procedure Eliminate_Error_Msg
(N
: Node_Id
; E
: Entity_Id
) is
773 for J
in Elim_Entities
.First
.. Elim_Entities
.Last
loop
774 if E
= Elim_Entities
.Table
(J
).Subp
then
775 Error_Msg_Sloc
:= Sloc
(Elim_Entities
.Table
(J
).Prag
);
776 Error_Msg_NE
("cannot reference subprogram & eliminated #", N
, E
);
781 -- If this is an internal operation generated for a protected operation,
782 -- its name does not match the source name, so just report the error.
784 if not Comes_From_Source
(E
)
785 and then Present
(First_Entity
(E
))
786 and then Is_Concurrent_Record_Type
(Etype
(First_Entity
(E
)))
789 ("cannot reference eliminated protected subprogram&", N
, E
);
791 -- Otherwise should not fall through, entry should be in table
795 ("subprogram& is called but its alias is eliminated", N
, E
);
796 -- raise Program_Error;
798 end Eliminate_Error_Msg
;
804 procedure Initialize
is
806 Elim_Hash_Table
.Reset
;
808 No_Elimination
:= True;
811 ------------------------------
812 -- Process_Eliminate_Pragma --
813 ------------------------------
815 procedure Process_Eliminate_Pragma
816 (Pragma_Node
: Node_Id
;
817 Arg_Unit_Name
: Node_Id
;
818 Arg_Entity
: Node_Id
;
819 Arg_Parameter_Types
: Node_Id
;
820 Arg_Result_Type
: Node_Id
;
821 Arg_Source_Location
: Node_Id
)
823 Data
: constant Access_Elim_Data
:= new Elim_Data
;
824 -- Build result data here
826 Elmt
: Access_Elim_Data
;
828 Num_Names
: Nat
:= 0;
829 -- Number of names in unit name
835 function OK_Selected_Component
(N
: Node_Id
) return Boolean;
836 -- Test if N is a selected component with all identifiers, or a selected
837 -- component whose selector is an operator symbol. As a side effect
838 -- if result is True, sets Num_Names to the number of names present
839 -- (identifiers, and operator if any).
841 ---------------------------
842 -- OK_Selected_Component --
843 ---------------------------
845 function OK_Selected_Component
(N
: Node_Id
) return Boolean is
847 if Nkind
(N
) = N_Identifier
848 or else Nkind
(N
) = N_Operator_Symbol
850 Num_Names
:= Num_Names
+ 1;
853 elsif Nkind
(N
) = N_Selected_Component
then
854 return OK_Selected_Component
(Prefix
(N
))
855 and then OK_Selected_Component
(Selector_Name
(N
));
860 end OK_Selected_Component
;
862 -- Start of processing for Process_Eliminate_Pragma
865 Data
.Prag
:= Pragma_Node
;
866 Error_Msg_Name_1
:= Name_Eliminate
;
868 -- Process Unit_Name argument
870 if Nkind
(Arg_Unit_Name
) = N_Identifier
then
871 Data
.Unit_Name
:= new Names
'(1 => Chars (Arg_Unit_Name));
874 elsif OK_Selected_Component (Arg_Unit_Name) then
875 Data.Unit_Name := new Names (1 .. Num_Names);
877 Arg_Uname := Arg_Unit_Name;
878 for J in reverse 2 .. Num_Names loop
879 Data.Unit_Name (J) := Chars (Selector_Name (Arg_Uname));
880 Arg_Uname := Prefix (Arg_Uname);
883 Data.Unit_Name (1) := Chars (Arg_Uname);
887 ("wrong form for Unit_Name parameter of pragma%", Arg_Unit_Name);
891 -- Process Entity argument
893 if Present (Arg_Entity) then
896 if Nkind (Arg_Entity) = N_Identifier
897 or else Nkind (Arg_Entity) = N_Operator_Symbol
899 Data.Entity_Name := Chars (Arg_Entity);
900 Data.Entity_Node := Arg_Entity;
901 Data.Entity_Scope := null;
903 elsif OK_Selected_Component (Arg_Entity) then
904 Data.Entity_Scope := new Names (1 .. Num_Names - 1);
905 Data.Entity_Name := Chars (Selector_Name (Arg_Entity));
906 Data.Entity_Node := Arg_Entity;
908 Arg_Ent := Prefix (Arg_Entity);
909 for J in reverse 2 .. Num_Names - 1 loop
910 Data.Entity_Scope (J) := Chars (Selector_Name (Arg_Ent));
911 Arg_Ent := Prefix (Arg_Ent);
914 Data.Entity_Scope (1) := Chars (Arg_Ent);
916 elsif Is_Config_Static_String (Arg_Entity) then
917 Data.Entity_Name := Name_Find;
918 Data.Entity_Node := Arg_Entity;
924 Data.Entity_Node := Empty;
925 Data.Entity_Name := Data.Unit_Name (Num_Names);
928 -- Process Parameter_Types argument
930 if Present (Arg_Parameter_Types) then
932 -- Here for aggregate case
934 if Nkind (Arg_Parameter_Types) = N_Aggregate then
935 Data.Parameter_Types :=
937 (1 .. List_Length (Expressions (Arg_Parameter_Types)));
939 Lit := First (Expressions (Arg_Parameter_Types));
940 for J in Data.Parameter_Types'Range loop
941 if Is_Config_Static_String (Lit) then
942 Data.Parameter_Types (J) := Name_Find;
949 -- Otherwise we must have case of one name, which looks like a
950 -- parenthesized literal rather than an aggregate.
952 elsif Paren_Count (Arg_Parameter_Types) /= 1 then
954 ("wrong form for argument of pragma Eliminate",
955 Arg_Parameter_Types);
958 elsif Is_Config_Static_String (Arg_Parameter_Types) then
959 String_To_Name_Buffer (Strval (Arg_Parameter_Types));
963 -- Parameterless procedure
965 Data.Parameter_Types := new Names'(1 => No_Name
);
968 Data
.Parameter_Types
:= new Names
'(1 => Name_Find);
976 -- Process Result_Types argument
978 if Present (Arg_Result_Type) then
979 if Is_Config_Static_String (Arg_Result_Type) then
980 Data.Result_Type := Name_Find;
985 -- Here if no Result_Types argument
988 Data.Result_Type := No_Name;
991 -- Process Source_Location argument
993 if Present (Arg_Source_Location) then
994 if Is_Config_Static_String (Arg_Source_Location) then
995 Data.Source_Location := Name_Find;
1000 Data.Source_Location := No_Name;
1003 Elmt := Elim_Hash_Table.Get (Hash_Subprograms.Get_Key (Data));
1005 -- If we already have an entry with this same key, then link
1006 -- it into the chain of entries for this key.
1008 if Elmt /= null then
1009 Data.Homonym := Elmt.Homonym;
1010 Elmt.Homonym := Data;
1012 -- Otherwise create a new entry
1015 Elim_Hash_Table.Set (Data);
1018 No_Elimination := False;
1019 end Process_Eliminate_Pragma;