[comp.lang.ada] Ada Constraints

WILSON@AMSTEL.llnl.gov (One Heppy Heppy 'Ket') (08/15/90)

Today's Ada Challenge:

   How can I force range checks on data presented to my Ada program from a
foreign (viz., C) routine?

   Here's the problem.  We have many Ada programs which receive many messages
from many C programs.  These C programs occasionally (erroneously) send
messages with values not in the ranges defined by our interface, and hence not
in the ranges defined by the associated Ada datatypes.

   By the time Ada gets involved, the data is already loaded (by the non-Ada
network routines) into an Ada record-type variable.  Our Ada compiler
(VAX Ada V1.5) sees no reason to perform any range checks, since it didn't do
the actual assignment which loaded the variable.  Eventually the program
performs an array index based on a component of the record variable, and gets
an access violation, or worse, crashes a task, or who-knows-what.

   My feeling is that I would like to force a range check on all components
of the record as soon as it is read in.  Ideally, I would like to perform this
check in the generic READ routine in my Ada interface to the network I/O
system.  However, even knowing the details of the record doesn't necessarily
help.

   Here are some ideas I considered, and then discarded:

	1.  Assignment to another variable of the same type.
	    Unfortunately, a smart optimizer (such as ours), will determine that
	    no check is necessary, assuming that the source variable has already
	    been checked.

	2.  Assignment to a variable of a derived type based on the record type.
	    The same problem exists as with idea (1), except that the optimizer
	    needs to be slightly smarter.

	3.  Try checking discrete fields individually, using "in".
	    Same problem as (1).

	4.  Try checking discrete fields individually, using predefined
	    operators.
	    For example, for an integer subrange, perform the test:

		if MESSAGE.INT /= INT_TYPE'last then
		    begin
			TEMP := INT_TYPE'succ( MESSAGE.INT );
		    exception
			when CONSTRAINT_ERROR =>
			    raise RANGE_CHECK_ERROR;
		    end;
		end if;

This last one (4) I think would work, but given the large number of programs,
message types, and components in each message, (and the fact that it couldn't
be implemented in the generic READ routine), this would be a major undertaking,
and difficult to maintain and enforce over the project lifecycle.  It would
also add more overhead than necessary to the program, by performing the
increment.  It's also possible that a smart compiler might only raise
CONSTRAINT_ERROR if the new value is exactly one greater than the upper limit,
checking with "=" rather than ">", in which case even this test wouldn't work.

   So that's the story.  I wish there were a pragma "CHECK" which could be
placed anywhere in the proper body of a subprogram.  In the absence of that,
I'll welcome any suggestions from the net.

   Thanks in advance,

				--- Rick Wilson
				    Lawrence Livermore National Laboratory
				    (415) 423-6662
				    wilson@derby.llnl.gov

bhanafee@ADS.COM (Brian Hanafee) (08/17/90)

In article <4CE1FEE5846FC025DB@icdc.llnl.gov> WILSON@AMSTEL.llnl.gov (One Heppy Heppy 'Ket') writes:
>Today's Ada Challenge:
>
>   How can I force range checks on data presented to my Ada program from a
>foreign (viz., C) routine?
>

[stuff deleted]

>   By the time Ada gets involved, the data is already loaded (by the non-Ada
>network routines) into an Ada record-type variable.  Our Ada compiler
>(VAX Ada V1.5) sees no reason to perform any range checks, since it didn't do
>the actual assignment which loaded the variable.  Eventually the program
>performs an array index based on a component of the record variable, and gets
>an access violation, or worse, crashes a task, or who-knows-what.

[list of possible approaches deleted]

>
>   Thanks in advance,
>
>				--- Rick Wilson
>				    Lawrence Livermore National Laboratory
>				    (415) 423-6662
>				    wilson@derby.llnl.gov

How about defining a generic like this:

generic
    type The_Range_Limited_Integer_Type is range <>;
function Checked (Item : in Integer)
  return The_Range_Limited_Integer_Type;


function Checked (Item : in Integer)
  return The_Range_Limited_Integer_Type is
begin
    return The_Range_Limited_Integer_Type (Item);
end Checked;

