[comp.std.c] Interpretation needed Re: function prototypes

shankar@hpclscu.HP.COM (Shankar Unni) (07/06/89)

A question has come up with regard to the interpretation of a parenthesized
sentence in Sec 3.5.4.3 (page 69, lines 24, 25 in the Dec 88 draft).

I'll quote here in full the paragraph in question (I've put in some blank
lines between conditions, just to make it a little easier to read - in the
standard document, all this is one giant paragraph):

     For two function types to be compatible, both shall specify compatible
     return types. Moreover, the parameter type lists, if both are present,
(a)  shall agree in the number of parameters and in the use of the ellipsis
     terminator; corresponding parameters shall have compatible types.

									 If
     one type has a parameter type list and the other type is specified by
     a function declarator that is not part of a function definition and that
     contains an empty identifier list, the parameter list shall not have an
(b)  ellipsis terminator and the type of each parameter shall be compatible
     with the type that results from the application of the default argument
     promotions.

		 If one type has a parameter type list and the other type is
     specified by a function definition that contains a (possibly empty)
     identifier list, both shall agree in the number of parameters and the
(c)  type of each prototype parameter shall be compatible with the type that
     results from the application of the default argument promotions to the
     type of the corresponding identifier.

					   (For each parameter declared with
     a function or array type, its type for these comparisons is the one that
     results from conversion to a pointer type, as in Sec 3.7.1. For each
(->) parameter declared with a qualified type, its type for these comparisons
(->) is the unqualified version of its declared type.)


(Phew!)

The clauses marked (a), (b) and (c) are three distinct combinations of
function-declarator styles (prototype - prototype, prototype - old-style
declaration, prototype - old-style definition).

The debate is whether the parenthesized sentences (especially the one marked
with "->") apply only to the last combination, or to all three. Specifically,
are all the following correct combinations of declarations? If so, what
are the composite types of these function types?:

(a)  int func1 (const int);
     int func1 (int p)       { /* can "p" be modified here? */ }

(b)  int func2 ();
     int func2 (const int);

(c)  int func3 (const int);
     int func3 (p) int p;    { /* can "p" be modified here? */ }

In both (a) and (c), this is a crucial question. The composite type
determines whether "p" is a const or not (in the body).

Another question is: if any of these is deemed to be wrong (let's say
that the types in (a) are not compatible): Am I violating a constraint
if I allow assignment to "p"? What if the declaration had  no const and
the definition had one?

My personal opinion (based on a "gut feeling") is that qualifiers should
be stripped from definitions *for the purposes of comparisons only*. I.e.
in case (a), the combination should be OK, and "p" should be modifiable.
If it was the other way round (declaration without "const", definition with
"const"), then "p" should not be modifiable. It shouldn't matter to the
caller whether the parameter is a const or not - it will never change
the semantics of the actual call.

Could the gurus please impart their wisdom to us on this earth-shaking topic?
It will be much appreciated. (p.s. the non-gurus are welcome with their
opinions, too, so don't be shy :-)).
--------
Shankar Unni
Hewlett-Packard California Language Lab.      Internet: shankar@hpda.hp.com
Phone: (408) 447-5797                             UUCP: ...!hplabs!hpda!shankar

walter@hpclwjm.HP.COM (Walter Murray) (07/06/89)

Shankar Unni writes:

> A question has come up with regard to the interpretation of a parenthesized
> sentence in Sec 3.5.4.3 (page 69, lines 24, 25 in the Dec 88 draft).

[excerpt from pANS deleted]

> The clauses marked (a), (b) and (c) are three distinct combinations of
> function-declarator styles (prototype - prototype, prototype - old-style
> declaration, prototype - old-style definition).

> The debate is whether the parenthesized sentences (especially the one marked
> with "->") apply only to the last combination, or to all three. 

As I read it, the parenthesized sentences apply to the entire paragraph.

> Specifically,
> are all the following correct combinations of declarations? If so, what
> are the composite types of these function types?:

> (a)  int func1 (const int);
>      int func1 (int p)       { /* can "p" be modified here? */ }

> (b)  int func2 ();
>      int func2 (const int);

> (c)  int func3 (const int);
>      int func3 (p) int p;    { /* can "p" be modified here? */ }

As I read it, all three examples are legal (strictly conforming),
and in both (a) and (c), "p" is a modifiable lvalue.

> In both (a) and (c), this is a crucial question. The composite type
> determines whether "p" is a const or not (in the body).

Why do you say this?  It seems to me that whether "p" may be treated
as modifiable by statements in the function body depends solely on how
"p" is declared in the function definition, and not on how it may
be declared in any other declarations of the function.

In (b) and (c) above, I would say that the composite type of
the function is "int (const int)".
In (a), although I think it's legal, I don't think the pANS defines
the composite type.  Fortunately, it doesn't make any difference,
as far as I can tell, whether the composite type is considered
to be "int (int)" or "int (const int)".

> My personal opinion (based on a "gut feeling") is that qualifiers should
> be stripped from definitions *for the purposes of comparisons only*. I.e.
> in case (a), the combination should be OK, and "p" should be modifiable.
> If it was the other way round (declaration without "const", definition with
> "const"), then "p" should not be modifiable. It shouldn't matter to the
> caller whether the parameter is a const or not - it will never change
> the semantics of the actual call.

I reach the same conclusions.

> Shankar Unni

Walter Murray

In case there's any doubt, these are my personal opinions.
----------

dfp@cbnewsl.ATT.COM (david.f.prosser) (07/06/89)

In article <12570014@hpclscu.HP.COM> shankar@hpclscu.HP.COM (Shankar Unni) writes:
>A question has come up with regard to the interpretation of a parenthesized
>sentence in Sec 3.5.4.3 (page 69, lines 24, 25 in the Dec 88 draft).
>
>I'll quote here in full the paragraph in question (I've put in some blank
>lines between conditions, just to make it a little easier to read - in the
>standard document, all this is one giant paragraph):

