aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/sem_ch5.adb
blob: 163365fc46a3f1577eef6c6a146d7bd547263f4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                              S E M _ C H 5                               --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--          Copyright (C) 1992-2005 Free Software Foundation, Inc.          --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
-- for  more details.  You should have  received  a copy of the GNU General --
-- Public License  distributed with GNAT;  see file COPYING.  If not, write --
-- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
-- MA 02111-1307, USA.                                                      --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

with Atree;    use Atree;
with Checks;   use Checks;
with Einfo;    use Einfo;
with Errout;   use Errout;
with Expander; use Expander;
with Exp_Util; use Exp_Util;
with Freeze;   use Freeze;
with Lib.Xref; use Lib.Xref;
with Nlists;   use Nlists;
with Nmake;    use Nmake;
with Opt;      use Opt;
with Sem;      use Sem;
with Sem_Case; use Sem_Case;
with Sem_Ch3;  use Sem_Ch3;
with Sem_Ch8;  use Sem_Ch8;
with Sem_Disp; use Sem_Disp;
with Sem_Eval; use Sem_Eval;
with Sem_Res;  use Sem_Res;
with Sem_Type; use Sem_Type;
with Sem_Util; use Sem_Util;
with Sem_Warn; use Sem_Warn;
with Stand;    use Stand;
with Sinfo;    use Sinfo;
with Targparm; use Targparm;
with Tbuild;   use Tbuild;
with Uintp;    use Uintp;

