d.zobnin.bugzilla created this revision.
d.zobnin.bugzilla added reviewers: rnk, majnemer.
d.zobnin.bugzilla added a subscriber: cfe-commits.

Allow typedef redefinition with different types in C if the types are equally 
qualified, sized and aligned. MSVC allows such redefinition, emits warning 
C4142: "benign redefinition of type" and propagates the type from the first 
typedef declaration in the entire redeclaration chain. 

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
@@ -6723,6 +6723,43 @@
   return false;
 }
 
+bool ASTContext::areMSCompatibleTypesInC(QualType OldType, QualType NewType) {
+  assert(getLangOpts().MicrosoftExt &&
+         "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 areMSCompatibleTypesInC(OldPointee, NewPointee);
+  }
+
+  // Enum vs float not allowed.
+  if ((OldCan->isRealFloatingType() && NewCan->isEnumeralType()) ||
+      (OldCan->isEnumeralType() && NewCan->isRealFloatingType()))
+    return false;
+
+  if (OldCan->isRealType() && NewCan->isRealType()) {
+    auto OldTypeInfo = getTypeInfo(OldCan);
+    auto NewTypeInfo = getTypeInfo(NewCan);
+    return (OldTypeInfo.Width == NewTypeInfo.Width &&
+            OldTypeInfo.Align == NewTypeInfo.Align);
+  }
+
+  return false;
+}
+
 //===----------------------------------------------------------------------===//
 // ObjCQualifiedIdTypesAreCompatible - Compatibility testing for qualified id's.
 //===----------------------------------------------------------------------===//
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -1842,9 +1842,31 @@
   Filter.done();
 }
 
