Hi rsmith,

We currently allow unqualified lookup for instance methods but not
static methods because we can't recover with a semantic 'this->'
insertion.

ATL headers have static methods that do unqualified lookup into
dependent base classes.  The pattern looks like:

  template <typename T> struct Foo : T {
    static int *getBarFromT() { return Bar; }
  };

Now we recover as if the user had written:

  template <typename T> struct Foo : T {
    static int *getBarFromT() { return Foo::Bar; }
  };

... which will eventually look up Bar in T at instantiation time.

Now we also emit a diagnostic in both cases.

http://reviews.llvm.org/D4079

Files:
  lib/Sema/SemaExpr.cpp
  test/SemaTemplate/ms-lookup-template-base-classes.cpp
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -1927,6 +1927,52 @@
   return true;
 }
 
+/// In Microsoft mode, if we are inside a template class member function whose
+/// parent class has dependent base classes, and we can't resolve an unqualified
+/// identifier, then assume the identifier is a member of a dependent base class.
+/// This doesn't precisely match MSVC's instantiation model, but it's close
+/// enough.
+static Expr *
+recoverFromMSUnqualifiedLookup(Sema &S, ASTContext &Context,
+                               DeclContext *DC, DeclarationNameInfo &NameInfo,
+                               SourceLocation TemplateKWLoc,
+                               const TemplateArgumentListInfo *TemplateArgs) {
+  CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC);
+
+  // Only try to recover from lookup into dependent bases inside methods.
+  if (!MD || !MD->getParent()->hasAnyDependentBases())
+    return nullptr;
+
+  // Diagnose this as unqualified lookup into a dependent base class.  If this
+  // is an instance method, we suggest inserting this-> as a fixit.
+  SourceLocation Loc = NameInfo.getLoc();
+  unsigned DI = diag::warn_found_via_dependent_bases_lookup;
+  DiagnosticBuilder DB = S.Diag(Loc, DI) << NameInfo.getName();
+
+  if (MD->isInstance()) {
+    DB << FixItHint::CreateInsertion(Loc, "this->");
+
+    // Since the 'this' expression is synthesized, we don't need to
+    // perform the double-lookup check.
+    QualType ThisType = MD->getThisType(Context);
+    NamedDecl *FirstQualifierInScope = nullptr;
+    return CXXDependentScopeMemberExpr::Create(
+        Context, /*This=*/nullptr, ThisType, /*IsArrow=*/true,
+        /*Op=*/SourceLocation(), NestedNameSpecifierLoc(), TemplateKWLoc,
+        FirstQualifierInScope, NameInfo, TemplateArgs);
+  }
+
+  // Synthesize a fake NNS that points to the derived class.  This will
+  // perform name lookup during template instantiation.
+  CXXScopeSpec SS;
+  auto *NNS = NestedNameSpecifier::Create(Context, nullptr, true,
+                                          MD->getParent()->getTypeForDecl());
+  SS.MakeTrivial(Context, NNS, SourceRange(Loc, Loc));
+  return DependentScopeDeclRefExpr::Create(
+      Context, SS.getWithLocInContext(Context), TemplateKWLoc, NameInfo,
+      TemplateArgs);
+}
+
 ExprResult Sema::ActOnIdExpression(Scope *S,
                                    CXXScopeSpec &SS,
                                    SourceLocation TemplateKWLoc,
@@ -2034,28 +2080,11 @@
   bool ADL = UseArgumentDependentLookup(SS, R, HasTrailingLParen);
 
   if (R.empty() && !ADL) {
-    // In Microsoft mode, if we are inside a template class member function
-    // whose parent class has dependent base classes, and we can't resolve
-    // an unqualified identifier, then assume the identifier is a member of a
-    // dependent base class.  The goal is to postpone name lookup to
-    // instantiation time to be able to search into the type dependent base
-    // classes.
-    // FIXME: If we want 100% compatibility with MSVC, we will have delay all
-    // unqualified name lookup.  Any name lookup during template parsing means
-    // clang might find something that MSVC doesn't.  For now, we only handle
-    // the common case of members of a dependent base class.
     if (SS.isEmpty() && getLangOpts().MSVCCompat) {
-      CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CurContext);
-      if (MD && MD->isInstance() && MD->getParent()->hasAnyDependentBases()) {
-        QualType ThisType = MD->getThisType(Context);
-        // Since the 'this' expression is synthesized, we don't need to
-        // perform the double-lookup check.
-        NamedDecl *FirstQualifierInScope = nullptr;
-        return CXXDependentScopeMemberExpr::Create(
-            Context, /*This=*/nullptr, ThisType, /*IsArrow=*/true,
-            /*Op=*/SourceLocation(), SS.getWithLocInContext(Context),
-            TemplateKWLoc, FirstQualifierInScope, NameInfo, TemplateArgs);
-      }
+      if (Expr *E = recoverFromMSUnqualifiedLookup(*this, Context, CurContext,
+                                                   NameInfo, TemplateKWLoc,
+                                                   TemplateArgs))
+        return E;
     }
 
     // Don't diagnose an empty lookup for inline assmebly.
Index: test/SemaTemplate/ms-lookup-template-base-classes.cpp
===================================================================
--- test/SemaTemplate/ms-lookup-template-base-classes.cpp
+++ test/SemaTemplate/ms-lookup-template-base-classes.cpp
@@ -64,7 +64,7 @@
 class B : public A<T> {
 public:
   void f() {
-    var = 3;
+    var = 3; // expected-warning {{use of identifier 'var' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
   }
 };
 
@@ -160,7 +160,7 @@
 class A : public T {
 public:
   void f(int hWnd) {
-    m_hWnd = 1;
+    m_hWnd = 1; // expected-warning {{use of identifier 'm_hWnd' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
   }
 };
 
@@ -204,18 +204,20 @@
   static int sa;
 };
 template <typename T> struct B : T {
-  int     foo() { return a; }
-  int    *bar() { return &a; }
+  int     foo() { return a; }           // expected-warning {{lookup into dependent bases}}
+  int    *bar() { return &a; }          // expected-warning {{lookup into dependent bases}}
   int     baz() { return T::a; }
   int T::*qux() { return &T::a; }
   static int T::*stuff() { return &T::a; }
   static int stuff1() { return T::sa; }
   static int *stuff2() { return &T::sa; }
+  static int stuff3() { return sa; }    // expected-warning {{lookup into dependent bases}}
+  static int *stuff4() { return &sa; }  // expected-warning {{lookup into dependent bases}}
 };
 
 template <typename T> struct C : T {
-  int     foo() { return b; }      // expected-error {{no member named 'b' in 'PR16014::C<PR16014::A>'}}
-  int    *bar() { return &b; }     // expected-error {{no member named 'b' in 'PR16014::C<PR16014::A>'}}
+  int     foo() { return b; }      // expected-error {{no member named 'b' in 'PR16014::C<PR16014::A>'}} expected-warning {{lookup into dependent bases}}
+  int    *bar() { return &b; }     // expected-error {{no member named 'b' in 'PR16014::C<PR16014::A>'}} expected-warning {{lookup into dependent bases}}
   int     baz() { return T::b; }   // expected-error {{no member named 'b' in 'PR16014::A'}}
   int T::*qux() { return &T::b; }  // expected-error {{no member named 'b' in 'PR16014::A'}}
   int T::*fuz() { return &U::a; }  // expected-error {{use of undeclared identifier 'U'}}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to