General Access type Ada - pointers

I am still confused on how all keyword works in a general access type
what's the difference between:
type int_access is access all Integer; to type int_access is access Integer;
for example:
type int_ptr is access all Integer;
Var : aliased Integer := 1;
Ptr : int_ptr := Var'Access;
the code works fine but if I remove the all keyword it gives an error that result must be general access type and I must add all.

Pool-specific access types -- those without the "all" -- can be used only for objects allocated in the heap (or in some user-defined storage pool) with the "new" keyword.
So this is OK:
type Int_Ptr is access Integer;
Prt: Int_Ptr := new Integer;
General access types -- those with the "all" -- can be used both for heap-allocated objects, and for any other object that is marked "aliased". So this is also OK:
type Int_Ptr is access all Integer;
Prt: Int_Ptr := new Integer;
So the rules, in brief, are:
without "all": only objects allocated with "new"
with "all": in addition, any object marked "aliased".

Explained here: Memory Management with Ada 2012.

Related

In Crystal FFI, how do I access a type in the C library?

The task I'm working on is to add support for the create_function interface to Crystal's SQLite binding: https://github.com/crystal-lang/crystal-sqlite3/issues/61
To access the parameters for a user-defined function, I need to access a C-style array (that is, a pointer to contiguous instances) of the sqlite3_value type, which if I'm not mistaken requires knowing the size of the type. But as far as I have found, there is no way to declare a Crystal type as an alias for a type defined in the C library.
Because it's a pointer, no, you don't necessarily need to know its layout. For opaque pointers this pattern is common in Crystal:
type Sqlite3Context = Void*
type Sqlite3Value = Void*
fun sqlite3_create_function(
[...]
xFunc : (Sqlite3Context, Int, Sqlite3Value*) ->,
[...]
)

Ada: What does 'is type access' mean?

I came across this piece of code from my assignment:
procedure Refs is
type Node is
record
Content : Integer;
Name : Character;
end record;
type XNode is access Node;
type NodeArray is array (Positive range 1 .. 5) of XNode;
[...]
And I cant seem to grok it (to the point that I could explain it to my grandmother) even after reading documentation, wiki etc.
Can someone explain in simple terms what that access keyword means ?
I know nothing about Ada, but thankfully the answer is just 3 seconds of Googling away: XNode is an access type for Node. An access type is a type which grants access to dynamically allocated values of another type.
In other words, it is a pointer. But don't confuse this with the C concept of a pointer. Ada pointers are pointer-safe and memory-safe, you cannot, for example, add 1 to it and have it point to a different piece of memory, or have it point to some random address and claim "this memory is now a Node" (aka type casting).
It is more like an object reference in Java, ECMAScript, Python, or Ruby.

Checking for Null Pointer in Ada

