[comp.lang.c++] type compatibility

waldemar@mit-vax.LCS.MIT.EDU (Waldemar Horwat) (01/21/89)

Sender: 
Reply-To: waldemar@mit-vax.UUCP (Waldemar Horwat)
Followup-To: 
Distribution: 
Organization: MIT Laboratory for Computer Science, Cambridge
Keywords: 

Given the declarations below, which of the following seven statements
(numbered 1, 2, 4, 5, 6, 7, and 8) are acceptable in a "portable" C++ program?
Specifically, I am trying to clarify the following issue:
  If a pointer to B can be passed to a function that takes a pointer to A,
  then can a pointer to a pointer to B be passed to a function that takes a
  pointer to a pointer to A?  What if A is void?
I'd also be interested in hearing what real compilers do with the following
code.
struct A {int x;};
struct B:A {int y;};

extern void f1(void *);
extern void g1(A *);
extern void i1(A &);

extern void f2(void **);
extern void g2(A **);
extern void h2(void *&);
extern void i2(A *&);

B b,*pb,**ppb;

void test()
  {
  f1(pb);   //1
  g1(pb);   //2
  i1(b);    //4
  
  f2(ppb);  //5
  g2(ppb);  //6
  h2(pb);   //7
  i2(pb);   //8
  }

				Waldemar Horwat
		      Internet: waldemar@vx.lcs.mit.edu

ark@alice.UUCP (Andrew Koenig) (01/21/89)

In article <5485@mit-vax.LCS.MIT.EDU>, waldemar@mit-vax.LCS.MIT.EDU (Waldemar Horwat) writes:

> Given the declarations below, which of the following seven statements
> (numbered 1, 2, 4, 5, 6, 7, and 8) are acceptable in a "portable" C++ program?

> struct A {int x;};
> struct B:A {int y;};

> extern void f1(void *);
> extern void g1(A *);
> extern void i1(A &);

> extern void f2(void **);
> extern void g2(A **);
> extern void h2(void *&);
> extern void i2(A *&);

> B b,*pb,**ppb;

Here are my comments, along with the messages that appear
when I run it on the (still experimental) cfront on my machine.

> void test()
>   {
>   f1(pb);   //1
		OK.  f1 takes a void* argument so you can give it
		a pointer to anything at all.
>   g1(pb);   //2
		OK.  g1 takes an A*, you've given it a B*, and
		B is (publicly) derived from A.
>   i1(b);    //4
		OK.  References are just like pointers.  i1 takes
		an A&, you've given it a B&.
>   f2(ppb);  //5   bad argument 1 type for f2(): B ** (void ** expected)
		No.  You can convert anything to void* but you can't
		convert anything to void**.  f2 takes a void** and ppb
		is a B**.
>   g2(ppb);  //6   bad argument 1 type for g2(): B ** (A ** expected)
		No.  Again, you can't treat a pointer to an B* as if
		if it were a pointer to an A*.
>   h2(pb);   //7   warning: coercion of lvalue needed: temporary used
			to initialize reference
		Yes (!).  h2 wants a reference to a void* and you've
		given it a B*.  So it converts the B* to a void*
		(no problem there) and binds the reference to that.
>   i2(pb);   //8   warning: coercion of lvalue needed: temporary used
			to initialize reference
		Yes -- same reason.  You can convert pb to an A*
		and then bind a reference to it.
>   }
-- 
				--Andrew Koenig
				  ark@europa.att.com

waldemar@mit-vax.LCS.MIT.EDU (Waldemar Horwat) (01/22/89)

>   f1(pb);   //1
>   g1(pb);   //2
>   i1(b);    //4
>   f2(ppb);  //5
>   g2(ppb);  //6
>   h2(pb);   //7
>   i2(pb);   //8

I tested the following two compilers so far and got different results:

AT&T CFront 1.2.1 2/16/87 accepts all seven statements and produces the
expected code.

GNU g++ 1.25.0 accepts all statements except 5.  I find it unusual that it can
coerce **B to **A but not **B to **void.

Given the diversity of compiler behavior in this area, I am curious as to what
the "right" behavior, if any, is.

				Waldemar Horwat

paul@gill.UUCP (Paul Nordstrom) (11/27/89)

While attempting to port InterViews to the Oregon C++ compiler, I came
across the following code (simplified drastically):


typedef const void* TextData;

static TextData SELF = (void*)-1;

class Text {
public:
    Text(TextData context = SELF);
protected:
    TextData context;
    int size;
};

class Whitespace : public Text {
public:
    Whitespace(int size, TextData context = SELF);
    virtual Text* Copy();
};

Text* Whitespace::Copy () {
#ifdef OREGON
  if ( context == this )
     return new Whitespace(size, SELF);
  else
     return new Whitespace(size, context);
#else
     return new Whitespace(size, context==this ? SELF : context);
#endif
}


The portion of code that is compiled when OREGON is defined compiles
(presumably) correctly under Oregon C++ (1.2Ag).  The #else side is
the code is as distributed in the Interviews release currently on
uunet.  Obviously (he says blithely :-) ) the two sections of code
are equivalent.  The Oregon compiler ("occ") yields the following
error message on the original code:


   27 *         return new Whitespace(size, context==this ? SELF :
context);
                                                          ^203
*** 203: Error: Operands are of differing or incompatible type


In case the spacing gets garbled, the ^203 is pointing to the
question mark in the line of code, indicating that that is where the
compiler thinks the error is.  Assuming that I am correct that the
two pieces of code are in fact equivalent, clearly occ has a bug of
some kind: one piece compiles, one does not.  My question is: are
both pieces of code correct, or both incorrect.  The issue I see is:
should the compiler allow the test "context==this" when context is a
void* and this is a Whitespace* ?  Any comments, anyone?


-- 
Paul Nordstrom
Gill & Co., L.P.
uunet!gill!paul

dan@oresoft.uu.net (Daniel Elbaum) (12/05/89)

In article <754@gill.UUCP> paul@gill.UUCP (Paul Nordstrom) writes:
:While attempting to port InterViews to the Oregon C++ compiler, I came
:across the following code (simplified drastically):

[simplified even more drastically:]

     Whitespace(int size, TextData context = SELF);

static const void* SELF = (void*)-1;
:
:Text* Whitespace::Copy () {
:#ifdef OREGON
:  if ( context == this )
:     return new Whitespace(size, SELF);
:  else
:     return new Whitespace(size, context);
:#else
:     return new Whitespace(size, context==this ? SELF : context);
:#endif
:}

The OREGON code compiles under occ; the conditional expression
gives this error:

:   27 *         return new Whitespace(size, context==this ? SELF : context);
:                                                          ^203
:*** 203: Error: Operands are of differing or incompatible type

The questions: 1) are the two bodies of code equivalent?
2) if so, are they both correct or are they incorrect?

One difference between the two fragments is that the first does
not impose restriction on the compatibility of |SELF| and |context|,
while the second does require that they both be arithmetic types,
or a mix of pointers and null constant expressions, or both
references, or both of type |void|.

In this case the types are both susceptible to conversion to a
common type, namely |void*|, so the type of the result will
be |void*|.  This type is not valid for the return value of
the function |Copy()|.
-- 
The disbelievers say: "Lo!  This is a mere wizard."  -Quran, Surah X

({uunet,tektronix,reed,sun!nosun,osu-cis,psu-cs}!oresoft!(dan)@oresoft.uu.net)