From 733d77f1b4110d6afa16e40a2c4965d635116189 Mon Sep 17 00:00:00 2001 From: Anders Carlsson Date: Fri, 27 Mar 2009 06:03:27 +0000 Subject: [PATCH] Implement checking for base class access. Right now it's overly conservative but that will change. (Also, protected isn't implemented right now). llvm-svn: 67827 --- .../clang/Basic/DiagnosticSemaKinds.td | 7 ++ clang/lib/Sema/SemaAccess.cpp | 65 +++++++++++++++++++ clang/test/SemaCXX/access-base-class.cpp | 51 +++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 clang/test/SemaCXX/access-base-class.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 05b8f512e182..c684f4ef52db 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1297,6 +1297,13 @@ def err_memptr_conv_via_virtual : Error< "conversion from pointer to member of class %0 to pointer to member " "of class %1 via virtual base %2 is not allowed">; +// C++ access control +def err_conv_to_inaccessible_base : Error< + "conversion from %0 to inaccessible base class %1">; +def note_inheritance_specifier_here : Note< + "'%0' inheritance specifier here">; +def note_inheritance_implicitly_private_here : Note< + "inheritance is implicitly 'private'">; // C++ member name lookup def err_ambiguous_member_multiple_subobjects : Error< diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index e44ef5f9ea48..85e1c2ed883b 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -11,7 +11,9 @@ // //===----------------------------------------------------------------------===// +#include "SemaInherit.h" #include "Sema.h" +#include "clang/AST/ASTContext.h" using namespace clang; /// SetMemberAccessSpecifier - Set the access specifier of a member. @@ -45,5 +47,68 @@ bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl, /// and report an error if it can't. [class.access.base] bool Sema::CheckBaseClassAccess(QualType Derived, QualType Base, BasePaths& Paths, SourceLocation AccessLoc) { + Base = Context.getCanonicalType(Base).getUnqualifiedType(); + assert(!Paths.isAmbiguous(Base) && + "Can't check base class access if set of paths is ambiguous"); + assert(Paths.isRecordingPaths() && + "Can't check base class access without recorded paths"); + + const CXXBaseSpecifier *InacessibleBase = 0; + + for (BasePaths::paths_iterator Path = Paths.begin(), PathsEnd = Paths.end(); + Path != PathsEnd; ++Path) { + + bool FoundInaccessibleBase = false; + + for (BasePath::const_iterator Element = Path->begin(), + ElementEnd = Path->end(); Element != ElementEnd; ++Element) { + const CXXBaseSpecifier *Base = Element->Base; + + switch (Base->getAccessSpecifier()) { + default: + assert(0 && "invalid access specifier"); + case AS_public: + // Nothing to do. + break; + case AS_private: + // FIXME: Check if the current function is a member or friend. + FoundInaccessibleBase = true; + break; + case AS_protected: + // FIXME: Implement + break; + } + + if (FoundInaccessibleBase) { + InacessibleBase = Base; + break; + } + } + + if (!FoundInaccessibleBase) { + // We found a path to the base, our work here is done. + InacessibleBase = 0; + break; + } + } + + if (InacessibleBase) { + Diag(AccessLoc, diag::err_conv_to_inaccessible_base) + << Derived << Base; + + AccessSpecifier AS = InacessibleBase->getAccessSpecifierAsWritten(); + + // If there's no written access specifier, then the inheritance specifier + // is implicitly private. + if (AS == AS_none) + Diag(InacessibleBase->getSourceRange().getBegin(), + diag::note_inheritance_implicitly_private_here); + else + Diag(InacessibleBase->getSourceRange().getBegin(), + diag::note_inheritance_specifier_here) << AS; + + return true; + } + return false; } diff --git a/clang/test/SemaCXX/access-base-class.cpp b/clang/test/SemaCXX/access-base-class.cpp new file mode 100644 index 000000000000..29586759b04e --- /dev/null +++ b/clang/test/SemaCXX/access-base-class.cpp @@ -0,0 +1,51 @@ +// RUN: clang-cc -fsyntax-only -verify %s +namespace T1 { + +class A { }; +class B : private A { }; // expected-note {{'private' inheritance specifier here}} + +void f(B* b) { + A *a = b; // expected-error{{conversion from 'class T1::B' to inaccessible base class 'class T1::A'}} \ + expected-error{{incompatible type initializing 'class T1::B *', expected 'class T1::A *'}} +} + +} + +namespace T2 { + +class A { }; +class B : A { }; // expected-note {{inheritance is implicitly 'private'}} + +void f(B* b) { + A *a = b; // expected-error {{conversion from 'class T2::B' to inaccessible base class 'class T2::A'}} \ + expected-error {{incompatible type initializing 'class T2::B *', expected 'class T2::A *'}} +} + +} + +namespace T3 { + +class A { }; +class B : public A { }; + +void f(B* b) { + A *a = b; +} + +} + +namespace T4 { + +class A {}; + +class B : private virtual A {}; +class C : public virtual A {}; + +class D : public B, public C {}; + +void f(D *d) { + // This takes the D->C->B->A path. + A *a = d; +} + +}