From 43d8863df9d02f81acdf5f73fbc288f285bf442e Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 4 Nov 2009 22:49:18 +0000 Subject: [PATCH] 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 --- lib/Sema/SemaCXXScopeSpec.cpp | 131 +++++++++--------- lib/Sema/SemaExprCXX.cpp | 8 +- .../member-template-access-expr.cpp | 10 ++ 3 files changed, 85 insertions(+), 64 deletions(-) diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index 10c138c755..ce3fb5f83c 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -22,6 +22,72 @@ #include "llvm/Support/raw_ostream.h" 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(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. /// /// \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()) return Tag->getDecl(); - return 0; + return ::getCurrentInstantiationOf(Context, CurContext, T); } /// \brief Compute the DeclContext that is associated with the given @@ -156,68 +222,7 @@ CXXRecordDecl *Sema::getCurrentInstantiationOf(NestedNameSpecifier *NNS) { return 0; 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(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; + return ::getCurrentInstantiationOf(Context, CurContext, T); } /// \brief Require that the context specified by SS be complete. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 4868c14835..6fdecc2675 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -2030,7 +2030,13 @@ Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc, QualType BaseType = BaseExpr->getType(); 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()) + BaseType = Ptr->getPointeeType(); + ObjectType = BaseType.getAsOpaquePtr(); return move(Base); } diff --git a/test/SemaTemplate/member-template-access-expr.cpp b/test/SemaTemplate/member-template-access-expr.cpp index 0238cd53c5..567c0d63b0 100644 --- a/test/SemaTemplate/member-template-access-expr.cpp +++ b/test/SemaTemplate/member-template-access-expr.cpp @@ -93,3 +93,13 @@ void f(X4 > x4i) { X2 x2; x4i.f >(x2); } + +template +struct X5 { + template + void f(); + + void g() { + this->f(); + } +}; -- 2.40.0