From: Reid Kleckner Date: Thu, 14 Aug 2014 23:34:52 +0000 (+0000) Subject: Limit our MSVC compat hack for nested names from dependent bases X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=99af5d60f1c51b67547fb82056221f2dd7f88aaa;p=clang 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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@215683 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index a70aca2ad8..b1b8b5d1dc 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -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(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; + } } } diff --git a/test/SemaTemplate/lookup-dependent-bases.cpp b/test/SemaTemplate/lookup-dependent-bases.cpp index 61fca4a2a4..f7867d8339 100644 --- a/test/SemaTemplate/lookup-dependent-bases.cpp +++ b/test/SemaTemplate/lookup-dependent-bases.cpp @@ -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 A { -public: - typedef C D; +template +struct A { + typedef C D; }; -template -class B : public A { -public: - void foo() { - D::foo2(); - } +template +struct B : A { + 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; // 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 +struct B : T { + struct C { + void foo() { + D::foo2(); // expected-error {{use of undeclared identifier 'D'}} + } + }; +}; + +template struct B; // 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 +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; // Instantiation has no warnings. +} diff --git a/test/SemaTemplate/ms-lookup-template-base-classes.cpp b/test/SemaTemplate/ms-lookup-template-base-classes.cpp index 40f73c0d9e..4bdb97bbbd 100644 --- a/test/SemaTemplate/ms-lookup-template-base-classes.cpp +++ b/test/SemaTemplate/ms-lookup-template-base-classes.cpp @@ -220,7 +220,8 @@ template struct C : T { 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'}} + 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; @@ -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 struct D : C { int x = f(); }; } + +namespace function_template_undef_impl { +template +void f() { + Undef::staticMethod(); // expected-error {{use of undeclared identifier 'Undef'}} + UndefVar.method(); // expected-error {{use of undeclared identifier 'UndefVar'}} +} +}