Topic: Array function params and const qualifier


Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/04/11
Raw View
In article rp3@mozart.wg.icl.co.uk, Adel El-Beik <A.El-Beik@man0506.wins.icl.co.uk> writes:
>I have a question concerning MSVC++ v.4.00 and the April DWP C++.
>
>The following code fails on MSVC++.  It doesn't like line labelled 1.
>It complains with
>
>C:\array\array.cpp(16) : error C2664: 'func' : cannot convert parameter 1
>from 'long [2][2]' to 'const long [][2]' (new behavior; please see help)

You have run into effectively the same rule that prohibits implicit
conversion from "T**" to "const T**". That prohibition is not new,
but is in the ISO/ANSI C standard, and has not been changed, since
it would open a hole in the type system.

I believe the subject is covered in the comp.lang.c FAQ list.

---
Steve Clamage, stephen.clamage@eng.sun.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
]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/04/12
Raw View
clamage@Eng.Sun.COM (Steve Clamage) writes:

>Adel El-Beik <A.El-Beik@man0506.wins.icl.co.uk> writes:
>>I have a question concerning MSVC++ v.4.00 and the April DWP C++.
>>
>>The following code fails on MSVC++.  It doesn't like line labelled 1.
>>It complains with
>>
>>C:\array\array.cpp(16) : error C2664: 'func' : cannot convert parameter 1
>>from 'long [2][2]' to 'const long [][2]' (new behavior; please see help)
>
>You have run into effectively the same rule that prohibits implicit
>conversion from "T**" to "const T**". That prohibition is not new,
>but is in the ISO/ANSI C standard, and has not been changed, since
>it would open a hole in the type system.

I suspect you are mistaken.  I believe Adel El-Beik has run into a bug
in MSVC++, rather than any prohibition in the draft C++ standard.
By my reading of the January 96 draft, I believe Adel El-Beik's code is
well-formed (i.e. legal).

[moderator's note: I agree I was wrong. Sorry for the confusion. -sdc ]

>I believe the subject is covered in the comp.lang.c FAQ list.

The comp.lang.c FAQ list may cover the case of conversion from `T**' to
`const T**' (although I couldn't find it in my old copy), but I'm sure
it does not cover the case of conversion from `T[n][m]' to `const
T(*)[m]', which I think is quite safe, and which is, I believe,
permitted by the draft C++ standard.

Here's the code again, and an explanation of why I believe the code is legal.

 typedef long bar[2][2];
 void func( const bar param ){}

 int main()
 {
  bar x;
  func(x);  // rejected by
 }

The parameter of `func' is declared to have type `const bar', where
`bar' is a typedef which names an array type.  Consulting 3.9.3/3,

|   3.9.3  CV-qualifiers                            [basic.type.qualifier]
|
| 2 A  compound  type  (_basic.compound_)  is  not cv-qualified by the cv-
|   qualifiers (if any) of the type from which it is compounded.  Any  cv-
|   qualifiers that appear in an array declaration apply to the array ele-
|   ment type, not the array type (_dcl.array_).

I think we can discern that the declared type of the
parameter is "array [2] of array [2] of const long", rather than
"const array [2] of array [2] of long".  The wording is quite vague [*]
but I'm pretty sure that's the intent.  However, this is certainly
the weakest step in my chain of reasoning.

 [*] What exactly is an "array declaration"?  Does `const bar'
     count as one?  Does this rule apply recursively to arrays
     of arrays?

Next, we need to consult 8.3.5/3.

|   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 long" to "pointer to array [2] of const long".

