This is an archive of the discontinued LLVM Phabricator instance.

26748 - clang-cl fails to compile atlctrlw.h header from WTL
Needs RevisionPublic

Authored by avt77 on Apr 25 2016, 4:36 AM.

Details

Reviewers
rnk
Summary

This is the first patch to fix clang-cl incompatibility prohibited to compile the header file. The patch covers the following case:

class CCommandBarCtrlBase {
public:

typedef int CMsgHookMap;

};

template <class T>
class CCommandBarCtrlImpl : public T {
public:

void foo() {
  void *p = new CMsgHookMap; // "new typename T::CMsgHookMap" works fine
}

};

void bar() {

CCommandBarCtrlImpl<CCommandBarCtrlBase> x;
x.foo();

}

Diff Detail

Event Timeline

avt77 updated this revision to Diff 54831.Apr 25 2016, 4:36 AM
avt77 retitled this revision from to 26748 - clang-cl fails to compile atlctrlw.h header from WTL .
avt77 updated this object.
avt77 added a reviewer: rnk.
avt77 added a subscriber: cfe-commits.
rnk requested changes to this revision.Apr 25 2016, 9:35 AM
rnk edited edge metadata.

We definitely should not rely on typo correction or other error recovery mechanisms to do this for us. Otherwise we can end up accepting ill-formed programs like this one:

struct MyStruct { };
template <typename T>
struct A : T {
  MyStruct * f() { return new MyStructX; } // typo correction will recover to MyStruct
};

I think the right way to do this is to form a dependent AST node like a DependentScopeDeclRefExpr. We do the same thing elsewhere under -fms-compatibility. This way, we can redo lookup at instantiation time.

The recovered AST of the test case you gave would resemble the source code new typename CCommandBarCtrlImpl::CMsgHookMap. By using the derived class name CCommandBarCtrlImpl instead of T, we don't have to guess which dependent base CMsgHookMap is coming from.

lib/Sema/SemaLookup.cpp
4440

This is a dangerous change, and should be done as a separate change if we still want to do it. I have seen cases where typo correction and -fms-compatibility end up fighting, and I don't think they've all been fixed.

This revision now requires changes to proceed.Apr 25 2016, 9:35 AM
avt77 added a comment.Apr 26 2016, 3:16 AM

OK, I'll try to implement something like a DependentScopeDeclRefExpr but because of the latest changes in Intel I'm afraid it will take some more time from me.

avt77 added a comment.May 20 2016, 3:05 AM

It seems I gave up with this issue :-( We have 2 possible situations here:

  1. A type from instantiated type used inside the template (like in the test I'm working on)
  2. A feature from instantiated type used inside the template (like below)

struct A {

int bar ();

}
template <class T> struct B : public T {

int foo () {return bar();}

....
}
void test (){

B<A> b;
int c = b.foo();

}
We can't made any assumption about the unknown identificator: is it type or a feature? In the first case we should deal with ParsedType but in the second case we should deal with DependentScopeDeclRefExpr. Of course we could create something like TypeOrExpr but I'm not sure it's an acceptable solution. The best way here is real implementation of late parsing for all in-class defined features from templates: this parsing should be done at instantiation point only (when we can discover all involved entities.).

What do you think about? Could you give me a hint how to resolve the issue without really late parsing?

rnk added a comment.May 20 2016, 9:46 AM

You've described the core issue with parsing C++ templates prior to instantiation, you have to know what identifiers are types, and that's why we need typename. :) MSVC only parses templates after instantiation, so it doesn't need typename.

To deal with the specific case in ATL, we can use the 'new' expression as a hint that the unknown identifier is actually a dependent type. There should be some bit of context we can look at in DiagnoseUnknownTypeName to know that we are in a new expression inside a dependent template, and then form a DependentNameType to delay checking until template instantiation.

In the general case, I don't think we can ever behave exactly like MSVC without implementing token-based template instantiation, and nobody wants to add an entirely new, untested, separate codepath just for MSVC-style template instantiation. It means giving up on compiling invalid C++ that MSVC accepts, such as this:

template <typename T>
struct A : T {
  void f() {
    g(TypeOrFunction());
  }
};
// In A<B>, it's a type
struct B {
  struct TypeOrFunction { };
  void g(TypeOrFunction);
};
template struct A<B>;
// In A<C>, it's a function
struct C {
  int TypeOrFunction();
  void g(int);
};
template struct A<C>;
rnk added a comment.May 20 2016, 3:41 PM
In D19479#435620, @rnk wrote:

To deal with the specific case in ATL, we can use the 'new' expression as a hint that the unknown identifier is actually a dependent type. There should be some bit of context we can look at in DiagnoseUnknownTypeName to know that we are in a new expression inside a dependent template, and then form a DependentNameType to delay checking until template instantiation.

I went ahead and put together a patch for this in http://reviews.llvm.org/D20500, that might supersede this patch.