Index: cfe/trunk/lib/Sema/SemaDecl.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp +++ cfe/trunk/lib/Sema/SemaDecl.cpp @@ -128,6 +128,56 @@ 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 @@ } 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 @@ 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 Index: cfe/trunk/test/SemaTemplate/ms-lookup-template-base-classes.cpp =================================================================== --- cfe/trunk/test/SemaTemplate/ms-lookup-template-base-classes.cpp +++ cfe/trunk/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; } + +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(); +}; +}