diff options
| author | John McCall <rjmccall@apple.com> | 2010-03-24 05:22:00 +0000 | 
|---|---|---|
| committer | John McCall <rjmccall@apple.com> | 2010-03-24 05:22:00 +0000 | 
| commit | c62bb64c658a92504ec9d9a2c8335e941e67fa71 (patch) | |
| tree | fa83a50f4d35591c59d9b38cb0e7cbba6fe598c8 /clang/lib/Sema | |
| parent | bcf0a47e7ac17500de98dc13ef0d6ad1bcde2802 (diff) | |
| download | bcm5719-llvm-c62bb64c658a92504ec9d9a2c8335e941e67fa71.tar.gz bcm5719-llvm-c62bb64c658a92504ec9d9a2c8335e941e67fa71.zip  | |
Implement a framework for the delay of arbitrary diagnostics within
templates.  So delay access-control diagnostics when (for example) the target
of a friend declaration is a specific specialization of a template.
I was surprised to find that this was required for an access-controlled selfhost.
llvm-svn: 99383
Diffstat (limited to 'clang/lib/Sema')
| -rw-r--r-- | clang/lib/Sema/Sema.h | 6 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaAccess.cpp | 348 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 17 | 
3 files changed, 309 insertions, 62 deletions
diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 23c74a854f6..85fa4f1742a 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -2648,9 +2648,13 @@ public:                                      unsigned DiagID,                                      bool ForceCheck = false,                                      bool ForceUnprivileged = false); -                                void CheckLookupAccess(const LookupResult &R); +  void HandleDependentAccessCheck(const DependentDiagnostic &DD, +                         const MultiLevelTemplateArgumentList &TemplateArgs); +  void PerformDependentDiagnostics(const DeclContext *Pattern, +                        const MultiLevelTemplateArgumentList &TemplateArgs); +    void HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx);    enum AbstractDiagSelID { diff --git a/clang/lib/Sema/SemaAccess.cpp b/clang/lib/Sema/SemaAccess.cpp index 40b320c1be5..e74c8f60c38 100644 --- a/clang/lib/Sema/SemaAccess.cpp +++ b/clang/lib/Sema/SemaAccess.cpp @@ -17,6 +17,7 @@  #include "clang/AST/CXXInheritance.h"  #include "clang/AST/DeclCXX.h"  #include "clang/AST/DeclFriend.h" +#include "clang/AST/DependentDiagnostic.h"  #include "clang/AST/ExprCXX.h"  using namespace clang; @@ -52,9 +53,11 @@ bool Sema::SetMemberAccessSpecifier(NamedDecl *MemberDecl,  namespace {  struct EffectiveContext { -  EffectiveContext() : Function(0) {} +  EffectiveContext() : Function(0), Dependent(false) {}    explicit EffectiveContext(DeclContext *DC) { +    Dependent = DC->isDependentContext(); +      if (isa<FunctionDecl>(DC)) {        Function = cast<FunctionDecl>(DC)->getCanonicalDecl();        DC = Function->getDeclContext(); @@ -75,14 +78,25 @@ struct EffectiveContext {      }    } +  bool isDependent() const { return Dependent; } +    bool includesClass(const CXXRecordDecl *R) const {      R = R->getCanonicalDecl();      return std::find(Records.begin(), Records.end(), R)               != Records.end();    } +  DeclContext *getPrimaryContext() const { +    assert((Function || !Records.empty()) && "context has no primary context"); +    if (Function) return Function; +    return Records[0]; +  } + +  typedef llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator record_iterator; +    llvm::SmallVector<CXXRecordDecl*, 4> Records;    FunctionDecl *Function; +  bool Dependent;  };  } @@ -93,88 +107,234 @@ static CXXRecordDecl *FindDeclaringClass(NamedDecl *D) {    return DeclaringClass;  } +static bool MightInstantiateTo(Sema &S, DeclContext *Context, +                               DeclContext *Friend) { +  if (Friend == Context) +    return true; + +  assert(!Friend->isDependentContext() && +         "can't handle friends with dependent contexts here"); + +  if (!Context->isDependentContext()) +    return false; + +  if (Friend->isFileContext()) +    return false; + +  // TODO: this is very conservative +  return true; +} + +// Asks whether the type in 'context' can ever instantiate to the type +// in 'friend'. +static bool MightInstantiateTo(Sema &S, CanQualType Context, CanQualType Friend) { +  if (Friend == Context) +    return true; + +  if (!Friend->isDependentType() && !Context->isDependentType()) +    return false; + +  // TODO: this is very conservative. +  return true; +} + +static bool MightInstantiateTo(Sema &S, +                               FunctionDecl *Context, +                               FunctionDecl *Friend) { +  if (Context->getDeclName() != Friend->getDeclName()) +    return false; + +  if (!MightInstantiateTo(S, +                          Context->getDeclContext(), +                          Friend->getDeclContext())) +    return false; + +  CanQual<FunctionProtoType> FriendTy +    = S.Context.getCanonicalType(Friend->getType()) +         ->getAs<FunctionProtoType>(); +  CanQual<FunctionProtoType> ContextTy +    = S.Context.getCanonicalType(Context->getType()) +         ->getAs<FunctionProtoType>(); + +  // There isn't any way that I know of to add qualifiers +  // during instantiation. +  if (FriendTy.getQualifiers() != ContextTy.getQualifiers()) +    return false; + +  if (FriendTy->getNumArgs() != ContextTy->getNumArgs()) +    return false; + +  if (!MightInstantiateTo(S, +                          ContextTy->getResultType(), +                          FriendTy->getResultType())) +    return false; + +  for (unsigned I = 0, E = FriendTy->getNumArgs(); I != E; ++I) +    if (!MightInstantiateTo(S, +                            ContextTy->getArgType(I), +                            FriendTy->getArgType(I))) +      return false; + +  return true; +} + +static bool MightInstantiateTo(Sema &S, +                               FunctionTemplateDecl *Context, +                               FunctionTemplateDecl *Friend) { +  return MightInstantiateTo(S, +                            Context->getTemplatedDecl(), +                            Friend->getTemplatedDecl()); +} +  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; +  if (EC.isDependent()) { +    CanQualType FriendTy +      = S.Context.getCanonicalType(S.Context.getTypeDeclType(Friend)); + +    for (EffectiveContext::record_iterator +           I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) { +      CanQualType ContextTy +        = S.Context.getCanonicalType(S.Context.getTypeDeclType(*I)); +      if (MightInstantiateTo(S, ContextTy, FriendTy)) +        return Sema::AR_dependent; +    } +  } +    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; +                                        CanQualType Friend) { +  if (const RecordType *RT = Friend->getAs<RecordType>()) +    return MatchesFriend(S, EC, cast<CXXRecordDecl>(RT->getDecl())); -    return Sema::AR_inaccessible; -  } +  // TODO: we can do better than this +  if (Friend->isDependentType()) +    return Sema::AR_dependent; -  NamedDecl *D -    = cast<NamedDecl>(Friend->getFriendDecl()->getCanonicalDecl()); +  return Sema::AR_inaccessible; +} -  // FIXME: declarations with dependent or templated scope. +/// Determines whether the given friend class template matches +/// anything in the effective context. +static Sema::AccessResult MatchesFriend(Sema &S, +                                        const EffectiveContext &EC, +                                        ClassTemplateDecl *Friend) { +  Sema::AccessResult OnFailure = Sema::AR_inaccessible; -  // 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; +  for (llvm::SmallVectorImpl<CXXRecordDecl*>::const_iterator +         I = EC.Records.begin(), E = EC.Records.end(); I != E; ++I) { +    CXXRecordDecl *Record = *I; -      // A specialization of the template... -      if (isa<ClassTemplateSpecializationDecl>(Record)) { -        CTD = cast<ClassTemplateSpecializationDecl>(Record) -                ->getSpecializedTemplate(); +    // Check whether the friend is the template of a class in the +    // context chain.  To do that, we need to figure out whether the +    // current class has a template: +    ClassTemplateDecl *CTD; -      // ... or the template pattern itself. -      } else { -        CTD = Record->getDescribedClassTemplate(); -      } +    // A specialization of the template... +    if (isa<ClassTemplateSpecializationDecl>(Record)) { +      CTD = cast<ClassTemplateSpecializationDecl>(Record) +        ->getSpecializedTemplate(); -      if (CTD && D == CTD->getCanonicalDecl()) -        return Sema::AR_accessible; +    // ... or the template pattern itself. +    } else { +      CTD = Record->getDescribedClassTemplate(); +      if (!CTD) continue;      } -    return Sema::AR_inaccessible; +    // It's a match. +    if (Friend == CTD->getCanonicalDecl()) +      return Sema::AR_accessible; + +    // If the template names don't match, it can't be a dependent +    // match.  This isn't true in C++0x because of template aliases. +    if (!S.LangOpts.CPlusPlus0x && CTD->getDeclName() != Friend->getDeclName()) +      continue; + +    // If the class's context can't instantiate to the friend's +    // context, it can't be a dependent match. +    if (!MightInstantiateTo(S, CTD->getDeclContext(), +                            Friend->getDeclContext())) +      continue; + +    // Otherwise, it's a dependent match. +    OnFailure = Sema::AR_dependent;    } -  // Same thing for function templates. -  if (isa<FunctionTemplateDecl>(D)) { -    if (!EC.Function) return Sema::AR_inaccessible; +  return OnFailure; +} -    FunctionTemplateDecl *FTD = EC.Function->getPrimaryTemplate(); -    if (!FTD) -      FTD = EC.Function->getDescribedFunctionTemplate(); +/// Determines whether the given friend function matches anything in +/// the effective context. +static Sema::AccessResult MatchesFriend(Sema &S, +                                        const EffectiveContext &EC, +                                        FunctionDecl *Friend) { +  if (!EC.Function) +    return Sema::AR_inaccessible; -    if (FTD && D == FTD->getCanonicalDecl()) -      return Sema::AR_accessible; -       +  if (Friend == EC.Function) +    return Sema::AR_accessible; + +  if (EC.isDependent() && MightInstantiateTo(S, EC.Function, Friend)) +    return Sema::AR_dependent; + +  return Sema::AR_inaccessible; +} + +/// Determines whether the given friend function template matches +/// anything in the effective context. +static Sema::AccessResult MatchesFriend(Sema &S, +                                        const EffectiveContext &EC, +                                        FunctionTemplateDecl *Friend) { +  if (!EC.Function) return Sema::AR_inaccessible; + +  FunctionTemplateDecl *FTD = EC.Function->getPrimaryTemplate(); +  if (!FTD) +    FTD = EC.Function->getDescribedFunctionTemplate(); +  if (!FTD)      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) +  if (Friend == FTD->getCanonicalDecl())      return Sema::AR_accessible; -  if (isa<CXXRecordDecl>(D)) -    return MatchesFriend(S, EC, cast<CXXRecordDecl>(D)); +  if (MightInstantiateTo(S, FTD, Friend)) +    return Sema::AR_dependent;    return Sema::AR_inaccessible;  } +/// Determines whether the given friend declaration matches anything +/// in the effective context. +static Sema::AccessResult MatchesFriend(Sema &S, +                                        const EffectiveContext &EC, +                                        FriendDecl *FriendD) { +  if (Type *T = FriendD->getFriendType()) +    return MatchesFriend(S, EC, T->getCanonicalTypeUnqualified()); + +  NamedDecl *Friend +    = cast<NamedDecl>(FriendD->getFriendDecl()->getCanonicalDecl()); + +  // FIXME: declarations with dependent or templated scope. + +  if (isa<ClassTemplateDecl>(Friend)) +    return MatchesFriend(S, EC, cast<ClassTemplateDecl>(Friend)); + +  if (isa<FunctionTemplateDecl>(Friend)) +    return MatchesFriend(S, EC, cast<FunctionTemplateDecl>(Friend)); + +  if (isa<CXXRecordDecl>(Friend)) +    return MatchesFriend(S, EC, cast<CXXRecordDecl>(Friend)); + +  assert(isa<FunctionDecl>(Friend) && "unknown friend decl kind"); +  return MatchesFriend(S, EC, cast<FunctionDecl>(Friend)); +} +  static Sema::AccessResult GetFriendKind(Sema &S,                                          const EffectiveContext &EC,                                          const CXXRecordDecl *Class) { @@ -230,6 +390,8 @@ static CXXBasePath *FindBestPath(Sema &S,    assert(FinalAccess != AS_none && "forbidden access after declaring class"); +  bool AnyDependent = false; +    // Derive the friend-modified access along each path.    for (CXXBasePaths::paths_iterator PI = Paths.begin(), PE = Paths.end();           PI != PE; ++PI) { @@ -260,7 +422,8 @@ static CXXBasePath *FindBestPath(Sema &S,            PathAccess = AS_public;            break;          case Sema::AR_dependent: -          return 0; +          AnyDependent = true; +          goto Next;          case Sema::AR_delayed:            llvm_unreachable("friend resolution is never delayed"); break;          } @@ -272,9 +435,23 @@ static CXXBasePath *FindBestPath(Sema &S,      if (BestPath == 0 || PathAccess < BestPath->Access) {        BestPath = &*PI;        BestPath->Access = PathAccess; + +      // Short-circuit if we found a public path. +      if (BestPath->Access == AS_public) +        return BestPath;      } + +  Next: ;    } +  assert((!BestPath || BestPath->Access != AS_public) && +         "fell out of loop with public path"); + +  // We didn't find a public path, but at least one path was subject +  // to dependent friendship, so delay the check. +  if (AnyDependent) +    return 0; +    return BestPath;  } @@ -402,7 +579,9 @@ static void DiagnoseBadAccess(Sema &S, SourceLocation Loc,  /// Try to elevate access using friend declarations.  This is  /// potentially quite expensive. -static void TryElevateAccess(Sema &S, +/// +/// \return true if elevation was dependent +static bool TryElevateAccess(Sema &S,                               const EffectiveContext &EC,                               const Sema::AccessedEntity &Entity,                               AccessSpecifier &Access) { @@ -424,14 +603,14 @@ static void TryElevateAccess(Sema &S,        switch (GetFriendKind(S, EC, DeclaringClass)) {        case Sema::AR_accessible: DeclAccess = AS_public; break;        case Sema::AR_inaccessible: break; -      case Sema::AR_dependent: /* FIXME: delay dependent friendship */ return; +      case Sema::AR_dependent: return true;        case Sema::AR_delayed: llvm_unreachable("friend status is never delayed");        }      }      if (DeclaringClass == NamingClass) {        Access = DeclAccess; -      return; +      return false;      }    } @@ -441,16 +620,31 @@ static void TryElevateAccess(Sema &S,    CXXBasePaths Paths;    CXXBasePath *Path = FindBestPath(S, EC, Entity.getNamingClass(),                                     DeclaringClass, DeclAccess, Paths); -  if (!Path) { -    // FIXME: delay dependent friendship -    return; -  } +  if (!Path) +    return true;    // Grab the access along the best path (note that this includes the    // final-step access).    AccessSpecifier NewAccess = Path->Access;    assert(NewAccess <= Access && "access along best path worse than direct?");    Access = NewAccess; +  return false; +} + +static void DelayAccess(Sema &S, +                        const EffectiveContext &EC, +                        SourceLocation Loc, +                        const Sema::AccessedEntity &Entity) { +  assert(EC.isDependent() && "delaying non-dependent access"); +  DeclContext *DC = EC.getPrimaryContext(); +  assert(DC->isDependentContext() && "delaying non-dependent access"); +  DependentDiagnostic::Create(S.Context, DC, DependentDiagnostic::Access, +                              Loc, +                              Entity.isMemberAccess(), +                              Entity.getAccess(), +                              Entity.getTargetDecl(), +                              Entity.getNamingClass(), +                              Entity.getDiag());  }  /// Checks access to an entity from the given effective context. @@ -474,10 +668,13 @@ static Sema::AccessResult CheckEffectiveAccess(Sema &S,      return Sema::AR_accessible;    // Try to elevate access. -  // FIXME: delay if elevation was dependent?    // TODO: on some code, it might be better to do the protected check    // without trying to elevate first. -  TryElevateAccess(S, EC, Entity, Access); +  if (TryElevateAccess(S, EC, Entity, Access)) { +    DelayAccess(S, EC, Loc, Entity); +    return Sema::AR_dependent; +  } +    if (Access == AS_public) return Sema::AR_accessible;    // Protected access. @@ -526,6 +723,35 @@ void Sema::HandleDelayedAccessCheck(DelayedDiagnostic &DD, Decl *Ctx) {      DD.Triggered = true;  } +void Sema::HandleDependentAccessCheck(const DependentDiagnostic &DD, +                        const MultiLevelTemplateArgumentList &TemplateArgs) { +  SourceLocation Loc = DD.getAccessLoc(); +  AccessSpecifier Access = DD.getAccess(); + +  Decl *NamingD = FindInstantiatedDecl(Loc, DD.getAccessNamingClass(), +                                       TemplateArgs); +  if (!NamingD) return; +  Decl *TargetD = FindInstantiatedDecl(Loc, DD.getAccessTarget(), +                                       TemplateArgs); +  if (!TargetD) return; + +  if (DD.isAccessToMember()) { +    AccessedEntity Entity(AccessedEntity::Member, +                          cast<CXXRecordDecl>(NamingD), +                          Access, +                          cast<NamedDecl>(TargetD)); +    Entity.setDiag(DD.getDiagnostic()); +    CheckAccess(*this, Loc, Entity); +  } else { +    AccessedEntity Entity(AccessedEntity::Base, +                          cast<CXXRecordDecl>(TargetD), +                          cast<CXXRecordDecl>(NamingD), +                          Access); +    Entity.setDiag(DD.getDiagnostic()); +    CheckAccess(*this, Loc, Entity); +  } +} +  Sema::AccessResult Sema::CheckUnresolvedLookupAccess(UnresolvedLookupExpr *E,                                                       DeclAccessPair Found) {    if (!getLangOptions().AccessControl || diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index dbe041c4aad..15a79461741 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -15,6 +15,7 @@  #include "clang/AST/ASTContext.h"  #include "clang/AST/DeclTemplate.h"  #include "clang/AST/DeclVisitor.h" +#include "clang/AST/DependentDiagnostic.h"  #include "clang/AST/Expr.h"  #include "clang/AST/ExprCXX.h"  #include "clang/AST/TypeLoc.h" @@ -1839,6 +1840,8 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,    ActOnFinishFunctionBody(DeclPtrTy::make(Function), move(Body),                            /*IsInstantiation=*/true); +  PerformDependentDiagnostics(PatternDecl, TemplateArgs); +    CurContext = PreviousContext;    DeclGroupRef DG(Function); @@ -2475,3 +2478,17 @@ void Sema::PerformPendingImplicitInstantiations(bool LocalOnly) {      InstantiateStaticDataMemberDefinition(/*FIXME:*/Inst.second, Var, true);    }  } + +void Sema::PerformDependentDiagnostics(const DeclContext *Pattern, +                       const MultiLevelTemplateArgumentList &TemplateArgs) { +  for (DeclContext::ddiag_iterator I = Pattern->ddiag_begin(), +         E = Pattern->ddiag_end(); I != E; ++I) { +    DependentDiagnostic *DD = *I; + +    switch (DD->getKind()) { +    case DependentDiagnostic::Access: +      HandleDependentAccessCheck(*DD, TemplateArgs); +      break; +    } +  } +}  | 