Generally speaking, Ada will raise a Constraint_Error if you attempt to dereference a null pointer (access type). However, this behavior is disabled if, for example, you have pragma Suppress (all_checks) in use.
Given this scenario, how would one go about checking to see if the access type points to 0x0 (null)?
Consider the following:
type My_Access_Type is access all My_Type'Class;
procedure myProcedure ( myAccess : in My_Access_Type ) is
begin
-- need to have check in here
end
if myAccess = null then
...
end if;
Although it won't necessarily point to 0x0. Access types are not pointers, and may be implemented in a different way than plain addresses.
Another option, that can help you, is to declare the pointer as "not null".
type My_Access is not null access My_Type;
this prevents the declaration of not initialized My_Access types.
X : My_Access; -- compile error
This solution as some disadvantages (see https://en.wikibooks.org/wiki/Ada_Programming/Types/access#Null_exclusions) and its correct usage depends on your needs.

Ada Syntax for initializing dynamically allocated array

What is the correct syntax for initializing a dynamically allocated array in Ada? I have tried this:
type Short_Array is array (Natural range <>) of Short;
Items : access Short_Array;
Items := new Short_Array(1..UpperBound)'(others => 0);
which results in a compiler error - "binary operator expected". And this:
type Short_Array is array (Natural range <>) of Short;
Items : access Short_Array;
Items := new Short_Array(1..UpperBound);
Items.all := (others => 0);
which seems to raise a SEGFAULT surprisingly. Not sure what's going on there but wanted to get the syntax right before I start chasing my tail.
If you are using Ada2012 you can do the following:
type Short_Array is array(Natural range <>) of Short with
Default_Component_Value => 0;
Items : access Short_Array := new Short_Array(1..UpperBound);
The use of default initial values for arrays is explained in section 2.6 of the Ada 2012 Rationale http://www.ada-auth.org/standards/12rat/html/Rat12-2-6.html
Another approach in Ada is to define the record as a discriminant record, with the discriminant determining the size of the array field.
type Items_Record (Size : Natural) is record
-- Some non-array fields of your record
Items : Short_Array(1..Size);
end record;
An instance of the record can then be allocated in an inner block
Get(Items_Size);
declare
My_Record : Items_Record(Size => Items_Size);
begin
-- Process the instance of Items_Record
end;
The record is dynamically allocated on the stack. If the record size is very large you will encounter a stack overflow issue. If not, this works very well. One advantage of this approach is the instance is automatically de-allocated when the end of the block is reached.
The SEGFAULT in your second example comes most certainly from the initialization.
Compare
type Short_Array is array (Natural range <>) of Short;
Items : access Short_Array;
Items := new Short_Array(1..UpperBound);
Items.all := (others => 0);
And this:
type Short_Array is array (Natural range <>) of Short;
Items : access Short_Array;
Items := new Short_Array(1..UpperBound);
for I in 1..UpperBound loop
Items(I) := 0;
end loop;
And play with different values of ulimit -Ss which sets the allowed size of the stack.
The point is that
Items.all := (others => 0);
allocates an array on the stack and copy it in your heap-allocated array. So you think you are working on the heap but still needs a lot of stack. If your array is too big for your ulimit -Ss (or considering both soft and hard limits ulimit -s), you will segfault (or get a STORAGE_ERROR) though you think you are all on the heap.
To mitigate this problem, (though it might not work in every situation, eg if UpperBound is dynamic…), you can compile your code either with:
"-fstack-usage" (to get usage information for each unit)
"-Wstack-usage=2000" (or any limit more accurate to your case, see gnat's documentation for more infos) to yield warnings for functions using too much stack (or having unbounded stack usage).
The second option could have issued a warning and pointed you to your stack overflow.

Ada 2005, access types, and local variable escape analysis

So when playing with access types on an Ada compiler which turns out to be Ada 2005, I try the following classic example:
type Node is record
next: access Node;
end record;
function reverselist(input: access Node) return access Node is
result: access Node := null;
this: access Node := input;
next: access Node := null;
begin
while this /= null loop
next := this.next;
this.next := result; -- [*]
result := this;
this := next;
end loop;
return result; -- [*]
end;
The two starred lines produce compilation errors because Ada 2005's analysis thinks I might be leaking a local pointer. (I know that Ada 2012 has better analysis and will accept this. However, I don't have an Ada 2012 compiler.)
Okay, so this can be easily solved by using a named pool access instead of the anonymous access Node; by using a pool access I tell the compiler that the pointers can't be referring to stack objects. That works fine.
However, then I tried using a named general access:
type Node;
type List is access Node;
type Node is record
next: List;
end record;
function reverselist(input: List) return access Node is
result: List := null;
this: List := input;
next: List := null;
begin
while this /= null loop
next := this.next;
this.next := result;
result := this;
this := next;
end loop;
return result;
end;
General access types can point at stack objects as well as heap objects; so why doesn't the compiler reject the second example for the same reasons that it rejected the first?
(I was also slightly surprised that anonymous access integer variables seem to end up with general access semantics. Why don't I have to use access all integer for anonymous general accesses?)
Although in general a general access type can point at non-heap objects (both global and stack), there are rules that would prevent a List from pointing to the stack. In this case, since List is not declared inside any subprogram, it cannot be made to point to a stack variable:
function reverselist(input: List) return access Node is
result: List := null;
this: List := input;
next: List := null;
Dummy : aliased Node;
begin
result := Dummy'Access; -- a compile-time error would occur at this point
while this /= null loop
next := this.next;
this.next := result;
result := this;
this := next;
end loop;
return result;
end;
Since the rules "ensure" that a List can never point to a stack variable, there's no need for the compiler to reject any of the other statements. (I put "ensure" in quotes because you could use 'Unchecked_Access to get around them, as well as Unchecked_Conversion or a number of other ways to get around the type system.)
If an access type is declared inside a subprogram, it can point to stack variables inside that subprogram. This is because no objects of that access type can be declared outside the subprogram and therefore it's not possible (barring unchecked tricks) for any object of this type to live after the subprogram is completed. It can also point to globals, or to heap objects. But it can't point to stack variables in a nested subprogram.
procedure Proc is
type Node is ...
type Acc is access all Node;
N1 : aliased Node;
procedure Nested_Proc is
N2 : aliased Node;
X : Acc;
begin
X := N1'Access; -- legal
X := N2'Access; -- illegal. N2 is too deep for this access type.
end;
So it's at the point where 'Access is used that the compiler makes sure that dangling references to no-longer-existing variables can't be created.
All this is governed by the accessibility rules, which are clearly spelled out in RM 3.10.2. (I'm kidding. 3.10.2 is very difficult to understand and tends to produce migraines and borderline insanity in those who try to read it.)
Edit: To follow up on your comment: Basically, I don't think the compiler is doing any sort of "robust analysis". Most of the rules are actually simpler than I made it sound. In Ada 2005, most access types, including most anonymous ones, have a "level" that depend on where the type is declared. If an access object's type is level L, it can't take an access value whose level is "deeper" (more nested) than L. Each anonymous access declares a new type, whose level usually depends on where it's declared. In your example that uses anonymous access types, the type of the next record field is library level since it's outside any procedure; the type of the result is deeper than that since it's inside a procedure. this.next := result is illegal because result, being deeper, could point to a stack variable, and this.next has a shallower level and thus this isn't allowed. For the named access type case, this.next := result is legal because result is just as shallow as the next field, and result wouldn't be allowed to point to a stack variable in the first place. In Ada 2012, I think it's different because the anonymous access objects have to keep track at runtime of the level of the thing they're point to. So this.next := result is legal, but (I think) will raise an exception at runtime if result points to a stack variable. In Ada 2012, these access objects will need some extra data to allow them to do this check; in Ada 2005, they could all be just simple pointers. (I could be wrong about some of the Ada 2012 changes.) But hopefully that isn't too complicated. The complications in 3.10.2 have to do with anonymous access parameters, anonymous access discriminants, and some other special cases that your example doesn't have anything to do with.
One solution is to restructure the code a bit; using an extended-return might do it for you:
type Node is record
next: access Node;
end record;
function reverse_list(input: access Node) return access Node is
this: access Node := input;
next: access Node := null;
begin
Return result: access Node := null do
while this /= null loop
next := this.next;
this.next := result;
result := this;
this := next;
end loop;
end return;
end reverse_list;
While accessibility-checks can get frustrating, I have an overall good opinion of them: we do not want dangling pointers.

Resources