Topic: standard const conversions & arrays


Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/06/08
Raw View
John Aldridge <jpsa@uk.gdscorp.com> writes:

| [...] I'm now worried by this example:
|
|   void fred (double const * const t[2][2]) {}
|   void bert (void) { double *x[2][2]; fred (x); }
|
| in which the function call needs to convert from
|
|   pointer to array [2] of pointer to double
|
| to
|
|   pointer to const array [2] of const pointer to const double
|
| Now, I'm happy that (by the argument summarized above) the compiler
| should insert the first two consts without moaning.
|
| I don't see any wording which would justify inserting the last one,
| though.  On the other hand g++, Sun CC and HP CC all compile without
| errors, so perhaps I've missed something again.

I believe you're right.  I think your second example is indeed ill-formed
according to the current draft working paper, even though it is perfectly
safe.  It would be nice if the draft standard could be changed to allow
qualification conversions involving a mixture of arrays and pointers.

I tried your example with the IRIX 5.3 version of SGI C++ (based on the
EDG front end version 2.19), and it compiled it fine too.  However,
both g++ 2.7.3 and SGI C++ have bugs with regard to these sort of conversions.
SGI C++ accepted

 void fred (int const **t);
 void bert (int **x) { fred (x); }

which is unsafe and definitely not allowed by the DWP.
g++ 2.7.2 got that one right, but got a more complicated case wrong:
both g++ 2.7.2 and SGI C++ accepted the following ill-formed program
without any diagnostics.  The program modifies a constant without
a cast.

 #include <stdio.h>
 extern const int c;
 void fred (int const * t[2][2]) { t[0][0] = &c; }
 void bert (void) { int *x[2][2]; fred (x); *x[0][0] = 42; }
 int main() {
  bert();
  printf("%d\n", c);
  return 0;
 }
 extern const int c = 1;

I'd be interested to know whether Sun CC and HP CC report a diagnostic
for this code.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: jpsa@apollo.uk.gdscorp.com (John Aldridge)
Date: 1996/06/05
Raw View
The April DWP includes the paragraph:

#3 A conversion can add type qualifiers at levels other than the first in
#  multi-level pointers, subject to the following rules:3)
#    Two pointer types T1 and T2 are similar if there exists a type T and
#    integer N>0 such that:
#
#            T1 is Tcv1,n * ... cv1,1 * cv1,0
#    and
#
#            T2 is Tcv2,n * ... cv2,1 * cv2,0
#    where each cvi,j is const, volatile, const volatile, or nothing.  An
#    expression of type T1 can be converted to type T2 if and only if the
#    following conditions are satisfied:
#
#      --the pointer types are similar.
#
#      --for every j>0, if const is in cv1,j then const is in cv2,j,  and
#        similarly for volatile.
#
#      --the  cv1,j and cv2,j are different, then const is in every cv2,k
#        for 0<k<j.

Which makes the case

   void fred (const double * const * const t) {}
   void bert (void) { double **x; fred (x); }

(which is illegal in C) legal in C++.  I think.

Now consider the example

   void fred (const double t[2][2]) {}
   void bert (void) { double x[2][2]; fred (x); }

in which a "pointer to array[2] of double" is converted to "pointer to
const array[2] of const double".  I think this is also a 'safe' conversion,
but does not seem to be allowed by the DWP wording, which only talks about
pointers and not arrays.

1.  Have I correctly interpreted the DWP?
2.  If so, is this an intentional or accidental omission?

Cheers,
John Aldridge
jpsa@uk.gdscorp.com
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/06/05
Raw View
jpsa@apollo.uk.gdscorp.com (John Aldridge) writes:

>The April DWP includes the paragraph:
>
>#3 A conversion can add type qualifiers at levels other than the first in
>#  multi-level pointers, subject to the following rules:3)
[...]
>Which makes the case
>
>   void fred (const double * const * const t) {}
>   void bert (void) { double **x; fred (x); }
>
>(which is illegal in C) legal in C++.  I think.

Yes, that's correct.

>Now consider the example
>
>   void fred (const double t[2][2]) {}
>   void bert (void) { double x[2][2]; fred (x); }
>
>in which a "pointer to array[2] of double" is converted to "pointer to
>const array[2] of const double".  I think this is also a 'safe' conversion,
>but does not seem to be allowed by the DWP wording, which only talks about
>pointers and not arrays.
>
>1.  Have I correctly interpreted the DWP?

No.  The example is well-formed.  The chain of reasoning required
to deduce this from the DWP is rather complicated, however, so it's
quite understandable that you may have missed it.

(The remainder of this post is an edited copy of remarks I made in a
post here on comp.std.c++ regarding a very similar example posted
about three months ago.)

|   8.3.5  Functions                                             [dcl.fct]
|
| 3 [...] The type of a function is determined using the follow-
|   ing rules.  The type of each parameter  is  determined  from  its  own
|   decl-specifier-seq and declarator.  After determining the type of each
|   parameter, any parameter of type "array of T" or  "function  returning
|   T"  is adjusted to be "pointer to T" or "pointer to function returning
|   T," respectively. [...]