There's a good reason why it's all one paragraph--so that the applicability
of the parenthetical sentences would be clear.

	[most of the quoted text deleted]

>					   (For each parameter declared with
>     a function or array type, its type for these comparisons is the one that
>     results from conversion to a pointer type, as in Sec 3.7.1. For each
>(->) parameter declared with a qualified type, its type for these comparisons
>(->) is the unqualified version of its declared type.)
>
>The debate is whether the parenthesized sentences (especially the one marked
>with "->") apply only to the last combination, or to all three.

They apply to the entire paragraph.

>Specifically,
>are all the following correct combinations of declarations? If so, what
>are the composite types of these function types?:

There is a misunderstanding here.  The parenthetical sentences are not
differences in type that participate in producing a (possibly different)
composite type.  Many people also seem to believe that type qualifiers
have this sort of connection with composite types, for whatever reason.
These sentences state that these "rewrites" of the parameter types are
part of the type comparison: types that are the same after the rewrite
are the same.

>
>(a)  int func1 (const int);
>     int func1 (int p)       { /* can "p" be modified here? */ }

Yes.  The type of the parameter is "int".

>
>(b)  int func2 ();
>     int func2 (const int);

These are compatible types.

>
>(c)  int func3 (const int);
>     int func3 (p) int p;    { /* can "p" be modified here? */ }

Yes.  The parameter has type "int".

>
>In both (a) and (c), this is a crucial question. The composite type
>determines whether "p" is a const or not (in the body).

No.  The rules for composite types have nothing to do with qualified
or unqualified versions of types.  The composite type for all of the
above examples is the same: function returning int with one int parameter.
Only those differences listed in section 3.1.2.6 (array with unknown vs.
known size, function without parameter information vs. with parameter
information) come into action in the creation of a composite type.

>
>Another question is: if any of these is deemed to be wrong (let's say
>that the types in (a) are not compatible): Am I violating a constraint
>if I allow assignment to "p"? What if the declaration had  no const and
>the definition had one?

All the above examples are compatible, but it is the actual type of the
function definition that determines the allowed behavior in the function:

	int f(const int);
	int f(int a){return ++a;}	/* valid */

	int g(int);
	int g(const int a){return ++a;}	/* invalid, but only due to the ++ */

>
>My personal opinion (based on a "gut feeling") is that qualifiers should
>be stripped from definitions *for the purposes of comparisons only*. I.e.
>in case (a), the combination should be OK, and "p" should be modifiable.
>If it was the other way round (declaration without "const", definition with
>"const"), then "p" should not be modifiable. It shouldn't matter to the
>caller whether the parameter is a const or not - it will never change
>the semantics of the actual call.

This is what the pANS describes.  It is unfortunate that the draft doesn't
seem to make it clear, and you have to rely on "gut feelings".

Dave Prosser	...not an official X3J11 answer...

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/06/89)

In article <12570014@hpclscu.HP.COM> shankar@hpclscu.HP.COM (Shankar Unni) writes:
>The debate is whether the parenthesized sentences (especially the one marked
>with "->") apply only to the last combination, or to all three.

All of them, of course.  That paragraph is specifying what it takes for
function interfaces to have compatible types, and the parenthetical
clarifies some aspects of parameter type compatibility, which is a
subissue within the more general function type compatibility issue.

kathy@hpfcdc.HP.COM (Kathy Harris) (07/07/89)

I'm still not clear on this.  Is it only the "top-level" qualifier that
is to be ignored for comparisons, or all qualifiers.  For example, are
the following compatible or not:

	int f(const int * p);
	int f(int * p);

where the 'const' qualifier applies to the 'int' not to the 'pointer'.

If you do ignore this for comparisons, then it still makes a difference
in the composite type, because it will determine whether the following
generates a diagnostic about passing a pointer to a constant to a plain
pointer.

	main() {
	  const int *q;
	  f(q);
	}

