diff options
Diffstat (limited to 'lib/Sema/SemaCXXScopeSpec.cpp')
-rw-r--r-- | lib/Sema/SemaCXXScopeSpec.cpp | 102 |
1 files changed, 97 insertions, 5 deletions
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index 5395869a02..bbc1bc52cb 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -13,6 +13,7 @@ #include "Sema.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/Parse/DeclSpec.h" #include "llvm/ADT/STLExtras.h" @@ -26,8 +27,14 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS) { NestedNameSpecifier *NNS = static_cast<NestedNameSpecifier *>(SS.getScopeRep()); - if (NNS->isDependent()) - return 0; + if (NNS->isDependent()) { + // If this nested-name-specifier refers to the current + // instantiation, return its DeclContext. + if (CXXRecordDecl *Record = getCurrentInstantiationOf(NNS)) + return Record; + else + return 0; + } switch (NNS->getKind()) { case NestedNameSpecifier::Identifier: @@ -61,6 +68,90 @@ bool Sema::isDependentScopeSpecifier(const CXXScopeSpec &SS) { return NNS->isDependent(); } +// \brief Determine whether this C++ scope specifier refers to an +// unknown specialization, i.e., a dependent type that is not the +// current instantiation. +bool Sema::isUnknownSpecialization(const CXXScopeSpec &SS) { + if (!isDependentScopeSpecifier(SS)) + return false; + + NestedNameSpecifier *NNS + = static_cast<NestedNameSpecifier *>(SS.getScopeRep()); + return getCurrentInstantiationOf(NNS) == 0; +} + +/// \brief If the given nested name specifier refers to the current +/// instantiation, return the declaration that corresponds to that +/// current instantiation (C++0x [temp.dep.type]p1). +/// +/// \param NNS a dependent nested name specifier. +CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) { + assert(getLangOptions().CPlusPlus && "Only callable in C++"); + assert(NNS->isDependent() && "Only dependent nested-name-specifier allowed"); + + QualType T = QualType(NNS->getAsType(), 0); + // If the nested name specifier does not refer to a type, then it + // does not refer to the current instantiation. + if (T.isNull()) + return 0; + + T = Context.getCanonicalType(T); + + for (DeclContext *Ctx = CurContext; Ctx; Ctx = Ctx->getParent()) { + // If we've hit a namespace or the global scope, then the + // nested-name-specifier can't refer to the current instantiation. + if (Ctx->isFileContext()) + return 0; + + // Skip non-class contexts. + CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Ctx); + if (!Record) + continue; + + // If this record type is not dependent, + if (!Record->isDependentType()) + return 0; + + // C++ [temp.dep.type]p1: + // + // In the definition of a class template, a nested class of a + // class template, a member of a class template, or a member of a + // nested class of a class template, a name refers to the current + // instantiation if it is + // -- the injected-class-name (9) of the class template or + // nested class, + // -- in the definition of a primary class template, the name + // of the class template followed by the template argument + // list of the primary template (as described below) + // enclosed in <>, + // -- in the definition of a nested class of a class template, + // the name of the nested class referenced as a member of + // the current instantiation, or + // -- in the definition of a partial specialization, the name + // of the class template followed by the template argument + // list of the partial specialization enclosed in <>. If + // the nth template parameter is a parameter pack, the nth + // template argument is a pack expansion (14.6.3) whose + // pattern is the name of the parameter pack. (FIXME) + // + // All of these options come down to having the + // nested-name-specifier type that is equivalent to the + // injected-class-name of one of the types that is currently in + // our context. + if (Context.getTypeDeclType(Record) == T) + return Record; + + if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) { + QualType InjectedClassName + = Template->getInjectedClassNameType(Context); + if (T == Context.getCanonicalType(InjectedClassName)) + return Template->getTemplatedDecl(); + } + } + + return 0; +} + /// \brief Require that the context specified by SS be complete. /// /// If SS refers to a type, this routine checks whether the type is @@ -113,9 +204,10 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, NestedNameSpecifier *Prefix = static_cast<NestedNameSpecifier *>(SS.getScopeRep()); - // If the prefix is already dependent, there is no name lookup to - // perform. Just build the resulting nested-name-specifier. - if (Prefix && Prefix->isDependent()) + // If the prefix already refers to an unknown specialization, there + // is no name lookup to perform. Just build the resulting + // nested-name-specifier. + if (Prefix && isUnknownSpecialization(SS)) return NestedNameSpecifier::Create(Context, Prefix, &II); NamedDecl *SD = LookupParsedName(S, &SS, &II, LookupNestedNameSpecifierName); |