SemaAccess.cpp revision d60e22e601852ae1345f01514318a0951dc09f89
1//===---- SemaAccess.cpp - C++ Access Control -------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file provides Sema routines for C++ access control semantics.
11//
12//===----------------------------------------------------------------------===//
13
14#include "Sema.h"
15#include "Lookup.h"
16#include "clang/AST/ASTContext.h"
17#include "clang/AST/CXXInheritance.h"
18#include "clang/AST/DeclCXX.h"
19#include "clang/AST/DeclFriend.h"
20#include "clang/AST/ExprCXX.h"
21
22using namespace clang;
23
24/// SetMemberAccessSpecifier - Set the access specifier of a member.
25/// Returns true on error (when the previous member decl access specifier
26/// is different from the new member decl access specifier).
27bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,
28                                    NamedDecl *PrevMemberDecl,
29                                    AccessSpecifier LexicalAS) {
30  if (!PrevMemberDecl) {
31    // Use the lexical access specifier.
32    MemberDecl->setAccess(LexicalAS);
33    return false;
34  }
35
36  // C++ [class.access.spec]p3: When a member is redeclared its access
37  // specifier must be same as its initial declaration.
38  if (LexicalAS != AS_none && LexicalAS != PrevMemberDecl->getAccess()) {
39    Diag(MemberDecl->getLocation(),
40         diag::err_class_redeclared_with_different_access)
41      << MemberDecl << LexicalAS;
42    Diag(PrevMemberDecl->getLocation(), diag::note_previous_access_declaration)
43      << PrevMemberDecl << PrevMemberDecl->getAccess();
44
45    MemberDecl->setAccess(LexicalAS);
46    return true;
47  }
48
49  MemberDecl->setAccess(PrevMemberDecl->getAccess());
50  return false;
51}
52
53namespace {
54struct EffectiveContext {
55  EffectiveContext() : Record(0), Function(0) {}
56
57  explicit EffectiveContext(DeclContext *DC) {
58    if (isa<FunctionDecl>(DC)) {
59      Function = cast<FunctionDecl>(DC)->getCanonicalDecl();
60      DC = Function->getDeclContext();
61    } else
62      Function = 0;
63
64    if (isa<CXXRecordDecl>(DC))
65      Record = cast<CXXRecordDecl>(DC)->getCanonicalDecl();
66    else
67      Record = 0;
68  }
69
70  bool isClass(const CXXRecordDecl *R) const {
71    return R->getCanonicalDecl() == Record;
72  }
73
74  CXXRecordDecl *Record;
75  FunctionDecl *Function;
76};
77}
78
79static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {
80  CXXRecordDecl *DeclaringClass = cast<CXXRecordDecl>(D->getDeclContext());
81  while (DeclaringClass->isAnonymousStructOrUnion())
82    DeclaringClass = cast<CXXRecordDecl>(DeclaringClass->getDeclContext());
83  return DeclaringClass;
84}
85
86static Sema::AccessResult GetFriendKind(Sema &S,
87                                        const EffectiveContext &EC,
88                                        const CXXRecordDecl *Class) {
89  // A class always has access to its own members.
90  if (EC.isClass(Class))
91    return Sema::AR_accessible;
92
93  // Okay, check friends.
94  for (CXXRecordDecl::friend_iterator I = Class->friend_begin(),
95         E = Class->friend_end(); I != E; ++I) {
96    FriendDecl *Friend = *I;
97
98    if (Type *T = Friend->getFriendType()) {
99      if (EC.Record &&
100          S.Context.hasSameType(QualType(T, 0),
101                                S.Context.getTypeDeclType(EC.Record)))
102        return Sema::AR_accessible;
103    } else {
104      NamedDecl *D
105        = cast<NamedDecl>(Friend->getFriendDecl()->getCanonicalDecl());
106
107      // The decl pointers in EC have been canonicalized, so pointer
108      // equality is sufficient.
109      if (D == EC.Function || D == EC.Record)
110        return Sema::AR_accessible;
111    }
112
113    // FIXME: templates! templated contexts! dependent delay!
114  }
115
116  // That's it, give up.
117  return Sema::AR_inaccessible;
118}
119
120/// Finds the best path from the naming class to the declaring class,
121/// taking friend declarations into account.
122///
123/// \return null if friendship is dependent
124static CXXBasePath *FindBestPath(Sema &S,
125                                 const EffectiveContext &EC,
126                                 CXXRecordDecl *Derived,
127                                 CXXRecordDecl *Base,
128                                 CXXBasePaths &Paths) {
129  // Derive the paths to the desired base.
130  bool isDerived = Derived->isDerivedFrom(Base, Paths);
131  assert(isDerived && "derived class not actually derived from base");
132  (void) isDerived;
133
134  CXXBasePath *BestPath = 0;
135
136  // Derive the friend-modified access along each path.
137  for (CXXBasePaths::paths_iterator PI = Paths.begin(), PE = Paths.end();
138         PI != PE; ++PI) {
139
140    // Walk through the path backwards.
141    AccessSpecifier PathAccess = AS_public;
142    CXXBasePath::iterator I = PI->end(), E = PI->begin();
143    while (I != E) {
144      --I;
145
146      AccessSpecifier BaseAccess = I->Base->getAccessSpecifier();
147      if (BaseAccess != AS_public) {
148        switch (GetFriendKind(S, EC, I->Class)) {
149        case Sema::AR_inaccessible: break;
150        case Sema::AR_accessible: BaseAccess = AS_public; break;
151        case Sema::AR_dependent: return 0;
152        case Sema::AR_delayed:
153          llvm_unreachable("friend resolution is never delayed"); break;
154        }
155      }
156
157      PathAccess = CXXRecordDecl::MergeAccess(BaseAccess, PathAccess);
158    }
159
160    // Note that we modify the path's Access field to the
161    // friend-modified access.
162    if (BestPath == 0 || PathAccess < BestPath->Access) {
163      BestPath = &*PI;
164      BestPath->Access = PathAccess;
165    }
166  }
167
168  return BestPath;
169}
170
171/// Diagnose the path which caused the given declaration or base class
172/// to become inaccessible.
173static void DiagnoseAccessPath(Sema &S,
174                               const EffectiveContext &EC,
175                               CXXRecordDecl *NamingClass,
176                               CXXRecordDecl *DeclaringClass,
177                               NamedDecl *D, AccessSpecifier Access) {
178  // Easy case: the decl's natural access determined its path access.
179  // We have to check against AS_private here in case Access is AS_none,
180  // indicating a non-public member of a private base class.
181  //
182  // DependentFriend should be impossible here.
183  if (D && (Access == D->getAccess() || D->getAccess() == AS_private)) {
184    switch (GetFriendKind(S, EC, DeclaringClass)) {
185    case Sema::AR_inaccessible: {
186      S.Diag(D->getLocation(), diag::note_access_natural)
187        << (unsigned) (Access == AS_protected)
188        << /*FIXME: not implicitly*/ 0;
189      return;
190    }
191
192    case Sema::AR_accessible: break;
193
194    case Sema::AR_dependent:
195    case Sema::AR_delayed:
196      llvm_unreachable("dependent/delayed not allowed");
197      return;
198    }
199  }
200
201  CXXBasePaths Paths;
202  CXXBasePath &Path = *FindBestPath(S, EC, NamingClass, DeclaringClass, Paths);
203
204  CXXBasePath::iterator I = Path.end(), E = Path.begin();
205  while (I != E) {
206    --I;
207
208    const CXXBaseSpecifier *BS = I->Base;
209    AccessSpecifier BaseAccess = BS->getAccessSpecifier();
210
211    // If this is public inheritance, or the derived class is a friend,
212    // skip this step.
213    if (BaseAccess == AS_public)
214      continue;
215
216    switch (GetFriendKind(S, EC, I->Class)) {
217    case Sema::AR_accessible: continue;
218    case Sema::AR_inaccessible: break;
219
220    case Sema::AR_dependent:
221    case Sema::AR_delayed:
222      llvm_unreachable("dependent friendship, should not be diagnosing");
223    }
224
225    // Check whether this base specifier is the tighest point
226    // constraining access.  We have to check against AS_private for
227    // the same reasons as above.
228    if (BaseAccess == AS_private || BaseAccess >= Access) {
229
230      // We're constrained by inheritance, but we want to say
231      // "declared private here" if we're diagnosing a hierarchy
232      // conversion and this is the final step.
233      unsigned diagnostic;
234      if (D) diagnostic = diag::note_access_constrained_by_path;
235      else if (I + 1 == Path.end()) diagnostic = diag::note_access_natural;
236      else diagnostic = diag::note_access_constrained_by_path;
237
238      S.Diag(BS->getSourceRange().getBegin(), diagnostic)
239        << BS->getSourceRange()
240        << (BaseAccess == AS_protected)
241        << (BS->getAccessSpecifierAsWritten() == AS_none);
242      return;
243    }
244  }
245
246  llvm_unreachable("access not apparently constrained by path");
247}
248
249/// Diagnose an inaccessible class member.
250static void DiagnoseInaccessibleMember(Sema &S, SourceLocation Loc,
251                                       const EffectiveContext &EC,
252                                       CXXRecordDecl *NamingClass,
253                                       AccessSpecifier Access,
254                                       const Sema::AccessedEntity &Entity) {
255  NamedDecl *D = Entity.getTargetDecl();
256  CXXRecordDecl *DeclaringClass = FindDeclaringClass(D);
257
258  if (isa<CXXConstructorDecl>(D)) {
259    unsigned DiagID = (Access == AS_protected ? diag::err_access_ctor_protected
260                                              : diag::err_access_ctor_private);
261    S.Diag(Loc, DiagID)
262      << S.Context.getTypeDeclType(DeclaringClass);
263  } else {
264    unsigned DiagID = (Access == AS_protected ? diag::err_access_protected
265                                              : diag::err_access_private);
266    S.Diag(Loc, DiagID)
267      << D->getDeclName()
268      << S.Context.getTypeDeclType(DeclaringClass);
269  }
270  DiagnoseAccessPath(S, EC, NamingClass, DeclaringClass, D, Access);
271}
272
273/// Diagnose an inaccessible hierarchy conversion.
274static void DiagnoseInaccessibleBase(Sema &S, SourceLocation Loc,
275                                     const EffectiveContext &EC,
276                                     AccessSpecifier Access,
277                                     const Sema::AccessedEntity &Entity,
278                                     Sema::AccessDiagnosticsKind ADK) {
279  if (ADK == Sema::ADK_covariance) {
280    S.Diag(Loc, diag::err_covariant_return_inaccessible_base)
281      << S.Context.getTypeDeclType(Entity.getDerivedClass())
282      << S.Context.getTypeDeclType(Entity.getBaseClass())
283      << (Access == AS_protected);
284  } else if (Entity.getKind() == Sema::AccessedEntity::BaseToDerivedConversion) {
285    S.Diag(Loc, diag::err_downcast_from_inaccessible_base)
286      << S.Context.getTypeDeclType(Entity.getDerivedClass())
287      << S.Context.getTypeDeclType(Entity.getBaseClass())
288      << (Access == AS_protected);
289  } else {
290    S.Diag(Loc, diag::err_upcast_to_inaccessible_base)
291      << S.Context.getTypeDeclType(Entity.getDerivedClass())
292      << S.Context.getTypeDeclType(Entity.getBaseClass())
293      << (Access == AS_protected);
294  }
295  DiagnoseAccessPath(S, EC, Entity.getDerivedClass(),
296                     Entity.getBaseClass(), 0, Access);
297}
298
299static void DiagnoseBadAccess(Sema &S,
300                              SourceLocation Loc,
301                              const EffectiveContext &EC,
302                              CXXRecordDecl *NamingClass,
303                              AccessSpecifier Access,
304                              const Sema::AccessedEntity &Entity,
305                              Sema::AccessDiagnosticsKind ADK) {
306  if (Entity.isMemberAccess())
307    DiagnoseInaccessibleMember(S, Loc, EC, NamingClass, Access, Entity);
308  else
309    DiagnoseInaccessibleBase(S, Loc, EC, Access, Entity, ADK);
310}
311
312
313/// Try to elevate access using friend declarations.  This is
314/// potentially quite expensive.
315static void TryElevateAccess(Sema &S,
316                             const EffectiveContext &EC,
317                             const Sema::AccessedEntity &Entity,
318                             AccessSpecifier &Access) {
319  CXXRecordDecl *DeclaringClass;
320  if (Entity.isMemberAccess()) {
321    DeclaringClass = FindDeclaringClass(Entity.getTargetDecl());
322  } else {
323    DeclaringClass = Entity.getBaseClass();
324  }
325  CXXRecordDecl *NamingClass = Entity.getNamingClass();
326
327  // Adjust the declaration of the referred entity.
328  AccessSpecifier DeclAccess = AS_none;
329  if (Entity.isMemberAccess()) {
330    NamedDecl *Target = Entity.getTargetDecl();
331
332    DeclAccess = Target->getAccess();
333    if (DeclAccess != AS_public) {
334      switch (GetFriendKind(S, EC, DeclaringClass)) {
335      case Sema::AR_accessible: DeclAccess = AS_public; break;
336      case Sema::AR_inaccessible: break;
337      case Sema::AR_dependent: /* FIXME: delay dependent friendship */ return;
338      case Sema::AR_delayed: llvm_unreachable("friend status is never delayed");
339      }
340    }
341
342    if (DeclaringClass == NamingClass) {
343      Access = DeclAccess;
344      return;
345    }
346  }
347
348  assert(DeclaringClass != NamingClass);
349
350  // Append the declaration's access if applicable.
351  CXXBasePaths Paths;
352  CXXBasePath *Path = FindBestPath(S, EC, Entity.getNamingClass(),
353                                   DeclaringClass, Paths);
354  if (!Path) {
355    // FIXME: delay dependent friendship
356    return;
357  }
358
359  // Grab the access along the best path.
360  AccessSpecifier NewAccess = Path->Access;
361  if (Entity.isMemberAccess())
362    NewAccess = CXXRecordDecl::MergeAccess(NewAccess, DeclAccess);
363
364  assert(NewAccess <= Access && "access along best path worse than direct?");
365  Access = NewAccess;
366}
367
368/// Checks access to an entity from the given effective context.
369static Sema::AccessResult CheckEffectiveAccess(Sema &S,
370                                               const EffectiveContext &EC,
371                                               SourceLocation Loc,
372                                         Sema::AccessedEntity const &Entity,
373                                         Sema::AccessDiagnosticsKind ADK) {
374  AccessSpecifier Access = Entity.getAccess();
375  assert(Access != AS_public);
376
377  CXXRecordDecl *NamingClass = Entity.getNamingClass();
378  while (NamingClass->isAnonymousStructOrUnion())
379    // This should be guaranteed by the fact that the decl has
380    // non-public access.  If not, we should make it guaranteed!
381    NamingClass = cast<CXXRecordDecl>(NamingClass);
382
383  if (!EC.Record) {
384    TryElevateAccess(S, EC, Entity, Access);
385    if (Access == AS_public) return Sema::AR_accessible;
386
387    if (ADK != Sema::ADK_quiet)
388      DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity, ADK);
389    return Sema::AR_inaccessible;
390  }
391
392  // White-list accesses from within the declaring class.
393  if (Access != AS_none && EC.isClass(NamingClass))
394    return Sema::AR_accessible;
395
396  // If the access is worse than 'protected', try to promote to it using
397  // friend declarations.
398  bool TriedElevation = false;
399  if (Access != AS_protected) {
400    TryElevateAccess(S, EC, Entity, Access);
401    if (Access == AS_public) return Sema::AR_accessible;
402    TriedElevation = true;
403  }
404
405  // Protected access.
406  if (Access == AS_protected) {
407    // FIXME: implement [class.protected]p1
408    if (EC.Record->isDerivedFrom(NamingClass))
409      return Sema::AR_accessible;
410
411    // FIXME: delay dependent classes
412  }
413
414  // We're about to reject;  one last chance to promote access.
415  if (!TriedElevation) {
416    TryElevateAccess(S, EC, Entity, Access);
417    if (Access == AS_public) return Sema::AR_accessible;
418  }
419
420  // Okay, that's it, reject it.
421  if (ADK != Sema::ADK_quiet)
422    DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity, ADK);
423  return Sema::AR_inaccessible;
424}
425
426static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc,
427                                      const Sema::AccessedEntity &Entity,
428                                      Sema::AccessDiagnosticsKind ADK
429                                        = Sema::ADK_normal) {
430  // If the access path is public, it's accessible everywhere.
431  if (Entity.getAccess() == AS_public)
432    return Sema::AR_accessible;
433
434  // If we're currently parsing a top-level declaration, delay
435  // diagnostics.  This is the only case where parsing a declaration
436  // can actually change our effective context for the purposes of
437  // access control.
438  if (S.CurContext->isFileContext() && S.ParsingDeclDepth) {
439    assert(ADK == Sema::ADK_normal && "delaying abnormal access check");
440    S.DelayedDiagnostics.push_back(
441        Sema::DelayedDiagnostic::makeAccess(Loc, Entity));
442    return Sema::AR_delayed;
443  }
444
445  return CheckEffectiveAccess(S, EffectiveContext(S.CurContext),
446                              Loc, Entity, ADK);
447}
448
449void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) {
450  // Pretend we did this from the context of the newly-parsed
451  // declaration.
452  EffectiveContext EC(Ctx->getDeclContext());
453
454  if (CheckEffectiveAccess(*this, EC, DD.Loc, DD.AccessData, ADK_normal))
455    DD.Triggered = true;
456}
457
458Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,
459                                                     NamedDecl *D,
460                                                     AccessSpecifier Access) {
461  if (!getLangOptions().AccessControl || !E->getNamingClass())
462    return AR_accessible;
463
464  return CheckAccess(*this, E->getNameLoc(),
465                 AccessedEntity::makeMember(E->getNamingClass(), Access, D));
466}
467
468/// Perform access-control checking on a previously-unresolved member
469/// access which has now been resolved to a member.
470Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E,
471                                                     NamedDecl *D,
472                                                     AccessSpecifier Access) {
473  if (!getLangOptions().AccessControl)
474    return AR_accessible;
475
476  return CheckAccess(*this, E->getMemberLoc(),
477                 AccessedEntity::makeMember(E->getNamingClass(), Access, D));
478}
479
480Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc,
481                                               const RecordType *RT) {
482  if (!getLangOptions().AccessControl)
483    return AR_accessible;
484
485  CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
486  CXXDestructorDecl *Dtor = NamingClass->getDestructor(Context);
487
488  AccessSpecifier Access = Dtor->getAccess();
489  if (Access == AS_public)
490    return AR_accessible;
491
492  return CheckAccess(*this, Loc,
493                 AccessedEntity::makeMember(NamingClass, Access, Dtor));
494}
495
496/// Checks access to a constructor.
497Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc,
498                                  CXXConstructorDecl *Constructor,
499                                  AccessSpecifier Access) {
500  if (!getLangOptions().AccessControl)
501    return AR_accessible;
502
503  CXXRecordDecl *NamingClass = Constructor->getParent();
504  return CheckAccess(*this, UseLoc,
505                 AccessedEntity::makeMember(NamingClass, Access, Constructor));
506}
507
508/// Checks access to an overloaded member operator, including
509/// conversion operators.
510Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc,
511                                                   Expr *ObjectExpr,
512                                                   NamedDecl *MemberOperator,
513                                                   AccessSpecifier Access) {
514  if (!getLangOptions().AccessControl)
515    return AR_accessible;
516
517  const RecordType *RT = ObjectExpr->getType()->getAs<RecordType>();
518  assert(RT && "found member operator but object expr not of record type");
519  CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl());
520
521  return CheckAccess(*this, OpLoc,
522            AccessedEntity::makeMember(NamingClass, Access, MemberOperator));
523}
524
525/// Checks access for a hierarchy conversion.
526///
527/// \param IsBaseToDerived whether this is a base-to-derived conversion (true)
528///     or a derived-to-base conversion (false)
529/// \param ForceCheck true if this check should be performed even if access
530///     control is disabled;  some things rely on this for semantics
531/// \param ForceUnprivileged true if this check should proceed as if the
532///     context had no special privileges
533/// \param ADK controls the kind of diagnostics that are used
534Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc,
535                                              bool IsBaseToDerived,
536                                              QualType Base,
537                                              QualType Derived,
538                                              const CXXBasePath &Path,
539                                              bool ForceCheck,
540                                              bool ForceUnprivileged,
541                                              AccessDiagnosticsKind ADK) {
542  if (!ForceCheck && !getLangOptions().AccessControl)
543    return AR_accessible;
544
545  if (Path.Access == AS_public)
546    return AR_accessible;
547
548  // TODO: preserve the information about which types exactly were used.
549  CXXRecordDecl *BaseD, *DerivedD;
550  BaseD = cast<CXXRecordDecl>(Base->getAs<RecordType>()->getDecl());
551  DerivedD = cast<CXXRecordDecl>(Derived->getAs<RecordType>()->getDecl());
552  AccessedEntity Entity = AccessedEntity::makeBaseClass(IsBaseToDerived,
553                                                        BaseD, DerivedD,
554                                                        Path.Access);
555
556  if (ForceUnprivileged)
557    return CheckEffectiveAccess(*this, EffectiveContext(),
558                                AccessLoc, Entity, ADK);
559  return CheckAccess(*this, AccessLoc, Entity, ADK);
560}
561
562/// Checks access to all the declarations in the given result set.
563void Sema::CheckLookupAccess(const LookupResult &R) {
564  assert(getLangOptions().AccessControl
565         && "performing access check without access control");
566  assert(R.getNamingClass() && "performing access check without naming class");
567
568  for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
569    if (I.getAccess() != AS_public)
570      CheckAccess(*this, R.getNameLoc(),
571                  AccessedEntity::makeMember(R.getNamingClass(),
572                                             I.getAccess(), *I));
573}
574