Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -10507,7 +10507,8 @@ const CXXScopeSpec &SS, LookupResult &R, OverloadCandidateSet::CandidateSetKind CSK, TemplateArgumentListInfo *ExplicitTemplateArgs, - ArrayRef Args) { + ArrayRef Args, + bool *DoDiagnoseEmptyLookup = nullptr) { if (SemaRef.ActiveTemplateInstantiations.empty() || !SS.isEmpty()) return false; @@ -10524,6 +10525,8 @@ // Don't diagnose names we find in classes; we get much better // diagnostics for these from DiagnoseEmptyLookup. R.clear(); + if (DoDiagnoseEmptyLookup) + *DoDiagnoseEmptyLookup = true; return false; } @@ -10673,15 +10676,16 @@ LookupResult R(SemaRef, ULE->getName(), ULE->getNameLoc(), Sema::LookupOrdinaryName); + bool DoDiagnoseEmptyLookup = EmptyLookup; if (!DiagnoseTwoPhaseLookup(SemaRef, Fn->getExprLoc(), SS, R, OverloadCandidateSet::CSK_Normal, - ExplicitTemplateArgs, Args) && - (!EmptyLookup || - SemaRef.DiagnoseEmptyLookup( - S, SS, R, - MakeValidator(SemaRef, dyn_cast(Fn), Args.size(), - ExplicitTemplateArgs != nullptr, AllowTypoCorrection), - ExplicitTemplateArgs, Args))) + ExplicitTemplateArgs, Args, + &DoDiagnoseEmptyLookup) && + (!DoDiagnoseEmptyLookup || SemaRef.DiagnoseEmptyLookup( + S, SS, R, + MakeValidator(SemaRef, dyn_cast(Fn), Args.size(), + ExplicitTemplateArgs != nullptr, AllowTypoCorrection), + ExplicitTemplateArgs, Args))) return ExprError(); assert(!R.empty() && "lookup results empty despite recovery"); @@ -10746,26 +10750,28 @@ // functions, including those from argument-dependent lookup. AddOverloadedCallCandidates(ULE, Args, *CandidateSet); - // If we found nothing, try to recover. - // BuildRecoveryCallExpr diagnoses the error itself, so we just bail - // out if it fails. - if (CandidateSet->empty()) { - // In Microsoft mode, if we are inside a template class member function then - // create a type dependent CallExpr. The goal is to postpone name lookup - // to instantiation time to be able to search into type dependent base - // classes. - if (getLangOpts().MSVCCompat && CurContext->isDependentContext() && - (isa(CurContext) || isa(CurContext))) { - CallExpr *CE = new (Context) CallExpr(Context, Fn, Args, - Context.DependentTy, VK_RValue, - RParenLoc); + if (getLangOpts().MSVCCompat && CurContext->isDependentContext() && + (isa(CurContext) || isa(CurContext))) { + + OverloadCandidateSet::iterator Best; + if (CandidateSet->empty() || + CandidateSet->BestViableFunction(*this, Fn->getLocStart(), Best) == + OR_No_Viable_Function) { + // In Microsoft mode, if we are inside a template class member function then + // create a type dependent CallExpr. The goal is to postpone name lookup + // to instantiation time to be able to search into type dependent base + // classes. + CallExpr *CE = new (Context) CallExpr( + Context, Fn, Args, Context.DependentTy, VK_RValue, RParenLoc); CE->setTypeDependent(true); *Result = CE; return true; } - return false; } + if (CandidateSet->empty()) + return false; + UnbridgedCasts.restore(); return false; } 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 @@ -547,3 +547,19 @@ XXX x; // expected-error {{unknown type name}} }; } + +namespace PR23810 { +void f(int); +struct Base { + void f(); // expected-note{{must qualify identifier to find this declaration in dependent base class}} +}; +template struct Template : T { + void member() { + f(); // expected-warning {{found via unqualified lookup into dependent bases}} + } +}; +void test() { + Template x; + x.member(); // expected-note{{requested here}} +}; +}