https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121563

--- Comment #18 from Harald van Dijk <harald at gigawatt dot nl> ---
(In reply to Christopher Bazley from comment #16)
> If I understood you correctly, your very simple general rule is "Multiple
> declarations are valid in file scope and in function scope, so long as they
> are not definitions." That seems a bit circular to me: it says that multiple
> declarations are valid so long as they are not the kind of declaration that
> would be invalid.

I think you responded from top to bottom and did not revisit, is that right?
The definition of "definition" that I referred to below does not depend on
whether multiple declarations are valid, so there is no circular reasoning.

> The existing rules for determining whether a declaration is a definition or
> not are not simple to me; I think they are one of the most complex parts of
> the language. Saying that the rule is simple if one can reliably distinguish
> a declaration from a definition doesn't help the average programmer.

Are they? I will leave it to others to determine what the average programmer
would think. Do keep in mind that if you are right that an average programmer
would not think it is valid, it is also not something that an average
programmer would write, and therefore there is little harm to that average
programmer in allowing it.

> I don't disagree that "Multiple declarations are valid in file scope and in
> function scope, so long as they are not definitions", I just don't find it
> of much practical use. It hinges on the definition of "definition", which is
> complex and context-dependent.
> 
> The problem isn't the semantics; it's the syntax. For example, I think it
> would be better if 'extern' were not implicit on function declarations
> within a function body and on file-scope definitions with external linkage.

That gets back to what I was saying earlier: possibly for good reason, you seem
to wish that C were a different language, and seem to base your proposal on
your preferred language, rather than the C we have today. I can relate to that,
there is plenty that I would also like to see different in C, but I think it's
better to set that aside. Even in the areas where I am not a fan of today's C,
I think it's better for new changes to be consistent with the language that we
already have, even in the parts where I wish it weren't so.

> This nuance did not seem to be relevant to me because I believed there to be
> no observable difference between a typedef name declaration and a typedef
> name definition. Typedef declarations do not behave like object definitions
> for obvious reasons: they do not "cause storage to be reserved for that
> object".

That seems fair. In standard C, in the case of typedefs, and likewise in the
case of structure definitions and enumerator definitions, as far as I know the
only possible visible effect that would distinguish definitions from
non-defining declarations would have been in the diagnostics.

> Why shouldn't parameter forward declarations be interpreted as definitions?

Given

  void f(int i, int j) {}

it is clear that storage is reserved for the parameters 'i' and 'j'. Their
addresses can be taken, they can be memcpy-ed into, they can have their
representations interpreted as raw bytes, they are objects. It is the
parameter-declarations 'int i' and 'int j' that causes that to happen.

Given

  void f(int j; int i, int j) {}

the storage reserved for parameters 'i' and 'j' is the exact same as in the
previous example, the storage for 'j' is not altered by the
parameter-forward-declaration appearing first. I cannot see how this would work
if we were to say that it is now the parameter-forward-declaration which is
responsible for reserving the storage for it. The only way that I can imagine
this working is if it remains the parameter-declarations that are responsible
for that.

But see below for an extra complication that I had not realised earlier.

> Thanks. It's not clear to me how this applies to tentative definitions, if
> at all. A whole translation unit must be parsed before it's possible to
> determine that "the translation unit contains no external definition for
> that identifier" (and then it seems to be unspecified which, if any, of the
> tentative definitions becomes the one that "causes storage to be reserved").

None of them, I think. Per 6.9.3 External object definitions, if we have a
translation unit

  int x;
  int x;

then both remain non-defining declarations. Instead, the effect is as if a
declaration is added, somewhere in the translation unit, that looks like

  int x = {};

and it is that implicitly added declaration that is the definition.

> Based on what? The proposed wording changes in n3681 or something else?

See above.

> The existing exceptions to the rule that "If an identifier has no linkage,
> there shall be no more than one declaration of the identifier (in a
> declarator or type specifier) with the same scope and in the same name
> space" (6.7.1 Constraints) concern typedef names and enumeration constants
> and tags. These are the same kinds of declaration that are definitions by
> virtue of being "...the first (or only) declaration of the identifier..."
> (6.7.1 Semantics).

Indeed. This is, in my opinion, an unnecessarily convoluted way of phrasing
that general rule, but it has the exact same effect as that general rule. But
it's good that you bring this up, because...

> N3681 proposes adding "a parameter in a parameter forward declaration list
> shall be redeclared in the parameter type list as specified in 6.7.7.4" to
> the list of exceptions in the Constraints; it would be possible to make the
> equivalent change to the Semantics, i.e. add
> 
> A definition of an identifier is a declaration for that identifier that for:
> 
> - a parameter, is the first (or only) declaration of the identifier.
> 
> I don't think this would make any observable difference but it would clear
> up the current ambiguity.

...under the current version of your proposed changes, I think

  void f(int x; int x) {}

may arguably be an error, despite that being very clearly intended to be valid
by your proposal, unless you make more changes there.

There is a complication that 6.7.1 may or may not include
parameter-declarations. Per the syntax, a parameter-declaration is not a
declaration. Yet the standard does use the wording "declaration of a
parameter", rather than "a parameter-declaration" (the syntax rule), which is
only valid if "declaration" is used as something other than that syntax rule,
and likewise, there does not appear to be any rule other than 6.7.1 to prohibit

  void f(int x, int x) {}

which clearly should not be valid. In general, when "declaration" is used, it
is ambiguous as to whether it refers to the general concept of "declaration" or
the syntax rule of "declaration". Earlier, I was writing on the assumption that
a parameter declaration is a declaration, specifically that it is a declaration
of a parameter. I think that is the intended interpretation, but I am far less
convinced of that now than I was before.

Similarly, it will need to be cleared up whether "declaration", in general,
includes a parameter-forward-declaration, and whatever the answer, it will need
a combing through the standard to find any possible instances where the
alternative is meant.

Reply via email to