Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -3864,6 +3864,9 @@ 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; +def ext_found_via_dependent_base_lookup : ExtWarn<"use of identifier %0 " + "found via unqualified lookup into dependent bases of class template %1 is a " + "Microsoft extension">, InGroup; 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 " Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -3497,6 +3497,40 @@ // Primary Expressions. SourceRange getExprRange(Expr *E) const; + /// \brief Checks if currently looking for symbols in a class with dependent + /// bases. Used in Microsoft Compatibility mode for better compatibility + /// during lookup into classes with dependent bases. + /// \param LastChanceToRecover true if no need to check for decl existence in + /// base dependent classes, false if still need to check. + /// \param SS If not nullptr and not empty the base class is calculated from + /// this scope. If not nullptr and empty and \a ObjectType is empty then it is + /// calculated in this method from the current context. + /// \param ObjectType If specified the base class is calculated from this + /// type. + /// \param II Used for diagnostic only. If \a SS is not nullptr and empty and + /// \a ObjectType is empty and \a is not nullptr \a SS is calculated and a + /// diagnostic is emitted. + /// \return true if currently trying to lookup in class with the dependent + /// base, false otherwise. + bool isInClassWithAnyDependentBase(SourceLocation Loc, + bool IsLastChanceToRecover, + CXXScopeSpec *SS, QualType ObjectType, + const IdentifierInfo *II); + + /// \brief Checks if the specified identifier \a II is a class template + /// declaration declared in a class with dependent bases (specified in \a SS + /// scope). + bool isClassTemplateInClassWithAnyDependentBase(CXXScopeSpec &SS, + SourceLocation Loc, + const IdentifierInfo &II); + + /// \brief Checks if the specified identifier \a II is any member declared in + /// a class with dependent bases (specified in \a SS scope). + bool isMemberInClassWithAnyDependentBase(CXXScopeSpec &SS, SourceLocation Loc, + const IdentifierInfo &II, + bool NonInstanceMemberOnly, + bool SupposeExistInBaseParm); + ExprResult ActOnIdExpression( Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, UnqualifiedId &Id, bool HasTrailingLParen, bool IsAddressOfOperand, Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -129,98 +129,166 @@ } namespace { -enum class UnqualifiedTypeNameLookupResult { +/// \brief Result of lookup for members with the specified identifier into a +/// class with any dependent base. +enum class IdNameLookupResult { + /// \brief No members found at all. NotFound, - FoundNonType, - FoundType + /// \brief If found a member, but its type does not match the expected one. + FoundNotSpecifiedKind, + /// \brief There are no members is found, but class has a base of class + /// TemplateTypeParmType and we are allowed to suppose that the specified + /// identifier may be a member of this base. + SupposeFoundSpecifiedKind, + /// \brief Found a member of the expected type. + FoundSpecifiedKind, }; } // namespace -/// \brief Tries to perform unqualified lookup of the type decls in bases for +/// \brief Tries to perform unqualified lookup of the decls in bases for /// dependent class. -/// \return \a NotFound if no any decls is found, \a FoundNotType if found not a -/// type decl, \a FoundType if only type decls are found. -static UnqualifiedTypeNameLookupResult -lookupUnqualifiedTypeNameInBase(Sema &S, const IdentifierInfo &II, - SourceLocation NameLoc, - const CXXRecordDecl *RD) { +/// \return \a NotFound if no any decls is found, \a FoundNotSpecifiedKind if +/// found a decl of different type, \a FoundSpecifiedKind if only decls of the +/// expected types are found. If \a SupposeExistInBaseParm is true and one of +/// the base classes is a TemplateTypeParmType then it is supposed that the +/// specified identifier may be a field of that base class. If there are no +/// other identifiers is found, \a SupposeFoundSpecifiedKind is returned in this +/// case. +static IdNameLookupResult lookupIdNameInDependentClass( + Sema &S, const IdentifierInfo &II, SourceLocation NameLoc, + const CXXRecordDecl *RD, bool SupposeExistInBaseParm, + const llvm::function_ref &CheckKind) { if (!RD->hasDefinition()) - return UnqualifiedTypeNameLookupResult::NotFound; + return IdNameLookupResult::NotFound; // Look for type decls in base classes. - UnqualifiedTypeNameLookupResult FoundTypeDecl = - UnqualifiedTypeNameLookupResult::NotFound; - for (const auto &Base : RD->bases()) { - const CXXRecordDecl *BaseRD = nullptr; - if (auto *BaseTT = Base.getType()->getAs()) - BaseRD = BaseTT->getAsCXXRecordDecl(); - else if (auto *TST = Base.getType()->getAs()) { - // Look for type decls in dependent base classes that have known primary - // templates. - if (!TST || !TST->isDependentType()) - continue; - auto *TD = TST->getTemplateName().getAsTemplateDecl(); - if (!TD) - continue; - auto *BasePrimaryTemplate = - dyn_cast_or_null(TD->getTemplatedDecl()); - if (!BasePrimaryTemplate) - continue; - BaseRD = BasePrimaryTemplate; - } - if (BaseRD) { - for (NamedDecl *ND : BaseRD->lookup(&II)) { - if (!isa(ND)) - return UnqualifiedTypeNameLookupResult::FoundNonType; - FoundTypeDecl = UnqualifiedTypeNameLookupResult::FoundType; - } - if (FoundTypeDecl == UnqualifiedTypeNameLookupResult::NotFound) { - switch (lookupUnqualifiedTypeNameInBase(S, II, NameLoc, BaseRD)) { - case UnqualifiedTypeNameLookupResult::FoundNonType: - return UnqualifiedTypeNameLookupResult::FoundNonType; - case UnqualifiedTypeNameLookupResult::FoundType: - FoundTypeDecl = UnqualifiedTypeNameLookupResult::FoundType; + IdNameLookupResult FoundDecl = IdNameLookupResult::NotFound; + for (NamedDecl *ND : RD->lookup(&II)) { + if (!CheckKind(ND)) + return IdNameLookupResult::FoundNotSpecifiedKind; + FoundDecl = IdNameLookupResult::FoundSpecifiedKind; + } + if (FoundDecl == IdNameLookupResult::NotFound) { + for (const auto &Base : RD->bases()) { + const CXXRecordDecl *BaseRD = nullptr; + auto BaseTy = S.getASTContext().getCanonicalType(Base.getType()); + if (const TagType *BaseTT = BaseTy->getAs()) + BaseRD = BaseTT->getAsCXXRecordDecl(); + else if (const TemplateSpecializationType *TST = + BaseTy->getAs()) { + // Look for type decls in dependent base classes that have known primary + // templates. + if (!TST || !TST->isDependentType()) + continue; + auto *TD = TST->getTemplateName().getAsTemplateDecl(); + if (!TD) + continue; + auto *BasePrimaryTemplate = + dyn_cast_or_null(TD->getTemplatedDecl()); + if (!BasePrimaryTemplate) + continue; + BaseRD = BasePrimaryTemplate; + } else if (SupposeExistInBaseParm && + BaseTy->getAs() && + FoundDecl == IdNameLookupResult::NotFound) { + FoundDecl = IdNameLookupResult::SupposeFoundSpecifiedKind; + } + if (BaseRD) { + switch (lookupIdNameInDependentClass( + S, II, NameLoc, BaseRD, SupposeExistInBaseParm, CheckKind)) { + case IdNameLookupResult::FoundNotSpecifiedKind: + return IdNameLookupResult::FoundNotSpecifiedKind; + case IdNameLookupResult::FoundSpecifiedKind: + FoundDecl = IdNameLookupResult::FoundSpecifiedKind; + break; + case IdNameLookupResult::SupposeFoundSpecifiedKind: + if (FoundDecl == IdNameLookupResult::NotFound) + FoundDecl = IdNameLookupResult::SupposeFoundSpecifiedKind; break; - case UnqualifiedTypeNameLookupResult::NotFound: + case IdNameLookupResult::NotFound: break; } } } } - return FoundTypeDecl; + return FoundDecl; } -static ParsedType recoverFromTypeInKnownDependentBase(Sema &S, - const IdentifierInfo &II, - SourceLocation NameLoc) { +static bool +isDeclInDependentClass(Sema &S, CXXScopeSpec &SS, SourceLocation Loc, + const IdentifierInfo &II, bool SupposeExistInBaseParm, + const std::function &CheckKind) { // Lookup in the parent class template context, if any. const CXXRecordDecl *RD = nullptr; - UnqualifiedTypeNameLookupResult FoundTypeDecl = - UnqualifiedTypeNameLookupResult::NotFound; - for (DeclContext *DC = S.CurContext; - DC && FoundTypeDecl == UnqualifiedTypeNameLookupResult::NotFound; + IdNameLookupResult FoundDecl = IdNameLookupResult::NotFound; + auto *DC = S.computeDeclContext(SS, /*EnteringContext=*/true); + if (!DC) + DC = S.CurContext; + for (; DC && FoundDecl == IdNameLookupResult::NotFound; DC = DC->getParent()) { // Look for type decls in dependent base classes that have known primary // templates. RD = dyn_cast(DC); if (RD && RD->getDescribedClassTemplate()) - FoundTypeDecl = lookupUnqualifiedTypeNameInBase(S, II, NameLoc, RD); + FoundDecl = lookupIdNameInDependentClass( + S, II, Loc, RD, SupposeExistInBaseParm, CheckKind); } - if (FoundTypeDecl != UnqualifiedTypeNameLookupResult::FoundType) + if (FoundDecl == IdNameLookupResult::FoundSpecifiedKind || + FoundDecl == IdNameLookupResult::SupposeFoundSpecifiedKind) { + if (SS.isEmpty()) { + auto *NNS = + NestedNameSpecifier::Create(S.getASTContext(), + /*Prefix=*/nullptr, + /*Template=*/false, RD->getTypeForDecl()); + SS.MakeTrivial(S.getASTContext(), NNS, SourceRange(Loc, Loc)); + } + return true; + } + + return false; +} + +bool Sema::isClassTemplateInClassWithAnyDependentBase( + CXXScopeSpec &SS, SourceLocation Loc, const IdentifierInfo &II) { + return isDeclInDependentClass( + *this, SS, Loc, II, /*SupposeExistInBaseParm=*/false, + [](NamedDecl *ND) -> bool { return isa(ND); }); +} + +bool Sema::isMemberInClassWithAnyDependentBase(CXXScopeSpec &SS, + SourceLocation Loc, + const IdentifierInfo &II, + bool NonInstanceMemberOnly, + bool SupposeExistInBaseParm) { + if (NonInstanceMemberOnly) + return isDeclInDependentClass( + *this, SS, Loc, II, SupposeExistInBaseParm, [](NamedDecl *ND) { + return ND->isCXXClassMember() && !ND->isCXXInstanceMember(); + }); + return isDeclInDependentClass( + *this, SS, Loc, II, SupposeExistInBaseParm, + [](NamedDecl *ND) { return ND->isCXXClassMember(); }); +} + +static ParsedType recoverFromTypeInKnownDependentBase(Sema &S, + CXXScopeSpec &SS, + const IdentifierInfo &II, + SourceLocation NameLoc) { + bool IsUnqualifiedLookup = SS.isEmpty(); + if (!isDeclInDependentClass( + S, SS, NameLoc, II, /*SupposeExistInBaseParm=*/false, + [](NamedDecl *ND) -> bool { return isa(ND); })) 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)); + if (IsUnqualifiedLookup) + S.Diag(NameLoc, diag::ext_found_via_dependent_base_lookup) + << &II << S.computeDeclContext(SS, /*EnteringContext=*/true); + QualType T = + Context.getDependentNameType(ETK_Typename, SS.getScopeRep(), &II); TypeLocBuilder Builder; DependentNameTypeLoc DepTL = Builder.push(T); @@ -265,8 +333,16 @@ // // We therefore do not perform any name lookup if the result would // refer to a member of an unknown specialization. - if (!isClassName && !IsCtorOrDtorName) + if (!isClassName && !IsCtorOrDtorName) { + // For qualified lookup in a class template in MSVC mode, look into + // dependent base classes where the primary class template is known. + if (getLangOpts().MSVCCompat) { + if (ParsedType TypeInBase = recoverFromTypeInKnownDependentBase( + *this, *SS, II, NameLoc)) + return TypeInBase; + } return ParsedType(); + } // We know from the grammar that this name refers to a type, // so build a dependent node to describe the type. @@ -312,13 +388,17 @@ // 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; - } + } + + // For unqualified lookup in a class template in MSVC mode, look into + // dependent base classes where the primary class template is known. + CXXScopeSpec NewSS; + if (SS && SS->isValid()) + NewSS = *SS; + if (Result.empty() && getLangOpts().MSVCCompat) { + if (ParsedType TypeInBase = + recoverFromTypeInKnownDependentBase(*this, NewSS, II, NameLoc)) + return TypeInBase; } NamedDecl *IIDecl = nullptr; @@ -745,9 +825,10 @@ // 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) { + CXXScopeSpec NewSS = SS; + if (Result.empty() && getLangOpts().MSVCCompat) { if (ParsedType TypeInBase = - recoverFromTypeInKnownDependentBase(*this, *Name, NameLoc)) + recoverFromTypeInKnownDependentBase(*this, NewSS, *Name, NameLoc)) return TypeInBase; } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -1950,51 +1950,51 @@ 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(S.CurContext)) - RD = MD->getParent(); +bool Sema::isInClassWithAnyDependentBase(SourceLocation Loc, + bool IsLastChanceToRecover, + CXXScopeSpec *SS, QualType ObjectType, + const IdentifierInfo *II) { + CXXRecordDecl *RD = nullptr; + if (!ObjectType.isNull()) { + // If ObjectType is defined - get cxx scope from this type. + RD = dyn_cast_or_null(computeDeclContext(ObjectType)); + } else if (!SS || SS->isEmpty()) { + // Only try to recover from lookup into dependent bases in static methods + // or contexts where 'this' is available. + QualType ThisType = getCurrentThisType(); + if (!ThisType.isNull()) + RD = ThisType->getPointeeType()->getAsCXXRecordDecl(); + else if (auto *MD = dyn_cast(CurContext)) + RD = MD->getParent(); + } else { + // Cxx scope is set - get class from this scope. + RD = dyn_cast_or_null( + computeDeclContext(*SS, /*EnteringContext=*/true)); + } 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(); - auto DB = S.Diag(Loc, diag::ext_undeclared_unqual_id_with_dependent_base); - DB << 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); + return false; + // Compute temp cxx scope to check that II member decl exists in this scope. + CXXScopeSpec TempSS; + if (!SS || SS->isEmpty()) { + auto *NNS = NestedNameSpecifier::Create( + Context, /*Prefix=*/nullptr, /*Template=*/false, RD->getTypeForDecl()); + TempSS.MakeTrivial(Context, NNS, SourceRange(Loc, Loc)); + } else { + TempSS = *SS; + } + if (isMemberInClassWithAnyDependentBase(TempSS, Loc, *II, + getCurrentThisType().isNull(), + IsLastChanceToRecover)) { + // Found specified member decl in dependent class. + if (ObjectType.isNull() && SS && SS->isEmpty()) { + *SS = TempSS; + if (II) + Diag(Loc, diag::ext_found_via_dependent_base_lookup) + << II << computeDeclContext(*SS, /*EnteringContext=*/true); + } + return true; + } + return false; } ExprResult @@ -2102,10 +2102,24 @@ bool ADL = UseArgumentDependentLookup(SS, R, HasTrailingLParen); if (R.empty() && !ADL) { - if (SS.isEmpty() && getLangOpts().MSVCCompat) { - if (Expr *E = recoverFromMSUnqualifiedLookup(*this, Context, NameInfo, - TemplateKWLoc, TemplateArgs)) - return E; + // 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. + if (SS.isEmpty() && getLangOpts().MSVCCompat && + isInClassWithAnyDependentBase(NameLoc, /*IsLastChanceToRecover=*/true, + &SS, QualType(), II)) { + if (getCurrentThisType().isNull()) + return DependentScopeDeclRefExpr::Create( + Context, SS.getWithLocInContext(Context), TemplateKWLoc, NameInfo, + TemplateArgs); + return CXXDependentScopeMemberExpr::Create( + Context, /*This=*/nullptr, getCurrentThisType(), + /*IsArrow=*/true, + /*Op=*/SourceLocation(), NestedNameSpecifierLoc(), TemplateKWLoc, + /*FirstQualifierInScope=*/nullptr, NameInfo, TemplateArgs); } // Don't diagnose an empty lookup for inline assembly. Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -168,7 +168,34 @@ LookupResult R(*this, TName, Name.getLocStart(), LookupOrdinaryName); LookupTemplateName(R, S, SS, ObjectType, EnteringContext, MemberOfUnknownSpecialization); - if (R.empty()) return TNK_Non_template; + if (R.empty()) { + // In MSVC mode resolve symbol in a class with dependent bases on + // instantiation. + if (getLangOpts().MSVCCompat && !hasTemplateKeyword && + Name.getKind() == UnqualifiedId::IK_Identifier) { + if (S && S->getFnParent() && + isInClassWithAnyDependentBase(Name.getLocStart(), + /*IsLastChanceToRecover=*/false, &SS, + ObjectType, Name.Identifier)) { + R.suppressDiagnostics(); + // Mark class template declaration as a TNK_Type_template for proper AST + // node generation if it used in expression in constructs like + // typecasts/constructor calls: + // if (Templ(a)) + // ^~~~~ + // typename + // use of undeclared identifier 'Templ' + if (isClassTemplateInClassWithAnyDependentBase(SS, Name.getLocStart(), + *Name.Identifier)) { + TemplateResult = TemplateTy::make(Context.getDependentTemplateName( + SS.getScopeRep(), Name.Identifier)); + return TNK_Type_template; + } + MemberOfUnknownSpecialization = true; + } + } + return TNK_Non_template; + } if (R.isAmbiguous()) { // Suppress diagnostics; we'll redo this lookup later. R.suppressDiagnostics(); Index: test/SemaCXX/MicrosoftCompatibility.cpp =================================================================== --- test/SemaCXX/MicrosoftCompatibility.cpp +++ test/SemaCXX/MicrosoftCompatibility.cpp @@ -166,14 +166,14 @@ typedef B Base2; typedef A Base3; - A::TYPE a1; // expected-warning {{missing 'typename' prior to dependent type name}} - Base1::TYPE a2; // expected-warning {{missing 'typename' prior to dependent type name}} + A::TYPE a1; + Base1::TYPE a2; - B::TYPE a3; // expected-warning {{missing 'typename' prior to dependent type name}} - Base2::TYPE a4; // expected-warning {{missing 'typename' prior to dependent type name}} + B::TYPE a3; + Base2::TYPE a4; - A::TYPE a5; // expected-error {{missing 'typename' prior to dependent type name}} - Base3::TYPE a6; // expected-error {{missing 'typename' prior to dependent type name}} + A::TYPE a5; + Base3::TYPE a6; }; class D { Index: test/SemaTemplate/ms-lookup-template-base-classes.cpp =================================================================== --- test/SemaTemplate/ms-lookup-template-base-classes.cpp +++ test/SemaTemplate/ms-lookup-template-base-classes.cpp @@ -64,7 +64,7 @@ class B : public A { public: void f() { - var = 3; // expected-warning {{use of undeclared identifier 'var'; unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}} + var = 3; // expected-warning {{use of identifier 'var' found via unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}} } }; @@ -160,7 +160,7 @@ class A : public T { public: void f(int hWnd) { - m_hWnd = 1; // expected-warning {{use of undeclared identifier 'm_hWnd'; unqualified lookup into dependent bases of class template 'A' is a Microsoft extension}} + m_hWnd = 1; // expected-warning {{use of identifier 'm_hWnd' found via unqualified lookup into dependent bases of class template 'A' is a Microsoft extension}} } }; @@ -281,7 +281,7 @@ 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}} + NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}} }; static_assert(sizeof(B) == 4, ""); } @@ -289,7 +289,7 @@ namespace struct_in_base { template struct A { struct NameFromBase {}; }; template struct B : A { - NameFromBase m; // expected-warning {{found via unqualified lookup into dependent bases}} + NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}} }; static_assert(sizeof(B) == 1, ""); } @@ -297,7 +297,7 @@ 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}} + NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}} }; static_assert(sizeof(B) == sizeof(A::NameFromBase), ""); } @@ -306,7 +306,7 @@ template struct A { typedef T NameFromBase; }; // expected-note {{member found by ambiguous name lookup}} template struct B { struct NameFromBase { T m; }; }; // expected-note {{member found by ambiguous name lookup}} template struct C : A, B { - NameFromBase m; // expected-error {{member 'NameFromBase' found in multiple base classes of different types}} expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} + NameFromBase m; // expected-error {{member 'NameFromBase' found in multiple base classes of different types}} expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'C' is a Microsoft extension}} }; static_assert(sizeof(C) == 4, ""); // expected-note {{in instantiation of template class 'two_types_in_base::C' requested here}} } @@ -322,7 +322,7 @@ 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}} + A m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}} }; } @@ -387,7 +387,7 @@ template struct B : A {}; template -struct C : B { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} +struct C : B { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'C' is a Microsoft extension}} } namespace type_in_second_dependent_base { @@ -396,7 +396,7 @@ template struct B { typedef T NameFromBase; }; template -struct D : A, B { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} +struct D : A, B { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'D' is a Microsoft extension}} } namespace type_in_second_non_dependent_base { @@ -405,7 +405,7 @@ template struct C : A, B {}; template -struct D : C { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} +struct D : C { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'D' is a Microsoft extension}} } namespace type_in_virtual_base_of_dependent_base { @@ -414,7 +414,7 @@ template struct B : virtual A {}; template -struct C : B, virtual A { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} +struct C : B, virtual A { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'C' is a Microsoft extension}} C c; } @@ -424,7 +424,7 @@ template struct B : public A {}; template -struct C : B, public A { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} expected-warning {{direct base 'A' is inaccessible due to ambiguity:}} +struct C : B, public A { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'C' is a Microsoft extension}} expected-warning {{direct base 'A' is inaccessible due to ambiguity:}} C c; // expected-note {{in instantiation of template class 'type_in_base_of_multiple_dependent_bases::C' requested here}} } @@ -434,14 +434,14 @@ struct C; template struct D : C { - NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} + NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}} }; struct E : C { - NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} + NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}} }; }; template struct B::C : B { - NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} + NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}} }; template struct F : B::C { NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}} @@ -508,14 +508,14 @@ struct A { typedef T NameFromBase; }; template struct B : A { - // expected-warning@+1 {{found via unqualified lookup into dependent bases}} + // expected-warning@+1 {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class template 'B' is a Microsoft extension}} 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}} + // expected-warning@+1 {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases}} int x = f(); }; } @@ -547,3 +547,119 @@ XXX x; // expected-error {{unknown type name}} }; } + +namespace complex_qualified_and_unqualified_lookup { +template struct Base { + T a; + // expected-note@+2 4 {{template is declared here}} + // expected-note@+1 2 {{must qualify identifier to find this declaration in dependent base class}} + template struct InnerBase {}; + typedef InnerBase TDInnerBase; + // expected-note@+1 {{must qualify identifier to find this declaration in dependent base class}} + template void f(G t) {} + // expected-note@+1 {{must qualify identifier to find this declaration in dependent base class}} + template static void fs(G t) {} +}; + +template struct Sub : Base { + // expected-note@+2 4 {{template is declared here}} + // expected-note@+1 2 {{must qualify identifier to find this declaration in dependent base class}} + template struct InnerSub {}; + typedef InnerSub TDInnerSub; + void g() {} +}; + +template struct SubSub : Sub { + // expected-warning@+1 {{use of identifier 'TDInnerBase' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + TDInnerBase b; + void g() { + typedef Base BaseTD; + BaseTD::TDInnerBase(); + typename Sub::template InnerBase(); + // expected-warning@+1 {{use of identifier 'a' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + a = T(); + // expected-warning@+1 {{use of identifier 'TDInnerBase' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + b = TDInnerBase(); + // expected-warning@+1 {{use of identifier 'f' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} + f(4); + // expected-warning@+2 {{use of identifier 'f' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + // expected-warning@+1 {{use 'template' keyword to treat 'f' as a dependent template name}} + f(4); + // expected-warning@+2 {{use of identifier 'f' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + // expected-warning@+1 {{use 'template' keyword to treat 'f' as a dependent template name}} + f(4); + this->f(4); + // expected-warning@+1 {{use 'template' keyword to treat 'f' as a dependent template name}} + this->f(4); + // expected-warning@+1 {{use 'template' keyword to treat 'f' as a dependent template name}} + this->f(4); + Sub::template f(4); + this->template f(4); + Sub::template f(4); + this->template f(4); + // expected-warning@+1 {{use of identifier 'InnerBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} + InnerBase(); // expected-error {{cannot refer to class template 'InnerBase' without a template argument list}} + // expected-warning@+1 {{use of identifier 'InnerBase' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + InnerBase(); + // expected-warning@+1 {{use of identifier 'TDInnerBase' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + TDInnerBase(); + // expected-warning@+1 {{use of identifier 'InnerSub' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} + InnerSub(); // expected-error {{cannot refer to class template 'InnerSub' without a template argument list}} + // expected-warning@+1 {{use of identifier 'InnerSub' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + InnerSub(); + // expected-warning@+1 {{use of identifier 'TDInnerSub' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + TDInnerSub(); + Sub::InnerBase(); + Sub::InnerBase(); // expected-error {{cannot refer to class template 'InnerBase' without a template argument list}} + Sub::TDInnerBase(); + Sub::InnerSub(); + Sub::InnerSub(); // expected-error {{cannot refer to class template 'InnerSub' without a template argument list}} + Sub::TDInnerSub(); + } + static void gs() { + typedef Base BaseTD; + BaseTD::TDInnerBase(); + // expected-warning@+1 {{use of identifier 'fs' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} + fs(4); + // expected-warning@+2 {{use of identifier 'fs' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + // expected-warning@+1 {{use 'template' keyword to treat 'fs' as a dependent template name}} + fs(4); + // expected-warning@+2 {{use of identifier 'fs' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + // expected-warning@+1 {{use 'template' keyword to treat 'fs' as a dependent template name}} + fs(4); + // expected-warning@+1 {{use of identifier 'InnerBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} + InnerBase(); // expected-error {{cannot refer to class template 'InnerBase' without a template argument list}} + // expected-warning@+1 {{use of identifier 'InnerBase' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + InnerBase(); + // expected-warning@+1 {{use of identifier 'TDInnerBase' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + TDInnerBase(); + // expected-warning@+1 {{use of identifier 'InnerSub' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + InnerSub(); + // expected-warning@+1 {{use of identifier 'InnerSub' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} + InnerSub(); // expected-error {{cannot refer to class template 'InnerSub' without a template argument list}} + // expected-warning@+1 {{use of identifier 'TDInnerSub' found via unqualified lookup into dependent bases of class template 'SubSub' is a Microsoft extension}} + TDInnerSub(); + Sub::fs(4); + // expected-warning@+1 {{use 'template' keyword to treat 'fs' as a dependent template name}} + Sub::fs(4); + // expected-warning@+1 {{use 'template' keyword to treat 'fs' as a dependent template name}} + Sub::fs(4); + Sub::InnerBase(); + Sub::InnerBase(); // expected-error {{cannot refer to class template 'InnerBase' without a template argument list}} + Sub::TDInnerBase(); + Sub::InnerSub(); + Sub::InnerSub(); // expected-error {{cannot refer to class template 'InnerSub' without a template argument list}} + Sub::TDInnerSub(); + } +}; + +void foo() { + Sub i; + SubSub ii; + i.g(); + // expected-note@+1 {{in instantiation of member function 'complex_qualified_and_unqualified_lookup::SubSub::g' requested here}} + ii.g(); + // expected-note@+1 {{in instantiation of member function 'complex_qualified_and_unqualified_lookup::SubSub::gs' requested here}} + SubSub::gs(); +} +} Index: test/SemaTemplate/typename-specifier.cpp =================================================================== --- test/SemaTemplate/typename-specifier.cpp +++ test/SemaTemplate/typename-specifier.cpp @@ -211,14 +211,19 @@ template void f(); template void f(); -// expected-error@+1 {{missing 'typename' prior to dependent type name 'S::type'}} +#ifndef MSVC +// expected-error@+2 {{missing 'typename' prior to dependent type name 'S::type'}} +#endif template void g() { f::type(int())>(); } // Adding typename does fix the diagnostic. template void h() { f::type(int())>(); } void j() { - g(); // expected-note-re {{in instantiation {{.*}} requested here}} +#ifndef MSVC + // expected-note-re@+2 {{in instantiation {{.*}} requested here}} +#endif + g(); h(); } } // namespace func_type_vs_construct_tmp