Implicitly declared K&R parameters.

Hello.

Consider the following K&R function definition:

void foo(implicit, explicit)
   int explicit;
{}

While traversing the corresponding AST chunk, we would like to be able to distinguish between parameter 'explicit' (which was explicitly declared) from parameter 'implicit' (which was implicitly declared).

Is there a reliable way of distinguishing between the two cases?

First (failed) attempt:
Usually, implicit declarations are provided with an invalid source location, but this is false for the case at hand: the location of the identifier is obtained.

Second (failed) attempt:
Since 'implicit' has no type written, we also checked for an invalid getTypeSpecStartLoc(), but again we obtained a valid location, which is the same as that of the identifier. Here are the relevant source lines in SemaDecl.cpp:

   // Implicitly declare the argument as type 'int' for lack of a better
   // type.
   DeclSpec DS;
   const char* PrevSpec; // unused
   unsigned DiagID; // unused
   DS.SetTypeSpecType(DeclSpec::TST_int, FTI.ArgInfo[i].IdentLoc,
                      PrevSpec, DiagID);

Third (maybe working) attempt:
We ended up checking whether or not the two source locations mentioned above happen to be the same source location:

   bool implicit = (param.getLocation() == param.getTypeSpecStartLoc());

This was working as expected on the few tests we tried.
However, we would like to avoid making wild guesses: is such a property a valid (i.e., supported) clang invariant or is it just working by chance?

For clarity, would you accept the addition of a method

   bool ParmVarDecl::isImplicitIntKNRParameter() const {
     return getLocation() == getTypeSpecTypeLoc();
   }

?

Regards,
Enea.

Hello.

Consider the following K&R function definition:

void foo(implicit, explicit)
  int explicit;
{}

While traversing the corresponding AST chunk, we would like to be able
to distinguish between parameter 'explicit' (which was explicitly
declared) from parameter 'implicit' (which was implicitly declared).

Is there a reliable way of distinguishing between the two cases?

First (failed) attempt:
Usually, implicit declarations are provided with an invalid source
location, but this is false for the case at hand: the location of the
identifier is obtained.

Second (failed) attempt:
Since 'implicit' has no type written, we also checked for an invalid
getTypeSpecStartLoc(), but again we obtained a valid location, which is
the same as that of the identifier. Here are the relevant source lines
in SemaDecl.cpp:

  // Implicitly declare the argument as type 'int' for lack of a better
  // type.
  DeclSpec DS;
  const char* PrevSpec; // unused
  unsigned DiagID; // unused
  DS.SetTypeSpecType(DeclSpec::TST_int, FTI.ArgInfo[i].IdentLoc,
                     PrevSpec, DiagID);

Third (maybe working) attempt:
We ended up checking whether or not the two source locations mentioned
above happen to be the same source location:

  bool implicit = (param.getLocation() == param.getTypeSpecStartLoc());

This was working as expected on the few tests we tried.
However, we would like to avoid making wild guesses: is such a property
a valid (i.e., supported) clang invariant or is it just working by chance?

It probably comes from building a "trivial" TypeSourceInfo for the parameter type, using the location of the identifier. It's somewhat of an accident, but it's also unlikely to change.

For clarity, would you accept the addition of a method

  bool ParmVarDecl::isImplicitIntKNRParameter() const {
    return getLocation() == getTypeSpecTypeLoc();
  }

?

There are likely to be other implicitly-generated ParmVarDecls that are likely to be caught by this, e.g., the ParmVarDecls for an implicitly-generated copy constructor or copy assignment operator. At the very least, this should check that getLocation() is valid and that the parameter is from a K&R function definition.

However, I do wonder you're really trying to solve the larger problem of "implicit int", which would likely require something completely different. For example, here's some ugly C89 code:

  f(x, y) {
    register z = x + y;
    return z;
  }

We'll don't keep track of the implicit int in f, x, y, or z. So, while I'm not opposed to the method you're suggesting (with fixes), I think it's only solving part of the problem that you're probably working on.

  - Doug

Douglas Gregor wrote:

Hello.

Consider the following K&R function definition:

void foo(implicit, explicit) int explicit; {}

[...]

bool implicit = (param.getLocation() == param.getTypeSpecStartLoc());

This was working as expected on the few tests we tried. However, we
would like to avoid making wild guesses: is such a property a valid
(i.e., supported) clang invariant or is it just working by chance?

It probably comes from building a "trivial" TypeSourceInfo for the
parameter type, using the location of the identifier. It's somewhat
of an accident, but it's also unlikely to change.

For clarity, would you accept the addition of a method

bool ParmVarDecl::isImplicitIntKNRParameter() const { return
getLocation() == getTypeSpecTypeLoc(); }

?

There are likely to be other implicitly-generated ParmVarDecls that
are likely to be caught by this, e.g., the ParmVarDecls for an
implicitly-generated copy constructor or copy assignment operator. At
the very least, this should check that getLocation() is valid and
that the parameter is from a K&R function definition.

However, I do wonder you're really trying to solve the larger problem
of "implicit int", which would likely require something completely
different. For example, here's some ugly C89 code:

f(x, y) { register z = x + y; return z; }

We'll don't keep track of the implicit int in f, x, y, or z. So,
while I'm not opposed to the method you're suggesting (with fixes), I
think it's only solving part of the problem that you're probably
working on.

- Doug

If we _always_ have a (trivial) TypeSourceInfo,
then the "implicit int" could be detected by checking

   const WrittenBuiltinSpecs&
   BuiltinTypeLoc::getWrittenBuiltinSpecs() const;

In C, if the (return) type is int and there is no written type specifier and no written sign specifier, then it must have been an implicit int.
But what if we do not have a TypeSourceInfo set?

Enea.

Then we should fix it, so that the TypeSourceInfo is always set in these cases.

  - Doug