From fc142cb60229143db3de1f60bcd561deebe8c2c1 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Wed, 11 Jun 2014 00:01:28 +0000 Subject: [PATCH] Allow lookup into dependent bases in more places under -fms-compatibility 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 struct Foo : T { static int *getBarFromT() { return Bar; } }; Now we recover as if the user had written: template 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 | 10 ++- lib/Sema/SemaExpr.cpp | 74 +++++++++++++------ .../ms-lookup-template-base-classes.cpp | 30 ++++++-- 3 files changed, 82 insertions(+), 32 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 6522303f65..1c9aef2ff6 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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; +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; +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; 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 " diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 041a9eb9d4..408a8a4089 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1762,7 +1762,7 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, // TODO: fixit for inserting 'Base::' 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(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(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. diff --git a/test/SemaTemplate/ms-lookup-template-base-classes.cpp b/test/SemaTemplate/ms-lookup-template-base-classes.cpp index 8f9653da05..434039500c 100644 --- a/test/SemaTemplate/ms-lookup-template-base-classes.cpp +++ b/test/SemaTemplate/ms-lookup-template-base-classes.cpp @@ -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 @@ -64,7 +64,7 @@ template class B : public A { 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 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 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'}} @@ -259,3 +261,17 @@ struct D { }; template struct A; // expected-note {{in instantiation of member function 'PR19233::A::baz' requested here}} } + +namespace nonmethod_missing_this { +template struct Base { int y = 42; }; +template struct Derived : Base { + 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; +} -- 2.40.0