package body Sem_Ch5 is

   Unblocked_Exit_Count : Nat := 0;
   --  This variable is used when processing if statements, case statements,
   --  and block statements. It counts the number of exit points that are
   --  not blocked by unconditional transfer instructions (for IF and CASE,
   --  these are the branches of the conditional, for a block, they are the
   --  statement sequence of the block, and the statement sequences of any
   --  exception handlers that are part of the block. When processing is
   --  complete, if this count is zero, it means that control cannot fall
   --  through the IF, CASE or block statement. This is used for the
   --  generation of warning messages. This variable is recursively saved
   --  on entry to processing the construct, and restored on exit.

   -----------------------
   -- Local Subprograms --
   -----------------------

   procedure Analyze_Iteration_Scheme (N : Node_Id);

   procedure Check_Possible_Current_Value_Condition (Cnode : Node_Id);
   --  Cnode is N_If_Statement, N_Elsif_Part, or N_Iteration_Scheme
   --  (the latter when a WHILE condition is present). This call checks
   --  if Condition (Cnode) is of the form ([NOT] var op val), where var
   --  is a simple object, val is known at compile time, and op is one
   --  of the six relational operators. If this is the case, and the
   --  Current_Value field of "var" is not set, then it is set to Cnode.
   --  See Exp_Util.Set_Current_Value_Condition for further details.

   ------------------------
   -- Analyze_Assignment --
   ------------------------

   procedure Analyze_Assignment (N : Node_Id) is
      Lhs  : constant Node_Id := Name (N);
      Rhs  : constant Node_Id := Expression (N);
      T1   : Entity_Id;
      T2   : Entity_Id;
      Decl : Node_Id;
      Ent  : Entity_Id;

      procedure Diagnose_Non_Variable_Lhs (N : Node_Id);
      --  N is the node for the left hand side of an assignment, and it
      --  is not a variable. This routine issues an appropriate diagnostic.

      procedure Set_Assignment_Type
        (Opnd      : Node_Id;
         Opnd_Type : in out Entity_Id);
      --  Opnd is either the Lhs or Rhs of the assignment, and Opnd_Type
      --  is the nominal subtype. This procedure is used to deal with cases
      --  where the nominal subtype must be replaced by the actual subtype.

      -------------------------------
      -- Diagnose_Non_Variable_Lhs --
      -------------------------------

      procedure Diagnose_Non_Variable_Lhs (N : Node_Id) is
      begin
         --  Not worth posting another error if left hand side already
         --  flagged as being illegal in some respect

         if Error_Posted (N) then
            return;

         --  Some special bad cases of entity names

         elsif Is_Entity_Name (N) then
            if Ekind (Entity (N)) = E_In_Parameter then
               Error_Msg_N
                 ("assignment to IN mode parameter not allowed", N);

            --  Private declarations in a protected object are turned into
            --  constants when compiling a protected function.

            elsif Present (Scope (Entity (N)))
              and then Is_Protected_Type (Scope (Entity (N)))
              and then
                (Ekind (Current_Scope) = E_Function
                  or else
                 Ekind (Enclosing_Dynamic_Scope (Current_Scope)) = E_Function)
            then
               Error_Msg_N
                 ("protected function cannot modify protected object", N);

            elsif Ekind (Entity (N)) = E_Loop_Parameter then
               Error_Msg_N
                 ("assignment to loop parameter not allowed", N);

            else
               Error_Msg_N
                 ("left hand side of assignment must be a variable", N);
            end if;

         --  For indexed components or selected components, test prefix

         elsif Nkind (N) = N_Indexed_Component then
            Diagnose_Non_Variable_Lhs (Prefix (N));

         --  Another special case for assignment to discriminant

         elsif Nkind (N) = N_Selected_Component then
            if Present (Entity (Selector_Name (N)))
              and then Ekind (Entity (Selector_Name (N))) = E_Discriminant
            then
               Error_Msg_N
                 ("assignment to discriminant not allowed", N);
            else
               Diagnose_Non_Variable_Lhs (Prefix (N));
            end if;

         else
            --  If we fall through, we have no special message to issue!

            Error_Msg_N ("left hand side of assignment must be a variable", N);
         end if;
      end Diagnose_Non_Variable_Lhs;

      -------------------------
      -- Set_Assignment_Type --
      -------------------------

      procedure Set_Assignment_Type
        (Opnd      : Node_Id;
         Opnd_Type : in out Entity_Id)
      is
      begin
         Require_Entity (Opnd);

         --  If the assignment operand is an in-out or out parameter, then we
         --  get the actual subtype (needed for the unconstrained case).
         --  If the operand is the actual in an entry declaration, then within
         --  the accept statement it is replaced with a local renaming, which
         --  may also have an actual subtype.

         if Is_Entity_Name (Opnd)
           and then (Ekind (Entity (Opnd)) = E_Out_Parameter
                      or else Ekind (Entity (Opnd)) =
                           E_In_Out_Parameter
                      or else Ekind (Entity (Opnd)) =
                           E_Generic_In_Out_Parameter
                      or else
                        (Ekind (Entity (Opnd)) = E_Variable
                          and then Nkind (Parent (Entity (Opnd))) =
                             N_Object_Renaming_Declaration
                          and then Nkind (Parent (Parent (Entity (Opnd)))) =
                             N_Accept_Statement))
         then
            Opnd_Type := Get_Actual_Subtype (Opnd);

         --  If assignment operand is a component reference, then we get the
         --  actual subtype of the component for the unconstrained case.

         elsif
           (Nkind (Opnd) = N_Selected_Component
             or else Nkind (Opnd) = N_Explicit_Dereference)
           and then not Is_Unchecked_Union (Opnd_Type)
         then
            Decl := Build_Actual_Subtype_Of_Component (Opnd_Type, Opnd);

            if Present (Decl) then
               Insert_Action (N, Decl);
               Mark_Rewrite_Insertion (Decl);
               Analyze (Decl);
               Opnd_Type := Defining_Identifier (Decl);
               Set_Etype (Opnd, Opnd_Type);
               Freeze_Itype (Opnd_Type, N);

            elsif Is_Constrained (Etype (Opnd)) then
               Opnd_Type := Etype (Opnd);
            end if;

         --  For slice, use the constrained subtype created for the slice

         elsif Nkind (Opnd) = N_Slice then
            Opnd_Type := Etype (Opnd);
         end if;
      end Set_Assignment_Type;

   --  Start of processing for Analyze_Assignment

   begin
      Analyze (Rhs);
      Analyze (Lhs);
      T1 := Etype (Lhs);

      --  In the most general case, both Lhs and Rhs can be overloaded, and we
      --  must compute the intersection of the possible types on each side.

      if Is_Overloaded (Lhs) then
         declare
            I  : Interp_Index;
            It : Interp;

         begin
            T1 := Any_Type;
            Get_First_Interp (Lhs, I, It);

            while Present (It.Typ) loop
               if Has_Compatible_Type (Rhs, It.Typ) then
                  if T1 /= Any_Type then

                     --  An explicit dereference is overloaded if the prefix
                     --  is. Try to remove the ambiguity on the prefix, the
                     --  error will be posted there if the ambiguity is real.

                     if Nkind (Lhs) = N_Explicit_Dereference then
                        declare
                           PI    : Interp_Index;
                           PI1   : Interp_Index := 0;
                           PIt   : Interp;
                           Found : Boolean;

                        begin
                           Found := False;
                           Get_First_Interp (Prefix (Lhs), PI, PIt);

                           while Present (PIt.Typ) loop
                              if Is_Access_Type (PIt.Typ)
                                and then Has_Compatible_Type
                                           (Rhs, Designated_Type (PIt.Typ))
                              then
                                 if Found then
                                    PIt :=
                                      Disambiguate (Prefix (Lhs),
                                        PI1, PI, Any_Type);

                                    if PIt = No_Interp then
                                       Error_Msg_N
                                         ("ambiguous left-hand side"
                                            & " in assignment", Lhs);
                                       exit;
                                    else
                                       Resolve (Prefix (Lhs), PIt.Typ);
                                    end if;

                                    exit;
                                 else
                                    Found := True;
                                    PI1 := PI;
                                 end if;
                              end if;

                              Get_Next_Interp (PI, PIt);
                           end loop;
                        end;

                     else
                        Error_Msg_N
                          ("ambiguous left-hand side in assignment", Lhs);
                        exit;
                     end if;
                  else
                     T1 := It.Typ;
                  end if;
               end if;

               Get_Next_Interp (I, It);
            end loop;
         end;

         if T1 = Any_Type then
            Error_Msg_N
              ("no valid types for left-hand side for assignment", Lhs);
            return;
         end if;
      end if;

      Resolve (Lhs, T1);

      if not Is_Variable (Lhs) then
         Diagnose_Non_Variable_Lhs (Lhs);
         return;

      elsif Is_Limited_Type (T1)
        and then not Assignment_OK (Lhs)
        and then not Assignment_OK (Original_Node (Lhs))
      then
         Error_Msg_N
           ("left hand of assignment must not be limited type", Lhs);
         Explain_Limited_Type (T1, Lhs);
         return;
      end if;

      --  Resolution may have updated the subtype, in case the left-hand
      --  side is a private protected component. Use the correct subtype
      --  to avoid scoping issues in the back-end.

      T1 := Etype (Lhs);
      Set_Assignment_Type (Lhs, T1);

      Resolve (Rhs, T1);
      Check_Unset_Reference (Rhs);

      --  Remaining steps are skipped if Rhs was syntactically in error

      if Rhs = Error then
         return;
      end if;

      T2 := Etype (Rhs);

      if Covers (T1, T2) then
         null;
      else
         Wrong_Type (Rhs, Etype (Lhs));
         return;
      end if;

      Set_Assignment_Type (Rhs, T2);

      if Total_Errors_Detected /= 0 then
         if No (T1) then
            T1 := Any_Type;
         end if;

         if No (T2) then
            T2 := Any_Type;
         end if;
      end if;

      if T1 = Any_Type or else T2 = Any_Type then
         return;
      end if;

      if (Is_Class_Wide_Type (T2) or else Is_Dynamically_Tagged (Rhs))
        and then not Is_Class_Wide_Type (T1)
      then
         Error_Msg_N ("dynamically tagged expression not allowed!", Rhs);

      elsif Is_Class_Wide_Type (T1)
        and then not Is_Class_Wide_Type (T2)
        and then not Is_Tag_Indeterminate (Rhs)
        and then not Is_Dynamically_Tagged (Rhs)
      then
         Error_Msg_N ("dynamically tagged expression required!", Rhs);
      end if;

      --  Tag propagation is done only in semantics mode only. If expansion
      --  is on, the rhs tag indeterminate function call has been expanded
      --  and tag propagation would have happened too late, so the
      --  propagation take place in expand_call instead.

      if not Expander_Active
        and then Is_Class_Wide_Type (T1)
        and then Is_Tag_Indeterminate (Rhs)
      then
         Propagate_Tag (Lhs, Rhs);
      end if;

      --  Ada 2005 (AI-231)

      if Ada_Version >= Ada_05
        and then Nkind (Rhs) = N_Null
        and then Is_Access_Type (T1)
        and then not Assignment_OK (Lhs)
        and then ((Is_Entity_Name (Lhs)
                     and then Can_Never_Be_Null (Entity (Lhs)))
                   or else Can_Never_Be_Null (Etype (Lhs)))
      then
         Apply_Compile_Time_Constraint_Error
           (N      => Lhs,
            Msg    => "(Ada 2005) NULL not allowed in null-excluding objects?",
            Reason => CE_Null_Not_Allowed);
      end if;

      if Is_Scalar_Type (T1) then
         Apply_Scalar_Range_Check (Rhs, Etype (Lhs));

      elsif Is_Array_Type (T1)
        and then
          (Nkind (Rhs) /= N_Type_Conversion
             or else Is_Constrained (Etype (Rhs)))
      then
         --  Assignment verifies that the length of the Lsh and Rhs are equal,
         --  but of course the indices do not have to match. If the right-hand
         --  side is a type conversion to an unconstrained type, a length check
         --  is performed on the expression itself during expansion. In rare
         --  cases, the redundant length check is computed on an index type
         --  with a different representation, triggering incorrect code in
         --  the back end.

         Apply_Length_Check (Rhs, Etype (Lhs));

      else
         --  Discriminant checks are applied in the course of expansion

         null;
      end if;

      --  Note: modifications of the Lhs may only be recorded after
      --  checks have been applied.

      Note_Possible_Modification (Lhs);

      --  ??? a real accessibility check is needed when ???

      --  Post warning for useless assignment

      if Warn_On_Redundant_Constructs

         --  We only warn for source constructs

         and then Comes_From_Source (N)

         --  Where the entity is the same on both sides

         and then Is_Entity_Name (Lhs)
         and then Is_Entity_Name (Original_Node (Rhs))
         and then Entity (Lhs) = Entity (Original_Node (Rhs))

         --  But exclude the case where the right side was an operation
         --  that got rewritten (e.g. JUNK + K, where K was known to be
         --  zero). We don't want to warn in such a case, since it is
         --  reasonable to write such expressions especially when K is
         --  defined symbolically in some other package.

        and then Nkind (Original_Node (Rhs)) not in N_Op
      then
         Error_Msg_NE
           ("?useless assignment of & to itself", N, Entity (Lhs));
      end if;

      --  Check for non-allowed composite assignment

      if not Support_Composite_Assign_On_Target
        and then (Is_Array_Type (T1) or else Is_Record_Type (T1))
        and then (not Has_Size_Clause (T1) or else Esize (T1) > 64)
      then
         Error_Msg_CRT ("composite assignment", N);
      end if;

      --  One more step. Let's see if we have a simple assignment of a
      --  known at compile time value to a simple variable. If so, we
      --  can record the value as the current value providing that:

      --    We still have a simple assignment statement (no expansion
      --    activity has modified it in some peculiar manner)

      --    The type is a discrete type

      --    The assignment is to a named entity

      --    The value is known at compile time

      if Nkind (N) /= N_Assignment_Statement
        or else not Is_Discrete_Type (T1)
        or else not Is_Entity_Name (Lhs)
        or else not Compile_Time_Known_Value (Rhs)
      then
         return;
      end if;

      Ent := Entity (Lhs);

      --  Capture value if save to do so

      if Safe_To_Capture_Value (N, Ent) then
         Set_Current_Value (Ent, Rhs);
      end if;
   end Analyze_Assignment;

   -----------------------------
   -- Analyze_Block_Statement --
   -----------------------------

   procedure Analyze_Block_Statement (N : Node_Id) is
      Decls : constant List_Id := Declarations (N);
      Id    : constant Node_Id := Identifier (N);
      HSS   : constant Node_Id := Handled_Statement_Sequence (N);

   begin
      --  If no handled statement sequence is present, things are really
      --  messed up, and we just return immediately (this is a defence
      --  against previous errors).

      if No (HSS) then
         return;
      end if;

      --  Normal processing with HSS present

      declare
         EH  : constant List_Id := Exception_Handlers (HSS);
         Ent : Entity_Id        := Empty;
         S   : Entity_Id;

         Save_Unblocked_Exit_Count : constant Nat := Unblocked_Exit_Count;
         --  Recursively save value of this global, will be restored on exit

      begin
         --  Initialize unblocked exit count for statements of begin block
         --  plus one for each excption handler that is present.

         Unblocked_Exit_Count := 1;

         if Present (EH) then
            Unblocked_Exit_Count := Unblocked_Exit_Count + List_Length (EH);
         end if;

         --  If a label is present analyze it and mark it as referenced

         if Present (Id) then
            Analyze (Id);
            Ent := Entity (Id);

            --  An error defense. If we have an identifier, but no entity,
            --  then something is wrong. If we have previous errors, then
            --  just remove the identifier and continue, otherwise raise
            --  an exception.

            if No (Ent) then
               if Total_Errors_Detected /= 0 then
                  Set_Identifier (N, Empty);
               else
                  raise Program_Error;
               end if;

            else
               Set_Ekind (Ent, E_Block);
               Generate_Reference (Ent, N, ' ');
               Generate_Definition (Ent);

               if Nkind (Parent (Ent)) = N_Implicit_Label_Declaration then
                  Set_Label_Construct (Parent (Ent), N);
               end if;
            end if;
         end if;

         --  If no entity set, create a label entity

         if No (Ent) then
            Ent := New_Internal_Entity (E_Block, Current_Scope, Sloc (N), 'B');
            Set_Identifier (N, New_Occurrence_Of (Ent, Sloc (N)));
            Set_Parent (Ent, N);
         end if;

         Set_Etype (Ent, Standard_Void_Type);
         Set_Block_Node (Ent, Identifier (N));
         New_Scope (Ent);

         if Present (Decls) then
            Analyze_Declarations (Decls);
            Check_Completion;
         end if;

         Analyze (HSS);
         Process_End_Label (HSS, 'e', Ent);

         --  If exception handlers are present, then we indicate that
         --  enclosing scopes contain a block with handlers. We only
         --  need to mark non-generic scopes.

         if Present (EH) then
            S := Scope (Ent);
            loop
               Set_Has_Nested_Block_With_Handler (S);
               exit when Is_Overloadable (S)
                 or else Ekind (S) = E_Package
                 or else Is_Generic_Unit (S);
               S := Scope (S);
            end loop;
         end if;

         Check_References (Ent);
         End_Scope;

         if Unblocked_Exit_Count = 0 then
            Unblocked_Exit_Count := Save_Unblocked_Exit_Count;
            Check_Unreachable_Code (N);
         else
            Unblocked_Exit_Count := Save_Unblocked_Exit_Count;
         end if;
      end;
   end Analyze_Block_Statement;

   ----------------------------
   -- Analyze_Case_Statement --
   ----------------------------

   procedure Analyze_Case_Statement (N : Node_Id) is
      Exp            : Node_Id;
      Exp_Type       : Entity_Id;
      Exp_Btype      : Entity_Id;
      Last_Choice    : Nat;
      Dont_Care      : Boolean;
      Others_Present : Boolean;

      Statements_Analyzed : Boolean := False;
      --  Set True if at least some statement sequences get analyzed.
      --  If False on exit, means we had a serious error that prevented
      --  full analysis of the case statement, and as a result it is not
      --  a good idea to output warning messages about unreachable code.

      Save_Unblocked_Exit_Count : constant Nat := Unblocked_Exit_Count;
      --  Recursively save value of this global, will be restored on exit

      procedure Non_Static_Choice_Error (Choice : Node_Id);
      --  Error routine invoked by the generic instantiation below when
      --  the case statment has a non static choice.

      procedure Process_Statements (Alternative : Node_Id);
      --  Analyzes all the statements associated to a case alternative.
      --  Needed by the generic instantiation below.

      package Case_Choices_Processing is new
        Generic_Choices_Processing
          (Get_Alternatives          => Alternatives,
           Get_Choices               => Discrete_Choices,
           Process_Empty_Choice      => No_OP,
           Process_Non_Static_Choice => Non_Static_Choice_Error,
           Process_Associated_Node   => Process_Statements);
      use Case_Choices_Processing;
      --  Instantiation of the generic choice processing package

      -----------------------------
      -- Non_Static_Choice_Error --
      -----------------------------

      procedure Non_Static_Choice_Error (Choice : Node_Id) is
      begin
         Flag_Non_Static_Expr
           ("choice given in case statement is not static!", Choice);
      end Non_Static_Choice_Error;

      ------------------------
      -- Process_Statements --
      ------------------------

      procedure Process_Statements (Alternative : Node_Id) is
         Choices : constant List_Id := Discrete_Choices (Alternative);
         Ent     : Entity_Id;

      begin
         Unblocked_Exit_Count := Unblocked_Exit_Count + 1;
         Statements_Analyzed := True;

         --  An interesting optimization. If the case statement expression
         --  is a simple entity, then we can set the current value within
         --  an alternative if the alternative has one possible value.

         --    case N is
         --      when 1      => alpha
         --      when 2 | 3  => beta
         --      when others => gamma

         --  Here we know that N is initially 1 within alpha, but for beta
         --  and gamma, we do not know anything more about the initial value.

         if Is_Entity_Name (Exp) then
            Ent := Entity (Exp);

            if Ekind (Ent) = E_Variable
                 or else
               Ekind (Ent) = E_In_Out_Parameter
                 or else
               Ekind (Ent) = E_Out_Parameter
            then
               if List_Length (Choices) = 1
                 and then Nkind (First (Choices)) in N_Subexpr
                 and then Compile_Time_Known_Value (First (Choices))
               then
                  Set_Current_Value (Entity (Exp), First (Choices));
               end if;

               Analyze_Statements (Statements (Alternative));

               --  After analyzing the case, set the current value to empty
               --  since we won't know what it is for the next alternative
               --  (unless reset by this same circuit), or after the case.

               Set_Current_Value (Entity (Exp), Empty);
               return;
            end if;
         end if;

         --  Case where expression is not an entity name of a variable

         Analyze_Statements (Statements (Alternative));
      end Process_Statements;

      --  Table to record choices. Put after subprograms since we make
      --  a call to Number_Of_Choices to get the right number of entries.

      Case_Table : Choice_Table_Type (1 .. Number_Of_Choices (N));

   --  Start of processing for Analyze_Case_Statement

   begin
      Unblocked_Exit_Count := 0;
      Exp := Expression (N);
      Analyze_And_Resolve (Exp, Any_Discrete);
      Check_Unset_Reference (Exp);
      Exp_Type  := Etype (Exp);
      Exp_Btype := Base_Type (Exp_Type);

      --  The expression must be of a discrete type which must be determinable
      --  independently of the context in which the expression occurs, but
      --  using the fact that the expression must be of a discrete type.
      --  Moreover, the type this expression must not be a character literal
      --  (which is always ambiguous) or, for Ada-83, a generic formal type.

      --  If error already reported by Resolve, nothing more to do

      if Exp_Btype = Any_Discrete
        or else Exp_Btype = Any_Type
      then
         return;

      elsif Exp_Btype = Any_Character then
         Error_Msg_N
           ("character literal as case expression is ambiguous", Exp);
         return;

      elsif Ada_Version = Ada_83
        and then (Is_Generic_Type (Exp_Btype)
                    or else Is_Generic_Type (Root_Type (Exp_Btype)))
      then
         Error_Msg_N
           ("(Ada 83) case expression cannot be of a generic type", Exp);
         return;
      end if;

      --  If the case expression is a formal object of mode in out, then
      --  treat it as having a nonstatic subtype by forcing use of the base
      --  type (which has to get passed to Check_Case_Choices below).  Also
      --  use base type when the case expression is parenthesized.

      if Paren_Count (Exp) > 0
        or else (Is_Entity_Name (Exp)
                  and then Ekind (Entity (Exp)) = E_Generic_In_Out_Parameter)
      then
         Exp_Type := Exp_Btype;
      end if;

      --  Call instantiated Analyze_Choices which does the rest of the work

      Analyze_Choices
        (N, Exp_Type, Case_Table, Last_Choice, Dont_Care, Others_Present);

      if Exp_Type = Universal_Integer and then not Others_Present then
         Error_Msg_N ("case on universal integer requires OTHERS choice", Exp);
      end if;

      --  If all our exits were blocked by unconditional transfers of control,
      --  then the entire CASE statement acts as an unconditional transfer of
      --  control, so treat it like one, and check unreachable code. Skip this
      --  test if we had serious errors preventing any statement analysis.

      if Unblocked_Exit_Count = 0 and then Statements_Analyzed then
         Unblocked_Exit_Count := Save_Unblocked_Exit_Count;
         Check_Unreachable_Code (N);
      else
         Unblocked_Exit_Count := Save_Unblocked_Exit_Count;
      end if;

      if not Expander_Active
        and then Compile_Time_Known_Value (Expression (N))
        and then Serious_Errors_Detected = 0
      then
         declare
            Chosen : constant Node_Id := Find_Static_Alternative (N);
            Alt    : Node_Id;

         begin
            Alt := First (Alternatives (N));

            while Present (Alt) loop
               if Alt /= Chosen then
                  Remove_Warning_Messages (Statements (Alt));
               end if;

               Next (Alt);
            end loop;
         end;
      end if;
   end Analyze_Case_Statement;

   ----------------------------
   -- Analyze_Exit_Statement --
   ----------------------------

   --  If the exit includes a name, it must be the name of a currently open
   --  loop. Otherwise there must be an innermost open loop on the stack,
   --  to which the statement implicitly refers.

   procedure Analyze_Exit_Statement (N : Node_Id) is
      Target   : constant Node_Id := Name (N);
      Cond     : constant Node_Id := Condition (N);
      Scope_Id : Entity_Id;
      U_Name   : Entity_Id;
      Kind     : Entity_Kind;

   begin
      if No (Cond) then
         Check_Unreachable_Code (N);
      end if;

      if Present (Target) then
         Analyze (Target);
         U_Name := Entity (Target);

         if not In_Open_Scopes (U_Name) or else Ekind (U_Name) /= E_Loop then
            Error_Msg_N ("invalid loop name in exit statement", N);
            return;
         else
            Set_Has_Exit (U_Name);
         end if;

      else
         U_Name := Empty;
      end if;

      for J in reverse 0 .. Scope_Stack.Last loop
         Scope_Id := Scope_Stack.Table (J).Entity;
         Kind := Ekind (Scope_Id);

         if Kind = E_Loop
           and then (No (Target) or else Scope_Id = U_Name) then
            Set_Has_Exit (Scope_Id);
            exit;

         elsif Kind = E_Block or else Kind = E_Loop then
            null;

         else
            Error_Msg_N
              ("cannot exit from program unit or accept statement", N);
            exit;
         end if;
      end loop;

      --  Verify that if present the condition is a Boolean expression

      if Present (Cond) then
         Analyze_And_Resolve (Cond, Any_Boolean);
         Check_Unset_Reference (Cond);
      end if;
   end Analyze_Exit_Statement;

   ----------------------------
   -- Analyze_Goto_Statement --
   ----------------------------

   procedure Analyze_Goto_Statement (N : Node_Id) is
      Label       : constant Node_Id := Name (N);
      Scope_Id    : Entity_Id;
      Label_Scope : Entity_Id;

   begin
      Check_Unreachable_Code (N);

      Analyze (Label);

      if Entity (Label) = Any_Id then
         return;

      elsif Ekind (Entity (Label)) /= E_Label then
         Error_Msg_N ("target of goto statement must be a label", Label);
         return;

      elsif not Reachable (Entity (Label)) then
         Error_Msg_N ("target of goto statement is not reachable", Label);
         return;
      end if;

      Label_Scope := Enclosing_Scope (Entity (Label));

      for J in reverse 0 .. Scope_Stack.Last loop
         Scope_Id := Scope_Stack.Table (J).Entity;

         if Label_Scope = Scope_Id
           or else (Ekind (Scope_Id) /= E_Block
                     and then Ekind (Scope_Id) /= E_Loop)
         then
            if Scope_Id /= Label_Scope then
               Error_Msg_N
                 ("cannot exit from program unit or accept statement", N);
            end if;

            return;
         end if;
      end loop;

      raise Program_Error;
   end Analyze_Goto_Statement;

   --------------------------
   -- Analyze_If_Statement --
   --------------------------

   --  A special complication arises in the analysis of if statements

   --  The expander has circuitry to completely delete code that it
   --  can tell will not be executed (as a result of compile time known
   --  conditions). In the analyzer, we ensure that code that will be
   --  deleted in this manner is analyzed but not expanded. This is
   --  obviously more efficient, but more significantly, difficulties
   --  arise if code is expanded and then eliminated (e.g. exception
   --  table entries disappear). Similarly, itypes generated in deleted
   --  code must be frozen from start, because the nodes on which they
   --  depend will not be available at the freeze point.

   procedure Analyze_If_Statement (N : Node_Id) is
      E : Node_Id;

      Save_Unblocked_Exit_Count : constant Nat := Unblocked_Exit_Count;
      --  Recursively save value of this global, will be restored on exit

      Save_In_Deleted_Code : Boolean;

      Del : Boolean := False;
      --  This flag gets set True if a True condition has been found,
      --  which means that remaining ELSE/ELSIF parts are deleted.

      procedure Analyze_Cond_Then (Cnode : Node_Id);
      --  This is applied to either the N_If_Statement node itself or
      --  to an N_Elsif_Part node. It deals with analyzing the condition
      --  and the THEN statements associated with it.

      -----------------------
      -- Analyze_Cond_Then --
      -----------------------

      procedure Analyze_Cond_Then (Cnode : Node_Id) is
         Cond : constant Node_Id := Condition (Cnode);
         Tstm : constant List_Id := Then_Statements (Cnode);

      begin
         Unblocked_Exit_Count := Unblocked_Exit_Count + 1;
         Analyze_And_Resolve (Cond, Any_Boolean);
         Check_Unset_Reference (Cond);
         Check_Possible_Current_Value_Condition (Cnode);

         --  If already deleting, then just analyze then statements

         if Del then
            Analyze_Statements (Tstm);

         --  Compile time known value, not deleting yet

         elsif Compile_Time_Known_Value (Cond) then
            Save_In_Deleted_Code := In_Deleted_Code;

            --  If condition is True, then analyze the THEN statements
            --  and set no expansion for ELSE and ELSIF parts.

            if Is_True (Expr_Value (Cond)) then
               Analyze_Statements (Tstm);
               Del := True;
               Expander_Mode_Save_And_Set (False);
               In_Deleted_Code := True;

            --  If condition is False, analyze THEN with expansion off

            else -- Is_False (Expr_Value (Cond))
               Expander_Mode_Save_And_Set (False);
               In_Deleted_Code := True;
               Analyze_Statements (Tstm);
               Expander_Mode_Restore;
               In_Deleted_Code := Save_In_Deleted_Code;
            end if;

         --  Not known at compile time, not deleting, normal analysis

         else
            Analyze_Statements (Tstm);
         end if;
      end Analyze_Cond_Then;

   --  Start of Analyze_If_Statement

   begin
      --  Initialize exit count for else statements. If there is no else
      --  part, this count will stay non-zero reflecting the fact that the
      --  uncovered else case is an unblocked exit.

      Unblocked_Exit_Count := 1;
      Analyze_Cond_Then (N);

      --  Now to analyze the elsif parts if any are present

      if Present (Elsif_Parts (N)) then
         E := First (Elsif_Parts (N));
         while Present (E) loop
            Analyze_Cond_Then (E);
            Next (E);
         end loop;
      end if;

      if Present (Else_Statements (N)) then
         Analyze_Statements (Else_Statements (N));
      end if;

      --  If all our exits were blocked by unconditional transfers of control,
      --  then the entire IF statement acts as an unconditional transfer of
      --  control, so treat it like one, and check unreachable code.

      if Unblocked_Exit_Count = 0 then
         Unblocked_Exit_Count := Save_Unblocked_Exit_Count;
         Check_Unreachable_Code (N);
      else
         Unblocked_Exit_Count := Save_Unblocked_Exit_Count;
      end if;

      if Del then
         Expander_Mode_Restore;
         In_Deleted_Code := Save_In_Deleted_Code;
      end if;

      if not Expander_Active
        and then Compile_Time_Known_Value (Condition (N))
        and then Serious_Errors_Detected = 0
      then
         if Is_True (Expr_Value (Condition (N))) then
            Remove_Warning_Messages (Else_Statements (N));

            if Present (Elsif_Parts (N)) then
               E := First (Elsif_Parts (N));

               while Present (E) loop
                  Remove_Warning_Messages (Then_Statements (E));
                  Next (E);
               end loop;
            end if;

         else
            Remove_Warning_Messages (Then_Statements (N));
         end if;
      end if;
   end Analyze_If_Statement;

   ----------------------------------------
   -- Analyze_Implicit_Label_Declaration --
   ----------------------------------------

   --  An implicit label declaration is generated in the innermost
   --  enclosing declarative part. This is done for labels as well as
   --  block and loop names.

   --  Note: any changes in this routine may need to be reflected in
   --  Analyze_Label_Entity.

   procedure Analyze_Implicit_Label_Declaration (N : Node_Id) is
      Id : constant Node_Id := Defining_Identifier (N);
   begin
      Enter_Name          (Id);
      Set_Ekind           (Id, E_Label);
      Set_Etype           (Id, Standard_Void_Type);
      Set_Enclosing_Scope (Id, Current_Scope);
   end Analyze_Implicit_Label_Declaration;

   ------------------------------
   -- Analyze_Iteration_Scheme --
   ------------------------------

   procedure Analyze_Iteration_Scheme (N : Node_Id) is

      procedure Process_Bounds (R : Node_Id);
      --  If the iteration is given by a range, create temporaries and
      --  assignment statements block to capture the bounds and perform
      --  required finalization actions in case a bound includes a function
      --  call that uses the temporary stack. We first pre-analyze a copy of
      --  the range in order to determine the expected type, and analyze
      --  and resolve the original bounds.

      procedure Check_Controlled_Array_Attribute (DS : Node_Id);
      --  If the bounds are given by a 'Range reference on a function call
      --  that returns a controlled array, introduce an explicit declaration
      --  to capture the bounds, so that the function result can be finalized
      --  in timely fashion.

      --------------------
      -- Process_Bounds --
      --------------------

      procedure Process_Bounds (R : Node_Id) is
         Loc          : constant Source_Ptr := Sloc (N);
         R_Copy       : constant Node_Id := New_Copy_Tree (R);
         Lo           : constant Node_Id := Low_Bound  (R);
         Hi           : constant Node_Id := High_Bound (R);
         New_Lo_Bound : Node_Id := Empty;
         New_Hi_Bound : Node_Id := Empty;
         Typ          : Entity_Id;

         function One_Bound
           (Original_Bound : Node_Id;
            Analyzed_Bound : Node_Id) return Node_Id;
         --  Create one declaration followed by one assignment statement
         --  to capture the value of bound. We create a separate assignment
         --  in order to force the creation of a block in case the bound
         --  contains a call that uses the secondary stack.

         ---------------
         -- One_Bound --
         ---------------

         function One_Bound
           (Original_Bound : Node_Id;
            Analyzed_Bound : Node_Id) return Node_Id
         is
            Assign   : Node_Id;
            Id       : Entity_Id;
            Decl     : Node_Id;
            Decl_Typ : Entity_Id;

         begin
            --  If the bound is a constant or an object, no need for a
            --  separate declaration. If the bound is the result of previous
            --  expansion it is already analyzed and should not be modified.
            --  Note that the Bound will be resolved later, if needed, as
            --  part of the call to Make_Index (literal bounds may need to
            --  be resolved to type Integer).

            if Analyzed (Original_Bound) then
               return Original_Bound;

            elsif Nkind (Analyzed_Bound) = N_Integer_Literal
              or else Is_Entity_Name (Analyzed_Bound)
            then
               Analyze_And_Resolve (Original_Bound, Typ);
               return Original_Bound;

            else
               Analyze_And_Resolve (Original_Bound, Typ);
            end if;

            Id :=
              Make_Defining_Identifier (Loc,
                Chars => New_Internal_Name ('S'));

            --  If the type of the discrete range is Universal_Integer, then
            --  the bound's type must be resolved to Integer, so the object
            --  used to hold the bound must also have type Integer.

            if Typ = Universal_Integer then
               Decl_Typ := Standard_Integer;
            else
               Decl_Typ := Typ;
            end if;

            Decl :=
              Make_Object_Declaration (Loc,
                Defining_Identifier => Id,
                Object_Definition   => New_Occurrence_Of (Decl_Typ, Loc));

            Insert_Before (Parent (N), Decl);
            Analyze (Decl);

            Assign :=
              Make_Assignment_Statement (Loc,
                Name        => New_Occurrence_Of (Id, Loc),
                Expression  => Relocate_Node (Original_Bound));

            Insert_Before (Parent (N), Assign);
            Analyze (Assign);

            Rewrite (Original_Bound, New_Occurrence_Of (Id, Loc));

            if Nkind (Assign) = N_Assignment_Statement then
               return Expression (Assign);
            else
               return Original_Bound;
            end if;
         end One_Bound;

      --  Start of processing for Process_Bounds

      begin
         --  Determine expected type of range by analyzing separate copy.

         Set_Parent (R_Copy, Parent (R));
         Pre_Analyze_And_Resolve (R_Copy);
         Typ := Etype (R_Copy);
         Set_Etype (R, Typ);

         New_Lo_Bound := One_Bound (Lo, Low_Bound  (R_Copy));
         New_Hi_Bound := One_Bound (Hi, High_Bound (R_Copy));

         --  Propagate staticness to loop range itself, in case the
         --  corresponding subtype is static.

         if New_Lo_Bound /= Lo
           and then Is_Static_Expression (New_Lo_Bound)
         then
            Rewrite  (Low_Bound (R), New_Copy (New_Lo_Bound));
         end if;

         if New_Hi_Bound /= Hi
           and then Is_Static_Expression (New_Hi_Bound)
         then
            Rewrite (High_Bound (R), New_Copy (New_Hi_Bound));
         end if;
      end Process_Bounds;

      --------------------------------------
      -- Check_Controlled_Array_Attribute --
      --------------------------------------

      procedure Check_Controlled_Array_Attribute (DS : Node_Id) is
      begin
         if Nkind (DS) = N_Attribute_Reference
            and then Is_Entity_Name (Prefix (DS))
            and then Ekind (Entity (Prefix (DS))) = E_Function
            and then Is_Array_Type (Etype (Entity (Prefix (DS))))
            and then
              Is_Controlled (
                Component_Type (Etype (Entity (Prefix (DS)))))
            and then Expander_Active
         then
            declare
               Loc  : constant Source_Ptr := Sloc (N);
               Arr  : constant Entity_Id :=
                        Etype (Entity (Prefix (DS)));
               Indx : constant Entity_Id :=
                        Base_Type (Etype (First_Index (Arr)));
               Subt : constant Entity_Id :=
                        Make_Defining_Identifier
                          (Loc, New_Internal_Name ('S'));
               Decl : Node_Id;

            begin
               Decl :=
                 Make_Subtype_Declaration (Loc,
                   Defining_Identifier => Subt,
                   Subtype_Indication  =>
                      Make_Subtype_Indication (Loc,
                        Subtype_Mark  => New_Reference_To (Indx, Loc),
                        Constraint =>
                          Make_Range_Constraint (Loc,
                            Relocate_Node (DS))));
               Insert_Before (Parent (N), Decl);
               Analyze (Decl);

               Rewrite (DS,
                  Make_Attribute_Reference (Loc,
                    Prefix => New_Reference_To (Subt, Loc),
                    Attribute_Name => Attribute_Name (DS)));
               Analyze (DS);
            end;
         end if;
      end Check_Controlled_Array_Attribute;

   --  Start of processing for Analyze_Iteration_Scheme

   begin
      --  For an infinite loop, there is no iteration scheme

      if No (N) then
         return;

      else
         declare
            Cond : constant Node_Id := Condition (N);

         begin
            --  For WHILE loop, verify that the condition is a Boolean
            --  expression and resolve and check it.

            if Present (Cond) then
               Analyze_And_Resolve (Cond, Any_Boolean);
               Check_Unset_Reference (Cond);

            --  Else we have a FOR loop

            else
               declare
                  LP : constant Node_Id   := Loop_Parameter_Specification (N);
                  Id : constant Entity_Id := Defining_Identifier (LP);
                  DS : constant Node_Id   := Discrete_Subtype_Definition (LP);

               begin
                  Enter_Name (Id);

                  --  We always consider the loop variable to be referenced,
                  --  since the loop may be used just for counting purposes.

                  Generate_Reference (Id, N, ' ');

                  --  Check for case of loop variable hiding a local
                  --  variable (used later on to give a nice warning
                  --  if the hidden variable is never assigned).

                  declare
                     H : constant Entity_Id := Homonym (Id);
                  begin
                     if Present (H)
                       and then Enclosing_Dynamic_Scope (H) =
                                Enclosing_Dynamic_Scope (Id)
                       and then Ekind (H) = E_Variable
                       and then Is_Discrete_Type (Etype (H))
                     then
                        Set_Hiding_Loop_Variable (H, Id);
                     end if;
                  end;

                  --  Now analyze the subtype definition. If it is
                  --  a range, create temporaries for bounds.

                  if Nkind (DS) = N_Range
                    and then Expander_Active
                  then
                     Process_Bounds (DS);
                  else
                     Analyze (DS);
                  end if;

                  if DS = Error then
                     return;
                  end if;

                  --  The subtype indication may denote the completion
                  --  of an incomplete type declaration.

                  if Is_Entity_Name (DS)
                    and then Present (Entity (DS))
                    and then Is_Type (Entity (DS))
                    and then Ekind (Entity (DS)) = E_Incomplete_Type
                  then
                     Set_Entity (DS, Get_Full_View (Entity (DS)));
                     Set_Etype  (DS, Entity (DS));
                  end if;

                  if not Is_Discrete_Type (Etype (DS)) then
                     Wrong_Type (DS, Any_Discrete);
                     Set_Etype (DS, Any_Type);
                  end if;

                  Check_Controlled_Array_Attribute (DS);

                  Make_Index (DS, LP);

                  Set_Ekind          (Id, E_Loop_Parameter);
                  Set_Etype          (Id, Etype (DS));
                  Set_Is_Known_Valid (Id, True);

                  --  The loop is not a declarative part, so the only entity
                  --  declared "within" must be frozen explicitly.

                  declare
                     Flist : constant List_Id := Freeze_Entity (Id, Sloc (N));
                  begin
                     if Is_Non_Empty_List (Flist) then
                        Insert_Actions (N, Flist);
                     end if;
                  end;

                  --  Check for null or possibly null range and issue warning.
                  --  We suppress such messages in generic templates and
                  --  instances, because in practice they tend to be dubious
                  --  in these cases.

                  if Nkind (DS) = N_Range
                    and then Comes_From_Source (N)
                  then
                     declare
                        L : constant Node_Id := Low_Bound  (DS);
                        H : constant Node_Id := High_Bound (DS);

                        Llo : Uint;
                        Lhi : Uint;
                        LOK : Boolean;
                        Hlo : Uint;
                        Hhi : Uint;
                        HOK : Boolean;

                     begin
                        Determine_Range (L, LOK, Llo, Lhi);
                        Determine_Range (H, HOK, Hlo, Hhi);

                        --  If range of loop is null, issue warning

                        if (LOK and HOK) and then Llo > Hhi then

                           --  Suppress the warning if inside a generic
                           --  template or instance, since in practice
                           --  they tend to be dubious in these cases since
                           --  they can result from intended parametrization.

                           if not Inside_A_Generic
                              and then not In_Instance
                           then
                              Error_Msg_N
                                ("?loop range is null, loop will not execute",
                                 DS);
                           end if;

                           --  Since we know the range of the loop is null,
                           --  set the appropriate flag to suppress any
                           --  warnings that would otherwise be issued in
                           --  the body of the loop that will not execute.
                           --  We do this even in the generic case, since
                           --  if it is dubious to warn on the null loop
                           --  itself, it is certainly dubious to warn for
                           --  conditions that occur inside it!

                           Set_Is_Null_Loop (Parent (N));

                        --  The other case for a warning is a reverse loop
                        --  where the upper bound is the integer literal
                        --  zero or one, and the lower bound can be positive.

                        --  For example, we have

                        --     for J in reverse N .. 1 loop

                        --  In practice, this is very likely to be a case
                        --  of reversing the bounds incorrectly in the range.

                        elsif Reverse_Present (LP)
                          and then Nkind (H) = N_Integer_Literal
                          and then (Intval (H) = Uint_0
                                      or else
                                    Intval (H) = Uint_1)
                          and then Lhi > Hhi
                        then
                           Error_Msg_N ("?loop range may be null", DS);
                        end if;
                     end;
                  end if;
               end;
            end if;
         end;
      end if;
   end Analyze_Iteration_Scheme;

   -------------------
   -- Analyze_Label --
   -------------------

   --  Note: the semantic work required for analyzing labels (setting them as
   --  reachable) was done in a prepass through the statements in the block,
   --  so that forward gotos would be properly handled. See Analyze_Statements
   --  for further details. The only processing required here is to deal with
   --  optimizations that depend on an assumption of sequential control flow,
   --  since of course the occurrence of a label breaks this assumption.

   procedure Analyze_Label (N : Node_Id) is
      pragma Warnings (Off, N);
   begin
      Kill_Current_Values;
   end Analyze_Label;

   --------------------------
   -- Analyze_Label_Entity --
   --------------------------

   procedure Analyze_Label_Entity (E : Entity_Id) is
   begin
      Set_Ekind           (E, E_Label);
      Set_Etype           (E, Standard_Void_Type);
      Set_Enclosing_Scope (E, Current_Scope);
      Set_Reachable       (E, True);
   end Analyze_Label_Entity;

   ----------------------------
   -- Analyze_Loop_Statement --
   ----------------------------

   procedure Analyze_Loop_Statement (N : Node_Id) is
      Id  : constant Node_Id := Identifier (N);
      Ent : Entity_Id;

   begin
      if Present (Id) then

         --  Make name visible, e.g. for use in exit statements. Loop
         --  labels are always considered to be referenced.

         Analyze (Id);
         Ent := Entity (Id);
         Generate_Reference  (Ent, N, ' ');
         Generate_Definition (Ent);

         --  If we found a label, mark its type. If not, ignore it, since it
         --  means we have a conflicting declaration, which would already have
         --  been diagnosed at declaration time. Set Label_Construct of the
         --  implicit label declaration, which is not created by the parser
         --  for generic units.

         if Ekind (Ent) = E_Label then
            Set_Ekind (Ent, E_Loop);

            if Nkind (Parent (Ent)) = N_Implicit_Label_Declaration then
               Set_Label_Construct (Parent (Ent), N);
            end if;
         end if;

      --  Case of no identifier present

      else
         Ent := New_Internal_Entity (E_Loop, Current_Scope, Sloc (N), 'L');
         Set_Etype (Ent,  Standard_Void_Type);
         Set_Parent (Ent, N);
      end if;

      --  Kill current values on entry to loop, since statements in body
      --  of loop may have been executed before the loop is entered.
      --  Similarly we kill values after the loop, since we do not know
      --  that the body of the loop was executed.

      Kill_Current_Values;
      New_Scope (Ent);
      Analyze_Iteration_Scheme (Iteration_Scheme (N));
      Analyze_Statements (Statements (N));
      Process_End_Label (N, 'e', Ent);
      End_Scope;
      Kill_Current_Values;
   end Analyze_Loop_Statement;

   ----------------------------
   -- Analyze_Null_Statement --
   ----------------------------

   --  Note: the semantics of the null statement is implemented by a single
   --  null statement, too bad everything isn't as simple as this!

   procedure Analyze_Null_Statement (N : Node_Id) is
      pragma Warnings (Off, N);
   begin
      null;
   end Analyze_Null_Statement;

   ------------------------
   -- Analyze_Statements --
   ------------------------

   procedure Analyze_Statements (L : List_Id) is
      S   : Node_Id;
      Lab : Entity_Id;

   begin
      --  The labels declared in the statement list are reachable from
      --  statements in the list. We do this as a prepass so that any
      --  goto statement will be properly flagged if its target is not
      --  reachable. This is not required, but is nice behavior!

      S := First (L);
      while Present (S) loop
         if Nkind (S) = N_Label then
            Analyze (Identifier (S));
            Lab := Entity (Identifier (S));

            --  If we found a label mark it as reachable

            if Ekind (Lab) = E_Label then
               Generate_Definition (Lab);
               Set_Reachable (Lab);

               if Nkind (Parent (Lab)) = N_Implicit_Label_Declaration then
                  Set_Label_Construct (Parent (Lab), S);
               end if;

            --  If we failed to find a label, it means the implicit declaration
            --  of the label was hidden.  A for-loop parameter can do this to
            --  a label with the same name inside the loop, since the implicit
            --  label declaration is in the innermost enclosing body or block
            --  statement.

            else
               Error_Msg_Sloc := Sloc (Lab);
               Error_Msg_N
                 ("implicit label declaration for & is hidden#",
                  Identifier (S));
            end if;
         end if;

         Next (S);
      end loop;

      --  Perform semantic analysis on all statements

      Conditional_Statements_Begin;

      S := First (L);
      while Present (S) loop
         Analyze (S);
         Next (S);
      end loop;

      Conditional_Statements_End;

      --  Make labels unreachable. Visibility is not sufficient, because
      --  labels in one if-branch for example are not reachable from the
      --  other branch, even though their declarations are in the enclosing
      --  declarative part.

      S := First (L);
      while Present (S) loop
         if Nkind (S) = N_Label then
            Set_Reachable (Entity (Identifier (S)), False);
         end if;

         Next (S);
      end loop;
   end Analyze_Statements;

   --------------------------------------------
   -- Check_Possible_Current_Value_Condition --
   --------------------------------------------

   procedure Check_Possible_Current_Value_Condition (Cnode : Node_Id) is
      Cond : Node_Id;

   begin
      --  Loop to deal with (ignore for now) any NOT operators present

      Cond := Condition (Cnode);
      while Nkind (Cond) = N_Op_Not loop
         Cond := Right_Opnd (Cond);
      end loop;

      --  Check possible relational operator

      if Nkind (Cond) = N_Op_Eq
           or else
         Nkind (Cond) = N_Op_Ne
           or else
         Nkind (Cond) = N_Op_Ge
           or else
         Nkind (Cond) = N_Op_Le
           or else
         Nkind (Cond) = N_Op_Gt
           or else
         Nkind (Cond) = N_Op_Lt
      then
         if Compile_Time_Known_Value (Right_Opnd (Cond))
           and then Nkind (Left_Opnd (Cond)) = N_Identifier
         then
            declare
               Ent : constant Entity_Id := Entity (Left_Opnd (Cond));

            begin
               if Ekind (Ent) = E_Variable
                    or else
                  Ekind (Ent) = E_Constant
                    or else
                  Is_Formal (Ent)
                    or else
                  Ekind (Ent) = E_Loop_Parameter
               then
                  --  Here we have a case where the Current_Value field
                  --  may need to be set. We set it if it is not already
                  --  set to a compile time expression value.

                  --  Note that this represents a decision that one
                  --  condition blots out another previous one. That's
                  --  certainly right if they occur at the same level.
                  --  If the second one is nested, then the decision is
                  --  neither right nor wrong (it would be equally OK
                  --  to leave the outer one in place, or take the new
                  --  inner one. Really we should record both, but our
                  --  data structures are not that elaborate.

                  if Nkind (Current_Value (Ent)) not in N_Subexpr then
                     Set_Current_Value (Ent, Cnode);
                  end if;
               end if;
            end;
         end if;
      end if;
   end Check_Possible_Current_Value_Condition;

   ----------------------------
   -- Check_Unreachable_Code --
   ----------------------------

   procedure Check_Unreachable_Code (N : Node_Id) is
      Error_Loc : Source_Ptr;
      P         : Node_Id;

   begin
      if Is_List_Member (N)
        and then Comes_From_Source (N)
      then
         declare
            Nxt : Node_Id;

         begin
            Nxt := Original_Node (Next (N));

            --  If a label follows us, then we never have dead code, since
            --  someone could branch to the label, so we just ignore it.

            if Nkind (Nxt) = N_Label then
               return;

            --  Otherwise see if we have a real statement following us

            elsif Present (Nxt)
              and then Comes_From_Source (Nxt)
              and then Is_Statement (Nxt)
            then
               --  Special very annoying exception. If we have a return that
               --  follows a raise, then we allow it without a warning, since
               --  the Ada RM annoyingly requires a useless return here!

               if Nkind (Original_Node (N)) /= N_Raise_Statement
                 or else Nkind (Nxt) /= N_Return_Statement
               then
                  --  The rather strange shenanigans with the warning message
                  --  here reflects the fact that Kill_Dead_Code is very good
                  --  at removing warnings in deleted code, and this is one
                  --  warning we would prefer NOT to have removed :-)

                  Error_Loc := Sloc (Nxt);

                  --  If we have unreachable code, analyze and remove the
                  --  unreachable code, since it is useless and we don't
                  --  want to generate junk warnings.

                  --  We skip this step if we are not in code generation mode.
                  --  This is the one case where we remove dead code in the
                  --  semantics as opposed to the expander, and we do not want
                  --  to remove code if we are not in code generation mode,
                  --  since this messes up the ASIS trees.

                  --  Note that one might react by moving the whole circuit to
                  --  exp_ch5, but then we lose the warning in -gnatc mode.

                  if Operating_Mode = Generate_Code then
                     loop
                        Nxt := Next (N);

                        --  Quit deleting when we have nothing more to delete
                        --  or if we hit a label (since someone could transfer
                        --  control to a label, so we should not delete it).

                        exit when No (Nxt) or else Nkind (Nxt) = N_Label;

                        --  Statement/declaration is to be deleted

                        Analyze (Nxt);
                        Remove (Nxt);
                        Kill_Dead_Code (Nxt);
                     end loop;
                  end if;

                  --  Now issue the warning

                  Error_Msg ("?unreachable code", Error_Loc);
               end if;

            --  If the unconditional transfer of control instruction is
            --  the last statement of a sequence, then see if our parent
            --  is one of the constructs for which we count unblocked exits,
            --  and if so, adjust the count.

            else
               P := Parent (N);

               --  Statements in THEN part or ELSE part of IF statement

               if Nkind (P) = N_If_Statement then
                  null;

               --  Statements in ELSIF part of an IF statement

               elsif Nkind (P) = N_Elsif_Part then
                  P := Parent (P);
                  pragma Assert (Nkind (P) = N_If_Statement);

               --  Statements in CASE statement alternative

               elsif Nkind (P) = N_Case_Statement_Alternative then
                  P := Parent (P);
                  pragma Assert (Nkind (P) = N_Case_Statement);

               --  Statements in body of block

               elsif Nkind (P) = N_Handled_Sequence_Of_Statements
                 and then Nkind (Parent (P)) = N_Block_Statement
               then
                  null;

               --  Statements in exception handler in a block

               elsif Nkind (P) = N_Exception_Handler
                 and then Nkind (Parent (P)) = N_Handled_Sequence_Of_Statements
                 and then Nkind (Parent (Parent (P))) = N_Block_Statement
               then
                  null;

               --  None of these cases, so return

               else
                  return;
               end if;

               --  This was one of the cases we are looking for (i.e. the
               --  parent construct was IF, CASE or block) so decrement count.

               Unblocked_Exit_Count := Unblocked_Exit_Count - 1;
            end if;
         end;
      end if;
   end Check_Unreachable_Code;

end Sem_Ch5;