]> granicus.if.org Git - clang/commitdiff
Implement non-dependent friend functions and classes.
authorJohn McCall <rjmccall@apple.com>
Wed, 17 Mar 2010 20:01:29 +0000 (20:01 +0000)
committerJohn McCall <rjmccall@apple.com>
Wed, 17 Mar 2010 20:01:29 +0000 (20:01 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@98764 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaAccess.cpp
test/CXX/temp/temp.decls/temp.friend/p1.cpp

index e7ec229d5271420744796090fea0ac7c64a113d5..6c01fee74c32478d290017c2958ad8a95970d3c8 100644 (file)
@@ -93,6 +93,88 @@ static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
   return DeclaringClass;
 }
 
+static Sema::AccessResult MatchesFriend(Sema &S,
+                                        const EffectiveContext &EC,
+                                        const CXXRecordDecl *Friend) {
+  // FIXME: close matches becuse of dependency
+  if (EC.includesClass(Friend))
+    return Sema::AR_accessible;
+
+  return Sema::AR_inaccessible;
+}
+
+static Sema::AccessResult MatchesFriend(Sema &S,
+                                        const EffectiveContext &EC,
+                                        FriendDecl *Friend) {
+  if (Type *T = Friend->getFriendType()) {
+    CanQualType CT = T->getCanonicalTypeUnqualified();
+    if (const RecordType *RT = CT->getAs<RecordType>())
+      return MatchesFriend(S, EC, cast<CXXRecordDecl>(RT->getDecl()));
+
+    // TODO: we can fail early for a lot of type classes.
+    if (T->isDependentType())
+      return Sema::AR_dependent;
+
+    return Sema::AR_inaccessible;
+  }
+
+  NamedDecl *D
+    = cast<NamedDecl>(Friend->getFriendDecl()->getCanonicalDecl());
+
+  // FIXME: declarations with dependent or templated scope.
+
+  // For class templates, we want to check whether any of the records
+  // are possible specializations of the template.
+  if (isa<ClassTemplateDecl>(D)) {
+    for (llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator
+           I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) {
+      CXXRecordDecl *Record = *I;
+      ClassTemplateDecl *CTD;
+
+      // A specialization of the template...
+      if (isa<ClassTemplateSpecializationDecl>(Record)) {
+        CTD = cast<ClassTemplateSpecializationDecl>(Record)
+                ->getSpecializedTemplate();
+
+      // ... or the template pattern itself.
+      } else {
+        CTD = Record->getDescribedClassTemplate();
+      }
+
+      if (CTD && D == CTD->getCanonicalDecl())
+        return Sema::AR_accessible;
+    }
+
+    return Sema::AR_inaccessible;
+  }
+
+  // Same thing for function templates.
+  if (isa<FunctionTemplateDecl>(D)) {
+    if (!EC.Function) return Sema::AR_inaccessible;
+
+    FunctionTemplateDecl *FTD = EC.Function->getPrimaryTemplate();
+    if (!FTD)
+      FTD = EC.Function->getDescribedFunctionTemplate();
+
+    if (FTD && D == FTD->getCanonicalDecl())
+      return Sema::AR_accessible;
+      
+    return Sema::AR_inaccessible;
+  }
+
+  // Friend functions.  FIXME: close matches due to dependency.
+  // 
+  // The decl pointers in EC have been canonicalized, so pointer
+  // equality is sufficient.
+  if (D == EC.Function)
+    return Sema::AR_accessible;
+
+  if (isa<CXXRecordDecl>(D))
+    return MatchesFriend(S, EC, cast<CXXRecordDecl>(D));
+
+  return Sema::AR_inaccessible;
+}
+
 static Sema::AccessResult GetFriendKind(Sema &S,
                                         const EffectiveContext &EC,
                                         const CXXRecordDecl *Class) {
@@ -107,26 +189,20 @@ static Sema::AccessResult GetFriendKind(Sema &S,
          E = Class->friend_end(); I != E; ++I) {
     FriendDecl *Friend = *I;
 
-    if (Type *T = Friend->getFriendType()) {
-      CanQualType CT = T->getCanonicalTypeUnqualified();
-      if (const RecordType *RT = CT->getAs<RecordType>())
-        if (EC.includesClass(cast<CXXRecordDecl>(RT->getDecl())))
-          return Sema::AR_accessible;
-    } else {
-      NamedDecl *D
-        = cast<NamedDecl>(Friend->getFriendDecl()->getCanonicalDecl());
-
-      // The decl pointers in EC have been canonicalized, so pointer
-      // equality is sufficient.
-      if (D == EC.Function)
-        return Sema::AR_accessible;
+    switch (MatchesFriend(S, EC, Friend)) {
+    case Sema::AR_accessible:
+      return Sema::AR_accessible;
 
-      if (isa<CXXRecordDecl>(D) &&
-          EC.includesClass(cast<CXXRecordDecl>(D)))
-        return Sema::AR_accessible;
-    }
+    case Sema::AR_inaccessible:
+      break;
 
-    // FIXME: templates! templated contexts! dependent delay!
+    case Sema::AR_dependent:
+      OnFailure = Sema::AR_dependent;
+      break;
+
+    case Sema::AR_delayed:
+      llvm_unreachable("cannot get delayed answer from MatchesFriend");
+    }
   }
 
   // That's it, give up.
index 7a28e70c9ae67843252e596d89ac1bf19714b45b..f1b3c814c4261a881f9d29121988f20abbbb9392 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm-only %s
+// RUN: %clang_cc1 -faccess-control -verify -emit-llvm-only %s
 
 template <typename T> struct Num {
   T value_;
@@ -54,3 +54,34 @@ int calc2() {
   Num<int> result = x * n;
   return result.get();
 }
+
+// Reduced from GNU <locale>
+namespace test1 {
+  class A {
+    bool b; // expected-note {{declared private here}}
+    template <typename T> friend bool has(const A&);
+  };
+  template <typename T> bool has(const A &x) {
+    return x.b;
+  }
+  template <typename T> bool hasnot(const A &x) {
+    return x.b; // expected-error {{'b' is a private member of 'test1::A'}}
+  }
+}
+
+namespace test2 {
+  class A {
+    bool b; // expected-note {{declared private here}}
+    template <typename T> friend class HasChecker;
+  };
+  template <typename T> class HasChecker {
+    bool check(A *a) {
+      return a->b;
+    }
+  };
+  template <typename T> class HasNotChecker {
+    bool check(A *a) {
+      return a->b; // expected-error {{'b' is a private member of 'test2::A'}}
+    }
+  };
+}