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