From: MCCRADY@TOROLAB6.VNET.IBM.COM ("Don McCrady")
Newsgroups: comp.std.c
Subject: const T**
Date: 16 Apr 92 14:51:18 GMT

>MCCRADY@TOROLAB6.VNET.IBM.COM ("Don McCrady") writes:
>>> From: donne@imec.be (Werner Donne)
>>>    const char **a;
>>>    char **b;
>>>   a = b;
>>> Why is the assignment illegal?
>>
>>This has been illegal ever since C, and I assume C++ hasn't relaxed
>>C's rules.  The relevant rule from the assignment operator, ANSI C
>>3.3.16.1, is:
>
>Right so far.
>
>>    One of the following shall hold:
>>        ...
>>        - both operands are pointers to qualified or unqualified
>>          versions of compatible types... [1]
>>
>>[1] Omitted is the phrase "and the type pointed to by the left has
>>all the qualifiers of the type pointed to by the right," which is
>>irrelevant to this discussion.
>
>Hardly irrelevant.  The additional phrase says that the LHS must have
>at least all of the type qualifiers (const and/or volatile - see 3.5.3)
>of the RHS but the opposite isn't necessarily true.
>[...]
>>Note that by similar reasoning, all of the following are illegal:
>>       b = a;
>No, this is perfectly legal.  That's what the additional phrase means.
>The RHS has no type qualifier so the LHS doesn't need one but the
>existence of one doesn't violate the rule.
>
>>       a == b;  /* or !=, or <, etc... */
>>       b == a;  /* as above */
>These are legal but from a different but similar rule in 3.3.9.

Sorry, but I don't think so.  This is a bit hairy.  In order to cover
all the bases, I'll pick a truly ugly example, and go step-by-step
through the type-checking:

      const char *const *a;
      char **b;

      a = b;

Find the constraints for the operands of '='.  These are stated in
ANSI C 3.3.16.1, "Simple Assignment".  Since 'a' is a pointer and
'b' is a pointer, the following constraint is chosen:

    both operands are pointers to qualified or unqualified
    versions of compatible types, and the type pointed to by
    the left has all the qualifiers of the type pointed to by
    the right."

Now, let 'a' have type "pointer to T1" and let 'b' have type
"pointer to T3".

1) Let's deal with the second part of the constraint first, and
   restate it thusly:  "does T1 have all the qualifiers of T2?"
   T1 is a "const pointer" and T2 is a "pointer", so the answer
   is "yes".  Thus, this constraint is satisfied, so the assignment
   is valid *so far*.  Note that this step would be unnecessary
   if T1 and T2 had no qualifiers; in the original post, the
   example was
             const char **x;
             char **y;
             x = y;
   in which T1 and T2 do not have qualifiers; in that example, this
   portion of the rule was completely irrelevant.

2) Now deal with the first part of the constraint, which is really
   what your question is about.  We can restate it as "when we
   ignore the qualifiers on T1 and T2, do they have compatible
   types?"  Let T1' (T1-primed) be the unqualified version of T1;
   T1 was "const pointer to T3", so now it is "pointer to T3".
   Let T2' (T2-primed) be the unqualified version of T2;  T2 was
   "pointer to T4", so now it is "pointer to T4" (i.e. unchanged).
   Note that it was here -- and only here -- that we ignored the
   qualifiers, because we were explicitely told to do so by the
   constraints of '='.

   Now, in order to fully satisfy the constraint, T3 and T4 must
   be compatible types (note that this means "strictly compatible",
   in other words, from here on down, we can no longer ignore
   qualifiers).

3) T3 has type "const char" and T4 has type "char".  ANSI C 3.5.3,
   "Type Qualifiers", states:

        For two qualified types to be compatible, both shall
        have the identically qualified version of a compatible
        type.

   Thus, T3 ("const char") is not compatible with T4 ("char"), which
   means that T2' is not compatible with T3', which means that T1
   is not compatible with T2, and which finally means that the
   assignment between 'a' and 'b' is not valid.

Similar steps can be performed to prove that '==' and '!=', etc. are
illegal.

I know several C compilers that enforce this strict level of type
compatibility checking for assignment, but none that enforce it for
equality (yet).

C compilers can get away with being lax about this, but C++ can't,
otherwise it would have trouble resolving overloaded functions and
user-defined conversions.

++don;

IBM Canada, Toronto