C++ typedef merging

Small patch for the merging of typedef as defined in C++ std. So this become
valid in c++ mode:

===============
typedef int I;
typedef int J;
typedef J L;
typedef int I;
typedef L I;
typedef I I;

Interesting, I didn't realize this was valid in C++.

However this code should produce an error (and don't):

===============
struct complex { /* ... */ };
typedef int complex; // error: redefinition

Okay, lets handle this as a later piece. There is also the strangeness in C++ where you have:

struct stat { .. };
struct stat stat();

"stat" is a function, but "struct stat" is the type, even though the tag namespace is normally searched for normal identifiers.

About the patch:

+ // FIXME: Does CPlusPlus0x langoption inplie CPlusPlus?
+ if(PP.getLangOptions().CPlusPlus || PP.getLangOptions().CPlusPlus0x) {
+ // see C++ 7.1.3 [dcl.typedef]

Yes, it does, you should just check CPlusPlus. Also, please change the comment above the 'if' to be something like:

   // Redefinitions of typedef are ok in C++ as long as the subsequent
   // definition is the same type as the first definition: C++ 7.1.3 [dcl.typedef]

+ if( TagDecl* oldTag = dyn_cast<TagDecl>(OldD) ) {
+ oldQT = Context.getTagDeclType( oldTag );

Please avoid the extraneous spaces, use something like:

+ if(TagDecl *oldTag = dyn_cast<TagDecl>(OldD)) {
+ oldQT = Context.getTagDeclType(oldTag);

Overall, the algorithm seems like a fine starting point. I'd suggest restructuring the code to look like this though:

+ QualType oldQT;
+ if( TagDecl* oldTag = dyn_cast<TagDecl>(OldD) ) {
+ oldQT = Context.getTagDeclType( oldTag );
+ } else if( TypedefDecl* oldTypeDef = dyn_cast<TypedefDecl>(OldD) ) {
+ oldQT = oldTypeDef->getUnderlyingType();
+ }

      if (!oldQT.isNull() &&
          oldQT.getCanonicalType() == New- >getUnderlyingType().getCanonicalType())
         return New;
      // otherwise fall through to the code that already emits diagnostics.

which seems a bit simpler. Also, can you come up with an example which would hit the TagDecl case here yet?

-Chris

There is also the
strangeness in C++ where you have:

struct stat { .. };
struct stat stat();

"stat" is a function, but "struct stat" is the type,

Yep, according to 3.4.4 basic.lookup.elab.

even though the
tag namespace is normally searched for normal identifiers.

Note, in ISO C++ there is no concept of "tag names", "ordinary names", "tag name space" or "ordinary name space" as in ISO C.

In the pre-Standard days, C++ did not even allow the struct stat case, but eventually the following rule from 3.3.7 basic.scope.hiding was added (because losing compatibility with POSIX (and probably other libraries) would have been a deal-breaker):

         A class name (9.1) or enumeration name (7.2) can be
         hidden by the name of an object, function, or enumerator
         declared in the same scope. If a class or enumeration
         name and an object, function, or enumerator are declared
         in the same scope (in any order) with the same name, the
         class or enumeration name is hidden wherever the object,
         function, or enumerator name is visible.

Note that this rule allows just enough hiding to maintain compatibility with this kind of C code while rendering the following examples ill-formed:

struct A {};
namespace A{} // Error

int B;
namespace B{} // Error

int C();
template <class T> struct C{}; // Error

template< class T> int D();
struct D{}; // Error

James Widman

Also note, the kind of a name introduced by a using-declaration is the same as the kind of name to which it refers; e.g. a using-declaration that names a class introduces a class-name into the scope where the using-declaration appears. Example:

namespace N {
   int A;
   template <class T> struct B;
}

struct A;
using N::A; // Ok

int B();
using N::B; // Error

James Widman

There is also the
strangeness in C++ where you have:

struct stat { .. };
struct stat stat();

"stat" is a function, but "struct stat" is the type,

Yep, according to 3.4.4 basic.lookup.elab.

even though the
tag namespace is normally searched for normal identifiers.

Note, in ISO C++ there is no concept of "tag names", "ordinary names",
"tag name space" or "ordinary name space" as in ISO C.

Right. This is part of the fun of handling C and C++ in a unified parser and representation: merging the two concepts together and making them self consistent. C++ does have a separate namespace for labels though still.

In the pre-Standard days, C++ did not even allow the struct stat case,
but eventually the following rule from 3.3.7 basic.scope.hiding was
added (because losing compatibility with POSIX (and probably other
libraries) would have been a deal-breaker):

         A class name (9.1) or enumeration name (7.2) can be
         hidden by the name of an object, function, or enumerator
         declared in the same scope. If a class or enumeration
         name and an object, function, or enumerator are declared
         in the same scope (in any order) with the same name, the
         class or enumeration name is hidden wherever the object,
         function, or enumerator name is visible.

Implementing this should be fairly straight-forward I think. Basically, when doing a scope lookup in C++ mode, it should search for anything with "IDNS_Tag or IDNS_Ordinary". If after "struct", just search the IDNS_Tag namespace. The end effect would be that C++ would see tag+ordinary as a unified namespace in most cases.

-Chris

Hi,

-----Message d'origine-----
De : Chris Lattner [mailto:clattner@apple.com]

which seems a bit simpler. Also, can you come up with an example
which would hit the TagDecl case here yet?

Sorry for the wait.

Here is a slightly modified patch following your indications.

We can't it the TagDecl because:

The function is called in Sema::ActOnDeclarator On line 726 of semadecl.cpp
only if a previous decl was found in the ordinary namespace (ScopedDecl
*PrevDecl = LookupScopedDecl(II, Decl::IDNS_Ordinary,...). However, the type
wich would cause to hit the tagdecl live in the Tag namespace IDNS_Tag. So
until we implement the basic C++ name lookup, I don't think we can hit the
tagdecl case.

Regards,

Cédric

CppMergedTypedef.patch (2.02 KB)

-----Message d'origine-----
De : Chris Lattner [mailto:clattner@apple.com]

which seems a bit simpler. Also, can you come up with an example
which would hit the TagDecl case here yet?

Sorry for the wait.

Here is a slightly modified patch following your indications.

We can't it the TagDecl because:

The function is called in Sema::ActOnDeclarator On line 726 of semadecl.cpp
only if a previous decl was found in the ordinary namespace (ScopedDecl
*PrevDecl = LookupScopedDecl(II, Decl::IDNS_Ordinary,...). However, the type
wich would cause to hit the tagdecl live in the Tag namespace IDNS_Tag. So
until we implement the basic C++ name lookup, I don't think we can hit the
tagdecl case.

Ok, I'm somewhat uncomfortable with this patch. It basically relies on implementing correct lookup for C++ names, but that hasn't been done yet. If we had that part first, I'd be more happy with this one. Regardless, some more specific thoughts:

@@ -17,6 +17,7 @@
  #include "clang/AST/Decl.h"
  #include "clang/AST/Expr.h"
  #include "clang/AST/Type.h"
+#include "clang/Lex/Preprocessor.h"

Please remove.