comp.lang.ada
 help / color / mirror / Atom feed
* Re: Range Check Query
  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-20 17:16 ` Do-While Jones
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 7+ messages in thread
From: Robert I. Eachus @ 1994-11-18 12:11 UTC (permalink / raw)


In article <9411181527.AA08827@eurocontrol.de> Bob Wells #402 <wel@EUROCONTROL.DE> writes:

 > Why does the following not raise a Constraint_Error exception?
 > (It doesn't even raise a compile time warning)

  You really have two different questions.  First

      if M_T(1).Dnspare = 2 then

  Why doesn't the compiler complain about this?  Why should it?  The
expression on the left is of TYPE Integer, and 2 is certainly
convertable to a value of TYPE Integer, and is in the range of
Integer.  The compiler is allowed to be smart and optimize this to
False.  (If it is that smart it should probably warn you of the fact
that it does so.  But I suspect it is smart enough to notice that
there are no guarentees on the value of Dnspare...)

   Second:

 > The incoming stream definitely has value of 2 occaisionaly in this
 > component yet it doesn't raise Constraint_Error?

     Your program is literally erroneous.  If the compiler was smart
enough to figure this out the "right" exception is PROGRAM_ERROR, but
anything the compiler does at this point is legal.  You have created
an illegal value in M_T(1), then read it, and you expect the compiler
to do something other than throw up its hands at this point?

     The ARG has been back and forth about this and other similar
cases.  The "best" thing to do in this case is to define a record type
in the read procedure which covers all possible values, and do
explicit range checking on that.  (The value returned can be converted
to the type with meaningful ranges.)

--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Range Check Query
@ 1994-11-18 15:27 Bob Wells #402
  1994-11-18 12:11 ` Robert I. Eachus
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Bob Wells #402 @ 1994-11-18 15:27 UTC (permalink / raw)


G'day everyone,
Hope you're all having a lovely Friday!

