Ada: Re-exporting enum type values - ada

I have an ada package in the form of
package C is
type Test is (Test_1, Test_2, Test_3);
end C;
and another package in the form of
with C;
package B is
subtype Test is C.Test;
end B;
and yet another in the form of
with B;
package A is
subtype Test is B.Test;
Item: Test := Test_1;
end A;
Naively, I would expect that a subtype defined in B, which is later subtyped by A, would be able to access members of the original type. However, upon inspection, the members of C are not even visible in the scope of B. This can be fixed by adding use c;, this seems like a find solution to a degree, however to use it in A, you would have to add with c; use c; to every package that transitively depends on C. This could lead to confusion as it wouldn't be implicitly obvious that you should be using C.
I would like to be able to "rexport" these types, so that I can abstract layers of my program better.

If you change package A to
with B;
package A is
subtype Test is B.Test;
use all type Test;
Item: Test := Test_1;
end A;
the code compiles. Not sure whether this will help.
This is an Ada 2012 feature; see ARM 8.4(8) and (8.1). use all type makes the primitive operations of the type visible (which includes enumeration literals).

If you really want to re-export the values, you can do this:
with C;
package B is
subtype Test is C.Test;
function Test_1 return Test is (C.Test_1);
function Test_2 return Test is (C.Test_2);
function Test_3 return Test is (C.Test_3);
end B;
Unfortunately, you can't use named numbers since enumerations are not numeric types. You can make these functions normal constants instead, but conceptually, this would execute code at elaboration time (the compiler will probably optimize it away, but you can't use pragma Preelaborate anymore).
This allows you to access the literals in A using B.Test_1 etc. This is also a proper abstraction as A will not depend on the literals defined in C anymore (you can rename the literals in C without affecting A, you do need to update B though to map the new names to the original ones).
Simply importing the literal names into the namespace of A is not an abstraction.

with B;
package A is
type Test is new B.Test;
Item: Test := Test_1;
end A;
...and all is well!

Ada has the concept of visibility, with a lot of rules that define when, where, and how things are visible. Understanding visibility is key to understanding Ada. One of the best discussions of visibility is in Ada Distilled. What you're encountering is a consequence of the visibility rules. This goes even further than enumeration literals:
package Pack is
type Int is range -(2 ** 15) + 1 .. 2 ** 15 - 1;
end Pack;
with Pack;
procedure Proc is
subtype Int is Pack.Int;
-- None of the operators of Int ("+", "*", ...) are directly visible here
begin
null;
end Proc;
Declarations in a package spec are only visible outside the package by using dotted notation, unless you explicitly make them directly visible elsewhere. use clauses are one way to make them visible; others are renames and, for enumeration literals, declaring constants of the type initialized using dotted notation.

Related

Does Ada have any idiomatic rules on when to use a function versus a procedure with an output parameter?