Kathy Harris
Hewlett Packard Colorado Language Lab
hplabs!hpdcdb!kathy

shankar@hpclscu.HP.COM (Shankar Unni) (07/08/89)

Thanx, all, for the replies. It's a little clearer now. Dave, you're right:
I was getting a little confused re: composite types and qualifiers. You
see, on the one hand, section 3.1.2.6 states (third bullet) that the
composite type of two function types with parameter lists is a function
type with a parameter list, with the type of each parameter being the
composite type of the corresponding parameters from the two function types.

On the other hand, if one has a parameter of type "const int", and the other
has a parameter of type "int", clearly there is no "composite type" of
these two types (via sec. 3.5.3).

Thus, the point to be made clear, IMO, is that the "function type" really
contains an imaginary parameter list, which is identical to the parameter
list with which it is declared, except that the qualifiers on the parameter
types are ignored.

E.g., in the function definition

   int foo (const int bar) { /* */ }
   
the type of "foo" is

   int () (int)

(my notation for: return type == int, parameter list = "int"). However, the
type of "bar" is "const int".

Am I off target? Maybe a slight clarification needs to be put into the
standard or the rationale at some stage.
----
Shankar.

dfp@cbnewsl.ATT.COM (david.f.prosser) (07/10/89)

In article <12040016@hpfcdc.HP.COM> kathy@hpfcdc.HP.COM (Kathy Harris) writes:
>
>I'm still not clear on this.  Is it only the "top-level" qualifier that
>is to be ignored for comparisons, or all qualifiers.  For example, are
>the following compatible or not:
>
>	int f(const int * p);
>	int f(int * p);
>
>where the 'const' qualifier applies to the 'int' not to the 'pointer'.

These are not compatible types.  Thus, in your example,

	main() {
	  extern f(int *);
	  const int *q;
	  f(q);
	}

a diagnostic is required.

Dave Prosser	...not an official X3J11 answer...

walter@hpclwjm.HP.COM (Walter Murray) (07/11/89)

Kathy Harris writes:

> I'm still not clear on this.  Is it only the "top-level" qualifier that
> is to be ignored for comparisons, or all qualifiers.

Only the "top-level" qualifier, as you call it.  This gets into
the concept of "top type", or "type category" as it is now called.
(See 3.1.2.5.)  I think the key statement is, "A derived type
is not qualified by the qualifiers (if any) of the type from which it
is derived."  Page 25, line 22, 12/88 dpANS.  For example,
"int * const p" is a qualified type, but "const int * p" is not, if
I have it right.

> For example, are
> the following compatible or not:

> 	int f(const int * p);
> 	int f(int * p);

> where the 'const' qualifier applies to the 'int' not to the 'pointer'.

These are *NOT* compatible.  But the following *ARE* compatible:

	 int f(int * const p);
	 int f(int * p);

I hope someone who understands "top type" will confirm my understanding
on this, or straighten me out.

> Kathy Harris

Walter Murray

Not speaking for X3J11 or Hewlett-Packard.
----------

dfp@cbnewsl.ATT.COM (david.f.prosser) (07/11/89)

In article <12570016@hpclscu.HP.COM> shankar@hpclscu.HP.COM (Shankar Unni) writes:
>Thus, the point to be made clear, IMO, is that the "function type" really
>contains an imaginary parameter list, which is identical to the parameter
>list with which it is declared, except that the qualifiers on the parameter
>types are ignored.
>
>E.g., in the function definition
>
>   int foo (const int bar) { /* */ }
>   
>the type of "foo" is
>
>   int () (int)
>
>(my notation for: return type == int, parameter list = "int"). However, the
>type of "bar" is "const int".
>
>Am I off target? Maybe a slight clarification needs to be put into the
>standard or the rationale at some stage.

I think you've got it.  Two types must be compatible before a composite type
from the two can be attempted.  When checking whether two prototypes are
compatible, the "meaningless" type qualifiers are stripped from each parameter,
and each function or array parameter is rewritten as an appropriate pointer
type.  It is this rewritten type that must be used for the composite type
construction.

On the other hand, it doesn't matter whether the "meaningless" qualifiers are
kept in the composite type, since they are always ignored for all purposes,
except within the function definition.

At this point, I doubt that anything will be done to the Rationale or the
pANS, but the interpretations phase of the Committee must answer these sorts
of questions, but it will probably takes months...

Dave Prosser	...not an official X3J11 answer...

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/12/89)

In article <12570017@hpclwjm.HP.COM> walter@hpclwjm.HP.COM (Walter Murray) writes:
>I hope someone who understands "top type" will confirm my understanding
>on this, or straighten me out.

I think you got it right.  A pointer to a qualified type is not itself
a qualified type (unless of course an ADDITIONAL qualifier is supplied
at the top level).

You think 3.1.2.5 is puzzling now, you should have seen it before the
last major overhaul.  I think it's about as clear as we are able to
make it.  I don't know if that reflects upon the actual language design
or upon our ability to describe it..