SemaAccess.cpp revision b020748a9954c995f2e616f50bb9ed4fe2df1f72
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 S.Diag(Loc, Entity.getDiag()) 259 << (Access == AS_protected) 260 << D->getDeclName() 261 << S.Context.getTypeDeclType(NamingClass) 262 << S.Context.getTypeDeclType(DeclaringClass); 263 DiagnoseAccessPath(S, EC, NamingClass, DeclaringClass, D, Access); 264} 265 266/// Diagnose an inaccessible hierarchy conversion. 267static void DiagnoseInaccessibleBase(Sema &S, SourceLocation Loc, 268 const EffectiveContext &EC, 269 AccessSpecifier Access, 270 const Sema::AccessedEntity &Entity) { 271 S.Diag(Loc, Entity.getDiag()) 272 << (Access == AS_protected) 273 << DeclarationName() 274 << S.Context.getTypeDeclType(Entity.getDerivedClass()) 275 << S.Context.getTypeDeclType(Entity.getBaseClass()); 276 DiagnoseAccessPath(S, EC, Entity.getDerivedClass(), 277 Entity.getBaseClass(), 0, Access); 278} 279 280static void DiagnoseBadAccess(Sema &S, SourceLocation Loc, 281 const EffectiveContext &EC, 282 CXXRecordDecl *NamingClass, 283 AccessSpecifier Access, 284 const Sema::AccessedEntity &Entity) { 285 if (Entity.isMemberAccess()) 286 DiagnoseInaccessibleMember(S, Loc, EC, NamingClass, Access, Entity); 287 else 288 DiagnoseInaccessibleBase(S, Loc, EC, Access, Entity); 289} 290 291 292/// Try to elevate access using friend declarations. This is 293/// potentially quite expensive. 294static void TryElevateAccess(Sema &S, 295 const EffectiveContext &EC, 296 const Sema::AccessedEntity &Entity, 297 AccessSpecifier &Access) { 298 CXXRecordDecl *DeclaringClass; 299 if (Entity.isMemberAccess()) { 300 DeclaringClass = FindDeclaringClass(Entity.getTargetDecl()); 301 } else { 302 DeclaringClass = Entity.getBaseClass(); 303 } 304 CXXRecordDecl *NamingClass = Entity.getNamingClass(); 305 306 // Adjust the declaration of the referred entity. 307 AccessSpecifier DeclAccess = AS_none; 308 if (Entity.isMemberAccess()) { 309 NamedDecl *Target = Entity.getTargetDecl(); 310 311 DeclAccess = Target->getAccess(); 312 if (DeclAccess != AS_public) { 313 switch (GetFriendKind(S, EC, DeclaringClass)) { 314 case Sema::AR_accessible: DeclAccess = AS_public; break; 315 case Sema::AR_inaccessible: break; 316 case Sema::AR_dependent: /* FIXME: delay dependent friendship */ return; 317 case Sema::AR_delayed: llvm_unreachable("friend status is never delayed"); 318 } 319 } 320 321 if (DeclaringClass == NamingClass) { 322 Access = DeclAccess; 323 return; 324 } 325 } 326 327 assert(DeclaringClass != NamingClass); 328 329 // Append the declaration's access if applicable. 330 CXXBasePaths Paths; 331 CXXBasePath *Path = FindBestPath(S, EC, Entity.getNamingClass(), 332 DeclaringClass, Paths); 333 if (!Path) { 334 // FIXME: delay dependent friendship 335 return; 336 } 337 338 // Grab the access along the best path. 339 AccessSpecifier NewAccess = Path->Access; 340 if (Entity.isMemberAccess()) 341 NewAccess = CXXRecordDecl::MergeAccess(NewAccess, DeclAccess); 342 343 assert(NewAccess <= Access && "access along best path worse than direct?"); 344 Access = NewAccess; 345} 346 347/// Checks access to an entity from the given effective context. 348static Sema::AccessResult CheckEffectiveAccess(Sema &S, 349 const EffectiveContext &EC, 350 SourceLocation Loc, 351 Sema::AccessedEntity const &Entity) { 352 AccessSpecifier Access = Entity.getAccess(); 353 assert(Access != AS_public); 354 355 CXXRecordDecl *NamingClass = Entity.getNamingClass(); 356 while (NamingClass->isAnonymousStructOrUnion()) 357 // This should be guaranteed by the fact that the decl has 358 // non-public access. If not, we should make it guaranteed! 359 NamingClass = cast<CXXRecordDecl>(NamingClass->getParent()); 360 361 if (!EC.Record) { 362 TryElevateAccess(S, EC, Entity, Access); 363 if (Access == AS_public) return Sema::AR_accessible; 364 365 if (!Entity.isQuiet()) 366 DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity); 367 return Sema::AR_inaccessible; 368 } 369 370 // White-list accesses from within the declaring class. 371 if (Access != AS_none && EC.isClass(NamingClass)) 372 return Sema::AR_accessible; 373 374 // If the access is worse than 'protected', try to promote to it using 375 // friend declarations. 376 bool TriedElevation = false; 377 if (Access != AS_protected) { 378 TryElevateAccess(S, EC, Entity, Access); 379 if (Access == AS_public) return Sema::AR_accessible; 380 TriedElevation = true; 381 } 382 383 // Protected access. 384 if (Access == AS_protected) { 385 // FIXME: implement [class.protected]p1 386 if (EC.Record->isDerivedFrom(NamingClass)) 387 return Sema::AR_accessible; 388 389 // FIXME: delay dependent classes 390 } 391 392 // We're about to reject; one last chance to promote access. 393 if (!TriedElevation) { 394 TryElevateAccess(S, EC, Entity, Access); 395 if (Access == AS_public) return Sema::AR_accessible; 396 } 397 398 // Okay, that's it, reject it. 399 if (!Entity.isQuiet()) 400 DiagnoseBadAccess(S, Loc, EC, NamingClass, Access, Entity); 401 return Sema::AR_inaccessible; 402} 403 404static Sema::AccessResult CheckAccess(Sema &S, SourceLocation Loc, 405 const Sema::AccessedEntity &Entity) { 406 // If the access path is public, it's accessible everywhere. 407 if (Entity.getAccess() == AS_public) 408 return Sema::AR_accessible; 409 410 // If we're currently parsing a top-level declaration, delay 411 // diagnostics. This is the only case where parsing a declaration 412 // can actually change our effective context for the purposes of 413 // access control. 414 if (S.CurContext->isFileContext() && S.ParsingDeclDepth) { 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); 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.getAccessData())) 430 DD.Triggered = true; 431} 432 433Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E, 434 NamedDecl *D, 435 AccessSpecifier Access) { 436 if (!getLangOptions().AccessControl || 437 !E->getNamingClass() || 438 Access == AS_public) 439 return AR_accessible; 440 441 AccessedEntity Entity(AccessedEntity::Member, 442 E->getNamingClass(), Access, D); 443 Entity.setDiag(diag::err_access) << E->getSourceRange(); 444 445 return CheckAccess(*this, E->getNameLoc(), Entity); 446} 447 448/// Perform access-control checking on a previously-unresolved member 449/// access which has now been resolved to a member. 450Sema::AccessResult Sema::CheckUnresolvedMemberAccess(UnresolvedMemberExpr *E, 451 NamedDecl *D, 452 AccessSpecifier Access) { 453 if (!getLangOptions().AccessControl || 454 Access == AS_public) 455 return AR_accessible; 456 457 AccessedEntity Entity(AccessedEntity::Member, 458 E->getNamingClass(), Access, D); 459 Entity.setDiag(diag::err_access) << E->getSourceRange(); 460 461 return CheckAccess(*this, E->getMemberLoc(), Entity); 462} 463 464Sema::AccessResult Sema::CheckDestructorAccess(SourceLocation Loc, 465 CXXDestructorDecl *Dtor, 466 const PartialDiagnostic &PDiag) { 467 if (!getLangOptions().AccessControl) 468 return AR_accessible; 469 470 // There's never a path involved when checking implicit destructor access. 471 AccessSpecifier Access = Dtor->getAccess(); 472 if (Access == AS_public) 473 return AR_accessible; 474 475 CXXRecordDecl *NamingClass = Dtor->getParent(); 476 AccessedEntity Entity(AccessedEntity::Member, NamingClass, Access, Dtor); 477 Entity.setDiag(PDiag); // TODO: avoid copy 478 479 return CheckAccess(*this, Loc, Entity); 480} 481 482/// Checks access to a constructor. 483Sema::AccessResult Sema::CheckConstructorAccess(SourceLocation UseLoc, 484 CXXConstructorDecl *Constructor, 485 AccessSpecifier Access) { 486 if (!getLangOptions().AccessControl || 487 Access == AS_public) 488 return AR_accessible; 489 490 CXXRecordDecl *NamingClass = Constructor->getParent(); 491 AccessedEntity Entity(AccessedEntity::Member, 492 NamingClass, Access, Constructor); 493 Entity.setDiag(diag::err_access_ctor); 494 495 return CheckAccess(*this, UseLoc, Entity); 496} 497 498/// Checks direct (i.e. non-inherited) access to an arbitrary class 499/// member. 500Sema::AccessResult Sema::CheckDirectMemberAccess(SourceLocation UseLoc, 501 NamedDecl *Target, 502 const PartialDiagnostic &Diag) { 503 AccessSpecifier Access = Target->getAccess(); 504 if (!getLangOptions().AccessControl || 505 Access == AS_public) 506 return AR_accessible; 507 508 CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(Target->getDeclContext()); 509 AccessedEntity Entity(AccessedEntity::Member, NamingClass, Access, Target); 510 Entity.setDiag(Diag); 511 return CheckAccess(*this, UseLoc, Entity); 512} 513 514 515/// Checks access to an overloaded member operator, including 516/// conversion operators. 517Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc, 518 Expr *ObjectExpr, 519 Expr *ArgExpr, 520 NamedDecl *MemberOperator, 521 AccessSpecifier Access) { 522 if (!getLangOptions().AccessControl || 523 Access == AS_public) 524 return AR_accessible; 525 526 const RecordType *RT = ObjectExpr->getType()->getAs<RecordType>(); 527 assert(RT && "found member operator but object expr not of record type"); 528 CXXRecordDecl *NamingClass = cast<CXXRecordDecl>(RT->getDecl()); 529 530 AccessedEntity Entity(AccessedEntity::Member, 531 NamingClass, Access, MemberOperator); 532 Entity.setDiag(diag::err_access) 533 << ObjectExpr->getSourceRange() 534 << (ArgExpr ? ArgExpr->getSourceRange() : SourceRange()); 535 536 return CheckAccess(*this, OpLoc, Entity); 537} 538 539/// Checks access for a hierarchy conversion. 540/// 541/// \param IsBaseToDerived whether this is a base-to-derived conversion (true) 542/// or a derived-to-base conversion (false) 543/// \param ForceCheck true if this check should be performed even if access 544/// control is disabled; some things rely on this for semantics 545/// \param ForceUnprivileged true if this check should proceed as if the 546/// context had no special privileges 547/// \param ADK controls the kind of diagnostics that are used 548Sema::AccessResult Sema::CheckBaseClassAccess(SourceLocation AccessLoc, 549 QualType Base, 550 QualType Derived, 551 const CXXBasePath &Path, 552 unsigned DiagID, 553 bool ForceCheck, 554 bool ForceUnprivileged) { 555 if (!ForceCheck && !getLangOptions().AccessControl) 556 return AR_accessible; 557 558 if (Path.Access == AS_public) 559 return AR_accessible; 560 561 CXXRecordDecl *BaseD, *DerivedD; 562 BaseD = cast<CXXRecordDecl>(Base->getAs<RecordType>()->getDecl()); 563 DerivedD = cast<CXXRecordDecl>(Derived->getAs<RecordType>()->getDecl()); 564 565 AccessedEntity Entity(AccessedEntity::Base, BaseD, DerivedD, Path.Access); 566 if (DiagID) 567 Entity.setDiag(DiagID) << Derived << Base; 568 569 if (ForceUnprivileged) 570 return CheckEffectiveAccess(*this, EffectiveContext(), AccessLoc, Entity); 571 return CheckAccess(*this, AccessLoc, Entity); 572} 573 574/// Checks access to all the declarations in the given result set. 575void Sema::CheckLookupAccess(const LookupResult &R) { 576 assert(getLangOptions().AccessControl 577 && "performing access check without access control"); 578 assert(R.getNamingClass() && "performing access check without naming class"); 579 580 for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { 581 if (I.getAccess() != AS_public) { 582 AccessedEntity Entity(AccessedEntity::Member, 583 R.getNamingClass(), I.getAccess(), *I); 584 Entity.setDiag(diag::err_access); 585 586 CheckAccess(*this, R.getNameLoc(), Entity); 587 } 588 } 589} 590