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 <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

llvm-svn: 210611
This commit is contained in:
Reid Kleckner 2014-06-11 00:01:28 +00:00
parent 2dace6e54b
commit 10ca24c631
3 changed files with 82 additions and 32 deletions

View 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 "

View 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.

View 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>;
}