CLang compiling windows.h from the Windows SDK

Hi,

Read this if you are interested in CLang compatibility with MSVC.

Today I did some testing compiling just <windows.h> in C and C++ mode
using CLang. (Let's forget MFC)
Note that including <windows.h> pulls close to 80000 lines of C/C++ declaration.
I used the <Windows.h> bundled with Visual Studio 2008 (SDK v7.0A).
BTW the <Windows.h> coming with the MinGW package is compiling fine
but that's to be expected I think.

I created a file "test.c" containing just 1 line:
#include "Windows.h"

Then I compiled in C mode and C++ mode using
clang -I"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include"
-D_MSC_VER=1500 -Wno-missing-declarations -Wno-invalid-token-paste
-Wno-unused-value -x c test.c
and
clang -I"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include"
-D_MSC_VER=1500 -Wno-missing-declarations -Wno-invalid-token-paste
-Wno-unused-value -x c++ test.c

_MSC_VER=1500 emulate MSVC 2008.

In C mode there are only 2 kinds of error:
1- MSVC allows typedef redefinition with a different type, CLang doesn't.
2- CLang flags this as a error:
            typedef struct tagPROPVARIANT PROPVARIANT;
            void foo(PROPVARIANT a); <====== array has
incomplete element type 'PROPVARIANT'
(both cl.exe and gcc.exe accepts foo prototype, note that
tagPROPVARIANT is actually undefined at this point)

In C++ mode there are only 3 kinds of error:
1- MSVC allows typedef redefinition with a different type, CLang
doesn't. (same thing as in C mode)
2- CLang doesn't recognize the __uuidof operator.
3. CLang doesn't like:
     template<typename T> void** IID_PPV_ARGS_Helper(T** pp)
     {
        static_cast<IUnknown*>(*pp); <=== clang error:
unknown type name 'IUnknown'
        return reinterpret_cast<void**>(pp);
     }
Note that 'IUnknown' is actually undefined at this point but MSVC accept it.

CLang is actually very close to be able to handle the core Windows
system headers as provided by Microsoft.
Should I report these as bugs?
Is CLang being able to parse the Windows SDKs core headers a worthwhile goal?

I think Per was also defining WIN32_LEAN_AND_MEAN in addition to setting
up _MSC_VER before including Windows.h to reduce what was included. You
can also try defining VC_EXTRALEAN to further reduce dependencies.

Example:

// test.c
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>

- Jesse

I think Per was also defining WIN32_LEAN_AND_MEAN in addition to setting
up _MSC_VER before including Windows.h to reduce what was included. You
can also try defining VC_EXTRALEAN to further reduce dependencies.

That's not a realistic use case, though. Some apps define these macros, many don't. If we're trying to get better Windows compatibility, we need to handle all of windows.h.

Hi,

Read this if you are interested in CLang compatibility with MSVC.

Today I did some testing compiling just <windows.h> in C and C++ mode
using CLang. (Let's forget MFC)
Note that including <windows.h> pulls close to 80000 lines of C/C++ declaration.
I used the <Windows.h> bundled with Visual Studio 2008 (SDK v7.0A).
BTW the <Windows.h> coming with the MinGW package is compiling fine
but that's to be expected I think.

I created a file "test.c" containing just 1 line:
#include "Windows.h"

Then I compiled in C mode and C++ mode using
clang -I"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include"
-D_MSC_VER=1500 -Wno-missing-declarations -Wno-invalid-token-paste
-Wno-unused-value -x c test.c
and
clang -I"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include"
-D_MSC_VER=1500 -Wno-missing-declarations -Wno-invalid-token-paste
-Wno-unused-value -x c++ test.c

_MSC_VER=1500 emulate MSVC 2008.

In C mode there are only 2 kinds of error:
1- MSVC allows typedef redefinition with a different type, CLang doesn't.

What types are involved? This may be a symptom of a different incompatibility in the type system.

2- CLang flags this as a error:
           typedef struct tagPROPVARIANT PROPVARIANT;
           void foo(PROPVARIANT a); <====== array has
incomplete element type 'PROPVARIANT'
(both cl.exe and gcc.exe accepts foo prototype, note that
tagPROPVARIANT is actually undefined at this point)

We sometimes downgrade errors to warnings when other compilers fail to diagnose a particular conformance issue. We can consider that here.

In C++ mode there are only 3 kinds of error:
1- MSVC allows typedef redefinition with a different type, CLang
doesn't. (same thing as in C mode)
2- CLang doesn't recognize the __uuidof operator.

This could/should be implemented under -fms-extensions.

3. CLang doesn't like:
    template<typename T> void** IID_PPV_ARGS_Helper(T** pp)
    {
       static_cast<IUnknown*>(*pp); <=== clang error:
unknown type name 'IUnknown'
       return reinterpret_cast<void**>(pp);
    }
Note that 'IUnknown' is actually undefined at this point but MSVC accept it.

Is it truly undeclared, or is it some kind of magic type? The former is very hard to deal with.

CLang is actually very close to be able to handle the core Windows
system headers as provided by Microsoft.
Should I report these as bugs?

Bugs or extension requests, yes. Please include something like [Microsoft] in the title so we can keep them separate from other bugs.

It is truly undeclared, this is due to windows being lax on what it accepts in templates. However, with some careful consideration, clang could possibly consider making it a magic type, if there is only a very small number of such hacks required to parse windows.h. I haven't done a careful analysis of how many such examples there are.

Chris

static_cast isn't a template, it's an operator. Does MSVC accept "IUnknown * i;" or "static_cast <foo*>(ptr)" when IUnknown and foo haven't been declared?

Sean

Ehm, I have a file "Unknwn.h" in my Microsoft SDK dir, that defines
IUnknown as:

  MIDL_INTERFACE("00000000-0000-0000-C000-000000000046")
  IUnknown
  {
  public:
  [...]

where MIDL_INTERFACE basically means "struct" (from MqOaI.h):

  #ifndef MIDL_INTERFACE
  #if _MSC_VER >= 1100
  #define MIDL_INTERFACE(x) struct __declspec(uuid(x)) __declspec(novtable)
  #else
  #define MIDL_INTERFACE(x) struct
  #endif //_MSC_VER
  #endif //MIDL_INTERFACE

The only declaration in Unknwn.h that I can't parse is this IUnknown
forward declaration, again in Unknwn.h:

  #ifndef __IUnknown_FWD_DEFINED__
  #define __IUnknown_FWD_DEFINED__
  typedef interface IUnknown IUnknown;
  #endif /* __IUnknown_FWD_DEFINED__ */

That "interface" keyword is probably something Microsoft-specific...

Oh yeah, I agree, I was just recommending that as a start to get the
basics all compiling.

- Jesse

Yeah, MSVC++ doesn't treat static_cast as a template, but if you look at
the function that has the static_cast<IUnknown*>(p) expression, the
function itself is a template, and IUnknown even though it's not a
dependent type of the template doesn't have it's name resolved until the
template function is instantiated in MSVC++.

interface is just a preprocessor macro, should be something like #define
interface struct nested in one the internal COM headers ComBase.h or
BaseTyps.h.

It doesn't matter that static_cast is a template or not.

Cl.exe will even parse this: (as long as IID_PPV_ARGS_Helper is not called)

   template<typename T> void** IID_PPV_ARGS_Helper(T** pp)
    {
  sdsa fdfdsf df sd fds++ ++ -- < >fdsfd;;;;;<<<>>>
    }

Any valid C++ token sequence will be accepted during a template
definition (or is that a declaration?). The way cl.exe works is
probably to save the template function as a token sequence list and
defer actual parsing and semantic analysis to instantiation time. In
our case IUnknown will then be declared.

/me cries

To get around this problem:
I create a file inc.h containing just 1 forward declaration:
struct IUnknown;

Then I call clang using the force include option "-include inc.h"
Work fine for me. For my needs, no patch needed in clang.

Yes, but clang still fails without it, meaning it doesn't support
Windows out of the box. Is there a way to force the declaration in clang
itself?

We should be able to do so pretty easily. We should also add an option to disable this, just in case someone was using IUnknown for something different.

Does anyone take issue with this?

Sean

Shouldn't it just be enabled when the ms-extensions flag is present?
Since IUnknown is used by windows anyways, it's kinda a reserved word. I
don't really think it should be yet another compiler flag.

We could push the forward declaration into the predefines buffer for Windows targets.

Shouldn't it just be enabled when the ms-extensions flag is present?
Since IUnknown is used by windows anyways, it's kinda a reserved word. I
don't really think it should be yet another compiler flag.

I agree that it should go with -fms-extensions. It's just a forward
declaration so it shouldn't really be an issue even if someone did
define it as something else.

Yes, it's in BaseTyps.h:

  #if defined(__cplusplus) && !defined(CINTERFACE)
  //#define interface struct FAR
  #define __STRUCT__ struct
  #define interface __STRUCT__
  [...]
  #else

  #define interface struct
  [...]

So there should be no need to do special magic for IUnknown, and it
looks like even the __declspec(novtable) and __declspec(uuid(x))
attributes are supported with -fms-extensions. :slight_smile:

You are right. MSVC doesn't allow typedef redefinition after all.
Here is a code simplification of what is happening in windows.h

struct A {
  char a;
  char b;
};

typedef int foo[1];
typedef int foo[(long)&(((A *)0)->b)];

clang will flag the second typedef as a redefintion. msvc and gcc don't.