d.zobnin.bugzilla updated this revision to Diff 46781.
d.zobnin.bugzilla added a comment.

Thanks for the review!

Updated the patch according to the comments: allowed only integer, enum and 
pointer types which have the same qualifiers, signedness, width and alignment. 
Changed the diagnostic text.

Please take a look.


http://reviews.llvm.org/D16770

Files:
  include/clang/AST/ASTContext.h
  include/clang/Basic/DiagnosticSemaKinds.td
  lib/AST/ASTContext.cpp
  lib/Sema/SemaDecl.cpp
  test/Sema/ms-benign-typedef-redef.c

Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -6729,6 +6729,42 @@
   return false;
 }
 
+bool ASTContext::areMSCompatibleTypedefTypesInC(QualType OldType,
+                                                QualType NewType) {
+  assert(getLangOpts().MSVCCompat &&
+         "This routine must be called in Microsoft mode only!");
+  assert(!getLangOpts().CPlusPlus && !getLangOpts().ObjC1 &&
+         !getLangOpts().ObjC2 && "This routine must be called in C mode only!");
+
+  QualType OldCan = getCanonicalType(OldType);
+  QualType NewCan = getCanonicalType(NewType);
+
+  if (OldCan.getCVRQualifiers() != NewCan.getCVRQualifiers())
+    return false;
+
+  if (OldCan->isPointerType() && NewCan->isPointerType()) {
+    QualType OldPointee = OldType->getPointeeType();
+    QualType NewPointee = NewType->getPointeeType();
+    // void * and char * are interchangeable.
+    if ((OldPointee->isCharType() && NewPointee->isVoidType()) ||
+        (OldPointee->isVoidType() && NewPointee->isCharType()))
+      return true;
+    return areMSCompatibleTypedefTypesInC(OldPointee, NewPointee);
+  }
+
+  bool EquallySignedInts = (OldCan->isSignedIntegerOrEnumerationType() &&
+                            NewCan->isSignedIntegerOrEnumerationType()) ||
+                           (OldCan->isUnsignedIntegerOrEnumerationType() &&
+                            NewCan->isUnsignedIntegerOrEnumerationType());
+  if (!EquallySignedInts)
+    return false;
+
+  auto OldTypeInfo = getTypeInfo(OldCan);
+  auto NewTypeInfo = getTypeInfo(NewCan);
+  return (OldTypeInfo.Width == NewTypeInfo.Width &&
+          OldTypeInfo.Align == NewTypeInfo.Align);
+}
+
 //===----------------------------------------------------------------------===//
 // ObjCQualifiedIdTypesAreCompatible - Compatibility testing for qualified id's.
 //===----------------------------------------------------------------------===//
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -1842,9 +1842,30 @@
   Filter.done();
 }
 
+static bool areMSCompatibleTypedefs(TypedefNameDecl *Old, TypedefNameDecl *New,
+                                    ASTContext &Context) {
+#ifndef NDEBUG
+  const LangOptions &Opts = Context.getLangOpts();
+  assert(Opts.MSVCCompat &&
+         "This routine must be called in Microsoft mode only!");
+  assert(!Opts.CPlusPlus && !Opts.ObjC1 && !Opts.ObjC2 &&
+         "This routine must be called in C mode only!");
+  assert(Old && New && "Expected valid typedef declarations!");
+#endif
+
+  // If both are locally-scoped, emit an error.
+  if (!Old->getDeclContext()->isFileContext() &&
+      !New->getDeclContext()->isFileContext())
+    return false;
+
+  return Context.areMSCompatibleTypedefTypesInC(Old->getUnderlyingType(),
+                                                New->getUnderlyingType());
+}
+
 bool Sema::isIncompatibleTypedef(TypeDecl *Old, TypedefNameDecl *New) {
   QualType OldType;
-  if (TypedefNameDecl *OldTypedef = dyn_cast<TypedefNameDecl>(Old))
+  auto *OldTypedef = dyn_cast<TypedefNameDecl>(Old);
+  if (OldTypedef)
     OldType = OldTypedef->getUnderlyingType();
   else
     OldType = Context.getTypeDeclType(Old);
@@ -1860,18 +1881,35 @@
     New->setInvalidDecl();
     return true;    
   }