In this case, the type of the parameter is adjusted from "array [2] of
array [2] of const double" to "pointer to array [2] of const double".

So, we are trying to convert a value of type "array [2] of array [2] of double"
to type "pointer to array [2] of const double".  According to 4/1,

|   4   Standard conversions                              [conv]
|
| 1 Standard conversions are implicit  conversions  defined  for  built-in
|   types.  The full set of such conversions is enumerated in this clause.
|   A standard conversion sequence is a sequence of  standard  conversions
|   in the following order:
|
|   --Zero or one conversion from the following set: lvalue-to-rvalue con-
|     version, array-to-pointer conversion, and  function-to-pointer  con-
|     version.
|
|   --Zero  or one conversion from the following set: integral promotions,
|     floating point promotion, integral conversions, floating point  con-
|     versions,   floating-integral   conversions,   pointer  conversions,
|     pointer to member conversions, base class conversions,  and  boolean
|     conversions.
|
|   --Zero or one qualification conversion.

The first conversion we apply, then, is an array-to-pointer conversion.
Consulting 4.2/1,

|   4.2  Array-to-pointer conversion                          [conv.array]
|
| 1 An  lvalue or rvalue of type "array of N T" or "array of unknown bound
|   of T" can be converted to an rvalue  of  type  "pointer  to  T."   The
|   result is a pointer to the first element of the array.

we see that we can convert our value of type "array [2] of array[2] of double"
to "pointer to array [2] of double".

We don't need to apply any conversions from the second set, but we
do need to apply a qualification conversion before we can get to
"pointer to array [2] of const double".  At first glance, 4.4/1

|   4.4  Qualification conversions                             [conv.qual]
|
| 1 An  rvalue of type "pointer to cv1 T" can be converted to an rvalue of
|   type "pointer to cv2 T" if "cv2 T" is more cv-qualified than  "cv1 T."

does not seem to be of any help, since the "const" is on the wrong
side of the "array of".  However, this must be read in the context
of 3.9.3/5.

|   3.9.3  CV-qualifiers                            [basic.type.qualifier]
|
| 5 In this International Standard, the notation cv (or cv1,  cv2,  etc.),
|   used  in  the description of types, represents an arbitrary set of cv-
|   qualifiers, i.e., one of {const}, {volatile},  {const,  volatile},  or
|   the  empty  set.  Cv-qualifiers applied to an array type attach to the
|   underlying element type, so the notation "cv T," where T is  an  array
|   type,  refers to an array whose elements are so-qualified.  Such array
|   types can be said to be more (or less) cv-qualified than  other  types
|   based on the cv-qualification of the underlying element types.

In our case, T is indeed an array type.  Reading 4.4/1 with `cv T'
everywhere replaced with `array of cv T', as is required by 3.9.3,
gives us the following rule:

||  An  rvalue of type "pointer to array of cv1 T" can be converted to an
||  rvalue of type "pointer to array of cv2 T" if "cv2 T" is more
||  cv-qualified than  "cv1 T."

Applying this rule to the case in question, we can convert to "pointer
to array [2] of double" to "pointer to array [2] of const double", since
"const double" is more cv-qualified than "double".  At this point, we
have shown that there exists a standard conversion sequence which converts
the type of the actual parameter of `fred' to the type of its formal
parameter, and so it should be clear that the example is well-formed.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: John Aldridge <jpsa@uk.gdscorp.com>
Date: 1996/06/07
Raw View
I asked about

>   void fred (const double t[2][2]) {}
>   void bert (void) { double x[2][2]; fred (x); }

and fjh@mundook.cs.mu.OZ.AU (Fergus Henderson) carefully explained
that it was legal because (summarising heavily)

| 1 An  rvalue of type "pointer to cv1 T" can be converted to an rvalue
of
|   type "pointer to cv2 T" if "cv2 T" is more cv-qualified than  "cv1
T."

and

|                     Cv-qualifiers applied to an array type attach to
the
|   underlying element type, so the notation "cv T," where T is  an
array
|   type,  refers to an array whose elements are so-qualified.  Such
array
|   types can be said to be more (or less) cv-qualified than  other
types
|   based on the cv-qualification of the underlying element types.

taken together mean

||  An  rvalue of type "pointer to array of cv1 T" can be converted to
an
||  rvalue of type "pointer to array of cv2 T" if "cv2 T" is more
||  cv-qualified than  "cv1 T."

Thank you.

Whilst happy that this covers the original question, I'm now worried
by this example:

   void fred (double const * const t[2][2]) {}
   void bert (void) { double *x[2][2]; fred (x); }

in which the function call needs to convert from

   pointer to array [2] of pointer to double

to

   pointer to const array [2] of const pointer to const double

Now, I'm happy that (by the argument summarized above) the compiler
should insert the first two consts without moaning.

I don't see any wording which would justify inserting the last one,
though.  On the other hand g++, Sun CC and HP CC all compile without
errors, so perhaps I've missed something again.

Cheers,
John Aldridge
jpsa@uk.gdscorp.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]