Ada suppress unreachable code or missing return - ada

I have a tagged type that implements a number of functions. In one case I need one of these functions to instead enter an infinite loop. Unfortunately as far as I can tell there is no way for me to compile this such that it doesn't raise a warning. I wish to still use -gnatwe to ensure there are no warnings in my code, but how can I implement such a function.
Here is what the function looks like:
function Foo (This : Some_Type) return Some_Type'Class is
begin
loop
Do_Useful_Stuff_Indefinitely_With (This);
end loop;
-- return This; (if needed?)
end Foo;
I have tried pragma (No_Return) except that is only applicable for procedures (and the Foo function is used as a proper function elsewhere so must have the same signature).
I also tried pragma Suppress (All_Checks) but that still raised a warning for unreachable code or missing return statement error.
Is there any way whatsoever to have a once-off function that runs forever without raising a warning?

Taking the same example that Jean-François provided, you can avoid the warning by declaring and calling a "private" procedure (you don't have to declare it in spec) wrapping your loop as in the following :
package body foo is
procedure Infinite_Loop is
begin
loop
null;
end loop;
end Infinite_Loop;
function bar return integer is
begin
Infinite_Loop;
return 12;
end bar;
end foo;

pragma Suppress (All_Checks) acts on run-time checks. Won't help you there. Leave that alone unless you focus on performance, but then you have -p option to do it using command line switches
The pragma Suppress suppresses compiler-generated run-time checks. If a run-time check is disabled, an exception may be suppressed and undefined behavior could result. pragma Suppress is used at the programmer's risk.
You need the return statement, but you can wrap it around 2 pragma warnings statements (A case where have you tried turning it off and on again? works)
pragma warnings(off,"unreachable code");
return This;
pragma warnings(on,"unreachable code");
note that the text is optional but enables to filter other warnings that could occur (if needed). It's better since turning off all warnings is generally bad practice.
Note that you have to turn warnings on again after the statement.
Self-contained demo. foo.adb looks like:
package body foo is
function bar return integer is
begin
loop
null;
end loop;
pragma warnings(off,"unreachable code");
return 12;
pragma warnings(on,"unreachable code");
end bar;
end foo;
foo.ads looks like:
package foo is
function bar return integer;
end foo;
If I comment out the pragma lines:
$ gcc -c -gnatwe foo.adb
foo.adb:8:05: warning: unreachable code
uncommenting them removes the warning.

Related

Ada: Manipulate a private type

I'm kinda new with Ada and recently got an error that I don't seem to know how to solve.
I have the following code:
data.ads
with Text_IO; use text_io;
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
package data is
type file is private;
type file_set is array (Integer range <>) of file;
procedure file_Print (T : in out file); --Not used
private
type file is record
start, deadline : integer;
end record;
end data;
Main.adb
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
procedure Main is
Num_files: integer:=3;
Files:file_set(1..Num_files);
begin
Files(1):=(2,10); -- Expected private type "file" defined at data.ads
for i in 1..Num_Files loop
Put(integer'Image(i));
New_Line;
data.File_Print(Files(i));
But I'm getting this error Expected private type "file" defined at data.ads
How can I access the file type and declare a new array of values in main?
That's right - you don't get to see or manipulate what's inside a private type. That would be breaking encapsulation. Bugs and security risks follow.
You can only interact with a private type via its methods : functions and procedures declared in the package where it's declared.
Now file_set is NOT a private type (you might consider making it private later, for better encapsulation, but for now ....) you can index it to access a file within it (using one of those procedures).
Files(1):=(2,10);
As you want to create a file here, you need a method to create a file ... a bit similar to a constructor in C++, but really more like the Object Factory design pattern. Add this to the package:
function new_file(start, deadline : integer) return file;
And implement it in the package body:
package body data is
function new_file(start, deadline : integer) return file is
begin
-- check these values are valid so we can guarantee a proper file
-- I have NO idea what start, deadline mean, so write your own checks!
-- also there are better ways, using preconditions in Ada-2012
-- without writing explicit checks, but this illustrates the idea
if deadline < NOW or start < 0 then
raise Program_Error;
end if;
return (start => start, deadline => deadline);
end new_file;
procedure file_Print (T : in out file) is ...
end package body;
and that gives the users of your package permission to write
Files(1):= new_file(2,10);
Files(2):= new_file(start => 3, deadline => 15);
but if they attempt to create garbage to exploit your system
Files(3):= new_file(-99,-10); -- Oh no you don't!
this is the ONLY way to create a file, so they can't bypass your checks.

Ada elaboration not occurring at all

I have an unusual situation in which elaboration code is simply not being executed at all. This is not an elaboration order issue, but rather an elaboration at all issue.
The problem is that I don't "with" the unit in question whatsoever, yet in theory it should still be accessible, as long as its elaboration occurs.
Of course I could just add a useless "with" for the unit in question, but in my real use case there are a large number of units that I would have to do that with.
My question is if there is any way either in the code, through pragmas, in the gpr project file, or through command-line switches that I could force the compiler to include a file even though it thinks the file isn't referenced?
Here is a minimal working example:
as.ads:
package As is
type A is tagged null record;
type Nothing is null record;
function Create (Ignored : not null access Nothing) return A;
function Image (From : A) return String;
end As;
as.adb:
package body As is
function Create (Ignored : not null access Nothing) return A is
(null record);
function Image (From : A) return String is ("A");
end As;
finder.ads:
with Ada.Tags;
package Finder is
procedure Register (Name : String; Tag : Ada.Tags.Tag);
function Find (Name : String; Default : Ada.Tags.Tag) return Ada.Tags.Tag;
end Finder;
finder.adb:
with Ada.Containers.Indefinite_Vectors;
package body Finder is
type Name_Tag (Size : Natural) is
record
Name : String (1 .. Size);
To : Ada.Tags.Tag;
end record;
package Name_Tag_Vectors is new Ada.Containers.Indefinite_Vectors (Positive, Name_Tag);
Name_Tags : Name_Tag_Vectors.Vector := Name_Tag_Vectors.Empty_Vector;
procedure Register (Name : String; Tag : Ada.Tags.Tag) is begin
Name_Tags.Append ((Name'Length, Name, Tag));
end Register;
function Find (Name : String; Default : Ada.Tags.Tag) return Ada.Tags.Tag is begin
for Tag of Name_Tags loop
if Tag.Name = Name then
return Tag.To;
end if;
end loop;
return Default;
end Find;
end Finder;
bs.ads:
with As;
package Bs is
type B is new As.A with null record;
function Create (Ignored : not null access As.Nothing) return B;
function Image (From : B) return String;
end Bs;
bs.adb:
with Finder;
package body Bs is
function Create (Ignored : not null access As.Nothing) return B is
(As.Create (Ignored) with null record);
function Image (From : B) return String is ("B");
begin
Finder.Register ("B", B'Tag);
end Bs;
test.adb:
with As; use As;
-- with Bs; -- (uncommenting this line solves my problem, but what if I had the rest of the alphabet?)
with Finder;
with Ada.Tags.Generic_Dispatching_Constructor;
with Ada.Text_IO;
procedure Test is
function Constructor is new Ada.Tags.Generic_Dispatching_Constructor (
T => A,
Parameters => Nothing,
Constructor => Create);
Nada : aliased Nothing := (null record);
What : A'Class := Constructor (Finder.Find ("B", A'Tag), Nada'Access);
begin
Ada.Text_IO.Put_Line (What.Image);
end Test;
The compiler thinks your package Bs isn't referenced because it isn't. You don't have a with clause for it, so it's not part of your program.
A simple example:
a.ads
package A is
procedure Blah;
end A;
a.adb
with Ada.Text_IO;
package body A is
procedure Blah is begin null; end Blah;
begin
Ada.Text_IO.Put_Line("Elaborate A");
end A;
b.ads
package B is
procedure Blah;
end B;
b.adb
with Ada.Text_IO;
package body B is
procedure Blah is begin null; end Blah;
begin
Ada.Text_IO.Put_Line("Elaborate B");
end B;
main.adb
with Ada.Text_IO;
with A;
procedure Main is
begin
Ada.Text_IO.Put_Line("Main");
end Main;
When I run main, it prints
Elaborate A
Main
It doesn't print Elaborate B because that package isn't part of the program; it's just a couple of source files in the same directory.
The obvious solution is to add the with clauses.
I don't know whether there's a less obvious solution. If there is, it's probably compiler-specific. But I'm not sure why a compiler would have a feature that lets you incorporate an otherwise unused package into a program.
What I’ve done (e.g. here ff) is to actually reference the units in the main program (with pragma Unreferenced to prevent warnings).
Alternatively, you could have a package e.g. Required_Units with all the necessary withs included, and then with that from the main program.
Even if there was some alternative process, you’d have to tell it what units you need to have included; might as well go with the flow and do it in Ada!
Since the package Bs is invisible to your program, so is the type B.
So the next question is: why do you need to register type B if it is not used anywhere?
If an Ada compiler did elaborate all units (packages or standalone subprograms) that are irrelevant to a main program, but are visible through source path, it would become really messy!...

Starting tasks again

I recently started Ada programming and now I'm stuck.
I created a program with multiple tasks. The main-task is managing incoming communication and as a consequence starts working-tasks or transfers data to the working-tasks.
The working-tasks are all of the same kind but with different identifiers.
They do their work and should finish after that. For example:
task body Access_Protected is
begin
accept Start(foo: in Integer; foo2: out Integer)
do something
end Start;
while Go_loop loop
select
accept Quit do
Go_loop := false;
end Quit;
or
accept Insert(foo3: in Integer)
do something
if something = 0 then
Go_loop := false;
end if;
end Insert;
or delay 2.0;
end select;
end loop;
end Access_Protected;
I understand that the working-task should be terminated when the Go_loop is finished. Am I right?
It works to start the task one time but when the main-task tries to restart the working-task by calling the Start procedure, nothing happens.
Can someone please tell me which point I am missing.
A task and subprogram are somewhat related in that when the body is completed the construct ends, this is to say that the construct ends with it's appropriate end; in the case of a procedure control returns to the caller, in the case of a function the exception PROGRAM_ERROR is raised, and in the case of a task the controlling "thread" terminates.
What's happening in your particular problem, it seems, boils down to the following:
Package Example is
Task Type Message_Task is
Entry Execute;
End Message_Task;
End Example;
Package Body Example is
Task Body Message_Task is
Use Ada.Text_IO;
Begin
accept Execute do
Put_Line( "Rendezvous!" );
end Execute;
delay 0.2; -- Stub delay.
Put_Line( "Finishing Task." );
-- Task Ends Here.
End Message_Task;
End Example;
--...
Test : Example.Message_Task;
--...
Test.Execute;
-- Test.Execute can't be accepted here because it can only accept "Execute"
-- the one time, as per the body's definition.
The reason that this really is like your problem is because, likewise once you say "X.Start(1,2)" another call to Start doesn't reset the position of the task's execution back up to that accept.
If you wanted the task to "stay alive" for further processing you could do one of two options.
Option 1 -- set up a 'protocol':
Package Example is
Task Type Message_Task is
Entry Initialization;
Entry Execute;
Entry Quit;
End Message_Task;
End Example;
Package Body Example is
Task Body Message_Task is
Use Ada.Text_IO;
Has_quit : Boolean := False;
Begin
Main:
loop
select
accept Initialization do
null;
end Initialization;
accept Execute do
null;
end Execute;
or
accept Quit do
Has_Quit := True;
end Quit;
end select;
Exit Main when Has_Quit;
end loop Main;
End Message_Task;
End Example;
Option 2 -- Allow termination.
Package Example is
Task Type Message_Task is
Entry Initialization;
Entry Execute;
End Message_Task;
End Example;
Package Body Example is
Task Body Message_Task is
Use Ada.Text_IO;
Has_quit : Boolean := False;
Begin
accept Initialization do
null;
end Initialization;
Main:
loop
select
accept Execute do
null;
end Execute;
or
terminate;
end select;
end loop Main;
End Message_Task;
End Example;
The subtle difference is Option 2 gets rid of the Quit entry, allowing the task to 'rest' on the terminate alternative while Option 1 is more explicit in control (and required in some cases), but requiring that Initialization & Execute be called in pairs.
A task only runs until it reaches the end of its main sequence of statements (ignoring various technicalities).
If you want a task to do something, and then pause until it receives an external trigger, you should put a loop around the statements you have in the task body.

Ada actual for "S" must be a variable

So here is a piece of my body file. I am getting the error "words.adb:75:42: actual for "S" must be a variable".
procedure Remove_Character(S : in out Ustring; C : in Character; Successful : out Boolean) is
begin
for I in 1..length(S) loop
if Element(S, I) = C then
Delete(S, I, I);
Successful := true;
return;
end if;
end loop;
Successful := false;
end Remove_Character;
function Is_Subset(Subset : Ustring; S : Ustring) return Boolean is
Could_Remove : Boolean;
begin
for I in 1..length(Subset) loop
Remove_Character(S , Element(Subset, I), Could_Remove);
if Could_Remove = false then
return false;
end if;
end loop;
return True;
end Is_Subset;
I understand where my error is coming from. Remove_Character uses S : in out Ustring while function Is_Subset uses S : in Ustring.
My question is how do I change the variable from Remove_Character into only an in Ustring?
Sorry if this is a tad jumbled, I'm fairly new to both programming and the site.
You can't, at least not directly.
I don't know what a UString is, but I presume the Delete procedure modifies it. If you changed the declaration of S in Remove_Character to S: in Ustring, you'd presumably get an error on the call to Delete.
The simplest approach I can think of would be to make a copy of S in Is_Subset:
Copy_Of_S: UString := S;
and then pass the (modifiable) copy to Remove_Character.
By "simplest", I mean it makes the smallest change to your existing code. But you should probably consider reorganizing it. Determining whether one UString is a subset of another by modifying one of the strings doesn't seem like the best approach; I'm sure there's a more efficient way to do it.
A minor and irrelevant point: this:
if Could_Remove = false then
is better written as:
if not Could_Remove then

Writing a function inside a PL/SQL page

I want to create a function that do a specific task inside a oracle package, I tried with the bellow code, but it gives an error that I don't understand.
CREATE OR REPLACE
PACKAGE DINIDU_EXE_PACKAGE AS
FUNCTION EXE14
(SUP_ID_ SUPPLIER_PART_PROJECT_TAB.SUPPLIER_ID%TYPE,PAR_ID_ SUPPLIER_PART_PROJECT_TAB.PART_ID%TYPE,PRO_ID_ SUPPLIER_PART_PROJECT_TAB.PROJECT_ID%TYPE) RETURN NUMBER IS
QUNTITY_FOR_A_PROJECT_ NUMBER;
BEGIN
SELECT QUENTITY AS QUNTITY_FOR_A_PROJECT_ FROM SUPPLIER_PART_PROJECT_TAB WHERE SUPPLIER_ID=SUP_ID AND PART_ID=PAR_ID AND PRO_ID=PROJECT_ID;
IF QUNTITY_FOR_A_PROJECT_ >0 THEN
RETURN QUNTITY_FOR_A_PROJECT_;
ELSE
RETURN 0;
END IF;
END EXE14;
END;
Error(6,1): PLS-00103: Encountered the symbol "QUNTITY_FOR_A_PROJECT_" when expecting one of the following: language
Egor is right in his comment. You are putting a function in to the package specification while it has to be in package body instead.
Only a reference to a function or its signature - function name and arguments list - have to be in the package specification. The actual function has to be coded in the body.
CREATE OR REPLACE PACKAGE DINIDU_EXE_PACKAGE AS
FUNCTION EXE14
( SUP_ID_ SUPPLIER_PART_PROJECT_TAB.SUPPLIER_ID%TYPE
, PAR_ID_ SUPPLIER_PART_PROJECT_TAB.PART_ID%TYPE
,PRO_ID_ SUPPLIER_PART_PROJECT_TAB.PROJECT_ID%TYPE)
END;
/
CREATE OR REPLACE PACKAGE BODY DINIDU_EXE_PACKAGE AS
FUNCTION EXE14
( SUP_ID_ SUPPLIER_PART_PROJECT_TAB.SUPPLIER_ID%TYPE
, PAR_ID_ SUPPLIER_PART_PROJECT_TAB.PART_ID%TYPE
,PRO_ID_ SUPPLIER_PART_PROJECT_TAB.PROJECT_ID%TYPE)
RETURN NUMBER IS
QUNTITY_FOR_A_PROJECT_ NUMBER;
BEGIN
SELECT QUENTITY AS QUNTITY_FOR_A_PROJECT_ FROM SUPPLIER_PART_PROJECT_TAB WHERE SUPPLIER_ID=SUP_ID AND PART_ID=PAR_ID AND PRO_ID=PROJECT_ID;
IF QUNTITY_FOR_A_PROJECT_ >0 THEN
RETURN QUNTITY_FOR_A_PROJECT_;
ELSE
RETURN 0;
END IF;
END EXE14;
END;
/
EDIT: see Egor's comment and Rachcha's answer for the actual cause of the compilation error.
Your code first declares QUNTITY_FOR_A_PROJECT_ as a local variable, but then your SELECT statement uses the same identifier as a column alias (QUENTITY AS QUNTITY_FOR_A_PROJECT_). More importantly, you have not selected the result INTO anything.
I think you meant to do something like this:
SELECT QUENTITY INTO QUNTITY_FOR_A_PROJECT_ FROM SUPPLIER_PART_PROJECT_TAB ...
Another problem: your function accepts the following parameters: SUP_ID_, PAR_ID_ and PRO_ID_, but you don't use them in your function. I suspect the identifiers are not used correctly in the query, but I cannot know for sure because I don't know what the columns of your SUPPLIER_PART_PROJECT_TAB table are.

Resources