Why does GNAT reject an array type with a default discriminant value? - ada

This (declaration) code raises a storage error:
type Vector is array (Integer range <>) of Integer;
type Array_Starting_At_One (Max : Integer := 0) is
record
Mat : Vector (1 .. Max);
end record;
X : Array_Starting_At_One;
I can't figure out why. If I specify the default, as in X : Array_Starting_At_One (Max => 0); the error disappears, although the Array_Starting_At_One type specification still triggers a warning that creation of such objects may raise Storage_Error.
I am not even trying to store a single bit, so this error doesn't make any sense to me:
raised STORAGE_ERROR : System.Memory.Alloc: object too large

When a variable is declared using the default discriminant, then the discriminant can later be changed via an assignment. This means that the compiler (at least GNAT does this) will allocate (on the stack) enough room to hold an object with any discriminant up to the maximum allowed value (Integer'Last in this case).
Either increase your stack size (not neccessarily recommended), or use a different subtype more suited to your problem:
subtype Foo is Integer range 1..10;
type Vector is array (Foo range <>) of Integer;

Certainly any array with an index range of 1..Integer'Last could raise Storage_Error. The compiler is warning you of this possibility. Try restricting the index range for your array type such as:
subtype Indices is Integer range 0..1024;
type Vector is Array (Indices range <>) of Integer;
type Array_Ending_At (Max : Indices := 0) is
record
Mat : Vector(1..Max);
end record;

As you notice, this is a compiler issue. The Janus/Ada compiler would accept your code without complaint or run-time exception.
Your variable X is an unconstrained object; the discriminant (and so the size of the object) can be changed by full-record assignment. The ARM is silent about how such things should be implemented. GNAT has chosen to allocate enough space for the largest possible value; since X is allocated on the stack, there will not be enough space for it with GNAT's default stack size (if you allocate it on the heap, you might not have a problem). Janus instead allocates only enough space for the current value, which may result in X.Mat being implicitly allocated on the heap.
The Janus way is more developer-friendly and acceptable for most applications, where the developer doesn't care where things are allocated, but there are situations where implicit heap allocation cannot be accepted, so the GNAT way is more general.

Related

How to add index to array in Ada, like append for array in Java?

I want to append an index to an Integer array during a loop. Like add 3 to [1,2] and get an array like [1,2,3]. I don't know how to write it in the format and I cannot get the answer on the Internet.
You can use Vectors to do the something similar using the & operator. You can access the individual elements just like an array, though you use () instead of []. Or you can just use a for loop and get the element directly.
See the below example:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
procedure jdoodle is
-- Create the package for a vector of integers
package Integer_Vectors is new Ada.Containers.Vectors(Positive,Integer);
-- Using subtype to make just the Vector type visible
subtype Vector is Integer_Vectors.Vector;
-- Make all the primitive operations of Vector visible
use all type Vector;
-- Create a variable
V : Vector;
begin
-- Add an element to the vector in each iteration
for i in 1..10 loop
V := V & i;
end loop;
-- Show the results
for Element of V loop
Put_Line(Element'Image);
end loop;
-- Print the 2nd element of the array
Put_Line(Integer'Image(V(2)));
end jdoodle;
Ada arrays are first class types, even when the array type is anonymous. Thus a 2-dimensional array is a different type than a 3-dimensional array. Furthermore, arrays are not defined by directly specifying the number of elements in a dimension as they are in languages derived from C.
For instance, if you define a 2-dimensional array such as
type array_2d is array (Integer range 0..1, Integer range 1..2);
You have defined an array with a first range of 0..1 and a second range of 1..2. This would be a square matrix containing 4 elements.
You cannot simply add another dimension to an object of the type array_2d described above. Such an array must be of a different type.
Furthermore, one cannot change the definition of an array object after it is created.
Ok, so while this is a simple question it gets into the interesting details of design very quickly. The first thing is that an array "always knows its bounds" -- this language design element impacts the usage of the language profoundly: instead of having to pass the length of the array as a parameter, and possibly going out-of-sync, like in C you simply pass the array and let the "it knows its bounds" take care of the management.
Second, the Array is always static-length, meaning it cannot be changed once created. Caveat: Creating an array can be done in a "dynamic" setting, like querying user-input.
Third, if the subtype is unconstrained than it can have "variable" length. -- This means that you can have something like Type Integer_Vector is Array(Positive Range <>) of Integer and have parameters that operate on any size value passed in (and return-values that can be any size). This, in turn, means that your handling of such subtypes tends itself toward the more general.
Fourth, all of these apply and combine so that a lot of the 'need' for dynamically sized arrays aren't needed -- yes, there are cases where it is needed, or where it is more convenient to have a single adjustable object; this is what Ada.Containers.Vectors addresses -- but in the absence of needing a truly dynamically-sizing object you can use processing for achieving your goals.
Consider the following example:
Type Integer_Vector is Array(Positive range <>) of Integer;
Function Append( Input : Integer_Vector; Element : Integer ) return Integer_Vector is
( Input & Element );
X : Constant Integer_Vector:= (1,2);
Y : Integer_Vector renames Append(X, 3);
These three design choices combine to allow some intere

Defining a bag Multiset with elements and multiplicity pairs In Ada

I have to meet a specification which says that I have to write a bag type which is a record with discriminant , which is the maximum capacity of the bag in the record I have to put an array that contains element and multiplicity pairs , I have written some code and updated it as well
type TArray is array (Integer range <>) of Elem,Multi;
type Bag (Max : Positive) is record
Data : TArray (1 .. Max);
Pointer : Natural := 0;
end record;
The code has been updated please check and suggest
The answer is quite simple : if you don't have any error messages at compilation phase, it's ok.
Well according to what you described, your code looks good. I have a question about it.
Do you plan to have negative indexes on your type TArray ? If not, why don't you use the same type as your discriminant ?
The only thing is that you'll have only half the values using Positive instead of Integer but in your record, you anyway already start at 1 and thus have the same range as Positive.

How can I increment an access type as if it were a C pointer?

How to increment an address of access type like a pointer in C? I am new to Ada...
procedure main is
type myarr is array(1..5)of integer; --creating array of 5 integer.
myarr_var:aliased myarr:=(2,5,7,9,0); --setting 5 values
type my_access is access all myarr; --creating access type (pointer)
var:my_access:=myarr_var'access; --holding address of 5 integer
begin;
-- since var holds the address of myarr_var now i want to increment
-- the address by adding 1 to print next value (5) as we do in c?
put((var+1).all); --???
-- i know this is bad but how to increment its base address of
-- var(pointer) so that it will point to next element 5?
end main;
Instantiate Interfaces.C.Pointers to do C-style pointer arithmetic in Ada.
Best explained by example:
with Ada.Text_IO; use Ada.Text_IO;
with Interfaces.C.Pointers;
procedure Access_Pointer_Arithmetic is
type Myarr_Indices is range 1 .. 5;
type Myarr is array (Myarr_Indices range <>) of aliased Integer;
Myarr_Terminator : constant Integer := 0;
package Myarr_Pointer_Arithmetic is new Interfaces.C.Pointers
(Myarr_Indices, Integer, Myarr, Myarr_Terminator);
use Myarr_Pointer_Arithmetic;
Myarr_Var : aliased Myarr := (2, 5, 7, 9, 0);
Var : Myarr_Pointer_Arithmetic.Pointer :=
Myarr_Var(Myarr_Var'First)'access;
begin
Put_Line(Integer'Image(Var.all));
Increment(Var);
Put_Line(Integer'Image(Var.all));
Increment(Var);
Put_Line(Integer'Image(Var.all));
Increment(Var);
Put_Line(Integer'Image(Var.all));
Var := Var - 2;
Put_Line(Integer'Image(Var.all));
end Access_Pointer_Arithmetic;
Running it:
C:\Ada\Sandbox\access_pointer_arithmetic
2
5
7
9
5
This package provides single increment/decrement, addition and subtraction of ptrdiff_t, and both terminator-specified and fixed length element array retrieval.
(Mind running off the end of the array... :-)
UPDATE : I just found out that there's a standard package Interfaces.C.Pointers that directly supports C-style pointer arithmetic, and now I see that Marc C.'s accepted answer covers its usage. You can probably ignore my answer, which discusses how you might do pointer arithmetic in Ada if Interfaces.C.Pointers didn't exist (which, in earlier versions of the language, it doesn't).
If you really want to do C-style pointer arithmetic on Ada access types, you can use the generic package System.Address_To_Access_Conversions to convert an object pointer type to System.Address, and the System.Storage_Elements package to perform C-like arithmetic on System.Address values.
Note that the target object type is a parameter to the System.Address_To_Access_Conversions generic package. The package itself defines the access type. You can't define your own access type and use it (at least not directly).
Keep in mind that C pointer arithmetic is defined in units of the size of the pointed-to object. So given:
int arr[10];
int *ptr = &arr[0];
the pointer value ptr + 3 points to arr[3], which is three int-sized chunks of memory past the location to which ptr points not necessarily three bytes. The "+" and "-" operators in System.Storage_Elements work with offsets in storage elements (which are very likely equivalent to C's "bytes").
So if you have an Ada pointer, um, I mean access value, that refers to an element of an array of Integers, then advancing to the next element of that array requires:
Using System.Address_To_Access_Conversions to convert the access type to System.Address;
Using the overloaded "+" operator in System.Storage_Elements to add the size of an Integer in bytes (Integer'Max_Size_In_Storage_Elements) to the System.Address value; and
Using System.Address_To_Access_Conversions again to convert the System.Address value back to your access type.
An alternative might be to write C code to do whatever pointer arithmetic you need, and use Interfaces.C to call that code from your Ada program.
But it's very likely that you don't need to do pointer arithmetic in Ada. C has pointer arithmetic in the core language; it even defines array indexing in terms of pointer arithmetic. Ada does not. There's rarely a good reason to perform pointer arithmetic in Ada. Just let arrays be arrays, and let the compiler figure out how to generate the best code to access their elements. (That code will likely involve pointer arithmetic on the CPU instruction level.)

Proving that reads from an array are initialised in SPARK

Given a very simple SPARK function that sums an array of integers:
function Add (X, Y : in Ints) return Ints is
Sum : Ints;
begin
for i in Ints'Range loop
pragma Loop_Invariant (for all j in Ints'First .. i - 1 => Sum(j) = X(j) + Y(j)); -- line 7
Sum(i) := X(i) + Y(i);
end loop;
return Sum; -- line 11
end Add;
(Where type Ints is array (Integer range 0 .. 9) of Integer_32)
Compiling without the loop invariant works fine (because I have a precondition that bounds the elements of X and Y such that overflow cannot occur). However, I need the invariant in order to show some properties of the post condition, but it results in:
7:69: warning: "Sum" may be referenced before it has a value
Phase 2 of 3: analysis and translation to intermediate language ...
7:10: "Sum" is not initialized
11:7: warning: "Sum" might not be initialized
I'm not sure how the concept of "being initialised" is expressed in the proof language so I don't know how to convince gnatprove that no uninitialised reads are occuring.
I can remove the warnings by explicitly setting all the elements of Sum to zero at the start of the function but I'm hoping that there's a better way.
In SPARK, arrays are considered as entire objects, and a component-by-component initialization like you do is not allowed. However, there is a heuristics in gnatprove which allows a simple for-loop over the range of the array, as you do. This is why without a loop invariant, you don't get the warning. This heuristics breaks down with the loop invariant, that's why you get the warning again.
You will have to accept the warning using pragma Warnings, as described here:
https://gcc.gnu.org/onlinedocs/gnat_rm/Pragma-Warnings.html
And to avoid the error you are getting at line 7, you may move the loop invariant after the assignment (and change the range of the quantificaton accordingly).

Ada "constant" keyword memory usage

Consider the following code :
My_Constant : constant := 2;
Is "My_Constant" a variable or a C language macro-like, so does it have a storage in memory ?
Note that constant just means that you cannot modify the object. It doesn't necessarily mean that the value of the constant is known at compile time. So there are three cases to consider:
(1) A constant with a type, whose value is known at compile time:
My_Constant : constant Integer := 3;
In this case, there's no reason for the compiler to allocate memory for the constant; it can use the value 3 whenever it sees My_Constant (and is likely to use 3 as the immediate operand of instructions, where possible; if it sees something like My_Constant * 2 then it can use the value 6 as an immediate operand). The compiler is allowed to allocate memory for the constant, but I don't think any decent compiler would do so, in a simple case like this with a small number. If it were a really large number that wouldn't fit into an immediate operand, it might make more sense to allocate space for the number somewhere (if it can do so in a way that saves on code space).
In a more complex case:
My_Record_Constant : constant Rec := (Field_1 => 100, Field_2 => 201, Field_3 => 44);
Here, a good compiler can decide whether or not to store the constant in memory based on how it's used. If the only uses are accesses of individual fields (My_Record_Constant.Field_1), the compiler could replace those with the integer values, as if they were integer constants, and there would be no need to store the entire record in memory.
However, using aliased will cause any constant to be forced into memory:
My_Constant : aliased constant Integer := 3;
Now memory has to be allocated because the program could say My_Constant'Access (the access type has to be access constant Integer).
(2) A constant whose value is not known at compile time:
My_Constant : constant Integer := Some_Function_Call (Parameter_1);
The function is called once, when the integer's declaration is elaborated. Since it is not a macro expansion, uses of My_Constant do not generate calls to the function. Example:
procedure Some_Procedure is
My_Constant : constant Integer := Some_Function_Call (Parameter_1);
begin
Put_Line (Integer'Image (My_Constant));
Put_Line (Integer'Image (My_Constant));
end Some_Procedure;
Some_Function_Call is called each time Some_Procedure is called, but it is called once, not two or three times.
Most likely, this requires the value to be stored in memory to hold the function result, so space will be allocated for My_Constant. (This still isn't a requirement. If a good optimizing compiler can somehow figure out that Some_Function_Call will return a known value, it can use that information.)
(3) A named number. This is the example you have, where there is no type:
My_Constant : constant := 2;
The language rules say the value must be known at compile time. This is the equivalent of using that number every time My_Constant is seen, so it's the closest thing to a C macro you're going to get in Ada. But the effect is basically the same as in (1) [except with fewer restrictions on type compatibility]. The compiler probably will not allocate space for it, but it might do so for a larger value. Note that this syntax is allowed only for numeric values (integer or real).
Another variation on option (1) above is for a constant array.
primes : constant array(integer range <>) of integer := (1, 3, 5, 7, 11, 13, 17, 19, 23);
If the compiler can see it being accessed by an index, it will have to store the array.
I doubt the compiler writers would try and special case any other obscure corner condition to save some memory - they have enough other special corner cases to worry about in Ada!

Resources