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