You can assign to a variable by having a function return a value to it:
My_Int : Integer := My_Math_Func [(optional params)];
Or you can do it like this with a procedure (assuming My_Int has already been declared):
My_Math_Proc ([optional params;] [in] out My_Int);
Obviously a procedure can't initialize a variable like the function does in the first example, but I'm hoping for some concrete, practical rules on when and why to pick one over the other.
Two to get you started...
When more than one result is to be returned, a procedure with several OUT parameters is often a good choice.
When the size of the object is unknown prior to the subprogram call, an OUT parameter cannot be used because it would have to be declared precisely the right size, but a function return can set the size by initialising the variable in the caller. This is commonly used with a variable declared in a Declare block, which can hold a different sized string each time it is invoked.
This example shows the variable "text" initialised by calling a Read_File function, to hold the contents of a different file on each iteration of the loop. Safe, no "malloc" or "free" or pointers necessary. (Filename is an array of filenames in this example)
for i in 1 .. last_file loop
declare
text : String := Read_File(Filename(i));
-- the size of "text" is determined by the file contents
begin
-- process the text here.
for j in text'range loop
if text(j) = '*' then
...
end loop;
end
end loop;
Edit : And I suppose I'd better mention the underlying mathematical principle, since Ada is based more closely on mathematical logic than many other languages.
Functions and procedures are both subprograms, but for different purposes:
a function is an abstraction over an expression : like a mathematical operator (and an operator in Ada is just a function). Ideally, it provides a result from a number of operands and nothing else, leaving them unchanged and having no state and no side effects. This ideal is called a "pure function" (and applying "pragma pure" asks the compiler to check its purity) - similar restrictions apply in functional programming (FP) languages. Pure functions allow a whole bunch of optimisation (because reordering them doesn't change results). In practice Ada is not that strict, allowing impure functions too.
a procedure is an abstraction over a statement. It generally has some physical effect (such as changing state) since it doesn't deliver a result.
So the logical separation between expressions and statements is carried over into subprograms (abstractions) as the separation between functions and procedures.
And this is probably the best way to decide which to use.
Brian Drummond already answered your question directly, but I wanted to add some additional info: If your type has some sort of initializing procedure, in Ada2005/Ada2012 you can convert it to an initializing function using extended return syntax. It will even work for limited types.
Say you have a package with a type like this:
package Example is
type My_Type is limited private;
procedure Initialize(Self : in out My_Type; Value : Integer);
procedure Print(Self : My_Type);
private
type My_Type is limited record
Value : Integer := 0;
end record;
end Example;
package body Example is
procedure Initialize(Self : in out My_Type; Value : Integer) is
begin
Self.Value := Value;
end Initialize;
procedure Print(Self : My_Type) is
begin
Ada.Text_IO.Put_Line(Self.Value'Image);
end Print;
end Example;
You can then make your own initializing function out of that procedure doing something like this:
function Make_My_Type (Value : Integer) return Example.My_Type is
begin
return Result : Example.My_Type do
Example.Initialize(Result,Value);
end return;
end Make_My_Type;
and you can initialize variables easily using the procedure hidden in your function underneath:
procedure Test
Thing : Example.My_Type := Make_My_Type(21);
begin
Example.Print(Thing);
end Test;
This is different than just making a variable and returning it. You are not able to do that with a limited type, but with extended return syntax, you can do it for any type.
Here is some additional info for extended return statements as well.

Wrapping an imported C pointer in an Ada tagged type of the same size

I'd like to replicate a C struct memory layout into an Ada type, and at the same time wrap the C fields (which are pointers) into tagged types that are at the same memory locations as the pointers to avoid extra memory use, while using dot notation on the pointee information in a confortable Ada way. This requires that the tagged Ada fields have the same size as the C pointers.
An example that works without tagged types is as follows:
with Ada.Text_IO; use Ada.Text_IO;
procedure Tag is
package P is
type Int_Ptr is access all Integer with Convention => C;
-- This would be in reality a C record
type Rec is record
I : Int_Ptr := new Integer'(42);
end record
with Convention => C;
-- This is the field wrapper that would be tagged
type Wrap_I is record -- Making this tagged breaks it
I : Int_Ptr;
end record
with Convention => C;
function Image (WI : Wrap_I) return String is
(WI.I.all'Img);
-- This is the Ada type corresponding to C Rec
type Wrap_Rec is record
I : Wrap_I;
end record
with Convention => C;
end P;
R : P.Rec;
W : P.Wrap_Rec with Address => R'Address, Import;
begin
Put_Line (P.Image (W.I)); -- Should be 42 if properly overlaid
-- This is the objective: to use dot notation on C pointed data:
-- Put_Line (W.I.Image);
end Tag;
Now, the objective is to make Wrap_I tagged. Once I mark it as such, GNAT warns that R and W sizes differ (and it raises a tag check failed at runtime, obviously). I do not need dispatching or anything but static calls, so in essence I wonder if there is a way of having a tagged type without storing its tag in memory (to be used statically only).
I have a much more traditional plan B so no need to suggest alternatives if this is not doable.
Thanks!
You can't have tagged types without tags, so it isn't doable.

Constant element in Ada object?

In Java or C#, you would often have class members that are final or readonly - they are set once and then never touched again. They can hold different values for different instances of the class.
Is there something similar in Ada? I've tried to create something similar in Ada thusly:
package MyPackage is
type MyObject is limited new OtherPackage.Object with private;
....
private
type MyObject (...) is limited new OtherPackage.Object with
record
M_MyField : Integer := 10;
M_MyConstantFactory : constant Factory.Object'Class := new Factory.Object;
end record;
end MyPackage;
This fails on the declaration of M_MyConstantFactory saying constant components are not permitted. Is there a way around this? A colleague suggested declaring it somewhere else in the package, but that would mean a single M_MyConstantFactory shared across all instances, which is not what I want.
Do I need to just accept that it is possible to modify the value once set and manually guard against that happening?
No. Not quite.
If your component is of a discrete type or an access type, you can make it a discriminant, and thus make it immutable.
with Ada.Integer_Text_IO;
procedure Immutable_Components is
type Instance (Immutable : Positive) is null record;
A : Instance := (Immutable => 1);
begin
Ada.Integer_Text_IO.Put (A.Immutable);
-- A.Immutable := 2; -- assignment to discriminant not allowed:
end Immutable_Components;
Before I answer the question, it would probably be helpful to distinguish between Ada and Java/C#'s modeling of objects. In Java, everything is an object, and so all constants have to be final -- in Ada things are a bit different, Ada's object-system ("tagged types" in Ada parlance) is built upon two items: records and type-derivation. This means that, when teaching OOP, we could arrive gradually introducing first type-derivation (eg Type Degree is new Integer;), then records (ie encapsulation), then private-types (ie information-hiding), and finally unifying everything together with tagged-types... all of which I'll assume you are cognizant of.
In Ada, a constant is just that: some object that can be read but [generally] not written to. (Things can get funny with eg memory-mapped IO.) So we could say:
Package Ex1 is
Type Stub1 is private; -- Some type, with almost nothing public.
C1 : Constant Stub1; -- A constant of that type.
Private
Type Stub1 is tagged record
Data_1 : Integer;
Data_2 : Float;
end;
-- And now we can tell the compiler what C1 _is_.
C1: Constant Stub1 := (Data_1 => 3, Data_2 => 1.2);
End Ex1;
That's how we'd make a constant for a tagged type while keeping its implementation details hidden; though, admittedly, we could have exposed everything and gotten rid of the entire private section.
Now we get to an interesting feature of records [and tagged types] called discriminants -- these are kind of like constants, and kind of like generic-types in other languages. With discriminants we could make a message-type that varies in size according to the message-length:
Package Ex2 is
Type Message(Length : Natural) is private; -- A message.
Function Create( Text : String ) return Message;
Private
Type Message(Length : Natural) is record
Data : String(1..Length) := (Others => ' '); -- Defaults to space-filled string.
end;
Function Create( Text : String ) return Message is
( Data => Text, Length => Text'Length );
End Ex2;
Now, in this case, when you do an assignment like X : Message := Create("Steve"); the variavle's type [unconstrained, in this example, becomes constrained in this case to Message(5) (because "Steve" is 5 characters) and so trying to re-assign with a different-sized message-string wouldn't work. (So, while you couldn't say X:= Create("Why") you can say X:= Create("Hello") because the discriminant [Length] here is 5.) -- So, in that manner discriminants can act like constant fields in some cases.
The limited keyword means that the type doesn't have an assignment [but does have initialization], so you could make the entire type behave as a constant; this is different than having the one component be constant though, certainly not as subtle as the distinction between T and T'Class (T'Class is the Type T and all types derived therefrom, where as T is only that type.)
You almost have the general solution (for cases when it can't be a discriminant) already. The type is limited private. Clients of the package can only modify it through the operations the pkg provides. As long as the operations don't modify the field in question, you have what you want (unless I misunderstood and the question is how to prevent yourself from modifying the field in the pkg body).

Ada Source code modifications using ASIS(Ada Semantics interface specifications)

i am developing a tool for finding sub type range overflow problems in Ada source code.for this purpose i am using ASIS for finding assignment statements in Ada source code and finding type of the variables on the right side of the assignment
expression.now i want to replace the variables(not of record type) of the assignment expression with 'first, 'last values of the variable type in the assignment statement so that i will get compilation error if any range overflow happens.below is an example what i am trying to convey.
procedure Example is
subtype A_Type is integer 1 .. 10;
subtype B_Type is integer -5 .. 5;
subtype C_Type is integer 1 .. 12;
A : A_Type;
B : B_Type;
C : C_Type;
begin
C := A + B;
end Example;
I want modify C := A + B; with C := A_Type'Last + B_Type'Last in the source code. C := A_Type'Last + B_Type'Last assignment statement will get warning at compile time or Constraint error during run time.
Is it possible do above modifications with ASIS?
For your purpose, you shouldn't rewrite the source text you are processing. You should rather write a new program, which only contains exactly the required declarations and assignments.
So the output should be something like:
with Type_Declarations;
procedure Test_Driver is
begin
declare
C : Type_Declarations.C_Type;
begin
C := Type_Declarations."+" (Type_Declarations.A_Type'First, Type_Declarations.B_Type'First);
C := Type_Declarations."+" (Type_Declarations.A_Type'First, Type_Declarations.B_Type'Last);
C := Type_Declarations."+" (Type_Declarations.A_Type'Last, Type_Declarations.B_Type'First);
C := Type_Declarations."+" (Type_Declarations.A_Type'Last, Type_Declarations.B_Type'Last);
end;
end Test_Driver;
ASIS was not designed to make modifications like that. You can, however take a look at libadalang from AdaCore, which supports this (and works on partial sources, so you won't have to precompile your sources)
GNAT includes utilities gnat2xml and xml2gnat; gnat2xml generates a representation of the source based on ASIS, and xml2gnat converts it back to Ada. You could maybe modify the XML output of the first and feed it back to the second.
Not that I’m recommending this; the XML schema isn’t documented, and is complicated.
If you want a tool that can apply modifications to Ada source code, you might be interested in our DMS Software Reengineering Toolkit with its Ada front end.
DMS parses source code to ASTs, and makes those ASTs available for modification using DMS's Abstract Syntax Tree procedural interface (direct hacking at the tree nodes) and/or DMS's rewrite rules (source-to-source transformations "if you see this replace it by that" written in [Ada] language surface syntax, that directly manipulates the trees. After your changes are made, DMS can prettyprint the source to regenerate valid Ada source code, even preserving comments and formatting in those places that have not been modified.

Make Frama-c show dependencies even of "dead branches"

I am using frama-c Aluminium-20160502 version and I want to find out the dependencies in a large program. When using the option -deps in the command line I found some dependencies are missing. In particular when several conditions are joined in one if, the dependency analysis stops whenever one condition is false. In this example here:
#include<stdio.h>
#include<stdbool.h>
/*Global variable definitions*/
bool A = true;
bool B = false;
bool C = true;
bool X;
bool Y;
bool res;
void main(){
if (A && B && C) {
res = X;
}else res = Y;
}
when I try: frama-c -deps program.c
frama shows the following dependencies:
[from] ====== DEPENDENCIES COMPUTED ======
These dependencies hold at termination for the executions that terminate:
[from] Function main:
res FROM A; B; Y
[from] ====== END OF DEPENDENCIES ======
so it does not reach the condition C because already B is false.
I wonder if there is a way to tell frama to compute all dependencies even if the condition is not fulfilled. I tried with the option -slevel but with no results. I know there is a way to use an interval Frama_C_interval(0,1) but when I use it the variable using this function is not shown in the dependencies. I would like to get X and Y dependent on A,B,C and res dependent on A,B,C,X,Y
Any ideas?
The From plugin uses the results of the Value analysis plugin. In your example, the values of A and B are sufficiently precise that Value is able to infer that the condition is entirely determined (since the && operator is lazily evaluated, from left to right) before reaching C, therefore C never affects the outcome and is thus not a dependency from the point of view of From.
Unfortunately, Frama_C_interval cannot be used directly at global initializers:
user error: Call to Frama_C_interval in constant.
You can, however, use a "hack" (not always the best solution, but works here):
volatile bool nondet;
bool A = nondet;
bool B = nondet;
bool C = nondet;
...
Note that because nondet is volatile, each variable A, B and C is assigned a different non-deterministic value.
In this case, Value has to consider both branches of the conditionals, and therefore C becomes a dependency in your example, since it is possible that C will be read during the execution of main. You'll then have:
These dependencies hold at termination for the executions that terminate:
[from] Function main:
res FROM A; B; C; X; Y
Note that some plugins require special treatment when dealing with volatile variables, so this is not always the best solution.
This however only deals with some kinds of dependencies. As mentioned in Chapter 6 of the Value Analysis user manual, the From plugin computes functional, imperative and operational dependencies. These do not include indirect control dependencies, which are those such as X from A, B, C, as you mention. For those, you need the PDG (Program Dependence Graph) plugin, but it does not currently have a textual output of the dependencies. You can use -pdg to compute it, and then -pdg-dot <file> to export the dependency graph in dot (graphviz) format. Here's what I get for your main function (using the volatile hack as mentioned previously):
Finally, as a side note: -slevel is mostly used to improve precision, but in your example you already have too much precision (that is, Value is already able to infer that C is never read inside main).

Resources