-  
+
   if (OldType != NewType &&
       !OldType->isDependentType() &&
       !NewType->isDependentType() &&
-      !Context.hasSameType(OldType, NewType)) { 
-    int Kind = isa<TypeAliasDecl>(Old) ? 1 : 0;
-    Diag(New->getLocation(), diag::err_redefinition_different_typedef)
-      << Kind << NewType << OldType;
-    if (Old->getLocation().isValid())
-      Diag(Old->getLocation(), diag::note_previous_definition);
-    New->setInvalidDecl();
-    return true;
+      !Context.hasSameType(OldType, NewType)) {
+    const LangOptions &Opts = Context.getLangOpts();
+    bool AllowedAsMicrosoftExtInC =
+        Opts.MSVCCompat && !Opts.CPlusPlus && !Opts.ObjC1 && !Opts.ObjC2 &&
+        OldTypedef && areMSCompatibleTypedefs(OldTypedef, New, Context);
+
+    SourceLocation OldLocation = Old->getLocation();
+    if (AllowedAsMicrosoftExtInC) {
+      Diag(New->getLocation(), diag::warn_int_typedef_redefinition_ignored)
+          << NewType << OldType;
+      if (OldTypedef->isModed())
+        New->setModedTypeSourceInfo(OldTypedef->getTypeSourceInfo(),
+                                    OldTypedef->getUnderlyingType());
+      else
+        New->setTypeSourceInfo(OldTypedef->getTypeSourceInfo());
+      OldLocation = OldTypedef->getFirstDecl()->getLocation();
+    } else {
+      int Kind = isa<TypeAliasDecl>(Old) ? 1 : 0;
+      Diag(New->getLocation(), diag::err_redefinition_different_typedef)
+          << Kind << NewType << OldType;
+      New->setInvalidDecl();
+    }
+    if (OldLocation.isValid())
+      Diag(OldLocation, diag::note_previous_definition);
+    return !AllowedAsMicrosoftExtInC;
   }
   return false;
 }
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -1769,6 +1769,19 @@
   /// types.
   bool areCompatibleVectorTypes(QualType FirstVec, QualType SecondVec);
 
