]> granicus.if.org Git - clang/commitdiff
Allow lookup into dependent bases in more places under -fms-compatibility
authorReid Kleckner <reid@kleckner.net>
Wed, 11 Jun 2014 00:01:28 +0000 (00:01 +0000)
committerReid Kleckner <reid@kleckner.net>
Wed, 11 Jun 2014 00:01:28 +0000 (00:01 +0000)
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 emit a diagnostic in both cases, and delay lookup in other
contexts where 'this' is available and refers to a class with dependent
bases.

Reviewed by: rsmith

Differential Revision: http://reviews.llvm.org/D4079

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@210611 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaExpr.cpp
test/SemaTemplate/ms-lookup-template-base-classes.cpp

index 6522303f650b4f6ff4a5ed5e08908bbda4557515..1c9aef2ff68b82251a0e0e919c848b780c8dbf34 100644 (file)
@@ -3662,9 +3662,13 @@ def err_unexpected_typedef : Error<
 def err_unexpected_namespace : Error<
   "unexpected namespace name %0: expected expression">;
 def err_undeclared_var_use : Error<"use of undeclared identifier %0">;
-def warn_found_via_dependent_bases_lookup : ExtWarn<"use of identifier %0 "
-   "found via unqualified lookup into dependent bases of class templates is a "
-   "Microsoft extension">, InGroup<Microsoft>;
+def ext_undeclared_unqual_id_with_dependent_base : ExtWarn<
+  "use of undeclared identifier %0; "
+  "unqualified lookup into dependent bases of class template %1 is a Microsoft extension">,
+  InGroup<Microsoft>;
+def ext_found_via_dependent_bases_lookup : ExtWarn<"use of identifier %0 "
+  "found via unqualified lookup into dependent bases of class templates is a "
+  "Microsoft extension">, InGroup<Microsoft>;
 def note_dependent_var_use : Note<"must qualify identifier to find this "
     "declaration in dependent base class">;
 def err_not_found_by_two_phase_lookup : Error<"call to function %0 that is neither "
index 041a9eb9d4444bae6b7ad4bb407f9508bcd12aff..408a8a40899cb7122cc677fce4aebed5b8c9a396 100644 (file)
@@ -1762,7 +1762,7 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
         // TODO: fixit for inserting 'Base<T>::' in the other cases.
         // Actually quite difficult!
         if (getLangOpts().MSVCCompat)
-          diagnostic = diag::warn_found_via_dependent_bases_lookup;
+          diagnostic = diag::ext_found_via_dependent_bases_lookup;
         if (isInstance) {
           Diag(R.getNameLoc(), diagnostic) << Name
             << FixItHint::CreateInsertion(R.getNameLoc(), "this->");
@@ -1927,6 +1927,54 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
   return true;
 }
 
+/// In Microsoft mode, if we are inside a template class 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.  We can only
+/// recover successfully in static methods, instance methods, and other contexts
+/// where 'this' is available.  This doesn't precisely match MSVC's
+/// instantiation model, but it's close enough.
+static Expr *
+recoverFromMSUnqualifiedLookup(Sema &S, ASTContext &Context,
+                               DeclarationNameInfo &NameInfo,
+                               SourceLocation TemplateKWLoc,
+                               const TemplateArgumentListInfo *TemplateArgs) {
+  // Only try to recover from lookup into dependent bases in static methods or
+  // contexts where 'this' is available.
+  QualType ThisType = S.getCurrentThisType();
+  const CXXRecordDecl *RD = nullptr;
+  if (!ThisType.isNull())
+    RD = ThisType->getPointeeType()->getAsCXXRecordDecl();
+  else if (auto *MD = dyn_cast<CXXMethodDecl>(S.CurContext))
+    RD = MD->getParent();
+  if (!RD || !RD->hasAnyDependentBases())
+    return nullptr;
+
+  // Diagnose this as unqualified lookup into a dependent base class.  If 'this'
+  // is available, suggest inserting 'this->' as a fixit.
+  SourceLocation Loc = NameInfo.getLoc();
+  DiagnosticBuilder DB =
+      S.Diag(Loc, diag::ext_undeclared_unqual_id_with_dependent_base)
+      << NameInfo.getName() << RD;
+
+  if (!ThisType.isNull()) {
+    DB << FixItHint::CreateInsertion(Loc, "this->");
+    return CXXDependentScopeMemberExpr::Create(
+        Context, /*This=*/nullptr, ThisType, /*IsArrow=*/true,
+        /*Op=*/SourceLocation(), NestedNameSpecifierLoc(), TemplateKWLoc,
+        /*FirstQualifierInScope=*/nullptr, 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, RD->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 +2082,10 @@ ExprResult Sema::ActOnIdExpression(Scope *S,
   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, NameInfo,
+                                                   TemplateKWLoc, TemplateArgs))
+        return E;
     }
 
     // Don't diagnose an empty lookup for inline assmebly.
index 8f9653da05e6cd7c7639ba2998086e38421d8f9b..434039500c2a30dbde848546e9fe1275cb6caf9a 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fms-compatibility -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++11 -fms-compatibility -fsyntax-only -verify %s
 
 
 template <class T>
@@ -64,7 +64,7 @@ template<class T>
 class B : public A<T> {
 public:
   void f() {
-    var = 3;
+    var = 3; // expected-warning {{use of undeclared identifier 'var'; unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}}
   }
 };
 
@@ -160,7 +160,7 @@ template <class T>
 class A : public T {
 public:
   void f(int hWnd) {
-    m_hWnd = 1;
+    m_hWnd = 1; // expected-warning {{use of undeclared identifier 'm_hWnd'; unqualified lookup into dependent bases of class template 'A' is a Microsoft extension}}
   }
 };
 
@@ -204,18 +204,20 @@ struct A {
   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'}}
@@ -259,3 +261,17 @@ struct D { };
 template struct A<D>; // expected-note {{in instantiation of member function 'PR19233::A<PR19233::D>::baz' requested here}}
 
 }
+
+namespace nonmethod_missing_this {
+template <typename T> struct Base { int y = 42; };
+template <typename T> struct Derived : Base<T> {
+  int x = y; // expected-warning {{lookup into dependent bases}}
+  auto foo(int j) -> decltype(y * j) { // expected-warning {{lookup into dependent bases}}
+    return y * j; // expected-warning {{lookup into dependent bases}}
+  }
+  int bar() {
+    return [&] { return y; }(); // expected-warning {{lookup into dependent bases}}
+  }
+};
+template struct Derived<int>;
+}