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>; +def warn_benign_redefinition_different_typedef : ExtWarn< + "benign typedef redefinition with different types%diff{ ($ vs $)|}0,1 " + "is a Microsoft extension">, 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 @@ -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(Old)) + TypedefNameDecl *OldTypedef = dyn_cast(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(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(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,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}} +}