+static bool areMSCompatibleTypedefs(TypedefNameDecl *OldTypedef,
+                                    TypedefNameDecl *NewTypedef,
+                                    ASTContext &Context) {
+#ifndef NDEBUG
+  const LangOptions &Opts = Context.getLangOpts();
+  assert(Opts.MicrosoftExt &&
+         "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(OldTypedef && NewTypedef && "Expected valid typedef declarations!");
+#endif
+
+  // If both are locally-scoped, emit an error.
+  if (!OldTypedef->getDeclContext()->isFileContext() &&
+      !NewTypedef->getDeclContext()->isFileContext())
+    return false;
+
+  return Context.areMSCompatibleTypesInC(OldTypedef->getUnderlyingType(),
+                                         NewTypedef->getUnderlyingType());
+}
+
 bool Sema::isIncompatibleTypedef(TypeDecl *Old, TypedefNameDecl *New) {
   QualType OldType;
-  if (TypedefNameDecl *OldTypedef = dyn_cast<TypedefNameDecl>(Old))
+  TypedefNameDecl *OldTypedef = dyn_cast<TypedefNameDecl>(Old);
+  if (OldTypedef)
     OldType = OldTypedef->getUnderlyingType();
   else
     OldType = Context.getTypeDeclType(Old);
@@ -1860,18 +1882,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.MicrosoftExt && !Opts.CPlusPlus && !Opts.ObjC1 && !Opts.ObjC2 &&
+        OldTypedef && areMSCompatibleTypedefs(OldTypedef, New, Context);
+
+    SourceLocation OldLocation = Old->getLocation();
+    if (AllowedAsMicrosoftExtInC) {
+      Diag(New->getLocation(), diag::warn_benign_redefinition_different_typedef)
+          << 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
@@ -1768,6 +1768,19 @@
   /// types.
   bool areCompatibleVectorTypes(QualType FirstVec, QualType SecondVec);
 
+  /// Return true is the given types are compatible in C from MSVC's point of
+  /// view.
+  //
+  // Conditions:
+  //   1. Both types are equally-qualified, sized and aligned;
+  //   2. Both types are either integer, floating-point, enumeral or pointers;
+  //   3. If pointers:
+  //     3.1. Levels of pointers are equal;
+  //     3.2. Pointee types are MSVC-compatible OR
+  //     3.3. One type points to void and another points to char.
+  //   4. Enumeral types are NOT compatible with floating-point types.
+  bool areMSCompatibleTypesInC(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
@@ -4262,6 +4262,9 @@
 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_benign_redefinition_different_typedef : ExtWarn<
+  "benign typedef redefinition with different types%diff{ ($ vs $)|}0,1 "
+  "is a Microsoft extension">, 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,101 @@
+// RUN: %clang_cc1 -triple i386-pc-win32 -fms-extensions -fsyntax-only -verify %s
+
+// Equally-sized and equally-aligned - OK.
+typedef unsigned char  UChar;
+typedef char           T1; // expected-note3{{previous}}
+typedef UChar          T1; // expected-warning{{benign typedef redefinition with different types}}
+typedef unsigned char  T1; // expected-warning{{benign typedef redefinition with different types}}
+typedef signed char    T1; // expected-warning{{benign typedef redefinition with different types}}
+typedef __int8         T1;
+
+typedef unsigned short UShort;
+typedef short          T2; // expected-note3{{previous}}
+typedef unsigned short T2; // expected-warning{{benign typedef redefinition with different types}}
+typedef UShort         T2; // expected-warning{{benign typedef redefinition with different types}}
+typedef __wchar_t      T2; // expected-warning{{benign typedef redefinition with different types}}
+typedef __int16        T2;
+
+typedef unsigned int   UInt;
+typedef int            T3; // expected-note5{{previous}}
+typedef unsigned int   T3; // expected-warning{{benign typedef redefinition with different types}}
+typedef UInt           T3; // expected-warning{{benign typedef redefinition with different types}}
+typedef long           T3; // expected-warning{{benign typedef redefinition with different types}}
+typedef unsigned long  T3; // expected-warning{{benign typedef redefinition with different types}}
+typedef __int32        T3;
+typedef float          T3; // expected-warning{{benign typedef redefinition with different types}}
+
+typedef unsigned long long ULongLong;
+typedef long long      T4; // expected-note2{{previous}}
+typedef ULongLong      T4; // expected-warning{{benign typedef redefinition with different types}}
+typedef __int64        T4;
+typedef double         T4; // expected-warning{{benign typedef redefinition with different types}}
+
+// Enum vs floating type - error.
+typedef enum { A }     E1; // expected-note{{previous}}
+typedef float          E1; // expected-error{{redefinition}}
+
+// Enum vs int, enum vs enum - OK.
+typedef enum { B }     E2; // expected-note{{previous}}
+typedef int            E2; // expected-warning{{benign typedef redefinition with different types}}
+
+typedef enum { C }     E3; // expected-note{{previous}}
+typedef enum { D, E }  E3; // expected-warning{{benign typedef redefinition with different types}}
+
+// Enum vs pointer - error.
+typedef enum { F }     E4; // expected-note{{previous}}
+typedef int          * E4; // expected-error{{redefinition}}
+
+// void * vs char * - OK.
+typedef char         * T5; // expected-note2{{previous}}
+typedef UChar        * T5; // expected-warning{{benign typedef redefinition with different types}}
+typedef void         * T5; // expected-warning{{benign typedef redefinition with different types}}
+
+typedef char        ** T6; // expected-note2{{previous}}
+typedef UChar       ** T6; // expected-warning{{benign typedef redefinition with different types}}
+typedef void        ** T6; // expected-warning{{benign typedef redefinition with different types}}
+
+// void * vs other pointer - error.
+typedef int          * T7; // expected-note{{previous}}
+typedef void         * T7; // expected-error{{redefinition}}
+
+// Pointers to compatible types - OK.
+typedef int          * T8; // expected-note2{{previous}}
+typedef float        * T8; // expected-warning{{benign typedef redefinition with different types}}
+typedef long         * T8; // expected-warning{{benign typedef redefinition with different types}}
+
+// Poiters with different levels - error.
+typedef int         ** T9; // expected-note{{previous}}
+typedef long         * T9; // expected-error{{redefinition}}
+
+// Different sizeof on floating types - error.
+typedef float          T10; // expected-note{{previous}}
+typedef double         T10; // expected-error{{redefinition}}
+
+// Different qualifiers - error.
+typedef UShort         T11; // expected-note{{previous}}
+typedef const short    T11; // expected-error{{redefinition}}
+
+typedef int * const    T12; // expected-note{{previous}}
+typedef long *         T12; // expected-error{{redefinition}}
+
+typedef UInt           T13; // expected-note{{previous}}
+typedef volatile float T13; // expected-error{{redefinition}}
+
+// Other than (enum | integer | floating | pointer) type - error.
+typedef struct S { int x; } S1; // expected-note{{previous}}
+typedef int                 S1; // expected-error{{redefinition}}
+
+typedef float T14;  // Different scopes - OK.
+
+int main() {
+  typedef long T14; // Different scopes - OK.
+
+  {
+    typedef float T15;
+  }
+  typedef int T15;
+
+  // Both are in the same non-file scope - error.
+  typedef int  T16; // expected-note{{previous}}
+  typedef long T16; // 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