diff --git a/clang/lib/Sema/Lookup.h b/clang/lib/Sema/Lookup.h index 096129958717..271bb5bcd4a7 100644 --- a/clang/lib/Sema/Lookup.h +++ b/clang/lib/Sema/Lookup.h @@ -424,6 +424,11 @@ public: Diagnose = false; } + /// Determines whether this lookup is suppressing diagnostics. + bool isSuppressingDiagnostics() const { + return Diagnose; + } + /// Sets a 'context' source range. void setContextRange(SourceRange SR) { NameContextRange = SR; diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 48b8d05d918f..396f144ddf2d 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -1103,10 +1103,12 @@ public: /// non-function. Ovl_NonFunction }; - OverloadKind CheckOverload(FunctionDecl *New, + OverloadKind CheckOverload(Scope *S, + FunctionDecl *New, const LookupResult &OldDecls, - NamedDecl *&OldDecl); - bool IsOverload(FunctionDecl *New, FunctionDecl *Old); + NamedDecl *&OldDecl, + bool IsForUsingDecl); + bool IsOverload(FunctionDecl *New, FunctionDecl *Old, bool IsForUsingDecl); bool TryImplicitConversion(InitializationSequence &Sequence, const InitializedEntity &Entity, @@ -1952,7 +1954,8 @@ public: OwningExprResult LookupMemberExpr(LookupResult &R, Expr *&Base, bool &IsArrow, SourceLocation OpLoc, CXXScopeSpec &SS, - DeclPtrTy ObjCImpDecl); + DeclPtrTy ObjCImpDecl, + bool HasTemplateArgs); bool CheckQualifiedMemberReference(Expr *BaseExpr, QualType BaseType, const CXXScopeSpec &SS, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 813fc6cd3c21..ddc5ef108a81 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2863,7 +2863,7 @@ static bool FindOverriddenMethod(const CXXBaseSpecifier *Specifier, // FIXME: Do we care about other names here too? if (Name.getNameKind() == DeclarationName::CXXDestructorName) { - // We really want to find the base class constructor here. + // We really want to find the base class destructor here. QualType T = Data->S->Context.getTypeDeclType(BaseRecord); CanQualType CT = Data->S->Context.getCanonicalType(T); @@ -2873,8 +2873,9 @@ static bool FindOverriddenMethod(const CXXBaseSpecifier *Specifier, for (Path.Decls = BaseRecord->lookup(Name); Path.Decls.first != Path.Decls.second; ++Path.Decls.first) { - if (CXXMethodDecl *MD = dyn_cast(*Path.Decls.first)) { - if (MD->isVirtual() && !Data->S->IsOverload(Data->Method, MD)) + NamedDecl *D = (*Path.Decls.first)->getUnderlyingDecl(); + if (CXXMethodDecl *MD = dyn_cast(D)) { + if (MD->isVirtual() && !Data->S->IsOverload(Data->Method, MD, false)) return true; } } @@ -3588,13 +3589,10 @@ void Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, } } - switch (CheckOverload(NewFD, Previous, OldDecl)) { + switch (CheckOverload(S, NewFD, Previous, OldDecl, + /*NewIsUsingDecl*/ false)) { case Ovl_Match: Redeclaration = true; - if (isa(OldDecl) && CurContext->isRecord()) { - HideUsingShadowDecl(S, cast(OldDecl)); - Redeclaration = false; - } break; case Ovl_NonFunction: diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 595a05b63393..43ddf8c06c9b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -3649,7 +3649,7 @@ bool Sema::CheckUsingShadowDecl(UsingDecl *Using, NamedDecl *Orig, FD = cast(Target); NamedDecl *OldDecl = 0; - switch (CheckOverload(FD, Previous, OldDecl)) { + switch (CheckOverload(0, FD, Previous, OldDecl, /*IsForUsingDecl*/ true)) { case Ovl_Overload: return false; @@ -3659,11 +3659,6 @@ bool Sema::CheckUsingShadowDecl(UsingDecl *Using, NamedDecl *Orig, // We found a decl with the exact signature. case Ovl_Match: - if (isa(OldDecl)) { - // Silently ignore the possible conflict. - return false; - } - // If we're in a record, we want to hide the target, so we // return true (without a diagnostic) to tell the caller not to // build a shadow decl. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index deb9e058c05e..ae56018b99ac 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -677,26 +677,6 @@ static void DecomposeUnqualifiedId(Sema &SemaRef, } } -/// Decompose the given template name into a list of lookup results. -/// -/// The unqualified ID must name a non-dependent template, which can -/// be more easily tested by checking whether DecomposeUnqualifiedId -/// found template arguments. -static void DecomposeTemplateName(LookupResult &R, const UnqualifiedId &Id) { - assert(Id.getKind() == UnqualifiedId::IK_TemplateId); - TemplateName TName = - Sema::TemplateTy::make(Id.TemplateId->Template).getAsVal(); - - if (TemplateDecl *TD = TName.getAsTemplateDecl()) - R.addDecl(TD); - else if (OverloadedTemplateStorage *OT = TName.getAsOverloadedTemplate()) - for (OverloadedTemplateStorage::iterator I = OT->begin(), E = OT->end(); - I != E; ++I) - R.addDecl(*I); - - R.resolveKind(); -} - /// Determines whether the given record is "fully-formed" at the given /// location, i.e. whether a qualified lookup into it is assured of /// getting consistent results already. @@ -2580,13 +2560,23 @@ bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr, static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, SourceRange BaseRange, const RecordType *RTy, - SourceLocation OpLoc, CXXScopeSpec &SS) { + SourceLocation OpLoc, CXXScopeSpec &SS, + bool HasTemplateArgs) { RecordDecl *RDecl = RTy->getDecl(); if (SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0), SemaRef.PDiag(diag::err_typecheck_incomplete_tag) << BaseRange)) return true; + if (HasTemplateArgs) { + // LookupTemplateName doesn't expect these both to exist simultaneously. + QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0); + + bool MOUS; + SemaRef.LookupTemplateName(R, 0, SS, ObjectType, false, MOUS); + return false; + } + DeclContext *DC = RDecl; if (SS.isSet()) { // If the member name was a qualified-id, look into the @@ -2660,14 +2650,14 @@ Sema::BuildMemberReferenceExpr(ExprArg BaseArg, QualType BaseType, if (IsArrow) RecordTy = RecordTy->getAs()->getPointeeType(); if (LookupMemberExprInRecord(*this, R, SourceRange(), RecordTy->getAs(), - OpLoc, SS)) + OpLoc, SS, TemplateArgs != 0)) return ExprError(); // Explicit member accesses. } else { OwningExprResult Result = LookupMemberExpr(R, Base, IsArrow, OpLoc, - SS, /*ObjCImpDecl*/ DeclPtrTy()); + SS, /*ObjCImpDecl*/ DeclPtrTy(), TemplateArgs != 0); if (Result.isInvalid()) { Owned(Base); @@ -2880,7 +2870,7 @@ Sema::OwningExprResult Sema::LookupMemberExpr(LookupResult &R, Expr *&BaseExpr, bool &IsArrow, SourceLocation OpLoc, CXXScopeSpec &SS, - DeclPtrTy ObjCImpDecl) { + DeclPtrTy ObjCImpDecl, bool HasTemplateArgs) { assert(BaseExpr && "no base expression"); // Perform default conversions. @@ -3057,7 +3047,7 @@ Sema::LookupMemberExpr(LookupResult &R, Expr *&BaseExpr, // Handle field access to simple records. if (const RecordType *RTy = BaseType->getAs()) { if (LookupMemberExprInRecord(*this, R, BaseExpr->getSourceRange(), - RTy, OpLoc, SS)) + RTy, OpLoc, SS, HasTemplateArgs)) return ExprError(); return Owned((Expr*) 0); } @@ -3259,44 +3249,24 @@ Sema::OwningExprResult Sema::ActOnMemberAccessExpr(Scope *S, ExprArg BaseArg, TemplateArgs); } else { LookupResult R(*this, Name, NameLoc, LookupMemberName); - if (TemplateArgs) { - // Re-use the lookup done for the template name. - DecomposeTemplateName(R, Id); + Result = LookupMemberExpr(R, Base, IsArrow, OpLoc, + SS, ObjCImpDecl, TemplateArgs != 0); - // Re-derive the naming class. - if (SS.isSet()) { - NestedNameSpecifier *Qualifier - = static_cast(SS.getScopeRep()); - if (const Type *Ty = Qualifier->getAsType()) - if (CXXRecordDecl *NamingClass = Ty->getAsCXXRecordDecl()) - R.setNamingClass(NamingClass); - } else { - QualType BaseType = Base->getType(); - if (const PointerType *Ptr = BaseType->getAs()) - BaseType = Ptr->getPointeeType(); - if (CXXRecordDecl *NamingClass = BaseType->getAsCXXRecordDecl()) - R.setNamingClass(NamingClass); - } - } else { - Result = LookupMemberExpr(R, Base, IsArrow, OpLoc, - SS, ObjCImpDecl); + if (Result.isInvalid()) { + Owned(Base); + return ExprError(); + } - if (Result.isInvalid()) { - Owned(Base); - return ExprError(); - } + if (Result.get()) { + // The only way a reference to a destructor can be used is to + // immediately call it, which falls into this case. If the + // next token is not a '(', produce a diagnostic and build the + // call now. + if (!HasTrailingLParen && + Id.getKind() == UnqualifiedId::IK_DestructorName) + return DiagnoseDtorReference(NameLoc, move(Result)); - if (Result.get()) { - // The only way a reference to a destructor can be used is to - // immediately call it, which falls into this case. If the - // next token is not a '(', produce a diagnostic and build the - // call now. - if (!HasTrailingLParen && - Id.getKind() == UnqualifiedId::IK_DestructorName) - return DiagnoseDtorReference(NameLoc, move(Result)); - - return move(Result); - } + return move(Result); } Result = BuildMemberReferenceExpr(ExprArg(*this, Base), Base->getType(), diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 24fdff375721..4baa307890e9 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -500,19 +500,54 @@ void OverloadCandidateSet::clear() { // identical (return types of functions are not part of the // signature), IsOverload returns false and MatchedDecl will be set to // point to the FunctionDecl for #2. +// +// 'NewIsUsingShadowDecl' indicates that 'New' is being introduced +// into a class by a using declaration. The rules for whether to hide +// shadow declarations ignore some properties which otherwise figure +// into a function template's signature. Sema::OverloadKind -Sema::CheckOverload(FunctionDecl *New, const LookupResult &Old, - NamedDecl *&Match) { +Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old, + NamedDecl *&Match, bool NewIsUsingDecl) { for (LookupResult::iterator I = Old.begin(), E = Old.end(); I != E; ++I) { - NamedDecl *OldD = (*I)->getUnderlyingDecl(); + NamedDecl *OldD = *I; + + bool OldIsUsingDecl = false; + if (isa(OldD)) { + OldIsUsingDecl = true; + + // We can always introduce two using declarations into the same + // context, even if they have identical signatures. + if (NewIsUsingDecl) continue; + + OldD = cast(OldD)->getTargetDecl(); + } + + // If either declaration was introduced by a using declaration, + // we'll need to use slightly different rules for matching. + // Essentially, these rules are the normal rules, except that + // function templates hide function templates with different + // return types or template parameter lists. + bool UseMemberUsingDeclRules = + (OldIsUsingDecl || NewIsUsingDecl) && CurContext->isRecord(); + if (FunctionTemplateDecl *OldT = dyn_cast(OldD)) { - if (!IsOverload(New, OldT->getTemplatedDecl())) { + if (!IsOverload(New, OldT->getTemplatedDecl(), UseMemberUsingDeclRules)) { + if (UseMemberUsingDeclRules && OldIsUsingDecl) { + HideUsingShadowDecl(S, cast(*I)); + continue; + } + Match = *I; return Ovl_Match; } } else if (FunctionDecl *OldF = dyn_cast(OldD)) { - if (!IsOverload(New, OldF)) { + if (!IsOverload(New, OldF, UseMemberUsingDeclRules)) { + if (UseMemberUsingDeclRules && OldIsUsingDecl) { + HideUsingShadowDecl(S, cast(*I)); + continue; + } + Match = *I; return Ovl_Match; } @@ -536,7 +571,8 @@ Sema::CheckOverload(FunctionDecl *New, const LookupResult &Old, return Ovl_Overload; } -bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old) { +bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, + bool UseUsingDeclRules) { FunctionTemplateDecl *OldTemplate = Old->getDescribedFunctionTemplate(); FunctionTemplateDecl *NewTemplate = New->getDescribedFunctionTemplate(); @@ -581,7 +617,10 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old) { // // We check the return type and template parameter lists for function // templates first; the remaining checks follow. - if (NewTemplate && + // + // However, we don't consider either of these when deciding whether + // a member introduced by a shadow declaration is hidden. + if (!UseUsingDeclRules && NewTemplate && (!TemplateParameterListsAreEqual(NewTemplate->getTemplateParameters(), OldTemplate->getTemplateParameters(), false, TPL_TemplateMatch) || diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index a2d4de5b1f79..f5f4853fb0f3 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -27,12 +27,12 @@ using namespace clang; /// \brief Determine whether the declaration found is acceptable as the name /// of a template and, if so, return that template declaration. Otherwise, /// returns NULL. -static NamedDecl *isAcceptableTemplateName(ASTContext &Context, NamedDecl *D) { - if (!D) - return 0; +static NamedDecl *isAcceptableTemplateName(ASTContext &Context, + NamedDecl *Orig) { + NamedDecl *D = Orig->getUnderlyingDecl(); if (isa(D)) - return D; + return Orig; if (CXXRecordDecl *Record = dyn_cast(D)) { // C++ [temp.local]p1: @@ -68,7 +68,7 @@ static void FilterAcceptableTemplateNames(ASTContext &C, LookupResult &R) { LookupResult::Filter filter = R.makeFilter(); while (filter.hasNext()) { NamedDecl *Orig = filter.next(); - NamedDecl *Repl = isAcceptableTemplateName(C, Orig->getUnderlyingDecl()); + NamedDecl *Repl = isAcceptableTemplateName(C, Orig); if (!Repl) filter.erase(); else if (Repl != Orig) { @@ -260,7 +260,7 @@ void Sema::LookupTemplateName(LookupResult &Found, if (DeclarationName Corrected = CorrectTypo(Found, S, &SS, LookupCtx, false, CTC_CXXCasts)) { FilterAcceptableTemplateNames(Context, Found); - if (!Found.empty() && isa(*Found.begin())) { + if (!Found.empty()) { if (LookupCtx) Diag(Found.getNameLoc(), diag::err_no_member_template_suggest) << Name << LookupCtx << Found.getLookupName() << SS.getRange() @@ -274,8 +274,7 @@ void Sema::LookupTemplateName(LookupResult &Found, if (TemplateDecl *Template = Found.getAsSingle()) Diag(Template->getLocation(), diag::note_previous_decl) << Template->getDeclName(); - } else - Found.clear(); + } } else { Found.clear(); } @@ -303,7 +302,7 @@ void Sema::LookupTemplateName(LookupResult &Found, // - if the name is found in the context of the entire // postfix-expression and does not name a class template, the name // found in the class of the object expression is used, otherwise - } else { + } else if (!Found.isSuppressingDiagnostics()) { // - if the name found is a class template, it must refer to the same // entity as the one found in the class of the object expression, // otherwise the program is ill-formed. diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 100ddcb141ca..df01be01f114 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1826,7 +1826,8 @@ public: Sema::LookupMemberName); OwningExprResult Result = getSema().LookupMemberExpr(R, Base, IsArrow, /*FIME:*/IvarLoc, - SS, DeclPtrTy()); + SS, DeclPtrTy(), + false); if (Result.isInvalid()) return getSema().ExprError(); @@ -1855,7 +1856,8 @@ public: bool IsArrow = false; OwningExprResult Result = getSema().LookupMemberExpr(R, Base, IsArrow, /*FIME:*/PropertyLoc, - SS, DeclPtrTy()); + SS, DeclPtrTy(), + false); if (Result.isInvalid()) return getSema().ExprError(); @@ -1903,7 +1905,8 @@ public: Sema::LookupMemberName); OwningExprResult Result = getSema().LookupMemberExpr(R, Base, IsArrow, /*FIME:*/IsaLoc, - SS, DeclPtrTy()); + SS, DeclPtrTy(), + false); if (Result.isInvalid()) return getSema().ExprError(); diff --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp index 25371c7029b9..cc28bf6c28c4 100644 --- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp +++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp @@ -111,34 +111,53 @@ namespace test3 { struct Derived1 : Base { using Base::foo; - template Opaque<2> foo() { return Opaque<2>(); } + template Opaque<2> foo() { return Opaque<2>(); } // expected-note {{invalid explicitly-specified argument for template parameter 'n'}} }; struct Derived2 : Base { - template Opaque<2> foo() { return Opaque<2>(); } + template Opaque<2> foo() { return Opaque<2>(); } // expected-note {{invalid explicitly-specified argument for template parameter 'n'}} using Base::foo; }; struct Derived3 : Base { using Base::foo; - template Opaque<3> foo() { return Opaque<3>(); } + template Opaque<3> foo() { return Opaque<3>(); } // expected-note {{invalid explicitly-specified argument for template parameter 'T'}} }; struct Derived4 : Base { - template Opaque<3> foo() { return Opaque<3>(); } + template Opaque<3> foo() { return Opaque<3>(); } // expected-note {{invalid explicitly-specified argument for template parameter 'T'}} using Base::foo; }; void test() { expect<0>(Base().foo()); expect<1>(Base().foo<0>()); - expect<0>(Derived1().foo()); + expect<0>(Derived1().foo()); // expected-error {{no matching member function for call to 'foo'}} expect<2>(Derived1().foo<0>()); - expect<0>(Derived2().foo()); + expect<0>(Derived2().foo()); // expected-error {{no matching member function for call to 'foo'}} expect<2>(Derived2().foo<0>()); expect<3>(Derived3().foo()); - expect<1>(Derived3().foo<0>()); + expect<1>(Derived3().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} expect<3>(Derived4().foo()); - expect<1>(Derived4().foo<0>()); + expect<1>(Derived4().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} + } +} + +// PR7384: access control for member templates. +namespace test4 { + class Base { + protected: + template void foo(T); + template void bar(T); // expected-note {{declared protected here}} + }; + + struct Derived : Base { + using Base::foo; + }; + + void test() { + Derived d; + d.foo(3); + d.bar(3); // expected-error {{'bar' is a protected member}} } } diff --git a/clang/test/SemaCXX/member-expr.cpp b/clang/test/SemaCXX/member-expr.cpp index 54a95936bed1..e83fdbf08705 100644 --- a/clang/test/SemaCXX/member-expr.cpp +++ b/clang/test/SemaCXX/member-expr.cpp @@ -72,3 +72,21 @@ namespace test4 { y.f(17); } } + +namespace test5 { + struct A { + template void foo(); + }; + + void test0(int x) { + x.A::foo(); // expected-error {{'int' is not a structure or union}} + } + + void test1(A *x) { + x.A::foo(); // expected-error {{'test5::A *' is a pointer}} + } + + void test2(A &x) { + x->A::foo(); // expected-error {{'test5::A' is not a pointer}} + } +}