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, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, 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 Sinput; use Sinput;
33 with Sinfo; use Sinfo;
34 with Snames; use Snames;
35 with Stand; use Stand;
36 with Stringt; use Stringt;
39 with GNAT.HTable; use GNAT.HTable;
40 package body Sem_Elim is
42 No_Elimination : Boolean;
43 -- Set True if no Eliminate pragmas active
49 -- A single pragma Eliminate is represented by the following record
52 type Access_Elim_Data is access Elim_Data;
54 type Names is array (Nat range <>) of Name_Id;
55 -- Type used to represent set of names. Used for names in Unit_Name
56 -- and also the set of names in Argument_Types.
58 type Access_Names is access Names;
60 type Elim_Data is record
62 Unit_Name : Access_Names;
63 -- Unit name, broken down into a set of names (e.g. A.B.C is
64 -- represented as Name_Id values for A, B, C in sequence).
66 Entity_Name : Name_Id;
67 -- Entity name if Entity parameter if present. If no Entity parameter
68 -- was supplied, then Entity_Node is set to Empty, and the Entity_Name
69 -- field contains the last identifier name in the Unit_Name.
71 Entity_Scope : Access_Names;
72 -- Static scope of the entity within the compilation unit represented by
75 Entity_Node : Node_Id;
76 -- Save node of entity argument, for posting error messages. Set
77 -- to Empty if there is no entity argument.
79 Parameter_Types : Access_Names;
80 -- Set to set of names given for parameter types. If no parameter
81 -- types argument is present, this argument is set to null.
83 Result_Type : Name_Id;
84 -- Result type name if Result_Types parameter present, No_Name if not
86 Source_Location : Name_Id;
87 -- String describing the source location of subprogram defining name if
88 -- Source_Location parameter present, No_Name if not
90 Hash_Link : Access_Elim_Data;
91 -- Link for hash table use
93 Homonym : Access_Elim_Data;
94 -- Pointer to next entry with same key
97 -- Node_Id for Eliminate pragma
105 -- Setup hash table using the Entity_Name field as the hash key
107 subtype Element is Elim_Data;
108 subtype Elmt_Ptr is Access_Elim_Data;
110 subtype Key is Name_Id;
112 type Header_Num is range 0 .. 1023;
114 Null_Ptr : constant Elmt_Ptr := null;
116 ----------------------
117 -- Hash_Subprograms --
118 ----------------------
120 package Hash_Subprograms is
122 function Equal (F1, F2 : Key) return Boolean;
123 pragma Inline (Equal);
125 function Get_Key (E : Elmt_Ptr) return Key;
126 pragma Inline (Get_Key);
128 function Hash (F : Key) return Header_Num;
129 pragma Inline (Hash);
131 function Next (E : Elmt_Ptr) return Elmt_Ptr;
132 pragma Inline (Next);
134 procedure Set_Next (E : Elmt_Ptr; Next : Elmt_Ptr);
135 pragma Inline (Set_Next);
137 end Hash_Subprograms;
139 package body Hash_Subprograms is
145 function Equal (F1, F2 : Key) return Boolean is
154 function Get_Key (E : Elmt_Ptr) return Key is
156 return E.Entity_Name;
163 function Hash (F : Key) return Header_Num is
165 return Header_Num (Int (F) mod 1024);
172 function Next (E : Elmt_Ptr) return Elmt_Ptr is
181 procedure Set_Next (E : Elmt_Ptr; Next : Elmt_Ptr) is
185 end Hash_Subprograms;
191 -- The following table records the data for each pragmas, using the
192 -- entity name as the hash key for retrieval. Entries in this table
193 -- are set by Process_Eliminate_Pragma and read by Check_Eliminated.
195 package Elim_Hash_Table is new Static_HTable (
196 Header_Num => Header_Num,
198 Elmt_Ptr => Elmt_Ptr,
199 Null_Ptr => Null_Ptr,
200 Set_Next => Hash_Subprograms.Set_Next,
201 Next => Hash_Subprograms.Next,
203 Get_Key => Hash_Subprograms.Get_Key,
204 Hash => Hash_Subprograms.Hash,
205 Equal => Hash_Subprograms.Equal);
207 -- The following table records entities for subprograms that are
208 -- eliminated, and corresponding eliminate pragmas that caused the
209 -- elimination. Entries in this table are set by Check_Eliminated
210 -- and read by Eliminate_Error_Msg.
212 type Elim_Entity_Entry is record
217 package Elim_Entities is new Table.Table (
218 Table_Component_Type => Elim_Entity_Entry,
219 Table_Index_Type => Name_Id,
220 Table_Low_Bound => First_Name_Id,
222 Table_Increment => 200,
223 Table_Name => "Elim_Entries");
225 ----------------------
226 -- Check_Eliminated --
227 ----------------------
229 procedure Check_Eliminated (E : Entity_Id) is
230 Elmt : Access_Elim_Data;
234 function Original_Chars (S : Entity_Id) return Name_Id;
235 -- If the candidate subprogram is a protected operation of a single
236 -- protected object, the scope of the operation is the created
237 -- protected type, and we have to retrieve the original name of
244 function Original_Chars (S : Entity_Id) return Name_Id is
246 if Ekind (S) /= E_Protected_Type
247 or else Comes_From_Source (S)
251 return Chars (Defining_Identifier (Original_Node (Parent (S))));
255 -- Start of processing for Check_Eliminated
258 if No_Elimination then
261 -- Elimination of objects and types is not implemented yet
263 elsif Ekind (E) not in Subprogram_Kind then
267 Elmt := Elim_Hash_Table.Get (Chars (E));
269 -- Loop through homonyms for this key
271 while Elmt /= null loop
273 procedure Set_Eliminated;
274 -- Set current subprogram entity as eliminated
276 procedure Set_Eliminated is
278 Set_Is_Eliminated (E);
279 Elim_Entities.Append ((Prag => Elmt.Prag, Subp => E));
283 -- First we check that the name of the entity matches
285 if Elmt.Entity_Name /= Chars (E) then
289 -- Then we need to see if the static scope matches within the
292 -- At the moment, gnatelim does not consider block statements as
293 -- scopes (even if a block is named)
296 while Ekind (Scop) = E_Block loop
297 Scop := Scope (Scop);
300 if Elmt.Entity_Scope /= null then
301 for J in reverse Elmt.Entity_Scope'Range loop
302 if Elmt.Entity_Scope (J) /= Original_Chars (Scop) then
306 Scop := Scope (Scop);
307 while Ekind (Scop) = E_Block loop
308 Scop := Scope (Scop);
311 if not Is_Compilation_Unit (Scop) and then J = 1 then
317 -- Now see if compilation unit matches
319 for J in reverse Elmt.Unit_Name'Range loop
320 if Elmt.Unit_Name (J) /= Chars (Scop) then
324 Scop := Scope (Scop);
325 while Ekind (Scop) = E_Block loop
326 Scop := Scope (Scop);
329 if Scop /= Standard_Standard and then J = 1 then
334 if Scop /= Standard_Standard then
338 -- Check for case of given entity is a library level subprogram
339 -- and we have the single parameter Eliminate case, a match!
341 if Is_Compilation_Unit (E)
342 and then Is_Subprogram (E)
343 and then No (Elmt.Entity_Node)
348 -- Check for case of type or object with two parameter case
350 elsif (Is_Type (E) or else Is_Object (E))
351 and then Elmt.Result_Type = No_Name
352 and then Elmt.Parameter_Types = null
357 -- Check for case of subprogram
359 elsif Ekind (E) = E_Function
360 or else Ekind (E) = E_Procedure
362 -- If Source_Location present, then see if it matches
364 if Elmt.Source_Location /= No_Name then
365 Get_Name_String (Elmt.Source_Location);
368 Sloc_Trace : constant String :=
369 Name_Buffer (1 .. Name_Len);
371 Idx : Natural := Sloc_Trace'First;
372 -- Index in Sloc_Trace, if equals to 0, then we have
373 -- completely traversed Sloc_Trace
375 Last : constant Natural := Sloc_Trace'Last;
378 Sindex : Source_File_Index;
380 function File_Mame_Match return Boolean;
381 -- This function is supposed to be called when Idx points
382 -- to the beginning of the new file name, and Name_Buffer
383 -- is set to contain the name of the proper source file
384 -- from the chain corresponding to the Sloc of E. First
385 -- it checks that these two files have the same name. If
386 -- this check is successful, moves Idx to point to the
387 -- beginning of the column number.
389 function Line_Num_Match return Boolean;
390 -- This function is supposed to be called when Idx points
391 -- to the beginning of the column number, and P is
392 -- set to point to the proper Sloc the chain
393 -- corresponding to the Sloc of E. First it checks that
394 -- the line number Idx points on and the line number
395 -- corresponding to P are the same. If this check is
396 -- successful, moves Idx to point to the beginning of
397 -- the next file name in Sloc_Trace. If there is no file
398 -- name any more, Idx is set to 0.
400 function Different_Trace_Lengths return Boolean;
401 -- From Idx and P, defines if there are in both traces
402 -- more element(s) in the instantiation chains. Returns
403 -- False if one trace contains more element(s), but
404 -- another does not. If both traces contains more
405 -- elements (that is, the function returns False), moves
406 -- P ahead in the chain corresponding to E, recomputes
407 -- Sindex and sets the name of the corresponding file in
410 function Skip_Spaces return Natural;
411 -- If Sloc_Trace (Idx) is not space character, returns
412 -- Idx. Otherwise returns the index of the nearest
413 -- non-space character in Sloc_Trace to the right of
414 -- Idx. Returns 0 if there is no such character.
416 function Different_Trace_Lengths return Boolean is
418 P := Instantiation (Sindex);
420 if (P = No_Location and then Idx /= 0)
422 (P /= No_Location and then Idx = 0)
427 if P /= No_Location then
428 Sindex := Get_Source_File_Index (P);
429 Get_Name_String (File_Name (Sindex));
434 end Different_Trace_Lengths;
436 function File_Mame_Match return Boolean is
445 for J in Idx .. Last loop
446 if Sloc_Trace (J) = ':' then
452 for J in reverse Idx .. Tmp_Idx loop
453 if Sloc_Trace (J) /= ' ' then
459 if Sloc_Trace (Idx .. End_Idx) =
460 Name_Buffer (1 .. Name_Len)
473 function Line_Num_Match return Boolean is
483 Sloc_Trace (Idx) in '0' .. '9'
486 (Character'Pos (Sloc_Trace (Idx)) -
487 Character'Pos ('0'));
492 if Get_Physical_Line_Number (P) =
493 Physical_Line_Number (N)
496 while Sloc_Trace (Idx) /= '['
503 if Sloc_Trace (Idx) = '['
520 function Skip_Spaces return Natural is
521 Res : Natural := Idx;
524 while Sloc_Trace (Res) = ' ' loop
538 Sindex := Get_Source_File_Index (P);
539 Get_Name_String (File_Name (Sindex));
545 if not File_Mame_Match then
547 elsif not Line_Num_Match then
551 if Different_Trace_Lengths then
558 -- If we have a Result_Type, then we must have a function
559 -- with the proper result type
561 if Elmt.Result_Type /= No_Name then
562 if Ekind (E) /= E_Function
563 or else Chars (Etype (E)) /= Elmt.Result_Type
569 -- If we have Parameter_Types, they must match
571 if Elmt.Parameter_Types /= null then
572 Form := First_Formal (E);
576 Elmt.Parameter_Types'Length = 1
578 Elmt.Parameter_Types (1) = No_Name
580 -- Parameterless procedure matches
584 elsif Elmt.Parameter_Types = null then
588 for J in Elmt.Parameter_Types'Range loop
591 Chars (Etype (Form)) /= Elmt.Parameter_Types (J)
599 if Present (Form) then
605 -- If we fall through, this is match
611 <<Continue>> Elmt := Elmt.Homonym;
616 end Check_Eliminated;
618 -------------------------
619 -- Eliminate_Error_Msg --
620 -------------------------
622 procedure Eliminate_Error_Msg (N : Node_Id; E : Entity_Id) is
624 for J in Elim_Entities.First .. Elim_Entities.Last loop
625 if E = Elim_Entities.Table (J).Subp then
626 Error_Msg_Sloc := Sloc (Elim_Entities.Table (J).Prag);
627 Error_Msg_NE ("cannot call subprogram & eliminated #", N, E);
632 -- Should never fall through, since entry should be in table
634 pragma Assert (False);
635 end Eliminate_Error_Msg;
641 procedure Initialize is
643 Elim_Hash_Table.Reset;
645 No_Elimination := True;
648 ------------------------------
649 -- Process_Eliminate_Pragma --
650 ------------------------------
652 procedure Process_Eliminate_Pragma
653 (Pragma_Node : Node_Id;
654 Arg_Unit_Name : Node_Id;
655 Arg_Entity : Node_Id;
656 Arg_Parameter_Types : Node_Id;
657 Arg_Result_Type : Node_Id;
658 Arg_Source_Location : Node_Id)
660 Data : constant Access_Elim_Data := new Elim_Data;
661 -- Build result data here
663 Elmt : Access_Elim_Data;
665 Num_Names : Nat := 0;
666 -- Number of names in unit name
672 function OK_Selected_Component (N : Node_Id) return Boolean;
673 -- Test if N is a selected component with all identifiers, or a
674 -- selected component whose selector is an operator symbol. As a
675 -- side effect if result is True, sets Num_Names to the number
676 -- of names present (identifiers and operator if any).
678 ---------------------------
679 -- OK_Selected_Component --
680 ---------------------------
682 function OK_Selected_Component (N : Node_Id) return Boolean is
684 if Nkind (N) = N_Identifier
685 or else Nkind (N) = N_Operator_Symbol
687 Num_Names := Num_Names + 1;
690 elsif Nkind (N) = N_Selected_Component then
691 return OK_Selected_Component (Prefix (N))
692 and then OK_Selected_Component (Selector_Name (N));
697 end OK_Selected_Component;
699 -- Start of processing for Process_Eliminate_Pragma
702 Data.Prag := Pragma_Node;
703 Error_Msg_Name_1 := Name_Eliminate;
705 -- Process Unit_Name argument
707 if Nkind (Arg_Unit_Name) = N_Identifier then
708 Data.Unit_Name := new Names'(1 => Chars (Arg_Unit_Name));
711 elsif OK_Selected_Component (Arg_Unit_Name) then
712 Data.Unit_Name := new Names (1 .. Num_Names);
714 Arg_Uname := Arg_Unit_Name;
715 for J in reverse 2 .. Num_Names loop
716 Data.Unit_Name (J) := Chars (Selector_Name (Arg_Uname));
717 Arg_Uname := Prefix (Arg_Uname);
720 Data.Unit_Name (1) := Chars (Arg_Uname);
724 ("wrong form for Unit_Name parameter of pragma%", Arg_Unit_Name);
728 -- Process Entity argument
730 if Present (Arg_Entity) then
733 if Nkind (Arg_Entity) = N_Identifier
734 or else Nkind (Arg_Entity) = N_Operator_Symbol
736 Data.Entity_Name := Chars (Arg_Entity);
737 Data.Entity_Node := Arg_Entity;
738 Data.Entity_Scope := null;
740 elsif OK_Selected_Component (Arg_Entity) then
741 Data.Entity_Scope := new Names (1 .. Num_Names - 1);
742 Data.Entity_Name := Chars (Selector_Name (Arg_Entity));
743 Data.Entity_Node := Arg_Entity;
745 Arg_Ent := Prefix (Arg_Entity);
746 for J in reverse 2 .. Num_Names - 1 loop
747 Data.Entity_Scope (J) := Chars (Selector_Name (Arg_Ent));
748 Arg_Ent := Prefix (Arg_Ent);
751 Data.Entity_Scope (1) := Chars (Arg_Ent);
753 elsif Nkind (Arg_Entity) = N_String_Literal then
754 String_To_Name_Buffer (Strval (Arg_Entity));
755 Data.Entity_Name := Name_Find;
756 Data.Entity_Node := Arg_Entity;
760 ("wrong form for Entity_Argument parameter of pragma%",
765 Data.Entity_Node := Empty;
766 Data.Entity_Name := Data.Unit_Name (Num_Names);
769 -- Process Parameter_Types argument
771 if Present (Arg_Parameter_Types) then
773 -- Case of one name, which looks like a parenthesized literal
774 -- rather than an aggregate.
776 if Nkind (Arg_Parameter_Types) = N_String_Literal
777 and then Paren_Count (Arg_Parameter_Types) = 1
779 String_To_Name_Buffer (Strval (Arg_Parameter_Types));
782 -- Parameterless procedure
783 Data.Parameter_Types := new Names'(1 => No_Name);
785 Data.Parameter_Types := new Names'(1 => Name_Find);
788 -- Otherwise must be an aggregate
790 elsif Nkind (Arg_Parameter_Types) /= N_Aggregate
791 or else Present (Component_Associations (Arg_Parameter_Types))
792 or else No (Expressions (Arg_Parameter_Types))
795 ("Parameter_Types for pragma% must be list of string literals",
796 Arg_Parameter_Types);
799 -- Here for aggregate case
802 Data.Parameter_Types :=
804 (1 .. List_Length (Expressions (Arg_Parameter_Types)));
806 Lit := First (Expressions (Arg_Parameter_Types));
807 for J in Data.Parameter_Types'Range loop
808 if Nkind (Lit) /= N_String_Literal then
810 ("parameter types for pragma% must be string literals",
815 String_To_Name_Buffer (Strval (Lit));
816 Data.Parameter_Types (J) := Name_Find;
822 -- Process Result_Types argument
824 if Present (Arg_Result_Type) then
826 if Nkind (Arg_Result_Type) /= N_String_Literal then
828 ("Result_Type argument for pragma% must be string literal",
833 String_To_Name_Buffer (Strval (Arg_Result_Type));
834 Data.Result_Type := Name_Find;
837 Data.Result_Type := No_Name;
840 -- Process Source_Location argument
842 if Present (Arg_Source_Location) then
844 if Nkind (Arg_Source_Location) /= N_String_Literal then
846 ("Source_Location argument for pragma% must be string literal",
847 Arg_Source_Location);
851 String_To_Name_Buffer (Strval (Arg_Source_Location));
852 Data.Source_Location := Name_Find;
855 Data.Source_Location := No_Name;
858 Elmt := Elim_Hash_Table.Get (Hash_Subprograms.Get_Key (Data));
860 -- If we already have an entry with this same key, then link
861 -- it into the chain of entries for this key.
864 Data.Homonym := Elmt.Homonym;
865 Elmt.Homonym := Data;
867 -- Otherwise create a new entry
870 Elim_Hash_Table.Set (Data);
873 No_Elimination := False;
874 end Process_Eliminate_Pragma;