and using it like this:

    function Checker is new Checked (The_Element_Type);

    The_Record.The_Element := Checker (The_Record.The_Element);


Since the compiler doesn't know how the generic function is used,
it can't assume the range will be good in all cases, so it should
have to embed a range check in the function instantiation.  The
only time I can think of when the optimizer might catch this is
if Checker gets inlined rather than called.

You'll have to write generics for all the possible root types
(e.g. float, long_integer, etc.).  Enumerated types might be
a little trickier.


Brian Hanafee
Advanced Decision Systems

prune@foxinsox.hf.intel.com.ogi.edu (08/17/90)

In article <4CE1FEE5846FC025DB@icdc.llnl.gov> WILSON@AMSTEL.llnl.gov (One Heppy Heppy 'Ket') writes:
>
>    How can I force range checks on data presented to my Ada program from a
> foreign (viz., C) routine?
>
>    Here's the problem.  We have many Ada programs which receive many messages
> from many C programs.  These C programs occasionally (erroneously) send
> messages with values not in the ranges defined by our interface, and hence not
> in the ranges defined by the associated Ada datatypes.
>
>    By the time Ada gets involved, the data is already loaded (by the non-Ada
> network routines) into an Ada record-type variable.  Our Ada compiler
> (VAX Ada V1.5) sees no reason to perform any range checks, since it didn't do
> the actual assignment which loaded the variable.  Eventually the program
> performs an array index based on a component of the record variable, and gets
> an access violation, or worse, crashes a task, or who-knows-what.

   The way I read this, your design problem is that you lack the direct
language tools to define the *true* interface.  C only recognizes certain
types, and sees no fault with the values it hands to the Ada routines.
The Ada compiler trusts that the programmer has taken care of any inter-
language issues, so that you effectively have an unchecked conversion from
C to Ada.  Your Ada compiler has a fairly intelligent optimizer, so you
can't fool it simply by sticking a finger up your nose and doing a little
dance.
   I recommend "properly" defining the interface from the Ada side.  Don't
try to fool the optimizer: if Digital upgrades it in 2.0, you could find
yourself SOL the next time you build your message system.
   For illustration, I will assume that your input contains an integer
subtype, an enumeration type, and a text message.

max_error_number:	constant integer := 150;
type snafu_number is byte_integer range 0 .. max_error_number;

type snafu_type is (no_problem, drain_clog, gfci_fault, incoming_missile,
	dirty_diaper, <etc.> );

synopsis_limit: constant integer := 40;
type snafu_string is string (1 .. synopsis_limit);

type C_message is record	-- This is what C sees
   error_number:	byte_integer;
   error_class:		byte_integer;
   error_text:		string(1 .. 40);
end;

-- And now, the Ada side of the story:

type Ada_message is record	-- How Ada *wants* to see it
   numeric_code:	snafu_number;
   error_type:		snafu_type;
   synopsis:		snafu_string;
end;

-- copy a C message into its Ada equivalent

function convert_C_message_to_Ada_message (give: in C_message)
	return Ada_message is

  take: Ada_message;

begin
  Ada_message.numeric_code := C_message.error_number;
  Ada_message.error_type := C_message.error_class;
  Ada_message.synopsis := C_message.error_text;
exception
  -- handle range errors here to your heart's content
end convert_C_message_to_Ada_message;


   This code can be added to your current system, in the Ada package that
gets the information from the C routine.  Simply feed a C_message to the
C routine that is providing you with the input; then copy that to the
Ada_message where you truly wanted the information.  You've given the
compiler all the information it can use about what the two records *really*
contain, and it can optimize and/or in-line as it sees fit.  I'm confident
that the Digital compiler, like ours, will optimize out the redundant
portions of the checks, and will make short work of what remains.
--------------------------------------------------------------------------------
DISCLAIMER: Intel pays me well for the opinions it wants; the other stuff
   remains mine. Some of the latter is posted; it's worth what you paid for it.

A buddhist nudist practices yoga bare.

local backbone: tektronix	myrddyn!prune@uunet.uu.net
USA phone:  (503) 696-4668	wickart@ijf2.hf.intel.com
Copyright (C) 1990, William D. Wickart.  All rights reserved.