From d60e22e601852ae1345f01514318a0951dc09f89 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 12 Mar 2010 01:19:31 +0000 Subject: [PATCH] Implement basic support for friend types and functions in non-dependent contexts. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@98321 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 13 ++++ include/clang/AST/DeclFriend.h | 72 +++++++++++++++++++++++ lib/AST/DeclCXX.cpp | 2 +- lib/AST/DeclFriend.cpp | 4 +- lib/Sema/SemaAccess.cpp | 29 ++++++++- test/CXX/class.access/class.friend/p1.cpp | 51 +++++++++++++++- 6 files changed, 166 insertions(+), 5 deletions(-) diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 547747cdb7..5abc1224d7 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -33,6 +33,7 @@ class CXXDestructorDecl; class CXXMethodDecl; class CXXRecordDecl; class CXXMemberLookupCriteria; +class FriendDecl; /// \brief Represents any kind of function declaration, whether it is a /// concrete function or a function template. @@ -299,6 +300,11 @@ class CXXRecordDecl : public RecordDecl { /// Definition - The declaration which defines this record. CXXRecordDecl *Definition; + /// FirstFriend - The first friend declaration in this class, or + /// null if there aren't any. This is actually currently stored + /// in reverse order. + FriendDecl *FirstFriend; + } *DefinitionData; struct DefinitionData &data() { @@ -459,6 +465,13 @@ public: return ctor_iterator(decls_end()); } + /// An iterator over friend declarations. All of these are defined + /// in DeclFriend.h. + class friend_iterator; + friend_iterator friend_begin() const; + friend_iterator friend_end() const; + void pushFriendDecl(FriendDecl *FD); + /// hasConstCopyConstructor - Determines whether this class has a /// copy constructor that accepts a const-qualified argument. bool hasConstCopyConstructor(ASTContext &Context) const; diff --git a/include/clang/AST/DeclFriend.h b/include/clang/AST/DeclFriend.h index 5b3f41db68..5022ad018f 100644 --- a/include/clang/AST/DeclFriend.h +++ b/include/clang/AST/DeclFriend.h @@ -42,6 +42,9 @@ private: // The declaration that's a friend of this class. FriendUnion Friend; + // A pointer to the next friend in the sequence. + FriendDecl *NextFriend; + // Location of the 'friend' specifier. SourceLocation FriendLoc; @@ -49,10 +52,14 @@ private: // template specialization. bool WasSpecialization; + friend class CXXRecordDecl::friend_iterator; + friend class CXXRecordDecl; + FriendDecl(DeclContext *DC, SourceLocation L, FriendUnion Friend, SourceLocation FriendL) : Decl(Decl::Friend, DC, L), Friend(Friend), + NextFriend(0), FriendLoc(FriendL), WasSpecialization(false) { } @@ -89,6 +96,71 @@ public: static bool classof(const FriendDecl *D) { return true; } static bool classofKind(Kind K) { return K == Decl::Friend; } }; + +/// An iterator over the friend declarations of a class. +class CXXRecordDecl::friend_iterator { + FriendDecl *Ptr; + + friend class CXXRecordDecl; + explicit friend_iterator(FriendDecl *Ptr) : Ptr(Ptr) {} +public: + friend_iterator() {} + + typedef FriendDecl *value_type; + typedef FriendDecl *reference; + typedef FriendDecl *pointer; + typedef int difference_type; + typedef std::forward_iterator_tag iterator_category; + + reference operator*() const { return Ptr; } + + friend_iterator &operator++() { + assert(Ptr && "attempt to increment past end of friend list"); + Ptr = Ptr->NextFriend; + return *this; + } + + friend_iterator operator++(int) { + friend_iterator tmp = *this; + ++*this; + return tmp; + } + + bool operator==(const friend_iterator &Other) const { + return Ptr == Other.Ptr; + } + + bool operator!=(const friend_iterator &Other) const { + return Ptr != Other.Ptr; + } + + friend_iterator &operator+=(difference_type N) { + assert(N >= 0 && "cannot rewind a CXXRecordDecl::friend_iterator"); + while (N--) + ++*this; + return *this; + } + + friend_iterator operator+(difference_type N) const { + friend_iterator tmp = *this; + tmp += N; + return tmp; + } +}; + +inline CXXRecordDecl::friend_iterator CXXRecordDecl::friend_begin() const { + return friend_iterator(data().FirstFriend); +} + +inline CXXRecordDecl::friend_iterator CXXRecordDecl::friend_end() const { + return friend_iterator(0); +} + +inline void CXXRecordDecl::pushFriendDecl(FriendDecl *FD) { + assert(FD->NextFriend == 0 && "friend already has next friend?"); + FD->NextFriend = data().FirstFriend; + data().FirstFriend = FD; +} } diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 72b7f49dff..cc743200b1 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -33,7 +33,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) HasTrivialCopyConstructor(true), HasTrivialCopyAssignment(true), HasTrivialDestructor(true), ComputedVisibleConversions(false), Bases(0), NumBases(0), VBases(0), NumVBases(0), - Definition(D) { + Definition(D), FirstFriend(0) { } CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, DeclContext *DC, diff --git a/lib/AST/DeclFriend.cpp b/lib/AST/DeclFriend.cpp index 8c7cadfbf7..ab3552db28 100644 --- a/lib/AST/DeclFriend.cpp +++ b/lib/AST/DeclFriend.cpp @@ -35,5 +35,7 @@ FriendDecl *FriendDecl::Create(ASTContext &C, DeclContext *DC, } #endif - return new (C) FriendDecl(DC, L, Friend, FriendL); + FriendDecl *FD = new (C) FriendDecl(DC, L, Friend, FriendL); + cast(DC)->pushFriendDecl(FD); + return FD; } diff --git a/lib/Sema/SemaAccess.cpp b/lib/Sema/SemaAccess.cpp index eca8bb4c2a..85c4662846 100644 --- a/lib/Sema/SemaAccess.cpp +++ b/lib/Sema/SemaAccess.cpp @@ -16,6 +16,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclFriend.h" #include "clang/AST/ExprCXX.h" using namespace clang; @@ -55,7 +56,7 @@ struct EffectiveContext { explicit EffectiveContext(DeclContext *DC) { if (isa(DC)) { - Function = cast(DC); + Function = cast(DC)->getCanonicalDecl(); DC = Function->getDeclContext(); } else Function = 0; @@ -85,10 +86,34 @@ static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) { static Sema::AccessResult GetFriendKind(Sema &S, const EffectiveContext &EC, const CXXRecordDecl *Class) { + // A class always has access to its own members. if (EC.isClass(Class)) return Sema::AR_accessible; - // FIXME: implement + // Okay, check friends. + for (CXXRecordDecl::friend_iterator I = Class->friend_begin(), + E = Class->friend_end(); I != E; ++I) { + FriendDecl *Friend = *I; + + if (Type *T = Friend->getFriendType()) { + if (EC.Record && + S.Context.hasSameType(QualType(T, 0), + S.Context.getTypeDeclType(EC.Record))) + return Sema::AR_accessible; + } else { + NamedDecl *D + = cast(Friend->getFriendDecl()->getCanonicalDecl()); + + // The decl pointers in EC have been canonicalized, so pointer + // equality is sufficient. + if (D == EC.Function || D == EC.Record) + return Sema::AR_accessible; + } + + // FIXME: templates! templated contexts! dependent delay! + } + + // That's it, give up. return Sema::AR_inaccessible; } diff --git a/test/CXX/class.access/class.friend/p1.cpp b/test/CXX/class.access/class.friend/p1.cpp index a06fb2e3e3..e1f2399e7b 100644 --- a/test/CXX/class.access/class.friend/p1.cpp +++ b/test/CXX/class.access/class.friend/p1.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -faccess-control -verify %s // C++'0x [class.friend] p1: // A friend of a class is a function or class that is given permission to use @@ -60,3 +60,52 @@ namespace N { x.g2(); // expected-error{{no member named 'g2' in 'N::X'}} } } + +namespace test0 { + class ClassFriend { + void test(); + }; + + class MemberFriend { + void test(); + }; + + void declared_test(); + + class Class { + static void member(); // expected-note {{declared private here}} + + friend class ClassFriend; + friend class UndeclaredClassFriend; + + friend void undeclared_test(); + friend void declared_test(); + friend void MemberFriend::test(); + }; + + void declared_test() { + Class::member(); + } + + void undeclared_test() { + Class::member(); + } + + void unfriended_test() { + Class::member(); // expected-error {{'member' is a private member of 'test0::Class'}} + } + + void ClassFriend::test() { + Class::member(); + } + + void MemberFriend::test() { + Class::member(); + } + + class UndeclaredClassFriend { + void test() { + Class::member(); + } + }; +} -- 2.40.0