diff options
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
-rw-r--r-- | include/clang/Sema/Lookup.h | 16 | ||||
-rw-r--r-- | lib/Sema/SemaAccess.cpp | 52 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 1 | ||||
-rw-r--r-- | test/SemaCXX/MicrosoftExtensions.cpp | 19 |
5 files changed, 88 insertions, 4 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 00cd81f7e3..e5354b6424 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -596,6 +596,10 @@ def err_class_redeclared_with_different_access : Error< "%0 redeclared with '%1' access">; def err_access : Error< "%1 is a %select{private|protected}0 member of %3">, AccessControl; +def war_ms_using_declaration_inaccessible : ExtWarn< + "using declaration refers to inaccessible member '%0', which refers " + "to accessible member '%1', accepted for Microsoft compatibility">, + AccessControl, InGroup<Microsoft>; def err_access_ctor : Error< "calling a %select{private|protected}0 constructor of class %2">, AccessControl; diff --git a/include/clang/Sema/Lookup.h b/include/clang/Sema/Lookup.h index 400a7cc919..dceed4efc9 100644 --- a/include/clang/Sema/Lookup.h +++ b/include/clang/Sema/Lookup.h @@ -282,6 +282,18 @@ public: return NamingClass != 0; } + /// \brief Set whether the name lookup is triggered by a + /// using declaration. + void setUsingDeclaration(bool U) { + UsingDeclaration = U; + } + + /// \brief Returns whether the name lookup is triggered by a + /// using declaration. + bool isUsingDeclaration() const { + return UsingDeclaration; + } + /// \brief Returns the 'naming class' for this lookup, i.e. the /// class which was looked into to find these results. /// @@ -603,6 +615,10 @@ private: bool HideTags; bool Diagnose; + + /// \brief True if the lookup is triggered by a using declaration. + /// Necessary to handle a MSVC bug. + bool UsingDeclaration; }; /// \brief Consumes visible declarations found when searching for diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index e92d19d13d..0eb9854562 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -146,8 +146,10 @@ struct AccessTarget : public AccessedEntity { MemberNonce _, CXXRecordDecl *NamingClass, DeclAccessPair FoundDecl, - QualType BaseObjectType) - : AccessedEntity(Context, Member, NamingClass, FoundDecl, BaseObjectType) { + QualType BaseObjectType, + bool IsUsingDecl = false) + : AccessedEntity(Context, Member, NamingClass, FoundDecl, BaseObjectType), + IsUsingDeclaration(IsUsingDecl) { initialize(); } @@ -216,6 +218,7 @@ private: DeclaringClass = DeclaringClass->getCanonicalDecl(); } + bool IsUsingDeclaration : 1; bool HasInstanceContext : 1; mutable bool CalculatedInstanceContext : 1; mutable const CXXRecordDecl *InstanceContext; @@ -1129,6 +1132,44 @@ static void DiagnoseBadAccess(Sema &S, SourceLocation Loc, DiagnoseAccessPath(S, EC, Entity); } +/// MSVC has a bug where if during an using declaration name lookup, +/// the declaration found is unaccessible (private) and that declaration +/// was bring into scope via another using declaration whose target +/// declaration is accessible (public) then no error is generated. +/// Example: +/// class A { +/// public: +/// int f(); +/// }; +/// class B : public A { +/// private: +/// using A::f; +/// }; +/// class C : public B { +/// private: +/// using B::f; +/// }; +/// +/// Here, B::f is private so this should fail in Standard C++, but +/// because B::f refers to A::f which is public MSVC accepts it. +static bool IsMicrosoftUsingDeclarationAccessBug(Sema& S, + SourceLocation AccessLoc, + AccessTarget &Entity) { + if (UsingShadowDecl *Shadow = + dyn_cast<UsingShadowDecl>(Entity.getTargetDecl())) { + const NamedDecl *OrigDecl = Entity.getTargetDecl()->getUnderlyingDecl(); + if (Entity.getTargetDecl()->getAccess() == AS_private && + (OrigDecl->getAccess() == AS_public || + OrigDecl->getAccess() == AS_protected)) { + S.Diag(AccessLoc, diag::war_ms_using_declaration_inaccessible) + << Shadow->getUsingDecl()->getQualifiedNameAsString() + << OrigDecl->getQualifiedNameAsString(); + return true; + } + } + return false; +} + /// Determines whether the accessed entity is accessible. Public members /// have been weeded out by this point. static AccessResult IsAccessible(Sema &S, @@ -1232,6 +1273,10 @@ static AccessResult CheckEffectiveAccess(Sema &S, AccessTarget &Entity) { assert(Entity.getAccess() != AS_public && "called for public access!"); + if (S.getLangOptions().Microsoft && + IsMicrosoftUsingDeclarationAccessBug(S, Loc, Entity)) + return AR_accessible; + switch (IsAccessible(S, EC, Entity)) { case AR_dependent: DelayDependentAccess(S, EC, Loc, Entity); @@ -1577,9 +1622,8 @@ void Sema::CheckLookupAccess(const LookupResult &R) { if (I.getAccess() != AS_public) { AccessTarget Entity(Context, AccessedEntity::Member, R.getNamingClass(), I.getPair(), - R.getBaseObjectType()); + R.getBaseObjectType(), R.isUsingDeclaration()); Entity.setDiag(diag::err_access); - CheckAccess(*this, R.getNameLoc(), Entity); } } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 4fc9bf1fdd..8bf7cb67b8 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -5345,6 +5345,7 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS, // Otherwise, look up the target name. LookupResult R(*this, NameInfo, LookupOrdinaryName); + R.setUsingDeclaration(true); // Unlike most lookups, we don't always want to hide tag // declarations: tag names are visible through the using declaration diff --git a/test/SemaCXX/MicrosoftExtensions.cpp b/test/SemaCXX/MicrosoftExtensions.cpp index 88e3922670..3391e7afb0 100644 --- a/test/SemaCXX/MicrosoftExtensions.cpp +++ b/test/SemaCXX/MicrosoftExtensions.cpp @@ -197,3 +197,22 @@ void pointer_to_integral_type_conv(char* ptr) { ch = (char)ptr; sh = (short)ptr; } + +namespace ms_using_declaration_bug { + +class A { +public: + int f(); +}; + +class B : public A { +private: + using A::f; +}; + +class C : public B { +private: + using B::f; // expected-warning {{using declaration refers to inaccessible member 'ms_using_declaration_bug::B::f', which refers to accessible member 'ms_using_declaration_bug::A::f', accepted for Microsoft compatibility}} +}; + +}
\ No newline at end of file |