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>; +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; def err_redefinition_different_typedef : Error< "%select{typedef|type alias|type alias template}0 " "redefinition with different types%diff{ ($ vs $)|}1,2">; 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(Old)) + auto *OldTypedef = dyn_cast(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(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(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: 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}} +}