forked from OSchip/llvm-project
Limit our MSVC compat hack for nested names from dependent bases
Previously, any undeclared unqualified id starting a nested name specifier in a dependent context would have its lookup retried during template instantiation. Now we limit that retry hack to methods of a class with dependent bases. Free function templates in particular are no longer affected by this hack. Also, diagnose this as a Microsoft extension. This has the downside that template authors may see this warning *and* an error during instantiation time about this identifier. Fixing that will probably require formalizing some kind of "delayed" identifier, instead of our ad-hoc solutions of forming dependent AST nodes when lookup fails. Based on a patch by Kim Gräsman! Differential Revision: http://reviews.llvm.org/D4854 llvm-svn: 215683
This commit is contained in:
parent
81db58e177
commit
062be331e2
|
@ -703,8 +703,13 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S,
|
|||
if (getLangOpts().MSVCCompat) {
|
||||
DeclContext *DC = LookupCtx ? LookupCtx : CurContext;
|
||||
if (DC->isDependentContext() && DC->isFunctionOrMethod()) {
|
||||
SS.Extend(Context, &Identifier, IdentifierLoc, CCLoc);
|
||||
return false;
|
||||
CXXRecordDecl *ContainingClass = dyn_cast<CXXRecordDecl>(DC->getParent());
|
||||
if (ContainingClass && ContainingClass->hasAnyDependentBases()) {
|
||||
Diag(IdentifierLoc, diag::ext_undeclared_unqual_id_with_dependent_base)
|
||||
<< &Identifier << ContainingClass;
|
||||
SS.Extend(Context, &Identifier, IdentifierLoc, CCLoc);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,55 @@
|
|||
// RUN: %clang_cc1 -fms-compatibility -fsyntax-only -verify %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
class C {
|
||||
public:
|
||||
static void foo2() { }
|
||||
namespace basic {
|
||||
struct C {
|
||||
static void foo2() {}
|
||||
};
|
||||
template <class T>
|
||||
class A {
|
||||
public:
|
||||
typedef C D;
|
||||
template <typename T>
|
||||
struct A {
|
||||
typedef C D;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class B : public A<T> {
|
||||
public:
|
||||
void foo() {
|
||||
D::foo2();
|
||||
}
|
||||
template <typename T>
|
||||
struct B : A<T> {
|
||||
void foo() {
|
||||
D::foo2(); // expected-warning {{use of undeclared identifier 'D'; unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}}
|
||||
}
|
||||
};
|
||||
|
||||
template struct B<int>; // Instantiation has no warnings.
|
||||
}
|
||||
|
||||
namespace nested_nodep_base {
|
||||
// There are limits to our hacks, MSVC accepts this, but we don't.
|
||||
struct A {
|
||||
struct D { static void foo2(); };
|
||||
};
|
||||
template <typename T>
|
||||
struct B : T {
|
||||
struct C {
|
||||
void foo() {
|
||||
D::foo2(); // expected-error {{use of undeclared identifier 'D'}}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template struct B<A>; // Instantiation has no warnings.
|
||||
}
|
||||
|
||||
namespace nested_dep_base {
|
||||
// We actually accept this because the inner class has a dependent base even
|
||||
// though it isn't a template.
|
||||
struct A {
|
||||
struct D { static void foo2(); };
|
||||
};
|
||||
template <typename T>
|
||||
struct B {
|
||||
struct C : T {
|
||||
void foo() {
|
||||
D::foo2(); // expected-warning {{use of undeclared identifier 'D'; unqualified lookup into dependent bases of class template 'C' is a Microsoft extension}}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template struct B<A>; // Instantiation has no warnings.
|
||||
}
|
||||
|
|
|
@ -220,7 +220,8 @@ template <typename T> struct C : T {
|
|||
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'}}
|
||||
int T::*fuz() { return &U::a; } // expected-error {{use of undeclared identifier 'U'}} \
|
||||
// expected-warning {{unqualified lookup into dependent bases of class template 'C'}}
|
||||
};
|
||||
|
||||
template struct B<A>;
|
||||
|
@ -249,7 +250,8 @@ struct A : T {
|
|||
::UndefClass::undef(); // expected-error {{no member named 'UndefClass' in the global namespace}}
|
||||
}
|
||||
void baz() {
|
||||
B::qux(); // expected-error {{use of undeclared identifier 'B'}}
|
||||
B::qux(); // expected-error {{use of undeclared identifier 'B'}} \
|
||||
// expected-warning {{unqualified lookup into dependent bases of class template 'A'}}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -460,3 +462,11 @@ template <typename T> struct D : C<T> {
|
|||
int x = f<NameFromBase>();
|
||||
};
|
||||
}
|
||||
|
||||
namespace function_template_undef_impl {
|
||||
template<class T>
|
||||
void f() {
|
||||
Undef::staticMethod(); // expected-error {{use of undeclared identifier 'Undef'}}
|
||||
UndefVar.method(); // expected-error {{use of undeclared identifier 'UndefVar'}}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue