]>
Commit | Line | Data |
---|---|---|
faee1e18 | 1 | ------------------------------------------------------------------------------ |
2 | -- -- | |
3 | -- GNAT COMPILER COMPONENTS -- | |
4 | -- -- | |
5 | -- S E M _ S P A R K -- | |
6 | -- -- | |
7 | -- B o d y -- | |
8 | -- -- | |
e9c75a1a | 9 | -- Copyright (C) 2017-2019, Free Software Foundation, Inc. -- |
faee1e18 | 10 | -- -- |
11 | -- GNAT is free software; you can redistribute it and/or modify it under -- | |
12 | -- terms of the GNU General Public License as published by the Free Soft- -- | |
13 | -- ware Foundation; either version 3, or (at your option) any later ver- -- | |
14 | -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- | |
15 | -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- | |
16 | -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- | |
17 | -- for more details. You should have received a copy of the GNU General -- | |
18 | -- Public License distributed with GNAT; see file COPYING3. If not, go to -- | |
19 | -- http://www.gnu.org/licenses for a complete copy of the license. -- | |
20 | -- -- | |
21 | -- GNAT was originally developed by the GNAT team at New York University. -- | |
22 | -- Extensive contributions were provided by Ada Core Technologies Inc. -- | |
23 | -- -- | |
24 | ------------------------------------------------------------------------------ | |
25 | ||
26 | with Atree; use Atree; | |
27 | with Einfo; use Einfo; | |
28 | with Errout; use Errout; | |
29 | with Namet; use Namet; | |
30 | with Nlists; use Nlists; | |
31 | with Opt; use Opt; | |
32 | with Osint; use Osint; | |
33 | with Sem_Prag; use Sem_Prag; | |
34 | with Sem_Util; use Sem_Util; | |
35 | with Sem_Aux; use Sem_Aux; | |
36 | with Sinfo; use Sinfo; | |
37 | with Snames; use Snames; | |
38 | with Treepr; use Treepr; | |
39 | ||
40 | with Ada.Unchecked_Deallocation; | |
41 | with GNAT.Dynamic_HTables; use GNAT.Dynamic_HTables; | |
42 | ||
43 | package body Sem_SPARK is | |
44 | ||
d087b9ca | 45 | --------------------------------------------------- |
46 | -- Handling of Permissions Associated with Paths -- | |
47 | --------------------------------------------------- | |
faee1e18 | 48 | |
49 | package Permissions is | |
50 | Elaboration_Context_Max : constant := 1009; | |
51 | -- The hash range | |
52 | ||
53 | type Elaboration_Context_Index is range 0 .. Elaboration_Context_Max - 1; | |
54 | ||
d087b9ca | 55 | function Elaboration_Context_Hash |
56 | (Key : Entity_Id) return Elaboration_Context_Index; | |
57 | -- The hash function | |
58 | ||
59 | -- Permission type associated with paths. These are related to but not | |
60 | -- the same as the states associated with names used in SPARK RM 3.10: | |
61 | -- Unrestricted, Observed, Borrowed, Moved. When ownership rules lead to | |
62 | -- a state change for a name, this may correspond to multiple permission | |
63 | -- changes for the paths corresponding to the name, its prefixes, and | |
64 | -- its extensions. For example, when an object is assigned to, the | |
65 | -- corresponding name gets into state Moved, while the path for the name | |
66 | -- gets permission Write_Only as well as every prefix of the name, and | |
67 | -- every suffix gets permission No_Access. | |
68 | ||
69 | type Perm_Kind_Option is | |
70 | (None, | |
71 | -- Special value used when no permission is passed around | |
72 | ||
73 | No_Access, | |
74 | -- The path cannot be accessed for reading or writing. This is the | |
75 | -- case for the path of a name in the Borrowed state. | |
76 | ||
77 | Read_Only, | |
78 | -- The path can only be accessed for reading. This is the case for | |
79 | -- the path of a name in the Observed state. | |
80 | ||
81 | Read_Write, | |
82 | -- The path can be accessed for both reading and writing. This is the | |
83 | -- case for the path of a name in the Unrestricted state. | |
84 | ||
85 | Write_Only | |
86 | -- The path can only be accessed for writing. This is the case for | |
87 | -- the path of a name in the Moved state. | |
88 | ); | |
faee1e18 | 89 | |
d087b9ca | 90 | subtype Perm_Kind is Perm_Kind_Option range No_Access .. Write_Only; |
91 | subtype Read_Perm is Perm_Kind range Read_Only .. Read_Write; | |
92 | subtype Write_Perm is Perm_Kind range Read_Write .. Write_Only; | |
faee1e18 | 93 | |
94 | type Perm_Tree_Wrapper; | |
95 | ||
96 | type Perm_Tree_Access is access Perm_Tree_Wrapper; | |
97 | -- A tree of permissions is defined, where the root is a whole object | |
98 | -- and tree branches follow access paths in memory. As Perm_Tree is a | |
99 | -- discriminated record, a wrapper type is used for the access type | |
100 | -- designating a subtree, to make it unconstrained so that it can be | |
101 | -- updated. | |
102 | ||
103 | -- Nodes in the permission tree are of different kinds | |
104 | ||
105 | type Path_Kind is | |
106 | (Entire_Object, -- Scalar object, or folded object of any type | |
107 | Reference, -- Unfolded object of access type | |
108 | Array_Component, -- Unfolded object of array type | |
109 | Record_Component -- Unfolded object of record type | |
110 | ); | |
111 | ||
112 | package Perm_Tree_Maps is new Simple_HTable | |
113 | (Header_Num => Elaboration_Context_Index, | |
d087b9ca | 114 | Key => Entity_Id, |
faee1e18 | 115 | Element => Perm_Tree_Access, |
116 | No_Element => null, | |
117 | Hash => Elaboration_Context_Hash, | |
118 | Equal => "="); | |
d087b9ca | 119 | -- The instantation of a hash table, with keys being entities and values |
120 | -- being pointers to permission trees. This is used to define global | |
121 | -- environment permissions (entities in that case are stand-alone | |
122 | -- objects or formal parameters), as well as the permissions for the | |
123 | -- extensions of a Record_Component node (entities in that case are | |
124 | -- record components). | |
faee1e18 | 125 | |
126 | -- The definition of permission trees. This is a tree, which has a | |
d087b9ca | 127 | -- permission at each node, and depending on the type of the node, can |
128 | -- have zero, one, or more children reached through an access to tree. | |
24dca06d | 129 | |
faee1e18 | 130 | type Perm_Tree (Kind : Path_Kind := Entire_Object) is record |
131 | Permission : Perm_Kind; | |
132 | -- Permission at this level in the path | |
133 | ||
134 | Is_Node_Deep : Boolean; | |
d087b9ca | 135 | -- Whether this node is of a "deep" type, i.e. an access type or a |
136 | -- composite type containing access type subcomponents. This | |
137 | -- corresponds to both "observing" and "owning" types in SPARK RM | |
138 | -- 3.10. To be used when moving the path. | |
faee1e18 | 139 | |
5bc0f990 | 140 | Explanation : Node_Id; |
141 | -- Node that can be used in an explanation for a permission mismatch | |
142 | ||
faee1e18 | 143 | case Kind is |
faee1e18 | 144 | -- An entire object is either a leaf (an object which cannot be |
145 | -- extended further in a path) or a subtree in folded form (which | |
146 | -- could later be unfolded further in another kind of node). The | |
147 | -- field Children_Permission specifies a permission for every | |
d087b9ca | 148 | -- extension of that node if that permission is different from the |
149 | -- node's permission. | |
faee1e18 | 150 | |
24dca06d | 151 | when Entire_Object => |
faee1e18 | 152 | Children_Permission : Perm_Kind; |
153 | ||
154 | -- Unfolded path of access type. The permission of the object | |
155 | -- pointed to is given in Get_All. | |
156 | ||
24dca06d | 157 | when Reference => |
faee1e18 | 158 | Get_All : Perm_Tree_Access; |
159 | ||
d087b9ca | 160 | -- Unfolded path of array type. The permission of elements is |
faee1e18 | 161 | -- given in Get_Elem. |
162 | ||
24dca06d | 163 | when Array_Component => |
faee1e18 | 164 | Get_Elem : Perm_Tree_Access; |
165 | ||
d087b9ca | 166 | -- Unfolded path of record type. The permission of the components |
167 | -- is given in Component. | |
faee1e18 | 168 | |
169 | when Record_Component => | |
170 | Component : Perm_Tree_Maps.Instance; | |
faee1e18 | 171 | end case; |
172 | end record; | |
173 | ||
174 | type Perm_Tree_Wrapper is record | |
175 | Tree : Perm_Tree; | |
176 | end record; | |
177 | -- We use this wrapper in order to have unconstrained discriminants | |
178 | ||
179 | type Perm_Env is new Perm_Tree_Maps.Instance; | |
d087b9ca | 180 | -- The definition of a permission environment for the analysis. This is |
181 | -- just a hash table from entities to permission trees. | |
faee1e18 | 182 | |
183 | type Perm_Env_Access is access Perm_Env; | |
184 | -- Access to permission environments | |
185 | ||
186 | package Env_Maps is new Simple_HTable | |
187 | (Header_Num => Elaboration_Context_Index, | |
188 | Key => Entity_Id, | |
189 | Element => Perm_Env_Access, | |
190 | No_Element => null, | |
191 | Hash => Elaboration_Context_Hash, | |
192 | Equal => "="); | |
193 | -- The instantiation of a hash table whose elements are permission | |
194 | -- environments. This hash table is used to save the environments at | |
195 | -- the entry of each loop, with the key being the loop label. | |
196 | ||
197 | type Env_Backups is new Env_Maps.Instance; | |
198 | -- The type defining the hash table saving the environments at the entry | |
199 | -- of each loop. | |
200 | ||
df177848 | 201 | package Variable_Maps is new Simple_HTable |
202 | (Header_Num => Elaboration_Context_Index, | |
203 | Key => Entity_Id, | |
204 | Element => Node_Id, | |
205 | No_Element => Empty, | |
206 | Hash => Elaboration_Context_Hash, | |
207 | Equal => "="); | |
208 | ||
209 | type Variable_Mapping is new Variable_Maps.Instance; | |
210 | -- Mapping from variables to nodes denoting paths that are observed or | |
211 | -- borrowed by the variable. | |
212 | ||
faee1e18 | 213 | -------------------- |
214 | -- Simple Getters -- | |
215 | -------------------- | |
216 | ||
217 | -- Simple getters to avoid having .all.Tree.Field everywhere. Of course, | |
218 | -- that's only for the top access, as otherwise this reverses the order | |
219 | -- in accesses visually. | |
220 | ||
221 | function Children_Permission (T : Perm_Tree_Access) return Perm_Kind; | |
222 | function Component (T : Perm_Tree_Access) return Perm_Tree_Maps.Instance; | |
5bc0f990 | 223 | function Explanation (T : Perm_Tree_Access) return Node_Id; |
faee1e18 | 224 | function Get_All (T : Perm_Tree_Access) return Perm_Tree_Access; |
225 | function Get_Elem (T : Perm_Tree_Access) return Perm_Tree_Access; | |
226 | function Is_Node_Deep (T : Perm_Tree_Access) return Boolean; | |
227 | function Kind (T : Perm_Tree_Access) return Path_Kind; | |
faee1e18 | 228 | function Permission (T : Perm_Tree_Access) return Perm_Kind; |
229 | ||
230 | ----------------------- | |
231 | -- Memory Management -- | |
232 | ----------------------- | |
233 | ||
234 | procedure Copy_Env | |
235 | (From : Perm_Env; | |
d087b9ca | 236 | To : in out Perm_Env); |
faee1e18 | 237 | -- Procedure to copy a permission environment |
238 | ||
df177848 | 239 | procedure Move_Env (From, To : in out Perm_Env); |
d087b9ca | 240 | -- Procedure to move a permission environment. It frees To, moves From |
241 | -- in To and sets From to Nil. | |
faee1e18 | 242 | |
df177848 | 243 | procedure Move_Variable_Mapping (From, To : in out Variable_Mapping); |
244 | -- Move a variable mapping, freeing memory as needed and resetting the | |
245 | -- source mapping. | |
246 | ||
247 | procedure Copy_Tree (From, To : Perm_Tree_Access); | |
faee1e18 | 248 | -- Procedure to copy a permission tree |
249 | ||
d087b9ca | 250 | procedure Free_Env (PE : in out Perm_Env); |
faee1e18 | 251 | -- Procedure to free a permission environment |
252 | ||
d087b9ca | 253 | procedure Free_Tree (PT : in out Perm_Tree_Access); |
faee1e18 | 254 | -- Procedure to free a permission tree |
255 | ||
256 | -------------------- | |
257 | -- Error Messages -- | |
258 | -------------------- | |
259 | ||
260 | procedure Perm_Mismatch | |
d087b9ca | 261 | (N : Node_Id; |
262 | Exp_Perm : Perm_Kind; | |
263 | Act_Perm : Perm_Kind; | |
5bc0f990 | 264 | Expl : Node_Id; |
d087b9ca | 265 | Forbidden_Perm : Boolean := False); |
faee1e18 | 266 | -- Issues a continuation error message about a mismatch between a |
267 | -- desired permission Exp_Perm and a permission obtained Act_Perm. N | |
268 | -- is the node on which the error is reported. | |
269 | ||
270 | end Permissions; | |
271 | ||
272 | package body Permissions is | |
273 | ||
274 | ------------------------- | |
275 | -- Children_Permission -- | |
276 | ------------------------- | |
277 | ||
24dca06d | 278 | function Children_Permission (T : Perm_Tree_Access) return Perm_Kind is |
faee1e18 | 279 | begin |
280 | return T.all.Tree.Children_Permission; | |
281 | end Children_Permission; | |
282 | ||
283 | --------------- | |
284 | -- Component -- | |
285 | --------------- | |
286 | ||
d087b9ca | 287 | function Component (T : Perm_Tree_Access) return Perm_Tree_Maps.Instance |
faee1e18 | 288 | is |
289 | begin | |
290 | return T.all.Tree.Component; | |
291 | end Component; | |
292 | ||
293 | -------------- | |
294 | -- Copy_Env -- | |
295 | -------------- | |
296 | ||
24dca06d | 297 | procedure Copy_Env (From : Perm_Env; To : in out Perm_Env) is |
faee1e18 | 298 | Comp_From : Perm_Tree_Access; |
24dca06d | 299 | Key_From : Perm_Tree_Maps.Key_Option; |
300 | Son : Perm_Tree_Access; | |
faee1e18 | 301 | |
302 | begin | |
d087b9ca | 303 | Free_Env (To); |
faee1e18 | 304 | Key_From := Get_First_Key (From); |
305 | while Key_From.Present loop | |
306 | Comp_From := Get (From, Key_From.K); | |
307 | pragma Assert (Comp_From /= null); | |
308 | ||
309 | Son := new Perm_Tree_Wrapper; | |
310 | Copy_Tree (Comp_From, Son); | |
311 | ||
312 | Set (To, Key_From.K, Son); | |
313 | Key_From := Get_Next_Key (From); | |
314 | end loop; | |
315 | end Copy_Env; | |
316 | ||
faee1e18 | 317 | --------------- |
318 | -- Copy_Tree -- | |
319 | --------------- | |
320 | ||
df177848 | 321 | procedure Copy_Tree (From, To : Perm_Tree_Access) is |
faee1e18 | 322 | begin |
d087b9ca | 323 | -- Copy the direct components of the tree |
324 | ||
faee1e18 | 325 | To.all := From.all; |
d087b9ca | 326 | |
327 | -- Now reallocate access components for a deep copy of the tree | |
328 | ||
faee1e18 | 329 | case Kind (From) is |
330 | when Entire_Object => | |
331 | null; | |
332 | ||
333 | when Reference => | |
334 | To.all.Tree.Get_All := new Perm_Tree_Wrapper; | |
faee1e18 | 335 | Copy_Tree (Get_All (From), Get_All (To)); |
336 | ||
337 | when Array_Component => | |
338 | To.all.Tree.Get_Elem := new Perm_Tree_Wrapper; | |
faee1e18 | 339 | Copy_Tree (Get_Elem (From), Get_Elem (To)); |
340 | ||
341 | when Record_Component => | |
342 | declare | |
343 | Comp_From : Perm_Tree_Access; | |
344 | Key_From : Perm_Tree_Maps.Key_Option; | |
345 | Son : Perm_Tree_Access; | |
346 | Hash_Table : Perm_Tree_Maps.Instance; | |
347 | begin | |
d087b9ca | 348 | -- We put a new hash table, so that it gets dealiased from |
349 | -- the Component (From) hash table. | |
faee1e18 | 350 | To.all.Tree.Component := Hash_Table; |
faee1e18 | 351 | Key_From := Perm_Tree_Maps.Get_First_Key |
352 | (Component (From)); | |
24dca06d | 353 | |
faee1e18 | 354 | while Key_From.Present loop |
355 | Comp_From := Perm_Tree_Maps.Get | |
356 | (Component (From), Key_From.K); | |
faee1e18 | 357 | pragma Assert (Comp_From /= null); |
358 | Son := new Perm_Tree_Wrapper; | |
faee1e18 | 359 | Copy_Tree (Comp_From, Son); |
faee1e18 | 360 | Perm_Tree_Maps.Set |
361 | (To.all.Tree.Component, Key_From.K, Son); | |
faee1e18 | 362 | Key_From := Perm_Tree_Maps.Get_Next_Key |
363 | (Component (From)); | |
364 | end loop; | |
365 | end; | |
366 | end case; | |
367 | end Copy_Tree; | |
368 | ||
369 | ------------------------------ | |
370 | -- Elaboration_Context_Hash -- | |
371 | ------------------------------ | |
372 | ||
373 | function Elaboration_Context_Hash | |
374 | (Key : Entity_Id) return Elaboration_Context_Index | |
375 | is | |
376 | begin | |
377 | return Elaboration_Context_Index (Key mod Elaboration_Context_Max); | |
378 | end Elaboration_Context_Hash; | |
379 | ||
380 | -------------- | |
381 | -- Free_Env -- | |
382 | -------------- | |
383 | ||
384 | procedure Free_Env (PE : in out Perm_Env) is | |
385 | CompO : Perm_Tree_Access; | |
386 | begin | |
387 | CompO := Get_First (PE); | |
388 | while CompO /= null loop | |
d087b9ca | 389 | Free_Tree (CompO); |
faee1e18 | 390 | CompO := Get_Next (PE); |
391 | end loop; | |
d087b9ca | 392 | |
393 | Reset (PE); | |
faee1e18 | 394 | end Free_Env; |
395 | ||
d087b9ca | 396 | --------------- |
397 | -- Free_Tree -- | |
398 | --------------- | |
faee1e18 | 399 | |
d087b9ca | 400 | procedure Free_Tree (PT : in out Perm_Tree_Access) is |
faee1e18 | 401 | procedure Free_Perm_Tree_Dealloc is |
402 | new Ada.Unchecked_Deallocation | |
403 | (Perm_Tree_Wrapper, Perm_Tree_Access); | |
404 | -- The deallocator for permission_trees | |
405 | ||
406 | begin | |
407 | case Kind (PT) is | |
408 | when Entire_Object => | |
d087b9ca | 409 | null; |
faee1e18 | 410 | |
411 | when Reference => | |
d087b9ca | 412 | Free_Tree (PT.all.Tree.Get_All); |
faee1e18 | 413 | |
414 | when Array_Component => | |
d087b9ca | 415 | Free_Tree (PT.all.Tree.Get_Elem); |
faee1e18 | 416 | |
417 | when Record_Component => | |
418 | declare | |
419 | Comp : Perm_Tree_Access; | |
420 | ||
421 | begin | |
faee1e18 | 422 | Comp := Perm_Tree_Maps.Get_First (Component (PT)); |
423 | while Comp /= null loop | |
24dca06d | 424 | |
faee1e18 | 425 | -- Free every Component subtree |
426 | ||
d087b9ca | 427 | Free_Tree (Comp); |
faee1e18 | 428 | Comp := Perm_Tree_Maps.Get_Next (Component (PT)); |
429 | end loop; | |
430 | end; | |
faee1e18 | 431 | end case; |
d087b9ca | 432 | |
433 | Free_Perm_Tree_Dealloc (PT); | |
434 | end Free_Tree; | |
faee1e18 | 435 | |
5bc0f990 | 436 | ----------------- |
437 | -- Explanation -- | |
438 | ----------------- | |
439 | ||
440 | function Explanation (T : Perm_Tree_Access) return Node_Id is | |
441 | begin | |
442 | return T.all.Tree.Explanation; | |
443 | end Explanation; | |
444 | ||
faee1e18 | 445 | ------------- |
446 | -- Get_All -- | |
447 | ------------- | |
448 | ||
24dca06d | 449 | function Get_All (T : Perm_Tree_Access) return Perm_Tree_Access is |
faee1e18 | 450 | begin |
451 | return T.all.Tree.Get_All; | |
452 | end Get_All; | |
453 | ||
454 | -------------- | |
455 | -- Get_Elem -- | |
456 | -------------- | |
457 | ||
24dca06d | 458 | function Get_Elem (T : Perm_Tree_Access) return Perm_Tree_Access is |
faee1e18 | 459 | begin |
460 | return T.all.Tree.Get_Elem; | |
461 | end Get_Elem; | |
462 | ||
463 | ------------------ | |
464 | -- Is_Node_Deep -- | |
465 | ------------------ | |
466 | ||
24dca06d | 467 | function Is_Node_Deep (T : Perm_Tree_Access) return Boolean is |
faee1e18 | 468 | begin |
469 | return T.all.Tree.Is_Node_Deep; | |
470 | end Is_Node_Deep; | |
471 | ||
472 | ---------- | |
473 | -- Kind -- | |
474 | ---------- | |
475 | ||
24dca06d | 476 | function Kind (T : Perm_Tree_Access) return Path_Kind is |
faee1e18 | 477 | begin |
478 | return T.all.Tree.Kind; | |
479 | end Kind; | |
480 | ||
d087b9ca | 481 | -------------- |
482 | -- Move_Env -- | |
483 | -------------- | |
faee1e18 | 484 | |
df177848 | 485 | procedure Move_Env (From, To : in out Perm_Env) is |
faee1e18 | 486 | begin |
d087b9ca | 487 | Free_Env (To); |
488 | To := From; | |
489 | From := Perm_Env (Perm_Tree_Maps.Nil); | |
490 | end Move_Env; | |
faee1e18 | 491 | |
df177848 | 492 | --------------------------- |
493 | -- Move_Variable_Mapping -- | |
494 | --------------------------- | |
495 | ||
496 | procedure Move_Variable_Mapping (From, To : in out Variable_Mapping) is | |
497 | begin | |
498 | Reset (To); | |
499 | To := From; | |
500 | From := Variable_Mapping (Variable_Maps.Nil); | |
501 | end Move_Variable_Mapping; | |
502 | ||
faee1e18 | 503 | ---------------- |
504 | -- Permission -- | |
505 | ---------------- | |
506 | ||
24dca06d | 507 | function Permission (T : Perm_Tree_Access) return Perm_Kind is |
faee1e18 | 508 | begin |
509 | return T.all.Tree.Permission; | |
510 | end Permission; | |
511 | ||
512 | ------------------- | |
513 | -- Perm_Mismatch -- | |
514 | ------------------- | |
515 | ||
d087b9ca | 516 | procedure Perm_Mismatch |
517 | (N : Node_Id; | |
518 | Exp_Perm : Perm_Kind; | |
519 | Act_Perm : Perm_Kind; | |
5bc0f990 | 520 | Expl : Node_Id; |
d087b9ca | 521 | Forbidden_Perm : Boolean := False) |
522 | is | |
faee1e18 | 523 | begin |
5bc0f990 | 524 | Error_Msg_Sloc := Sloc (Expl); |
525 | ||
d087b9ca | 526 | if Forbidden_Perm then |
5bc0f990 | 527 | if Exp_Perm = No_Access then |
528 | Error_Msg_N ("\object was moved #", N); | |
d087b9ca | 529 | else |
5bc0f990 | 530 | raise Program_Error; |
d087b9ca | 531 | end if; |
532 | else | |
5bc0f990 | 533 | case Exp_Perm is |
534 | when Write_Perm => | |
535 | if Act_Perm = Read_Only then | |
536 | Error_Msg_N | |
537 | ("\object was declared as not writeable #", N); | |
538 | else | |
539 | Error_Msg_N ("\object was moved #", N); | |
540 | end if; | |
541 | ||
542 | when Read_Only => | |
543 | Error_Msg_N ("\object was moved #", N); | |
544 | ||
545 | when No_Access => | |
546 | raise Program_Error; | |
547 | end case; | |
d087b9ca | 548 | end if; |
faee1e18 | 549 | end Perm_Mismatch; |
550 | ||
551 | end Permissions; | |
552 | ||
553 | use Permissions; | |
554 | ||
555 | -------------------------------------- | |
556 | -- Analysis modes for AST traversal -- | |
557 | -------------------------------------- | |
558 | ||
d087b9ca | 559 | -- The different modes for analysis. This allows checking whether a path |
560 | -- has the right permission, and also updating permissions when a path is | |
561 | -- moved, borrowed, or observed. | |
faee1e18 | 562 | |
a42b8e6a | 563 | type Extended_Checking_Mode is |
faee1e18 | 564 | |
a42b8e6a | 565 | (Read_Subexpr, |
566 | -- Special value used for assignment, to check that subexpressions of | |
567 | -- the assigned path are readable. | |
568 | ||
569 | Read, | |
72b189b6 | 570 | -- Default mode |
faee1e18 | 571 | |
572 | Move, | |
d087b9ca | 573 | -- Move semantics. Checks that paths have Read_Write permission. After |
574 | -- moving a path, its permission and the permission of its prefixes are | |
575 | -- set to Write_Only, while the permission of its extensions is set to | |
576 | -- No_Access. | |
faee1e18 | 577 | |
578 | Assign, | |
579 | -- Used for the target of an assignment, or an actual parameter with | |
d087b9ca | 580 | -- mode OUT. Checks that paths have Write_Perm permission. After |
581 | -- assigning to a path, its permission and the permission of its | |
582 | -- extensions are set to Read_Write. The permission of its prefixes may | |
583 | -- be normalized from Write_Only to Read_Write depending on other | |
584 | -- permissions in the tree (a prefix gets permission Read_Write when all | |
585 | -- its extensions become Read_Write). | |
24dca06d | 586 | |
587 | Borrow, | |
d087b9ca | 588 | -- Borrow semantics. Checks that paths have Read_Write permission. After |
589 | -- borrowing a path, its permission and the permission of its prefixes | |
590 | -- and extensions are set to No_Access. | |
faee1e18 | 591 | |
592 | Observe | |
d087b9ca | 593 | -- Observe semantics. Checks that paths have Read_Perm permission. After |
594 | -- observing a path, its permission and the permission of its prefixes | |
595 | -- and extensions are set to Read_Only. | |
faee1e18 | 596 | ); |
597 | ||
a42b8e6a | 598 | subtype Checking_Mode is Extended_Checking_Mode range Read .. Observe; |
599 | ||
d087b9ca | 600 | type Result_Kind is (Folded, Unfolded); |
faee1e18 | 601 | -- The type declaration to discriminate in the Perm_Or_Tree type |
602 | ||
603 | -- The result type of the function Get_Perm_Or_Tree. This returns either a | |
604 | -- tree when it found the appropriate tree, or a permission when the search | |
605 | -- finds a leaf and the subtree we are looking for is folded. In the last | |
606 | -- case, we return instead the Children_Permission field of the leaf. | |
607 | ||
608 | type Perm_Or_Tree (R : Result_Kind) is record | |
609 | case R is | |
5bc0f990 | 610 | when Folded => |
611 | Found_Permission : Perm_Kind; | |
612 | Explanation : Node_Id; | |
613 | when Unfolded => | |
614 | Tree_Access : Perm_Tree_Access; | |
faee1e18 | 615 | end case; |
616 | end record; | |
617 | ||
d087b9ca | 618 | type Error_Status is (OK, Error); |
619 | ||
faee1e18 | 620 | ----------------------- |
621 | -- Local subprograms -- | |
622 | ----------------------- | |
623 | ||
d087b9ca | 624 | function "<=" (P1, P2 : Perm_Kind) return Boolean; |
625 | function ">=" (P1, P2 : Perm_Kind) return Boolean; | |
626 | function Glb (P1, P2 : Perm_Kind) return Perm_Kind; | |
627 | function Lub (P1, P2 : Perm_Kind) return Perm_Kind; | |
628 | ||
629 | procedure Check_Assignment (Target : Node_Or_Entity_Id; Expr : Node_Id); | |
630 | -- Handle assignment as part of an assignment statement or an object | |
631 | -- declaration. | |
faee1e18 | 632 | |
633 | procedure Check_Call_Statement (Call : Node_Id); | |
634 | ||
635 | procedure Check_Callable_Body (Body_N : Node_Id); | |
d087b9ca | 636 | -- Entry point for the analysis of a subprogram or entry body |
faee1e18 | 637 | |
638 | procedure Check_Declaration (Decl : Node_Id); | |
639 | ||
7bd7174a | 640 | procedure Check_Declaration_Legality |
641 | (Decl : Node_Id; | |
642 | Force : Boolean; | |
643 | Legal : in out Boolean); | |
644 | -- Check the legality of declaration Decl regarding rules not related to | |
645 | -- permissions. Update Legal to False if a rule is violated. Issue an | |
646 | -- error message if Force is True and Emit_Messages returns True. | |
647 | ||
a42b8e6a | 648 | procedure Check_Expression (Expr : Node_Id; Mode : Extended_Checking_Mode); |
d087b9ca | 649 | pragma Precondition (Nkind_In (Expr, N_Index_Or_Discriminant_Constraint, |
650 | N_Range_Constraint, | |
9ac24568 | 651 | N_Subtype_Indication, |
1e5359c0 | 652 | N_Digits_Constraint, |
653 | N_Delta_Constraint) | |
d087b9ca | 654 | or else Nkind (Expr) in N_Subexpr); |
faee1e18 | 655 | |
d087b9ca | 656 | procedure Check_Globals (Subp : Entity_Id); |
657 | -- This procedure takes a subprogram called and checks the permission of | |
658 | -- globals. | |
659 | ||
660 | -- Checking proceduress for safe pointer usage. These procedures traverse | |
661 | -- the AST, check nodes for correct permissions according to SPARK RM 3.10, | |
662 | -- and update permissions depending on the node kind. The main procedure | |
663 | -- Check_Node is mutually recursive with the specialized procedures that | |
664 | -- follow. | |
faee1e18 | 665 | |
666 | procedure Check_List (L : List_Id); | |
667 | -- Calls Check_Node on each element of the list | |
668 | ||
d087b9ca | 669 | procedure Check_Loop_Statement (Stmt : Node_Id); |
faee1e18 | 670 | |
671 | procedure Check_Node (N : Node_Id); | |
d087b9ca | 672 | -- Main traversal procedure to check safe pointer usage |
faee1e18 | 673 | |
7d2da10f | 674 | procedure Check_Old_Loop_Entry (N : Node_Id); |
675 | -- Check SPARK RM 3.10(14) regarding 'Old and 'Loop_Entry | |
676 | ||
faee1e18 | 677 | procedure Check_Package_Body (Pack : Node_Id); |
678 | ||
d087b9ca | 679 | procedure Check_Package_Spec (Pack : Node_Id); |
faee1e18 | 680 | |
d087b9ca | 681 | procedure Check_Parameter_Or_Global |
682 | (Expr : Node_Id; | |
df177848 | 683 | Typ : Entity_Id; |
684 | Kind : Formal_Kind; | |
d087b9ca | 685 | Subp : Entity_Id; |
686 | Global_Var : Boolean); | |
687 | -- Check the permission of every actual parameter or global | |
688 | ||
df177848 | 689 | procedure Check_Pragma (Prag : Node_Id); |
690 | ||
d087b9ca | 691 | procedure Check_Source_Of_Borrow_Or_Observe |
692 | (Expr : Node_Id; | |
693 | Status : out Error_Status); | |
faee1e18 | 694 | |
695 | procedure Check_Statement (Stmt : Node_Id); | |
696 | ||
7bd7174a | 697 | procedure Check_Type_Legality |
698 | (Typ : Entity_Id; | |
699 | Force : Boolean; | |
700 | Legal : in out Boolean); | |
d087b9ca | 701 | -- Check that type Typ is either not deep, or that it is an observing or |
702 | -- owning type according to SPARK RM 3.10 | |
703 | ||
5bc0f990 | 704 | function Get_Expl (N : Node_Or_Entity_Id) return Node_Id; |
705 | -- The function that takes a name as input and returns an explanation node | |
706 | -- for the permission associated with it. | |
707 | ||
f8e6d133 | 708 | function Get_Observed_Or_Borrowed_Expr (Expr : Node_Id) return Node_Id; |
709 | pragma Precondition (Is_Path_Expression (Expr)); | |
710 | -- Return the expression being borrowed/observed when borrowing or | |
711 | -- observing Expr. If Expr is a call to a traversal function, this is | |
712 | -- the first actual, otherwise it is Expr. | |
713 | ||
d087b9ca | 714 | function Get_Perm (N : Node_Or_Entity_Id) return Perm_Kind; |
faee1e18 | 715 | -- The function that takes a name as input and returns a permission |
d087b9ca | 716 | -- associated with it. |
faee1e18 | 717 | |
718 | function Get_Perm_Or_Tree (N : Node_Id) return Perm_Or_Tree; | |
d087b9ca | 719 | pragma Precondition (Is_Path_Expression (N)); |
720 | -- This function gets a node and looks recursively to find the appropriate | |
721 | -- subtree for that node. If the tree is folded on that node, then it | |
722 | -- returns the permission given at the right level. | |
faee1e18 | 723 | |
724 | function Get_Perm_Tree (N : Node_Id) return Perm_Tree_Access; | |
d087b9ca | 725 | pragma Precondition (Is_Path_Expression (N)); |
726 | -- This function gets a node and looks recursively to find the appropriate | |
727 | -- subtree for that node. If the tree is folded, then it unrolls the tree | |
728 | -- up to the appropriate level. | |
729 | ||
df177848 | 730 | function Get_Root_Object |
731 | (Expr : Node_Id; | |
30cf324b | 732 | Through_Traversal : Boolean := True; |
733 | Is_Traversal : Boolean := False) return Entity_Id; | |
d087b9ca | 734 | -- Return the root of the path expression Expr, or Empty for an allocator, |
8699de72 | 735 | -- NULL, or a function call. Through_Traversal is True if it should follow |
30cf324b | 736 | -- through calls to traversal functions. Is_Traversal is True if this |
737 | -- corresponds to a value returned from a traversal function, which should | |
738 | -- allow if-expressions and case-expressions that refer to the same root, | |
739 | -- even if the paths are not the same in all branches. | |
d087b9ca | 740 | |
741 | generic | |
742 | with procedure Handle_Parameter_Or_Global | |
743 | (Expr : Node_Id; | |
df177848 | 744 | Formal_Typ : Entity_Id; |
d087b9ca | 745 | Param_Mode : Formal_Kind; |
746 | Subp : Entity_Id; | |
747 | Global_Var : Boolean); | |
748 | procedure Handle_Globals (Subp : Entity_Id); | |
749 | -- Handling of globals is factored in a generic instantiated below | |
750 | ||
751 | function Has_Array_Component (Expr : Node_Id) return Boolean; | |
752 | pragma Precondition (Is_Path_Expression (Expr)); | |
753 | -- This function gets a node and looks recursively to determine whether the | |
754 | -- given path has any array component. | |
faee1e18 | 755 | |
faee1e18 | 756 | procedure Hp (P : Perm_Env); |
757 | -- A procedure that outputs the hash table. This function is used only in | |
758 | -- the debugger to look into a hash table. | |
759 | pragma Unreferenced (Hp); | |
760 | ||
9ac24568 | 761 | procedure Illegal_Global_Usage (N : Node_Or_Entity_Id; E : Entity_Id); |
faee1e18 | 762 | pragma No_Return (Illegal_Global_Usage); |
763 | -- A procedure that is called when deep globals or aliased globals are used | |
764 | -- without any global aspect. | |
765 | ||
30cf324b | 766 | function Is_Path_Expression |
767 | (Expr : Node_Id; | |
768 | Is_Traversal : Boolean := False) return Boolean; | |
769 | -- Return whether Expr corresponds to a path. Is_Traversal is True if this | |
770 | -- corresponds to a value returned from a traversal function, which should | |
771 | -- allow if-expressions and case-expressions. | |
d087b9ca | 772 | |
30cf324b | 773 | function Is_Subpath_Expression |
774 | (Expr : Node_Id; | |
775 | Is_Traversal : Boolean := False) return Boolean; | |
776 | -- Return True if Expr can be part of a path expression. Is_Traversal is | |
777 | -- True if this corresponds to a value returned from a traversal function, | |
778 | -- which should allow if-expressions and case-expressions. | |
9ac24568 | 779 | |
df177848 | 780 | function Is_Prefix_Or_Almost (Pref, Expr : Node_Id) return Boolean; |
781 | -- Determine if the candidate Prefix is indeed a prefix of Expr, or almost | |
782 | -- a prefix, in the sense that they could still refer to overlapping memory | |
783 | -- locations. | |
784 | ||
df177848 | 785 | function Is_Traversal_Function_Call (Expr : Node_Id) return Boolean; |
786 | ||
d087b9ca | 787 | function Loop_Of_Exit (N : Node_Id) return Entity_Id; |
788 | -- A function that takes an exit statement node and returns the entity of | |
789 | -- the loop that this statement is exiting from. | |
790 | ||
791 | procedure Merge_Env (Source : in out Perm_Env; Target : in out Perm_Env); | |
792 | -- Merge Target and Source into Target, and then deallocate the Source | |
793 | ||
faee1e18 | 794 | procedure Perm_Error |
d087b9ca | 795 | (N : Node_Id; |
796 | Perm : Perm_Kind; | |
797 | Found_Perm : Perm_Kind; | |
5bc0f990 | 798 | Expl : Node_Id; |
d087b9ca | 799 | Forbidden_Perm : Boolean := False); |
faee1e18 | 800 | -- A procedure that is called when the permissions found contradict the |
d087b9ca | 801 | -- rules established by the RM. This function is called with the node and |
802 | -- the permission that was expected, and issues an error message with the | |
803 | -- appropriate values. | |
faee1e18 | 804 | |
805 | procedure Perm_Error_Subprogram_End | |
806 | (E : Entity_Id; | |
807 | Subp : Entity_Id; | |
808 | Perm : Perm_Kind; | |
5bc0f990 | 809 | Found_Perm : Perm_Kind; |
810 | Expl : Node_Id); | |
faee1e18 | 811 | -- A procedure that is called when the permissions found contradict the |
d087b9ca | 812 | -- rules established by the RM at the end of subprograms. This function is |
813 | -- called with the node, the node of the returning function, and the | |
814 | -- permission that was expected, and adds an error message with the | |
faee1e18 | 815 | -- appropriate values. |
816 | ||
d087b9ca | 817 | procedure Process_Path (Expr : Node_Id; Mode : Checking_Mode); |
818 | pragma Precondition (Is_Path_Expression (Expr)); | |
819 | -- Check correct permission for the path in the checking mode Mode, and | |
820 | -- update permissions of the path and its prefixes/extensions. | |
faee1e18 | 821 | |
822 | procedure Return_Globals (Subp : Entity_Id); | |
823 | -- Takes a subprogram as input, and checks that all borrowed global items | |
d087b9ca | 824 | -- of the subprogram indeed have Read_Write permission at the end of the |
825 | -- subprogram execution. | |
826 | ||
827 | procedure Return_Parameter_Or_Global | |
828 | (Id : Entity_Id; | |
df177848 | 829 | Typ : Entity_Id; |
830 | Kind : Formal_Kind; | |
d087b9ca | 831 | Subp : Entity_Id; |
832 | Global_Var : Boolean); | |
833 | -- Auxiliary procedure to Return_Parameters and Return_Globals | |
834 | ||
835 | procedure Return_Parameters (Subp : Entity_Id); | |
836 | -- Takes a subprogram as input, and checks that all out parameters of the | |
837 | -- subprogram indeed have Read_Write permission at the end of the | |
838 | -- subprogram execution. | |
faee1e18 | 839 | |
5bc0f990 | 840 | procedure Set_Perm_Extensions |
841 | (T : Perm_Tree_Access; | |
842 | P : Perm_Kind; | |
843 | Expl : Node_Id); | |
faee1e18 | 844 | -- This procedure takes an access to a permission tree and modifies the |
845 | -- tree so that any strict extensions of the given tree become of the | |
846 | -- access specified by parameter P. | |
847 | ||
5bc0f990 | 848 | procedure Set_Perm_Extensions_Move |
849 | (T : Perm_Tree_Access; | |
850 | E : Entity_Id; | |
851 | Expl : Node_Id); | |
d087b9ca | 852 | -- Set permissions to |
853 | -- No for any extension with more .all | |
854 | -- W for any deep extension with same number of .all | |
855 | -- RW for any shallow extension with same number of .all | |
faee1e18 | 856 | |
24dca06d | 857 | function Set_Perm_Prefixes |
d087b9ca | 858 | (N : Node_Id; |
5bc0f990 | 859 | Perm : Perm_Kind_Option; |
860 | Expl : Node_Id) return Perm_Tree_Access; | |
d087b9ca | 861 | pragma Precondition (Is_Path_Expression (N)); |
862 | -- This function modifies the permissions of a given node in the permission | |
863 | -- environment as well as all the prefixes of the path, to the new | |
864 | -- permission Perm. The general rule here is that everybody updates the | |
865 | -- permission of the subtree they are returning. | |
866 | ||
867 | procedure Set_Perm_Prefixes_Assign (N : Node_Id); | |
868 | pragma Precondition (Is_Path_Expression (N)); | |
869 | -- This procedure takes a name as an input and sets in the permission | |
870 | -- tree the given permission to the given name. The general rule here is | |
871 | -- that everybody updates the permission of the subtree it is returning. | |
872 | -- The permission of the assigned path has been set to RW by the caller. | |
873 | -- | |
874 | -- Case where we have to normalize a tree after the correct permissions | |
875 | -- have been assigned already. We look for the right subtree at the given | |
876 | -- path, actualize its permissions, and then call the normalization on its | |
877 | -- parent. | |
878 | -- | |
879 | -- Example: We assign x.y and x.z, and then Set_Perm_Prefixes_Assign will | |
880 | -- change the permission of x to RW because all of its components have | |
881 | -- permission RW. | |
faee1e18 | 882 | |
883 | procedure Setup_Globals (Subp : Entity_Id); | |
884 | -- Takes a subprogram as input, and sets up the environment by adding | |
885 | -- global items with appropriate permissions. | |
886 | ||
887 | procedure Setup_Parameter_Or_Global | |
e47eea05 | 888 | (Id : Entity_Id; |
df177848 | 889 | Typ : Entity_Id; |
890 | Kind : Formal_Kind; | |
d087b9ca | 891 | Subp : Entity_Id; |
5bc0f990 | 892 | Global_Var : Boolean; |
893 | Expl : Node_Id); | |
faee1e18 | 894 | -- Auxiliary procedure to Setup_Parameters and Setup_Globals |
895 | ||
896 | procedure Setup_Parameters (Subp : Entity_Id); | |
897 | -- Takes a subprogram as input, and sets up the environment by adding | |
898 | -- formal parameters with appropriate permissions. | |
899 | ||
94654315 | 900 | procedure Setup_Protected_Components (Subp : Entity_Id); |
901 | -- Takes a protected operation as input, and sets up the environment by | |
902 | -- adding protected components with appropriate permissions. | |
903 | ||
faee1e18 | 904 | ---------------------- |
905 | -- Global Variables -- | |
906 | ---------------------- | |
907 | ||
908 | Current_Perm_Env : Perm_Env; | |
909 | -- The permission environment that is used for the analysis. This | |
910 | -- environment can be saved, modified, reinitialized, but should be the | |
911 | -- only one valid from which to extract the permissions of the paths in | |
912 | -- scope. The analysis ensures at each point that this variables contains | |
913 | -- a valid permission environment with all bindings in scope. | |
914 | ||
d087b9ca | 915 | Inside_Procedure_Call : Boolean := False; |
916 | -- Set while checking the actual parameters of a procedure or entry call | |
917 | ||
918 | Inside_Elaboration : Boolean := False; | |
919 | -- Set during package spec/body elaboration, during which move and local | |
920 | -- observe/borrow are not allowed. As a result, no other checking is needed | |
921 | -- during elaboration. | |
faee1e18 | 922 | |
923 | Current_Loops_Envs : Env_Backups; | |
924 | -- This variable contains saves of permission environments at each loop the | |
925 | -- analysis entered. Each saved environment can be reached with the label | |
926 | -- of the loop. | |
927 | ||
928 | Current_Loops_Accumulators : Env_Backups; | |
929 | -- This variable contains the environments used as accumulators for loops, | |
930 | -- that consist of the merge of all environments at each exit point of | |
931 | -- the loop (which can also be the entry point of the loop in the case of | |
932 | -- non-infinite loops), each of them reachable from the label of the loop. | |
933 | -- We require that the environment stored in the accumulator be less | |
934 | -- restrictive than the saved environment at the beginning of the loop, and | |
935 | -- the permission environment after the loop is equal to the accumulator. | |
936 | ||
df177848 | 937 | Current_Borrowers : Variable_Mapping; |
938 | -- Mapping from borrowers to the path borrowed | |
939 | ||
940 | Current_Observers : Variable_Mapping; | |
941 | -- Mapping from observers to the path observed | |
942 | ||
d087b9ca | 943 | -------------------- |
944 | -- Handle_Globals -- | |
945 | -------------------- | |
faee1e18 | 946 | |
d087b9ca | 947 | -- Generic procedure is defined first so that instantiations can be defined |
948 | -- anywhere afterwards. | |
faee1e18 | 949 | |
d087b9ca | 950 | procedure Handle_Globals (Subp : Entity_Id) is |
faee1e18 | 951 | |
d087b9ca | 952 | -- Local subprograms |
faee1e18 | 953 | |
d087b9ca | 954 | procedure Handle_Globals_From_List |
955 | (First_Item : Node_Id; | |
956 | Kind : Formal_Kind); | |
957 | -- Handle global items from the list starting at Item | |
faee1e18 | 958 | |
d087b9ca | 959 | procedure Handle_Globals_Of_Mode (Global_Mode : Name_Id); |
960 | -- Handle global items for the mode Global_Mode | |
faee1e18 | 961 | |
d087b9ca | 962 | ------------------------------ |
963 | -- Handle_Globals_From_List -- | |
964 | ------------------------------ | |
faee1e18 | 965 | |
d087b9ca | 966 | procedure Handle_Globals_From_List |
967 | (First_Item : Node_Id; | |
968 | Kind : Formal_Kind) | |
969 | is | |
970 | Item : Node_Id := First_Item; | |
971 | E : Entity_Id; | |
faee1e18 | 972 | |
d087b9ca | 973 | begin |
974 | while Present (Item) loop | |
975 | E := Entity (Item); | |
faee1e18 | 976 | |
d087b9ca | 977 | -- Ignore abstract states, which play no role in pointer aliasing |
faee1e18 | 978 | |
d087b9ca | 979 | if Ekind (E) = E_Abstract_State then |
980 | null; | |
981 | else | |
982 | Handle_Parameter_Or_Global (Expr => Item, | |
1e5359c0 | 983 | Formal_Typ => Retysp (Etype (Item)), |
d087b9ca | 984 | Param_Mode => Kind, |
985 | Subp => Subp, | |
986 | Global_Var => True); | |
987 | end if; | |
df177848 | 988 | |
d087b9ca | 989 | Next_Global (Item); |
990 | end loop; | |
991 | end Handle_Globals_From_List; | |
faee1e18 | 992 | |
d087b9ca | 993 | ---------------------------- |
994 | -- Handle_Globals_Of_Mode -- | |
995 | ---------------------------- | |
faee1e18 | 996 | |
d087b9ca | 997 | procedure Handle_Globals_Of_Mode (Global_Mode : Name_Id) is |
998 | Kind : Formal_Kind; | |
faee1e18 | 999 | |
d087b9ca | 1000 | begin |
1001 | case Global_Mode is | |
1002 | when Name_Input | |
1003 | | Name_Proof_In | |
1004 | => | |
1005 | Kind := E_In_Parameter; | |
faee1e18 | 1006 | |
d087b9ca | 1007 | when Name_Output => |
1008 | Kind := E_Out_Parameter; | |
faee1e18 | 1009 | |
d087b9ca | 1010 | when Name_In_Out => |
1011 | Kind := E_In_Out_Parameter; | |
faee1e18 | 1012 | |
d087b9ca | 1013 | when others => |
1014 | raise Program_Error; | |
1015 | end case; | |
faee1e18 | 1016 | |
d087b9ca | 1017 | -- Check both global items from Global and Refined_Global pragmas |
faee1e18 | 1018 | |
d087b9ca | 1019 | Handle_Globals_From_List (First_Global (Subp, Global_Mode), Kind); |
1020 | Handle_Globals_From_List | |
1021 | (First_Global (Subp, Global_Mode, Refined => True), Kind); | |
1022 | end Handle_Globals_Of_Mode; | |
faee1e18 | 1023 | |
d087b9ca | 1024 | -- Start of processing for Handle_Globals |
faee1e18 | 1025 | |
d087b9ca | 1026 | begin |
1027 | Handle_Globals_Of_Mode (Name_Proof_In); | |
1028 | Handle_Globals_Of_Mode (Name_Input); | |
1029 | Handle_Globals_Of_Mode (Name_Output); | |
1030 | Handle_Globals_Of_Mode (Name_In_Out); | |
1031 | end Handle_Globals; | |
faee1e18 | 1032 | |
d087b9ca | 1033 | ---------- |
1034 | -- "<=" -- | |
1035 | ---------- | |
faee1e18 | 1036 | |
d087b9ca | 1037 | function "<=" (P1, P2 : Perm_Kind) return Boolean is |
1038 | begin | |
1039 | return P2 >= P1; | |
1040 | end "<="; | |
faee1e18 | 1041 | |
d087b9ca | 1042 | ---------- |
1043 | -- ">=" -- | |
1044 | ---------- | |
faee1e18 | 1045 | |
d087b9ca | 1046 | function ">=" (P1, P2 : Perm_Kind) return Boolean is |
1047 | begin | |
1048 | case P2 is | |
1049 | when No_Access => return True; | |
1050 | when Read_Only => return P1 in Read_Perm; | |
1051 | when Write_Only => return P1 in Write_Perm; | |
1052 | when Read_Write => return P1 = Read_Write; | |
1053 | end case; | |
1054 | end ">="; | |
faee1e18 | 1055 | |
d087b9ca | 1056 | ---------------------- |
1057 | -- Check_Assignment -- | |
1058 | ---------------------- | |
faee1e18 | 1059 | |
d087b9ca | 1060 | procedure Check_Assignment (Target : Node_Or_Entity_Id; Expr : Node_Id) is |
df177848 | 1061 | |
1062 | -- Local subprograms | |
1063 | ||
1064 | procedure Handle_Borrow | |
1065 | (Var : Entity_Id; | |
1066 | Expr : Node_Id; | |
1067 | Is_Decl : Boolean); | |
1068 | -- Update map of current borrowers | |
1069 | ||
1070 | procedure Handle_Observe | |
1071 | (Var : Entity_Id; | |
1072 | Expr : Node_Id; | |
1073 | Is_Decl : Boolean); | |
1074 | -- Update map of current observers | |
1075 | ||
1076 | ------------------- | |
1077 | -- Handle_Borrow -- | |
1078 | ------------------- | |
1079 | ||
1080 | procedure Handle_Borrow | |
1081 | (Var : Entity_Id; | |
1082 | Expr : Node_Id; | |
1083 | Is_Decl : Boolean) | |
1084 | is | |
f8e6d133 | 1085 | Borrowed : constant Node_Id := Get_Observed_Or_Borrowed_Expr (Expr); |
df177848 | 1086 | |
1087 | begin | |
df177848 | 1088 | -- SPARK RM 3.10(8): If the type of the target is an anonymous |
1089 | -- access-to-variable type (an owning access type), the source shall | |
1090 | -- be an owning access object [..] whose root object is the target | |
1091 | -- object itself. | |
1092 | ||
1093 | -- ??? In fact we could be slightly more permissive in allowing even | |
1094 | -- a call to a traversal function of the right form. | |
1095 | ||
1096 | if not Is_Decl | |
1097 | and then (Is_Traversal_Function_Call (Expr) | |
1098 | or else Get_Root_Object (Borrowed) /= Var) | |
1099 | then | |
1e5359c0 | 1100 | if Emit_Messages then |
1101 | Error_Msg_NE | |
1102 | ("source of assignment must have & as root" & | |
1103 | " (SPARK RM 3.10(8)))", | |
1104 | Expr, Var); | |
1105 | end if; | |
df177848 | 1106 | return; |
1107 | end if; | |
1108 | ||
1109 | Set (Current_Borrowers, Var, Borrowed); | |
1110 | end Handle_Borrow; | |
1111 | ||
1112 | -------------------- | |
1113 | -- Handle_Observe -- | |
1114 | -------------------- | |
1115 | ||
1116 | procedure Handle_Observe | |
1117 | (Var : Entity_Id; | |
1118 | Expr : Node_Id; | |
1119 | Is_Decl : Boolean) | |
1120 | is | |
f8e6d133 | 1121 | Observed : constant Node_Id := Get_Observed_Or_Borrowed_Expr (Expr); |
df177848 | 1122 | |
f8e6d133 | 1123 | begin |
df177848 | 1124 | -- ??? We are currently using the same restriction for observers |
1125 | -- as for borrowers. To be seen if the SPARK RM current rule really | |
1126 | -- allows more uses. | |
1127 | ||
1128 | if not Is_Decl | |
1129 | and then (Is_Traversal_Function_Call (Expr) | |
1130 | or else Get_Root_Object (Observed) /= Var) | |
1131 | then | |
1e5359c0 | 1132 | if Emit_Messages then |
1133 | Error_Msg_NE | |
1134 | ("source of assignment must have & as root" & | |
1135 | " (SPARK RM 3.10(8)))", | |
1136 | Expr, Var); | |
1137 | end if; | |
df177848 | 1138 | return; |
1139 | end if; | |
1140 | ||
1141 | Set (Current_Observers, Var, Observed); | |
1142 | end Handle_Observe; | |
1143 | ||
1144 | -- Local variables | |
1145 | ||
d087b9ca | 1146 | Target_Typ : constant Node_Id := Etype (Target); |
df177848 | 1147 | Is_Decl : constant Boolean := Nkind (Target) = N_Defining_Identifier; |
d087b9ca | 1148 | Target_Root : Entity_Id; |
1149 | Expr_Root : Entity_Id; | |
1150 | Perm : Perm_Kind; | |
1151 | Status : Error_Status; | |
7bd7174a | 1152 | Dummy : Boolean := True; |
faee1e18 | 1153 | |
df177848 | 1154 | -- Start of processing for Check_Assignment |
1155 | ||
d087b9ca | 1156 | begin |
7bd7174a | 1157 | Check_Type_Legality (Target_Typ, Force => True, Legal => Dummy); |
382c4351 | 1158 | |
d087b9ca | 1159 | if Is_Anonymous_Access_Type (Target_Typ) then |
1160 | Check_Source_Of_Borrow_Or_Observe (Expr, Status); | |
382c4351 | 1161 | |
d087b9ca | 1162 | if Status /= OK then |
1163 | return; | |
1164 | end if; | |
382c4351 | 1165 | |
df177848 | 1166 | if Is_Decl then |
d087b9ca | 1167 | Target_Root := Target; |
1168 | else | |
1169 | Target_Root := Get_Root_Object (Target); | |
1170 | end if; | |
5abc6fac | 1171 | |
d087b9ca | 1172 | Expr_Root := Get_Root_Object (Expr); |
faee1e18 | 1173 | |
7a5b8c31 | 1174 | -- SPARK RM 3.10(7): For an assignment statement where the target is |
1175 | -- a stand-alone object of an anonymous access-to-object type. | |
24dca06d | 1176 | |
df177848 | 1177 | pragma Assert (Present (Target_Root)); |
24dca06d | 1178 | |
7a5b8c31 | 1179 | -- If the type of the target is an anonymous access-to-constant type |
1180 | -- (an observing access type), the source shall be an owning access | |
1181 | -- object denoted by a name that is not in the Moved state, and whose | |
1182 | -- root object is not in the Moved state and is not declared at a | |
1183 | -- statically deeper accessibility level than that of the target | |
1184 | -- object. | |
24dca06d | 1185 | |
d087b9ca | 1186 | if Is_Access_Constant (Target_Typ) then |
1187 | Perm := Get_Perm (Expr); | |
24dca06d | 1188 | |
d087b9ca | 1189 | if Perm = No_Access then |
1190 | Perm_Error (Expr, No_Access, No_Access, | |
5bc0f990 | 1191 | Expl => Get_Expl (Expr), |
d087b9ca | 1192 | Forbidden_Perm => True); |
1193 | return; | |
1194 | end if; | |
24dca06d | 1195 | |
d087b9ca | 1196 | Perm := Get_Perm (Expr_Root); |
1197 | ||
1198 | if Perm = No_Access then | |
1199 | Perm_Error (Expr, No_Access, No_Access, | |
5bc0f990 | 1200 | Expl => Get_Expl (Expr_Root), |
d087b9ca | 1201 | Forbidden_Perm => True); |
1202 | return; | |
1203 | end if; | |
24dca06d | 1204 | |
d087b9ca | 1205 | -- ??? check accessibility level |
24dca06d | 1206 | |
7a5b8c31 | 1207 | -- If the type of the target is an anonymous access-to-variable |
1208 | -- type (an owning access type), the source shall be an owning | |
1209 | -- access object denoted by a name that is in the Unrestricted | |
1210 | -- state, and whose root object is the target object itself. | |
24dca06d | 1211 | |
f8e6d133 | 1212 | Check_Expression (Expr, Observe); |
df177848 | 1213 | Handle_Observe (Target_Root, Expr, Is_Decl); |
1214 | ||
d087b9ca | 1215 | else |
1216 | Perm := Get_Perm (Expr); | |
24dca06d | 1217 | |
d087b9ca | 1218 | if Perm /= Read_Write then |
5bc0f990 | 1219 | Perm_Error (Expr, Read_Write, Perm, Expl => Get_Expl (Expr)); |
d087b9ca | 1220 | return; |
1221 | end if; | |
24dca06d | 1222 | |
df177848 | 1223 | if not Is_Decl then |
1224 | if not Is_Entity_Name (Target) then | |
1e5359c0 | 1225 | if Emit_Messages then |
1226 | Error_Msg_N | |
1227 | ("target of borrow must be stand-alone variable", | |
1228 | Target); | |
1229 | end if; | |
df177848 | 1230 | return; |
24dca06d | 1231 | |
df177848 | 1232 | elsif Target_Root /= Expr_Root then |
1e5359c0 | 1233 | if Emit_Messages then |
1234 | Error_Msg_NE | |
1235 | ("source of borrow must be variable &", | |
1236 | Expr, Target); | |
1237 | end if; | |
df177848 | 1238 | return; |
1239 | end if; | |
d087b9ca | 1240 | end if; |
df177848 | 1241 | |
f8e6d133 | 1242 | Check_Expression (Expr, Borrow); |
df177848 | 1243 | Handle_Borrow (Target_Root, Expr, Is_Decl); |
d087b9ca | 1244 | end if; |
24dca06d | 1245 | |
d087b9ca | 1246 | elsif Is_Deep (Target_Typ) then |
24dca06d | 1247 | |
d087b9ca | 1248 | if Is_Path_Expression (Expr) then |
1249 | Check_Expression (Expr, Move); | |
24dca06d | 1250 | |
d087b9ca | 1251 | else |
1e5359c0 | 1252 | if Emit_Messages then |
1253 | Error_Msg_N ("expression not allowed as source of move", Expr); | |
1254 | end if; | |
d087b9ca | 1255 | return; |
1256 | end if; | |
24dca06d | 1257 | |
d087b9ca | 1258 | else |
1259 | Check_Expression (Expr, Read); | |
1260 | end if; | |
1261 | end Check_Assignment; | |
24dca06d | 1262 | |
d087b9ca | 1263 | -------------------------- |
1264 | -- Check_Call_Statement -- | |
1265 | -------------------------- | |
24dca06d | 1266 | |
d087b9ca | 1267 | procedure Check_Call_Statement (Call : Node_Id) is |
24dca06d | 1268 | |
d087b9ca | 1269 | Subp : constant Entity_Id := Get_Called_Entity (Call); |
24dca06d | 1270 | |
d087b9ca | 1271 | -- Local subprograms |
24dca06d | 1272 | |
d087b9ca | 1273 | procedure Check_Param (Formal : Entity_Id; Actual : Node_Id); |
1274 | -- Check the permission of every actual parameter | |
24dca06d | 1275 | |
d087b9ca | 1276 | procedure Update_Param (Formal : Entity_Id; Actual : Node_Id); |
1277 | -- Update the permission of OUT actual parameters | |
24dca06d | 1278 | |
d087b9ca | 1279 | ----------------- |
1280 | -- Check_Param -- | |
1281 | ----------------- | |
5abc6fac | 1282 | |
d087b9ca | 1283 | procedure Check_Param (Formal : Entity_Id; Actual : Node_Id) is |
1284 | begin | |
df177848 | 1285 | Check_Parameter_Or_Global |
1286 | (Expr => Actual, | |
1e5359c0 | 1287 | Typ => Retysp (Etype (Formal)), |
df177848 | 1288 | Kind => Ekind (Formal), |
1289 | Subp => Subp, | |
1290 | Global_Var => False); | |
d087b9ca | 1291 | end Check_Param; |
5abc6fac | 1292 | |
d087b9ca | 1293 | ------------------ |
1294 | -- Update_Param -- | |
1295 | ------------------ | |
24dca06d | 1296 | |
d087b9ca | 1297 | procedure Update_Param (Formal : Entity_Id; Actual : Node_Id) is |
1298 | Mode : constant Entity_Kind := Ekind (Formal); | |
24dca06d | 1299 | |
d087b9ca | 1300 | begin |
1301 | case Formal_Kind'(Mode) is | |
1302 | when E_Out_Parameter => | |
1303 | Check_Expression (Actual, Assign); | |
24dca06d | 1304 | |
d087b9ca | 1305 | when others => |
1306 | null; | |
1307 | end case; | |
1308 | end Update_Param; | |
24dca06d | 1309 | |
d087b9ca | 1310 | procedure Check_Params is new |
1311 | Iterate_Call_Parameters (Check_Param); | |
24dca06d | 1312 | |
d087b9ca | 1313 | procedure Update_Params is new |
1314 | Iterate_Call_Parameters (Update_Param); | |
24dca06d | 1315 | |
d087b9ca | 1316 | -- Start of processing for Check_Call_Statement |
24dca06d | 1317 | |
d087b9ca | 1318 | begin |
1319 | Inside_Procedure_Call := True; | |
1320 | Check_Params (Call); | |
1e5359c0 | 1321 | if Ekind (Get_Called_Entity (Call)) = E_Subprogram_Type then |
1322 | if Emit_Messages then | |
1323 | Error_Msg_N | |
1324 | ("call through access to subprogram is not allowed in SPARK", | |
1325 | Call); | |
1326 | end if; | |
1327 | else | |
1328 | Check_Globals (Get_Called_Entity (Call)); | |
1329 | end if; | |
24dca06d | 1330 | |
d087b9ca | 1331 | Inside_Procedure_Call := False; |
1332 | Update_Params (Call); | |
1333 | end Check_Call_Statement; | |
24dca06d | 1334 | |
d087b9ca | 1335 | ------------------------- |
1336 | -- Check_Callable_Body -- | |
1337 | ------------------------- | |
24dca06d | 1338 | |
d087b9ca | 1339 | procedure Check_Callable_Body (Body_N : Node_Id) is |
df177848 | 1340 | Save_In_Elab : constant Boolean := Inside_Elaboration; |
1341 | Body_Id : constant Entity_Id := Defining_Entity (Body_N); | |
1342 | Spec_Id : constant Entity_Id := Unique_Entity (Body_Id); | |
1343 | Prag : constant Node_Id := SPARK_Pragma (Body_Id); | |
1344 | ||
1345 | Saved_Env : Perm_Env; | |
1346 | Saved_Borrowers : Variable_Mapping; | |
1347 | Saved_Observers : Variable_Mapping; | |
24dca06d | 1348 | |
d087b9ca | 1349 | begin |
1350 | -- Only SPARK bodies are analyzed | |
1351 | ||
9ac24568 | 1352 | if No (Prag) |
1353 | or else Get_SPARK_Mode_From_Annotation (Prag) /= Opt.On | |
1354 | then | |
d087b9ca | 1355 | return; |
1356 | end if; | |
24dca06d | 1357 | |
d087b9ca | 1358 | Inside_Elaboration := False; |
24dca06d | 1359 | |
9eaacd61 | 1360 | if Ekind (Spec_Id) = E_Function |
1361 | and then Is_Anonymous_Access_Type (Etype (Spec_Id)) | |
1362 | and then not Is_Traversal_Function (Spec_Id) | |
1363 | then | |
1e5359c0 | 1364 | if Emit_Messages then |
1365 | Error_Msg_N ("anonymous access type for result only allowed for " | |
1366 | & "traversal functions", Spec_Id); | |
1367 | end if; | |
9eaacd61 | 1368 | return; |
1369 | end if; | |
1370 | ||
d087b9ca | 1371 | -- Save environment and put a new one in place |
24dca06d | 1372 | |
d087b9ca | 1373 | Move_Env (Current_Perm_Env, Saved_Env); |
df177848 | 1374 | Move_Variable_Mapping (Current_Borrowers, Saved_Borrowers); |
1375 | Move_Variable_Mapping (Current_Observers, Saved_Observers); | |
24dca06d | 1376 | |
d087b9ca | 1377 | -- Add formals and globals to the environment with adequate permissions |
24dca06d | 1378 | |
d087b9ca | 1379 | if Is_Subprogram_Or_Entry (Spec_Id) then |
1380 | Setup_Parameters (Spec_Id); | |
1381 | Setup_Globals (Spec_Id); | |
1382 | end if; | |
24dca06d | 1383 | |
94654315 | 1384 | -- For protected operations, add protected components to the environment |
1385 | -- with adequate permissions. | |
1386 | ||
1387 | if Is_Protected_Operation (Spec_Id) then | |
1388 | Setup_Protected_Components (Spec_Id); | |
1389 | end if; | |
1390 | ||
d087b9ca | 1391 | -- Analyze the body of the subprogram |
24dca06d | 1392 | |
d087b9ca | 1393 | Check_List (Declarations (Body_N)); |
1394 | Check_Node (Handled_Statement_Sequence (Body_N)); | |
24dca06d | 1395 | |
d087b9ca | 1396 | -- Check the read-write permissions of borrowed parameters/globals |
24dca06d | 1397 | |
d087b9ca | 1398 | if Ekind_In (Spec_Id, E_Procedure, E_Entry) |
1399 | and then not No_Return (Spec_Id) | |
1400 | then | |
1401 | Return_Parameters (Spec_Id); | |
1402 | Return_Globals (Spec_Id); | |
1403 | end if; | |
24dca06d | 1404 | |
d087b9ca | 1405 | -- Restore the saved environment and free the current one |
24dca06d | 1406 | |
d087b9ca | 1407 | Move_Env (Saved_Env, Current_Perm_Env); |
df177848 | 1408 | Move_Variable_Mapping (Saved_Borrowers, Current_Borrowers); |
1409 | Move_Variable_Mapping (Saved_Observers, Current_Observers); | |
24dca06d | 1410 | |
d087b9ca | 1411 | Inside_Elaboration := Save_In_Elab; |
1412 | end Check_Callable_Body; | |
24dca06d | 1413 | |
d087b9ca | 1414 | ----------------------- |
1415 | -- Check_Declaration -- | |
1416 | ----------------------- | |
24dca06d | 1417 | |
d087b9ca | 1418 | procedure Check_Declaration (Decl : Node_Id) is |
1419 | Target : constant Entity_Id := Defining_Identifier (Decl); | |
1420 | Target_Typ : constant Node_Id := Etype (Target); | |
1421 | Expr : Node_Id; | |
7bd7174a | 1422 | Dummy : Boolean := True; |
24dca06d | 1423 | |
d087b9ca | 1424 | begin |
7bd7174a | 1425 | -- Start with legality rules not related to permissions |
1426 | ||
1427 | Check_Declaration_Legality (Decl, Force => True, Legal => Dummy); | |
1428 | ||
1429 | -- Now check permission-related legality rules | |
1430 | ||
d087b9ca | 1431 | case N_Declaration'(Nkind (Decl)) is |
1432 | when N_Full_Type_Declaration => | |
7bd7174a | 1433 | null; |
24dca06d | 1434 | |
d087b9ca | 1435 | -- ??? What about component declarations with defaults. |
24dca06d | 1436 | |
d087b9ca | 1437 | when N_Subtype_Declaration => |
1438 | Check_Expression (Subtype_Indication (Decl), Read); | |
24dca06d | 1439 | |
d087b9ca | 1440 | when N_Object_Declaration => |
583f62e9 | 1441 | Expr := Expression (Decl); |
1442 | ||
7bd7174a | 1443 | if Present (Expr) then |
1444 | Check_Assignment (Target => Target, | |
1445 | Expr => Expr); | |
1446 | end if; | |
1447 | ||
1448 | if Is_Deep (Target_Typ) then | |
1449 | declare | |
1450 | Tree : constant Perm_Tree_Access := | |
1451 | new Perm_Tree_Wrapper' | |
1452 | (Tree => | |
1453 | (Kind => Entire_Object, | |
1454 | Is_Node_Deep => True, | |
1455 | Explanation => Decl, | |
1456 | Permission => Read_Write, | |
1457 | Children_Permission => Read_Write)); | |
1458 | begin | |
1459 | Set (Current_Perm_Env, Target, Tree); | |
1460 | end; | |
1461 | end if; | |
1462 | ||
1463 | when N_Iterator_Specification => | |
1464 | null; | |
1465 | ||
1466 | when N_Loop_Parameter_Specification => | |
1467 | null; | |
1468 | ||
1469 | -- Checking should not be called directly on these nodes | |
1470 | ||
1471 | when N_Function_Specification | |
1472 | | N_Entry_Declaration | |
1473 | | N_Procedure_Specification | |
1474 | | N_Component_Declaration | |
1475 | => | |
1476 | raise Program_Error; | |
1477 | ||
1478 | -- Ignored constructs for pointer checking | |
1479 | ||
1480 | when N_Formal_Object_Declaration | |
1481 | | N_Formal_Type_Declaration | |
1482 | | N_Incomplete_Type_Declaration | |
1483 | | N_Private_Extension_Declaration | |
1484 | | N_Private_Type_Declaration | |
1485 | | N_Protected_Type_Declaration | |
1486 | => | |
1487 | null; | |
1488 | ||
1489 | -- The following nodes are rewritten by semantic analysis | |
1490 | ||
1491 | when N_Expression_Function => | |
1492 | raise Program_Error; | |
1493 | end case; | |
1494 | end Check_Declaration; | |
1495 | ||
1496 | -------------------------------- | |
1497 | -- Check_Declaration_Legality -- | |
1498 | -------------------------------- | |
1499 | ||
1500 | procedure Check_Declaration_Legality | |
1501 | (Decl : Node_Id; | |
1502 | Force : Boolean; | |
1503 | Legal : in out Boolean) | |
1504 | is | |
1505 | function Original_Emit_Messages return Boolean renames Emit_Messages; | |
1506 | ||
1507 | function Emit_Messages return Boolean; | |
1508 | -- Local wrapper around generic formal parameter Emit_Messages, to | |
1509 | -- check the value of parameter Force before calling the original | |
1510 | -- Emit_Messages, and setting Legal to False. | |
1511 | ||
1512 | ------------------- | |
1513 | -- Emit_Messages -- | |
1514 | ------------------- | |
1515 | ||
1516 | function Emit_Messages return Boolean is | |
1517 | begin | |
1518 | Legal := False; | |
1519 | return Force and then Original_Emit_Messages; | |
1520 | end Emit_Messages; | |
1521 | ||
1522 | -- Local variables | |
1523 | ||
1524 | Target : constant Entity_Id := Defining_Identifier (Decl); | |
1525 | Target_Typ : constant Node_Id := Etype (Target); | |
1526 | Expr : Node_Id; | |
1527 | ||
1528 | -- Start of processing for Check_Declaration_Legality | |
1529 | ||
1530 | begin | |
1531 | case N_Declaration'(Nkind (Decl)) is | |
1532 | when N_Full_Type_Declaration => | |
1533 | Check_Type_Legality (Target, Force, Legal); | |
1534 | ||
1535 | when N_Subtype_Declaration => | |
1536 | null; | |
1537 | ||
1538 | when N_Object_Declaration => | |
1539 | Expr := Expression (Decl); | |
1540 | ||
1541 | Check_Type_Legality (Target_Typ, Force, Legal); | |
24dca06d | 1542 | |
583f62e9 | 1543 | -- A declaration of a stand-alone object of an anonymous access |
1544 | -- type shall have an explicit initial value and shall occur | |
1545 | -- immediately within a subprogram body, an entry body, or a | |
1546 | -- block statement (SPARK RM 3.10(4)). | |
1547 | ||
1548 | if Is_Anonymous_Access_Type (Target_Typ) then | |
1549 | declare | |
1550 | Scop : constant Entity_Id := Scope (Target); | |
1551 | begin | |
1552 | if not Is_Local_Context (Scop) then | |
1553 | if Emit_Messages then | |
1554 | Error_Msg_N | |
1555 | ("object of anonymous access type must be declared " | |
1556 | & "immediately within a subprogram, entry or block " | |
1557 | & "(SPARK RM 3.10(4))", Decl); | |
1558 | end if; | |
1559 | end if; | |
1560 | end; | |
1561 | ||
1562 | if No (Expr) then | |
1563 | if Emit_Messages then | |
1564 | Error_Msg_N ("object of anonymous access type must be " | |
1565 | & "initialized (SPARK RM 3.10(4))", Decl); | |
1566 | end if; | |
1567 | end if; | |
1568 | end if; | |
1569 | ||
d087b9ca | 1570 | when N_Iterator_Specification => |
1571 | null; | |
faee1e18 | 1572 | |
d087b9ca | 1573 | when N_Loop_Parameter_Specification => |
1574 | null; | |
faee1e18 | 1575 | |
1576 | -- Checking should not be called directly on these nodes | |
1577 | ||
24dca06d | 1578 | when N_Function_Specification |
faee1e18 | 1579 | | N_Entry_Declaration |
1580 | | N_Procedure_Specification | |
24dca06d | 1581 | | N_Component_Declaration |
faee1e18 | 1582 | => |
1583 | raise Program_Error; | |
1584 | ||
1585 | -- Ignored constructs for pointer checking | |
1586 | ||
1587 | when N_Formal_Object_Declaration | |
1588 | | N_Formal_Type_Declaration | |
1589 | | N_Incomplete_Type_Declaration | |
1590 | | N_Private_Extension_Declaration | |
1591 | | N_Private_Type_Declaration | |
1592 | | N_Protected_Type_Declaration | |
1593 | => | |
1594 | null; | |
1595 | ||
1596 | -- The following nodes are rewritten by semantic analysis | |
1597 | ||
1598 | when N_Expression_Function => | |
1599 | raise Program_Error; | |
1600 | end case; | |
7bd7174a | 1601 | end Check_Declaration_Legality; |
faee1e18 | 1602 | |
1603 | ---------------------- | |
1604 | -- Check_Expression -- | |
1605 | ---------------------- | |
1606 | ||
a42b8e6a | 1607 | procedure Check_Expression |
1608 | (Expr : Node_Id; | |
1609 | Mode : Extended_Checking_Mode) | |
1610 | is | |
d087b9ca | 1611 | -- Local subprograms |
24dca06d | 1612 | |
d087b9ca | 1613 | function Is_Type_Name (Expr : Node_Id) return Boolean; |
1614 | -- Detect when a path expression is in fact a type name | |
faee1e18 | 1615 | |
a080baa9 | 1616 | procedure Move_Expression (Expr : Node_Id); |
1617 | -- Some subexpressions are only analyzed in Move mode. This is a | |
1618 | -- specialized version of Check_Expression for that case. | |
1619 | ||
1620 | procedure Move_Expression_List (L : List_Id); | |
1621 | -- Call Move_Expression on every expression in the list L | |
1622 | ||
d087b9ca | 1623 | procedure Read_Expression (Expr : Node_Id); |
1624 | -- Most subexpressions are only analyzed in Read mode. This is a | |
1625 | -- specialized version of Check_Expression for that case. | |
24dca06d | 1626 | |
d087b9ca | 1627 | procedure Read_Expression_List (L : List_Id); |
1628 | -- Call Read_Expression on every expression in the list L | |
faee1e18 | 1629 | |
d087b9ca | 1630 | procedure Read_Indexes (Expr : Node_Id); |
d087b9ca | 1631 | -- When processing a path, the index expressions and function call |
1632 | -- arguments occurring on the path should be analyzed in Read mode. | |
24dca06d | 1633 | |
d087b9ca | 1634 | ------------------ |
1635 | -- Is_Type_Name -- | |
1636 | ------------------ | |
faee1e18 | 1637 | |
d087b9ca | 1638 | function Is_Type_Name (Expr : Node_Id) return Boolean is |
1639 | begin | |
1640 | return Nkind_In (Expr, N_Expanded_Name, N_Identifier) | |
1641 | and then Is_Type (Entity (Expr)); | |
1642 | end Is_Type_Name; | |
faee1e18 | 1643 | |
a080baa9 | 1644 | --------------------- |
1645 | -- Move_Expression -- | |
1646 | --------------------- | |
1647 | ||
1648 | -- Distinguish the case where the argument is a path expression that | |
1649 | -- needs explicit moving. | |
1650 | ||
1651 | procedure Move_Expression (Expr : Node_Id) is | |
1652 | begin | |
1653 | if Is_Path_Expression (Expr) then | |
1654 | Check_Expression (Expr, Move); | |
1655 | else | |
1656 | Read_Expression (Expr); | |
1657 | end if; | |
1658 | end Move_Expression; | |
1659 | ||
1660 | -------------------------- | |
1661 | -- Move_Expression_List -- | |
1662 | -------------------------- | |
1663 | ||
1664 | procedure Move_Expression_List (L : List_Id) is | |
1665 | N : Node_Id; | |
1666 | begin | |
1667 | N := First (L); | |
1668 | while Present (N) loop | |
1669 | Move_Expression (N); | |
1670 | Next (N); | |
1671 | end loop; | |
1672 | end Move_Expression_List; | |
1673 | ||
d087b9ca | 1674 | --------------------- |
1675 | -- Read_Expression -- | |
1676 | --------------------- | |
faee1e18 | 1677 | |
d087b9ca | 1678 | procedure Read_Expression (Expr : Node_Id) is |
1679 | begin | |
1680 | Check_Expression (Expr, Read); | |
1681 | end Read_Expression; | |
faee1e18 | 1682 | |
d087b9ca | 1683 | -------------------------- |
1684 | -- Read_Expression_List -- | |
1685 | -------------------------- | |
faee1e18 | 1686 | |
d087b9ca | 1687 | procedure Read_Expression_List (L : List_Id) is |
1688 | N : Node_Id; | |
1689 | begin | |
1690 | N := First (L); | |
1691 | while Present (N) loop | |
1692 | Read_Expression (N); | |
1693 | Next (N); | |
1694 | end loop; | |
1695 | end Read_Expression_List; | |
faee1e18 | 1696 | |
d087b9ca | 1697 | ------------------ |
1698 | -- Read_Indexes -- | |
1699 | ------------------ | |
faee1e18 | 1700 | |
d087b9ca | 1701 | procedure Read_Indexes (Expr : Node_Id) is |
faee1e18 | 1702 | |
d087b9ca | 1703 | -- Local subprograms |
faee1e18 | 1704 | |
a080baa9 | 1705 | function Is_Singleton_Choice (Choices : List_Id) return Boolean; |
1706 | -- Return whether Choices is a singleton choice | |
1707 | ||
d087b9ca | 1708 | procedure Read_Param (Formal : Entity_Id; Actual : Node_Id); |
a080baa9 | 1709 | -- Call Read_Expression on the actual |
1710 | ||
1711 | ------------------------- | |
1712 | -- Is_Singleton_Choice -- | |
1713 | ------------------------- | |
1714 | ||
1715 | function Is_Singleton_Choice (Choices : List_Id) return Boolean is | |
1716 | Choice : constant Node_Id := First (Choices); | |
1717 | begin | |
1718 | return List_Length (Choices) = 1 | |
1719 | and then Nkind (Choice) /= N_Others_Choice | |
1720 | and then not Nkind_In (Choice, N_Subtype_Indication, N_Range) | |
1721 | and then not | |
1722 | (Nkind_In (Choice, N_Identifier, N_Expanded_Name) | |
1723 | and then Is_Type (Entity (Choice))); | |
1724 | end Is_Singleton_Choice; | |
e47eea05 | 1725 | |
d087b9ca | 1726 | ---------------- |
1727 | -- Read_Param -- | |
1728 | ---------------- | |
faee1e18 | 1729 | |
d087b9ca | 1730 | procedure Read_Param (Formal : Entity_Id; Actual : Node_Id) is |
1731 | pragma Unreferenced (Formal); | |
1732 | begin | |
1733 | Read_Expression (Actual); | |
1734 | end Read_Param; | |
faee1e18 | 1735 | |
d087b9ca | 1736 | procedure Read_Params is new Iterate_Call_Parameters (Read_Param); |
faee1e18 | 1737 | |
d087b9ca | 1738 | -- Start of processing for Read_Indexes |
faee1e18 | 1739 | |
d087b9ca | 1740 | begin |
9ac24568 | 1741 | if not Is_Subpath_Expression (Expr) then |
1e5359c0 | 1742 | if Emit_Messages then |
1743 | Error_Msg_N | |
1744 | ("name expected here for move/borrow/observe", Expr); | |
1745 | end if; | |
9eaacd61 | 1746 | return; |
1747 | end if; | |
1748 | ||
d087b9ca | 1749 | case N_Subexpr'(Nkind (Expr)) is |
1750 | when N_Identifier | |
1751 | | N_Expanded_Name | |
1752 | | N_Null | |
1753 | => | |
1754 | null; | |
faee1e18 | 1755 | |
d087b9ca | 1756 | when N_Explicit_Dereference |
1757 | | N_Selected_Component | |
1758 | => | |
1759 | Read_Indexes (Prefix (Expr)); | |
faee1e18 | 1760 | |
d087b9ca | 1761 | when N_Indexed_Component => |
1762 | Read_Indexes (Prefix (Expr)); | |
1763 | Read_Expression_List (Expressions (Expr)); | |
faee1e18 | 1764 | |
d087b9ca | 1765 | when N_Slice => |
1766 | Read_Indexes (Prefix (Expr)); | |
1767 | Read_Expression (Discrete_Range (Expr)); | |
faee1e18 | 1768 | |
a080baa9 | 1769 | -- The argument of an allocator is moved as part of the implicit |
1770 | -- assignment. | |
1771 | ||
d087b9ca | 1772 | when N_Allocator => |
a080baa9 | 1773 | Move_Expression (Expression (Expr)); |
faee1e18 | 1774 | |
d087b9ca | 1775 | when N_Function_Call => |
1776 | Read_Params (Expr); | |
1e5359c0 | 1777 | if Ekind (Get_Called_Entity (Expr)) = E_Subprogram_Type then |
1778 | if Emit_Messages then | |
1779 | Error_Msg_N | |
1780 | ("call through access to subprogram is not allowed in " | |
1781 | & "SPARK", Expr); | |
1782 | end if; | |
1783 | else | |
1784 | Check_Globals (Get_Called_Entity (Expr)); | |
1785 | end if; | |
faee1e18 | 1786 | |
9ac24568 | 1787 | when N_Op_Concat => |
1788 | Read_Expression (Left_Opnd (Expr)); | |
1789 | Read_Expression (Right_Opnd (Expr)); | |
1790 | ||
d087b9ca | 1791 | when N_Qualified_Expression |
1792 | | N_Type_Conversion | |
1793 | | N_Unchecked_Type_Conversion | |
1794 | => | |
1795 | Read_Indexes (Expression (Expr)); | |
faee1e18 | 1796 | |
a080baa9 | 1797 | when N_Aggregate => |
1798 | declare | |
1799 | Assocs : constant List_Id := Component_Associations (Expr); | |
1800 | CL : List_Id; | |
1801 | Assoc : Node_Id := Nlists.First (Assocs); | |
1802 | Choice : Node_Id; | |
1803 | ||
1804 | begin | |
1805 | -- The subexpressions of an aggregate are moved as part | |
1806 | -- of the implicit assignments. Handle the positional | |
1807 | -- components first. | |
1808 | ||
1809 | Move_Expression_List (Expressions (Expr)); | |
1810 | ||
98eb8111 | 1811 | -- Handle the named components next |
a080baa9 | 1812 | |
1813 | while Present (Assoc) loop | |
1814 | CL := Choices (Assoc); | |
1815 | ||
1816 | -- For an array aggregate, we should also check that the | |
1817 | -- expressions used in choices are readable. | |
1818 | ||
1819 | if Is_Array_Type (Etype (Expr)) then | |
1820 | Choice := Nlists.First (CL); | |
1821 | while Present (Choice) loop | |
1822 | if Nkind (Choice) /= N_Others_Choice then | |
1823 | Read_Expression (Choice); | |
1824 | end if; | |
1825 | Next (Choice); | |
1826 | end loop; | |
1827 | end if; | |
1828 | ||
1829 | -- There can be only one element for a value of deep type | |
1830 | -- in order to avoid aliasing. | |
1831 | ||
1e5359c0 | 1832 | if not Box_Present (Assoc) |
9ac24568 | 1833 | and then Is_Deep (Etype (Expression (Assoc))) |
a080baa9 | 1834 | and then not Is_Singleton_Choice (CL) |
1e5359c0 | 1835 | and then Emit_Messages |
a080baa9 | 1836 | then |
98eb8111 | 1837 | Error_Msg_F |
1838 | ("singleton choice required to prevent aliasing", | |
1839 | First (CL)); | |
a080baa9 | 1840 | end if; |
1841 | ||
1842 | -- The subexpressions of an aggregate are moved as part | |
1843 | -- of the implicit assignments. | |
1844 | ||
9ac24568 | 1845 | if not Box_Present (Assoc) then |
1846 | Move_Expression (Expression (Assoc)); | |
1847 | end if; | |
a080baa9 | 1848 | |
1849 | Next (Assoc); | |
1850 | end loop; | |
1851 | end; | |
1852 | ||
1853 | when N_Extension_Aggregate => | |
1854 | declare | |
1855 | Exprs : constant List_Id := Expressions (Expr); | |
1856 | Assocs : constant List_Id := Component_Associations (Expr); | |
1857 | CL : List_Id; | |
1858 | Assoc : Node_Id := Nlists.First (Assocs); | |
1859 | ||
1860 | begin | |
1861 | Move_Expression (Ancestor_Part (Expr)); | |
1862 | ||
1863 | -- No positional components allowed at this stage | |
1864 | ||
1865 | if Present (Exprs) then | |
1866 | raise Program_Error; | |
1867 | end if; | |
1868 | ||
1869 | while Present (Assoc) loop | |
1870 | CL := Choices (Assoc); | |
1871 | ||
1872 | -- Only singleton components allowed at this stage | |
1873 | ||
1874 | if not Is_Singleton_Choice (CL) then | |
1875 | raise Program_Error; | |
1876 | end if; | |
1877 | ||
1878 | -- The subexpressions of an aggregate are moved as part | |
1879 | -- of the implicit assignments. | |
1880 | ||
9ac24568 | 1881 | if not Box_Present (Assoc) then |
1882 | Move_Expression (Expression (Assoc)); | |
1883 | end if; | |
a080baa9 | 1884 | |
1885 | Next (Assoc); | |
1886 | end loop; | |
1887 | end; | |
1888 | ||
30cf324b | 1889 | when N_If_Expression => |
1890 | declare | |
1891 | Cond : constant Node_Id := First (Expressions (Expr)); | |
1892 | Then_Part : constant Node_Id := Next (Cond); | |
1893 | Else_Part : constant Node_Id := Next (Then_Part); | |
1894 | begin | |
1895 | Read_Expression (Cond); | |
1896 | Read_Indexes (Then_Part); | |
1897 | Read_Indexes (Else_Part); | |
1898 | end; | |
1899 | ||
1900 | when N_Case_Expression => | |
1901 | declare | |
1902 | Cases : constant List_Id := Alternatives (Expr); | |
1903 | Cur_Case : Node_Id := First (Cases); | |
1904 | ||
1905 | begin | |
1906 | Read_Expression (Expression (Expr)); | |
1907 | ||
1908 | while Present (Cur_Case) loop | |
1909 | Read_Indexes (Expression (Cur_Case)); | |
1910 | Next (Cur_Case); | |
1911 | end loop; | |
1912 | end; | |
1913 | ||
9ac24568 | 1914 | when N_Attribute_Reference => |
1915 | pragma Assert | |
1916 | (Get_Attribute_Id (Attribute_Name (Expr)) = | |
1917 | Attribute_Loop_Entry | |
1918 | or else | |
1e5359c0 | 1919 | Get_Attribute_Id (Attribute_Name (Expr)) = Attribute_Update |
1920 | or else | |
1921 | Get_Attribute_Id (Attribute_Name (Expr)) = Attribute_Image); | |
9ac24568 | 1922 | |
1923 | Read_Expression (Prefix (Expr)); | |
1924 | ||
1925 | if Get_Attribute_Id (Attribute_Name (Expr)) = Attribute_Update | |
1e5359c0 | 1926 | or else (Get_Attribute_Id (Attribute_Name (Expr)) = |
1927 | Attribute_Image | |
1928 | and then Is_Type_Name (Prefix (Expr))) | |
9ac24568 | 1929 | then |
1930 | Read_Expression_List (Expressions (Expr)); | |
1931 | end if; | |
1932 | ||
d087b9ca | 1933 | when others => |
1934 | raise Program_Error; | |
1935 | end case; | |
1936 | end Read_Indexes; | |
faee1e18 | 1937 | |
d087b9ca | 1938 | -- Start of processing for Check_Expression |
faee1e18 | 1939 | |
d087b9ca | 1940 | begin |
1941 | if Is_Type_Name (Expr) then | |
1942 | return; | |
faee1e18 | 1943 | |
d087b9ca | 1944 | elsif Is_Path_Expression (Expr) then |
a42b8e6a | 1945 | if Mode /= Assign then |
1946 | Read_Indexes (Expr); | |
1947 | end if; | |
1948 | ||
1949 | if Mode /= Read_Subexpr then | |
1950 | Process_Path (Expr, Mode); | |
1951 | end if; | |
1952 | ||
d087b9ca | 1953 | return; |
1954 | end if; | |
faee1e18 | 1955 | |
d087b9ca | 1956 | -- Expressions that are not path expressions should only be analyzed in |
1957 | -- Read mode. | |
faee1e18 | 1958 | |
9eaacd61 | 1959 | if Mode /= Read then |
1e5359c0 | 1960 | if Emit_Messages then |
1961 | Error_Msg_N ("name expected here for move/borrow/observe", Expr); | |
1962 | end if; | |
9eaacd61 | 1963 | return; |
1964 | end if; | |
faee1e18 | 1965 | |
d087b9ca | 1966 | -- Special handling for nodes that may contain evaluated expressions in |
1967 | -- the form of constraints. | |
faee1e18 | 1968 | |
d087b9ca | 1969 | case Nkind (Expr) is |
1970 | when N_Index_Or_Discriminant_Constraint => | |
1971 | declare | |
1972 | Assn : Node_Id := First (Constraints (Expr)); | |
faee1e18 | 1973 | begin |
d087b9ca | 1974 | while Present (Assn) loop |
1975 | case Nkind (Assn) is | |
1976 | when N_Discriminant_Association => | |
1977 | Read_Expression (Expression (Assn)); | |
faee1e18 | 1978 | |
d087b9ca | 1979 | when others => |
1980 | Read_Expression (Assn); | |
1981 | end case; | |
faee1e18 | 1982 | |
d087b9ca | 1983 | Next (Assn); |
1984 | end loop; | |
1985 | end; | |
1986 | return; | |
1987 | ||
1988 | when N_Range_Constraint => | |
1989 | Read_Expression (Range_Expression (Expr)); | |
1990 | return; | |
faee1e18 | 1991 | |
d087b9ca | 1992 | when N_Subtype_Indication => |
1993 | if Present (Constraint (Expr)) then | |
1994 | Read_Expression (Constraint (Expr)); | |
1995 | end if; | |
1996 | return; | |
faee1e18 | 1997 | |
9ac24568 | 1998 | when N_Digits_Constraint => |
1999 | Read_Expression (Digits_Expression (Expr)); | |
2000 | if Present (Range_Constraint (Expr)) then | |
2001 | Read_Expression (Range_Constraint (Expr)); | |
2002 | end if; | |
2003 | return; | |
2004 | ||
1e5359c0 | 2005 | when N_Delta_Constraint => |
2006 | Read_Expression (Delta_Expression (Expr)); | |
2007 | if Present (Range_Constraint (Expr)) then | |
2008 | Read_Expression (Range_Constraint (Expr)); | |
2009 | end if; | |
2010 | return; | |
2011 | ||
d087b9ca | 2012 | when others => |
2013 | null; | |
2014 | end case; | |
faee1e18 | 2015 | |
d087b9ca | 2016 | -- At this point Expr can only be a subexpression |
faee1e18 | 2017 | |
d087b9ca | 2018 | case N_Subexpr'(Nkind (Expr)) is |
faee1e18 | 2019 | |
d087b9ca | 2020 | when N_Binary_Op |
d087b9ca | 2021 | | N_Short_Circuit |
2022 | => | |
2023 | Read_Expression (Left_Opnd (Expr)); | |
2024 | Read_Expression (Right_Opnd (Expr)); | |
faee1e18 | 2025 | |
9ac24568 | 2026 | when N_Membership_Test => |
2027 | Read_Expression (Left_Opnd (Expr)); | |
2028 | if Present (Right_Opnd (Expr)) then | |
2029 | Read_Expression (Right_Opnd (Expr)); | |
2030 | else | |
2031 | declare | |
2032 | Cases : constant List_Id := Alternatives (Expr); | |
2033 | Cur_Case : Node_Id := First (Cases); | |
2034 | ||
2035 | begin | |
2036 | while Present (Cur_Case) loop | |
2037 | Read_Expression (Cur_Case); | |
2038 | Next (Cur_Case); | |
2039 | end loop; | |
2040 | end; | |
2041 | end if; | |
2042 | ||
d087b9ca | 2043 | when N_Unary_Op => |
2044 | Read_Expression (Right_Opnd (Expr)); | |
faee1e18 | 2045 | |
d087b9ca | 2046 | when N_Attribute_Reference => |
2047 | declare | |
2048 | Aname : constant Name_Id := Attribute_Name (Expr); | |
2049 | Attr_Id : constant Attribute_Id := Get_Attribute_Id (Aname); | |
2050 | Pref : constant Node_Id := Prefix (Expr); | |
2051 | Args : constant List_Id := Expressions (Expr); | |
faee1e18 | 2052 | |
d087b9ca | 2053 | begin |
2054 | case Attr_Id is | |
2055 | ||
2056 | -- The following attributes take either no arguments, or | |
2057 | -- arguments that do not refer to evaluated expressions | |
2058 | -- (like Length or Loop_Entry), hence only the prefix | |
2059 | -- needs to be read. | |
2060 | ||
2061 | when Attribute_Address | |
2062 | | Attribute_Alignment | |
2063 | | Attribute_Callable | |
2064 | | Attribute_Component_Size | |
2065 | | Attribute_Constrained | |
2066 | | Attribute_First | |
2067 | | Attribute_First_Bit | |
2068 | | Attribute_Last | |
2069 | | Attribute_Last_Bit | |
2070 | | Attribute_Length | |
2071 | | Attribute_Loop_Entry | |
2072 | | Attribute_Object_Size | |
2073 | | Attribute_Position | |
2074 | | Attribute_Size | |
2075 | | Attribute_Terminated | |
2076 | | Attribute_Valid | |
2077 | | Attribute_Value_Size | |
2078 | => | |
2079 | Read_Expression (Pref); | |
2080 | ||
2081 | -- The following attributes take a type name as prefix, | |
2082 | -- hence only the arguments need to be read. | |
2083 | ||
2084 | when Attribute_Ceiling | |
2085 | | Attribute_Floor | |
2086 | | Attribute_Max | |
2087 | | Attribute_Min | |
2088 | | Attribute_Mod | |
2089 | | Attribute_Pos | |
2090 | | Attribute_Pred | |
2091 | | Attribute_Remainder | |
2092 | | Attribute_Rounding | |
2093 | | Attribute_Succ | |
2094 | | Attribute_Truncation | |
2095 | | Attribute_Val | |
2096 | | Attribute_Value | |
2097 | => | |
2098 | Read_Expression_List (Args); | |
faee1e18 | 2099 | |
d087b9ca | 2100 | -- Attributes Image and Img either take a type name as |
2101 | -- prefix with an expression in argument, or the expression | |
2102 | -- directly as prefix. Adapt to each case. | |
faee1e18 | 2103 | |
d087b9ca | 2104 | when Attribute_Image |
2105 | | Attribute_Img | |
2106 | => | |
2107 | if No (Args) then | |
2108 | Read_Expression (Pref); | |
2109 | else | |
2110 | Read_Expression_List (Args); | |
2111 | end if; | |
faee1e18 | 2112 | |
d087b9ca | 2113 | -- Attribute Update takes expressions as both prefix and |
2114 | -- arguments, so both need to be read. | |
faee1e18 | 2115 | |
d087b9ca | 2116 | when Attribute_Update => |
2117 | Read_Expression (Pref); | |
2118 | Read_Expression_List (Args); | |
faee1e18 | 2119 | |
d087b9ca | 2120 | -- Attribute Modulus does not reference the evaluated |
2121 | -- expression, so it can be ignored for this analysis. | |
24dca06d | 2122 | |
d087b9ca | 2123 | when Attribute_Modulus => |
2124 | null; | |
faee1e18 | 2125 | |
9ac24568 | 2126 | -- The following attributes apply to types; there are no |
2127 | -- expressions to read. | |
2128 | ||
2129 | when Attribute_Class | |
2130 | | Attribute_Storage_Size | |
2131 | => | |
2132 | null; | |
2133 | ||
d087b9ca | 2134 | -- Postconditions should not be analyzed |
2135 | ||
2136 | when Attribute_Old | |
2137 | | Attribute_Result | |
2138 | => | |
2139 | raise Program_Error; | |
2140 | ||
2141 | when others => | |
1e5359c0 | 2142 | null; |
d087b9ca | 2143 | end case; |
faee1e18 | 2144 | end; |
faee1e18 | 2145 | |
d087b9ca | 2146 | when N_Range => |
2147 | Read_Expression (Low_Bound (Expr)); | |
2148 | Read_Expression (High_Bound (Expr)); | |
faee1e18 | 2149 | |
d087b9ca | 2150 | when N_If_Expression => |
2151 | Read_Expression_List (Expressions (Expr)); | |
faee1e18 | 2152 | |
2153 | when N_Case_Expression => | |
2154 | declare | |
d087b9ca | 2155 | Cases : constant List_Id := Alternatives (Expr); |
2156 | Cur_Case : Node_Id := First (Cases); | |
faee1e18 | 2157 | |
2158 | begin | |
d087b9ca | 2159 | while Present (Cur_Case) loop |
2160 | Read_Expression (Expression (Cur_Case)); | |
2161 | Next (Cur_Case); | |
2162 | end loop; | |
faee1e18 | 2163 | |
d087b9ca | 2164 | Read_Expression (Expression (Expr)); |
2165 | end; | |
faee1e18 | 2166 | |
d087b9ca | 2167 | when N_Qualified_Expression |
2168 | | N_Type_Conversion | |
2169 | | N_Unchecked_Type_Conversion | |
2170 | => | |
2171 | Read_Expression (Expression (Expr)); | |
faee1e18 | 2172 | |
d087b9ca | 2173 | when N_Quantified_Expression => |
2174 | declare | |
2175 | For_In_Spec : constant Node_Id := | |
2176 | Loop_Parameter_Specification (Expr); | |
2177 | For_Of_Spec : constant Node_Id := | |
2178 | Iterator_Specification (Expr); | |
df177848 | 2179 | For_Of_Spec_Typ : Node_Id; |
2180 | ||
d087b9ca | 2181 | begin |
2182 | if Present (For_In_Spec) then | |
2183 | Read_Expression (Discrete_Subtype_Definition (For_In_Spec)); | |
2184 | else | |
2185 | Read_Expression (Name (For_Of_Spec)); | |
df177848 | 2186 | For_Of_Spec_Typ := Subtype_Indication (For_Of_Spec); |
2187 | if Present (For_Of_Spec_Typ) then | |
2188 | Read_Expression (For_Of_Spec_Typ); | |
2189 | end if; | |
d087b9ca | 2190 | end if; |
faee1e18 | 2191 | |
d087b9ca | 2192 | Read_Expression (Condition (Expr)); |
2193 | end; | |
faee1e18 | 2194 | |
d087b9ca | 2195 | when N_Character_Literal |
2196 | | N_Numeric_Or_String_Literal | |
2197 | | N_Operator_Symbol | |
2198 | | N_Raise_Expression | |
2199 | | N_Raise_xxx_Error | |
2200 | => | |
2201 | null; | |
faee1e18 | 2202 | |
d087b9ca | 2203 | when N_Delta_Aggregate |
2204 | | N_Target_Name | |
2205 | => | |
1e5359c0 | 2206 | null; |
faee1e18 | 2207 | |
d087b9ca | 2208 | -- Procedure calls are handled in Check_Node |
faee1e18 | 2209 | |
d087b9ca | 2210 | when N_Procedure_Call_Statement => |
faee1e18 | 2211 | raise Program_Error; |
2212 | ||
d087b9ca | 2213 | -- Path expressions are handled before this point |
faee1e18 | 2214 | |
a080baa9 | 2215 | when N_Aggregate |
2216 | | N_Allocator | |
d087b9ca | 2217 | | N_Expanded_Name |
2218 | | N_Explicit_Dereference | |
a080baa9 | 2219 | | N_Extension_Aggregate |
d087b9ca | 2220 | | N_Function_Call |
2221 | | N_Identifier | |
2222 | | N_Indexed_Component | |
faee1e18 | 2223 | | N_Null |
d087b9ca | 2224 | | N_Selected_Component |
2225 | | N_Slice | |
faee1e18 | 2226 | => |
d087b9ca | 2227 | raise Program_Error; |
2228 | ||
faee1e18 | 2229 | -- The following nodes are never generated in GNATprove mode |
2230 | ||
2231 | when N_Expression_With_Actions | |
2232 | | N_Reference | |
2233 | | N_Unchecked_Expression | |
2234 | => | |
2235 | raise Program_Error; | |
faee1e18 | 2236 | end case; |
2237 | end Check_Expression; | |
2238 | ||
faee1e18 | 2239 | ---------------- |
2240 | -- Check_List -- | |
2241 | ---------------- | |
2242 | ||
2243 | procedure Check_List (L : List_Id) is | |
2244 | N : Node_Id; | |
2245 | begin | |
2246 | N := First (L); | |
2247 | while Present (N) loop | |
2248 | Check_Node (N); | |
2249 | Next (N); | |
2250 | end loop; | |
2251 | end Check_List; | |
2252 | ||
2253 | -------------------------- | |
2254 | -- Check_Loop_Statement -- | |
2255 | -------------------------- | |
2256 | ||
d087b9ca | 2257 | procedure Check_Loop_Statement (Stmt : Node_Id) is |
2258 | ||
2259 | -- Local Subprograms | |
2260 | ||
2261 | procedure Check_Is_Less_Restrictive_Env | |
2262 | (Exiting_Env : Perm_Env; | |
2263 | Entry_Env : Perm_Env); | |
2264 | -- This procedure checks that the Exiting_Env environment is less | |
2265 | -- restrictive than the Entry_Env environment. | |
2266 | ||
2267 | procedure Check_Is_Less_Restrictive_Tree | |
2268 | (New_Tree : Perm_Tree_Access; | |
2269 | Orig_Tree : Perm_Tree_Access; | |
2270 | E : Entity_Id); | |
2271 | -- Auxiliary procedure to check that the tree New_Tree is less | |
2272 | -- restrictive than the tree Orig_Tree for the entity E. | |
2273 | ||
2274 | procedure Perm_Error_Loop_Exit | |
2275 | (E : Entity_Id; | |
2276 | Loop_Id : Node_Id; | |
2277 | Perm : Perm_Kind; | |
5bc0f990 | 2278 | Found_Perm : Perm_Kind; |
2279 | Expl : Node_Id); | |
d087b9ca | 2280 | -- A procedure that is called when the permissions found contradict |
2281 | -- the rules established by the RM at the exit of loops. This function | |
2282 | -- is called with the entity, the node of the enclosing loop, the | |
2283 | -- permission that was expected, and the permission found, and issues | |
2284 | -- an appropriate error message. | |
2285 | ||
2286 | ----------------------------------- | |
2287 | -- Check_Is_Less_Restrictive_Env -- | |
2288 | ----------------------------------- | |
2289 | ||
2290 | procedure Check_Is_Less_Restrictive_Env | |
2291 | (Exiting_Env : Perm_Env; | |
2292 | Entry_Env : Perm_Env) | |
2293 | is | |
2294 | Comp_Entry : Perm_Tree_Maps.Key_Option; | |
2295 | Iter_Entry, Iter_Exit : Perm_Tree_Access; | |
faee1e18 | 2296 | |
d087b9ca | 2297 | begin |
2298 | Comp_Entry := Get_First_Key (Entry_Env); | |
2299 | while Comp_Entry.Present loop | |
2300 | Iter_Entry := Get (Entry_Env, Comp_Entry.K); | |
2301 | pragma Assert (Iter_Entry /= null); | |
2302 | Iter_Exit := Get (Exiting_Env, Comp_Entry.K); | |
2303 | pragma Assert (Iter_Exit /= null); | |
2304 | Check_Is_Less_Restrictive_Tree | |
2305 | (New_Tree => Iter_Exit, | |
2306 | Orig_Tree => Iter_Entry, | |
2307 | E => Comp_Entry.K); | |
2308 | Comp_Entry := Get_Next_Key (Entry_Env); | |
2309 | end loop; | |
2310 | end Check_Is_Less_Restrictive_Env; | |
faee1e18 | 2311 | |
d087b9ca | 2312 | ------------------------------------ |
2313 | -- Check_Is_Less_Restrictive_Tree -- | |
2314 | ------------------------------------ | |
faee1e18 | 2315 | |
d087b9ca | 2316 | procedure Check_Is_Less_Restrictive_Tree |
2317 | (New_Tree : Perm_Tree_Access; | |
2318 | Orig_Tree : Perm_Tree_Access; | |
2319 | E : Entity_Id) | |
2320 | is | |
2321 | -- Local subprograms | |
2322 | ||
2323 | procedure Check_Is_Less_Restrictive_Tree_Than | |
2324 | (Tree : Perm_Tree_Access; | |
2325 | Perm : Perm_Kind; | |
2326 | E : Entity_Id); | |
2327 | -- Auxiliary procedure to check that the tree N is less restrictive | |
2328 | -- than the given permission P. | |
2329 | ||
2330 | procedure Check_Is_More_Restrictive_Tree_Than | |
2331 | (Tree : Perm_Tree_Access; | |
2332 | Perm : Perm_Kind; | |
2333 | E : Entity_Id); | |
2334 | -- Auxiliary procedure to check that the tree N is more restrictive | |
2335 | -- than the given permission P. | |
2336 | ||
2337 | ----------------------------------------- | |
2338 | -- Check_Is_Less_Restrictive_Tree_Than -- | |
2339 | ----------------------------------------- | |
2340 | ||
2341 | procedure Check_Is_Less_Restrictive_Tree_Than | |
2342 | (Tree : Perm_Tree_Access; | |
2343 | Perm : Perm_Kind; | |
2344 | E : Entity_Id) | |
2345 | is | |
2346 | begin | |
2347 | if not (Permission (Tree) >= Perm) then | |
2348 | Perm_Error_Loop_Exit | |
5bc0f990 | 2349 | (E, Stmt, Permission (Tree), Perm, Explanation (Tree)); |
d087b9ca | 2350 | end if; |
faee1e18 | 2351 | |
d087b9ca | 2352 | case Kind (Tree) is |
2353 | when Entire_Object => | |
2354 | if not (Children_Permission (Tree) >= Perm) then | |
2355 | Perm_Error_Loop_Exit | |
5bc0f990 | 2356 | (E, Stmt, Children_Permission (Tree), Perm, |
2357 | Explanation (Tree)); | |
faee1e18 | 2358 | |
d087b9ca | 2359 | end if; |
faee1e18 | 2360 | |
d087b9ca | 2361 | when Reference => |
2362 | Check_Is_Less_Restrictive_Tree_Than | |
2363 | (Get_All (Tree), Perm, E); | |
faee1e18 | 2364 | |
d087b9ca | 2365 | when Array_Component => |
2366 | Check_Is_Less_Restrictive_Tree_Than | |
2367 | (Get_Elem (Tree), Perm, E); | |
2368 | ||
2369 | when Record_Component => | |
2370 | declare | |
2371 | Comp : Perm_Tree_Access; | |
2372 | begin | |
2373 | Comp := Perm_Tree_Maps.Get_First (Component (Tree)); | |
2374 | while Comp /= null loop | |
2375 | Check_Is_Less_Restrictive_Tree_Than (Comp, Perm, E); | |
2376 | Comp := | |
2377 | Perm_Tree_Maps.Get_Next (Component (Tree)); | |
2378 | end loop; | |
2379 | end; | |
2380 | end case; | |
2381 | end Check_Is_Less_Restrictive_Tree_Than; | |
2382 | ||
2383 | ----------------------------------------- | |
2384 | -- Check_Is_More_Restrictive_Tree_Than -- | |
2385 | ----------------------------------------- | |
2386 | ||
2387 | procedure Check_Is_More_Restrictive_Tree_Than | |
2388 | (Tree : Perm_Tree_Access; | |
2389 | Perm : Perm_Kind; | |
2390 | E : Entity_Id) | |
2391 | is | |
2392 | begin | |
2393 | if not (Perm >= Permission (Tree)) then | |
2394 | Perm_Error_Loop_Exit | |
5bc0f990 | 2395 | (E, Stmt, Permission (Tree), Perm, Explanation (Tree)); |
d087b9ca | 2396 | end if; |
2397 | ||
2398 | case Kind (Tree) is | |
2399 | when Entire_Object => | |
2400 | if not (Perm >= Children_Permission (Tree)) then | |
2401 | Perm_Error_Loop_Exit | |
5bc0f990 | 2402 | (E, Stmt, Children_Permission (Tree), Perm, |
2403 | Explanation (Tree)); | |
d087b9ca | 2404 | end if; |
2405 | ||
2406 | when Reference => | |
2407 | Check_Is_More_Restrictive_Tree_Than | |
2408 | (Get_All (Tree), Perm, E); | |
2409 | ||
2410 | when Array_Component => | |
2411 | Check_Is_More_Restrictive_Tree_Than | |
2412 | (Get_Elem (Tree), Perm, E); | |
2413 | ||
2414 | when Record_Component => | |
2415 | declare | |
2416 | Comp : Perm_Tree_Access; | |
2417 | begin | |
2418 | Comp := Perm_Tree_Maps.Get_First (Component (Tree)); | |
2419 | while Comp /= null loop | |
2420 | Check_Is_More_Restrictive_Tree_Than (Comp, Perm, E); | |
2421 | Comp := | |
2422 | Perm_Tree_Maps.Get_Next (Component (Tree)); | |
2423 | end loop; | |
2424 | end; | |
2425 | end case; | |
2426 | end Check_Is_More_Restrictive_Tree_Than; | |
2427 | ||
2428 | -- Start of processing for Check_Is_Less_Restrictive_Tree | |
2429 | ||
2430 | begin | |
2431 | if not (Permission (New_Tree) <= Permission (Orig_Tree)) then | |
2432 | Perm_Error_Loop_Exit | |
2433 | (E => E, | |
2434 | Loop_Id => Stmt, | |
2435 | Perm => Permission (New_Tree), | |
5bc0f990 | 2436 | Found_Perm => Permission (Orig_Tree), |
2437 | Expl => Explanation (New_Tree)); | |
d087b9ca | 2438 | end if; |
2439 | ||
2440 | case Kind (New_Tree) is | |
2441 | ||
2442 | -- Potentially folded tree. We check the other tree Orig_Tree to | |
2443 | -- check whether it is folded or not. If folded we just compare | |
2444 | -- their Permission and Children_Permission, if not, then we | |
2445 | -- look at the Children_Permission of the folded tree against | |
2446 | -- the unfolded tree Orig_Tree. | |
2447 | ||
2448 | when Entire_Object => | |
2449 | case Kind (Orig_Tree) is | |
2450 | when Entire_Object => | |
2451 | if not (Children_Permission (New_Tree) <= | |
2452 | Children_Permission (Orig_Tree)) | |
2453 | then | |
2454 | Perm_Error_Loop_Exit | |
2455 | (E, Stmt, | |
2456 | Children_Permission (New_Tree), | |
5bc0f990 | 2457 | Children_Permission (Orig_Tree), |
2458 | Explanation (New_Tree)); | |
d087b9ca | 2459 | end if; |
2460 | ||
2461 | when Reference => | |
2462 | Check_Is_More_Restrictive_Tree_Than | |
2463 | (Get_All (Orig_Tree), Children_Permission (New_Tree), E); | |
2464 | ||
2465 | when Array_Component => | |
2466 | Check_Is_More_Restrictive_Tree_Than | |
2467 | (Get_Elem (Orig_Tree), Children_Permission (New_Tree), E); | |
2468 | ||
2469 | when Record_Component => | |
2470 | declare | |
2471 | Comp : Perm_Tree_Access; | |
2472 | begin | |
2473 | Comp := Perm_Tree_Maps.Get_First | |
2474 | (Component (Orig_Tree)); | |
2475 | while Comp /= null loop | |
2476 | Check_Is_More_Restrictive_Tree_Than | |
2477 | (Comp, Children_Permission (New_Tree), E); | |
2478 | Comp := Perm_Tree_Maps.Get_Next | |
2479 | (Component (Orig_Tree)); | |
2480 | end loop; | |
2481 | end; | |
2482 | end case; | |
2483 | ||
2484 | when Reference => | |
2485 | case Kind (Orig_Tree) is | |
2486 | when Entire_Object => | |
2487 | Check_Is_Less_Restrictive_Tree_Than | |
2488 | (Get_All (New_Tree), Children_Permission (Orig_Tree), E); | |
2489 | ||
2490 | when Reference => | |
2491 | Check_Is_Less_Restrictive_Tree | |
2492 | (Get_All (New_Tree), Get_All (Orig_Tree), E); | |
2493 | ||
2494 | when others => | |
2495 | raise Program_Error; | |
2496 | end case; | |
2497 | ||
2498 | when Array_Component => | |
2499 | case Kind (Orig_Tree) is | |
2500 | when Entire_Object => | |
2501 | Check_Is_Less_Restrictive_Tree_Than | |
2502 | (Get_Elem (New_Tree), Children_Permission (Orig_Tree), E); | |
2503 | ||
2504 | when Array_Component => | |
2505 | Check_Is_Less_Restrictive_Tree | |
2506 | (Get_Elem (New_Tree), Get_Elem (Orig_Tree), E); | |
2507 | ||
2508 | when others => | |
2509 | raise Program_Error; | |
2510 | end case; | |
2511 | ||
2512 | when Record_Component => | |
2513 | declare | |
2514 | CompN : Perm_Tree_Access; | |
2515 | begin | |
2516 | CompN := | |
2517 | Perm_Tree_Maps.Get_First (Component (New_Tree)); | |
2518 | case Kind (Orig_Tree) is | |
2519 | when Entire_Object => | |
2520 | while CompN /= null loop | |
2521 | Check_Is_Less_Restrictive_Tree_Than | |
2522 | (CompN, Children_Permission (Orig_Tree), E); | |
2523 | ||
2524 | CompN := | |
2525 | Perm_Tree_Maps.Get_Next (Component (New_Tree)); | |
2526 | end loop; | |
2527 | ||
2528 | when Record_Component => | |
2529 | declare | |
2530 | ||
2531 | KeyO : Perm_Tree_Maps.Key_Option; | |
2532 | CompO : Perm_Tree_Access; | |
2533 | begin | |
2534 | KeyO := Perm_Tree_Maps.Get_First_Key | |
2535 | (Component (Orig_Tree)); | |
2536 | while KeyO.Present loop | |
1e5359c0 | 2537 | CompN := Perm_Tree_Maps.Get |
2538 | (Component (New_Tree), KeyO.K); | |
2539 | CompO := Perm_Tree_Maps.Get | |
2540 | (Component (Orig_Tree), KeyO.K); | |
d087b9ca | 2541 | pragma Assert (CompO /= null); |
2542 | ||
2543 | Check_Is_Less_Restrictive_Tree (CompN, CompO, E); | |
2544 | ||
2545 | KeyO := Perm_Tree_Maps.Get_Next_Key | |
2546 | (Component (Orig_Tree)); | |
d087b9ca | 2547 | end loop; |
2548 | end; | |
2549 | ||
2550 | when others => | |
2551 | raise Program_Error; | |
2552 | end case; | |
2553 | end; | |
2554 | end case; | |
2555 | end Check_Is_Less_Restrictive_Tree; | |
2556 | ||
2557 | -------------------------- | |
2558 | -- Perm_Error_Loop_Exit -- | |
2559 | -------------------------- | |
2560 | ||
2561 | procedure Perm_Error_Loop_Exit | |
2562 | (E : Entity_Id; | |
2563 | Loop_Id : Node_Id; | |
2564 | Perm : Perm_Kind; | |
5bc0f990 | 2565 | Found_Perm : Perm_Kind; |
2566 | Expl : Node_Id) | |
d087b9ca | 2567 | is |
2568 | begin | |
1e5359c0 | 2569 | if Emit_Messages then |
2570 | Error_Msg_Node_2 := Loop_Id; | |
2571 | Error_Msg_N | |
2572 | ("insufficient permission for & when exiting loop &", E); | |
2573 | Perm_Mismatch (Exp_Perm => Perm, | |
2574 | Act_Perm => Found_Perm, | |
2575 | N => Loop_Id, | |
2576 | Expl => Expl); | |
2577 | end if; | |
d087b9ca | 2578 | end Perm_Error_Loop_Exit; |
2579 | ||
2580 | -- Local variables | |
2581 | ||
2582 | Loop_Name : constant Entity_Id := Entity (Identifier (Stmt)); | |
2583 | Loop_Env : constant Perm_Env_Access := new Perm_Env; | |
2584 | Scheme : constant Node_Id := Iteration_Scheme (Stmt); | |
2585 | ||
2586 | -- Start of processing for Check_Loop_Statement | |
2587 | ||
2588 | begin | |
2589 | -- Save environment prior to the loop | |
2590 | ||
2591 | Copy_Env (From => Current_Perm_Env, To => Loop_Env.all); | |
2592 | ||
2593 | -- Add saved environment to loop environment | |
2594 | ||
2595 | Set (Current_Loops_Envs, Loop_Name, Loop_Env); | |
2596 | ||
2597 | -- If the loop is not a plain-loop, then it may either never be entered, | |
2598 | -- or it may be exited after a number of iterations. Hence add the | |
faee1e18 | 2599 | -- current permission environment as the initial loop exit environment. |
2600 | -- Otherwise, the loop exit environment remains empty until it is | |
2601 | -- populated by analyzing exit statements. | |
2602 | ||
d087b9ca | 2603 | if Present (Iteration_Scheme (Stmt)) then |
faee1e18 | 2604 | declare |
d80ef1ad | 2605 | Exit_Env : constant Perm_Env_Access := new Perm_Env; |
24dca06d | 2606 | |
faee1e18 | 2607 | begin |
2608 | Copy_Env (From => Current_Perm_Env, To => Exit_Env.all); | |
2609 | Set (Current_Loops_Accumulators, Loop_Name, Exit_Env); | |
2610 | end; | |
2611 | end if; | |
2612 | ||
2613 | -- Analyze loop | |
2614 | ||
d087b9ca | 2615 | if Present (Scheme) then |
2616 | ||
2617 | -- Case of a WHILE loop | |
2618 | ||
2619 | if Present (Condition (Scheme)) then | |
2620 | Check_Expression (Condition (Scheme), Read); | |
2621 | ||
2622 | -- Case of a FOR loop | |
2623 | ||
2624 | else | |
2625 | declare | |
2626 | Param_Spec : constant Node_Id := | |
2627 | Loop_Parameter_Specification (Scheme); | |
2628 | Iter_Spec : constant Node_Id := Iterator_Specification (Scheme); | |
2629 | begin | |
2630 | if Present (Param_Spec) then | |
2631 | Check_Expression | |
2632 | (Discrete_Subtype_Definition (Param_Spec), Read); | |
2633 | else | |
2634 | Check_Expression (Name (Iter_Spec), Read); | |
2635 | if Present (Subtype_Indication (Iter_Spec)) then | |
2636 | Check_Expression (Subtype_Indication (Iter_Spec), Read); | |
2637 | end if; | |
2638 | end if; | |
2639 | end; | |
2640 | end if; | |
2641 | end if; | |
2642 | ||
2643 | Check_List (Statements (Stmt)); | |
2644 | ||
2645 | -- Check that environment gets less restrictive at end of loop | |
2646 | ||
2647 | Check_Is_Less_Restrictive_Env | |
2648 | (Exiting_Env => Current_Perm_Env, | |
2649 | Entry_Env => Loop_Env.all); | |
faee1e18 | 2650 | |
faee1e18 | 2651 | -- Set environment to the one for exiting the loop |
2652 | ||
2653 | declare | |
2654 | Exit_Env : constant Perm_Env_Access := | |
2655 | Get (Current_Loops_Accumulators, Loop_Name); | |
2656 | begin | |
2657 | Free_Env (Current_Perm_Env); | |
2658 | ||
2659 | -- In the normal case, Exit_Env is not null and we use it. In the | |
2660 | -- degraded case of a plain-loop without exit statements, Exit_Env is | |
2661 | -- null, and we use the initial permission environment at the start | |
2662 | -- of the loop to continue analysis. Any environment would be fine | |
2663 | -- here, since the code after the loop is dead code, but this way we | |
2664 | -- avoid spurious errors by having at least variables in scope inside | |
2665 | -- the environment. | |
2666 | ||
2667 | if Exit_Env /= null then | |
2668 | Copy_Env (From => Exit_Env.all, To => Current_Perm_Env); | |
382c4351 | 2669 | Free_Env (Loop_Env.all); |
2670 | Free_Env (Exit_Env.all); | |
faee1e18 | 2671 | else |
2672 | Copy_Env (From => Loop_Env.all, To => Current_Perm_Env); | |
382c4351 | 2673 | Free_Env (Loop_Env.all); |
faee1e18 | 2674 | end if; |
faee1e18 | 2675 | end; |
2676 | end Check_Loop_Statement; | |
2677 | ||
2678 | ---------------- | |
2679 | -- Check_Node -- | |
2680 | ---------------- | |
2681 | ||
2682 | procedure Check_Node (N : Node_Id) is | |
7d2da10f | 2683 | |
2684 | procedure Check_Subprogram_Contract (N : Node_Id); | |
2685 | -- Check the postcondition-like contracts for use of 'Old | |
2686 | ||
2687 | ------------------------------- | |
2688 | -- Check_Subprogram_Contract -- | |
2689 | ------------------------------- | |
2690 | ||
2691 | procedure Check_Subprogram_Contract (N : Node_Id) is | |
2692 | begin | |
2693 | if Nkind (N) = N_Subprogram_Declaration | |
2694 | or else Acts_As_Spec (N) | |
2695 | then | |
2696 | declare | |
2697 | E : constant Entity_Id := Unique_Defining_Entity (N); | |
2698 | Post : constant Node_Id := | |
2699 | Get_Pragma (E, Pragma_Postcondition); | |
2700 | Cases : constant Node_Id := | |
2701 | Get_Pragma (E, Pragma_Contract_Cases); | |
2702 | begin | |
2703 | Check_Old_Loop_Entry (Post); | |
2704 | Check_Old_Loop_Entry (Cases); | |
2705 | end; | |
2706 | ||
2707 | elsif Nkind (N) = N_Subprogram_Body then | |
2708 | declare | |
2709 | E : constant Entity_Id := Defining_Entity (N); | |
2710 | Ref_Post : constant Node_Id := | |
2711 | Get_Pragma (E, Pragma_Refined_Post); | |
2712 | begin | |
2713 | Check_Old_Loop_Entry (Ref_Post); | |
2714 | end; | |
2715 | end if; | |
2716 | end Check_Subprogram_Contract; | |
2717 | ||
2718 | -- Start of processing for Check_Node | |
2719 | ||
faee1e18 | 2720 | begin |
2721 | case Nkind (N) is | |
2722 | when N_Declaration => | |
2723 | Check_Declaration (N); | |
2724 | ||
faee1e18 | 2725 | when N_Body_Stub => |
2726 | Check_Node (Get_Body_From_Stub (N)); | |
2727 | ||
2728 | when N_Statement_Other_Than_Procedure_Call => | |
2729 | Check_Statement (N); | |
2730 | ||
d087b9ca | 2731 | when N_Procedure_Call_Statement => |
2732 | Check_Call_Statement (N); | |
2733 | ||
faee1e18 | 2734 | when N_Package_Body => |
9ac24568 | 2735 | if not Is_Generic_Unit (Unique_Defining_Entity (N)) then |
2736 | Check_Package_Body (N); | |
2737 | end if; | |
faee1e18 | 2738 | |
7d2da10f | 2739 | when N_Subprogram_Body => |
9ac24568 | 2740 | if not Is_Generic_Unit (Unique_Defining_Entity (N)) then |
7d2da10f | 2741 | Check_Subprogram_Contract (N); |
9ac24568 | 2742 | Check_Callable_Body (N); |
2743 | end if; | |
faee1e18 | 2744 | |
7d2da10f | 2745 | when N_Entry_Body |
2746 | | N_Task_Body | |
2747 | => | |
2748 | Check_Callable_Body (N); | |
2749 | ||
faee1e18 | 2750 | when N_Protected_Body => |
2751 | Check_List (Declarations (N)); | |
2752 | ||
2753 | when N_Package_Declaration => | |
d087b9ca | 2754 | Check_Package_Spec (N); |
faee1e18 | 2755 | |
2756 | when N_Handled_Sequence_Of_Statements => | |
2757 | Check_List (Statements (N)); | |
2758 | ||
df177848 | 2759 | when N_Pragma => |
2760 | Check_Pragma (N); | |
2761 | ||
7d2da10f | 2762 | when N_Subprogram_Declaration => |
2763 | Check_Subprogram_Contract (N); | |
2764 | ||
7bd7174a | 2765 | -- Attribute references in statement position are not supported in |
2766 | -- SPARK and will be rejected by GNATprove. | |
2767 | ||
2768 | when N_Attribute_Reference => | |
2769 | null; | |
2770 | ||
faee1e18 | 2771 | -- Ignored constructs for pointer checking |
2772 | ||
2773 | when N_Abstract_Subprogram_Declaration | |
2774 | | N_At_Clause | |
2775 | | N_Attribute_Definition_Clause | |
fe48a434 | 2776 | | N_Call_Marker |
faee1e18 | 2777 | | N_Delta_Constraint |
2778 | | N_Digits_Constraint | |
2779 | | N_Empty | |
2780 | | N_Enumeration_Representation_Clause | |
2781 | | N_Exception_Declaration | |
2782 | | N_Exception_Renaming_Declaration | |
2783 | | N_Formal_Package_Declaration | |
2784 | | N_Formal_Subprogram_Declaration | |
2785 | | N_Freeze_Entity | |
2786 | | N_Freeze_Generic_Entity | |
2787 | | N_Function_Instantiation | |
2788 | | N_Generic_Function_Renaming_Declaration | |
2789 | | N_Generic_Package_Declaration | |
2790 | | N_Generic_Package_Renaming_Declaration | |
2791 | | N_Generic_Procedure_Renaming_Declaration | |
2792 | | N_Generic_Subprogram_Declaration | |
2793 | | N_Implicit_Label_Declaration | |
2794 | | N_Itype_Reference | |
2795 | | N_Label | |
2796 | | N_Number_Declaration | |
2797 | | N_Object_Renaming_Declaration | |
2798 | | N_Others_Choice | |
2799 | | N_Package_Instantiation | |
2800 | | N_Package_Renaming_Declaration | |
faee1e18 | 2801 | | N_Procedure_Instantiation |
1e5359c0 | 2802 | | N_Raise_xxx_Error |
faee1e18 | 2803 | | N_Record_Representation_Clause |
faee1e18 | 2804 | | N_Subprogram_Renaming_Declaration |
2805 | | N_Task_Type_Declaration | |
2806 | | N_Use_Package_Clause | |
2807 | | N_With_Clause | |
2808 | | N_Use_Type_Clause | |
2809 | | N_Validate_Unchecked_Conversion | |
29484d70 | 2810 | | N_Variable_Reference_Marker |
24dca06d | 2811 | | N_Discriminant_Association |
2812 | ||
d087b9ca | 2813 | -- ??? check whether we should do something special for |
2814 | -- N_Discriminant_Association, or maybe raise Program_Error. | |
5abc6fac | 2815 | => |
faee1e18 | 2816 | null; |
faee1e18 | 2817 | |
d087b9ca | 2818 | -- Checking should not be called directly on these nodes |
2819 | ||
2820 | when others => | |
faee1e18 | 2821 | raise Program_Error; |
2822 | end case; | |
faee1e18 | 2823 | end Check_Node; |
2824 | ||
7d2da10f | 2825 | -------------------------- |
2826 | -- Check_Old_Loop_Entry -- | |
2827 | -------------------------- | |
2828 | ||
2829 | procedure Check_Old_Loop_Entry (N : Node_Id) is | |
2830 | ||
2831 | function Check_Attribute (N : Node_Id) return Traverse_Result; | |
2832 | ||
2833 | --------------------- | |
2834 | -- Check_Attribute -- | |
2835 | --------------------- | |
2836 | ||
2837 | function Check_Attribute (N : Node_Id) return Traverse_Result is | |
2838 | Attr_Id : Attribute_Id; | |
2839 | Aname : Name_Id; | |
2840 | Pref : Node_Id; | |
2841 | ||
2842 | begin | |
2843 | if Nkind (N) = N_Attribute_Reference then | |
2844 | Attr_Id := Get_Attribute_Id (Attribute_Name (N)); | |
2845 | Aname := Attribute_Name (N); | |
2846 | ||
2847 | if Attr_Id = Attribute_Old | |
2848 | or else Attr_Id = Attribute_Loop_Entry | |
2849 | then | |
2850 | Pref := Prefix (N); | |
2851 | ||
2852 | if Is_Deep (Etype (Pref)) then | |
2853 | if Nkind (Pref) /= N_Function_Call then | |
2854 | if Emit_Messages then | |
2855 | Error_Msg_Name_1 := Aname; | |
2856 | Error_Msg_N | |
2857 | ("prefix of % attribute must be a function call " | |
2858 | & "(SPARK RM 3.10(14))", Pref); | |
2859 | end if; | |
2860 | ||
2861 | elsif Is_Traversal_Function_Call (Pref) then | |
2862 | if Emit_Messages then | |
2863 | Error_Msg_Name_1 := Aname; | |
2864 | Error_Msg_N | |
2865 | ("prefix of % attribute should not call a traversal " | |
2866 | & "function (SPARK RM 3.10(14))", Pref); | |
2867 | end if; | |
2868 | end if; | |
2869 | end if; | |
2870 | end if; | |
2871 | end if; | |
2872 | ||
2873 | return OK; | |
2874 | end Check_Attribute; | |
2875 | ||
2876 | procedure Check_All is new Traverse_Proc (Check_Attribute); | |
2877 | ||
2878 | -- Start of processing for Check_Old_Loop_Entry | |
2879 | ||
2880 | begin | |
2881 | Check_All (N); | |
2882 | end Check_Old_Loop_Entry; | |
2883 | ||
faee1e18 | 2884 | ------------------------ |
2885 | -- Check_Package_Body -- | |
2886 | ------------------------ | |
2887 | ||
2888 | procedure Check_Package_Body (Pack : Node_Id) is | |
d087b9ca | 2889 | Save_In_Elab : constant Boolean := Inside_Elaboration; |
2890 | Spec : constant Node_Id := | |
2891 | Package_Specification (Corresponding_Spec (Pack)); | |
7754aad4 | 2892 | Id : constant Entity_Id := Defining_Entity (Pack); |
2893 | Prag : constant Node_Id := SPARK_Pragma (Id); | |
2894 | Aux_Prag : constant Node_Id := SPARK_Aux_Pragma (Id); | |
d087b9ca | 2895 | Saved_Env : Perm_Env; |
faee1e18 | 2896 | |
2897 | begin | |
7754aad4 | 2898 | if Present (Prag) |
2899 | and then Get_SPARK_Mode_From_Annotation (Prag) = Opt.On | |
d087b9ca | 2900 | then |
7754aad4 | 2901 | Inside_Elaboration := True; |
faee1e18 | 2902 | |
7754aad4 | 2903 | -- Save environment and put a new one in place |
faee1e18 | 2904 | |
7754aad4 | 2905 | Move_Env (Current_Perm_Env, Saved_Env); |
d087b9ca | 2906 | |
7754aad4 | 2907 | -- Reanalyze package spec to have its variables in the environment |
faee1e18 | 2908 | |
7754aad4 | 2909 | Check_List (Visible_Declarations (Spec)); |
2910 | Check_List (Private_Declarations (Spec)); | |
faee1e18 | 2911 | |
7754aad4 | 2912 | -- Check declarations and statements in the special mode for |
2913 | -- elaboration. | |
faee1e18 | 2914 | |
7754aad4 | 2915 | Check_List (Declarations (Pack)); |
faee1e18 | 2916 | |
7754aad4 | 2917 | if Present (Aux_Prag) |
2918 | and then Get_SPARK_Mode_From_Annotation (Aux_Prag) = Opt.On | |
2919 | then | |
2920 | Check_Node (Handled_Statement_Sequence (Pack)); | |
2921 | end if; | |
faee1e18 | 2922 | |
7754aad4 | 2923 | -- Restore the saved environment and free the current one |
faee1e18 | 2924 | |
7754aad4 | 2925 | Move_Env (Saved_Env, Current_Perm_Env); |
faee1e18 | 2926 | |
7754aad4 | 2927 | Inside_Elaboration := Save_In_Elab; |
2928 | end if; | |
faee1e18 | 2929 | end Check_Package_Body; |
2930 | ||
d087b9ca | 2931 | ------------------------ |
2932 | -- Check_Package_Spec -- | |
2933 | ------------------------ | |
faee1e18 | 2934 | |
d087b9ca | 2935 | procedure Check_Package_Spec (Pack : Node_Id) is |
2936 | Save_In_Elab : constant Boolean := Inside_Elaboration; | |
2937 | Spec : constant Node_Id := Specification (Pack); | |
7754aad4 | 2938 | Id : constant Entity_Id := Defining_Entity (Pack); |
2939 | Prag : constant Node_Id := SPARK_Pragma (Id); | |
2940 | Aux_Prag : constant Node_Id := SPARK_Aux_Pragma (Id); | |
d087b9ca | 2941 | Saved_Env : Perm_Env; |
faee1e18 | 2942 | |
d087b9ca | 2943 | begin |
7754aad4 | 2944 | if Present (Prag) |
2945 | and then Get_SPARK_Mode_From_Annotation (Prag) = Opt.On | |
2946 | then | |
2947 | Inside_Elaboration := True; | |
faee1e18 | 2948 | |
7754aad4 | 2949 | -- Save environment and put a new one in place |
faee1e18 | 2950 | |
7754aad4 | 2951 | Move_Env (Current_Perm_Env, Saved_Env); |
faee1e18 | 2952 | |
7754aad4 | 2953 | -- Check declarations in the special mode for elaboration |
faee1e18 | 2954 | |
7754aad4 | 2955 | Check_List (Visible_Declarations (Spec)); |
faee1e18 | 2956 | |
7754aad4 | 2957 | if Present (Aux_Prag) |
2958 | and then Get_SPARK_Mode_From_Annotation (Aux_Prag) = Opt.On | |
2959 | then | |
2960 | Check_List (Private_Declarations (Spec)); | |
2961 | end if; | |
faee1e18 | 2962 | |
94654315 | 2963 | -- Restore the saved environment and free the current one. As part of |
2964 | -- the restoration, the environment of the package spec is merged in | |
2965 | -- the enclosing environment, which may be an enclosing | |
2966 | -- package/subprogram spec or body which has access to the variables | |
2967 | -- of the package spec. | |
faee1e18 | 2968 | |
94654315 | 2969 | Merge_Env (Saved_Env, Current_Perm_Env); |
7754aad4 | 2970 | |
2971 | Inside_Elaboration := Save_In_Elab; | |
2972 | end if; | |
d087b9ca | 2973 | end Check_Package_Spec; |
faee1e18 | 2974 | |
d087b9ca | 2975 | ------------------------------- |
2976 | -- Check_Parameter_Or_Global -- | |
2977 | ------------------------------- | |
faee1e18 | 2978 | |
d087b9ca | 2979 | procedure Check_Parameter_Or_Global |
2980 | (Expr : Node_Id; | |
df177848 | 2981 | Typ : Entity_Id; |
2982 | Kind : Formal_Kind; | |
d087b9ca | 2983 | Subp : Entity_Id; |
2984 | Global_Var : Boolean) | |
2985 | is | |
df177848 | 2986 | Mode : Checking_Mode; |
2987 | Status : Error_Status; | |
faee1e18 | 2988 | |
2989 | begin | |
df177848 | 2990 | if not Global_Var |
2991 | and then Is_Anonymous_Access_Type (Typ) | |
2992 | then | |
2993 | Check_Source_Of_Borrow_Or_Observe (Expr, Status); | |
2994 | ||
2995 | if Status /= OK then | |
2996 | return; | |
2997 | end if; | |
2998 | end if; | |
2999 | ||
3000 | case Kind is | |
d087b9ca | 3001 | when E_In_Parameter => |
faee1e18 | 3002 | |
d087b9ca | 3003 | -- Inputs of functions have R permission only |
faee1e18 | 3004 | |
d087b9ca | 3005 | if Ekind (Subp) = E_Function then |
3006 | Mode := Read; | |
faee1e18 | 3007 | |
d087b9ca | 3008 | -- Input global variables have R permission only |
faee1e18 | 3009 | |
d087b9ca | 3010 | elsif Global_Var then |
3011 | Mode := Read; | |
faee1e18 | 3012 | |
d087b9ca | 3013 | -- Anonymous access to constant is an observe |
faee1e18 | 3014 | |
d087b9ca | 3015 | elsif Is_Anonymous_Access_Type (Typ) |
3016 | and then Is_Access_Constant (Typ) | |
3017 | then | |
3018 | Mode := Observe; | |
faee1e18 | 3019 | |
d087b9ca | 3020 | -- Other access types are a borrow |
faee1e18 | 3021 | |
d087b9ca | 3022 | elsif Is_Access_Type (Typ) then |
3023 | Mode := Borrow; | |
faee1e18 | 3024 | |
d087b9ca | 3025 | -- Deep types other than access types define an observe |
24dca06d | 3026 | |
d087b9ca | 3027 | elsif Is_Deep (Typ) then |
3028 | Mode := Observe; | |
24dca06d | 3029 | |
d087b9ca | 3030 | -- Otherwise the variable is read |
24dca06d | 3031 | |
d087b9ca | 3032 | else |
3033 | Mode := Read; | |
3034 | end if; | |
24dca06d | 3035 | |
d087b9ca | 3036 | when E_Out_Parameter => |
3037 | Mode := Assign; | |
24dca06d | 3038 | |
d087b9ca | 3039 | when E_In_Out_Parameter => |
3040 | Mode := Move; | |
3041 | end case; | |
faee1e18 | 3042 | |
a42b8e6a | 3043 | if Mode = Assign then |
3044 | Check_Expression (Expr, Read_Subexpr); | |
3045 | end if; | |
3046 | ||
d087b9ca | 3047 | Check_Expression (Expr, Mode); |
3048 | end Check_Parameter_Or_Global; | |
24dca06d | 3049 | |
d087b9ca | 3050 | procedure Check_Globals_Inst is |
3051 | new Handle_Globals (Check_Parameter_Or_Global); | |
24dca06d | 3052 | |
d087b9ca | 3053 | procedure Check_Globals (Subp : Entity_Id) renames Check_Globals_Inst; |
faee1e18 | 3054 | |
df177848 | 3055 | ------------------ |
3056 | -- Check_Pragma -- | |
3057 | ------------------ | |
3058 | ||
3059 | procedure Check_Pragma (Prag : Node_Id) is | |
3060 | Prag_Id : constant Pragma_Id := Get_Pragma_Id (Prag); | |
3061 | Arg1 : constant Node_Id := | |
3062 | First (Pragma_Argument_Associations (Prag)); | |
1e5359c0 | 3063 | Arg2 : Node_Id := Empty; |
df177848 | 3064 | |
3065 | begin | |
3066 | if Present (Arg1) then | |
3067 | Arg2 := Next (Arg1); | |
3068 | end if; | |
3069 | ||
3070 | case Prag_Id is | |
3071 | when Pragma_Check => | |
3072 | declare | |
3073 | Expr : constant Node_Id := Expression (Arg2); | |
3074 | begin | |
3075 | Check_Expression (Expr, Read); | |
7d2da10f | 3076 | |
3077 | -- Prefix of Loop_Entry should be checked inside any assertion | |
3078 | -- where it may appear. | |
3079 | ||
3080 | Check_Old_Loop_Entry (Expr); | |
df177848 | 3081 | end; |
3082 | ||
7d2da10f | 3083 | -- Prefix of Loop_Entry should be checked inside a Loop_Variant |
3084 | ||
3085 | when Pragma_Loop_Variant => | |
3086 | Check_Old_Loop_Entry (Prag); | |
3087 | ||
df177848 | 3088 | -- There is no need to check contracts, as these can only access |
3089 | -- inputs and outputs of the subprogram. Inputs are checked | |
3090 | -- independently for R permission. Outputs are checked | |
3091 | -- independently to have RW permission on exit. | |
3092 | ||
583f62e9 | 3093 | -- Postconditions are checked for correct use of 'Old, but starting |
3094 | -- from the corresponding declaration, in order to avoid dealing with | |
3095 | -- with contracts on generic subprograms, which are not handled in | |
3096 | -- GNATprove. | |
3097 | ||
3098 | when Pragma_Precondition | |
df177848 | 3099 | | Pragma_Postcondition |
583f62e9 | 3100 | | Pragma_Contract_Cases |
df177848 | 3101 | | Pragma_Refined_Post |
3102 | => | |
3103 | null; | |
3104 | ||
3105 | -- The same holds for the initial condition after package | |
3106 | -- elaboration, for the different reason that library-level | |
3107 | -- variables can only be left in RW state after elaboration. | |
3108 | ||
3109 | when Pragma_Initial_Condition => | |
3110 | null; | |
3111 | ||
3112 | -- These pragmas should have been rewritten and/or removed in | |
3113 | -- GNATprove mode. | |
3114 | ||
3115 | when Pragma_Assert | |
3116 | | Pragma_Assert_And_Cut | |
3117 | | Pragma_Assume | |
3118 | | Pragma_Compile_Time_Error | |
3119 | | Pragma_Compile_Time_Warning | |
3120 | | Pragma_Debug | |
3121 | | Pragma_Loop_Invariant | |
3122 | => | |
3123 | raise Program_Error; | |
3124 | ||
3125 | when others => | |
3126 | null; | |
3127 | end case; | |
3128 | end Check_Pragma; | |
3129 | ||
faee1e18 | 3130 | ------------------------- |
3131 | -- Check_Safe_Pointers -- | |
3132 | ------------------------- | |
3133 | ||
3134 | procedure Check_Safe_Pointers (N : Node_Id) is | |
3135 | ||
3136 | -- Local subprograms | |
3137 | ||
3138 | procedure Check_List (L : List_Id); | |
3139 | -- Call the main analysis procedure on each element of the list | |
3140 | ||
3141 | procedure Initialize; | |
3142 | -- Initialize global variables before starting the analysis of a body | |
3143 | ||
3144 | ---------------- | |
3145 | -- Check_List -- | |
3146 | ---------------- | |
3147 | ||
3148 | procedure Check_List (L : List_Id) is | |
3149 | N : Node_Id; | |
3150 | begin | |
3151 | N := First (L); | |
3152 | while Present (N) loop | |
3153 | Check_Safe_Pointers (N); | |
3154 | Next (N); | |
3155 | end loop; | |
3156 | end Check_List; | |
3157 | ||
3158 | ---------------- | |
3159 | -- Initialize -- | |
3160 | ---------------- | |
3161 | ||
3162 | procedure Initialize is | |
3163 | begin | |
3164 | Reset (Current_Loops_Envs); | |
3165 | Reset (Current_Loops_Accumulators); | |
3166 | Reset (Current_Perm_Env); | |
faee1e18 | 3167 | end Initialize; |
3168 | ||
faee1e18 | 3169 | -- Start of processing for Check_Safe_Pointers |
3170 | ||
3171 | begin | |
3172 | Initialize; | |
d087b9ca | 3173 | |
faee1e18 | 3174 | case Nkind (N) is |
3175 | when N_Compilation_Unit => | |
3176 | Check_Safe_Pointers (Unit (N)); | |
3177 | ||
3178 | when N_Package_Body | |
3179 | | N_Package_Declaration | |
7d2da10f | 3180 | | N_Subprogram_Declaration |
faee1e18 | 3181 | | N_Subprogram_Body |
3182 | => | |
a42b8e6a | 3183 | declare |
3184 | E : constant Entity_Id := Defining_Entity (N); | |
3185 | Prag : constant Node_Id := SPARK_Pragma (E); | |
3186 | -- SPARK_Mode pragma in application | |
d087b9ca | 3187 | |
a42b8e6a | 3188 | begin |
3189 | if Ekind (Unique_Entity (E)) in Generic_Unit_Kind then | |
3190 | null; | |
faee1e18 | 3191 | |
a42b8e6a | 3192 | elsif Present (Prag) then |
3193 | if Get_SPARK_Mode_From_Annotation (Prag) = Opt.On then | |
3194 | Check_Node (N); | |
3195 | end if; | |
faee1e18 | 3196 | |
a42b8e6a | 3197 | elsif Nkind (N) = N_Package_Body then |
3198 | Check_List (Declarations (N)); | |
3199 | ||
3200 | elsif Nkind (N) = N_Package_Declaration then | |
3201 | Check_List (Private_Declarations (Specification (N))); | |
3202 | Check_List (Visible_Declarations (Specification (N))); | |
3203 | end if; | |
3204 | end; | |
faee1e18 | 3205 | |
3206 | when others => | |
3207 | null; | |
3208 | end case; | |
3209 | end Check_Safe_Pointers; | |
3210 | ||
d087b9ca | 3211 | --------------------------------------- |
3212 | -- Check_Source_Of_Borrow_Or_Observe -- | |
3213 | --------------------------------------- | |
faee1e18 | 3214 | |
d087b9ca | 3215 | procedure Check_Source_Of_Borrow_Or_Observe |
3216 | (Expr : Node_Id; | |
3217 | Status : out Error_Status) | |
3218 | is | |
df177848 | 3219 | Root : Entity_Id; |
3220 | ||
d087b9ca | 3221 | begin |
df177848 | 3222 | if Is_Path_Expression (Expr) then |
3223 | Root := Get_Root_Object (Expr); | |
3224 | else | |
3225 | Root := Empty; | |
3226 | end if; | |
3227 | ||
d087b9ca | 3228 | Status := OK; |
faee1e18 | 3229 | |
d087b9ca | 3230 | -- SPARK RM 3.10(3): If the target of an assignment operation is an |
3231 | -- object of an anonymous access-to-object type (including copy-in for | |
3232 | -- a parameter), then the source shall be a name denoting a part of a | |
3233 | -- stand-alone object, a part of a parameter, or a call to a traversal | |
3234 | -- function. | |
faee1e18 | 3235 | |
df177848 | 3236 | if No (Root) then |
1e5359c0 | 3237 | if Emit_Messages then |
3238 | if Nkind (Expr) = N_Function_Call then | |
3239 | Error_Msg_N | |
3240 | ("incorrect borrow or observe (SPARK RM 3.10(3))", Expr); | |
3241 | Error_Msg_N | |
3242 | ("\function called must be a traversal function", Expr); | |
3243 | else | |
3244 | Error_Msg_N | |
3245 | ("incorrect borrow or observe (SPARK RM 3.10(3))", Expr); | |
3246 | Error_Msg_N | |
3247 | ("\expression must be part of stand-alone object or " & | |
3248 | "parameter", | |
3249 | Expr); | |
3250 | end if; | |
d087b9ca | 3251 | end if; |
24dca06d | 3252 | |
d087b9ca | 3253 | Status := Error; |
3254 | end if; | |
3255 | end Check_Source_Of_Borrow_Or_Observe; | |
24dca06d | 3256 | |
d087b9ca | 3257 | --------------------- |
3258 | -- Check_Statement -- | |
3259 | --------------------- | |
24dca06d | 3260 | |
d087b9ca | 3261 | procedure Check_Statement (Stmt : Node_Id) is |
24dca06d | 3262 | begin |
3263 | case N_Statement_Other_Than_Procedure_Call'(Nkind (Stmt)) is | |
d087b9ca | 3264 | |
3265 | -- An entry call is handled like other calls | |
3266 | ||
24dca06d | 3267 | when N_Entry_Call_Statement => |
3268 | Check_Call_Statement (Stmt); | |
3269 | ||
d087b9ca | 3270 | -- An assignment may correspond to a move, a borrow, or an observe |
24dca06d | 3271 | |
3272 | when N_Assignment_Statement => | |
d087b9ca | 3273 | declare |
df177848 | 3274 | Target : constant Node_Id := Name (Stmt); |
a42b8e6a | 3275 | |
d087b9ca | 3276 | begin |
a42b8e6a | 3277 | -- Start with checking that the subexpressions of the target |
3278 | -- path are readable, before possibly updating the permission | |
3279 | -- of these subexpressions in Check_Assignment. | |
3280 | ||
3281 | Check_Expression (Target, Read_Subexpr); | |
3282 | ||
d087b9ca | 3283 | Check_Assignment (Target => Target, |
3284 | Expr => Expression (Stmt)); | |
24dca06d | 3285 | |
df177848 | 3286 | -- ??? We need a rule that forbids targets of assignment for |
3287 | -- which the path is not known, for example when there is a | |
3288 | -- function call involved (which includes calls to traversal | |
3289 | -- functions). Otherwise there is no way to update the | |
3290 | -- corresponding path permission. | |
3291 | ||
3292 | if No (Get_Root_Object | |
3293 | (Target, Through_Traversal => False)) | |
3294 | then | |
1e5359c0 | 3295 | if Emit_Messages then |
3296 | Error_Msg_N ("illegal target for assignment", Target); | |
3297 | end if; | |
df177848 | 3298 | return; |
d087b9ca | 3299 | end if; |
df177848 | 3300 | |
3301 | Check_Expression (Target, Assign); | |
d087b9ca | 3302 | end; |
24dca06d | 3303 | |
d087b9ca | 3304 | when N_Block_Statement => |
df177848 | 3305 | Check_List (Declarations (Stmt)); |
3306 | Check_Node (Handled_Statement_Sequence (Stmt)); | |
24dca06d | 3307 | |
df177848 | 3308 | -- Remove local borrowers and observers |
24dca06d | 3309 | |
df177848 | 3310 | declare |
3311 | Decl : Node_Id := First (Declarations (Stmt)); | |
3312 | Var : Entity_Id; | |
3313 | begin | |
3314 | while Present (Decl) loop | |
3315 | if Nkind (Decl) = N_Object_Declaration then | |
3316 | Var := Defining_Identifier (Decl); | |
3317 | Remove (Current_Borrowers, Var); | |
3318 | Remove (Current_Observers, Var); | |
3319 | end if; | |
24dca06d | 3320 | |
df177848 | 3321 | Next (Decl); |
3322 | end loop; | |
d087b9ca | 3323 | end; |
24dca06d | 3324 | |
d087b9ca | 3325 | when N_Case_Statement => |
3326 | declare | |
3327 | Alt : Node_Id; | |
3328 | Saved_Env : Perm_Env; | |
3329 | -- Copy of environment for analysis of the different cases | |
3330 | New_Env : Perm_Env; | |
3331 | -- Accumulator for the different cases | |
24dca06d | 3332 | |
d087b9ca | 3333 | begin |
3334 | Check_Expression (Expression (Stmt), Read); | |
24dca06d | 3335 | |
d087b9ca | 3336 | -- Save environment |
24dca06d | 3337 | |
d087b9ca | 3338 | Copy_Env (Current_Perm_Env, Saved_Env); |
24dca06d | 3339 | |
d087b9ca | 3340 | -- First alternative |
24dca06d | 3341 | |
d087b9ca | 3342 | Alt := First (Alternatives (Stmt)); |
3343 | Check_List (Statements (Alt)); | |
3344 | Next (Alt); | |
24dca06d | 3345 | |
d087b9ca | 3346 | -- Cleanup |
24dca06d | 3347 | |
d087b9ca | 3348 | Move_Env (Current_Perm_Env, New_Env); |
24dca06d | 3349 | |
d087b9ca | 3350 | -- Other alternatives |
24dca06d | 3351 | |
d087b9ca | 3352 | while Present (Alt) loop |
24dca06d | 3353 | |
d087b9ca | 3354 | -- Restore environment |
24dca06d | 3355 | |
d087b9ca | 3356 | Copy_Env (Saved_Env, Current_Perm_Env); |
24dca06d | 3357 | |
d087b9ca | 3358 | -- Next alternative |
24dca06d | 3359 | |
d087b9ca | 3360 | Check_List (Statements (Alt)); |
3361 | Next (Alt); | |
24dca06d | 3362 | |
d087b9ca | 3363 | -- Merge Current_Perm_Env into New_Env |
24dca06d | 3364 | |
d087b9ca | 3365 | Merge_Env (Source => Current_Perm_Env, Target => New_Env); |
3366 | end loop; | |
24dca06d | 3367 | |
d087b9ca | 3368 | Move_Env (New_Env, Current_Perm_Env); |
3369 | Free_Env (Saved_Env); | |
3370 | end; | |
24dca06d | 3371 | |
d087b9ca | 3372 | when N_Delay_Relative_Statement |
3373 | | N_Delay_Until_Statement | |
3374 | => | |
3375 | Check_Expression (Expression (Stmt), Read); | |
24dca06d | 3376 | |
d087b9ca | 3377 | when N_Loop_Statement => |
3378 | Check_Loop_Statement (Stmt); | |
24dca06d | 3379 | |
d087b9ca | 3380 | when N_Simple_Return_Statement => |
3381 | declare | |
3382 | Subp : constant Entity_Id := | |
3383 | Return_Applies_To (Return_Statement_Entity (Stmt)); | |
3384 | Expr : constant Node_Id := Expression (Stmt); | |
3385 | begin | |
3386 | if Present (Expression (Stmt)) then | |
3387 | declare | |
3388 | Return_Typ : constant Entity_Id := | |
3389 | Etype (Expression (Stmt)); | |
382c4351 | 3390 | |
d087b9ca | 3391 | begin |
3392 | -- SPARK RM 3.10(5): A return statement that applies | |
3393 | -- to a traversal function that has an anonymous | |
3394 | -- access-to-constant (respectively, access-to-variable) | |
3395 | -- result type, shall return either the literal null | |
3396 | -- or an access object denoted by a direct or indirect | |
3397 | -- observer (respectively, borrower) of the traversed | |
3398 | -- parameter. | |
3399 | ||
3400 | if Is_Anonymous_Access_Type (Return_Typ) then | |
3401 | pragma Assert (Is_Traversal_Function (Subp)); | |
3402 | ||
30cf324b | 3403 | if Nkind (Expr) /= N_Null then |
d087b9ca | 3404 | declare |
3405 | Expr_Root : constant Entity_Id := | |
30cf324b | 3406 | Get_Root_Object (Expr, Is_Traversal => True); |
d087b9ca | 3407 | Param : constant Entity_Id := |
3408 | First_Formal (Subp); | |
3409 | begin | |
30cf324b | 3410 | if Param /= Expr_Root and then Emit_Messages then |
d087b9ca | 3411 | Error_Msg_NE |
3412 | ("returned value must be rooted in " | |
3413 | & "traversed parameter & " | |
3414 | & "(SPARK RM 3.10(5))", | |
3415 | Stmt, Param); | |
3416 | end if; | |
3417 | end; | |
3418 | end if; | |
24dca06d | 3419 | |
d087b9ca | 3420 | -- Move expression to caller |
24dca06d | 3421 | |
d087b9ca | 3422 | elsif Is_Deep (Return_Typ) then |
24dca06d | 3423 | |
d087b9ca | 3424 | if Is_Path_Expression (Expr) then |
3425 | Check_Expression (Expr, Move); | |
24dca06d | 3426 | |
d087b9ca | 3427 | else |
1e5359c0 | 3428 | if Emit_Messages then |
3429 | Error_Msg_N | |
3430 | ("expression not allowed as source of move", | |
3431 | Expr); | |
3432 | end if; | |
d087b9ca | 3433 | return; |
3434 | end if; | |
24dca06d | 3435 | |
d087b9ca | 3436 | else |
3437 | Check_Expression (Expr, Read); | |
24dca06d | 3438 | end if; |
24dca06d | 3439 | |
df177848 | 3440 | if Ekind_In (Subp, E_Procedure, E_Entry) |
3441 | and then not No_Return (Subp) | |
3442 | then | |
3443 | Return_Parameters (Subp); | |
3444 | Return_Globals (Subp); | |
3445 | end if; | |
d087b9ca | 3446 | end; |
24dca06d | 3447 | end if; |
faee1e18 | 3448 | end; |
3449 | ||
d087b9ca | 3450 | when N_Extended_Return_Statement => |
faee1e18 | 3451 | declare |
d087b9ca | 3452 | Subp : constant Entity_Id := |
3453 | Return_Applies_To (Return_Statement_Entity (Stmt)); | |
3454 | Decls : constant List_Id := Return_Object_Declarations (Stmt); | |
1e5359c0 | 3455 | Decl : constant Node_Id := Last_Non_Pragma (Decls); |
d087b9ca | 3456 | Obj : constant Entity_Id := Defining_Identifier (Decl); |
3457 | Perm : Perm_Kind; | |
faee1e18 | 3458 | |
3459 | begin | |
d087b9ca | 3460 | -- SPARK RM 3.10(5): return statement of traversal function |
faee1e18 | 3461 | |
1e5359c0 | 3462 | if Is_Traversal_Function (Subp) and then Emit_Messages then |
d087b9ca | 3463 | Error_Msg_N |
3464 | ("extended return cannot apply to a traversal function", | |
3465 | Stmt); | |
3466 | end if; | |
faee1e18 | 3467 | |
d087b9ca | 3468 | Check_List (Return_Object_Declarations (Stmt)); |
3469 | Check_Node (Handled_Statement_Sequence (Stmt)); | |
faee1e18 | 3470 | |
afd77d39 | 3471 | if Is_Deep (Etype (Obj)) then |
3472 | Perm := Get_Perm (Obj); | |
faee1e18 | 3473 | |
afd77d39 | 3474 | if Perm /= Read_Write then |
3475 | Perm_Error (Decl, Read_Write, Perm, | |
3476 | Expl => Get_Expl (Obj)); | |
3477 | end if; | |
d087b9ca | 3478 | end if; |
faee1e18 | 3479 | |
df177848 | 3480 | if Ekind_In (Subp, E_Procedure, E_Entry) |
3481 | and then not No_Return (Subp) | |
3482 | then | |
3483 | Return_Parameters (Subp); | |
3484 | Return_Globals (Subp); | |
3485 | end if; | |
faee1e18 | 3486 | end; |
3487 | ||
d087b9ca | 3488 | -- On loop exit, merge the current permission environment with the |
3489 | -- accumulator for the given loop. | |
faee1e18 | 3490 | |
d087b9ca | 3491 | when N_Exit_Statement => |
faee1e18 | 3492 | declare |
d087b9ca | 3493 | Loop_Name : constant Entity_Id := Loop_Of_Exit (Stmt); |
3494 | Saved_Accumulator : constant Perm_Env_Access := | |
3495 | Get (Current_Loops_Accumulators, Loop_Name); | |
3496 | Environment_Copy : constant Perm_Env_Access := | |
3497 | new Perm_Env; | |
faee1e18 | 3498 | begin |
d087b9ca | 3499 | Copy_Env (Current_Perm_Env, Environment_Copy.all); |
faee1e18 | 3500 | |
d087b9ca | 3501 | if Saved_Accumulator = null then |
3502 | Set (Current_Loops_Accumulators, | |
3503 | Loop_Name, Environment_Copy); | |
3504 | else | |
3505 | Merge_Env (Source => Environment_Copy.all, | |
3506 | Target => Saved_Accumulator.all); | |
3507 | -- ??? Either free Environment_Copy, or change the type | |
3508 | -- of loop accumulators to directly store permission | |
3509 | -- environments. | |
3510 | end if; | |
3511 | end; | |
faee1e18 | 3512 | |
d087b9ca | 3513 | -- On branches, analyze each branch independently on a fresh copy of |
3514 | -- the permission environment, then merge the resulting permission | |
3515 | -- environments. | |
faee1e18 | 3516 | |
3517 | when N_If_Statement => | |
3518 | declare | |
3519 | Saved_Env : Perm_Env; | |
d087b9ca | 3520 | New_Env : Perm_Env; |
faee1e18 | 3521 | -- Accumulator for the different branches |
3522 | ||
faee1e18 | 3523 | begin |
d087b9ca | 3524 | Check_Expression (Condition (Stmt), Read); |
faee1e18 | 3525 | |
3526 | -- Save environment | |
3527 | ||
24dca06d | 3528 | Copy_Env (Current_Perm_Env, Saved_Env); |
faee1e18 | 3529 | |
d087b9ca | 3530 | -- THEN branch |
faee1e18 | 3531 | |
3532 | Check_List (Then_Statements (Stmt)); | |
d087b9ca | 3533 | Move_Env (Current_Perm_Env, New_Env); |
faee1e18 | 3534 | |
d087b9ca | 3535 | -- ELSIF branches |
24dca06d | 3536 | |
faee1e18 | 3537 | declare |
d087b9ca | 3538 | Branch : Node_Id; |
faee1e18 | 3539 | begin |
d087b9ca | 3540 | Branch := First (Elsif_Parts (Stmt)); |
3541 | while Present (Branch) loop | |
faee1e18 | 3542 | |
d087b9ca | 3543 | -- Restore current permission environment |
faee1e18 | 3544 | |
24dca06d | 3545 | Copy_Env (Saved_Env, Current_Perm_Env); |
d087b9ca | 3546 | Check_Expression (Condition (Branch), Read); |
3547 | Check_List (Then_Statements (Branch)); | |
3548 | ||
3549 | -- Merge current permission environment | |
3550 | ||
3551 | Merge_Env (Source => Current_Perm_Env, Target => New_Env); | |
3552 | Next (Branch); | |
faee1e18 | 3553 | end loop; |
3554 | end; | |
3555 | ||
d087b9ca | 3556 | -- ELSE branch |
faee1e18 | 3557 | |
d087b9ca | 3558 | -- Restore current permission environment |
faee1e18 | 3559 | |
24dca06d | 3560 | Copy_Env (Saved_Env, Current_Perm_Env); |
faee1e18 | 3561 | Check_List (Else_Statements (Stmt)); |
3562 | ||
d087b9ca | 3563 | -- Merge current permission environment |
faee1e18 | 3564 | |
d087b9ca | 3565 | Merge_Env (Source => Current_Perm_Env, Target => New_Env); |
faee1e18 | 3566 | |
d087b9ca | 3567 | -- Cleanup |
3568 | ||
3569 | Move_Env (New_Env, Current_Perm_Env); | |
faee1e18 | 3570 | Free_Env (Saved_Env); |
3571 | end; | |
3572 | ||
d087b9ca | 3573 | -- We should ideally ignore the branch after raising an exception, |
3574 | -- so that it is not taken into account in merges. For now, just | |
3575 | -- propagate the current environment. | |
3576 | ||
3577 | when N_Raise_Statement => | |
3578 | null; | |
3579 | ||
3580 | when N_Null_Statement => | |
3581 | null; | |
3582 | ||
faee1e18 | 3583 | -- Unsupported constructs in SPARK |
3584 | ||
3585 | when N_Abort_Statement | |
3586 | | N_Accept_Statement | |
3587 | | N_Asynchronous_Select | |
3588 | | N_Code_Statement | |
3589 | | N_Conditional_Entry_Call | |
3590 | | N_Goto_Statement | |
3591 | | N_Requeue_Statement | |
3592 | | N_Selective_Accept | |
3593 | | N_Timed_Entry_Call | |
3594 | => | |
1e5359c0 | 3595 | null; |
faee1e18 | 3596 | |
faee1e18 | 3597 | -- The following nodes are never generated in GNATprove mode |
3598 | ||
3599 | when N_Compound_Statement | |
3600 | | N_Free_Statement | |
3601 | => | |
3602 | raise Program_Error; | |
3603 | end case; | |
3604 | end Check_Statement; | |
3605 | ||
7bd7174a | 3606 | ------------------------- |
3607 | -- Check_Type_Legality -- | |
3608 | ------------------------- | |
3609 | ||
3610 | procedure Check_Type_Legality | |
3611 | (Typ : Entity_Id; | |
3612 | Force : Boolean; | |
3613 | Legal : in out Boolean) | |
3614 | is | |
3615 | function Original_Emit_Messages return Boolean renames Emit_Messages; | |
3616 | ||
3617 | function Emit_Messages return Boolean; | |
3618 | -- Local wrapper around generic formal parameter Emit_Messages, to | |
3619 | -- check the value of parameter Force before calling the original | |
3620 | -- Emit_Messages, and setting Legal to False. | |
3621 | ||
3622 | ------------------- | |
3623 | -- Emit_Messages -- | |
3624 | ------------------- | |
3625 | ||
3626 | function Emit_Messages return Boolean is | |
3627 | begin | |
3628 | Legal := False; | |
3629 | return Force and then Original_Emit_Messages; | |
3630 | end Emit_Messages; | |
3631 | ||
3632 | -- Local variables | |
d087b9ca | 3633 | |
1e5359c0 | 3634 | Check_Typ : constant Entity_Id := Retysp (Typ); |
f8e6d133 | 3635 | |
7bd7174a | 3636 | -- Start of processing for Check_Type_Legality |
3637 | ||
d087b9ca | 3638 | begin |
f8e6d133 | 3639 | case Type_Kind'(Ekind (Check_Typ)) is |
d087b9ca | 3640 | when Access_Kind => |
1e5359c0 | 3641 | case Access_Kind'(Ekind (Check_Typ)) is |
df177848 | 3642 | when E_Access_Type |
3643 | | E_Anonymous_Access_Type | |
3644 | => | |
d087b9ca | 3645 | null; |
3646 | when E_Access_Subtype => | |
7bd7174a | 3647 | Check_Type_Legality (Base_Type (Check_Typ), Force, Legal); |
d087b9ca | 3648 | when E_Access_Attribute_Type => |
1e5359c0 | 3649 | if Emit_Messages then |
3650 | Error_Msg_N ("access attribute not allowed in SPARK", | |
3651 | Check_Typ); | |
3652 | end if; | |
d087b9ca | 3653 | when E_Allocator_Type => |
1e5359c0 | 3654 | if Emit_Messages then |
3655 | Error_Msg_N ("missing type resolution", Check_Typ); | |
3656 | end if; | |
d087b9ca | 3657 | when E_General_Access_Type => |
1e5359c0 | 3658 | if Emit_Messages then |
3659 | Error_Msg_NE | |
3660 | ("general access type & not allowed in SPARK", | |
3661 | Check_Typ, Check_Typ); | |
3662 | end if; | |
d087b9ca | 3663 | when Access_Subprogram_Kind => |
1e5359c0 | 3664 | if Emit_Messages then |
3665 | Error_Msg_NE | |
3666 | ("access to subprogram type & not allowed in SPARK", | |
3667 | Check_Typ, Check_Typ); | |
3668 | end if; | |
d087b9ca | 3669 | end case; |
3670 | ||
3671 | when E_Array_Type | |
3672 | | E_Array_Subtype | |
3673 | => | |
7bd7174a | 3674 | Check_Type_Legality (Component_Type (Check_Typ), Force, Legal); |
d087b9ca | 3675 | |
3676 | when Record_Kind => | |
f8e6d133 | 3677 | if Is_Deep (Check_Typ) |
3678 | and then (Is_Tagged_Type (Check_Typ) | |
3679 | or else Is_Class_Wide_Type (Check_Typ)) | |
d087b9ca | 3680 | then |
1e5359c0 | 3681 | if Emit_Messages then |
3682 | Error_Msg_NE | |
3683 | ("tagged type & cannot be owning in SPARK", | |
3684 | Check_Typ, Check_Typ); | |
3685 | end if; | |
d087b9ca | 3686 | |
3687 | else | |
3688 | declare | |
3689 | Comp : Entity_Id; | |
3690 | begin | |
f8e6d133 | 3691 | Comp := First_Component_Or_Discriminant (Check_Typ); |
d087b9ca | 3692 | while Present (Comp) loop |
1e5359c0 | 3693 | |
3694 | -- Ignore components which are not visible in SPARK | |
3695 | ||
3696 | if Component_Is_Visible_In_SPARK (Comp) then | |
7bd7174a | 3697 | Check_Type_Legality (Etype (Comp), Force, Legal); |
1e5359c0 | 3698 | end if; |
d087b9ca | 3699 | Next_Component_Or_Discriminant (Comp); |
3700 | end loop; | |
3701 | end; | |
3702 | end if; | |
3703 | ||
3704 | when Scalar_Kind | |
3705 | | E_String_Literal_Subtype | |
3706 | | Protected_Kind | |
3707 | | Task_Kind | |
3708 | | Incomplete_Kind | |
3709 | | E_Exception_Type | |
3710 | | E_Subprogram_Type | |
3711 | => | |
3712 | null; | |
3713 | ||
1e5359c0 | 3714 | -- Do not check type whose full view is not SPARK |
d087b9ca | 3715 | |
3716 | when E_Private_Type | |
3717 | | E_Private_Subtype | |
3718 | | E_Limited_Private_Type | |
3719 | | E_Limited_Private_Subtype | |
3720 | => | |
1e5359c0 | 3721 | null; |
d087b9ca | 3722 | end case; |
7bd7174a | 3723 | end Check_Type_Legality; |
d087b9ca | 3724 | |
5bc0f990 | 3725 | -------------- |
3726 | -- Get_Expl -- | |
3727 | -------------- | |
3728 | ||
3729 | function Get_Expl (N : Node_Or_Entity_Id) return Node_Id is | |
3730 | begin | |
3731 | -- Special case for the object declared in an extended return statement | |
3732 | ||
3733 | if Nkind (N) = N_Defining_Identifier then | |
3734 | declare | |
3735 | C : constant Perm_Tree_Access := | |
3736 | Get (Current_Perm_Env, Unique_Entity (N)); | |
3737 | begin | |
3738 | pragma Assert (C /= null); | |
3739 | return Explanation (C); | |
3740 | end; | |
3741 | ||
3742 | -- The expression is a call to a traversal function | |
3743 | ||
3744 | elsif Is_Traversal_Function_Call (N) then | |
3745 | return N; | |
3746 | ||
3747 | -- The expression is directly rooted in an object | |
3748 | ||
3749 | elsif Present (Get_Root_Object (N, Through_Traversal => False)) then | |
3750 | declare | |
3751 | Tree_Or_Perm : constant Perm_Or_Tree := Get_Perm_Or_Tree (N); | |
3752 | begin | |
3753 | case Tree_Or_Perm.R is | |
3754 | when Folded => | |
3755 | return Tree_Or_Perm.Explanation; | |
3756 | ||
3757 | when Unfolded => | |
3758 | pragma Assert (Tree_Or_Perm.Tree_Access /= null); | |
3759 | return Explanation (Tree_Or_Perm.Tree_Access); | |
3760 | end case; | |
3761 | end; | |
3762 | ||
3763 | -- The expression is a function call, an allocation, or null | |
3764 | ||
3765 | else | |
3766 | return N; | |
3767 | end if; | |
3768 | end Get_Expl; | |
3769 | ||
f8e6d133 | 3770 | ----------------------------------- |
3771 | -- Get_Observed_Or_Borrowed_Expr -- | |
3772 | ----------------------------------- | |
3773 | ||
3774 | function Get_Observed_Or_Borrowed_Expr (Expr : Node_Id) return Node_Id is | |
3775 | begin | |
3776 | if Is_Traversal_Function_Call (Expr) then | |
3777 | return First_Actual (Expr); | |
3778 | else | |
3779 | return Expr; | |
3780 | end if; | |
3781 | end Get_Observed_Or_Borrowed_Expr; | |
3782 | ||
faee1e18 | 3783 | -------------- |
3784 | -- Get_Perm -- | |
3785 | -------------- | |
3786 | ||
d087b9ca | 3787 | function Get_Perm (N : Node_Or_Entity_Id) return Perm_Kind is |
faee1e18 | 3788 | begin |
d087b9ca | 3789 | -- Special case for the object declared in an extended return statement |
3790 | ||
3791 | if Nkind (N) = N_Defining_Identifier then | |
3792 | declare | |
3793 | C : constant Perm_Tree_Access := | |
3794 | Get (Current_Perm_Env, Unique_Entity (N)); | |
3795 | begin | |
3796 | pragma Assert (C /= null); | |
3797 | return Permission (C); | |
3798 | end; | |
faee1e18 | 3799 | |
df177848 | 3800 | -- The expression is a call to a traversal function |
faee1e18 | 3801 | |
df177848 | 3802 | elsif Is_Traversal_Function_Call (N) then |
3803 | declare | |
3804 | Callee : constant Entity_Id := Get_Called_Entity (N); | |
3805 | begin | |
3806 | if Is_Access_Constant (Etype (Callee)) then | |
3807 | return Read_Only; | |
3808 | else | |
3809 | return Read_Write; | |
3810 | end if; | |
3811 | end; | |
3812 | ||
3813 | -- The expression is directly rooted in an object | |
3814 | ||
3815 | elsif Present (Get_Root_Object (N, Through_Traversal => False)) then | |
d087b9ca | 3816 | declare |
3817 | Tree_Or_Perm : constant Perm_Or_Tree := Get_Perm_Or_Tree (N); | |
3818 | begin | |
3819 | case Tree_Or_Perm.R is | |
3820 | when Folded => | |
3821 | return Tree_Or_Perm.Found_Permission; | |
faee1e18 | 3822 | |
d087b9ca | 3823 | when Unfolded => |
3824 | pragma Assert (Tree_Or_Perm.Tree_Access /= null); | |
3825 | return Permission (Tree_Or_Perm.Tree_Access); | |
3826 | end case; | |
3827 | end; | |
3828 | ||
3829 | -- The expression is a function call, an allocation, or null | |
3830 | ||
3831 | else | |
3832 | return Read_Write; | |
3833 | end if; | |
faee1e18 | 3834 | end Get_Perm; |
3835 | ||
3836 | ---------------------- | |
3837 | -- Get_Perm_Or_Tree -- | |
3838 | ---------------------- | |
3839 | ||
3840 | function Get_Perm_Or_Tree (N : Node_Id) return Perm_Or_Tree is | |
3841 | begin | |
3842 | case Nkind (N) is | |
3843 | ||
d087b9ca | 3844 | when N_Expanded_Name |
3845 | | N_Identifier | |
faee1e18 | 3846 | => |
3847 | declare | |
faee1e18 | 3848 | C : constant Perm_Tree_Access := |
d087b9ca | 3849 | Get (Current_Perm_Env, Unique_Entity (Entity (N))); |
faee1e18 | 3850 | begin |
82c20af0 | 3851 | -- Except during elaboration, the root object should have been |
3852 | -- declared and entered into the current permission | |
3853 | -- environment. | |
3854 | ||
3855 | if not Inside_Elaboration | |
3856 | and then C = null | |
3857 | then | |
9ac24568 | 3858 | Illegal_Global_Usage (N, N); |
82c20af0 | 3859 | end if; |
3860 | ||
d087b9ca | 3861 | return (R => Unfolded, Tree_Access => C); |
faee1e18 | 3862 | end; |
3863 | ||
a080baa9 | 3864 | -- For a nonterminal path, we get the permission tree of its |
d087b9ca | 3865 | -- prefix, and then get the subtree associated with the extension, |
3866 | -- if unfolded. If folded, we return the permission associated with | |
3867 | -- children. | |
faee1e18 | 3868 | |
d087b9ca | 3869 | when N_Explicit_Dereference |
3870 | | N_Indexed_Component | |
3871 | | N_Selected_Component | |
3872 | | N_Slice | |
3873 | => | |
faee1e18 | 3874 | declare |
24dca06d | 3875 | C : constant Perm_Or_Tree := Get_Perm_Or_Tree (Prefix (N)); |
faee1e18 | 3876 | begin |
3877 | case C.R is | |
d087b9ca | 3878 | |
3879 | -- Some earlier prefix was already folded, return the | |
3880 | -- permission found. | |
3881 | ||
3882 | when Folded => | |
faee1e18 | 3883 | return C; |
3884 | ||
3885 | when Unfolded => | |
d087b9ca | 3886 | case Kind (C.Tree_Access) is |
3887 | ||
3888 | -- If the prefix tree is already folded, return the | |
3889 | -- children permission. | |
3890 | ||
3891 | when Entire_Object => | |
3892 | return (R => Folded, | |
3893 | Found_Permission => | |
5bc0f990 | 3894 | Children_Permission (C.Tree_Access), |
3895 | Explanation => | |
3896 | Explanation (C.Tree_Access)); | |
d087b9ca | 3897 | |
3898 | when Reference => | |
3899 | pragma Assert (Nkind (N) = N_Explicit_Dereference); | |
3900 | return (R => Unfolded, | |
3901 | Tree_Access => Get_All (C.Tree_Access)); | |
3902 | ||
3903 | when Record_Component => | |
3904 | pragma Assert (Nkind (N) = N_Selected_Component); | |
3905 | declare | |
3906 | Comp : constant Entity_Id := | |
1e5359c0 | 3907 | Original_Record_Component |
3908 | (Entity (Selector_Name (N))); | |
d087b9ca | 3909 | D : constant Perm_Tree_Access := |
3910 | Perm_Tree_Maps.Get | |
3911 | (Component (C.Tree_Access), Comp); | |
3912 | begin | |
3913 | pragma Assert (D /= null); | |
24dca06d | 3914 | return (R => Unfolded, |
d087b9ca | 3915 | Tree_Access => D); |
3916 | end; | |
3917 | ||
3918 | when Array_Component => | |
3919 | pragma Assert (Nkind (N) = N_Indexed_Component | |
3920 | or else | |
3921 | Nkind (N) = N_Slice); | |
3922 | pragma Assert (Get_Elem (C.Tree_Access) /= null); | |
3923 | return (R => Unfolded, | |
3924 | Tree_Access => Get_Elem (C.Tree_Access)); | |
3925 | end case; | |
faee1e18 | 3926 | end case; |
3927 | end; | |
faee1e18 | 3928 | |
d087b9ca | 3929 | when N_Qualified_Expression |
3930 | | N_Type_Conversion | |
3931 | | N_Unchecked_Type_Conversion | |
faee1e18 | 3932 | => |
d087b9ca | 3933 | return Get_Perm_Or_Tree (Expression (N)); |
faee1e18 | 3934 | |
d087b9ca | 3935 | when others => |
3936 | raise Program_Error; | |
3937 | end case; | |
3938 | end Get_Perm_Or_Tree; | |
faee1e18 | 3939 | |
d087b9ca | 3940 | ------------------- |
3941 | -- Get_Perm_Tree -- | |
3942 | ------------------- | |
faee1e18 | 3943 | |
d087b9ca | 3944 | function Get_Perm_Tree (N : Node_Id) return Perm_Tree_Access is |
3945 | begin | |
5bc0f990 | 3946 | return Set_Perm_Prefixes (N, None, Empty); |
d087b9ca | 3947 | end Get_Perm_Tree; |
24dca06d | 3948 | |
d087b9ca | 3949 | --------------------- |
3950 | -- Get_Root_Object -- | |
3951 | --------------------- | |
24dca06d | 3952 | |
df177848 | 3953 | function Get_Root_Object |
3954 | (Expr : Node_Id; | |
30cf324b | 3955 | Through_Traversal : Boolean := True; |
3956 | Is_Traversal : Boolean := False) return Entity_Id | |
df177848 | 3957 | is |
30cf324b | 3958 | function GRO (Expr : Node_Id) return Entity_Id; |
3959 | -- Local wrapper on the actual function, to propagate the values of | |
3960 | -- optional parameters. | |
3961 | ||
3962 | --------- | |
3963 | -- GRO -- | |
3964 | --------- | |
3965 | ||
3966 | function GRO (Expr : Node_Id) return Entity_Id is | |
3967 | begin | |
3968 | return Get_Root_Object (Expr, Through_Traversal, Is_Traversal); | |
3969 | end GRO; | |
3970 | ||
3971 | Get_Root_Object : Boolean; | |
3972 | pragma Unmodified (Get_Root_Object); | |
3973 | -- Local variable to mask the name of function Get_Root_Object, to | |
3974 | -- prevent direct call. Instead GRO wrapper should be called. | |
3975 | ||
3976 | -- Start of processing for Get_Root_Object | |
3977 | ||
d087b9ca | 3978 | begin |
30cf324b | 3979 | if not Is_Subpath_Expression (Expr, Is_Traversal) then |
1e5359c0 | 3980 | if Emit_Messages then |
3981 | Error_Msg_N ("name expected here for path", Expr); | |
3982 | end if; | |
9eaacd61 | 3983 | return Empty; |
3984 | end if; | |
3985 | ||
d087b9ca | 3986 | case Nkind (Expr) is |
3987 | when N_Expanded_Name | |
3988 | | N_Identifier | |
3989 | => | |
3990 | return Entity (Expr); | |
faee1e18 | 3991 | |
d087b9ca | 3992 | when N_Explicit_Dereference |
3993 | | N_Indexed_Component | |
3994 | | N_Selected_Component | |
3995 | | N_Slice | |
3996 | => | |
30cf324b | 3997 | return GRO (Prefix (Expr)); |
faee1e18 | 3998 | |
a080baa9 | 3999 | -- There is no root object for an (extension) aggregate, allocator, |
9ac24568 | 4000 | -- concat, or NULL. |
faee1e18 | 4001 | |
a080baa9 | 4002 | when N_Aggregate |
4003 | | N_Allocator | |
4004 | | N_Extension_Aggregate | |
d087b9ca | 4005 | | N_Null |
9ac24568 | 4006 | | N_Op_Concat |
d087b9ca | 4007 | => |
4008 | return Empty; | |
faee1e18 | 4009 | |
df177848 | 4010 | -- In the case of a call to a traversal function, the root object is |
4011 | -- the root of the traversed parameter. Otherwise there is no root | |
4012 | -- object. | |
4013 | ||
4014 | when N_Function_Call => | |
4015 | if Through_Traversal | |
4016 | and then Is_Traversal_Function_Call (Expr) | |
4017 | then | |
30cf324b | 4018 | return GRO (First_Actual (Expr)); |
df177848 | 4019 | else |
4020 | return Empty; | |
4021 | end if; | |
4022 | ||
d087b9ca | 4023 | when N_Qualified_Expression |
4024 | | N_Type_Conversion | |
4025 | | N_Unchecked_Type_Conversion | |
4026 | => | |
30cf324b | 4027 | return GRO (Expression (Expr)); |
24dca06d | 4028 | |
9ac24568 | 4029 | when N_Attribute_Reference => |
4030 | pragma Assert | |
4031 | (Get_Attribute_Id (Attribute_Name (Expr)) = | |
4032 | Attribute_Loop_Entry | |
4033 | or else | |
4034 | Get_Attribute_Id (Attribute_Name (Expr)) = | |
1e5359c0 | 4035 | Attribute_Update |
4036 | or else Get_Attribute_Id (Attribute_Name (Expr)) = | |
4037 | Attribute_Image); | |
9ac24568 | 4038 | return Empty; |
4039 | ||
30cf324b | 4040 | when N_If_Expression => |
4041 | if Is_Traversal then | |
4042 | declare | |
4043 | Cond : constant Node_Id := First (Expressions (Expr)); | |
4044 | Then_Part : constant Node_Id := Next (Cond); | |
4045 | Else_Part : constant Node_Id := Next (Then_Part); | |
4046 | Then_Root : constant Entity_Id := GRO (Then_Part); | |
4047 | Else_Root : constant Entity_Id := GRO (Else_Part); | |
4048 | begin | |
4049 | if Nkind (Then_Part) = N_Null then | |
4050 | return Else_Root; | |
4051 | elsif Nkind (Else_Part) = N_Null then | |
4052 | return Then_Part; | |
4053 | elsif Then_Root = Else_Root then | |
4054 | return Then_Root; | |
4055 | else | |
4056 | if Emit_Messages then | |
4057 | Error_Msg_N | |
4058 | ("same name expected here in each branch", Expr); | |
4059 | end if; | |
4060 | return Empty; | |
4061 | end if; | |
4062 | end; | |
4063 | else | |
4064 | if Emit_Messages then | |
4065 | Error_Msg_N ("name expected here for path", Expr); | |
4066 | end if; | |
4067 | return Empty; | |
4068 | end if; | |
4069 | ||
4070 | when N_Case_Expression => | |
4071 | if Is_Traversal then | |
4072 | declare | |
4073 | Cases : constant List_Id := Alternatives (Expr); | |
4074 | Cur_Case : Node_Id := First (Cases); | |
4075 | Cur_Root : Entity_Id; | |
4076 | Common_Root : Entity_Id := Empty; | |
4077 | ||
4078 | begin | |
4079 | while Present (Cur_Case) loop | |
4080 | Cur_Root := GRO (Expression (Cur_Case)); | |
4081 | ||
4082 | if Common_Root = Empty then | |
4083 | Common_Root := Cur_Root; | |
4084 | elsif Common_Root /= Cur_Root then | |
4085 | if Emit_Messages then | |
4086 | Error_Msg_N | |
4087 | ("same name expected here in each branch", Expr); | |
4088 | end if; | |
4089 | return Empty; | |
4090 | end if; | |
4091 | Next (Cur_Case); | |
4092 | end loop; | |
4093 | ||
4094 | return Common_Root; | |
4095 | end; | |
4096 | else | |
4097 | if Emit_Messages then | |
4098 | Error_Msg_N ("name expected here for path", Expr); | |
4099 | end if; | |
4100 | return Empty; | |
4101 | end if; | |
4102 | ||
d087b9ca | 4103 | when others => |
4104 | raise Program_Error; | |
4105 | end case; | |
4106 | end Get_Root_Object; | |
24dca06d | 4107 | |
d087b9ca | 4108 | --------- |
4109 | -- Glb -- | |
4110 | --------- | |
24dca06d | 4111 | |
d087b9ca | 4112 | function Glb (P1, P2 : Perm_Kind) return Perm_Kind |
4113 | is | |
4114 | begin | |
4115 | case P1 is | |
4116 | when No_Access => | |
4117 | return No_Access; | |
4118 | ||
4119 | when Read_Only => | |
4120 | case P2 is | |
4121 | when No_Access | |
4122 | | Write_Only | |
4123 | => | |
4124 | return No_Access; | |
24dca06d | 4125 | |
d087b9ca | 4126 | when Read_Perm => |
4127 | return Read_Only; | |
4128 | end case; | |
24dca06d | 4129 | |
d087b9ca | 4130 | when Write_Only => |
4131 | case P2 is | |
4132 | when No_Access | |
4133 | | Read_Only | |
4134 | => | |
4135 | return No_Access; | |
faee1e18 | 4136 | |
d087b9ca | 4137 | when Write_Perm => |
4138 | return Write_Only; | |
4139 | end case; | |
faee1e18 | 4140 | |
d087b9ca | 4141 | when Read_Write => |
4142 | return P2; | |
faee1e18 | 4143 | end case; |
d087b9ca | 4144 | end Glb; |
faee1e18 | 4145 | |
d087b9ca | 4146 | ------------------------- |
4147 | -- Has_Array_Component -- | |
4148 | ------------------------- | |
faee1e18 | 4149 | |
d087b9ca | 4150 | function Has_Array_Component (Expr : Node_Id) return Boolean is |
faee1e18 | 4151 | begin |
d087b9ca | 4152 | case Nkind (Expr) is |
4153 | when N_Expanded_Name | |
4154 | | N_Identifier | |
4155 | => | |
4156 | return False; | |
faee1e18 | 4157 | |
d087b9ca | 4158 | when N_Explicit_Dereference |
4159 | | N_Selected_Component | |
4160 | => | |
4161 | return Has_Array_Component (Prefix (Expr)); | |
faee1e18 | 4162 | |
d087b9ca | 4163 | when N_Indexed_Component |
4164 | | N_Slice | |
4165 | => | |
4166 | return True; | |
faee1e18 | 4167 | |
d087b9ca | 4168 | when N_Allocator |
4169 | | N_Null | |
4170 | | N_Function_Call | |
faee1e18 | 4171 | => |
d087b9ca | 4172 | return False; |
faee1e18 | 4173 | |
d087b9ca | 4174 | when N_Qualified_Expression |
4175 | | N_Type_Conversion | |
4176 | | N_Unchecked_Type_Conversion | |
4177 | => | |
4178 | return Has_Array_Component (Expression (Expr)); | |
faee1e18 | 4179 | |
d087b9ca | 4180 | when others => |
4181 | raise Program_Error; | |
4182 | end case; | |
4183 | end Has_Array_Component; | |
faee1e18 | 4184 | |
d087b9ca | 4185 | -------- |
4186 | -- Hp -- | |
4187 | -------- | |
24dca06d | 4188 | |
d087b9ca | 4189 | procedure Hp (P : Perm_Env) is |
4190 | Elem : Perm_Tree_Maps.Key_Option; | |
faee1e18 | 4191 | |
d087b9ca | 4192 | begin |
4193 | Elem := Get_First_Key (P); | |
4194 | while Elem.Present loop | |
4195 | Print_Node_Briefly (Elem.K); | |
4196 | Elem := Get_Next_Key (P); | |
4197 | end loop; | |
4198 | end Hp; | |
faee1e18 | 4199 | |
d087b9ca | 4200 | -------------------------- |
4201 | -- Illegal_Global_Usage -- | |
4202 | -------------------------- | |
faee1e18 | 4203 | |
9ac24568 | 4204 | procedure Illegal_Global_Usage (N : Node_Or_Entity_Id; E : Entity_Id) |
4205 | is | |
d087b9ca | 4206 | begin |
9ac24568 | 4207 | Error_Msg_NE ("cannot use global variable & of deep type", N, E); |
d087b9ca | 4208 | Error_Msg_N ("\without prior declaration in a Global aspect", N); |
4209 | Errout.Finalize (Last_Call => True); | |
4210 | Errout.Output_Messages; | |
4211 | Exit_Program (E_Errors); | |
4212 | end Illegal_Global_Usage; | |
faee1e18 | 4213 | |
d087b9ca | 4214 | ------------- |
4215 | -- Is_Deep -- | |
4216 | ------------- | |
4217 | ||
4218 | function Is_Deep (Typ : Entity_Id) return Boolean is | |
4219 | begin | |
1e5359c0 | 4220 | case Type_Kind'(Ekind (Retysp (Typ))) is |
d087b9ca | 4221 | when Access_Kind => |
4222 | return True; | |
faee1e18 | 4223 | |
d087b9ca | 4224 | when E_Array_Type |
4225 | | E_Array_Subtype | |
4226 | => | |
1e5359c0 | 4227 | return Is_Deep (Component_Type (Retysp (Typ))); |
d087b9ca | 4228 | |
4229 | when Record_Kind => | |
4230 | declare | |
4231 | Comp : Entity_Id; | |
faee1e18 | 4232 | begin |
1e5359c0 | 4233 | Comp := First_Component_Or_Discriminant (Retysp (Typ)); |
d087b9ca | 4234 | while Present (Comp) loop |
1e5359c0 | 4235 | |
4236 | -- Ignore components not visible in SPARK | |
4237 | ||
4238 | if Component_Is_Visible_In_SPARK (Comp) | |
4239 | and then Is_Deep (Etype (Comp)) | |
4240 | then | |
d087b9ca | 4241 | return True; |
4242 | end if; | |
4243 | Next_Component_Or_Discriminant (Comp); | |
4244 | end loop; | |
4245 | end; | |
4246 | return False; | |
24dca06d | 4247 | |
d087b9ca | 4248 | when Scalar_Kind |
4249 | | E_String_Literal_Subtype | |
4250 | | Protected_Kind | |
4251 | | Task_Kind | |
4252 | | Incomplete_Kind | |
4253 | | E_Exception_Type | |
4254 | | E_Subprogram_Type | |
4255 | => | |
4256 | return False; | |
faee1e18 | 4257 | |
1e5359c0 | 4258 | -- Ignore full view of types if it is not in SPARK |
faee1e18 | 4259 | |
d087b9ca | 4260 | when E_Private_Type |
4261 | | E_Private_Subtype | |
4262 | | E_Limited_Private_Type | |
4263 | | E_Limited_Private_Subtype | |
4264 | => | |
1e5359c0 | 4265 | return False; |
d087b9ca | 4266 | end case; |
4267 | end Is_Deep; | |
faee1e18 | 4268 | |
7bd7174a | 4269 | -------------- |
4270 | -- Is_Legal -- | |
4271 | -------------- | |
4272 | ||
4273 | function Is_Legal (N : Node_Id) return Boolean is | |
4274 | Legal : Boolean := True; | |
4275 | ||
4276 | begin | |
4277 | case Nkind (N) is | |
4278 | when N_Declaration => | |
4279 | Check_Declaration_Legality (N, Force => False, Legal => Legal); | |
4280 | when others => | |
4281 | null; | |
4282 | end case; | |
4283 | ||
4284 | return Legal; | |
4285 | end Is_Legal; | |
4286 | ||
583f62e9 | 4287 | ---------------------- |
4288 | -- Is_Local_Context -- | |
4289 | ---------------------- | |
4290 | ||
4291 | function Is_Local_Context (Scop : Entity_Id) return Boolean is | |
4292 | begin | |
4293 | return Is_Subprogram_Or_Entry (Scop) | |
4294 | or else Ekind (Scop) = E_Block; | |
4295 | end Is_Local_Context; | |
4296 | ||
d087b9ca | 4297 | ------------------------ |
4298 | -- Is_Path_Expression -- | |
4299 | ------------------------ | |
24dca06d | 4300 | |
30cf324b | 4301 | function Is_Path_Expression |
4302 | (Expr : Node_Id; | |
4303 | Is_Traversal : Boolean := False) return Boolean | |
4304 | is | |
4305 | function IPE (Expr : Node_Id) return Boolean; | |
4306 | -- Local wrapper on the actual function, to propagate the values of | |
4307 | -- optional parameter Is_Traversal. | |
4308 | ||
4309 | --------- | |
4310 | -- IPE -- | |
4311 | --------- | |
4312 | ||
4313 | function IPE (Expr : Node_Id) return Boolean is | |
4314 | begin | |
4315 | return Is_Path_Expression (Expr, Is_Traversal); | |
4316 | end IPE; | |
4317 | ||
4318 | Is_Path_Expression : Boolean; | |
4319 | pragma Unmodified (Is_Path_Expression); | |
4320 | -- Local variable to mask the name of function Is_Path_Expression, to | |
4321 | -- prevent direct call. Instead IPE wrapper should be called. | |
4322 | ||
4323 | -- Start of processing for Is_Path_Expression | |
4324 | ||
d087b9ca | 4325 | begin |
4326 | case Nkind (Expr) is | |
4327 | when N_Expanded_Name | |
4328 | | N_Explicit_Dereference | |
4329 | | N_Identifier | |
4330 | | N_Indexed_Component | |
4331 | | N_Selected_Component | |
4332 | | N_Slice | |
4333 | => | |
4334 | return True; | |
faee1e18 | 4335 | |
d087b9ca | 4336 | -- Special value NULL corresponds to an empty path |
faee1e18 | 4337 | |
d087b9ca | 4338 | when N_Null => |
4339 | return True; | |
24dca06d | 4340 | |
a080baa9 | 4341 | -- Object returned by an (extension) aggregate, an allocator, or |
4342 | -- a function call corresponds to a path. | |
faee1e18 | 4343 | |
a080baa9 | 4344 | when N_Aggregate |
4345 | | N_Allocator | |
4346 | | N_Extension_Aggregate | |
d087b9ca | 4347 | | N_Function_Call |
4348 | => | |
4349 | return True; | |
faee1e18 | 4350 | |
d087b9ca | 4351 | when N_Qualified_Expression |
4352 | | N_Type_Conversion | |
4353 | | N_Unchecked_Type_Conversion | |
4354 | => | |
30cf324b | 4355 | return IPE (Expression (Expr)); |
4356 | ||
4357 | -- When returning from a traversal function, consider an | |
4358 | -- if-expression as a possible path expression. | |
4359 | ||
4360 | when N_If_Expression => | |
4361 | if Is_Traversal then | |
4362 | declare | |
4363 | Cond : constant Node_Id := First (Expressions (Expr)); | |
4364 | Then_Part : constant Node_Id := Next (Cond); | |
4365 | Else_Part : constant Node_Id := Next (Then_Part); | |
4366 | begin | |
4367 | return IPE (Then_Part) | |
4368 | and then IPE (Else_Part); | |
4369 | end; | |
4370 | else | |
4371 | return False; | |
4372 | end if; | |
4373 | ||
4374 | -- When returning from a traversal function, consider | |
4375 | -- a case-expression as a possible path expression. | |
4376 | ||
4377 | when N_Case_Expression => | |
4378 | if Is_Traversal then | |
4379 | declare | |
4380 | Cases : constant List_Id := Alternatives (Expr); | |
4381 | Cur_Case : Node_Id := First (Cases); | |
4382 | ||
4383 | begin | |
4384 | while Present (Cur_Case) loop | |
4385 | if not IPE (Expression (Cur_Case)) then | |
4386 | return False; | |
4387 | end if; | |
4388 | Next (Cur_Case); | |
4389 | end loop; | |
4390 | ||
4391 | return True; | |
4392 | end; | |
4393 | else | |
4394 | return False; | |
4395 | end if; | |
faee1e18 | 4396 | |
d087b9ca | 4397 | when others => |
4398 | return False; | |
4399 | end case; | |
4400 | end Is_Path_Expression; | |
faee1e18 | 4401 | |
df177848 | 4402 | ------------------------- |
4403 | -- Is_Prefix_Or_Almost -- | |
4404 | ------------------------- | |
4405 | ||
4406 | function Is_Prefix_Or_Almost (Pref, Expr : Node_Id) return Boolean is | |
4407 | ||
4408 | type Expr_Array is array (Positive range <>) of Node_Id; | |
4409 | -- Sequence of expressions that make up a path | |
4410 | ||
4411 | function Get_Expr_Array (Expr : Node_Id) return Expr_Array; | |
4412 | pragma Precondition (Is_Path_Expression (Expr)); | |
4413 | -- Return the sequence of expressions that make up a path | |
4414 | ||
4415 | -------------------- | |
4416 | -- Get_Expr_Array -- | |
4417 | -------------------- | |
4418 | ||
4419 | function Get_Expr_Array (Expr : Node_Id) return Expr_Array is | |
4420 | begin | |
4421 | case Nkind (Expr) is | |
4422 | when N_Expanded_Name | |
4423 | | N_Identifier | |
4424 | => | |
4425 | return Expr_Array'(1 => Expr); | |
4426 | ||
4427 | when N_Explicit_Dereference | |
4428 | | N_Indexed_Component | |
4429 | | N_Selected_Component | |
4430 | | N_Slice | |
4431 | => | |
4432 | return Get_Expr_Array (Prefix (Expr)) & Expr; | |
4433 | ||
4434 | when N_Qualified_Expression | |
4435 | | N_Type_Conversion | |
4436 | | N_Unchecked_Type_Conversion | |
4437 | => | |
4438 | return Get_Expr_Array (Expression (Expr)); | |
4439 | ||
4440 | when others => | |
4441 | raise Program_Error; | |
4442 | end case; | |
4443 | end Get_Expr_Array; | |
4444 | ||
4445 | -- Local variables | |
4446 | ||
4447 | Prefix_Path : constant Expr_Array := Get_Expr_Array (Pref); | |
4448 | Expr_Path : constant Expr_Array := Get_Expr_Array (Expr); | |
4449 | ||
4450 | Prefix_Root : constant Node_Id := Prefix_Path (1); | |
4451 | Expr_Root : constant Node_Id := Expr_Path (1); | |
4452 | ||
4453 | Common_Len : constant Positive := | |
4454 | Positive'Min (Prefix_Path'Length, Expr_Path'Length); | |
4455 | ||
4456 | -- Start of processing for Is_Prefix_Or_Almost | |
4457 | ||
4458 | begin | |
4459 | if Entity (Prefix_Root) /= Entity (Expr_Root) then | |
4460 | return False; | |
4461 | end if; | |
4462 | ||
4463 | for J in 2 .. Common_Len loop | |
4464 | declare | |
4465 | Prefix_Elt : constant Node_Id := Prefix_Path (J); | |
4466 | Expr_Elt : constant Node_Id := Expr_Path (J); | |
4467 | begin | |
4468 | case Nkind (Prefix_Elt) is | |
4469 | when N_Explicit_Dereference => | |
4470 | if Nkind (Expr_Elt) /= N_Explicit_Dereference then | |
4471 | return False; | |
4472 | end if; | |
4473 | ||
4474 | when N_Selected_Component => | |
4475 | if Nkind (Expr_Elt) /= N_Selected_Component | |
1e5359c0 | 4476 | or else Original_Record_Component |
4477 | (Entity (Selector_Name (Prefix_Elt))) | |
4478 | /= Original_Record_Component | |
4479 | (Entity (Selector_Name (Expr_Elt))) | |
df177848 | 4480 | then |
4481 | return False; | |
4482 | end if; | |
4483 | ||
4484 | when N_Indexed_Component | |
4485 | | N_Slice | |
4486 | => | |
4487 | if not Nkind_In (Expr_Elt, N_Indexed_Component, N_Slice) then | |
4488 | return False; | |
4489 | end if; | |
4490 | ||
4491 | when others => | |
4492 | raise Program_Error; | |
4493 | end case; | |
4494 | end; | |
4495 | end loop; | |
4496 | ||
4497 | -- If the expression path is longer than the prefix one, then at this | |
4498 | -- point the prefix property holds. | |
4499 | ||
4500 | if Expr_Path'Length > Prefix_Path'Length then | |
4501 | return True; | |
4502 | ||
4503 | -- Otherwise check if none of the remaining path elements in the | |
4504 | -- candidate prefix involve a dereference. | |
4505 | ||
4506 | else | |
4507 | for J in Common_Len + 1 .. Prefix_Path'Length loop | |
4508 | if Nkind (Prefix_Path (J)) = N_Explicit_Dereference then | |
4509 | return False; | |
4510 | end if; | |
4511 | end loop; | |
4512 | ||
4513 | return True; | |
4514 | end if; | |
4515 | end Is_Prefix_Or_Almost; | |
4516 | ||
9ac24568 | 4517 | --------------------------- |
4518 | -- Is_Subpath_Expression -- | |
4519 | --------------------------- | |
4520 | ||
30cf324b | 4521 | function Is_Subpath_Expression |
4522 | (Expr : Node_Id; | |
4523 | Is_Traversal : Boolean := False) return Boolean | |
4524 | is | |
9ac24568 | 4525 | begin |
30cf324b | 4526 | return Is_Path_Expression (Expr, Is_Traversal) |
a62147e6 | 4527 | |
4528 | or else (Nkind_In (Expr, N_Qualified_Expression, | |
4529 | N_Type_Conversion, | |
4530 | N_Unchecked_Type_Conversion) | |
4531 | and then Is_Subpath_Expression (Expression (Expr))) | |
4532 | ||
9ac24568 | 4533 | or else (Nkind (Expr) = N_Attribute_Reference |
4534 | and then | |
4535 | (Get_Attribute_Id (Attribute_Name (Expr)) = | |
4536 | Attribute_Update | |
4537 | or else | |
4538 | Get_Attribute_Id (Attribute_Name (Expr)) = | |
1e5359c0 | 4539 | Attribute_Loop_Entry |
4540 | or else | |
4541 | Get_Attribute_Id (Attribute_Name (Expr)) = | |
4542 | Attribute_Image)) | |
a62147e6 | 4543 | |
4544 | or else Nkind (Expr) = N_Op_Concat; | |
9ac24568 | 4545 | end Is_Subpath_Expression; |
4546 | ||
d087b9ca | 4547 | --------------------------- |
4548 | -- Is_Traversal_Function -- | |
4549 | --------------------------- | |
faee1e18 | 4550 | |
d087b9ca | 4551 | function Is_Traversal_Function (E : Entity_Id) return Boolean is |
4552 | begin | |
4553 | return Ekind (E) = E_Function | |
faee1e18 | 4554 | |
d087b9ca | 4555 | -- A function is said to be a traversal function if the result type of |
4556 | -- the function is an anonymous access-to-object type, | |
faee1e18 | 4557 | |
d087b9ca | 4558 | and then Is_Anonymous_Access_Type (Etype (E)) |
faee1e18 | 4559 | |
d087b9ca | 4560 | -- the function has at least one formal parameter, |
faee1e18 | 4561 | |
d087b9ca | 4562 | and then Present (First_Formal (E)) |
faee1e18 | 4563 | |
d087b9ca | 4564 | -- and the function's first parameter is of an access type. |
faee1e18 | 4565 | |
1e5359c0 | 4566 | and then Is_Access_Type (Retysp (Etype (First_Formal (E)))); |
d087b9ca | 4567 | end Is_Traversal_Function; |
faee1e18 | 4568 | |
df177848 | 4569 | -------------------------------- |
4570 | -- Is_Traversal_Function_Call -- | |
4571 | -------------------------------- | |
4572 | ||
4573 | function Is_Traversal_Function_Call (Expr : Node_Id) return Boolean is | |
4574 | begin | |
4575 | return Nkind (Expr) = N_Function_Call | |
4576 | and then Present (Get_Called_Entity (Expr)) | |
4577 | and then Is_Traversal_Function (Get_Called_Entity (Expr)); | |
4578 | end Is_Traversal_Function_Call; | |
4579 | ||
d087b9ca | 4580 | ------------------ |
4581 | -- Loop_Of_Exit -- | |
4582 | ------------------ | |
faee1e18 | 4583 | |
d087b9ca | 4584 | function Loop_Of_Exit (N : Node_Id) return Entity_Id is |
4585 | Nam : Node_Id := Name (N); | |
4586 | Stmt : Node_Id := N; | |
4587 | begin | |
4588 | if No (Nam) then | |
4589 | while Present (Stmt) loop | |
4590 | Stmt := Parent (Stmt); | |
4591 | if Nkind (Stmt) = N_Loop_Statement then | |
4592 | Nam := Identifier (Stmt); | |
4593 | exit; | |
4594 | end if; | |
4595 | end loop; | |
4596 | end if; | |
4597 | return Entity (Nam); | |
4598 | end Loop_Of_Exit; | |
faee1e18 | 4599 | |
d087b9ca | 4600 | --------- |
4601 | -- Lub -- | |
4602 | --------- | |
24dca06d | 4603 | |
d087b9ca | 4604 | function Lub (P1, P2 : Perm_Kind) return Perm_Kind is |
4605 | begin | |
4606 | case P1 is | |
4607 | when No_Access => | |
4608 | return P2; | |
4609 | ||
4610 | when Read_Only => | |
4611 | case P2 is | |
4612 | when No_Access | |
4613 | | Read_Only | |
4614 | => | |
4615 | return Read_Only; | |
faee1e18 | 4616 | |
d087b9ca | 4617 | when Write_Perm => |
4618 | return Read_Write; | |
4619 | end case; | |
24dca06d | 4620 | |
d087b9ca | 4621 | when Write_Only => |
4622 | case P2 is | |
4623 | when No_Access | |
4624 | | Write_Only | |
4625 | => | |
4626 | return Write_Only; | |
faee1e18 | 4627 | |
d087b9ca | 4628 | when Read_Perm => |
4629 | return Read_Write; | |
4630 | end case; | |
faee1e18 | 4631 | |
d087b9ca | 4632 | when Read_Write => |
4633 | return Read_Write; | |
4634 | end case; | |
4635 | end Lub; | |
faee1e18 | 4636 | |
d087b9ca | 4637 | --------------- |
4638 | -- Merge_Env -- | |
4639 | --------------- | |
faee1e18 | 4640 | |
d087b9ca | 4641 | procedure Merge_Env (Source : in out Perm_Env; Target : in out Perm_Env) is |
faee1e18 | 4642 | |
d087b9ca | 4643 | -- Local subprograms |
faee1e18 | 4644 | |
d087b9ca | 4645 | procedure Apply_Glb_Tree |
4646 | (A : Perm_Tree_Access; | |
4647 | P : Perm_Kind); | |
faee1e18 | 4648 | |
d087b9ca | 4649 | procedure Merge_Trees |
4650 | (Target : Perm_Tree_Access; | |
4651 | Source : Perm_Tree_Access); | |
24dca06d | 4652 | |
d087b9ca | 4653 | -------------------- |
4654 | -- Apply_Glb_Tree -- | |
4655 | -------------------- | |
faee1e18 | 4656 | |
d087b9ca | 4657 | procedure Apply_Glb_Tree |
4658 | (A : Perm_Tree_Access; | |
4659 | P : Perm_Kind) | |
4660 | is | |
4661 | begin | |
4662 | A.all.Tree.Permission := Glb (Permission (A), P); | |
faee1e18 | 4663 | |
d087b9ca | 4664 | case Kind (A) is |
4665 | when Entire_Object => | |
4666 | A.all.Tree.Children_Permission := | |
4667 | Glb (Children_Permission (A), P); | |
faee1e18 | 4668 | |
d087b9ca | 4669 | when Reference => |
4670 | Apply_Glb_Tree (Get_All (A), P); | |
24dca06d | 4671 | |
d087b9ca | 4672 | when Array_Component => |
4673 | Apply_Glb_Tree (Get_Elem (A), P); | |
faee1e18 | 4674 | |
d087b9ca | 4675 | when Record_Component => |
4676 | declare | |
4677 | Comp : Perm_Tree_Access; | |
4678 | begin | |
4679 | Comp := Perm_Tree_Maps.Get_First (Component (A)); | |
4680 | while Comp /= null loop | |
4681 | Apply_Glb_Tree (Comp, P); | |
4682 | Comp := Perm_Tree_Maps.Get_Next (Component (A)); | |
4683 | end loop; | |
4684 | end; | |
4685 | end case; | |
4686 | end Apply_Glb_Tree; | |
24dca06d | 4687 | |
d087b9ca | 4688 | ----------------- |
4689 | -- Merge_Trees -- | |
4690 | ----------------- | |
24dca06d | 4691 | |
d087b9ca | 4692 | procedure Merge_Trees |
4693 | (Target : Perm_Tree_Access; | |
4694 | Source : Perm_Tree_Access) | |
4695 | is | |
4696 | Perm : constant Perm_Kind := | |
4697 | Glb (Permission (Target), Permission (Source)); | |
24dca06d | 4698 | |
d087b9ca | 4699 | begin |
4700 | pragma Assert (Is_Node_Deep (Target) = Is_Node_Deep (Source)); | |
4701 | Target.all.Tree.Permission := Perm; | |
faee1e18 | 4702 | |
d087b9ca | 4703 | case Kind (Target) is |
4704 | when Entire_Object => | |
4705 | declare | |
4706 | Child_Perm : constant Perm_Kind := | |
4707 | Children_Permission (Target); | |
faee1e18 | 4708 | |
d087b9ca | 4709 | begin |
4710 | case Kind (Source) is | |
4711 | when Entire_Object => | |
4712 | Target.all.Tree.Children_Permission := | |
4713 | Glb (Child_Perm, Children_Permission (Source)); | |
4714 | ||
4715 | when Reference => | |
4716 | Copy_Tree (Source, Target); | |
4717 | Target.all.Tree.Permission := Perm; | |
4718 | Apply_Glb_Tree (Get_All (Target), Child_Perm); | |
4719 | ||
4720 | when Array_Component => | |
4721 | Copy_Tree (Source, Target); | |
4722 | Target.all.Tree.Permission := Perm; | |
4723 | Apply_Glb_Tree (Get_Elem (Target), Child_Perm); | |
4724 | ||
4725 | when Record_Component => | |
4726 | Copy_Tree (Source, Target); | |
4727 | Target.all.Tree.Permission := Perm; | |
4728 | declare | |
4729 | Comp : Perm_Tree_Access; | |
faee1e18 | 4730 | |
d087b9ca | 4731 | begin |
4732 | Comp := | |
4733 | Perm_Tree_Maps.Get_First (Component (Target)); | |
4734 | while Comp /= null loop | |
4735 | -- Apply glb tree on every component subtree | |
4736 | ||
4737 | Apply_Glb_Tree (Comp, Child_Perm); | |
4738 | Comp := Perm_Tree_Maps.Get_Next | |
4739 | (Component (Target)); | |
4740 | end loop; | |
4741 | end; | |
4742 | end case; | |
4743 | end; | |
faee1e18 | 4744 | |
d087b9ca | 4745 | when Reference => |
4746 | case Kind (Source) is | |
4747 | when Entire_Object => | |
4748 | Apply_Glb_Tree (Get_All (Target), | |
4749 | Children_Permission (Source)); | |
faee1e18 | 4750 | |
d087b9ca | 4751 | when Reference => |
4752 | Merge_Trees (Get_All (Target), Get_All (Source)); | |
faee1e18 | 4753 | |
d087b9ca | 4754 | when others => |
4755 | raise Program_Error; | |
faee1e18 | 4756 | |
d087b9ca | 4757 | end case; |
faee1e18 | 4758 | |
d087b9ca | 4759 | when Array_Component => |
4760 | case Kind (Source) is | |
4761 | when Entire_Object => | |
4762 | Apply_Glb_Tree (Get_Elem (Target), | |
4763 | Children_Permission (Source)); | |
faee1e18 | 4764 | |
d087b9ca | 4765 | when Array_Component => |
4766 | Merge_Trees (Get_Elem (Target), Get_Elem (Source)); | |
faee1e18 | 4767 | |
d087b9ca | 4768 | when others => |
4769 | raise Program_Error; | |
faee1e18 | 4770 | |
d087b9ca | 4771 | end case; |
faee1e18 | 4772 | |
d087b9ca | 4773 | when Record_Component => |
4774 | case Kind (Source) is | |
4775 | when Entire_Object => | |
4776 | declare | |
4777 | Child_Perm : constant Perm_Kind := | |
4778 | Children_Permission (Source); | |
faee1e18 | 4779 | |
d087b9ca | 4780 | Comp : Perm_Tree_Access; |
faee1e18 | 4781 | |
d087b9ca | 4782 | begin |
4783 | Comp := Perm_Tree_Maps.Get_First | |
4784 | (Component (Target)); | |
4785 | while Comp /= null loop | |
4786 | -- Apply glb tree on every component subtree | |
4787 | ||
4788 | Apply_Glb_Tree (Comp, Child_Perm); | |
4789 | Comp := | |
4790 | Perm_Tree_Maps.Get_Next (Component (Target)); | |
4791 | end loop; | |
4792 | end; | |
faee1e18 | 4793 | |
d087b9ca | 4794 | when Record_Component => |
4795 | declare | |
4796 | Key_Source : Perm_Tree_Maps.Key_Option; | |
4797 | CompTarget : Perm_Tree_Access; | |
4798 | CompSource : Perm_Tree_Access; | |
faee1e18 | 4799 | |
d087b9ca | 4800 | begin |
4801 | Key_Source := Perm_Tree_Maps.Get_First_Key | |
4802 | (Component (Source)); | |
faee1e18 | 4803 | |
d087b9ca | 4804 | while Key_Source.Present loop |
4805 | CompSource := Perm_Tree_Maps.Get | |
4806 | (Component (Source), Key_Source.K); | |
4807 | CompTarget := Perm_Tree_Maps.Get | |
4808 | (Component (Target), Key_Source.K); | |
faee1e18 | 4809 | |
d087b9ca | 4810 | pragma Assert (CompSource /= null); |
4811 | Merge_Trees (CompTarget, CompSource); | |
faee1e18 | 4812 | |
d087b9ca | 4813 | Key_Source := Perm_Tree_Maps.Get_Next_Key |
4814 | (Component (Source)); | |
4815 | end loop; | |
4816 | end; | |
faee1e18 | 4817 | |
d087b9ca | 4818 | when others => |
4819 | raise Program_Error; | |
4820 | end case; | |
4821 | end case; | |
4822 | end Merge_Trees; | |
faee1e18 | 4823 | |
d087b9ca | 4824 | -- Local variables |
faee1e18 | 4825 | |
d087b9ca | 4826 | CompTarget : Perm_Tree_Access; |
4827 | CompSource : Perm_Tree_Access; | |
4828 | KeyTarget : Perm_Tree_Maps.Key_Option; | |
faee1e18 | 4829 | |
d087b9ca | 4830 | -- Start of processing for Merge_Env |
faee1e18 | 4831 | |
d087b9ca | 4832 | begin |
4833 | KeyTarget := Get_First_Key (Target); | |
4834 | -- Iterate over every tree of the environment in the target, and merge | |
4835 | -- it with the source if there is such a similar one that exists. If | |
4836 | -- there is none, then skip. | |
4837 | while KeyTarget.Present loop | |
faee1e18 | 4838 | |
d087b9ca | 4839 | CompSource := Get (Source, KeyTarget.K); |
4840 | CompTarget := Get (Target, KeyTarget.K); | |
faee1e18 | 4841 | |
d087b9ca | 4842 | pragma Assert (CompTarget /= null); |
faee1e18 | 4843 | |
d087b9ca | 4844 | if CompSource /= null then |
4845 | Merge_Trees (CompTarget, CompSource); | |
4846 | Remove (Source, KeyTarget.K); | |
4847 | end if; | |
faee1e18 | 4848 | |
d087b9ca | 4849 | KeyTarget := Get_Next_Key (Target); |
4850 | end loop; | |
faee1e18 | 4851 | |
d087b9ca | 4852 | -- Iterate over every tree of the environment of the source. And merge |
4853 | -- again. If there is not any tree of the target then just copy the tree | |
4854 | -- from source to target. | |
4855 | declare | |
4856 | KeySource : Perm_Tree_Maps.Key_Option; | |
4857 | begin | |
4858 | KeySource := Get_First_Key (Source); | |
4859 | while KeySource.Present loop | |
faee1e18 | 4860 | |
d087b9ca | 4861 | CompSource := Get (Source, KeySource.K); |
4862 | CompTarget := Get (Target, KeySource.K); | |
faee1e18 | 4863 | |
d087b9ca | 4864 | if CompTarget = null then |
4865 | CompTarget := new Perm_Tree_Wrapper'(CompSource.all); | |
4866 | Copy_Tree (CompSource, CompTarget); | |
4867 | Set (Target, KeySource.K, CompTarget); | |
4868 | else | |
4869 | Merge_Trees (CompTarget, CompSource); | |
4870 | end if; | |
faee1e18 | 4871 | |
d087b9ca | 4872 | KeySource := Get_Next_Key (Source); |
4873 | end loop; | |
4874 | end; | |
faee1e18 | 4875 | |
d087b9ca | 4876 | Free_Env (Source); |
4877 | end Merge_Env; | |
faee1e18 | 4878 | |
4879 | ---------------- | |
24dca06d | 4880 | -- Perm_Error -- |
faee1e18 | 4881 | ---------------- |
4882 | ||
24dca06d | 4883 | procedure Perm_Error |
d087b9ca | 4884 | (N : Node_Id; |
4885 | Perm : Perm_Kind; | |
4886 | Found_Perm : Perm_Kind; | |
5bc0f990 | 4887 | Expl : Node_Id; |
d087b9ca | 4888 | Forbidden_Perm : Boolean := False) |
faee1e18 | 4889 | is |
24dca06d | 4890 | procedure Set_Root_Object |
4891 | (Path : Node_Id; | |
4892 | Obj : out Entity_Id; | |
4893 | Deref : out Boolean); | |
4894 | -- Set the root object Obj, and whether the path contains a dereference, | |
4895 | -- from a path Path. | |
faee1e18 | 4896 | |
24dca06d | 4897 | --------------------- |
4898 | -- Set_Root_Object -- | |
4899 | --------------------- | |
faee1e18 | 4900 | |
24dca06d | 4901 | procedure Set_Root_Object |
4902 | (Path : Node_Id; | |
4903 | Obj : out Entity_Id; | |
4904 | Deref : out Boolean) | |
4905 | is | |
4906 | begin | |
4907 | case Nkind (Path) is | |
4908 | when N_Identifier | |
4909 | | N_Expanded_Name | |
4910 | => | |
4911 | Obj := Entity (Path); | |
4912 | Deref := False; | |
faee1e18 | 4913 | |
24dca06d | 4914 | when N_Type_Conversion |
4915 | | N_Unchecked_Type_Conversion | |
4916 | | N_Qualified_Expression | |
4917 | => | |
4918 | Set_Root_Object (Expression (Path), Obj, Deref); | |
faee1e18 | 4919 | |
24dca06d | 4920 | when N_Indexed_Component |
4921 | | N_Selected_Component | |
4922 | | N_Slice | |
4923 | => | |
4924 | Set_Root_Object (Prefix (Path), Obj, Deref); | |
faee1e18 | 4925 | |
24dca06d | 4926 | when N_Explicit_Dereference => |
4927 | Set_Root_Object (Prefix (Path), Obj, Deref); | |
4928 | Deref := True; | |
faee1e18 | 4929 | |
24dca06d | 4930 | when others => |
4931 | raise Program_Error; | |
4932 | end case; | |
4933 | end Set_Root_Object; | |
4934 | -- Local variables | |
faee1e18 | 4935 | |
24dca06d | 4936 | Root : Entity_Id; |
4937 | Is_Deref : Boolean; | |
faee1e18 | 4938 | |
24dca06d | 4939 | -- Start of processing for Perm_Error |
faee1e18 | 4940 | |
24dca06d | 4941 | begin |
4942 | Set_Root_Object (N, Root, Is_Deref); | |
faee1e18 | 4943 | |
1e5359c0 | 4944 | if Emit_Messages then |
4945 | if Is_Deref then | |
4946 | Error_Msg_NE | |
4947 | ("insufficient permission on dereference from &", N, Root); | |
4948 | else | |
4949 | Error_Msg_NE ("insufficient permission for &", N, Root); | |
4950 | end if; | |
faee1e18 | 4951 | |
1e5359c0 | 4952 | Perm_Mismatch (N, Perm, Found_Perm, Expl, Forbidden_Perm); |
4953 | end if; | |
faee1e18 | 4954 | end Perm_Error; |
4955 | ||
4956 | ------------------------------- | |
4957 | -- Perm_Error_Subprogram_End -- | |
4958 | ------------------------------- | |
4959 | ||
4960 | procedure Perm_Error_Subprogram_End | |
4961 | (E : Entity_Id; | |
4962 | Subp : Entity_Id; | |
4963 | Perm : Perm_Kind; | |
5bc0f990 | 4964 | Found_Perm : Perm_Kind; |
4965 | Expl : Node_Id) | |
faee1e18 | 4966 | is |
4967 | begin | |
1e5359c0 | 4968 | if Emit_Messages then |
4969 | Error_Msg_Node_2 := Subp; | |
4970 | Error_Msg_NE ("insufficient permission for & when returning from &", | |
4971 | Subp, E); | |
4972 | Perm_Mismatch (Subp, Perm, Found_Perm, Expl); | |
4973 | end if; | |
faee1e18 | 4974 | end Perm_Error_Subprogram_End; |
4975 | ||
4976 | ------------------ | |
4977 | -- Process_Path -- | |
4978 | ------------------ | |
4979 | ||
d087b9ca | 4980 | procedure Process_Path (Expr : Node_Id; Mode : Checking_Mode) is |
df177848 | 4981 | |
4982 | procedure Check_Not_Borrowed (Expr : Node_Id; Root : Entity_Id); | |
4983 | -- Check expression Expr originating in Root was not borrowed | |
4984 | ||
4985 | procedure Check_Not_Observed (Expr : Node_Id; Root : Entity_Id); | |
4986 | -- Check expression Expr originating in Root was not observed | |
4987 | ||
4988 | ------------------------ | |
4989 | -- Check_Not_Borrowed -- | |
4990 | ------------------------ | |
4991 | ||
4992 | procedure Check_Not_Borrowed (Expr : Node_Id; Root : Entity_Id) is | |
4993 | begin | |
4994 | -- An expression without root object cannot be borrowed | |
4995 | ||
4996 | if No (Root) then | |
4997 | return; | |
4998 | end if; | |
4999 | ||
5000 | -- Otherwise, try to match the expression with one of the borrowed | |
5001 | -- expressions. | |
5002 | ||
5003 | declare | |
5004 | Key : Variable_Maps.Key_Option := | |
5005 | Get_First_Key (Current_Borrowers); | |
5006 | Var : Entity_Id; | |
5007 | Borrowed : Node_Id; | |
a6f3144f | 5008 | B_Pledge : Entity_Id := Empty; |
df177848 | 5009 | |
5010 | begin | |
a6f3144f | 5011 | -- Search for a call to a pledge function or a global pragma in |
5012 | -- the parents of Expr. | |
5013 | ||
5014 | declare | |
5015 | Call : Node_Id := Expr; | |
5016 | begin | |
5017 | while Present (Call) | |
5018 | and then | |
5019 | (Nkind (Call) /= N_Function_Call | |
5020 | or else not Is_Pledge_Function (Get_Called_Entity (Call))) | |
5021 | loop | |
5022 | -- Do not check for borrowed objects in global contracts | |
5023 | -- ??? However we should keep these objects in the borrowed | |
5024 | -- state when verifying the subprogram so that we can make | |
5025 | -- sure that they are only read inside pledges. | |
5026 | -- ??? There is probably a better way to disable checking of | |
5027 | -- borrows inside global contracts. | |
5028 | ||
5029 | if Nkind (Call) = N_Pragma | |
5030 | and then Get_Pragma_Id (Pragma_Name (Call)) = Pragma_Global | |
5031 | then | |
5032 | return; | |
5033 | end if; | |
5034 | ||
5035 | Call := Parent (Call); | |
5036 | end loop; | |
5037 | ||
5038 | if Present (Call) | |
5039 | and then Nkind (First_Actual (Call)) in N_Has_Entity | |
5040 | then | |
5041 | B_Pledge := Entity (First_Actual (Call)); | |
5042 | end if; | |
5043 | end; | |
5044 | ||
df177848 | 5045 | while Key.Present loop |
5046 | Var := Key.K; | |
5047 | Borrowed := Get (Current_Borrowers, Var); | |
5048 | ||
1e5359c0 | 5049 | if Is_Prefix_Or_Almost (Pref => Borrowed, Expr => Expr) |
a6f3144f | 5050 | and then Var /= B_Pledge |
1e5359c0 | 5051 | and then Emit_Messages |
5052 | then | |
df177848 | 5053 | Error_Msg_Sloc := Sloc (Borrowed); |
5bc0f990 | 5054 | Error_Msg_N ("object was borrowed #", Expr); |
df177848 | 5055 | end if; |
5056 | ||
5057 | Key := Get_Next_Key (Current_Borrowers); | |
5058 | end loop; | |
5059 | end; | |
5060 | end Check_Not_Borrowed; | |
5061 | ||
5062 | ------------------------ | |
5063 | -- Check_Not_Observed -- | |
5064 | ------------------------ | |
5065 | ||
5066 | procedure Check_Not_Observed (Expr : Node_Id; Root : Entity_Id) is | |
5067 | begin | |
5068 | -- An expression without root object cannot be observed | |
5069 | ||
5070 | if No (Root) then | |
5071 | return; | |
5072 | end if; | |
5073 | ||
5074 | -- Otherwise, try to match the expression with one of the observed | |
5075 | -- expressions. | |
5076 | ||
5077 | declare | |
5078 | Key : Variable_Maps.Key_Option := | |
5079 | Get_First_Key (Current_Observers); | |
5080 | Var : Entity_Id; | |
5081 | Observed : Node_Id; | |
5082 | ||
5083 | begin | |
5084 | while Key.Present loop | |
5085 | Var := Key.K; | |
5086 | Observed := Get (Current_Observers, Var); | |
5087 | ||
1e5359c0 | 5088 | if Is_Prefix_Or_Almost (Pref => Observed, Expr => Expr) |
5089 | and then Emit_Messages | |
5090 | then | |
df177848 | 5091 | Error_Msg_Sloc := Sloc (Observed); |
5bc0f990 | 5092 | Error_Msg_N ("object was observed #", Expr); |
df177848 | 5093 | end if; |
5094 | ||
5095 | Key := Get_Next_Key (Current_Observers); | |
5096 | end loop; | |
5097 | end; | |
5098 | end Check_Not_Observed; | |
5099 | ||
5100 | -- Local variables | |
5101 | ||
d087b9ca | 5102 | Expr_Type : constant Entity_Id := Etype (Expr); |
5103 | Root : Entity_Id := Get_Root_Object (Expr); | |
f8e6d133 | 5104 | Perm : Perm_Kind_Option; |
d087b9ca | 5105 | |
df177848 | 5106 | -- Start of processing for Process_Path |
5107 | ||
5abc6fac | 5108 | begin |
d087b9ca | 5109 | -- Nothing to do if the root type is not deep, or the path is not rooted |
5110 | -- in an object. | |
faee1e18 | 5111 | |
d087b9ca | 5112 | if not Present (Root) |
c7ff8e7a | 5113 | or else not Is_Object (Root) |
d087b9ca | 5114 | or else not Is_Deep (Etype (Root)) |
faee1e18 | 5115 | then |
5116 | return; | |
5117 | end if; | |
5118 | ||
d087b9ca | 5119 | -- Identify the root type for the path |
faee1e18 | 5120 | |
d087b9ca | 5121 | Root := Unique_Entity (Root); |
faee1e18 | 5122 | |
f8e6d133 | 5123 | -- Except during elaboration, the root object should have been declared |
5124 | -- and entered into the current permission environment. | |
faee1e18 | 5125 | |
f8e6d133 | 5126 | if not Inside_Elaboration |
5127 | and then Get (Current_Perm_Env, Root) = null | |
5128 | then | |
9ac24568 | 5129 | Illegal_Global_Usage (Expr, Root); |
d087b9ca | 5130 | end if; |
faee1e18 | 5131 | |
f8e6d133 | 5132 | -- During elaboration, only the validity of operations is checked, no |
5133 | -- need to compute the permission of Expr. | |
5134 | ||
5135 | if Inside_Elaboration then | |
5136 | Perm := None; | |
5137 | else | |
5138 | Perm := Get_Perm (Expr); | |
5139 | end if; | |
faee1e18 | 5140 | |
df177848 | 5141 | -- Check permissions |
5142 | ||
d087b9ca | 5143 | case Mode is |
faee1e18 | 5144 | |
d087b9ca | 5145 | when Read => |
faee1e18 | 5146 | |
d087b9ca | 5147 | -- No checking needed during elaboration |
faee1e18 | 5148 | |
d087b9ca | 5149 | if Inside_Elaboration then |
24dca06d | 5150 | return; |
5151 | end if; | |
faee1e18 | 5152 | |
d087b9ca | 5153 | -- Check path is readable |
faee1e18 | 5154 | |
d087b9ca | 5155 | if Perm not in Read_Perm then |
5bc0f990 | 5156 | Perm_Error (Expr, Read_Only, Perm, Expl => Get_Expl (Expr)); |
d087b9ca | 5157 | return; |
5158 | end if; | |
faee1e18 | 5159 | |
d087b9ca | 5160 | when Move => |
faee1e18 | 5161 | |
d087b9ca | 5162 | -- Forbidden on deep path during elaboration, otherwise no |
5163 | -- checking needed. | |
faee1e18 | 5164 | |
d087b9ca | 5165 | if Inside_Elaboration then |
5166 | if Is_Deep (Expr_Type) | |
5167 | and then not Inside_Procedure_Call | |
5168 | and then Present (Get_Root_Object (Expr)) | |
1e5359c0 | 5169 | and then Emit_Messages |
d087b9ca | 5170 | then |
5171 | Error_Msg_N ("illegal move during elaboration", Expr); | |
faee1e18 | 5172 | end if; |
5173 | ||
d087b9ca | 5174 | return; |
5175 | end if; | |
faee1e18 | 5176 | |
d087b9ca | 5177 | -- For deep path, check RW permission, otherwise R permission |
faee1e18 | 5178 | |
d087b9ca | 5179 | if not Is_Deep (Expr_Type) then |
5180 | if Perm not in Read_Perm then | |
5bc0f990 | 5181 | Perm_Error (Expr, Read_Only, Perm, Expl => Get_Expl (Expr)); |
d087b9ca | 5182 | end if; |
5183 | return; | |
5184 | end if; | |
faee1e18 | 5185 | |
d087b9ca | 5186 | -- SPARK RM 3.10(1): At the point of a move operation the state of |
5187 | -- the source object (if any) shall be Unrestricted. | |
faee1e18 | 5188 | |
d087b9ca | 5189 | if Perm /= Read_Write then |
5bc0f990 | 5190 | Perm_Error (Expr, Read_Write, Perm, Expl => Get_Expl (Expr)); |
24dca06d | 5191 | return; |
5192 | end if; | |
faee1e18 | 5193 | |
d087b9ca | 5194 | when Assign => |
faee1e18 | 5195 | |
d087b9ca | 5196 | -- No checking needed during elaboration |
faee1e18 | 5197 | |
d087b9ca | 5198 | if Inside_Elaboration then |
5199 | return; | |
5200 | end if; | |
faee1e18 | 5201 | |
d087b9ca | 5202 | -- For assignment, check W permission |
faee1e18 | 5203 | |
d087b9ca | 5204 | if Perm not in Write_Perm then |
5bc0f990 | 5205 | Perm_Error (Expr, Write_Only, Perm, Expl => Get_Expl (Expr)); |
d087b9ca | 5206 | return; |
5207 | end if; | |
faee1e18 | 5208 | |
d087b9ca | 5209 | when Borrow => |
5210 | ||
583f62e9 | 5211 | -- Forbidden during elaboration, an error is already issued in |
5212 | -- Check_Declaration, just return. | |
d087b9ca | 5213 | |
5214 | if Inside_Elaboration then | |
d087b9ca | 5215 | return; |
5216 | end if; | |
faee1e18 | 5217 | |
d087b9ca | 5218 | -- For borrowing, check RW permission |
faee1e18 | 5219 | |
d087b9ca | 5220 | if Perm /= Read_Write then |
5bc0f990 | 5221 | Perm_Error (Expr, Read_Write, Perm, Expl => Get_Expl (Expr)); |
d087b9ca | 5222 | return; |
5223 | end if; | |
24dca06d | 5224 | |
d087b9ca | 5225 | when Observe => |
faee1e18 | 5226 | |
583f62e9 | 5227 | -- Forbidden during elaboration, an error is already issued in |
5228 | -- Check_Declaration, just return. | |
24dca06d | 5229 | |
d087b9ca | 5230 | if Inside_Elaboration then |
d087b9ca | 5231 | return; |
5232 | end if; | |
faee1e18 | 5233 | |
d087b9ca | 5234 | -- For borrowing, check R permission |
5235 | ||
5236 | if Perm not in Read_Perm then | |
5bc0f990 | 5237 | Perm_Error (Expr, Read_Only, Perm, Expl => Get_Expl (Expr)); |
d087b9ca | 5238 | return; |
5239 | end if; | |
df177848 | 5240 | end case; |
faee1e18 | 5241 | |
df177848 | 5242 | -- Check path was not borrowed |
faee1e18 | 5243 | |
df177848 | 5244 | Check_Not_Borrowed (Expr, Root); |
5245 | ||
5246 | -- For modes that require W permission, check path was not observed | |
5247 | ||
5248 | case Mode is | |
5249 | when Read | Observe => | |
5250 | null; | |
5251 | when Assign | Move | Borrow => | |
5252 | Check_Not_Observed (Expr, Root); | |
5253 | end case; | |
5254 | ||
5255 | -- Do not update permission environment when handling calls | |
5256 | ||
5257 | if Inside_Procedure_Call then | |
5258 | return; | |
5259 | end if; | |
5260 | ||
5261 | -- Update the permissions | |
5262 | ||
5263 | case Mode is | |
5264 | ||
5265 | when Read => | |
5266 | null; | |
5267 | ||
5268 | when Move => | |
5269 | ||
5270 | -- SPARK RM 3.10(1): After a move operation, the state of the | |
5271 | -- source object (if any) becomes Moved. | |
5272 | ||
5273 | if Present (Get_Root_Object (Expr)) then | |
5274 | declare | |
5275 | Tree : constant Perm_Tree_Access := | |
5bc0f990 | 5276 | Set_Perm_Prefixes (Expr, Write_Only, Expl => Expr); |
df177848 | 5277 | begin |
5278 | pragma Assert (Tree /= null); | |
5bc0f990 | 5279 | Set_Perm_Extensions_Move (Tree, Etype (Expr), Expl => Expr); |
df177848 | 5280 | end; |
5281 | end if; | |
5282 | ||
5283 | when Assign => | |
5284 | ||
5285 | -- If there is no root object, or the tree has an array component, | |
5286 | -- then the permissions do not get modified by the assignment. | |
5287 | ||
5288 | if No (Get_Root_Object (Expr)) | |
5289 | or else Has_Array_Component (Expr) | |
5290 | then | |
d087b9ca | 5291 | return; |
5292 | end if; | |
5293 | ||
df177848 | 5294 | -- Set permission RW for the path and its extensions |
5295 | ||
5296 | declare | |
5297 | Tree : constant Perm_Tree_Access := Get_Perm_Tree (Expr); | |
5298 | begin | |
5299 | Tree.all.Tree.Permission := Read_Write; | |
5bc0f990 | 5300 | Set_Perm_Extensions (Tree, Read_Write, Expl => Expr); |
df177848 | 5301 | |
5302 | -- Normalize the permission tree | |
5303 | ||
5304 | Set_Perm_Prefixes_Assign (Expr); | |
5305 | end; | |
5306 | ||
f8e6d133 | 5307 | -- Borrowing and observing of paths is handled by the variables |
5308 | -- Current_Borrowers and Current_Observers. | |
d087b9ca | 5309 | |
f8e6d133 | 5310 | when Borrow | Observe => |
5311 | null; | |
d087b9ca | 5312 | end case; |
5313 | end Process_Path; | |
faee1e18 | 5314 | |
24dca06d | 5315 | -------------------- |
5316 | -- Return_Globals -- | |
5317 | -------------------- | |
faee1e18 | 5318 | |
24dca06d | 5319 | procedure Return_Globals (Subp : Entity_Id) is |
faee1e18 | 5320 | |
d087b9ca | 5321 | procedure Return_Global |
5322 | (Expr : Node_Id; | |
df177848 | 5323 | Typ : Entity_Id; |
5324 | Kind : Formal_Kind; | |
d087b9ca | 5325 | Subp : Entity_Id; |
5326 | Global_Var : Boolean); | |
5327 | -- Proxy procedure to return globals, to adjust for the type of first | |
5328 | -- parameter expected by Return_Parameter_Or_Global. | |
faee1e18 | 5329 | |
d087b9ca | 5330 | ------------------- |
5331 | -- Return_Global -- | |
5332 | ------------------- | |
faee1e18 | 5333 | |
d087b9ca | 5334 | procedure Return_Global |
5335 | (Expr : Node_Id; | |
df177848 | 5336 | Typ : Entity_Id; |
5337 | Kind : Formal_Kind; | |
d087b9ca | 5338 | Subp : Entity_Id; |
5339 | Global_Var : Boolean) | |
24dca06d | 5340 | is |
24dca06d | 5341 | begin |
d087b9ca | 5342 | Return_Parameter_Or_Global |
df177848 | 5343 | (Id => Entity (Expr), |
5344 | Typ => Typ, | |
5345 | Kind => Kind, | |
5346 | Subp => Subp, | |
5347 | Global_Var => Global_Var); | |
d087b9ca | 5348 | end Return_Global; |
faee1e18 | 5349 | |
d087b9ca | 5350 | procedure Return_Globals_Inst is new Handle_Globals (Return_Global); |
faee1e18 | 5351 | |
24dca06d | 5352 | -- Start of processing for Return_Globals |
faee1e18 | 5353 | |
24dca06d | 5354 | begin |
d087b9ca | 5355 | Return_Globals_Inst (Subp); |
24dca06d | 5356 | end Return_Globals; |
faee1e18 | 5357 | |
24dca06d | 5358 | -------------------------------- |
5359 | -- Return_Parameter_Or_Global -- | |
5360 | -------------------------------- | |
faee1e18 | 5361 | |
d087b9ca | 5362 | procedure Return_Parameter_Or_Global |
5363 | (Id : Entity_Id; | |
df177848 | 5364 | Typ : Entity_Id; |
5365 | Kind : Formal_Kind; | |
d087b9ca | 5366 | Subp : Entity_Id; |
5367 | Global_Var : Boolean) | |
24dca06d | 5368 | is |
24dca06d | 5369 | begin |
d087b9ca | 5370 | -- Shallow parameters and globals need not be considered |
5371 | ||
5372 | if not Is_Deep (Typ) then | |
5373 | return; | |
faee1e18 | 5374 | |
df177848 | 5375 | elsif Kind = E_In_Parameter then |
faee1e18 | 5376 | |
d087b9ca | 5377 | -- Input global variables are observed only |
faee1e18 | 5378 | |
d087b9ca | 5379 | if Global_Var then |
5380 | return; | |
5381 | ||
5382 | -- Anonymous access to constant is an observe | |
5383 | ||
5384 | elsif Is_Anonymous_Access_Type (Typ) | |
5385 | and then Is_Access_Constant (Typ) | |
24dca06d | 5386 | then |
d087b9ca | 5387 | return; |
5388 | ||
5389 | -- Deep types other than access types define an observe | |
5390 | ||
5391 | elsif not Is_Access_Type (Typ) then | |
5392 | return; | |
24dca06d | 5393 | end if; |
d087b9ca | 5394 | end if; |
faee1e18 | 5395 | |
d087b9ca | 5396 | -- All other parameters and globals should return with mode RW to the |
5397 | -- caller. | |
faee1e18 | 5398 | |
d087b9ca | 5399 | declare |
5400 | Tree : constant Perm_Tree_Access := Get (Current_Perm_Env, Id); | |
5401 | begin | |
5402 | if Permission (Tree) /= Read_Write then | |
24dca06d | 5403 | Perm_Error_Subprogram_End |
5404 | (E => Id, | |
5405 | Subp => Subp, | |
d087b9ca | 5406 | Perm => Read_Write, |
5bc0f990 | 5407 | Found_Perm => Permission (Tree), |
5408 | Expl => Explanation (Tree)); | |
24dca06d | 5409 | end if; |
d087b9ca | 5410 | end; |
5411 | end Return_Parameter_Or_Global; | |
5412 | ||
5413 | ----------------------- | |
5414 | -- Return_Parameters -- | |
5415 | ----------------------- | |
5416 | ||
5417 | procedure Return_Parameters (Subp : Entity_Id) is | |
5418 | Formal : Entity_Id; | |
5419 | begin | |
5420 | Formal := First_Formal (Subp); | |
5421 | while Present (Formal) loop | |
df177848 | 5422 | Return_Parameter_Or_Global |
5423 | (Id => Formal, | |
1e5359c0 | 5424 | Typ => Retysp (Etype (Formal)), |
df177848 | 5425 | Kind => Ekind (Formal), |
5426 | Subp => Subp, | |
5427 | Global_Var => False); | |
d087b9ca | 5428 | Next_Formal (Formal); |
5429 | end loop; | |
5430 | end Return_Parameters; | |
faee1e18 | 5431 | |
24dca06d | 5432 | ------------------------- |
5433 | -- Set_Perm_Extensions -- | |
5434 | ------------------------- | |
faee1e18 | 5435 | |
5bc0f990 | 5436 | procedure Set_Perm_Extensions |
5437 | (T : Perm_Tree_Access; | |
5438 | P : Perm_Kind; | |
5439 | Expl : Node_Id) is | |
d087b9ca | 5440 | |
24dca06d | 5441 | procedure Free_Perm_Tree_Children (T : Perm_Tree_Access); |
d087b9ca | 5442 | -- Free the permission tree of children if any, prio to replacing T |
5443 | ||
5444 | ----------------------------- | |
5445 | -- Free_Perm_Tree_Children -- | |
5446 | ----------------------------- | |
5447 | ||
24dca06d | 5448 | procedure Free_Perm_Tree_Children (T : Perm_Tree_Access) is |
5449 | begin | |
5450 | case Kind (T) is | |
5451 | when Entire_Object => | |
5452 | null; | |
faee1e18 | 5453 | |
24dca06d | 5454 | when Reference => |
d087b9ca | 5455 | Free_Tree (T.all.Tree.Get_All); |
faee1e18 | 5456 | |
24dca06d | 5457 | when Array_Component => |
d087b9ca | 5458 | Free_Tree (T.all.Tree.Get_Elem); |
faee1e18 | 5459 | |
24dca06d | 5460 | when Record_Component => |
5461 | declare | |
d087b9ca | 5462 | Hashtbl : Perm_Tree_Maps.Instance := Component (T); |
5463 | Comp : Perm_Tree_Access; | |
faee1e18 | 5464 | |
24dca06d | 5465 | begin |
d087b9ca | 5466 | Comp := Perm_Tree_Maps.Get_First (Hashtbl); |
24dca06d | 5467 | while Comp /= null loop |
d087b9ca | 5468 | Free_Tree (Comp); |
5469 | Comp := Perm_Tree_Maps.Get_Next (Hashtbl); | |
24dca06d | 5470 | end loop; |
faee1e18 | 5471 | |
d087b9ca | 5472 | Perm_Tree_Maps.Reset (Hashtbl); |
24dca06d | 5473 | end; |
5474 | end case; | |
5475 | end Free_Perm_Tree_Children; | |
faee1e18 | 5476 | |
d087b9ca | 5477 | -- Start of processing for Set_Perm_Extensions |
faee1e18 | 5478 | |
24dca06d | 5479 | begin |
5480 | Free_Perm_Tree_Children (T); | |
d087b9ca | 5481 | T.all.Tree := Perm_Tree'(Kind => Entire_Object, |
5482 | Is_Node_Deep => Is_Node_Deep (T), | |
5bc0f990 | 5483 | Explanation => Expl, |
d087b9ca | 5484 | Permission => Permission (T), |
5485 | Children_Permission => P); | |
24dca06d | 5486 | end Set_Perm_Extensions; |
faee1e18 | 5487 | |
24dca06d | 5488 | ------------------------------ |
d087b9ca | 5489 | -- Set_Perm_Extensions_Move -- |
24dca06d | 5490 | ------------------------------ |
faee1e18 | 5491 | |
d087b9ca | 5492 | procedure Set_Perm_Extensions_Move |
5bc0f990 | 5493 | (T : Perm_Tree_Access; |
5494 | E : Entity_Id; | |
5495 | Expl : Node_Id) | |
faee1e18 | 5496 | is |
1e5359c0 | 5497 | Check_Ty : constant Entity_Id := Retysp (E); |
faee1e18 | 5498 | begin |
d087b9ca | 5499 | -- Shallow extensions are set to RW |
b11290d7 | 5500 | |
d087b9ca | 5501 | if not Is_Node_Deep (T) then |
5bc0f990 | 5502 | Set_Perm_Extensions (T, Read_Write, Expl => Expl); |
d087b9ca | 5503 | return; |
5504 | end if; | |
24dca06d | 5505 | |
d087b9ca | 5506 | -- Deep extensions are set to W before .all and NO afterwards |
faee1e18 | 5507 | |
d087b9ca | 5508 | T.all.Tree.Permission := Write_Only; |
faee1e18 | 5509 | |
d087b9ca | 5510 | case T.all.Tree.Kind is |
faee1e18 | 5511 | |
d087b9ca | 5512 | -- For a folded tree of composite type, unfold the tree for better |
5513 | -- precision. | |
faee1e18 | 5514 | |
d087b9ca | 5515 | when Entire_Object => |
1e5359c0 | 5516 | case Ekind (Check_Ty) is |
d087b9ca | 5517 | when E_Array_Type |
5518 | | E_Array_Subtype | |
5519 | => | |
faee1e18 | 5520 | declare |
d087b9ca | 5521 | C : constant Perm_Tree_Access := |
5522 | new Perm_Tree_Wrapper' | |
5523 | (Tree => | |
5524 | (Kind => Entire_Object, | |
5525 | Is_Node_Deep => Is_Node_Deep (T), | |
5bc0f990 | 5526 | Explanation => Expl, |
d087b9ca | 5527 | Permission => Read_Write, |
5528 | Children_Permission => Read_Write)); | |
faee1e18 | 5529 | begin |
1e5359c0 | 5530 | Set_Perm_Extensions_Move |
5531 | (C, Component_Type (Check_Ty), Expl); | |
d087b9ca | 5532 | T.all.Tree := (Kind => Array_Component, |
5533 | Is_Node_Deep => Is_Node_Deep (T), | |
5bc0f990 | 5534 | Explanation => Expl, |
d087b9ca | 5535 | Permission => Write_Only, |
5536 | Get_Elem => C); | |
faee1e18 | 5537 | end; |
24dca06d | 5538 | |
d087b9ca | 5539 | when Record_Kind => |
faee1e18 | 5540 | declare |
d087b9ca | 5541 | C : Perm_Tree_Access; |
5542 | Comp : Entity_Id; | |
faee1e18 | 5543 | Hashtbl : Perm_Tree_Maps.Instance; |
5544 | ||
faee1e18 | 5545 | begin |
1e5359c0 | 5546 | Comp := First_Component_Or_Discriminant (Check_Ty); |
d087b9ca | 5547 | while Present (Comp) loop |
1e5359c0 | 5548 | |
5549 | -- Unfold components which are visible in SPARK | |
5550 | ||
5551 | if Component_Is_Visible_In_SPARK (Comp) then | |
5552 | C := new Perm_Tree_Wrapper' | |
5553 | (Tree => | |
5554 | (Kind => Entire_Object, | |
5555 | Is_Node_Deep => Is_Deep (Etype (Comp)), | |
5556 | Explanation => Expl, | |
5557 | Permission => Read_Write, | |
5558 | Children_Permission => Read_Write)); | |
5559 | Set_Perm_Extensions_Move (C, Etype (Comp), Expl); | |
5560 | ||
5561 | -- Hidden components are never deep | |
5562 | ||
5563 | else | |
5564 | C := new Perm_Tree_Wrapper' | |
5565 | (Tree => | |
5566 | (Kind => Entire_Object, | |
5567 | Is_Node_Deep => False, | |
5568 | Explanation => Expl, | |
5569 | Permission => Read_Write, | |
5570 | Children_Permission => Read_Write)); | |
5571 | Set_Perm_Extensions (C, Read_Write, Expl => Expl); | |
5572 | end if; | |
5573 | ||
5574 | Perm_Tree_Maps.Set | |
5575 | (Hashtbl, Original_Record_Component (Comp), C); | |
d087b9ca | 5576 | Next_Component_Or_Discriminant (Comp); |
faee1e18 | 5577 | end loop; |
faee1e18 | 5578 | |
d087b9ca | 5579 | T.all.Tree := |
5580 | (Kind => Record_Component, | |
5581 | Is_Node_Deep => Is_Node_Deep (T), | |
5bc0f990 | 5582 | Explanation => Expl, |
d087b9ca | 5583 | Permission => Write_Only, |
5584 | Component => Hashtbl); | |
faee1e18 | 5585 | end; |
faee1e18 | 5586 | |
d087b9ca | 5587 | -- Otherwise, extensions are set to NO |
faee1e18 | 5588 | |
d087b9ca | 5589 | when others => |
5bc0f990 | 5590 | Set_Perm_Extensions (T, No_Access, Expl); |
d087b9ca | 5591 | end case; |
faee1e18 | 5592 | |
d087b9ca | 5593 | when Reference => |
5bc0f990 | 5594 | Set_Perm_Extensions (T, No_Access, Expl); |
faee1e18 | 5595 | |
d087b9ca | 5596 | when Array_Component => |
1e5359c0 | 5597 | Set_Perm_Extensions_Move |
5598 | (Get_Elem (T), Component_Type (Check_Ty), Expl); | |
faee1e18 | 5599 | |
d087b9ca | 5600 | when Record_Component => |
faee1e18 | 5601 | declare |
d087b9ca | 5602 | C : Perm_Tree_Access; |
5603 | Comp : Entity_Id; | |
faee1e18 | 5604 | |
d087b9ca | 5605 | begin |
1e5359c0 | 5606 | Comp := First_Component_Or_Discriminant (Check_Ty); |
d087b9ca | 5607 | while Present (Comp) loop |
1e5359c0 | 5608 | C := Perm_Tree_Maps.Get |
5609 | (Component (T), Original_Record_Component (Comp)); | |
d087b9ca | 5610 | pragma Assert (C /= null); |
1e5359c0 | 5611 | |
5612 | -- Move visible components | |
5613 | ||
5614 | if Component_Is_Visible_In_SPARK (Comp) then | |
5615 | Set_Perm_Extensions_Move (C, Etype (Comp), Expl); | |
5616 | ||
5617 | -- Hidden components are never deep | |
5618 | ||
5619 | else | |
5620 | Set_Perm_Extensions (C, Read_Write, Expl => Expl); | |
5621 | end if; | |
5622 | ||
d087b9ca | 5623 | Next_Component_Or_Discriminant (Comp); |
5624 | end loop; | |
5625 | end; | |
faee1e18 | 5626 | end case; |
d087b9ca | 5627 | end Set_Perm_Extensions_Move; |
faee1e18 | 5628 | |
d087b9ca | 5629 | ----------------------- |
5630 | -- Set_Perm_Prefixes -- | |
5631 | ----------------------- | |
faee1e18 | 5632 | |
d087b9ca | 5633 | function Set_Perm_Prefixes |
5634 | (N : Node_Id; | |
5bc0f990 | 5635 | Perm : Perm_Kind_Option; |
5636 | Expl : Node_Id) return Perm_Tree_Access | |
faee1e18 | 5637 | is |
5638 | begin | |
faee1e18 | 5639 | case Nkind (N) is |
d087b9ca | 5640 | when N_Expanded_Name |
5641 | | N_Identifier | |
faee1e18 | 5642 | => |
5643 | declare | |
d087b9ca | 5644 | E : constant Entity_Id := Unique_Entity (Entity (N)); |
5645 | C : constant Perm_Tree_Access := Get (Current_Perm_Env, E); | |
24dca06d | 5646 | pragma Assert (C /= null); |
faee1e18 | 5647 | |
5648 | begin | |
d087b9ca | 5649 | if Perm /= None then |
5650 | C.all.Tree.Permission := Glb (Perm, Permission (C)); | |
5651 | end if; | |
faee1e18 | 5652 | |
faee1e18 | 5653 | return C; |
5654 | end; | |
5655 | ||
a080baa9 | 5656 | -- For a nonterminal path, we set the permission tree of its prefix, |
d087b9ca | 5657 | -- and then we extract from the returned pointer the subtree and |
5658 | -- assign an adequate permission to it, if unfolded. If folded, | |
5659 | -- we unroll the tree one level. | |
faee1e18 | 5660 | |
d087b9ca | 5661 | when N_Explicit_Dereference => |
faee1e18 | 5662 | declare |
5663 | C : constant Perm_Tree_Access := | |
5bc0f990 | 5664 | Set_Perm_Prefixes (Prefix (N), Perm, Expl); |
d087b9ca | 5665 | pragma Assert (C /= null); |
5666 | pragma Assert (Kind (C) = Entire_Object | |
5667 | or else Kind (C) = Reference); | |
faee1e18 | 5668 | begin |
d087b9ca | 5669 | -- The tree is already unfolded. Replace the permission of the |
5670 | -- dereference. | |
24dca06d | 5671 | |
d087b9ca | 5672 | if Kind (C) = Reference then |
5673 | declare | |
5674 | D : constant Perm_Tree_Access := Get_All (C); | |
5675 | pragma Assert (D /= null); | |
faee1e18 | 5676 | |
d087b9ca | 5677 | begin |
5678 | if Perm /= None then | |
5679 | D.all.Tree.Permission := Glb (Perm, Permission (D)); | |
5680 | end if; | |
5681 | ||
5682 | return D; | |
5683 | end; | |
5684 | ||
5685 | -- The tree is folded. Expand it. | |
5686 | ||
5687 | else | |
5688 | declare | |
5689 | pragma Assert (Kind (C) = Entire_Object); | |
5690 | ||
5691 | Child_P : constant Perm_Kind := Children_Permission (C); | |
5692 | D : constant Perm_Tree_Access := | |
5693 | new Perm_Tree_Wrapper' | |
5694 | (Tree => | |
5695 | (Kind => Entire_Object, | |
5696 | Is_Node_Deep => Is_Deep (Etype (N)), | |
5bc0f990 | 5697 | Explanation => Expl, |
d087b9ca | 5698 | Permission => Child_P, |
5699 | Children_Permission => Child_P)); | |
5700 | begin | |
5701 | if Perm /= None then | |
5702 | D.all.Tree.Permission := Perm; | |
5703 | end if; | |
faee1e18 | 5704 | |
d087b9ca | 5705 | C.all.Tree := (Kind => Reference, |
5706 | Is_Node_Deep => Is_Node_Deep (C), | |
5bc0f990 | 5707 | Explanation => Expl, |
d087b9ca | 5708 | Permission => Permission (C), |
5709 | Get_All => D); | |
5710 | return D; | |
5711 | end; | |
5712 | end if; | |
5713 | end; | |
24dca06d | 5714 | |
d087b9ca | 5715 | when N_Selected_Component => |
5716 | declare | |
5717 | C : constant Perm_Tree_Access := | |
5bc0f990 | 5718 | Set_Perm_Prefixes (Prefix (N), Perm, Expl); |
d087b9ca | 5719 | pragma Assert (C /= null); |
faee1e18 | 5720 | pragma Assert (Kind (C) = Entire_Object |
5721 | or else Kind (C) = Record_Component); | |
d087b9ca | 5722 | begin |
5723 | -- The tree is already unfolded. Replace the permission of the | |
5724 | -- component. | |
faee1e18 | 5725 | |
5726 | if Kind (C) = Record_Component then | |
faee1e18 | 5727 | declare |
1e5359c0 | 5728 | Comp : constant Entity_Id := |
5729 | Original_Record_Component | |
5730 | (Entity (Selector_Name (N))); | |
d087b9ca | 5731 | D : constant Perm_Tree_Access := |
5732 | Perm_Tree_Maps.Get (Component (C), Comp); | |
5733 | pragma Assert (D /= null); | |
faee1e18 | 5734 | |
5735 | begin | |
d087b9ca | 5736 | if Perm /= None then |
5737 | D.all.Tree.Permission := Glb (Perm, Permission (D)); | |
faee1e18 | 5738 | end if; |
5739 | ||
d087b9ca | 5740 | return D; |
faee1e18 | 5741 | end; |
24dca06d | 5742 | |
d087b9ca | 5743 | -- The tree is folded. Expand it. |
faee1e18 | 5744 | |
d087b9ca | 5745 | else |
5746 | declare | |
5747 | pragma Assert (Kind (C) = Entire_Object); | |
faee1e18 | 5748 | |
d087b9ca | 5749 | D : Perm_Tree_Access; |
df177848 | 5750 | D_This : Perm_Tree_Access; |
d087b9ca | 5751 | Comp : Node_Id; |
5752 | P : Perm_Kind; | |
5753 | Child_P : constant Perm_Kind := Children_Permission (C); | |
faee1e18 | 5754 | Hashtbl : Perm_Tree_Maps.Instance; |
d087b9ca | 5755 | -- Create an empty hash table |
faee1e18 | 5756 | |
5757 | begin | |
d087b9ca | 5758 | Comp := |
1e5359c0 | 5759 | First_Component_Or_Discriminant |
5760 | (Retysp (Etype (Prefix (N)))); | |
24dca06d | 5761 | |
d087b9ca | 5762 | while Present (Comp) loop |
5763 | if Perm /= None | |
1e5359c0 | 5764 | and then Original_Record_Component (Comp) = |
5765 | Original_Record_Component | |
5766 | (Entity (Selector_Name (N))) | |
d087b9ca | 5767 | then |
5768 | P := Perm; | |
5769 | else | |
5770 | P := Child_P; | |
5771 | end if; | |
faee1e18 | 5772 | |
d087b9ca | 5773 | D := new Perm_Tree_Wrapper' |
faee1e18 | 5774 | (Tree => |
5775 | (Kind => Entire_Object, | |
1e5359c0 | 5776 | Is_Node_Deep => |
5777 | -- Hidden components are never deep | |
5778 | Component_Is_Visible_In_SPARK (Comp) | |
5779 | and then Is_Deep (Etype (Comp)), | |
5bc0f990 | 5780 | Explanation => Expl, |
d087b9ca | 5781 | Permission => P, |
5782 | Children_Permission => Child_P)); | |
1e5359c0 | 5783 | Perm_Tree_Maps.Set |
5784 | (Hashtbl, Original_Record_Component (Comp), D); | |
df177848 | 5785 | |
5786 | -- Store the tree to return for this component | |
5787 | ||
1e5359c0 | 5788 | if Original_Record_Component (Comp) = |
5789 | Original_Record_Component | |
5790 | (Entity (Selector_Name (N))) | |
5791 | then | |
df177848 | 5792 | D_This := D; |
5793 | end if; | |
5794 | ||
d087b9ca | 5795 | Next_Component_Or_Discriminant (Comp); |
faee1e18 | 5796 | end loop; |
5797 | ||
d087b9ca | 5798 | C.all.Tree := (Kind => Record_Component, |
5799 | Is_Node_Deep => Is_Node_Deep (C), | |
5bc0f990 | 5800 | Explanation => Expl, |
d087b9ca | 5801 | Permission => Permission (C), |
5802 | Component => Hashtbl); | |
df177848 | 5803 | return D_This; |
faee1e18 | 5804 | end; |
faee1e18 | 5805 | end if; |
5806 | end; | |
5807 | ||
faee1e18 | 5808 | when N_Indexed_Component |
5809 | | N_Slice | |
5810 | => | |
5811 | declare | |
5812 | C : constant Perm_Tree_Access := | |
5bc0f990 | 5813 | Set_Perm_Prefixes (Prefix (N), Perm, Expl); |
d087b9ca | 5814 | pragma Assert (C /= null); |
faee1e18 | 5815 | pragma Assert (Kind (C) = Entire_Object |
5816 | or else Kind (C) = Array_Component); | |
d087b9ca | 5817 | begin |
5818 | -- The tree is already unfolded. Replace the permission of the | |
5819 | -- component. | |
faee1e18 | 5820 | |
5821 | if Kind (C) = Array_Component then | |
d087b9ca | 5822 | declare |
5823 | D : constant Perm_Tree_Access := Get_Elem (C); | |
5824 | pragma Assert (D /= null); | |
24dca06d | 5825 | |
d087b9ca | 5826 | begin |
5827 | if Perm /= None then | |
5828 | D.all.Tree.Permission := Glb (Perm, Permission (D)); | |
5829 | end if; | |
24dca06d | 5830 | |
d087b9ca | 5831 | return D; |
5832 | end; | |
faee1e18 | 5833 | |
d087b9ca | 5834 | -- The tree is folded. Expand it. |
faee1e18 | 5835 | |
d087b9ca | 5836 | else |
5837 | declare | |
5838 | pragma Assert (Kind (C) = Entire_Object); | |
5839 | ||
5840 | Child_P : constant Perm_Kind := Children_Permission (C); | |
5841 | D : constant Perm_Tree_Access := | |
5842 | new Perm_Tree_Wrapper' | |
5843 | (Tree => | |
5844 | (Kind => Entire_Object, | |
5845 | Is_Node_Deep => Is_Node_Deep (C), | |
5bc0f990 | 5846 | Explanation => Expl, |
d087b9ca | 5847 | Permission => Child_P, |
5848 | Children_Permission => Child_P)); | |
faee1e18 | 5849 | begin |
d087b9ca | 5850 | if Perm /= None then |
5851 | D.all.Tree.Permission := Perm; | |
5852 | end if; | |
faee1e18 | 5853 | |
5854 | C.all.Tree := (Kind => Array_Component, | |
5855 | Is_Node_Deep => Is_Node_Deep (C), | |
5bc0f990 | 5856 | Explanation => Expl, |
d087b9ca | 5857 | Permission => Permission (C), |
5858 | Get_Elem => D); | |
5859 | return D; | |
faee1e18 | 5860 | end; |
faee1e18 | 5861 | end if; |
5862 | end; | |
5863 | ||
d087b9ca | 5864 | when N_Qualified_Expression |
5865 | | N_Type_Conversion | |
5866 | | N_Unchecked_Type_Conversion | |
5867 | => | |
5bc0f990 | 5868 | return Set_Perm_Prefixes (Expression (N), Perm, Expl); |
faee1e18 | 5869 | |
d087b9ca | 5870 | when others => |
5871 | raise Program_Error; | |
5872 | end case; | |
5873 | end Set_Perm_Prefixes; | |
24dca06d | 5874 | |
d087b9ca | 5875 | ------------------------------ |
5876 | -- Set_Perm_Prefixes_Assign -- | |
5877 | ------------------------------ | |
faee1e18 | 5878 | |
d087b9ca | 5879 | procedure Set_Perm_Prefixes_Assign (N : Node_Id) is |
5880 | C : constant Perm_Tree_Access := Get_Perm_Tree (N); | |
faee1e18 | 5881 | |
d087b9ca | 5882 | begin |
5883 | case Kind (C) is | |
5884 | when Entire_Object => | |
5885 | pragma Assert (Children_Permission (C) = Read_Write); | |
5886 | C.all.Tree.Permission := Read_Write; | |
24dca06d | 5887 | |
d087b9ca | 5888 | when Reference => |
5889 | C.all.Tree.Permission := | |
5890 | Lub (Permission (C), Permission (Get_All (C))); | |
faee1e18 | 5891 | |
d087b9ca | 5892 | when Array_Component => |
5893 | null; | |
24dca06d | 5894 | |
d087b9ca | 5895 | when Record_Component => |
5896 | declare | |
5897 | Comp : Perm_Tree_Access; | |
5898 | Perm : Perm_Kind := Read_Write; | |
faee1e18 | 5899 | |
d087b9ca | 5900 | begin |
5901 | -- We take the Glb of all the descendants, and then update the | |
5902 | -- permission of the node with it. | |
24dca06d | 5903 | |
d087b9ca | 5904 | Comp := Perm_Tree_Maps.Get_First (Component (C)); |
5905 | while Comp /= null loop | |
5906 | Perm := Glb (Perm, Permission (Comp)); | |
5907 | Comp := Perm_Tree_Maps.Get_Next (Component (C)); | |
5908 | end loop; | |
faee1e18 | 5909 | |
d087b9ca | 5910 | C.all.Tree.Permission := Lub (Permission (C), Perm); |
5911 | end; | |
5912 | end case; | |
faee1e18 | 5913 | |
d087b9ca | 5914 | case Nkind (N) is |
faee1e18 | 5915 | |
d087b9ca | 5916 | -- Base identifier end recursion |
faee1e18 | 5917 | |
d087b9ca | 5918 | when N_Expanded_Name |
5919 | | N_Identifier | |
5920 | => | |
5921 | null; | |
24dca06d | 5922 | |
d087b9ca | 5923 | when N_Explicit_Dereference |
5924 | | N_Indexed_Component | |
5925 | | N_Selected_Component | |
5926 | | N_Slice | |
5927 | => | |
5928 | Set_Perm_Prefixes_Assign (Prefix (N)); | |
faee1e18 | 5929 | |
d087b9ca | 5930 | when N_Qualified_Expression |
5931 | | N_Type_Conversion | |
5932 | | N_Unchecked_Type_Conversion | |
5933 | => | |
5934 | Set_Perm_Prefixes_Assign (Expression (N)); | |
faee1e18 | 5935 | |
5936 | when others => | |
5937 | raise Program_Error; | |
faee1e18 | 5938 | end case; |
d087b9ca | 5939 | end Set_Perm_Prefixes_Assign; |
faee1e18 | 5940 | |
5941 | ------------------- | |
5942 | -- Setup_Globals -- | |
5943 | ------------------- | |
5944 | ||
5945 | procedure Setup_Globals (Subp : Entity_Id) is | |
faee1e18 | 5946 | |
d087b9ca | 5947 | procedure Setup_Global |
5948 | (Expr : Node_Id; | |
df177848 | 5949 | Typ : Entity_Id; |
5950 | Kind : Formal_Kind; | |
d087b9ca | 5951 | Subp : Entity_Id; |
5952 | Global_Var : Boolean); | |
5953 | -- Proxy procedure to set up globals, to adjust for the type of first | |
5954 | -- parameter expected by Setup_Parameter_Or_Global. | |
faee1e18 | 5955 | |
d087b9ca | 5956 | ------------------ |
5957 | -- Setup_Global -- | |
5958 | ------------------ | |
faee1e18 | 5959 | |
d087b9ca | 5960 | procedure Setup_Global |
5961 | (Expr : Node_Id; | |
df177848 | 5962 | Typ : Entity_Id; |
5963 | Kind : Formal_Kind; | |
d087b9ca | 5964 | Subp : Entity_Id; |
5965 | Global_Var : Boolean) | |
faee1e18 | 5966 | is |
faee1e18 | 5967 | begin |
d087b9ca | 5968 | Setup_Parameter_Or_Global |
df177848 | 5969 | (Id => Entity (Expr), |
5970 | Typ => Typ, | |
5971 | Kind => Kind, | |
5972 | Subp => Subp, | |
5bc0f990 | 5973 | Global_Var => Global_Var, |
5974 | Expl => Expr); | |
d087b9ca | 5975 | end Setup_Global; |
faee1e18 | 5976 | |
d087b9ca | 5977 | procedure Setup_Globals_Inst is new Handle_Globals (Setup_Global); |
faee1e18 | 5978 | |
5979 | -- Start of processing for Setup_Globals | |
5980 | ||
5981 | begin | |
d087b9ca | 5982 | Setup_Globals_Inst (Subp); |
faee1e18 | 5983 | end Setup_Globals; |
5984 | ||
5985 | ------------------------------- | |
5986 | -- Setup_Parameter_Or_Global -- | |
5987 | ------------------------------- | |
5988 | ||
5989 | procedure Setup_Parameter_Or_Global | |
e47eea05 | 5990 | (Id : Entity_Id; |
df177848 | 5991 | Typ : Entity_Id; |
5992 | Kind : Formal_Kind; | |
d087b9ca | 5993 | Subp : Entity_Id; |
5bc0f990 | 5994 | Global_Var : Boolean; |
5995 | Expl : Node_Id) | |
faee1e18 | 5996 | is |
d087b9ca | 5997 | Perm : Perm_Kind_Option; |
faee1e18 | 5998 | |
5999 | begin | |
df177848 | 6000 | case Kind is |
d087b9ca | 6001 | when E_In_Parameter => |
382c4351 | 6002 | |
d087b9ca | 6003 | -- Shallow parameters and globals need not be considered |
faee1e18 | 6004 | |
d087b9ca | 6005 | if not Is_Deep (Typ) then |
6006 | Perm := None; | |
faee1e18 | 6007 | |
d087b9ca | 6008 | -- Inputs of functions have R permission only |
faee1e18 | 6009 | |
d087b9ca | 6010 | elsif Ekind (Subp) = E_Function then |
6011 | Perm := Read_Only; | |
faee1e18 | 6012 | |
d087b9ca | 6013 | -- Input global variables have R permission only |
faee1e18 | 6014 | |
d087b9ca | 6015 | elsif Global_Var then |
6016 | Perm := Read_Only; | |
faee1e18 | 6017 | |
d087b9ca | 6018 | -- Anonymous access to constant is an observe |
6019 | ||
6020 | elsif Is_Anonymous_Access_Type (Typ) | |
6021 | and then Is_Access_Constant (Typ) | |
6022 | then | |
6023 | Perm := Read_Only; | |
6024 | ||
6025 | -- Other access types are a borrow | |
6026 | ||
6027 | elsif Is_Access_Type (Typ) then | |
6028 | Perm := Read_Write; | |
6029 | ||
6030 | -- Deep types other than access types define an observe | |
faee1e18 | 6031 | |
24dca06d | 6032 | else |
d087b9ca | 6033 | Perm := Read_Only; |
24dca06d | 6034 | end if; |
faee1e18 | 6035 | |
d087b9ca | 6036 | when E_Out_Parameter |
6037 | | E_In_Out_Parameter | |
6038 | => | |
6039 | -- Shallow parameters and globals need not be considered | |
6040 | ||
6041 | if not Is_Deep (Typ) then | |
6042 | Perm := None; | |
faee1e18 | 6043 | |
d087b9ca | 6044 | -- Functions cannot have outputs in SPARK |
6045 | ||
6046 | elsif Ekind (Subp) = E_Function then | |
d087b9ca | 6047 | return; |
6048 | ||
6049 | -- Deep types define a borrow or a move | |
6050 | ||
6051 | else | |
6052 | Perm := Read_Write; | |
6053 | end if; | |
faee1e18 | 6054 | end case; |
6055 | ||
d087b9ca | 6056 | if Perm /= None then |
6057 | declare | |
6058 | Tree : constant Perm_Tree_Access := | |
6059 | new Perm_Tree_Wrapper' | |
6060 | (Tree => | |
6061 | (Kind => Entire_Object, | |
6062 | Is_Node_Deep => Is_Deep (Etype (Id)), | |
5bc0f990 | 6063 | Explanation => Expl, |
d087b9ca | 6064 | Permission => Perm, |
6065 | Children_Permission => Perm)); | |
6066 | begin | |
6067 | Set (Current_Perm_Env, Id, Tree); | |
6068 | end; | |
6069 | end if; | |
faee1e18 | 6070 | end Setup_Parameter_Or_Global; |
6071 | ||
6072 | ---------------------- | |
6073 | -- Setup_Parameters -- | |
6074 | ---------------------- | |
6075 | ||
d087b9ca | 6076 | procedure Setup_Parameters (Subp : Entity_Id) is |
6077 | Formal : Entity_Id; | |
faee1e18 | 6078 | begin |
6079 | Formal := First_Formal (Subp); | |
6080 | while Present (Formal) loop | |
e47eea05 | 6081 | Setup_Parameter_Or_Global |
df177848 | 6082 | (Id => Formal, |
1e5359c0 | 6083 | Typ => Retysp (Etype (Formal)), |
df177848 | 6084 | Kind => Ekind (Formal), |
6085 | Subp => Subp, | |
5bc0f990 | 6086 | Global_Var => False, |
6087 | Expl => Formal); | |
faee1e18 | 6088 | Next_Formal (Formal); |
6089 | end loop; | |
6090 | end Setup_Parameters; | |
5abc6fac | 6091 | |
94654315 | 6092 | -------------------------------- |
6093 | -- Setup_Protected_Components -- | |
6094 | -------------------------------- | |
6095 | ||
6096 | procedure Setup_Protected_Components (Subp : Entity_Id) is | |
6097 | Typ : constant Entity_Id := Scope (Subp); | |
6098 | Comp : Entity_Id; | |
6099 | Kind : Formal_Kind; | |
6100 | ||
6101 | begin | |
6102 | Comp := First_Component_Or_Discriminant (Typ); | |
6103 | ||
6104 | -- The protected object is an implicit input of protected functions, and | |
6105 | -- an implicit input-output of protected procedures and entries. | |
6106 | ||
6107 | if Ekind (Subp) = E_Function then | |
6108 | Kind := E_In_Parameter; | |
6109 | else | |
6110 | Kind := E_In_Out_Parameter; | |
6111 | end if; | |
6112 | ||
6113 | while Present (Comp) loop | |
6114 | Setup_Parameter_Or_Global | |
6115 | (Id => Comp, | |
1e5359c0 | 6116 | Typ => Retysp (Etype (Comp)), |
94654315 | 6117 | Kind => Kind, |
6118 | Subp => Subp, | |
6119 | Global_Var => False, | |
6120 | Expl => Comp); | |
1e5359c0 | 6121 | |
94654315 | 6122 | Next_Component_Or_Discriminant (Comp); |
6123 | end loop; | |
6124 | end Setup_Protected_Components; | |
6125 | ||
faee1e18 | 6126 | end Sem_SPARK; |