111
|
1 ------------------------------------------------------------------------------
|
|
2 -- --
|
|
3 -- GNAT COMPILER COMPONENTS --
|
|
4 -- --
|
|
5 -- S E M _ E L I M --
|
|
6 -- --
|
|
7 -- B o d y --
|
|
8 -- --
|
|
9 -- Copyright (C) 1997-2017, Free Software Foundation, Inc. --
|
|
10 -- --
|
|
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. --
|
|
20 -- --
|
|
21 -- GNAT was originally developed by the GNAT team at New York University. --
|
|
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
|
|
23 -- --
|
|
24 ------------------------------------------------------------------------------
|
|
25
|
|
26 with Atree; use Atree;
|
|
27 with Einfo; use Einfo;
|
|
28 with Errout; use Errout;
|
|
29 with Lib; use Lib;
|
|
30 with Namet; use Namet;
|
|
31 with Nlists; use Nlists;
|
|
32 with Opt; use Opt;
|
|
33 with Sem; use Sem;
|
|
34 with Sem_Aux; use Sem_Aux;
|
|
35 with Sem_Prag; use Sem_Prag;
|
|
36 with Sem_Util; use Sem_Util;
|
|
37 with Sinput; use Sinput;
|
|
38 with Sinfo; use Sinfo;
|
|
39 with Snames; use Snames;
|
|
40 with Stand; use Stand;
|
|
41 with Stringt; use Stringt;
|
|
42 with Table;
|
|
43
|
|
44 with GNAT.HTable; use GNAT.HTable;
|
|
45
|
|
46 package body Sem_Elim is
|
|
47
|
|
48 No_Elimination : Boolean;
|
|
49 -- Set True if no Eliminate pragmas active
|
|
50
|
|
51 ---------------------
|
|
52 -- Data Structures --
|
|
53 ---------------------
|
|
54
|
|
55 -- A single pragma Eliminate is represented by the following record
|
|
56
|
|
57 type Elim_Data;
|
|
58 type Access_Elim_Data is access Elim_Data;
|
|
59
|
|
60 type Names is array (Nat range <>) of Name_Id;
|
|
61 -- Type used to represent set of names. Used for names in Unit_Name
|
|
62 -- and also the set of names in Argument_Types.
|
|
63
|
|
64 type Access_Names is access Names;
|
|
65
|
|
66 type Elim_Data is record
|
|
67
|
|
68 Unit_Name : Access_Names;
|
|
69 -- Unit name, broken down into a set of names (e.g. A.B.C is
|
|
70 -- represented as Name_Id values for A, B, C in sequence).
|
|
71
|
|
72 Entity_Name : Name_Id;
|
|
73 -- Entity name if Entity parameter if present. If no Entity parameter
|
|
74 -- was supplied, then Entity_Node is set to Empty, and the Entity_Name
|
|
75 -- field contains the last identifier name in the Unit_Name.
|
|
76
|
|
77 Entity_Scope : Access_Names;
|
|
78 -- Static scope of the entity within the compilation unit represented by
|
|
79 -- Unit_Name.
|
|
80
|
|
81 Entity_Node : Node_Id;
|
|
82 -- Save node of entity argument, for posting error messages. Set
|
|
83 -- to Empty if there is no entity argument.
|
|
84
|
|
85 Parameter_Types : Access_Names;
|
|
86 -- Set to set of names given for parameter types. If no parameter
|
|
87 -- types argument is present, this argument is set to null.
|
|
88
|
|
89 Result_Type : Name_Id;
|
|
90 -- Result type name if Result_Types parameter present, No_Name if not
|
|
91
|
|
92 Source_Location : Name_Id;
|
|
93 -- String describing the source location of subprogram defining name if
|
|
94 -- Source_Location parameter present, No_Name if not
|
|
95
|
|
96 Hash_Link : Access_Elim_Data;
|
|
97 -- Link for hash table use
|
|
98
|
|
99 Homonym : Access_Elim_Data;
|
|
100 -- Pointer to next entry with same key
|
|
101
|
|
102 Prag : Node_Id;
|
|
103 -- Node_Id for Eliminate pragma
|
|
104
|
|
105 end record;
|
|
106
|
|
107 ----------------
|
|
108 -- Hash_Table --
|
|
109 ----------------
|
|
110
|
|
111 -- Setup hash table using the Entity_Name field as the hash key
|
|
112
|
|
113 subtype Element is Elim_Data;
|
|
114 subtype Elmt_Ptr is Access_Elim_Data;
|
|
115
|
|
116 subtype Key is Name_Id;
|
|
117
|
|
118 type Header_Num is range 0 .. 1023;
|
|
119
|
|
120 Null_Ptr : constant Elmt_Ptr := null;
|
|
121
|
|
122 ----------------------
|
|
123 -- Hash_Subprograms --
|
|
124 ----------------------
|
|
125
|
|
126 package Hash_Subprograms is
|
|
127
|
|
128 function Equal (F1, F2 : Key) return Boolean;
|
|
129 pragma Inline (Equal);
|
|
130
|
|
131 function Get_Key (E : Elmt_Ptr) return Key;
|
|
132 pragma Inline (Get_Key);
|
|
133
|
|
134 function Hash (F : Key) return Header_Num;
|
|
135 pragma Inline (Hash);
|
|
136
|
|
137 function Next (E : Elmt_Ptr) return Elmt_Ptr;
|
|
138 pragma Inline (Next);
|
|
139
|
|
140 procedure Set_Next (E : Elmt_Ptr; Next : Elmt_Ptr);
|
|
141 pragma Inline (Set_Next);
|
|
142
|
|
143 end Hash_Subprograms;
|
|
144
|
|
145 package body Hash_Subprograms is
|
|
146
|
|
147 -----------
|
|
148 -- Equal --
|
|
149 -----------
|
|
150
|
|
151 function Equal (F1, F2 : Key) return Boolean is
|
|
152 begin
|
|
153 return F1 = F2;
|
|
154 end Equal;
|
|
155
|
|
156 -------------
|
|
157 -- Get_Key --
|
|
158 -------------
|
|
159
|
|
160 function Get_Key (E : Elmt_Ptr) return Key is
|
|
161 begin
|
|
162 return E.Entity_Name;
|
|
163 end Get_Key;
|
|
164
|
|
165 ----------
|
|
166 -- Hash --
|
|
167 ----------
|
|
168
|
|
169 function Hash (F : Key) return Header_Num is
|
|
170 begin
|
|
171 return Header_Num (Int (F) mod 1024);
|
|
172 end Hash;
|
|
173
|
|
174 ----------
|
|
175 -- Next --
|
|
176 ----------
|
|
177
|
|
178 function Next (E : Elmt_Ptr) return Elmt_Ptr is
|
|
179 begin
|
|
180 return E.Hash_Link;
|
|
181 end Next;
|
|
182
|
|
183 --------------
|
|
184 -- Set_Next --
|
|
185 --------------
|
|
186
|
|
187 procedure Set_Next (E : Elmt_Ptr; Next : Elmt_Ptr) is
|
|
188 begin
|
|
189 E.Hash_Link := Next;
|
|
190 end Set_Next;
|
|
191 end Hash_Subprograms;
|
|
192
|
|
193 ------------
|
|
194 -- Tables --
|
|
195 ------------
|
|
196
|
|
197 -- The following table records the data for each pragma, using the
|
|
198 -- entity name as the hash key for retrieval. Entries in this table
|
|
199 -- are set by Process_Eliminate_Pragma and read by Check_Eliminated.
|
|
200
|
|
201 package Elim_Hash_Table is new Static_HTable (
|
|
202 Header_Num => Header_Num,
|
|
203 Element => Element,
|
|
204 Elmt_Ptr => Elmt_Ptr,
|
|
205 Null_Ptr => Null_Ptr,
|
|
206 Set_Next => Hash_Subprograms.Set_Next,
|
|
207 Next => Hash_Subprograms.Next,
|
|
208 Key => Key,
|
|
209 Get_Key => Hash_Subprograms.Get_Key,
|
|
210 Hash => Hash_Subprograms.Hash,
|
|
211 Equal => Hash_Subprograms.Equal);
|
|
212
|
|
213 -- The following table records entities for subprograms that are
|
|
214 -- eliminated, and corresponding eliminate pragmas that caused the
|
|
215 -- elimination. Entries in this table are set by Check_Eliminated
|
|
216 -- and read by Eliminate_Error_Msg.
|
|
217
|
|
218 type Elim_Entity_Entry is record
|
|
219 Prag : Node_Id;
|
|
220 Subp : Entity_Id;
|
|
221 end record;
|
|
222
|
|
223 package Elim_Entities is new Table.Table (
|
|
224 Table_Component_Type => Elim_Entity_Entry,
|
|
225 Table_Index_Type => Name_Id'Base,
|
|
226 Table_Low_Bound => First_Name_Id,
|
|
227 Table_Initial => 50,
|
|
228 Table_Increment => 200,
|
|
229 Table_Name => "Elim_Entries");
|
|
230
|
|
231 ----------------------
|
|
232 -- Check_Eliminated --
|
|
233 ----------------------
|
|
234
|
|
235 procedure Check_Eliminated (E : Entity_Id) is
|
|
236 Elmt : Access_Elim_Data;
|
|
237 Scop : Entity_Id;
|
|
238 Form : Entity_Id;
|
|
239 Up : Nat;
|
|
240
|
|
241 begin
|
|
242 if No_Elimination then
|
|
243 return;
|
|
244
|
|
245 -- Elimination of objects and types is not implemented yet
|
|
246
|
|
247 elsif Ekind (E) not in Subprogram_Kind then
|
|
248 return;
|
|
249 end if;
|
|
250
|
|
251 -- Loop through homonyms for this key
|
|
252
|
|
253 Elmt := Elim_Hash_Table.Get (Chars (E));
|
|
254 while Elmt /= null loop
|
|
255 Check_Homonyms : declare
|
|
256 procedure Set_Eliminated;
|
|
257 -- Set current subprogram entity as eliminated
|
|
258
|
|
259 --------------------
|
|
260 -- Set_Eliminated --
|
|
261 --------------------
|
|
262
|
|
263 procedure Set_Eliminated is
|
|
264 Overridden : Entity_Id;
|
|
265
|
|
266 begin
|
|
267 if Is_Dispatching_Operation (E) then
|
|
268
|
|
269 -- If an overriding dispatching primitive is eliminated then
|
|
270 -- its parent must have been eliminated. If the parent is an
|
|
271 -- inherited operation, check the operation that it renames,
|
|
272 -- because flag Eliminated is only set on source operations.
|
|
273
|
|
274 Overridden := Overridden_Operation (E);
|
|
275
|
|
276 if Present (Overridden)
|
|
277 and then not Comes_From_Source (Overridden)
|
|
278 and then Present (Alias (Overridden))
|
|
279 then
|
|
280 Overridden := Alias (Overridden);
|
|
281 end if;
|
|
282
|
|
283 if Present (Overridden)
|
|
284 and then not Is_Eliminated (Overridden)
|
|
285 and then not Is_Abstract_Subprogram (Overridden)
|
|
286 then
|
|
287 Error_Msg_Name_1 := Chars (E);
|
|
288 Error_Msg_N ("cannot eliminate subprogram %", E);
|
|
289 return;
|
|
290 end if;
|
|
291 end if;
|
|
292
|
|
293 Set_Is_Eliminated (E);
|
|
294 Elim_Entities.Append ((Prag => Elmt.Prag, Subp => E));
|
|
295 end Set_Eliminated;
|
|
296
|
|
297 -- Start of processing for Check_Homonyms
|
|
298
|
|
299 begin
|
|
300 -- First we check that the name of the entity matches
|
|
301
|
|
302 if Elmt.Entity_Name /= Chars (E) then
|
|
303 goto Continue;
|
|
304 end if;
|
|
305
|
|
306 -- Find enclosing unit, and verify that its name and those of its
|
|
307 -- parents match.
|
|
308
|
|
309 Scop := Cunit_Entity (Current_Sem_Unit);
|
|
310
|
|
311 -- Now see if compilation unit matches
|
|
312
|
|
313 Up := Elmt.Unit_Name'Last;
|
|
314
|
|
315 -- If we are within a subunit, the name in the pragma has been
|
|
316 -- parsed as a child unit, but the current compilation unit is in
|
|
317 -- fact the parent in which the subunit is embedded. We must skip
|
|
318 -- the first name which is that of the subunit to match the pragma
|
|
319 -- specification. Body may be that of a package or subprogram.
|
|
320
|
|
321 declare
|
|
322 Par : Node_Id;
|
|
323
|
|
324 begin
|
|
325 Par := Parent (E);
|
|
326 while Present (Par) loop
|
|
327 if Nkind (Par) = N_Subunit then
|
|
328 if Chars (Defining_Entity (Proper_Body (Par))) =
|
|
329 Elmt.Unit_Name (Up)
|
|
330 then
|
|
331 Up := Up - 1;
|
|
332 exit;
|
|
333
|
|
334 else
|
|
335 goto Continue;
|
|
336 end if;
|
|
337 end if;
|
|
338
|
|
339 Par := Parent (Par);
|
|
340 end loop;
|
|
341 end;
|
|
342
|
|
343 for J in reverse Elmt.Unit_Name'First .. Up loop
|
|
344 if Elmt.Unit_Name (J) /= Chars (Scop) then
|
|
345 goto Continue;
|
|
346 end if;
|
|
347
|
|
348 Scop := Scope (Scop);
|
|
349
|
|
350 if Scop /= Standard_Standard and then J = 1 then
|
|
351 goto Continue;
|
|
352 end if;
|
|
353 end loop;
|
|
354
|
|
355 if Scop /= Standard_Standard then
|
|
356 goto Continue;
|
|
357 end if;
|
|
358
|
|
359 if Present (Elmt.Entity_Node)
|
|
360 and then Elmt.Entity_Scope /= null
|
|
361 then
|
|
362 -- Check that names of enclosing scopes match. Skip blocks and
|
|
363 -- wrapper package of subprogram instances, which do not appear
|
|
364 -- in the pragma.
|
|
365
|
|
366 Scop := Scope (E);
|
|
367
|
|
368 for J in reverse Elmt.Entity_Scope'Range loop
|
|
369 while Ekind (Scop) = E_Block
|
|
370 or else
|
|
371 (Ekind (Scop) = E_Package
|
|
372 and then Is_Wrapper_Package (Scop))
|
|
373 loop
|
|
374 Scop := Scope (Scop);
|
|
375 end loop;
|
|
376
|
|
377 if Elmt.Entity_Scope (J) /= Chars (Scop) then
|
|
378 if Ekind (Scop) /= E_Protected_Type
|
|
379 or else Comes_From_Source (Scop)
|
|
380 then
|
|
381 goto Continue;
|
|
382
|
|
383 -- For simple protected declarations, retrieve the source
|
|
384 -- name of the object, which appeared in the Eliminate
|
|
385 -- pragma.
|
|
386
|
|
387 else
|
|
388 declare
|
|
389 Decl : constant Node_Id :=
|
|
390 Original_Node (Parent (Scop));
|
|
391
|
|
392 begin
|
|
393 if Elmt.Entity_Scope (J) /=
|
|
394 Chars (Defining_Identifier (Decl))
|
|
395 then
|
|
396 if J > 0 then
|
|
397 null;
|
|
398 end if;
|
|
399 goto Continue;
|
|
400 end if;
|
|
401 end;
|
|
402 end if;
|
|
403
|
|
404 end if;
|
|
405
|
|
406 Scop := Scope (Scop);
|
|
407 end loop;
|
|
408 end if;
|
|
409
|
|
410 -- If given entity is a library level subprogram and pragma had a
|
|
411 -- single parameter, a match.
|
|
412
|
|
413 if Is_Compilation_Unit (E)
|
|
414 and then Is_Subprogram (E)
|
|
415 and then No (Elmt.Entity_Node)
|
|
416 then
|
|
417 Set_Eliminated;
|
|
418 return;
|
|
419
|
|
420 -- Check for case of type or object with two parameter case
|
|
421
|
|
422 elsif (Is_Type (E) or else Is_Object (E))
|
|
423 and then Elmt.Result_Type = No_Name
|
|
424 and then Elmt.Parameter_Types = null
|
|
425 then
|
|
426 Set_Eliminated;
|
|
427 return;
|
|
428
|
|
429 -- Check for case of subprogram
|
|
430
|
|
431 elsif Ekind_In (E, E_Function, E_Procedure) then
|
|
432
|
|
433 -- If Source_Location present, then see if it matches
|
|
434
|
|
435 if Elmt.Source_Location /= No_Name then
|
|
436 Get_Name_String (Elmt.Source_Location);
|
|
437
|
|
438 declare
|
|
439 Sloc_Trace : constant String :=
|
|
440 Name_Buffer (1 .. Name_Len);
|
|
441
|
|
442 Idx : Natural := Sloc_Trace'First;
|
|
443 -- Index in Sloc_Trace, if equals to 0, then we have
|
|
444 -- completely traversed Sloc_Trace
|
|
445
|
|
446 Last : constant Natural := Sloc_Trace'Last;
|
|
447
|
|
448 P : Source_Ptr;
|
|
449 Sindex : Source_File_Index;
|
|
450
|
|
451 function File_Name_Match return Boolean;
|
|
452 -- This function is supposed to be called when Idx points
|
|
453 -- to the beginning of the new file name, and Name_Buffer
|
|
454 -- is set to contain the name of the proper source file
|
|
455 -- from the chain corresponding to the Sloc of E. First
|
|
456 -- it checks that these two files have the same name. If
|
|
457 -- this check is successful, moves Idx to point to the
|
|
458 -- beginning of the column number.
|
|
459
|
|
460 function Line_Num_Match return Boolean;
|
|
461 -- This function is supposed to be called when Idx points
|
|
462 -- to the beginning of the column number, and P is
|
|
463 -- set to point to the proper Sloc the chain
|
|
464 -- corresponding to the Sloc of E. First it checks that
|
|
465 -- the line number Idx points on and the line number
|
|
466 -- corresponding to P are the same. If this check is
|
|
467 -- successful, moves Idx to point to the beginning of
|
|
468 -- the next file name in Sloc_Trace. If there is no file
|
|
469 -- name any more, Idx is set to 0.
|
|
470
|
|
471 function Different_Trace_Lengths return Boolean;
|
|
472 -- From Idx and P, defines if there are in both traces
|
|
473 -- more element(s) in the instantiation chains. Returns
|
|
474 -- False if one trace contains more element(s), but
|
|
475 -- another does not. If both traces contains more
|
|
476 -- elements (that is, the function returns False), moves
|
|
477 -- P ahead in the chain corresponding to E, recomputes
|
|
478 -- Sindex and sets the name of the corresponding file in
|
|
479 -- Name_Buffer
|
|
480
|
|
481 function Skip_Spaces return Natural;
|
|
482 -- If Sloc_Trace (Idx) is not space character, returns
|
|
483 -- Idx. Otherwise returns the index of the nearest
|
|
484 -- non-space character in Sloc_Trace to the right of Idx.
|
|
485 -- Returns 0 if there is no such character.
|
|
486
|
|
487 -----------------------------
|
|
488 -- Different_Trace_Lengths --
|
|
489 -----------------------------
|
|
490
|
|
491 function Different_Trace_Lengths return Boolean is
|
|
492 begin
|
|
493 P := Instantiation (Sindex);
|
|
494
|
|
495 if (P = No_Location and then Idx /= 0)
|
|
496 or else
|
|
497 (P /= No_Location and then Idx = 0)
|
|
498 then
|
|
499 return True;
|
|
500
|
|
501 else
|
|
502 if P /= No_Location then
|
|
503 Sindex := Get_Source_File_Index (P);
|
|
504 Get_Name_String (File_Name (Sindex));
|
|
505 end if;
|
|
506
|
|
507 return False;
|
|
508 end if;
|
|
509 end Different_Trace_Lengths;
|
|
510
|
|
511 ---------------------
|
|
512 -- File_Name_Match --
|
|
513 ---------------------
|
|
514
|
|
515 function File_Name_Match return Boolean is
|
|
516 Tmp_Idx : Natural;
|
|
517 End_Idx : Natural;
|
|
518
|
|
519 begin
|
|
520 if Idx = 0 then
|
|
521 return False;
|
|
522 end if;
|
|
523
|
|
524 -- Find first colon. If no colon, then return False.
|
|
525 -- If there is a colon, Tmp_Idx is set to point just
|
|
526 -- before the colon.
|
|
527
|
|
528 Tmp_Idx := Idx - 1;
|
|
529 loop
|
|
530 if Tmp_Idx >= Last then
|
|
531 return False;
|
|
532 elsif Sloc_Trace (Tmp_Idx + 1) = ':' then
|
|
533 exit;
|
|
534 else
|
|
535 Tmp_Idx := Tmp_Idx + 1;
|
|
536 end if;
|
|
537 end loop;
|
|
538
|
|
539 -- Find last non-space before this colon. If there is
|
|
540 -- no space character before this colon, then return
|
|
541 -- False. Otherwise, End_Idx is set to point to this
|
|
542 -- non-space character.
|
|
543
|
|
544 End_Idx := Tmp_Idx;
|
|
545 loop
|
|
546 if End_Idx < Idx then
|
|
547 return False;
|
|
548
|
|
549 elsif Sloc_Trace (End_Idx) /= ' ' then
|
|
550 exit;
|
|
551
|
|
552 else
|
|
553 End_Idx := End_Idx - 1;
|
|
554 end if;
|
|
555 end loop;
|
|
556
|
|
557 -- Now see if file name matches what is in Name_Buffer
|
|
558 -- and if so, step Idx past it and return True. If the
|
|
559 -- name does not match, return False.
|
|
560
|
|
561 if Sloc_Trace (Idx .. End_Idx) =
|
|
562 Name_Buffer (1 .. Name_Len)
|
|
563 then
|
|
564 Idx := Tmp_Idx + 2;
|
|
565 Idx := Skip_Spaces;
|
|
566 return True;
|
|
567 else
|
|
568 return False;
|
|
569 end if;
|
|
570 end File_Name_Match;
|
|
571
|
|
572 --------------------
|
|
573 -- Line_Num_Match --
|
|
574 --------------------
|
|
575
|
|
576 function Line_Num_Match return Boolean is
|
|
577 N : Nat := 0;
|
|
578
|
|
579 begin
|
|
580 if Idx = 0 then
|
|
581 return False;
|
|
582 end if;
|
|
583
|
|
584 while Idx <= Last
|
|
585 and then Sloc_Trace (Idx) in '0' .. '9'
|
|
586 loop
|
|
587 N := N * 10 +
|
|
588 (Character'Pos (Sloc_Trace (Idx)) -
|
|
589 Character'Pos ('0'));
|
|
590 Idx := Idx + 1;
|
|
591 end loop;
|
|
592
|
|
593 if Get_Physical_Line_Number (P) =
|
|
594 Physical_Line_Number (N)
|
|
595 then
|
|
596 while Idx <= Last and then
|
|
597 Sloc_Trace (Idx) /= '['
|
|
598 loop
|
|
599 Idx := Idx + 1;
|
|
600 end loop;
|
|
601
|
|
602 if Idx <= Last then
|
|
603 pragma Assert (Sloc_Trace (Idx) = '[');
|
|
604 Idx := Idx + 1;
|
|
605 Idx := Skip_Spaces;
|
|
606 else
|
|
607 Idx := 0;
|
|
608 end if;
|
|
609
|
|
610 return True;
|
|
611
|
|
612 else
|
|
613 return False;
|
|
614 end if;
|
|
615 end Line_Num_Match;
|
|
616
|
|
617 -----------------
|
|
618 -- Skip_Spaces --
|
|
619 -----------------
|
|
620
|
|
621 function Skip_Spaces return Natural is
|
|
622 Res : Natural;
|
|
623
|
|
624 begin
|
|
625 Res := Idx;
|
|
626 while Sloc_Trace (Res) = ' ' loop
|
|
627 Res := Res + 1;
|
|
628
|
|
629 if Res > Last then
|
|
630 Res := 0;
|
|
631 exit;
|
|
632 end if;
|
|
633 end loop;
|
|
634
|
|
635 return Res;
|
|
636 end Skip_Spaces;
|
|
637
|
|
638 begin
|
|
639 P := Sloc (E);
|
|
640 Sindex := Get_Source_File_Index (P);
|
|
641 Get_Name_String (File_Name (Sindex));
|
|
642
|
|
643 Idx := Skip_Spaces;
|
|
644 while Idx > 0 loop
|
|
645 if not File_Name_Match then
|
|
646 goto Continue;
|
|
647 elsif not Line_Num_Match then
|
|
648 goto Continue;
|
|
649 end if;
|
|
650
|
|
651 if Different_Trace_Lengths then
|
|
652 goto Continue;
|
|
653 end if;
|
|
654 end loop;
|
|
655 end;
|
|
656 end if;
|
|
657
|
|
658 -- If we have a Result_Type, then we must have a function with
|
|
659 -- the proper result type.
|
|
660
|
|
661 if Elmt.Result_Type /= No_Name then
|
|
662 if Ekind (E) /= E_Function
|
|
663 or else Chars (Etype (E)) /= Elmt.Result_Type
|
|
664 then
|
|
665 goto Continue;
|
|
666 end if;
|
|
667 end if;
|
|
668
|
|
669 -- If we have Parameter_Types, they must match
|
|
670
|
|
671 if Elmt.Parameter_Types /= null then
|
|
672 Form := First_Formal (E);
|
|
673
|
|
674 if No (Form)
|
|
675 and then Elmt.Parameter_Types'Length = 1
|
|
676 and then Elmt.Parameter_Types (1) = No_Name
|
|
677 then
|
|
678 -- Parameterless procedure matches
|
|
679
|
|
680 null;
|
|
681
|
|
682 elsif Elmt.Parameter_Types = null then
|
|
683 goto Continue;
|
|
684
|
|
685 else
|
|
686 for J in Elmt.Parameter_Types'Range loop
|
|
687 if No (Form)
|
|
688 or else
|
|
689 Chars (Etype (Form)) /= Elmt.Parameter_Types (J)
|
|
690 then
|
|
691 goto Continue;
|
|
692 else
|
|
693 Next_Formal (Form);
|
|
694 end if;
|
|
695 end loop;
|
|
696
|
|
697 if Present (Form) then
|
|
698 goto Continue;
|
|
699 end if;
|
|
700 end if;
|
|
701 end if;
|
|
702
|
|
703 -- If we fall through, this is match
|
|
704
|
|
705 Set_Eliminated;
|
|
706 return;
|
|
707 end if;
|
|
708 end Check_Homonyms;
|
|
709
|
|
710 <<Continue>>
|
|
711 Elmt := Elmt.Homonym;
|
|
712 end loop;
|
|
713
|
|
714 return;
|
|
715 end Check_Eliminated;
|
|
716
|
|
717 -------------------------------------
|
|
718 -- Check_For_Eliminated_Subprogram --
|
|
719 -------------------------------------
|
|
720
|
|
721 procedure Check_For_Eliminated_Subprogram (N : Node_Id; S : Entity_Id) is
|
|
722 Ultimate_Subp : constant Entity_Id := Ultimate_Alias (S);
|
|
723 Enclosing_Subp : Entity_Id;
|
|
724
|
|
725 begin
|
|
726 -- No check needed within a default expression for a formal, since this
|
|
727 -- is not really a use, and the expression (a call or attribute) may
|
|
728 -- never be used if the enclosing subprogram is itself eliminated.
|
|
729
|
|
730 if In_Spec_Expression then
|
|
731 return;
|
|
732 end if;
|
|
733
|
|
734 if Is_Eliminated (Ultimate_Subp)
|
|
735 and then not Inside_A_Generic
|
|
736 and then not Is_Generic_Unit (Cunit_Entity (Current_Sem_Unit))
|
|
737 then
|
|
738 Enclosing_Subp := Current_Subprogram;
|
|
739 while Present (Enclosing_Subp) loop
|
|
740 if Is_Eliminated (Enclosing_Subp) then
|
|
741 return;
|
|
742 end if;
|
|
743
|
|
744 Enclosing_Subp := Enclosing_Subprogram (Enclosing_Subp);
|
|
745 end loop;
|
|
746
|
|
747 -- Emit error, unless we are within an instance body and the expander
|
|
748 -- is disabled, indicating an instance within an enclosing generic.
|
|
749 -- In an instance, the ultimate alias is an internal entity, so place
|
|
750 -- the message on the original subprogram.
|
|
751
|
|
752 if In_Instance_Body and then not Expander_Active then
|
|
753 null;
|
|
754
|
|
755 elsif Comes_From_Source (Ultimate_Subp) then
|
|
756 Eliminate_Error_Msg (N, Ultimate_Subp);
|
|
757
|
|
758 else
|
|
759 Eliminate_Error_Msg (N, S);
|
|
760 end if;
|
|
761 end if;
|
|
762 end Check_For_Eliminated_Subprogram;
|
|
763
|
|
764 -------------------------
|
|
765 -- Eliminate_Error_Msg --
|
|
766 -------------------------
|
|
767
|
|
768 procedure Eliminate_Error_Msg (N : Node_Id; E : Entity_Id) is
|
|
769 begin
|
|
770 for J in Elim_Entities.First .. Elim_Entities.Last loop
|
|
771 if E = Elim_Entities.Table (J).Subp then
|
|
772 Error_Msg_Sloc := Sloc (Elim_Entities.Table (J).Prag);
|
|
773 Error_Msg_NE ("cannot reference subprogram & eliminated #", N, E);
|
|
774 return;
|
|
775 end if;
|
|
776 end loop;
|
|
777
|
|
778 -- If this is an internal operation generated for a protected operation,
|
|
779 -- its name does not match the source name, so just report the error.
|
|
780
|
|
781 if not Comes_From_Source (E)
|
|
782 and then Present (First_Entity (E))
|
|
783 and then Is_Concurrent_Record_Type (Etype (First_Entity (E)))
|
|
784 then
|
|
785 Error_Msg_NE
|
|
786 ("cannot reference eliminated protected subprogram", N, E);
|
|
787
|
|
788 -- Otherwise should not fall through, entry should be in table
|
|
789
|
|
790 else
|
|
791 Error_Msg_NE
|
|
792 ("subprogram& is called but its alias is eliminated", N, E);
|
|
793 -- raise Program_Error;
|
|
794 end if;
|
|
795 end Eliminate_Error_Msg;
|
|
796
|
|
797 ----------------
|
|
798 -- Initialize --
|
|
799 ----------------
|
|
800
|
|
801 procedure Initialize is
|
|
802 begin
|
|
803 Elim_Hash_Table.Reset;
|
|
804 Elim_Entities.Init;
|
|
805 No_Elimination := True;
|
|
806 end Initialize;
|
|
807
|
|
808 ------------------------------
|
|
809 -- Process_Eliminate_Pragma --
|
|
810 ------------------------------
|
|
811
|
|
812 procedure Process_Eliminate_Pragma
|
|
813 (Pragma_Node : Node_Id;
|
|
814 Arg_Unit_Name : Node_Id;
|
|
815 Arg_Entity : Node_Id;
|
|
816 Arg_Parameter_Types : Node_Id;
|
|
817 Arg_Result_Type : Node_Id;
|
|
818 Arg_Source_Location : Node_Id)
|
|
819 is
|
|
820 Data : constant Access_Elim_Data := new Elim_Data;
|
|
821 -- Build result data here
|
|
822
|
|
823 Elmt : Access_Elim_Data;
|
|
824
|
|
825 Num_Names : Nat := 0;
|
|
826 -- Number of names in unit name
|
|
827
|
|
828 Lit : Node_Id;
|
|
829 Arg_Ent : Entity_Id;
|
|
830 Arg_Uname : Node_Id;
|
|
831
|
|
832 function OK_Selected_Component (N : Node_Id) return Boolean;
|
|
833 -- Test if N is a selected component with all identifiers, or a selected
|
|
834 -- component whose selector is an operator symbol. As a side effect
|
|
835 -- if result is True, sets Num_Names to the number of names present
|
|
836 -- (identifiers, and operator if any).
|
|
837
|
|
838 ---------------------------
|
|
839 -- OK_Selected_Component --
|
|
840 ---------------------------
|
|
841
|
|
842 function OK_Selected_Component (N : Node_Id) return Boolean is
|
|
843 begin
|
|
844 if Nkind (N) = N_Identifier
|
|
845 or else Nkind (N) = N_Operator_Symbol
|
|
846 then
|
|
847 Num_Names := Num_Names + 1;
|
|
848 return True;
|
|
849
|
|
850 elsif Nkind (N) = N_Selected_Component then
|
|
851 return OK_Selected_Component (Prefix (N))
|
|
852 and then OK_Selected_Component (Selector_Name (N));
|
|
853
|
|
854 else
|
|
855 return False;
|
|
856 end if;
|
|
857 end OK_Selected_Component;
|
|
858
|
|
859 -- Start of processing for Process_Eliminate_Pragma
|
|
860
|
|
861 begin
|
|
862 Data.Prag := Pragma_Node;
|
|
863 Error_Msg_Name_1 := Name_Eliminate;
|
|
864
|
|
865 -- Process Unit_Name argument
|
|
866
|
|
867 if Nkind (Arg_Unit_Name) = N_Identifier then
|
|
868 Data.Unit_Name := new Names'(1 => Chars (Arg_Unit_Name));
|
|
869 Num_Names := 1;
|
|
870
|
|
871 elsif OK_Selected_Component (Arg_Unit_Name) then
|
|
872 Data.Unit_Name := new Names (1 .. Num_Names);
|
|
873
|
|
874 Arg_Uname := Arg_Unit_Name;
|
|
875 for J in reverse 2 .. Num_Names loop
|
|
876 Data.Unit_Name (J) := Chars (Selector_Name (Arg_Uname));
|
|
877 Arg_Uname := Prefix (Arg_Uname);
|
|
878 end loop;
|
|
879
|
|
880 Data.Unit_Name (1) := Chars (Arg_Uname);
|
|
881
|
|
882 else
|
|
883 Error_Msg_N
|
|
884 ("wrong form for Unit_Name parameter of pragma%", Arg_Unit_Name);
|
|
885 return;
|
|
886 end if;
|
|
887
|
|
888 -- Process Entity argument
|
|
889
|
|
890 if Present (Arg_Entity) then
|
|
891 Num_Names := 0;
|
|
892
|
|
893 if Nkind (Arg_Entity) = N_Identifier
|
|
894 or else Nkind (Arg_Entity) = N_Operator_Symbol
|
|
895 then
|
|
896 Data.Entity_Name := Chars (Arg_Entity);
|
|
897 Data.Entity_Node := Arg_Entity;
|
|
898 Data.Entity_Scope := null;
|
|
899
|
|
900 elsif OK_Selected_Component (Arg_Entity) then
|
|
901 Data.Entity_Scope := new Names (1 .. Num_Names - 1);
|
|
902 Data.Entity_Name := Chars (Selector_Name (Arg_Entity));
|
|
903 Data.Entity_Node := Arg_Entity;
|
|
904
|
|
905 Arg_Ent := Prefix (Arg_Entity);
|
|
906 for J in reverse 2 .. Num_Names - 1 loop
|
|
907 Data.Entity_Scope (J) := Chars (Selector_Name (Arg_Ent));
|
|
908 Arg_Ent := Prefix (Arg_Ent);
|
|
909 end loop;
|
|
910
|
|
911 Data.Entity_Scope (1) := Chars (Arg_Ent);
|
|
912
|
|
913 elsif Is_Config_Static_String (Arg_Entity) then
|
|
914 Data.Entity_Name := Name_Find;
|
|
915 Data.Entity_Node := Arg_Entity;
|
|
916
|
|
917 else
|
|
918 return;
|
|
919 end if;
|
|
920 else
|
|
921 Data.Entity_Node := Empty;
|
|
922 Data.Entity_Name := Data.Unit_Name (Num_Names);
|
|
923 end if;
|
|
924
|
|
925 -- Process Parameter_Types argument
|
|
926
|
|
927 if Present (Arg_Parameter_Types) then
|
|
928
|
|
929 -- Here for aggregate case
|
|
930
|
|
931 if Nkind (Arg_Parameter_Types) = N_Aggregate then
|
|
932 Data.Parameter_Types :=
|
|
933 new Names
|
|
934 (1 .. List_Length (Expressions (Arg_Parameter_Types)));
|
|
935
|
|
936 Lit := First (Expressions (Arg_Parameter_Types));
|
|
937 for J in Data.Parameter_Types'Range loop
|
|
938 if Is_Config_Static_String (Lit) then
|
|
939 Data.Parameter_Types (J) := Name_Find;
|
|
940 Next (Lit);
|
|
941 else
|
|
942 return;
|
|
943 end if;
|
|
944 end loop;
|
|
945
|
|
946 -- Otherwise we must have case of one name, which looks like a
|
|
947 -- parenthesized literal rather than an aggregate.
|
|
948
|
|
949 elsif Paren_Count (Arg_Parameter_Types) /= 1 then
|
|
950 Error_Msg_N
|
|
951 ("wrong form for argument of pragma Eliminate",
|
|
952 Arg_Parameter_Types);
|
|
953 return;
|
|
954
|
|
955 elsif Is_Config_Static_String (Arg_Parameter_Types) then
|
|
956 String_To_Name_Buffer (Strval (Arg_Parameter_Types));
|
|
957
|
|
958 if Name_Len = 0 then
|
|
959
|
|
960 -- Parameterless procedure
|
|
961
|
|
962 Data.Parameter_Types := new Names'(1 => No_Name);
|
|
963
|
|
964 else
|
|
965 Data.Parameter_Types := new Names'(1 => Name_Find);
|
|
966 end if;
|
|
967
|
|
968 else
|
|
969 return;
|
|
970 end if;
|
|
971 end if;
|
|
972
|
|
973 -- Process Result_Types argument
|
|
974
|
|
975 if Present (Arg_Result_Type) then
|
|
976 if Is_Config_Static_String (Arg_Result_Type) then
|
|
977 Data.Result_Type := Name_Find;
|
|
978 else
|
|
979 return;
|
|
980 end if;
|
|
981
|
|
982 -- Here if no Result_Types argument
|
|
983
|
|
984 else
|
|
985 Data.Result_Type := No_Name;
|
|
986 end if;
|
|
987
|
|
988 -- Process Source_Location argument
|
|
989
|
|
990 if Present (Arg_Source_Location) then
|
|
991 if Is_Config_Static_String (Arg_Source_Location) then
|
|
992 Data.Source_Location := Name_Find;
|
|
993 else
|
|
994 return;
|
|
995 end if;
|
|
996 else
|
|
997 Data.Source_Location := No_Name;
|
|
998 end if;
|
|
999
|
|
1000 Elmt := Elim_Hash_Table.Get (Hash_Subprograms.Get_Key (Data));
|
|
1001
|
|
1002 -- If we already have an entry with this same key, then link
|
|
1003 -- it into the chain of entries for this key.
|
|
1004
|
|
1005 if Elmt /= null then
|
|
1006 Data.Homonym := Elmt.Homonym;
|
|
1007 Elmt.Homonym := Data;
|
|
1008
|
|
1009 -- Otherwise create a new entry
|
|
1010
|
|
1011 else
|
|
1012 Elim_Hash_Table.Set (Data);
|
|
1013 end if;
|
|
1014
|
|
1015 No_Elimination := False;
|
|
1016 end Process_Eliminate_Pragma;
|
|
1017
|
|
1018 end Sem_Elim;
|