[net.lang] Unchecked conversion in Ada

macrakis@harvard.ARPA (Stavros Macrakis) (06/18/85)

More Ada misinformation.  This is getting dull.

In my article <192@harvard.ARPA>, I wrote:
> > ...unchecked_conversion, is so simple to implement (you just turn
> > off type checking!)....  This is the main thing you need to do
> > C-like low-level pointer manipulations.

Darin Johnson replies: <266@sdcc13> ...say C programmers start to use
> Ada....  [what] if they ... [p]ut unchecked_conversion at the start
> of all their programs.  Then when they wanted to make distributions,
> they would remove it.... [and eventually] suggest that untyped_
> conversion should be the default...

This is not how Unchecked_conversion works.  It is a generic function
which can be instantiated to produce a function which converts
between any two types bit-to-bit; something like C's casts.  So there
is no danger of global unchecked conversions.  See Appendix for an
example (or study Ada before talking about it!).

Thus, although it is in principle possible to commit the usual C
uglinesses in Ada, the language reminds you (...!) whenever you're
slipping into low-think.  I was simply refuting the argument `you
can't do xxx in Ada', not saying that you ought to do xxx in Ada!

	-s

Appendix  How to use unchecked_conversion in Ada

--  Suppose the type of the records in a file is determined in the
--  file itself.  Then you cannot expect to use the standard Ada
--  sequential I/O package.  Instead, you read into a binary buffer
--  and use unchecked_conversion.

type rec is ...;  subtype bufslice is ...;
...
function Buf_to_rec(words: bufslice) return rec
	is new Unchecked_conversion(bufslice,rec);  -- no code generated
...
thisrec := Buf_to_rec(Buffer(i..i+rec'size-1));	
		-- assuming Buffer is an array of storage-units.

Alternatively, you can use pointers:
...
type recptr is access rec;
...
function Bufaddr_to_recptr(adr: address) return recptr
	is new Unchecked_conversion(address,recptr);  -- no code generated
...
thisrecptr := Bufaddr_to_recptr(Buffer(i)'address);
		  -- no code generated for conversion nor 'address

guy@sun.uucp (Guy Harris) (06/20/85)

> a function which converts between any two types bit-to-bit; something
> like C's casts.

C casts are not bit-for-bit conversions unless the conversion happens to be
implemented as such.  This is the case for signed <-> unsigned conversions
on two's complement machines, pointer <-> integer conversions, and pointer
<-> pointer conversions in most cases on byte-addressible machines.

C compilers don't complain about conversions between pointers of different
types, nor do they complain about various other type conversions.  "lint"
will complain about more of them.  I've found bugs in kernel code I've
written that would have been found by "lint" (for those of you who think
strong type checking is a Bad Thing in system code).

Note that the idea of bit-for-bit conversion operators dates back to PL/I's
"unspec" (which, like PL/I's pointers, was typeless); Mesa has a bit-for-bit
type conversion operator "loophole" similar in concept to Ada's.

	Guy Harris

macrakis@harvard.ARPA (Stavros Macrakis) (06/21/85)

> > ...converts between...two types bit-to-bit; something like C's casts.

> C casts are not bit-for-bit conversions unless the conversion happens
> to be implemented as such...

I would put it differently: C casts ARE bit-for-bit conversions except
sometimes for numerical types.  To the extent that one can speak of C
having any semantics at all, this makes the semantics of C casts grossly
inconsistent.  For instance, (int) 2.3 == 2, but (*var=2.3, *((int *)
var) ) == garbage; I don't even want to begin to think about the
semantics of conversions involving unsigned's.

I also don't understand what `happens to be implemented as' means.  What
other possibilities are there for converting pointers to integers,
incompatible pointers, etc.?

So perhaps it was a bad idea to compare Unchecked_conversion in Ada to C
casts.  I just wanted to use a reference point familiar to C users.

	-s

guy@sun.uucp (Guy Harris) (06/23/85)

