Access checking for implicit user-defined conversions.

llvm-svn: 94971
This commit is contained in:
John McCall 2010-02-01 03:16:54 +00:00
parent ed8ca56eeb
commit 760af170ff
6 changed files with 148 additions and 66 deletions

View File

@ -16,7 +16,6 @@
#define LLVM_CLANG_AST_UNRESOLVEDSET_H
#include <iterator>
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallVector.h"
#include "clang/Basic/Specifiers.h"
@ -24,12 +23,58 @@ namespace clang {
class NamedDecl;
/// A POD class for pairing a NamedDecl* with an access specifier.
/// Can be put into unions.
class DeclAccessPair {
NamedDecl *Ptr; // we'd use llvm::PointerUnion, but it isn't trivial
enum { Mask = 0x3 };
public:
static DeclAccessPair make(NamedDecl *D, AccessSpecifier AS) {
DeclAccessPair p;
p.set(D, AS);
return p;
}
NamedDecl *getDecl() const {
return (NamedDecl*) (~Mask & (uintptr_t) Ptr);
}
AccessSpecifier getAccess() const {
return AccessSpecifier(Mask & (uintptr_t) Ptr);
}
void setDecl(NamedDecl *D) {
set(D, getAccess());
}
void setAccess(AccessSpecifier AS) {
set(getDecl(), AS);
}
void set(NamedDecl *D, AccessSpecifier AS) {
Ptr = reinterpret_cast<NamedDecl*>(uintptr_t(AS) |
reinterpret_cast<uintptr_t>(D));
}
operator NamedDecl*() const { return getDecl(); }
NamedDecl *operator->() const { return getDecl(); }
};
}
// Take a moment to tell SmallVector that this is POD.
namespace llvm {
template<typename> struct isPodLike;
template<> struct isPodLike<clang::DeclAccessPair> {
static const bool value = true;
};
}
namespace clang {
/// The iterator over UnresolvedSets. Serves as both the const and
/// non-const iterator.
class UnresolvedSetIterator {
typedef llvm::PointerIntPair<NamedDecl*, 2> DeclEntry;
typedef llvm::SmallVectorImpl<DeclEntry> DeclsTy;
private:
typedef llvm::SmallVectorImpl<DeclAccessPair> DeclsTy;
typedef DeclsTy::iterator IteratorTy;
IteratorTy ir;
@ -47,8 +92,8 @@ public:
typedef NamedDecl *reference;
typedef std::iterator_traits<IteratorTy>::iterator_category iterator_category;
NamedDecl *getDecl() const { return ir->getPointer(); }
AccessSpecifier getAccess() const { return AccessSpecifier(ir->getInt()); }
NamedDecl *getDecl() const { return ir->getDecl(); }
AccessSpecifier getAccess() const { return ir->getAccess(); }
NamedDecl *operator*() const { return getDecl(); }
@ -87,7 +132,6 @@ public:
/// in a lot of places, but isn't really worth breaking into its own
/// header right now.
class UnresolvedSetImpl {
typedef UnresolvedSetIterator::DeclEntry DeclEntry;
typedef UnresolvedSetIterator::DeclsTy DeclsTy;
// Don't allow direct construction, and only permit subclassing by
@ -114,7 +158,7 @@ public:
}
void addDecl(NamedDecl *D, AccessSpecifier AS) {
decls().push_back(DeclEntry(D, AS));
decls().push_back(DeclAccessPair::make(D, AS));
}
/// Replaces the given declaration with the new one, once.
@ -122,19 +166,19 @@ public:
/// \return true if the set changed
bool replace(const NamedDecl* Old, NamedDecl *New) {
for (DeclsTy::iterator I = decls().begin(), E = decls().end(); I != E; ++I)
if (I->getPointer() == Old)
return (I->setPointer(New), true);
if (I->getDecl() == Old)
return (I->setDecl(New), true);
return false;
}
/// Replaces the declaration at the given iterator with the new one,
/// preserving the original access bits.
void replace(iterator I, NamedDecl *New) {
I.ir->setPointer(New);
I.ir->setDecl(New);
}
void replace(iterator I, NamedDecl *New, AccessSpecifier AS) {
*I.ir = DeclEntry(New, AS);
I.ir->set(New, AS);
}
void erase(unsigned I) {
@ -148,7 +192,7 @@ public:
}
void setAccess(iterator I, AccessSpecifier AS) {
I.ir->setInt(AS);
I.ir->setAccess(AS);
}
void clear() { decls().clear(); }
@ -161,41 +205,8 @@ public:
decls().append(I.ir, E.ir);
}
/// A proxy reference for implementing operator[].
class Proxy {
DeclEntry &Ref;
friend class UnresolvedSetImpl;
Proxy(DeclEntry &Ref) : Ref(Ref) {}
public:
NamedDecl *getDecl() const { return Ref.getPointer(); }
void setDecl(NamedDecl *D) { Ref.setPointer(D); }
AccessSpecifier getAccess() const { return AccessSpecifier(Ref.getInt()); }
void setAccess(AccessSpecifier AS) const { Ref.setInt(AS); }
NamedDecl* operator->() const { return getDecl(); }
operator NamedDecl*() const { return getDecl(); }
Proxy &operator=(const Proxy &D) { Ref = D.Ref; return *this; }
};
Proxy operator[](unsigned I) { return Proxy(decls()[I]); }
/// A proxy reference for implementing operator[] const.
class ConstProxy {
const DeclEntry &Ref;
friend class UnresolvedSetImpl;
ConstProxy(const DeclEntry &Ref) : Ref(Ref) {}
public:
NamedDecl *getDecl() const { return Ref.getPointer(); }
AccessSpecifier getAccess() const { return AccessSpecifier(Ref.getInt()); }
NamedDecl *operator->() const { return getDecl(); }
operator NamedDecl*() const { return getDecl(); }
};
ConstProxy operator[](unsigned I) const { return ConstProxy(decls()[I]); }
DeclAccessPair &operator[](unsigned I) { return decls()[I]; }
const DeclAccessPair &operator[](unsigned I) const { return decls()[I]; }
private:
// These work because the only permitted subclass is UnresolvedSetImpl
@ -211,7 +222,7 @@ private:
/// A set of unresolved declarations
template <unsigned InlineCapacity> class UnresolvedSet :
public UnresolvedSetImpl {
llvm::SmallVector<UnresolvedSetImpl::DeclEntry, InlineCapacity> Decls;
llvm::SmallVector<DeclAccessPair, InlineCapacity> Decls;
};

View File

@ -2415,6 +2415,8 @@ public:
bool CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
NamedDecl *D,
AccessSpecifier Access);
bool CheckConstructorAccess(SourceLocation Loc, CXXConstructorDecl *D,
AccessSpecifier Access);
bool CheckMemberOperatorAccess(SourceLocation Loc, Expr *ObjectExpr,
NamedDecl *D, AccessSpecifier Access);
bool CheckAccess(const LookupResult &R, NamedDecl *D, AccessSpecifier Access);

