From: Reid Kleckner Date: Tue, 8 Jul 2014 20:05:48 +0000 (+0000) Subject: Allow more lookup of types in dependent base classes X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3df7e8ef65f609d3475b15569b087ba0fff3f6ee;p=clang Allow more lookup of types in dependent base classes MSVC appears to perform name lookup into dependent base classes when the dependent base class has a known primary template. This allows them to know whether some unqualified ids are types or not, which allows them to parse more class templates without typename keywords. We can do the same thing when type name lookup fails, and if we find a single type decl in one of our dependent base classes, recover as though the user wrote 'typename MyClass::TypeFromBase'. This allows us to parse some COM smart pointer classes in wrl/client.h from the Windows 8 SDK. Reviewers: rsmith Differential Revision: http://reviews.llvm.org/D4237 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@212561 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 791b4daf01..49612b0592 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -128,6 +128,56 @@ bool Sema::isSimpleTypeSpecifier(tok::TokenKind Kind) const { return false; } +static ParsedType recoverFromTypeInKnownDependentBase(Sema &S, + const IdentifierInfo &II, + SourceLocation NameLoc) { + auto *RD = dyn_cast(S.CurContext); + if (!RD || !RD->getDescribedClassTemplate()) + return ParsedType(); + + // Look for type decls in dependent base classes that have known primary + // templates. + bool FoundTypeDecl = false; + for (const auto &Base : RD->bases()) { + auto *TST = Base.getType()->getAs(); + if (!TST || !TST->isDependentType()) + continue; + auto *TD = TST->getTemplateName().getAsTemplateDecl(); + if (!TD) + continue; + auto *BasePrimaryTemplate = cast(TD->getTemplatedDecl()); + for (NamedDecl *ND : BasePrimaryTemplate->lookup(&II)) { + if (FoundTypeDecl) + return ParsedType(); + FoundTypeDecl = isa(ND); + if (!FoundTypeDecl) + return ParsedType(); + } + } + if (!FoundTypeDecl) + return ParsedType(); + + // We found some types in dependent base classes. Recover as if the user + // wrote 'typename MyClass::II' instead of 'II'. We'll fully resolve the + // lookup during template instantiation. + S.Diag(NameLoc, diag::ext_found_via_dependent_bases_lookup) << &II; + + ASTContext &Context = S.Context; + auto *NNS = NestedNameSpecifier::Create(Context, nullptr, false, + cast(Context.getRecordType(RD))); + QualType T = Context.getDependentNameType(ETK_Typename, NNS, &II); + + CXXScopeSpec SS; + SS.MakeTrivial(Context, NNS, SourceRange(NameLoc)); + + TypeLocBuilder Builder; + DependentNameTypeLoc DepTL = Builder.push(T); + DepTL.setNameLoc(NameLoc); + DepTL.setElaboratedKeywordLoc(SourceLocation()); + DepTL.setQualifierLoc(SS.getWithLocInContext(Context)); + return S.CreateParsedType(T, Builder.getTypeSourceInfo(Context, T)); +} + /// \brief If the identifier refers to a type name within this scope, /// return the declaration of that type. /// @@ -209,6 +259,14 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, } else { // Perform unqualified name lookup. LookupName(Result, S); + + // For unqualified lookup in a class template in MSVC mode, look into + // dependent base classes where the primary class template is known. + if (Result.empty() && getLangOpts().MSVCCompat && (!SS || SS->isEmpty())) { + if (ParsedType TypeInBase = + recoverFromTypeInKnownDependentBase(*this, II, NameLoc)) + return TypeInBase; + } } NamedDecl *IIDecl = nullptr; @@ -630,6 +688,14 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, LookupResult Result(*this, Name, NameLoc, LookupOrdinaryName); LookupParsedName(Result, S, &SS, !CurMethod); + // For unqualified lookup in a class template in MSVC mode, look into + // dependent base classes where the primary class template is known. + if (Result.empty() && SS.isEmpty() && getLangOpts().MSVCCompat) { + if (ParsedType TypeInBase = + recoverFromTypeInKnownDependentBase(*this, *Name, NameLoc)) + return TypeInBase; + } + // Perform lookup for Objective-C instance variables (including automatically // synthesized instance variables), if we're in an Objective-C method. // FIXME: This lookup really, really needs to be folded in to the normal diff --git a/test/SemaTemplate/ms-lookup-template-base-classes.cpp b/test/SemaTemplate/ms-lookup-template-base-classes.cpp index 434039500c..635f0b0cd0 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 -std=c++11 -fms-compatibility -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++1y -fms-compatibility -fsyntax-only -verify %s template @@ -275,3 +275,184 @@ template struct Derived : Base { }; template struct Derived; } + +namespace typedef_in_base { +template struct A { typedef T NameFromBase; }; +template struct B : A { + NameFromBase m; // expected-warning {{found via unqualified lookup into dependent bases}} +}; +static_assert(sizeof(B) == 4, ""); +} + +namespace struct_in_base { +template struct A { struct NameFromBase {}; }; +template struct B : A { + NameFromBase m; // expected-warning {{found via unqualified lookup into dependent bases}} +}; +static_assert(sizeof(B) == 1, ""); +} + +namespace enum_in_base { +template struct A { enum NameFromBase { X }; }; +template struct B : A { + NameFromBase m; // expected-warning {{found via unqualified lookup into dependent bases}} +}; +static_assert(sizeof(B) == sizeof(A::NameFromBase), ""); +} + +namespace two_types_in_base { +template struct A { typedef T NameFromBase; }; +template struct B { struct NameFromBase { T m; }; }; +template struct C : A, B { + NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}} +}; +static_assert(sizeof(C) == 4, ""); +} + +namespace type_and_decl_in_base { +template struct A { typedef T NameFromBase; }; +template struct B { static const T NameFromBase = 42; }; +template struct C : A, B { + NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}} +}; +} + +namespace classify_type_from_base { +template struct A { struct NameFromBase {}; }; +template struct B : A { + A m; // expected-warning {{found via unqualified lookup into dependent bases}} +}; +} + +namespace classify_nontype_from_base { +// MSVC does not do lookup of non-type declarations from dependent template base +// classes. The extra lookup only applies to types. +template struct A { void NameFromBase() {} }; +template struct B { }; +template struct C : A { + B a; // correct + B b; // expected-error {{use of undeclared identifier 'NameFromBase'}} +}; +} + +namespace template_in_base { +template struct A { + template struct NameFromBase { U x; }; +}; +template struct B : A { + // Correct form. + typename B::template NameFromBase m; +}; +template struct C : A { + // Incorrect form. + NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}} + //expected-error@-1 {{expected member name or ';' after declaration specifiers}} +}; +} + +namespace type_in_inner_class_in_base { +template +struct A { + struct B { typedef T NameFromBase; }; +}; +template +struct C : A::B { NameFromBase m; }; // expected-error {{unknown type name 'NameFromBase'}} +} + +namespace type_in_inner_template_class_in_base { +template +struct A { + template struct B { typedef U InnerType; }; +}; +template +struct C : A::template B { + NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}} +}; +} + +namespace have_nondependent_base { +template +struct A { + // Nothing, lookup should fail. +}; +template +struct B : A { NameFromBase m; }; // expected-error {{unknown type name 'NameFromBase'}} +struct C : A { NameFromBase m; }; // expected-error {{unknown type name 'NameFromBase'}} +} + +namespace lookup_in_function_contexts { +template struct A { typedef T NameFromBase; }; +template +struct B : A { + // expected-warning@+1 {{lookup into dependent bases}} + static auto lateSpecifiedFunc() -> decltype(NameFromBase()) { + return {}; + } + + // FIXME: MSVC accepts all of the code below that isn't C++14 only. Downgrade + // these errors to warnings. + + static void memberFunc() { + NameFromBase x; // expected-error {{unknown type name 'NameFromBase'}} + } + + static void funcLocalClass() { + struct X { + NameFromBase x; // expected-error {{unknown type name 'NameFromBase'}} + } y; + } + + void localClassMethod() { + struct X { + void bar() { + NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}} + } + } x; + x.bar(); + } + + static void funcLambda() { + auto l = []() { + NameFromBase x; // expected-error {{unknown type name 'NameFromBase'}} + }; + l(); + } + + static constexpr int constexprFunc() { + NameFromBase x = {}; // expected-error {{unknown type name 'NameFromBase'}} + // FIXME: Suppress this diagnostic, we have an initializer. + // expected-error@-2 {{variables defined in a constexpr function must be initialized}} + return sizeof(x); + } + + static auto autoFunc() { + NameFromBase x; // expected-error {{unknown type name 'NameFromBase'}} + return x; + } +}; + +// Force us to parse the methods. +template struct B; +} + +namespace function_template_deduction { +// Overloaded function templates. +template int f() { return N; } +template int f() { return sizeof(T); } + +// Dependent base class with type. +template +struct A { typedef T NameFromBase; }; +template +struct B : A { + // expected-warning@+1 {{found via unqualified lookup into dependent bases}} + int x = f(); +}; + +// Dependent base class with enum. +template struct C { enum { NameFromBase = 4 }; }; +template struct D : C { + // expected-warning@+1 {{use of undeclared identifier 'NameFromBase'; unqualified lookup into dependent bases}} + int x = f(); +}; +}