So, we are trying to convert a value of type "array [2] of array [2] of long"
to type "pointer to array [2] of const long".  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 long"
to "pointer to array [2] of long".

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 long".  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 long" to "pointer to array [2] of const long", since
"const long" is more cv-qualified than "long".  At this point, we
have shown that there exists a standard conversion sequence which converts
the type of the actual parameter of `func' 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: jason@cygnus.com (Jason Merrill)
Date: 1996/04/12
Raw View
>>>>> Steve Clamage <clamage@Eng.Sun.COM> writes:

> In article rp3@mozart.wg.icl.co.uk, Adel El-Beik <A.El-Beik@man0506.wins.icl.co.uk> writes:
>> I have a question concerning MSVC++ v.4.00 and the April DWP C++.
>>
>> The following code fails on MSVC++.  It doesn't like line labelled 1.
>> It complains with
>>
>> C:\array\array.cpp(16) : error C2664: 'func' : cannot convert parameter 1
>> from 'long [2][2]' to 'const long [][2]' (new behavior; please see help)

> You have run into effectively the same rule that prohibits implicit
> conversion from "T**" to "const T**". That prohibition is not new,
> but is in the ISO/ANSI C standard, and has not been changed, since
> it would open a hole in the type system.

But converting long (*)[2] to const long (*)[2] is more like converting
long ** to const long *const * than it is like converting long ** to const
long **, because there is no way to modify the array -- there is, in fact,
no array entity in memory, only two longs.  A long (*)[2] points to two
longs, whereas a const long (*)[2] points to two const longs.  I don't see
a constness hole there.

I suspect this is an oversight.

Jason


[ 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@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/04/15
Raw View
[Note: this article is crossposted to comp.std.c++ and comp.std.c.]

In comp.std.c++, jason@cygnus.com (Jason Merrill) writes:

>>>>>> Steve Clamage <clamage@Eng.Sun.COM> writes:
>
>> Adel El-Beik <A.El-Beik@man0506.wins.icl.co.uk> writes:
>>> I have a question concerning MSVC++ v.4.00 and the April DWP C++.
[...]
>>> C:\array\array.cpp(16) : error C2664: 'func' : cannot convert parameter 1
>>> from 'long [2][2]' to 'const long [][2]' (new behavior; please see help)
>
>> You have run into effectively the same rule that prohibits implicit
>> conversion from "T**" to "const T**". That prohibition is not new,
>> but is in the ISO/ANSI C standard, and has not been changed, since
>> it would open a hole in the type system.
>
>But converting long (*)[2] to const long (*)[2] is more like converting
>long ** to const long *const * than it is like converting long ** to const
>long **, because there is no way to modify the array -- there is, in fact,
>no array entity in memory, only two longs.  A long (*)[2] points to two
>longs, whereas a const long (*)[2] points to two const longs.  I don't see
>a constness hole there.
>
>I suspect this is an oversight.

Yes, but the oversight is in the implementation, not in the standard.
The conversion in question is allowed by the draft C++ standard.  (See
my other post to comp.std.c++ in this thread for a detailed explanation.)
The conversion is also allowed by a variety of other implementations,
including cfront, gcc, and SGI C++ (EDG).

Interestingly, even though this conversion is presumably disallowed by
the ANSI/ISO C standard, many supposedly conforming C compilers fail to
issue a diagnostic for it.  Of the six I tried, only two issued any
diagnostic, even with all the appropriate options for selecting ANSI
conformance and enabling warnings.

Compiler Options      Diagnostic?
-------- -------      -----------
GNU C  gcc -ansi -pedantic -Wall   No
SGI C  cc -ansi -pedantic -fullwarn -wlint,p  No
DEC OSF/1 C cc -std1     No
DEC C  cc -std1 -migrate -check   No
Sun C  cc -Xc -v     Yes (warning)
LCC  lcc -A -A     Yes (error)

--
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
                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
]





Author: Adel El-Beik <A.El-Beik@man0506.wins.icl.co.uk>
Date: 1996/04/10
Raw View
I have a question concerning MSVC++ v.4.00 and the April DWP C++.

The following code fails on MSVC++.  It doesn't like line labelled 1.
It complains with

C:\array\array.cpp(16) : error C2664: 'func' : cannot convert parameter 1
from 'long [2][2]' to 'const long [][2]' (new behavior; please see help)

Now as far as I can understand from DWP an implicit conversion should
take place on x. i.e. it is const qualified, and so MSVC++ should not
raise an error.  MSVC++ accepts line labelled 2, which should be
what MSVC++ converts the funcion argument and parameter to on 1.

Is their something I've missed, i.e. MSVC++ behaviour matches April
DWP ?  Or is MSVC++ followed its own standard, Microsoft claim to
be following evolving standard.

Thanks in advance.

Adel.

// CODE START


typedef long bar[2][2];
typedef const long (*const_bar)[2];


void func( const bar param ){}



void main()
{
 bar x;

 func( x );               // 1

 func( (const_bar)x );    // 2
}

// CODE END
---
[ 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                             ]