+  /// Return true is the given typedef types are compatible in C from MSVC's
+  /// point of view.
+  //
+  // Conditions:
+  //   1. Both typedef types are either integer, enumeral or pointers;
+  //   2. Both typedef types have the same qualifiers and signedness;
+  //   3. Both typedef types have the same size and alignment;
+  //   4. If pointers:
+  //     4.1. Levels of pointers are equal;
+  //     4.2. Pointee types are MSVC-compatible OR
+  //     4.3. One type points to void and another points to char.
+  bool areMSCompatibleTypedefTypesInC(QualType OldType, QualType NewType);
+
   /// \brief Return true if this is an \c NSObject object with its \c NSObject
   /// attribute set.
   static bool isObjCNSObjectType(QualType Ty) {
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -4264,6 +4264,10 @@
 def warn_forward_class_redefinition : Warning<
   "redefinition of forward class %0 of a typedef name of an object type is ignored">,
   InGroup<DiagGroup<"objc-forward-class-redefinition">>;
+def warn_int_typedef_redefinition_ignored : ExtWarn<
+  "ignoring conflicting integer typedef redefinition%diff{ ($ vs $)|}0,1 "
+  "as a Microsoft extension; types have the same width and signedness">,
+  InGroup<Microsoft>;
 def err_redefinition_different_typedef : Error<
   "%select{typedef|type alias|type alias template}0 "
   "redefinition with different types%diff{ ($ vs $)|}1,2">;
Index: test/Sema/ms-benign-typedef-redef.c
===================================================================
--- test/Sema/ms-benign-typedef-redef.c
+++ test/Sema/ms-benign-typedef-redef.c
@@ -0,0 +1,110 @@
+// RUN: %clang_cc1 -triple i386-pc-win32 -fms-compatibility -fsyntax-only -verify %s
+
+// Check char types.
+typedef unsigned char       UC1;
+typedef unsigned __int8     UC1;
+
+typedef unsigned char       UC2;  // expected-note{{previous}}
+typedef signed char         UC2;  // expected-error{{redefinition}}
+
+typedef char                SC1;  // expected-note{{previous}}
+typedef signed char         SC1;  // expected-warning{{ignoring conflicting integer typedef redefinition}}
+typedef __int8              SC1;
+// Check short types.
+typedef unsigned short      US1;  // expected-note{{previous}}
+typedef __wchar_t           US1;  // expected-warning{{ignoring conflicting integer typedef redefinition}}
+typedef unsigned __int16    US1;
+
+typedef unsigned short      US2;  // expected-note{{previous}}
+typedef short               US2;  // expected-error{{redefinition}}
+
+typedef short               SS1;
+typedef __int16             SS1;
+// Check int types.
+typedef unsigned int        UI1;  // expected-note{{previous}}
+typedef unsigned long       UI1;  // expected-warning{{ignoring conflicting integer typedef redefinition}}
+typedef unsigned __int32    UI1;
+
+typedef unsigned int        UI2;  // expected-note{{previous}}
+typedef long                UI2;  // expected-error{{redefinition}}
+// Check long long types.
+typedef unsigned long long  ULL;
+typedef unsigned __int64    ULL;
+
+typedef long long           LL1;
+typedef __int64             LL1;
+
+typedef unsigned long long  LL2;  // expected-note{{previous}}
+typedef __int64             LL2;  // expected-error{{redefinition}}
+// Integer vs floating type - error.
+typedef enum { A }          IF1;  // expected-note{{previous}}
+typedef float               IF1;  // expected-error{{redefinition}}
+
+typedef int                 IF2;  // expected-note{{previous}}
+typedef float               IF2;  // expected-error{{redefinition}}
+
+typedef long long           IF3;  // expected-note{{previous}}
+typedef double              IF3;  // expected-error{{redefinition}}
+// Enum vs int, enum vs enum - OK.
+typedef enum { B }          IE1;  // expected-note{{previous}}
+typedef int                 IE1;  // expected-warning{{ignoring conflicting integer typedef redefinition}}
+
+typedef enum { C }          IE2;  // expected-note{{previous}}
+typedef enum { D, E }       IE2;  // expected-warning{{ignoring conflicting integer typedef redefinition}}
+// Enum vs pointer - error.
+typedef enum { F }          IE3;  // expected-note{{previous}}
+typedef int               * IE3;  // expected-error{{redefinition}}
+// Check pointer types.
+// Poiters with different levels - error.
+typedef int              ** DL1;  // expected-note{{previous}}
+typedef long              * DL1;  // expected-error{{redefinition}}
+// void * vs char * - OK.
+typedef char              * PT1;  // expected-note{{previous}}
+typedef void              * PT1;  // expected-warning{{ignoring conflicting integer typedef redefinition}}
+
+typedef unsigned char       UChar;
+typedef UChar             * PT2;  // expected-note{{previous}}
+typedef void              * PT2;  // expected-warning{{ignoring conflicting integer typedef redefinition}}
+
+typedef char             ** PT3;  // expected-note{{previous}}
+typedef void             ** PT3;  // expected-warning{{ignoring conflicting integer typedef redefinition}}
+// void * vs other pointer - error.
+typedef int               * PT4;  // expected-note{{previous}}
+typedef void              * PT4;  // expected-error{{redefinition}}
+// Check pointee types.
+typedef int               * PT5;  // expected-note{{previous}}
+typedef long              * PT5;  // expected-warning{{ignoring conflicting integer typedef redefinition}}
+
+typedef unsigned int      * PT6;  // expected-note{{previous}}
+typedef long              * PT6;  // expected-error{{redefinition}}
+
+typedef int               * PT7;  // expected-note{{previous}}
+typedef float             * PT7;  // expected-error{{redefinition}}
+// Different qualifiers - error.
+typedef short               DQ1;  // expected-note{{previous}}
+typedef const short         DQ1;  // expected-error{{redefinition}}
+
+typedef int   * const       DQ2;  // expected-note{{previous}}
+typedef long  *             DQ2;  // expected-error{{redefinition}}
+
+typedef int               * DQ3;  // expected-note{{previous}}
+typedef volatile int      * DQ3;  // expected-error{{redefinition}}
+// Other than (enum | integer | pointer) type - error.
+typedef struct S { int x; } OT1;  // expected-note{{previous}}
+typedef int                 OT1;  // expected-error{{redefinition}}
+
+typedef float               OT2;  // expected-note{{previous}}
+typedef double              OT2;  // expected-error{{redefinition}}
+
+typedef int                 DS1;  // Different scopes - OK.
+
+int main() {
+  typedef long              DS1;  // Different scopes - OK.
+  {
+    typedef long            DS2;  // Different scopes - OK.
+  }
+  typedef int               DS2;  // Different scopes - OK.
+  // Both are in the same non-file scope - error.
+  typedef int               LS1;  // expected-note{{previous}}
+  typedef long              LS1;  // expected-error{{redefinition}}
+}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to