1------------------------------------------------------------------------------
2--                                                                          --
3--                         GNAT COMPILER COMPONENTS                         --
4--                                                                          --
5--                             P A R . L A B L                              --
6--                                                                          --
7--                                 B o d y                                  --
8--                                                                          --
9--          Copyright (C) 1992-2013, 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
26separate (Par)
27procedure Labl is
28   Enclosing_Body_Or_Block : Node_Id;
29   --  Innermost enclosing body or block statement
30
31   Label_Decl_Node : Node_Id;
32   --  Implicit label declaration node
33
34   Defining_Ident_Node : Node_Id;
35   --  Defining identifier node for implicit label declaration
36
37   Next_Label_Elmt : Elmt_Id;
38   --  Next element on label element list
39
40   Label_Node : Node_Id;
41   --  Next label node to process
42
43   function Find_Enclosing_Body_Or_Block (N : Node_Id) return Node_Id;
44   --  Find the innermost body or block that encloses N
45
46   function Find_Enclosing_Body (N : Node_Id) return Node_Id;
47   --  Find the innermost body that encloses N
48
49   procedure Check_Distinct_Labels;
50   --  Checks the rule in RM-5.1(11), which requires distinct identifiers
51   --  for all the labels in a given body.
52
53   procedure Find_Natural_Loops;
54   --  Recognizes loops created by backward gotos, and rewrites the
55   --  corresponding statements into a proper loop, for optimization
56   --  purposes (for example, to control reclaiming local storage).
57
58   ---------------------------
59   -- Check_Distinct_Labels --
60   ---------------------------
61
62   procedure Check_Distinct_Labels is
63      Label_Id : constant Node_Id := Identifier (Label_Node);
64
65      Enclosing_Body : constant Node_Id :=
66                         Find_Enclosing_Body (Enclosing_Body_Or_Block);
67      --  Innermost enclosing body
68
69      Next_Other_Label_Elmt : Elmt_Id := First_Elmt (Label_List);
70      --  Next element on label element list
71
72      Other_Label : Node_Id;
73      --  Next label node to process
74
75   begin
76      --  Loop through all the labels, and if we find some other label
77      --  (i.e. not Label_Node) that has the same identifier,
78      --  and whose innermost enclosing body is the same,
79      --  then we have an error.
80
81      --  Note that in the worst case, this is quadratic in the number
82      --  of labels.  However, labels are not all that common, and this
83      --  is only called for explicit labels.
84
85      --  ???Nonetheless, the efficiency could be improved. For example,
86      --  call Labl for each body, rather than once per compilation.
87
88      while Present (Next_Other_Label_Elmt) loop
89         Other_Label := Node (Next_Other_Label_Elmt);
90
91         exit when Label_Node = Other_Label;
92
93         if Chars (Label_Id) = Chars (Identifier (Other_Label))
94           and then Enclosing_Body = Find_Enclosing_Body (Other_Label)
95         then
96            Error_Msg_Sloc := Sloc (Other_Label);
97            Error_Msg_N ("& conflicts with label#", Label_Id);
98            exit;
99         end if;
100
101         Next_Elmt (Next_Other_Label_Elmt);
102      end loop;
103   end Check_Distinct_Labels;
104
105   -------------------------
106   -- Find_Enclosing_Body --
107   -------------------------
108
109   function Find_Enclosing_Body (N : Node_Id) return Node_Id is
110      Result : Node_Id := N;
111
112   begin
113      --  This is the same as Find_Enclosing_Body_Or_Block, except
114      --  that we skip block statements and accept statements, instead
115      --  of stopping at them.
116
117      while Present (Result)
118        and then Nkind (Result) /= N_Entry_Body
119        and then Nkind (Result) /= N_Task_Body
120        and then Nkind (Result) /= N_Package_Body
121        and then Nkind (Result) /= N_Subprogram_Body
122      loop
123         Result := Parent (Result);
124      end loop;
125
126      return Result;
127   end Find_Enclosing_Body;
128
129   ----------------------------------
130   -- Find_Enclosing_Body_Or_Block --
131   ----------------------------------
132
133   function Find_Enclosing_Body_Or_Block (N : Node_Id) return Node_Id is
134      Result : Node_Id := Parent (N);
135
136   begin
137      --  Climb up the parent chain until we find a body or block
138
139      while Present (Result)
140        and then Nkind (Result) /= N_Accept_Statement
141        and then Nkind (Result) /= N_Entry_Body
142        and then Nkind (Result) /= N_Task_Body
143        and then Nkind (Result) /= N_Package_Body
144        and then Nkind (Result) /= N_Subprogram_Body
145        and then Nkind (Result) /= N_Block_Statement
146      loop
147         Result := Parent (Result);
148      end loop;
149
150      return Result;
151   end Find_Enclosing_Body_Or_Block;
152
153   ------------------------
154   -- Find_Natural_Loops --
155   ------------------------
156
157   procedure Find_Natural_Loops is
158      Node_List : constant Elist_Id := New_Elmt_List;
159      N         : Elmt_Id;
160      Succ      : Elmt_Id;
161
162      function Goto_Id (Goto_Node : Node_Id) return Name_Id;
163      --  Find Name_Id of goto statement, which may be an expanded name
164
165      function Matches
166        (Label_Node : Node_Id;
167         Goto_Node  : Node_Id) return Boolean;
168      --  A label and a goto are candidates for a loop if the names match,
169      --  and both nodes appear in the same body. In addition, both must
170      --  appear in the same statement list. If they are not in the same
171      --  statement list, the goto is from within an nested structure, and
172      --  the label is not a header. We ignore the case where the goto is
173      --  within a conditional structure, and capture only infinite loops.
174
175      procedure Merge;
176      --  Merge labels and goto statements in order of increasing sloc value.
177      --  Discard labels of loop and block statements.
178
179      procedure No_Header (N : Elmt_Id);
180      --  The label N is known not to be a loop header. Scan forward and
181      --  remove all subsequent gotos that may have this node as a target.
182
183      procedure Process_Goto (N : Elmt_Id);
184      --  N is a forward jump. Scan forward and remove all subsequent gotos
185      --  that may have the same target, to preclude spurious loops.
186
187      procedure Rewrite_As_Loop
188        (Loop_Header : Node_Id;
189         Loop_End    : Node_Id);
190      --  Given a label and a backwards goto, rewrite intervening statements
191      --  as a loop. Remove the label from the node list, and rewrite the
192      --  goto with the body of the new loop.
193
194      procedure Try_Loop (N : Elmt_Id);
195      --  N is a label that may be a loop header. Scan forward to find some
196      --  backwards goto with which to make a loop. Do nothing if there is
197      --  an intervening label that is not part of a loop, or more than one
198      --  goto with this target.
199
200      -------------
201      -- Goto_Id --
202      -------------
203
204      function Goto_Id (Goto_Node : Node_Id) return Name_Id is
205      begin
206         if Nkind (Name (Goto_Node)) = N_Identifier then
207            return Chars (Name (Goto_Node));
208
209         elsif Nkind (Name (Goto_Node)) = N_Selected_Component then
210            return Chars (Selector_Name (Name (Goto_Node)));
211         else
212
213            --  In case of error, return Id that can't match anything
214
215            return Name_Null;
216         end if;
217      end Goto_Id;
218
219      -------------
220      -- Matches --
221      -------------
222
223      function Matches
224        (Label_Node : Node_Id;
225         Goto_Node  :  Node_Id) return Boolean
226      is
227      begin
228         return Chars (Identifier (Label_Node)) = Goto_Id (Goto_Node)
229           and then Find_Enclosing_Body (Label_Node) =
230                    Find_Enclosing_Body (Goto_Node);
231      end Matches;
232
233      -----------
234      -- Merge --
235      -----------
236
237      procedure Merge is
238         L1 : Elmt_Id;
239         G1 : Elmt_Id;
240
241      begin
242         L1 := First_Elmt (Label_List);
243         G1 := First_Elmt (Goto_List);
244
245         while Present (L1)
246           and then Present (G1)
247         loop
248            if Sloc (Node (L1)) < Sloc (Node (G1)) then
249
250               --  Optimization: remove labels of loops and blocks, which
251               --  play no role in what follows.
252
253               if Nkind (Node (L1)) /= N_Loop_Statement
254                 and then Nkind (Node (L1)) /= N_Block_Statement
255               then
256                  Append_Elmt (Node (L1), Node_List);
257               end if;
258
259               Next_Elmt (L1);
260
261            else
262               Append_Elmt (Node (G1), Node_List);
263               Next_Elmt (G1);
264            end if;
265         end loop;
266
267         while Present (L1) loop
268            Append_Elmt (Node (L1), Node_List);
269            Next_Elmt (L1);
270         end loop;
271
272         while Present (G1) loop
273            Append_Elmt (Node (G1), Node_List);
274            Next_Elmt (G1);
275         end loop;
276      end Merge;
277
278      ---------------
279      -- No_Header --
280      ---------------
281
282      procedure No_Header (N : Elmt_Id) is
283         S1, S2 : Elmt_Id;
284
285      begin
286         S1 := Next_Elmt (N);
287         while Present (S1) loop
288            S2 := Next_Elmt (S1);
289            if Nkind (Node (S1)) = N_Goto_Statement
290              and then Matches (Node (N), Node (S1))
291            then
292               Remove_Elmt (Node_List, S1);
293            end if;
294
295            S1 := S2;
296         end loop;
297      end No_Header;
298
299      ------------------
300      -- Process_Goto --
301      ------------------
302
303      procedure Process_Goto (N : Elmt_Id) is
304         Goto1 : constant Node_Id := Node (N);
305         Goto2 : Node_Id;
306         S, S1 : Elmt_Id;
307
308      begin
309         S := Next_Elmt (N);
310
311         while Present (S) loop
312            S1 := Next_Elmt (S);
313            Goto2 := Node (S);
314
315            if Nkind (Goto2) = N_Goto_Statement
316              and then Goto_Id (Goto1) = Goto_Id (Goto2)
317              and then Find_Enclosing_Body (Goto1) =
318                       Find_Enclosing_Body (Goto2)
319            then
320
321               --  Goto2 may have the same target, remove it from
322               --  consideration.
323
324               Remove_Elmt (Node_List, S);
325            end if;
326
327            S := S1;
328         end loop;
329      end Process_Goto;
330
331      ---------------------
332      -- Rewrite_As_Loop --
333      ---------------------
334
335      procedure Rewrite_As_Loop
336        (Loop_Header : Node_Id;
337         Loop_End    : Node_Id)
338      is
339         Loop_Body : constant List_Id := New_List;
340         Loop_Stmt : constant Node_Id :=
341                       New_Node (N_Loop_Statement, Sloc (Loop_Header));
342         Stat      : Node_Id;
343         Next_Stat : Node_Id;
344
345      begin
346         Stat := Next (Loop_Header);
347         while Stat /= Loop_End loop
348            Next_Stat := Next (Stat);
349            Remove (Stat);
350            Append (Stat, Loop_Body);
351            Stat := Next_Stat;
352         end loop;
353
354         Set_Statements (Loop_Stmt, Loop_Body);
355         Set_Identifier (Loop_Stmt, Identifier (Loop_Header));
356
357         Remove (Loop_Header);
358         Rewrite (Loop_End, Loop_Stmt);
359         Error_Msg_N
360           ("info: code between label and backwards goto rewritten as loop??",
361             Loop_End);
362      end Rewrite_As_Loop;
363
364      --------------
365      -- Try_Loop --
366      --------------
367
368      procedure Try_Loop (N : Elmt_Id) is
369         Source : Elmt_Id;
370         Found  : Boolean := False;
371         S1     : Elmt_Id;
372
373      begin
374         S1 := Next_Elmt (N);
375         while Present (S1) loop
376            if Nkind (Node (S1)) = N_Goto_Statement
377              and then Matches (Node (N), Node (S1))
378            then
379               if not Found then
380
381                  --  If the label and the goto are both in the same statement
382                  --  list, then we've found a loop. Note that labels and goto
383                  --  statements are always part of some list, so In_Same_List
384                  --  always makes sense.
385
386                  if In_Same_List (Node (N), Node (S1)) then
387                     Source := S1;
388                     Found  := True;
389
390                  --  The goto is within some nested structure
391
392                  else
393                     No_Header (N);
394                     return;
395                  end if;
396
397               else
398                  --  More than one goto with the same target
399
400                  No_Header (N);
401                  return;
402               end if;
403
404            elsif Nkind (Node (S1)) = N_Label
405              and then not Found
406            then
407               --  Intervening label before possible end of loop. Current
408               --  label is not a candidate. This is conservative, because
409               --  the label might not be the target of any jumps, but not
410               --  worth dealing with useless labels.
411
412               No_Header (N);
413               return;
414
415            else
416               --  If the node is a loop_statement, it corresponds to a
417               --  label-goto pair rewritten as a loop. Continue forward scan.
418
419               null;
420            end if;
421
422            Next_Elmt (S1);
423         end loop;
424
425         if Found then
426            Rewrite_As_Loop (Node (N), Node (Source));
427            Remove_Elmt (Node_List, N);
428            Remove_Elmt (Node_List, Source);
429         end if;
430      end Try_Loop;
431
432   begin
433      --  Start of processing for Find_Natural_Loops
434
435      Merge;
436
437      N := First_Elmt (Node_List);
438      while Present (N) loop
439         Succ := Next_Elmt (N);
440
441         if Nkind (Node (N)) = N_Label then
442            if No (Succ) then
443               exit;
444
445            elsif Nkind (Node (Succ)) = N_Label then
446               Try_Loop (Succ);
447
448               --  If a loop was found, the label has been removed, and
449               --  the following goto rewritten as the loop body.
450
451               Succ := Next_Elmt (N);
452
453               if Nkind (Node (Succ)) = N_Label then
454
455                  --  Following label was not removed, so current label
456                  --  is not a candidate header.
457
458                  No_Header (N);
459
460               else
461
462                  --  Following label was part of inner loop. Current
463                  --  label is still a candidate.
464
465                  Try_Loop (N);
466                  Succ := Next_Elmt (N);
467               end if;
468
469            elsif Nkind (Node (Succ)) = N_Goto_Statement then
470               Try_Loop (N);
471               Succ := Next_Elmt (N);
472            end if;
473
474         elsif Nkind (Node (N)) = N_Goto_Statement then
475            Process_Goto (N);
476            Succ := Next_Elmt (N);
477         end if;
478
479         N := Succ;
480      end loop;
481   end Find_Natural_Loops;
482
483--  Start of processing for Par.Labl
484
485begin
486   Next_Label_Elmt := First_Elmt (Label_List);
487   while Present (Next_Label_Elmt) loop
488      Label_Node := Node (Next_Label_Elmt);
489
490      if not Comes_From_Source (Label_Node) then
491         goto Next_Label;
492      end if;
493
494      --  Find the innermost enclosing body or block, which is where
495      --  we need to implicitly declare this label
496
497      Enclosing_Body_Or_Block := Find_Enclosing_Body_Or_Block (Label_Node);
498
499      --  If we didn't find a parent, then the label in question never got
500      --  hooked into a reasonable declarative part. This happens only in
501      --  error situations, and we simply ignore the entry (we aren't going
502      --  to get into the semantics in any case given the error).
503
504      if Present (Enclosing_Body_Or_Block) then
505         Check_Distinct_Labels;
506
507         --  Now create the implicit label declaration node and its
508         --  corresponding defining identifier. Note that the defining
509         --  occurrence of a label is the implicit label declaration that
510         --  we are creating. The label itself is an applied occurrence.
511
512         Label_Decl_Node :=
513           New_Node (N_Implicit_Label_Declaration, Sloc (Label_Node));
514         Defining_Ident_Node :=
515           New_Entity (N_Defining_Identifier, Sloc (Identifier (Label_Node)));
516         Set_Chars (Defining_Ident_Node, Chars (Identifier (Label_Node)));
517         Set_Defining_Identifier (Label_Decl_Node, Defining_Ident_Node);
518         Set_Label_Construct (Label_Decl_Node, Label_Node);
519
520         --  The following makes sure that Comes_From_Source is appropriately
521         --  set for the entity, depending on whether the label appeared in
522         --  the source explicitly or not.
523
524         Set_Comes_From_Source
525          (Defining_Ident_Node, Comes_From_Source (Identifier (Label_Node)));
526
527         --  Now attach the implicit label declaration to the appropriate
528         --  declarative region, creating a declaration list if none exists
529
530         if No (Declarations (Enclosing_Body_Or_Block)) then
531            Set_Declarations (Enclosing_Body_Or_Block, New_List);
532         end if;
533
534         Append (Label_Decl_Node, Declarations (Enclosing_Body_Or_Block));
535      end if;
536
537      <<Next_Label>>
538         Next_Elmt (Next_Label_Elmt);
539   end loop;
540
541   Find_Natural_Loops;
542
543end Labl;
544