When starting a C++ member access expression, make sure to compute the

type of the object even when it is dependent. Specifically, this makes
sure that we get the right type for "this->", which is important when
performing name lookup into this scope to determine whether an
identifier or operator-function-id is a template name.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@86060 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Douglas Gregor 2009-11-04 22:49:18 +00:00
Родитель 655fe9b0d9
Коммит 43d8863df9
3 изменённых файлов: 85 добавлений и 64 удалений

Просмотреть файл

@ -22,6 +22,72 @@
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
using namespace clang; using namespace clang;
/// \brief Find the current instantiation that associated with the given type.
static CXXRecordDecl *
getCurrentInstantiationOf(ASTContext &Context, DeclContext *CurContext,
QualType T) {
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: parameter packs)
//
// 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.getCanonicalType(Context.getTypeDeclType(Record)) == T)
return Record;
if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) {
QualType InjectedClassName
= Template->getInjectedClassNameType(Context);
if (T == Context.getCanonicalType(InjectedClassName))
return Template->getTemplatedDecl();
}
// FIXME: check for class template partial specializations
}
return 0;
}
/// \brief Compute the DeclContext that is associated with the given type. /// \brief Compute the DeclContext that is associated with the given type.
/// ///
/// \param T the type for which we are attempting to find a DeclContext. /// \param T the type for which we are attempting to find a DeclContext.
@ -33,7 +99,7 @@ DeclContext *Sema::computeDeclContext(QualType T) {
if (const TagType *Tag = T->getAs<TagType>()) if (const TagType *Tag = T->getAs<TagType>())
return Tag->getDecl(); return Tag->getDecl();
return 0; return ::getCurrentInstantiationOf(Context, CurContext, T);
} }
/// \brief Compute the DeclContext that is associated with the given /// \brief Compute the DeclContext that is associated with the given
@ -156,68 +222,7 @@ CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) {
return 0; return 0;
QualType T = QualType(NNS->getAsType(), 0); QualType T = QualType(NNS->getAsType(), 0);
// If the nested name specifier does not refer to a type, then it return ::getCurrentInstantiationOf(Context, CurContext, T);
// 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: parameter packs)
//
// 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.getCanonicalType(Context.getTypeDeclType(Record)) == T)
return Record;
if (ClassTemplateDecl *Template = Record->getDescribedClassTemplate()) {
QualType InjectedClassName
= Template->getInjectedClassNameType(Context);
if (T == Context.getCanonicalType(InjectedClassName))
return Template->getTemplatedDecl();
}
// FIXME: check for class template partial specializations
}
return 0;
} }
/// \brief Require that the context specified by SS be complete. /// \brief Require that the context specified by SS be complete.

Просмотреть файл

@ -2030,7 +2030,13 @@ Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc,
QualType BaseType = BaseExpr->getType(); QualType BaseType = BaseExpr->getType();
if (BaseType->isDependentType()) { if (BaseType->isDependentType()) {
// FIXME: member of the current instantiation // If we have a pointer to a dependent type and are using the -> operator,
// the object type is the type that the pointer points to. We might still
// have enough information about that type to do something useful.
if (OpKind == tok::arrow)
if (const PointerType *Ptr = BaseType->getAs<PointerType>())
BaseType = Ptr->getPointeeType();
ObjectType = BaseType.getAsOpaquePtr(); ObjectType = BaseType.getAsOpaquePtr();
return move(Base); return move(Base);
} }

Просмотреть файл

@ -93,3 +93,13 @@ void f(X4<X3<int> > x4i) {
X2<sizeof(int)> x2; X2<sizeof(int)> x2;
x4i.f<X2<sizeof(int)> >(x2); x4i.f<X2<sizeof(int)> >(x2);
} }
template<typename T>
struct X5 {
template<typename U>
void f();
void g() {
this->f<T*>();
}
};