comp.lang.ada
 help / color / mirror / Atom feed
From: do_while@owens.ridgecrest.ca.us (Do-While Jones)
Subject: Re: Range Check Query
Date: Sun, 20 Nov 1994 17:16:40 GMT
Date: 1994-11-20T17:16:40+00:00	[thread overview]
Message-ID: <CzKtBt.86v@ridgecrest.ca.us> (raw)
In-Reply-To: 9411181527.AA08827@eurocontrol.de


In comp.lang.ada article 2387, Bob Wells was surprised not
to get a constraint error in a piece of code he inherited.
I took his code fragment and turned it into the program
below.  It reproduces the effect he observed.

------------------------------------------------------------

with UNCHECKED_CONVERSION,
     TEXT_IO;

procedure Test is

  type dn18906 is  -- layout is for the 18906 message  .
    record
      dnspare  : integer range 0 .. 1;
      dnspare2 : integer range 0 .. 3;
      dnch1    : integer range 0 .. (2**7) - 1;
      dnch2    : integer range 0 .. (2**7) - 1;
    end record;

   for dn18906 use
     record at mod 1;
       dnspare at 0 range 0 .. 1;
       dnspare2 at 0 range 2 .. 7;
       dnch1 at 0 range 8 .. 15;
       dnch2 at 0 range 16 .. 23;
     end record;

   for dn18906'size use 24;

   N_Data : constant := 2; -- Added by DWJ for next line.

   type Dn_189_Data is array(1 .. N_Data) of Dn18906;
   pragma Pack(Dn_189_Data);

   M_T : Dn_189_Data;

-- Next line deleted by DWJ.
-- for M_T use at P_Mesg_Conv(P_Ohead) + 12;

   -- P_Mesg_Conv is an Unchecked_Conversion of an access
   -- type to a system address. The access type points to an
   -- incoming byte stream.

-- The following added by DWJ to turn the example into a
-- complete program.

  type Integer_24 is range 0..2**24-1;
  for  Integer_24'SIZE use 24;

  DATA_1 : Integer_24;

  function Fake_Data is new UNCHECKED_CONVERSION(
    Integer_24, dn18906);

  function To_Integer_24 is new UNCHECKED_CONVERSION(
    dn18906, Integer_24);

  procedure Put_Line(VALUE : dn18906) is
    I24 : Integer_24;
  begin
    I24 := To_Integer_24(VALUE);
    TEXT_IO.Put_Line(Integer_24'Image(I24));
  end Put_Line;

begin

  DATA_1 := 2;
  M_T(1) := Fake_Data(DATA_1);
  M_T(2) := Fake_Data(100);

  TEXT_IO.Put("M_T(1) =");
  Put_Line(M_T(1));

  if M_T(1).Dnspare = 2 then
    TEXT_IO.Put_Line("Dnspare = 2");
  else
    TEXT_IO.Put_Line("Dnspare /= 2");
  end if;

  begin
    M_T(2).Dnspare := M_T(1).Dnspare;
    TEXT_IO.Put("M_T(2) =");
    Put_Line(M_T(2));
  exception
    when CONSTRAINT_ERROR =>
      TEXT_IO.Put_Line(
      "CE raised on assignment to M_T(2).");
  end;

  begin
    M_T(2).Dnspare := 2;
    TEXT_IO.Put_Line(
    "OOPS! Should not have printed this.");
  exception
    when CONSTRAINT_ERROR =>
    TEXT_IO.Put_Line(
    "CE raised when it was supposed to!");
  end;

exception
  when CONSTRAINT_ERROR =>
    TEXT_IO.Put_Line("CONSTRAINT_ERROR Raised.");
end Test;

-------------------------------------------------
The surprising (but correct) output is:

M_T(1) = 2
Dnspare /= 2
M_T(2) = 102
CE raised when it was supposed to!

Why did Ada not catch the constraint error earlier?  Because
she was specifically instructed NOT to check by the use of
UNCHECKED_CONVERSION.  The original program said:

   for M_T use at P_Mesg_Conv(P_Ohead) + 12;

   -- P_Mesg_Conv is an Unchecked_Conversion of an access
   -- type to a system address. The access type points to an
   -- incoming byte stream.

Ada was told that M_T contained valid data that did not need
to be checked, so she took your word for it.  She didn't
check.  When she came to the statement

  if M_T(1).Dnspare = 2 then
    TEXT_IO.Put_Line("Dnspare = 2");
  else
    TEXT_IO.Put_Line("Dnspare /= 2");
  end if;

she knew that M_T(1).Dnspare could not possibly be 2, so she
wisely didn't bother to check to see if was.  (At least, my
1989 vintage Alsys DOS 386 Ada compiler was smart enough to
do this, even without the optimization turned on.)

When she came to the line that said

    M_T(2).Dnspare := M_T(1).Dnspare;

she knew that M_T(1).Dnspare was already guaranteed to be in
range, so she didn't check the assignment to M_T(2).

But when came to the line that said

    M_T(2).Dnspare := 2;

she knew the value 2 had not been previously checked, so she
checked it and found it out of range.

My old DOS compiler didn't give me a compile-time warning,
but there is no requirement that it must.  I suspect my
newer Telesoft/Alsys compiler that runs on my Sparcstation 2
(in my other office 30 miles away from here) would have
given me a compile-time error because it typically tells me
that kind of error.

The point is, Ada checks limits on assignment because that
is the logical time to do it.  She doesn't check limits when
reading variables because the value should have been checked
when it was assigned.  It would just slow down the program
to do unnecessary range checks every time a variable is
read.  She doesn't check ranges when assigning a value
obtained from another object of the same subtype, because
that value has already been checked.

You can get into trouble when you use UNCHECKED_CONVERSION.
That is why it is considered "unsafe programming."  Thank
you for illustrating the point so nicely for us.



-- 
            +--------------------------------+
            |    Know                 Ada    |
            |        [Ada's Portrait]        |
            |    Will              Travel    |
            | wire do_while@ridgecrest.ca.us |
            +--------------------------------+



  parent reply	other threads:[~1994-11-20 17:16 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1994-11-18 15:27 Range Check Query Bob Wells #402
1994-11-18 12:11 ` Robert I. Eachus
1994-11-19 16:58   ` Robert Dewar
1994-11-21 10:57     ` Robert I. Eachus
1994-11-20 17:16 ` Do-While Jones [this message]
1994-11-21 16:00 ` Norman H. Cohen
1994-11-23 17:31 ` Kent Mitchell
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox