1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 1997-2004 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 2, 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 COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 ------------------------------------------------------------------------------
27 with Atree
; use Atree
;
28 with Einfo
; use Einfo
;
29 with Errout
; use Errout
;
30 with Namet
; use Namet
;
31 with Nlists
; use Nlists
;
32 with Sem_Prag
; use Sem_Prag
;
33 with Sinput
; use Sinput
;
34 with Sinfo
; use Sinfo
;
35 with Snames
; use Snames
;
36 with Stand
; use Stand
;
37 with Stringt
; use Stringt
;
40 with GNAT
.HTable
; use GNAT
.HTable
;
42 package body Sem_Elim
is
44 No_Elimination
: Boolean;
45 -- Set True if no Eliminate pragmas active
51 -- A single pragma Eliminate is represented by the following record
54 type Access_Elim_Data
is access Elim_Data
;
56 type Names
is array (Nat
range <>) of Name_Id
;
57 -- Type used to represent set of names. Used for names in Unit_Name
58 -- and also the set of names in Argument_Types.
60 type Access_Names
is access Names
;
62 type Elim_Data
is record
64 Unit_Name
: Access_Names
;
65 -- Unit name, broken down into a set of names (e.g. A.B.C is
66 -- represented as Name_Id values for A, B, C in sequence).
68 Entity_Name
: Name_Id
;
69 -- Entity name if Entity parameter if present. If no Entity parameter
70 -- was supplied, then Entity_Node is set to Empty, and the Entity_Name
71 -- field contains the last identifier name in the Unit_Name.
73 Entity_Scope
: Access_Names
;
74 -- Static scope of the entity within the compilation unit represented by
77 Entity_Node
: Node_Id
;
78 -- Save node of entity argument, for posting error messages. Set
79 -- to Empty if there is no entity argument.
81 Parameter_Types
: Access_Names
;
82 -- Set to set of names given for parameter types. If no parameter
83 -- types argument is present, this argument is set to null.
85 Result_Type
: Name_Id
;
86 -- Result type name if Result_Types parameter present, No_Name if not
88 Source_Location
: Name_Id
;
89 -- String describing the source location of subprogram defining name if
90 -- Source_Location parameter present, No_Name if not
92 Hash_Link
: Access_Elim_Data
;
93 -- Link for hash table use
95 Homonym
: Access_Elim_Data
;
96 -- Pointer to next entry with same key
99 -- Node_Id for Eliminate pragma
107 -- Setup hash table using the Entity_Name field as the hash key
109 subtype Element
is Elim_Data
;
110 subtype Elmt_Ptr
is Access_Elim_Data
;
112 subtype Key
is Name_Id
;
114 type Header_Num
is range 0 .. 1023;
116 Null_Ptr
: constant Elmt_Ptr
:= null;
118 ----------------------
119 -- Hash_Subprograms --
120 ----------------------
122 package Hash_Subprograms
is
124 function Equal
(F1
, F2
: Key
) return Boolean;
125 pragma Inline
(Equal
);
127 function Get_Key
(E
: Elmt_Ptr
) return Key
;
128 pragma Inline
(Get_Key
);
130 function Hash
(F
: Key
) return Header_Num
;
131 pragma Inline
(Hash
);
133 function Next
(E
: Elmt_Ptr
) return Elmt_Ptr
;
134 pragma Inline
(Next
);
136 procedure Set_Next
(E
: Elmt_Ptr
; Next
: Elmt_Ptr
);
137 pragma Inline
(Set_Next
);
139 end Hash_Subprograms
;
141 package body Hash_Subprograms
is
147 function Equal
(F1
, F2
: Key
) return Boolean is
156 function Get_Key
(E
: Elmt_Ptr
) return Key
is
158 return E
.Entity_Name
;
165 function Hash
(F
: Key
) return Header_Num
is
167 return Header_Num
(Int
(F
) mod 1024);
174 function Next
(E
: Elmt_Ptr
) return Elmt_Ptr
is
183 procedure Set_Next
(E
: Elmt_Ptr
; Next
: Elmt_Ptr
) is
187 end Hash_Subprograms
;
193 -- The following table records the data for each pragmas, using the
194 -- entity name as the hash key for retrieval. Entries in this table
195 -- are set by Process_Eliminate_Pragma and read by Check_Eliminated.
197 package Elim_Hash_Table
is new Static_HTable
(
198 Header_Num
=> Header_Num
,
200 Elmt_Ptr
=> Elmt_Ptr
,
201 Null_Ptr
=> Null_Ptr
,
202 Set_Next
=> Hash_Subprograms
.Set_Next
,
203 Next
=> Hash_Subprograms
.Next
,
205 Get_Key
=> Hash_Subprograms
.Get_Key
,
206 Hash
=> Hash_Subprograms
.Hash
,
207 Equal
=> Hash_Subprograms
.Equal
);
209 -- The following table records entities for subprograms that are
210 -- eliminated, and corresponding eliminate pragmas that caused the
211 -- elimination. Entries in this table are set by Check_Eliminated
212 -- and read by Eliminate_Error_Msg.
214 type Elim_Entity_Entry
is record
219 package Elim_Entities
is new Table
.Table
(
220 Table_Component_Type
=> Elim_Entity_Entry
,
221 Table_Index_Type
=> Name_Id
,
222 Table_Low_Bound
=> First_Name_Id
,
224 Table_Increment
=> 200,
225 Table_Name
=> "Elim_Entries");
227 ----------------------
228 -- Check_Eliminated --
229 ----------------------
231 procedure Check_Eliminated
(E
: Entity_Id
) is
232 Elmt
: Access_Elim_Data
;
236 function Original_Chars
(S
: Entity_Id
) return Name_Id
;
237 -- If the candidate subprogram is a protected operation of a single
238 -- protected object, the scope of the operation is the created
239 -- protected type, and we have to retrieve the original name of
246 function Original_Chars
(S
: Entity_Id
) return Name_Id
is
248 if Ekind
(S
) /= E_Protected_Type
249 or else Comes_From_Source
(S
)
253 return Chars
(Defining_Identifier
(Original_Node
(Parent
(S
))));
257 -- Start of processing for Check_Eliminated
260 if No_Elimination
then
263 -- Elimination of objects and types is not implemented yet
265 elsif Ekind
(E
) not in Subprogram_Kind
then
269 -- Loop through homonyms for this key
271 Elmt
:= Elim_Hash_Table
.Get
(Chars
(E
));
272 while Elmt
/= null loop
274 procedure Set_Eliminated
;
275 -- Set current subprogram entity as eliminated
277 procedure Set_Eliminated
is
279 Set_Is_Eliminated
(E
);
280 Elim_Entities
.Append
((Prag
=> Elmt
.Prag
, Subp
=> E
));
284 -- First we check that the name of the entity matches
286 if Elmt
.Entity_Name
/= Chars
(E
) then
290 -- Then we need to see if the static scope matches within the
293 -- At the moment, gnatelim does not consider block statements as
294 -- scopes (even if a block is named)
297 while Ekind
(Scop
) = E_Block
loop
298 Scop
:= Scope
(Scop
);
301 if Elmt
.Entity_Scope
/= null then
302 for J
in reverse Elmt
.Entity_Scope
'Range loop
303 if Elmt
.Entity_Scope
(J
) /= Original_Chars
(Scop
) then
307 Scop
:= Scope
(Scop
);
308 while Ekind
(Scop
) = E_Block
loop
309 Scop
:= Scope
(Scop
);
312 if not Is_Compilation_Unit
(Scop
) and then J
= 1 then
318 -- Now see if compilation unit matches
320 for J
in reverse Elmt
.Unit_Name
'Range loop
321 if Elmt
.Unit_Name
(J
) /= Chars
(Scop
) then
325 Scop
:= Scope
(Scop
);
326 while Ekind
(Scop
) = E_Block
loop
327 Scop
:= Scope
(Scop
);
330 if Scop
/= Standard_Standard
and then J
= 1 then
335 if Scop
/= Standard_Standard
then
339 -- Check for case of given entity is a library level subprogram
340 -- and we have the single parameter Eliminate case, a match!
342 if Is_Compilation_Unit
(E
)
343 and then Is_Subprogram
(E
)
344 and then No
(Elmt
.Entity_Node
)
349 -- Check for case of type or object with two parameter case
351 elsif (Is_Type
(E
) or else Is_Object
(E
))
352 and then Elmt
.Result_Type
= No_Name
353 and then Elmt
.Parameter_Types
= null
358 -- Check for case of subprogram
360 elsif Ekind
(E
) = E_Function
361 or else Ekind
(E
) = E_Procedure
363 -- If Source_Location present, then see if it matches
365 if Elmt
.Source_Location
/= No_Name
then
366 Get_Name_String
(Elmt
.Source_Location
);
369 Sloc_Trace
: constant String :=
370 Name_Buffer
(1 .. Name_Len
);
372 Idx
: Natural := Sloc_Trace
'First;
373 -- Index in Sloc_Trace, if equals to 0, then we have
374 -- completely traversed Sloc_Trace
376 Last
: constant Natural := Sloc_Trace
'Last;
379 Sindex
: Source_File_Index
;
381 function File_Name_Match
return Boolean;
382 -- This function is supposed to be called when Idx points
383 -- to the beginning of the new file name, and Name_Buffer
384 -- is set to contain the name of the proper source file
385 -- from the chain corresponding to the Sloc of E. First
386 -- it checks that these two files have the same name. If
387 -- this check is successful, moves Idx to point to the
388 -- beginning of the column number.
390 function Line_Num_Match
return Boolean;
391 -- This function is supposed to be called when Idx points
392 -- to the beginning of the column number, and P is
393 -- set to point to the proper Sloc the chain
394 -- corresponding to the Sloc of E. First it checks that
395 -- the line number Idx points on and the line number
396 -- corresponding to P are the same. If this check is
397 -- successful, moves Idx to point to the beginning of
398 -- the next file name in Sloc_Trace. If there is no file
399 -- name any more, Idx is set to 0.
401 function Different_Trace_Lengths
return Boolean;
402 -- From Idx and P, defines if there are in both traces
403 -- more element(s) in the instantiation chains. Returns
404 -- False if one trace contains more element(s), but
405 -- another does not. If both traces contains more
406 -- elements (that is, the function returns False), moves
407 -- P ahead in the chain corresponding to E, recomputes
408 -- Sindex and sets the name of the corresponding file in
411 function Skip_Spaces
return Natural;
412 -- If Sloc_Trace (Idx) is not space character, returns
413 -- Idx. Otherwise returns the index of the nearest
414 -- non-space character in Sloc_Trace to the right of
415 -- Idx. Returns 0 if there is no such character.
417 -----------------------------
418 -- Different_Trace_Lengths --
419 -----------------------------
421 function Different_Trace_Lengths
return Boolean is
423 P
:= Instantiation
(Sindex
);
425 if (P
= No_Location
and then Idx
/= 0)
427 (P
/= No_Location
and then Idx
= 0)
432 if P
/= No_Location
then
433 Sindex
:= Get_Source_File_Index
(P
);
434 Get_Name_String
(File_Name
(Sindex
));
439 end Different_Trace_Lengths
;
441 ---------------------
442 -- File_Name_Match --
443 ---------------------
445 function File_Name_Match
return Boolean is
454 -- Find first colon. If no colon, then return False.
455 -- If there is a colon, Tmp_Idx is set to point just
460 if Tmp_Idx
>= Last
then
462 elsif Sloc_Trace
(Tmp_Idx
+ 1) = ':' then
465 Tmp_Idx
:= Tmp_Idx
+ 1;
469 -- Find last non-space before this colon. If there
470 -- is no no space character before this colon, then
471 -- return False. Otherwise, End_Idx set to point to
472 -- this non-space character.
476 if End_Idx
< Idx
then
478 elsif Sloc_Trace
(End_Idx
) /= ' ' then
481 End_Idx
:= End_Idx
- 1;
485 -- Now see if file name matches what is in Name_Buffer
486 -- and if so, step Idx past it and return True. If the
487 -- name does not match, return False.
489 if Sloc_Trace
(Idx
.. End_Idx
) =
490 Name_Buffer
(1 .. Name_Len
)
504 function Line_Num_Match
return Boolean is
513 and then Sloc_Trace
(Idx
) in '0' .. '9'
516 (Character'Pos (Sloc_Trace
(Idx
)) -
517 Character'Pos ('0'));
521 if Get_Physical_Line_Number
(P
) =
522 Physical_Line_Number
(N
)
524 while Idx
<= Last
and then
525 Sloc_Trace
(Idx
) /= '['
530 if Idx
<= Last
and then
531 Sloc_Trace
(Idx
) = '['
549 function Skip_Spaces
return Natural is
550 Res
: Natural := Idx
;
553 while Sloc_Trace
(Res
) = ' ' loop
567 Sindex
:= Get_Source_File_Index
(P
);
568 Get_Name_String
(File_Name
(Sindex
));
572 if not File_Name_Match
then
574 elsif not Line_Num_Match
then
578 if Different_Trace_Lengths
then
585 -- If we have a Result_Type, then we must have a function
586 -- with the proper result type
588 if Elmt
.Result_Type
/= No_Name
then
589 if Ekind
(E
) /= E_Function
590 or else Chars
(Etype
(E
)) /= Elmt
.Result_Type
596 -- If we have Parameter_Types, they must match
598 if Elmt
.Parameter_Types
/= null then
599 Form
:= First_Formal
(E
);
602 and then Elmt
.Parameter_Types
'Length = 1
603 and then Elmt
.Parameter_Types
(1) = No_Name
605 -- Parameterless procedure matches
609 elsif Elmt
.Parameter_Types
= null then
613 for J
in Elmt
.Parameter_Types
'Range loop
616 Chars
(Etype
(Form
)) /= Elmt
.Parameter_Types
(J
)
624 if Present
(Form
) then
630 -- If we fall through, this is match
638 Elmt
:= Elmt
.Homonym
;
642 end Check_Eliminated
;
644 -------------------------
645 -- Eliminate_Error_Msg --
646 -------------------------
648 procedure Eliminate_Error_Msg
(N
: Node_Id
; E
: Entity_Id
) is
650 for J
in Elim_Entities
.First
.. Elim_Entities
.Last
loop
651 if E
= Elim_Entities
.Table
(J
).Subp
then
652 Error_Msg_Sloc
:= Sloc
(Elim_Entities
.Table
(J
).Prag
);
653 Error_Msg_NE
("cannot call subprogram & eliminated #", N
, E
);
658 -- Should never fall through, since entry should be in table
661 end Eliminate_Error_Msg
;
667 procedure Initialize
is
669 Elim_Hash_Table
.Reset
;
671 No_Elimination
:= True;
674 ------------------------------
675 -- Process_Eliminate_Pragma --
676 ------------------------------
678 procedure Process_Eliminate_Pragma
679 (Pragma_Node
: Node_Id
;
680 Arg_Unit_Name
: Node_Id
;
681 Arg_Entity
: Node_Id
;
682 Arg_Parameter_Types
: Node_Id
;
683 Arg_Result_Type
: Node_Id
;
684 Arg_Source_Location
: Node_Id
)
686 Data
: constant Access_Elim_Data
:= new Elim_Data
;
687 -- Build result data here
689 Elmt
: Access_Elim_Data
;
691 Num_Names
: Nat
:= 0;
692 -- Number of names in unit name
698 function OK_Selected_Component
(N
: Node_Id
) return Boolean;
699 -- Test if N is a selected component with all identifiers, or a
700 -- selected component whose selector is an operator symbol. As a
701 -- side effect if result is True, sets Num_Names to the number
702 -- of names present (identifiers and operator if any).
704 ---------------------------
705 -- OK_Selected_Component --
706 ---------------------------
708 function OK_Selected_Component
(N
: Node_Id
) return Boolean is
710 if Nkind
(N
) = N_Identifier
711 or else Nkind
(N
) = N_Operator_Symbol
713 Num_Names
:= Num_Names
+ 1;
716 elsif Nkind
(N
) = N_Selected_Component
then
717 return OK_Selected_Component
(Prefix
(N
))
718 and then OK_Selected_Component
(Selector_Name
(N
));
723 end OK_Selected_Component
;
725 -- Start of processing for Process_Eliminate_Pragma
728 Data
.Prag
:= Pragma_Node
;
729 Error_Msg_Name_1
:= Name_Eliminate
;
731 -- Process Unit_Name argument
733 if Nkind
(Arg_Unit_Name
) = N_Identifier
then
734 Data
.Unit_Name
:= new Names
'(1 => Chars (Arg_Unit_Name));
737 elsif OK_Selected_Component (Arg_Unit_Name) then
738 Data.Unit_Name := new Names (1 .. Num_Names);
740 Arg_Uname := Arg_Unit_Name;
741 for J in reverse 2 .. Num_Names loop
742 Data.Unit_Name (J) := Chars (Selector_Name (Arg_Uname));
743 Arg_Uname := Prefix (Arg_Uname);
746 Data.Unit_Name (1) := Chars (Arg_Uname);
750 ("wrong form for Unit_Name parameter of pragma%", Arg_Unit_Name);
754 -- Process Entity argument
756 if Present (Arg_Entity) then
759 if Nkind (Arg_Entity) = N_Identifier
760 or else Nkind (Arg_Entity) = N_Operator_Symbol
762 Data.Entity_Name := Chars (Arg_Entity);
763 Data.Entity_Node := Arg_Entity;
764 Data.Entity_Scope := null;
766 elsif OK_Selected_Component (Arg_Entity) then
767 Data.Entity_Scope := new Names (1 .. Num_Names - 1);
768 Data.Entity_Name := Chars (Selector_Name (Arg_Entity));
769 Data.Entity_Node := Arg_Entity;
771 Arg_Ent := Prefix (Arg_Entity);
772 for J in reverse 2 .. Num_Names - 1 loop
773 Data.Entity_Scope (J) := Chars (Selector_Name (Arg_Ent));
774 Arg_Ent := Prefix (Arg_Ent);
777 Data.Entity_Scope (1) := Chars (Arg_Ent);
779 elsif Is_Config_Static_String (Arg_Entity) then
780 Data.Entity_Name := Name_Find;
781 Data.Entity_Node := Arg_Entity;
787 Data.Entity_Node := Empty;
788 Data.Entity_Name := Data.Unit_Name (Num_Names);
791 -- Process Parameter_Types argument
793 if Present (Arg_Parameter_Types) then
795 -- Here for aggregate case
797 if Nkind (Arg_Parameter_Types) = N_Aggregate then
798 Data.Parameter_Types :=
800 (1 .. List_Length (Expressions (Arg_Parameter_Types)));
802 Lit := First (Expressions (Arg_Parameter_Types));
803 for J in Data.Parameter_Types'Range loop
804 if Is_Config_Static_String (Lit) then
805 Data.Parameter_Types (J) := Name_Find;
812 -- Otherwise we must have case of one name, which looks like a
813 -- parenthesized literal rather than an aggregate.
815 elsif Paren_Count (Arg_Parameter_Types) /= 1 then
817 ("wrong form for argument of pragma Eliminate",
818 Arg_Parameter_Types);
821 elsif Is_Config_Static_String (Arg_Parameter_Types) then
822 String_To_Name_Buffer (Strval (Arg_Parameter_Types));
826 -- Parameterless procedure
828 Data.Parameter_Types := new Names'(1 => No_Name
);
831 Data
.Parameter_Types
:= new Names
'(1 => Name_Find);
839 -- Process Result_Types argument
841 if Present (Arg_Result_Type) then
842 if Is_Config_Static_String (Arg_Result_Type) then
843 Data.Result_Type := Name_Find;
848 -- Here if no Result_Types argument
851 Data.Result_Type := No_Name;
854 -- Process Source_Location argument
856 if Present (Arg_Source_Location) then
857 if Is_Config_Static_String (Arg_Source_Location) then
858 Data.Source_Location := Name_Find;
863 Data.Source_Location := No_Name;
866 Elmt := Elim_Hash_Table.Get (Hash_Subprograms.Get_Key (Data));
868 -- If we already have an entry with this same key, then link
869 -- it into the chain of entries for this key.
872 Data.Homonym := Elmt.Homonym;
873 Elmt.Homonym := Data;
875 -- Otherwise create a new entry
878 Elim_Hash_Table.Set (Data);
881 No_Elimination := False;
882 end Process_Eliminate_Pragma;