Challenge 7 – Initialization, array checks and range checks
Ever found yourself not knowing what to put in this backpack your little one is carrying to school? This helper program does it for you, or your kid, or your kid’s teacher. Because not everyone has the same priorities.
Have a look at the following code and see if you can spot the errors. Once you think you’ve got them all, click on the “Go CodePeer” button to see how well you matched up to CodePeer’s comprehensive and rapid analysis of the same code.
Error line 65: “medium: array index check might fail: requires Res in 1..100”
Here’s how CodePeer helps you coming to this conclusion: Res is the result of calling Find_More_Useful, which returns 0 if no useful item was found. Hence, the code should test that Res is not null before accessing array B at index Res.
Error line 81: “medium: validity check: Res might be uninitialized”
Here’s how CodePeer helps you coming to this conclusion: Res is a local variable which is only assigned to in the loop if some fun item is found. Therefore it might be uninitialized when returning it on line 81. Instead, it should be default initialized to 0 on line 72.
Error line 102: “medium: array index check might fail: requires Best in 1..100”
Here’s how CodePeer helps you coming to this conclusion: Same as the error on line 65.
Error line 119: “low: range check might fail: requires (Total_Weight + Stuff(Best).Something.W) in 0..10_000”
Here’s how CodePeer helps you coming to this conclusion: Summing the weights of the selected items might well overcome the 10 kg limit that a variable of type Weight should respect. Instead, this addition should be protected against overflow, for example by calling Weight’Min appropriately before assigning to Total_Weight.
package Backpack is
Useless : constant := 0;
Mandatory : constant := 5;
type Utility is range Useless .. Mandatory;
Boring : constant := 0;
So_Cooool : constant := 5;
type Fun is range Boring .. So_Cooool;
subtype Weight is Natural range 0 .. 10_000; -- in grams
Scoliosis_Limit : constant := 6_000;
type Item is record
U : Utility;
F : Fun;
W : Weight;
end record;
-- content of the backpack
type Content is array (Positive range <>) of Item;
type Belonging is record
Something : Item;
Available : Boolean;
end record;
type Extended is range 0 .. 100;
subtype Index is Extended range 1 .. 100;
-- set of stuff to choose from to populate the backpack
type Belongings is array (Index) of Belonging;
type Strategy is (Teacher, -- maximize utility
Mom_And_Pop, -- maximize utility and minimize weight
Kiddo); -- maximize fun and avoid punishment
-- Choose items from Stuff to populate Choice according to Strat
procedure Back_To_School (Strat : Strategy;
Stuff : in out Belongings;
Choice : in out Content;
Num : out Natural);
end Backpack;
package body Backpack is
function Find_More_Useful (B : Belongings) return Extended is
Res : Extended := 0;
U : Utility := Useless;
begin
for I in Index loop
if B(I).Something.U > U then
Res := I;
U := B(I).Something.U;
end if;
end loop;
return Res;
end Find_More_Useful;
function Find_Mandatory (B : Belongings) return Extended is
Res : Extended;
begin
Res := Find_More_Useful (B);
if B (Res).Something.U /= Mandatory then
Res := 0;
end if;
return Res;
end Find_Mandatory;
function Find_More_Fun (B : Belongings) return Extended is
Res : Extended;
F : Fun := Boring;
begin
for I in Index loop
if B(I).Something.F > F then
Res := I;
F := B(I).Something.F;
end if;
end loop;
return Res;
end Find_More_Fun;
procedure Back_To_School
(Strat : Strategy;
Stuff : in out Belongings;
Choice : in out Content;
Num : out Natural)
is
Best : Extended := 1;
Total_Weight : Weight := 0;
begin
Num := 0;
for Next in Positive range Choice'Range loop
exit when Best = 0; -- no more stuff to pack, exit.
case Strat is
when Teacher | Mom_And_Pop =>
Best := Find_More_Useful (Stuff); -- take only useful stuff
if Strat = Mom_And_Pop and then
Stuff (Best).Something.U /= Mandatory and then
Total_Weight + Stuff (Best).Something.W >= Scoliosis_Limit
then
-- Stop piling non-mandatory stuff before the scoliosis.
Best := 0;
end if;
when Kiddo =>
Best := Find_Mandatory (Stuff); -- start with mandatory stuff
if Best = 0 then
Best := Find_More_Fun (Stuff); -- then pile fun stuff
end if;
end case;
-- Found something useful/fun to bring in class? Put it in backpack.
if Best /= 0 then
Stuff (Best).Available := False;
Choice (Next) := Stuff (Best).Something;
Total_Weight := Total_Weight + Stuff (Best).Something.W;
Num := Num + 1;
end if;
end loop;
end Back_To_School;
end Backpack;