Why does the following not raise a Constraint_Error exception?
(It doesn't even raise a compile time warning)

  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;

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

   M_T : Dn_189_Data;
   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.

As Bart Simpson says "I didn't do it, I didn't do it!" That is,
I've been "lucky" enough to inherit this code.


OK, then in the body of this package we have:


   if M_T(1).Dnspare = 2 then

--     do something

   end if;

The incoming stream definitely has value of 2 occaisionaly in this
component yet it doesn't raise Constraint_Error?

Once again we are using the

Verdix Ada Compiler, Copyright 1984, 1992
VADSworks for Sun-4 -> MC68020/30/vxWorks, (VADS 6.0.5)
Mon Aug 17 09:11:00 EST 1992 2.0.3(b)

Any help would be greatly appreciated. Thanks and 'avagoodweegend!

@                   --------
@          ////  - ( G'day! )
@         (o o)     --------
@ ----oOO--(_)--OOo--------------------------------------------------------
  Bob Wells                      "There are more things in heaven and earth
                                  than are dreamt of in your philosophy."
@ INTERNET: wel@eurocontrol.de                 CompuServe:      100272,3004
@ The Ada WWW Server is http://lglwww.epfl.ch/Ada/                 Team Ada
@ For exciting Ada info enter 'finger wel@s4ecawel.eurocontrol.de'



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Range Check Query
  1994-11-18 12:11 ` Robert I. Eachus
@ 1994-11-19 16:58   ` Robert Dewar
  1994-11-21 10:57     ` Robert I. Eachus
  0 siblings, 1 reply; 7+ messages in thread
From: Robert Dewar @ 1994-11-19 16:58 UTC (permalink / raw)


"If the compiler is smart enough [to recognize that a comparison of a value
of limited range with a constant is always False], it should probably warn .."

First, that's not an easy check to do, it certainly doesn't fall out free,
because it requires the generalized notion of the subtype of a result, where
in the language we are only ever interested in the base type for operands
of an operator. Certainly it could be done with a special check.

Second, are you really *sure* that you want this warning. Yes I know you
can suppress warnings, but the trick is to keep warnings useful so that
people don't need to suppress them in normal cases.

I an very dubious that this is a desirable approach

Robert Eachus' analysis of the original question is certainly quite
correct, there is no basis to expect range constraint to be raised
in either situation.




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Range Check Query
  1994-11-18 15:27 Range Check Query Bob Wells #402
  1994-11-18 12:11 ` Robert I. Eachus
@ 1994-11-20 17:16 ` Do-While Jones
  1994-11-21 16:00 ` Norman H. Cohen
  1994-11-23 17:31 ` Kent Mitchell
  3 siblings, 0 replies; 7+ messages in thread
From: Do-While Jones @ 1994-11-20 17:16 UTC (permalink / raw)



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 |
            +--------------------------------+



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Range Check Query
  1994-11-19 16:58   ` Robert Dewar
@ 1994-11-21 10:57     ` Robert I. Eachus
  0 siblings, 0 replies; 7+ messages in thread
From: Robert I. Eachus @ 1994-11-21 10:57 UTC (permalink / raw)


In article <3alasi$9eg@gnat.cs.nyu.edu> dewar@cs.nyu.edu (Robert Dewar) writes:

 > First, that's not an easy check to do, it certainly doesn't fall out free,
 > because it requires the generalized notion of the subtype of a result, where
 > in the language we are only ever interested in the base type for operands
 > of an operator. Certainly it could be done with a special check.

   It is better done with a flow analysis that tracks ALL the values a
scalar variable can have at that point in the program, and it is
fairly useless to do this in Ada unless you are willing to not only do
inter-procedural analysis, but to look at all units in the library
(and create additional dependences...)

  > Second, are you really *sure* that you want this warning. Yes I know you
  > can suppress warnings, but the trick is to keep warnings useful so that
  > people don't need to suppress them in normal cases.

    IF you need this kind of compilation tool, then yes you do want
the warning.  It is not the sort of thing you do in a casual
development environment.  It is the sort of tool you want when doing
safety-critical code, and under those conditions you sometimes want a
warning for ANY statement that can cause a predefined exception to be
raised.  In fact, I have built tools to "browse" compiled code looking
for the sequences for raising predefined exceptions.

    But you have to start with a "very smart" compiler for such a tool
to be useful.

  > I an very dubious that this is a desirable approach

     It is, but it is not for ordinary software.

  > Robert Eachus' analysis of the original question is certainly quite
  > correct, there is no basis to expect range constraint to be raised
  > in either situation.

    Thanks, and I agree, except as mentioned above.  It is a shame
that any useable language definition requires that we allow erroneous
programs to exist, but that reflects reality and Goedels' Proof.  Even
in safety-critical systems you sometimes need to write code which is
technically erroneous, and there are cases where the compiler just
can't help you--you need a good programmer who knows the potential
pitfalls.  The best you can hope for is a compiler that does a good
job with warnings of saying "Here be Dragons."

    Is looking at all the possible places that exceptions can be
raised worthwhile?  Yes, I've found it so.  When I have done it, again
with ferociously optimized code, about one third to one forth of the
occurrences tagged were potential errors.  (Lines were tagged if they
were not in the scope of a local exception handler, and could raise an
exception.)  On the other hand 90% of those potential errors were for
situations only of concern in a safety analysis--for example, opening
a file without a handler for Device_Error.




--

					Robert I. Eachus

with Standard_Disclaimer;
use  Standard_Disclaimer;
function Message (Text: in Clever_Ideas) return Better_Ideas is...



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Range Check Query
  1994-11-18 15:27 Range Check Query Bob Wells #402
  1994-11-18 12:11 ` Robert I. Eachus
  1994-11-20 17:16 ` Do-While Jones
@ 1994-11-21 16:00 ` Norman H. Cohen
  1994-11-23 17:31 ` Kent Mitchell
  3 siblings, 0 replies; 7+ messages in thread
From: Norman H. Cohen @ 1994-11-21 16:00 UTC (permalink / raw)


In article <9411181527.AA08827@eurocontrol.de>, Bob Wells #402
<wel@EUROCONTROL.DE> writes: 

|> Why does the following not raise a Constraint_Error exception?
|> (It doesn't even raise a compile time warning)
|>
|>   type dn18906 is  -- layout is for the 18906 message  .
|>     record
|>       dnspare  : integer range 0 .. 1;
...
|>     end record;
|>
|>    for dn18906 use
|>      record at mod 1;
...
|>      end record;
|>
|>    for dn18906'size use 24;
|>
|>    type Dn_189_Data is array(1 .. N_Data) of Dn18906;
|>    pragma Pack(Dn_189_Data);
|>
|>    M_T : Dn_189_Data;
|>    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.
...
|> OK, then in the body of this package we have: 
|>
|>
|>    if M_T(1).Dnspare = 2 then
|>
|> --     do something
|>
|>    end if;
|>
|> The incoming stream definitely has value of 2 occaisionaly in this
|> component yet it doesn't raise Constraint_Error?

As others have pointed out, the actual error arises during the unchecked
conversion.  In the words of RM 13.10.2(3), "Whenever unchecked
conversions are used, it is the programmer's repsonsibility to ensure
that these conversion maintain the properties guaranteed by the language
for objects of the target type.  Programs that violate these properties
by means of unchecked conversions are erroneous."

This leaves the question of why unchecked conversion doesn't raise
Constraint_Error.  The answer:  If it did, they'd have to call it checked
conversion.

The solution to your problem:  In Ada 9X, use the attribute M_T(1)'Valid;
in Ada 83, check the validity of your bits before uncheckedly converting
to dn18906.  One way to do this is to define a type in which each
possible combination of bits represents a valid value, e.g.: 

   type Raw_Unvalidated_dn18906 is  -- layout is for the 18906 message  .
     record
       dnspare  : integer range 0 .. 3; -- instead of 0 .. 1
...
     end record;

    for Raw_Unvalidated_dn18906 use
      record
         dnspare at 0 range 0 .. 1;
...
      end record;

Uncheckedly convert to (a pointer to) this type first, which is
guaranteed not to be erroneous because every bit pattern represents a
valid value of this type.  If the dnspare component is 2 or 3, reject the
data as invalid.  Otherwise, use it as valid data.  It's a matter of
taste whether, having performed this explicit test, you want to convert
to (a pointer to) dn18906 afterward, to document the fact that the data
has been validated and is known to obey the constraints of that type.

--
Norman H. Cohen    ncohen@watson.ibm.com



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Range Check Query
  1994-11-18 15:27 Range Check Query Bob Wells #402
                   ` (2 preceding siblings ...)
  1994-11-21 16:00 ` Norman H. Cohen
@ 1994-11-23 17:31 ` Kent Mitchell
  3 siblings, 0 replies; 7+ messages in thread
From: Kent Mitchell @ 1994-11-23 17:31 UTC (permalink / raw)


Bob Wells #402 (wel@EUROCONTROL.DE) wrote:
:   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;

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

:    M_T : Dn_189_Data;
:    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.

: OK, then in the body of this package we have:


:    if M_T(1).Dnspare = 2 then

: --     do something

:    end if;

: The incoming stream definitely has value of 2 occaisionaly in this
: component yet it doesn't raise Constraint_Error?

: Once again we are using the

: Verdix Ada Compiler, Copyright 1984, 1992
: VADSworks for Sun-4 -> MC68020/30/vxWorks, (VADS 6.0.5)
: Mon Aug 17 09:11:00 EST 1992 2.0.3(b)

Hmmm ... this is an interesting program from a number of aspects.  I don't
think you'd get a constraint error because there never is a checked
assignment.  You will not get a range check from unchecked_conversion nor
will you get a range check on the boolean expression (because it's not an
assignment).  

The second interesting problem is that I'd never expect the true arm of the
if to be taken as the compiler should have supressed this as dead code (of
course that depends on the optomization level).  I do see how it "could"
take the arm if it's not supressed because the code *does* allocate 2 bits
for the Dnspare field so a value of 2 *is* possible.

To sum this all up, given a basically eronious program, anything is
possible.

--
Kent Mitchell                   | One possible reason that things aren't
Technical Consultant            | going according to plan is .....
Rational Software Corporation   | that there never *was* a plan!



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~1994-11-23 17:31 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
1994-11-21 16:00 ` Norman H. Cohen
1994-11-23 17:31 ` Kent Mitchell

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