View File

@ -306,7 +306,24 @@ bool Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
return false;
}
/// Checks access to an overloaded member operator.
/// Checks access to a constructor.
bool Sema::CheckConstructorAccess(SourceLocation UseLoc,
CXXConstructorDecl *Constructor,
AccessSpecifier Access) {
if (!getLangOptions().AccessControl)
return false;
CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(Constructor->getParent());
LookupResult R(*this, Constructor->getDeclName(), UseLoc, LookupOrdinaryName);
R.suppressDiagnostics();
R.setNamingClass(NamingClass);
return CheckAccess(R, Constructor, Access);
}
/// Checks access to an overloaded member operator, including
/// conversion operators.
bool Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
Expr *ObjectExpr,
NamedDecl *MemberOperator,

View File

@ -1971,7 +1971,8 @@ void InitializationSequence::AddAddressOverloadResolutionStep(
Step S;
S.Kind = SK_ResolveAddressOfOverloadedFunction;
S.Type = Function->getType();
S.Function = Function;
// Access is currently ignored for these.
S.Function = DeclAccessPair::make(Function, AccessSpecifier(0));
Steps.push_back(S);
}
@ -1992,11 +1993,12 @@ void InitializationSequence::AddReferenceBindingStep(QualType T,
}
void InitializationSequence::AddUserConversionStep(FunctionDecl *Function,
AccessSpecifier Access,
QualType T) {
Step S;
S.Kind = SK_UserConversion;
S.Type = T;
S.Function = Function;
S.Function = DeclAccessPair::make(Function, Access);
Steps.push_back(S);
}
@ -2029,11 +2031,12 @@ void InitializationSequence::AddListInitializationStep(QualType T) {
void
InitializationSequence::AddConstructorInitializationStep(
CXXConstructorDecl *Constructor,
AccessSpecifier Access,
QualType T) {
Step S;
S.Kind = SK_ConstructorInitialization;
S.Type = T;
S.Function = Constructor;
S.Function = DeclAccessPair::make(Constructor, Access);
Steps.push_back(S);
}
@ -2246,7 +2249,8 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S,
T2 = cv1T1;
// Add the user-defined conversion step.
Sequence.AddUserConversionStep(Function, T2.getNonReferenceType());
Sequence.AddUserConversionStep(Function, Best->getAccess(),
T2.getNonReferenceType());
// Determine whether we need to perform derived-to-base or
// cv-qualification adjustments.
@ -2577,10 +2581,11 @@ static void TryConstructorInitialization(Sema &S,
// Add the constructor initialization step. Any cv-qualification conversion is
// subsumed by the initialization.
if (Kind.getKind() == InitializationKind::IK_Copy) {
Sequence.AddUserConversionStep(Best->Function, DestType);
Sequence.AddUserConversionStep(Best->Function, Best->getAccess(), DestType);
} else {
Sequence.AddConstructorInitializationStep(
cast<CXXConstructorDecl>(Best->Function),
Best->getAccess(),
DestType);
}
}
@ -2778,13 +2783,13 @@ static void TryUserDefinedConversion(Sema &S,
if (isa<CXXConstructorDecl>(Function)) {
// Add the user-defined conversion step. Any cv-qualification conversion is
// subsumed by the initialization.
Sequence.AddUserConversionStep(Function, DestType);
Sequence.AddUserConversionStep(Function, Best->getAccess(), DestType);
return;
}
// Add the user-defined conversion step that calls the conversion function.
QualType ConvType = Function->getResultType().getNonReferenceType();
Sequence.AddUserConversionStep(Function, ConvType);
Sequence.AddUserConversionStep(Function, Best->getAccess(), ConvType);
// If the conversion following the call to the conversion function is
// interesting, add it as a separate step.
@ -3250,7 +3255,9 @@ InitializationSequence::Perform(Sema &S,
case SK_ResolveAddressOfOverloadedFunction:
// Overload resolution determined which function invoke; update the
// initializer to reflect that choice.
CurInit = S.FixOverloadedFunctionReference(move(CurInit), Step->Function);
// Access control was done in overload resolution.
CurInit = S.FixOverloadedFunctionReference(move(CurInit),
cast<FunctionDecl>(Step->Function.getDecl()));
break;
case SK_CastDerivedToBaseRValue:
@ -3329,13 +3336,14 @@ InitializationSequence::Perform(Sema &S,
// or a conversion function.
CastExpr::CastKind CastKind = CastExpr::CK_Unknown;
bool IsCopy = false;
if (CXXConstructorDecl *Constructor
= dyn_cast<CXXConstructorDecl>(Step->Function)) {
FunctionDecl *Fn = cast<FunctionDecl>(Step->Function.getDecl());
AccessSpecifier FnAccess = Step->Function.getAccess();
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Fn)) {
// Build a call to the selected constructor.
ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(S);
SourceLocation Loc = CurInitExpr->getLocStart();
CurInit.release(); // Ownership transferred into MultiExprArg, below.
// Determine the arguments required to actually perform the constructor
// call.
if (S.CompleteConstructorCall(Constructor,
@ -3350,6 +3358,8 @@ InitializationSequence::Perform(Sema &S,
move_arg(ConstructorArgs));
if (CurInit.isInvalid())
return S.ExprError();
S.CheckConstructorAccess(Kind.getLocation(), Constructor, FnAccess);
CastKind = CastExpr::CK_ConstructorConversion;
QualType Class = S.Context.getTypeDeclType(Constructor->getParent());
@ -3358,8 +3368,11 @@ InitializationSequence::Perform(Sema &S,
IsCopy = true;
} else {
// Build a call to the conversion function.
CXXConversionDecl *Conversion = cast<CXXConversionDecl>(Step->Function);
CXXConversionDecl *Conversion = cast<CXXConversionDecl>(Fn);
S.CheckMemberOperatorAccess(Kind.getLocation(), CurInitExpr,
Conversion, FnAccess);
// FIXME: Should we move this initialization into a separate
// derived-to-base conversion? I believe the answer is "no", because
// we don't want to turn off access control here for c-style casts.
@ -3424,8 +3437,8 @@ InitializationSequence::Perform(Sema &S,
case SK_ConstructorInitialization: {
CXXConstructorDecl *Constructor
= cast<CXXConstructorDecl>(Step->Function);
= cast<CXXConstructorDecl>(Step->Function.getDecl());
// Build a call to the selected constructor.
ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(S);
SourceLocation Loc = Kind.getLocation();
@ -3444,6 +3457,9 @@ InitializationSequence::Perform(Sema &S,
Entity.getKind() == InitializedEntity::EK_Base);
if (CurInit.isInvalid())
return S.ExprError();
// Only check access if all of that succeeded.
S.CheckConstructorAccess(Loc, Constructor, Step->Function.getAccess());
bool Elidable
= cast<CXXConstructExpr>((Expr *)CurInit.get())->isElidable();

View File

@ -15,6 +15,7 @@
#include "SemaOverload.h"
#include "clang/AST/Type.h"
#include "clang/AST/UnresolvedSet.h"
#include "clang/Parse/Action.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/PointerIntPair.h"
@ -449,7 +450,11 @@ public:
/// \brief When Kind == SK_ResolvedOverloadedFunction or Kind ==
/// SK_UserConversion, the function that the expression should be
/// resolved to or the conversion function to call, respectively.
FunctionDecl *Function;
///
/// Always a FunctionDecl.
/// For conversion decls, the naming class is the source type.
/// For construct decls, the naming class is the target type.
DeclAccessPair Function;
/// \brief When Kind = SK_ConversionSequence, the implicit conversion
/// sequence
@ -616,7 +621,9 @@ public:
/// \brief Add a new step invoking a conversion function, which is either
/// a constructor or a conversion function.
void AddUserConversionStep(FunctionDecl *Function, QualType T);
void AddUserConversionStep(FunctionDecl *Function,
AccessSpecifier Access,
QualType T);
/// \brief Add a new step that performs a qualification conversion to the
/// given type.
@ -631,6 +638,7 @@ public:
/// \brief Add a constructor-initialization step.
void AddConstructorInitializationStep(CXXConstructorDecl *Constructor,
AccessSpecifier Access,
QualType T);
/// \brief Add a zero-initialization step.

View File

@ -15,6 +15,8 @@
// to create and destroy a static data member is performed as if
// these calls appeared in the scope of the member's class.
struct Public {}; struct Protected {}; struct Private {};
namespace test0 {
class A {
typedef int type; // expected-note {{declared private here}}
@ -24,3 +26,29 @@ namespace test0 {
A::type foo() { } // expected-error {{access to private member}}
A::type A::foo() { }
}
// conversion decls
namespace test1 {
class A {
public:
A();
operator Public ();
A(Public);
protected:
operator Protected (); // expected-note {{declared protected here}}
A(Protected); // expected-note {{declared protected here}}
private:
operator Private (); // expected-note {{declared private here}}
A(Private); // expected-note {{declared private here}}
};
void test() {
A a;
Public pub = a;
Protected prot = a; // expected-error {{access to protected member}}
Private priv = a; // expected-error {{access to private member}}
A apub = pub;
A aprot = prot; // expected-error {{access to protected member}}
A apriv = priv; // expected-error {{access to private member}}
}
}