1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- S Y S T E M . V A L U E _ U --
9 -- Copyright (C) 1992-2023, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 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. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 package body System
.Value_U
is
34 -- Ghost code, loop invariants and assertions in this unit are meant for
35 -- analysis only, not for run-time checking, as it would be too costly
36 -- otherwise. This is enforced by setting the assertion policy to Ignore.
38 pragma Assertion_Policy
(Ghost
=> Ignore
,
39 Loop_Invariant
=> Ignore
,
41 Assert_And_Cut
=> Ignore
,
42 Subprogram_Variant
=> Ignore
);
44 use type Spec
.Uns_Option
;
45 use type Spec
.Split_Value_Ghost
;
49 procedure Lemma_Digit_Not_Last
55 Pre
=> Str
'Last /= Positive'Last
56 and then From
in Str
'Range
57 and then To
in From
.. Str
'Last
58 and then Str
(From
) in '0' .. '9' |
'a' .. 'f' |
'A' .. 'F'
59 and then P
in From
.. To
60 and then P
<= Spec
.Last_Hexa_Ghost
(Str
(From
.. To
)) + 1
61 and then Spec
.Is_Based_Format_Ghost
(Str
(From
.. To
)),
63 (if Str
(P
) in '0' .. '9' |
'a' .. 'f' |
'A' .. 'F'
64 then P
<= Spec
.Last_Hexa_Ghost
(Str
(From
.. To
)));
66 procedure Lemma_Underscore_Not_Last
72 Pre
=> Str
'Last /= Positive'Last
73 and then From
in Str
'Range
74 and then To
in From
.. Str
'Last
75 and then Str
(From
) in '0' .. '9' |
'a' .. 'f' |
'A' .. 'F'
76 and then P
in From
.. To
77 and then Str
(P
) = '_'
78 and then P
<= Spec
.Last_Hexa_Ghost
(Str
(From
.. To
)) + 1
79 and then Spec
.Is_Based_Format_Ghost
(Str
(From
.. To
)),
80 Post
=> P
+ 1 <= Spec
.Last_Hexa_Ghost
(Str
(From
.. To
))
81 and then Str
(P
+ 1) in '0' .. '9' |
'a' .. 'f' |
'A' .. 'F';
83 -----------------------------
84 -- Local lemma null bodies --
85 -----------------------------
87 procedure Lemma_Digit_Not_Last
94 procedure Lemma_Underscore_Not_Last
101 -----------------------
102 -- Scan_Raw_Unsigned --
103 -----------------------
105 procedure Scan_Raw_Unsigned
107 Ptr
: not null access Integer;
112 -- Local copy of the pointer
115 -- Accumulated unsigned integer result
120 Overflow
: Boolean := False;
121 -- Set True if overflow is detected at any point
123 Base_Char
: Character;
124 -- Base character (# or :) in based case
127 -- Base value (reset in based case)
132 Ptr_Old
: constant Integer := Ptr
.all
134 Last_Num_Init
: constant Integer :=
135 Last_Number_Ghost
(Str
(Ptr
.all .. Max
))
137 Init_Val
: constant Spec
.Uns_Option
:=
138 Spec
.Scan_Based_Number_Ghost
(Str
, Ptr
.all, Last_Num_Init
)
140 Starts_As_Based
: constant Boolean :=
141 Last_Num_Init
< Max
- 1
142 and then Str
(Last_Num_Init
+ 1) in '#' |
':'
143 and then Str
(Last_Num_Init
+ 2) in
144 '0' .. '9' |
'a' .. 'f' |
'A' .. 'F'
146 Last_Num_Based
: constant Integer :=
148 then Spec
.Last_Hexa_Ghost
(Str
(Last_Num_Init
+ 2 .. Max
))
151 Is_Based
: constant Boolean :=
153 and then Last_Num_Based
< Max
154 and then Str
(Last_Num_Based
+ 1) = Str
(Last_Num_Init
+ 1)
156 Based_Val
: constant Spec
.Uns_Option
:=
157 (if Starts_As_Based
and then not Init_Val
.Overflow
158 then Spec
.Scan_Based_Number_Ghost
159 (Str
, Last_Num_Init
+ 2, Last_Num_Based
, Init_Val
.Value
)
162 First_Exp
: constant Integer :=
163 (if Is_Based
then Last_Num_Based
+ 2 else Last_Num_Init
+ 1)
167 -- We do not tolerate strings with Str'Last = Positive'Last
169 if Str
'Last = Positive'Last then
170 raise Program_Error
with
171 "string upper bound is Positive'Last, not supported";
175 Spec
.Lemma_Scan_Based_Number_Ghost_Step
(Str
, P
, Last_Num_Init
);
176 Uval
:= Character'Pos (Str
(P
)) - Character'Pos ('0');
179 -- Scan out digits of what is either the number or the base.
180 -- In either case, we are definitely scanning out in base 10.
183 Umax
: constant Uns
:= (Uns
'Last - 9) / 10;
184 -- Max value which cannot overflow on accumulating next digit
186 Umax10
: constant Uns
:= Uns
'Last / 10;
187 -- Numbers bigger than Umax10 overflow if multiplied by 10
190 -- Loop through decimal digits
192 pragma Loop_Invariant
(P
in P
'Loop_Entry .. Last_Num_Init
+ 1);
193 pragma Loop_Invariant
194 (if Overflow
then Init_Val
.Overflow
);
195 pragma Loop_Invariant
197 then Init_Val
= Spec
.Scan_Based_Number_Ghost
198 (Str
, P
, Last_Num_Init
, Acc
=> Uval
));
202 Digit
:= Character'Pos (Str
(P
)) - Character'Pos ('0');
204 -- Non-digit encountered
207 if Str
(P
) = '_' then
208 Spec
.Lemma_Scan_Based_Number_Ghost_Underscore
209 (Str
, P
, Last_Num_Init
, Acc
=> Uval
);
210 Scan_Underscore
(Str
, P
, Ptr
, Max
, False);
215 -- Accumulate result, checking for overflow
218 Spec
.Lemma_Scan_Based_Number_Ghost_Step
219 (Str
, P
, Last_Num_Init
, Acc
=> Uval
);
220 Spec
.Lemma_Scan_Based_Number_Ghost_Overflow
221 (Str
, P
, Last_Num_Init
, Acc
=> Uval
);
225 (Spec
.Hexa_To_Unsigned_Ghost
(Str
(P
)) = Digit
);
226 Uval
:= 10 * Uval
+ Digit
;
229 then Init_Val
= Spec
.Scan_Based_Number_Ghost
230 (Str
, P
+ 1, Last_Num_Init
, Acc
=> Uval
));
232 elsif Uval
> Umax10
then
236 Uval
:= 10 * Uval
+ Digit
;
238 if Uval
< Umax10
then
243 then Init_Val
= Spec
.Scan_Based_Number_Ghost
244 (Str
, P
+ 1, Last_Num_Init
, Acc
=> Uval
));
250 Spec
.Lemma_Scan_Based_Number_Ghost_Base
251 (Str
, P
, Last_Num_Init
, Acc
=> Uval
);
254 pragma Assert_And_Cut
255 (P
= Last_Num_Init
+ 1
256 and then Overflow
= Init_Val
.Overflow
257 and then (if not Overflow
then Init_Val
.Value
= Uval
));
261 -- Deal with based case. We recognize either the standard '#' or the
262 -- allowed alternative replacement ':' (see RM J.2(3)).
264 if P
< Max
and then (Str
(P
) = '#' or else Str
(P
) = ':') then
265 Base_Char
:= Str
(P
);
270 -- Check base value. Overflow is set True if we find a bad base, or
271 -- a digit that is out of range of the base. That way, we scan out
272 -- the numeral that is still syntactically correct, though illegal.
273 -- We use a safe base of 16 for this scan, to avoid zero divide.
275 if Base
not in 2 .. 16 then
280 -- Scan out based integer
283 Umax
: constant Uns
:= (Uns
'Last - Base
+ 1) / Base
;
284 -- Max value which cannot overflow on accumulating next digit
286 UmaxB
: constant Uns
:= Uns
'Last / Base
;
287 -- Numbers bigger than UmaxB overflow if multiplied by base
291 (if Str
(P
) in '0' .. '9' |
'A' .. 'F' |
'a' .. 'f'
292 then Spec
.Is_Based_Format_Ghost
(Str
(P
.. Max
)));
294 -- Loop to scan out based integer value
297 -- We require a digit at this stage
299 if Str
(P
) in '0' .. '9' then
300 Digit
:= Character'Pos (Str
(P
)) - Character'Pos ('0');
302 elsif Str
(P
) in 'A' .. 'F' then
304 Character'Pos (Str
(P
)) - (Character'Pos ('A') - 10);
306 elsif Str
(P
) in 'a' .. 'f' then
308 Character'Pos (Str
(P
)) - (Character'Pos ('a') - 10);
310 -- If we don't have a digit, then this is not a based number
311 -- after all, so we use the value we scanned out as the base
312 -- (now in Base), and the pointer to the base character was
313 -- already stored in Ptr.all.
316 Spec
.Lemma_Scan_Based_Number_Ghost_Base
317 (Str
, P
, Last_Num_Based
, Base
, Uval
);
320 pragma Assert
(Ptr
.all = Last_Num_Init
+ 1);
322 (if Starts_As_Based
then P
= Last_Num_Based
+ 1);
323 pragma Assert
(not Is_Based
);
324 pragma Assert
(if not Overflow
then Uval
= Init_Val
.Value
);
328 pragma Loop_Invariant
(P
in P
'Loop_Entry .. Last_Num_Based
);
329 pragma Loop_Invariant
330 (Str
(P
) in '0' .. '9' |
'a' .. 'f' |
'A' .. 'F'
331 and then Digit
= Spec
.Hexa_To_Unsigned_Ghost
(Str
(P
)));
332 pragma Loop_Invariant
333 (if Overflow
'Loop_Entry then Overflow
);
334 pragma Loop_Invariant
336 (Overflow
'Loop_Entry or else Based_Val
.Overflow
));
337 pragma Loop_Invariant
339 then Based_Val
= Spec
.Scan_Based_Number_Ghost
340 (Str
, P
, Last_Num_Based
, Base
, Uval
));
341 pragma Loop_Invariant
(Ptr
.all = Last_Num_Init
+ 1);
343 Spec
.Lemma_Scan_Based_Number_Ghost_Step
344 (Str
, P
, Last_Num_Based
, Base
, Uval
);
345 Spec
.Lemma_Scan_Based_Number_Ghost_Overflow
346 (Str
, P
, Last_Num_Based
, Base
, Uval
);
348 -- If digit is too large, just signal overflow and continue.
349 -- The idea here is to keep scanning as long as the input is
350 -- syntactically valid, even if we have detected overflow
352 if Digit
>= Base
then
355 -- Here we accumulate the value, checking overflow
357 elsif Uval
<= Umax
then
358 Uval
:= Base
* Uval
+ Digit
;
361 then Based_Val
= Spec
.Scan_Based_Number_Ghost
362 (Str
, P
+ 1, Last_Num_Based
, Base
, Uval
));
364 elsif Uval
> UmaxB
then
368 Uval
:= Base
* Uval
+ Digit
;
375 then Based_Val
= Spec
.Scan_Based_Number_Ghost
376 (Str
, P
+ 1, Last_Num_Based
, Base
, Uval
));
379 -- If at end of string with no base char, not a based number
380 -- but we signal Constraint_Error and set the pointer past
381 -- the end of the field, since this is what the ACVC tests
382 -- seem to require, see CE3704N, line 204.
391 -- If terminating base character, we are done with loop
393 if Str
(P
) = Base_Char
then
395 pragma Assert
(P
= Last_Num_Based
+ 1);
396 pragma Assert
(Ptr
.all = Last_Num_Based
+ 2);
397 pragma Assert
(Starts_As_Based
);
398 pragma Assert
(Last_Num_Based
< Max
);
399 pragma Assert
(Str
(Last_Num_Based
+ 1) = Base_Char
);
400 pragma Assert
(Base_Char
= Str
(Last_Num_Init
+ 1));
401 pragma Assert
(Is_Based
);
402 Spec
.Lemma_Scan_Based_Number_Ghost_Base
403 (Str
, P
, Last_Num_Based
, Base
, Uval
);
406 -- Deal with underscore
408 elsif Str
(P
) = '_' then
409 Lemma_Underscore_Not_Last
(Str
, P
, Last_Num_Init
+ 2, Max
);
410 Spec
.Lemma_Scan_Based_Number_Ghost_Underscore
411 (Str
, P
, Last_Num_Based
, Base
, Uval
);
412 Scan_Underscore
(Str
, P
, Ptr
, Max
, True);
415 then Based_Val
= Spec
.Scan_Based_Number_Ghost
416 (Str
, P
, Last_Num_Based
, Base
, Uval
));
417 pragma Assert
(Str
(P
) /= '_');
418 pragma Assert
(Str
(P
) /= Base_Char
);
421 Lemma_Digit_Not_Last
(Str
, P
, Last_Num_Init
+ 2, Max
);
422 pragma Assert
(Str
(P
) /= '_');
423 pragma Assert
(Str
(P
) /= Base_Char
);
427 (if Starts_As_Based
then P
= Last_Num_Based
+ 1
428 else P
= Last_Num_Init
+ 2);
430 (Last_Num_Init
< Max
- 1
431 and then Str
(Last_Num_Init
+ 1) in '#' |
':');
435 or else Init_Val
.Value
not in 2 .. 16
436 or else (Starts_As_Based
and then Based_Val
.Overflow
)));
438 (Overflow
/= Spec
.Scan_Split_No_Overflow_Ghost
(Str
, Ptr_Old
, Max
));
441 pragma Assert_And_Cut
442 (Overflow
/= Spec
.Scan_Split_No_Overflow_Ghost
(Str
, Ptr_Old
, Max
)
444 (if not Overflow
then
445 (if Is_Based
then Uval
= Based_Val
.Value
446 else Uval
= Init_Val
.Value
))
447 and then Ptr
.all = First_Exp
448 and then Base
in 2 .. 16
450 (if not Overflow
then
451 (if Is_Based
then Base
= Init_Val
.Value
else Base
= 10)));
453 -- Come here with scanned unsigned value in Uval. The only remaining
454 -- required step is to deal with exponent if one is present.
456 Scan_Exponent
(Str
, Ptr
, Max
, Expon
);
459 (Ptr
.all = Spec
.Raw_Unsigned_Last_Ghost
(Str
, Ptr_Old
, Max
));
462 then Spec
.Scan_Split_Value_Ghost
(Str
, Ptr_Old
, Max
) =
463 (Uval
, Base
, Expon
));
465 if Expon
/= 0 and then Uval
/= 0 then
467 -- For non-zero value, scale by exponent value. No need to do this
468 -- efficiently, since use of exponent in integer literals is rare,
469 -- and in any case the exponent cannot be very large.
472 UmaxB
: constant Uns
:= Uns
'Last / Base
;
473 -- Numbers bigger than UmaxB overflow if multiplied by base
475 Res_Val
: constant Spec
.Uns_Option
:=
476 Spec
.Exponent_Unsigned_Ghost
(Uval
, Expon
, Base
)
479 for J
in 1 .. Expon
loop
480 pragma Loop_Invariant
481 (if Overflow
'Loop_Entry then Overflow
);
482 pragma Loop_Invariant
484 then Overflow
'Loop_Entry or else Res_Val
.Overflow
);
485 pragma Loop_Invariant
(Uval
/= 0);
486 pragma Loop_Invariant
488 then Res_Val
= Spec
.Exponent_Unsigned_Ghost
489 (Uval
, Expon
- J
+ 1, Base
));
492 ((Uval
> UmaxB
) = Spec
.Scan_Overflows_Ghost
(0, Base
, Uval
));
495 Spec
.Lemma_Exponent_Unsigned_Ghost_Overflow
496 (Uval
, Expon
- J
+ 1, Base
);
501 Spec
.Lemma_Exponent_Unsigned_Ghost_Step
502 (Uval
, Expon
- J
+ 1, Base
);
506 Spec
.Lemma_Exponent_Unsigned_Ghost_Base
(Uval
, 0, Base
);
510 Spec
.Raw_Unsigned_No_Overflow_Ghost
(Str
, Ptr_Old
, Max
));
511 pragma Assert
(if not Overflow
then Res_Val
= (False, Uval
));
514 Spec
.Lemma_Exponent_Unsigned_Ghost_Base
(Uval
, Expon
, Base
);
516 (if Expon
= 0 or else Uval
= 0 then
517 Spec
.Exponent_Unsigned_Ghost
(Uval
, Expon
, Base
) = (False, Uval
));
520 Spec
.Raw_Unsigned_No_Overflow_Ghost
(Str
, Ptr_Old
, Max
));
522 (if not Overflow
then
523 Uval
= Spec
.Scan_Raw_Unsigned_Ghost
(Str
, Ptr_Old
, Max
));
525 -- Return result, dealing with overflow
530 (GNATprove
, Intentional
,
531 "call to nonreturning subprogram might be executed",
532 "it is expected that Constraint_Error is raised in case of"
537 end Scan_Raw_Unsigned
;
543 procedure Scan_Unsigned
545 Ptr
: not null access Integer;
550 -- Save location of first non-blank character
555 """Start"" is set by ""Scan_Plus_Sign"" but not used after the call");
556 Scan_Plus_Sign
(Str
, Ptr
, Max
, Start
);
559 """Start"" is set by ""Scan_Plus_Sign"" but not used after the call");
561 if Str
(Ptr
.all) not in '0' .. '9' then
566 Scan_Raw_Unsigned
(Str
, Ptr
, Max
, Res
);
573 function Value_Unsigned
(Str
: String) return Uns
is
575 -- We have to special case Str'Last = Positive'Last because the normal
576 -- circuit ends up setting P to Str'Last + 1 which is out of bounds. We
577 -- deal with this by converting to a subtype which fixes the bounds.
579 if Str
'Last = Positive'Last then
581 subtype NT
is String (1 .. Str
'Length);
582 procedure Prove_Is_Unsigned_Ghost
with
584 Pre
=> Str
'Length < Natural'Last
585 and then not Only_Space_Ghost
(Str
, Str
'First, Str
'Last)
586 and then Spec
.Is_Unsigned_Ghost
(Spec
.Slide_To_1
(Str
)),
587 Post
=> Spec
.Is_Unsigned_Ghost
(NT
(Str
));
588 procedure Prove_Is_Unsigned_Ghost
is null;
590 Prove_Is_Unsigned_Ghost
;
591 return Value_Unsigned
(NT
(Str
));
594 -- Normal case where Str'Last < Positive'Last
599 P
: aliased Integer := Str
'First;
600 Non_Blank
: constant Positive := First_Non_Space_Ghost
601 (Str
, Str
'First, Str
'Last)
603 Fst_Num
: constant Positive :=
604 (if Str
(Non_Blank
) = '+' then Non_Blank
+ 1 else Non_Blank
)
608 P_Acc
: constant not null access Integer := P
'Access;
610 Scan_Unsigned
(Str
, P_Acc
, Str
'Last, V
);
614 (P
= Spec
.Raw_Unsigned_Last_Ghost
(Str
, Fst_Num
, Str
'Last));
616 (V
= Spec
.Scan_Raw_Unsigned_Ghost
(Str
, Fst_Num
, Str
'Last));
618 Scan_Trailing_Blanks
(Str
, P
);
621 (Spec
.Is_Value_Unsigned_Ghost
622 (Spec
.Slide_If_Necessary
(Str
), V
));