> > > ...converts between...two types bit-to-bit; something like C's casts.
> 
> > C casts are not bit-for-bit conversions unless the conversion happens
> > to be implemented as such...
> 
> I would put it differently: C casts ARE bit-for-bit conversions except
> sometimes for numerical types.

"You play Bach your way; I will play him his way."

	-W. Landowska

You can read K&R your way; I'll read it their way.  The only mention they
make of a conversion being a copy of the bits is under the int-to-unsigned
conversion:

	The value is the least unsigned integer congruent to the signed
	integer (module 2^wordsize).  *In a 2's complement representation*,
	this conversion is conceptual and there is no actual change in
	the bit pattern.

Maybe they should have left the sentence about 2's complement representation
out, so people wouldn't think of it as a bit-for-bit copy.

As for pointer-to-integer conversions:

	A pointer may be converted to any of the integral types
	large enough to hold it. ... The mapping function is also
	machine dependent, but is intended to be unsurprising to those
	who know the addressing structure of the machine.

This may be a bit-for-bit copy on most machines, but it need not be.

> To the extent that one can speak of C having any semantics at all, this
> makes the semantics of C casts grossly inconsistent.  For instance,
> (int) 2.3 == 2, but (*var=2.3, *((int *) var) ) == garbage;

So?  Presumably, in other languages (like Ada), if you convert 2.3 to an
integer, you get 2, while if you do an (unchecked) conversion of a pointer
to a floating-point variable into a pointer to an integer, and then
dereference that pointer, you also get an integer with the same bit pattern
as the floating-point number in that variable (which isn't "garbage" if
you're picking the bits apart).

> I don't even want to begin to think about the semantics of conversions
> involving unsigned's.

Why not?  The C rules are mathematically reasonable; what else would you do,
aside from generating an exception of the signed quantity is negative.  Or
is the lack of checking what you object to?  I might agree with you on that
one, although C doesn't have an exception mechanism (people have debated the
merits of general exception mechanisms, so they're not considered a Good
Thing by the entire community).

> I also don't understand what `happens to be implemented as' means.  What
> other possibilities are there for converting pointers to integers,
> incompatible pointers, etc.?

Well, I'm not sure why you have trouble understanding it, but I'll explain;
I was referring to the int <-> unsigned conversion, which happens to be
implemented that way on 2's complement machines.  Pointer-to-pointer
conversions aren't necessarily bit-for-bit on word-addressed machines; byte
pointers (like "char *") and word-pointers (like all other pointers might
be) don't have the same bit pattern (or even the same number of bits) in all
implementations.

> So perhaps it was a bad idea to compare Unchecked_conversion in Ada to C
> casts.  I just wanted to use a reference point familiar to C users.

A better comparison would point out that C has no formal unchecked
conversions, and as such certain conversions which should require the
programmer to say "yes, I'm cheating, but I know what I'm doing" don't.  If
C had such a concept, conversion of pointers to integers, integers to
pointers, and pointers to other types of pointers should require that the
programmer explicity specify the conversion as unchecked, so that they're at
least reminded that they're Doing Something Potentially Nasty.

In practice, the portable C compiler does generate warnings for all three of
those cases; you can defeat those warnings by doing the cast on the RHS of
the assignment instead of by default in the assignment.  However:

	1) this is a gross hack, since a cast is merely a way to explicitly
	   indicate a coercion - or at least that's what it should be ("An
	   expression preceded by the parenthesized name of a data type
	   causes conversion of the value of the expression to the named
	   type.")  "a = b" and "a = (typeof a)b" should be equivalent
	   expressions.

	2) warnings can be ignored, so to be truly secure it should give
	   something stronger than a warning.

C certainly has its share of things to criticize; however, a large number of
them are shared by other popular languages of its general class.  Criticism
should be accurate and fair; I think the point you really want to make
(i.e., that certain conversions in C should not be so cavalierly accepted)
can be made in a better way (by saying as much, rather than by dismissing C
casts as bit-for-bit copies).

	Guy Harris