From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=BAYES_00,INVALID_DATE autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,e258612d447226e4 X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 1994-11-20 09:46:07 PST Newsgroups: comp.lang.ada Path: nntp.gmd.de!newsserver.jvnc.net!news.cac.psu.edu!news.pop.psu.edu!hudson.lm.com!netline-fddi.jpl.nasa.gov!nntp-server.caltech.edu!news.ridgecrest.ca.us!owens!do_while From: do_while@owens.ridgecrest.ca.us (Do-While Jones) Subject: Re: Range Check Query Message-ID: Sender: usenet@ridgecrest.ca.us (Ridgenet Usenet admin) Organization: RidgeNet - SLIP/PPP Internet, Ridgecrest, CA. (619) 371-3501 References: <9411181527.AA08827@eurocontrol.de> Date: Sun, 20 Nov 1994 17:16:40 GMT Date: 1994-11-20T17:16:40+00:00 List-Id: 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 | +--------------------------------+