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(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(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 { 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 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 struct C : T { - int foo() { return b; } // expected-error {{no member named 'b' in 'PR16014::C'}} - int *bar() { return &b; } // expected-error {{no member named 'b' in 'PR16014::C'}} + int foo() { return b; } // expected-error {{no member named 'b' in 'PR16014::C'}} expected-warning {{lookup into dependent bases}} + int *bar() { return &b; } // expected-error {{no member named 'b' in 'PR16014::C'}} 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'}}