forked from OSchip/llvm-project
Implement the notions of the "current instantiation" and "unknown
specialization" within a C++ template, and permit name lookup into the current instantiation. For example, given: template<typename T, typename U> struct X { typedef T type; X* x1; // current instantiation X<T, U> *x2; // current instantiation X<U, T> *x3; // not current instantiation ::X<type, U> *x4; // current instantiation X<typename X<type, U>::type, U>: *x5; // current instantiation }; llvm-svn: 71471
This commit is contained in:
parent
c14b594b14
commit
c9f9b86732
|
@ -408,7 +408,7 @@ public:
|
|||
/// the CXXRecordDecl X<T>::A. When a complete definition of
|
||||
/// X<int>::A is required, it will be instantiated from the
|
||||
/// declaration returned by getInstantiatedFromMemberClass().
|
||||
CXXRecordDecl *getInstantiatedFromMemberClass() {
|
||||
CXXRecordDecl *getInstantiatedFromMemberClass() const {
|
||||
return TemplateOrInstantiation.dyn_cast<CXXRecordDecl*>();
|
||||
}
|
||||
|
||||
|
@ -429,7 +429,7 @@ public:
|
|||
/// CXXRecordDecl that from a ClassTemplateDecl, while
|
||||
/// getDescribedClassTemplate() retrieves the ClassTemplateDecl from
|
||||
/// a CXXRecordDecl.
|
||||
ClassTemplateDecl *getDescribedClassTemplate() {
|
||||
ClassTemplateDecl *getDescribedClassTemplate() const {
|
||||
return TemplateOrInstantiation.dyn_cast<ClassTemplateDecl*>();
|
||||
}
|
||||
|
||||
|
|
|
@ -507,9 +507,10 @@ OverloadedOperatorKind FunctionDecl::getOverloadedOperator() const {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool TagDecl::isDependentType() const {
|
||||
if (isa<TemplateDecl>(this))
|
||||
return true;
|
||||
|
||||
if (const CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(this))
|
||||
if (Record->getDescribedClassTemplate())
|
||||
return true;
|
||||
|
||||
if (const TagDecl *TD = dyn_cast_or_null<TagDecl>(getDeclContext()))
|
||||
return TD->isDependentType();
|
||||
|
||||
|
|
|
@ -1602,6 +1602,8 @@ public:
|
|||
|
||||
DeclContext *computeDeclContext(const CXXScopeSpec &SS);
|
||||
bool isDependentScopeSpecifier(const CXXScopeSpec &SS);
|
||||
CXXRecordDecl *getCurrentInstantiationOf(NestedNameSpecifier *NNS);
|
||||
bool isUnknownSpecialization(const CXXScopeSpec &SS);
|
||||
|
||||
/// ActOnCXXGlobalScopeSpecifier - Return the object that represents the
|
||||
/// global scope ('::').
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -64,16 +64,15 @@ Sema::TypeTy *Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
|
|||
// qualified-id denotes a type, forming an
|
||||
// elaborated-type-specifier (7.1.5.3).
|
||||
//
|
||||
// We therefore do not perform any name lookup up SS is a dependent
|
||||
// scope name. FIXME: we will need to perform a special kind of
|
||||
// lookup if the scope specifier names a member of the current
|
||||
// instantiation.
|
||||
if (SS && isDependentScopeSpecifier(*SS))
|
||||
// We therefore do not perform any name lookup if the result would
|
||||
// refer to a member of an unknown specialization.
|
||||
if (SS && isUnknownSpecialization(*SS))
|
||||
return 0;
|
||||
|
||||
LookupResult Result
|
||||
= LookupParsedName(S, SS, &II, LookupOrdinaryName, false, false);
|
||||
|
||||
NamedDecl *IIDecl = 0;
|
||||
LookupResult Result = LookupParsedName(S, SS, &II, LookupOrdinaryName,
|
||||
false, false);
|
||||
switch (Result.getKind()) {
|
||||
case LookupResult::NotFound:
|
||||
case LookupResult::FoundOverloaded:
|
||||
|
@ -3402,7 +3401,7 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK,
|
|||
}
|
||||
} else if (TK == TK_Reference && SS.isEmpty() && Name &&
|
||||
(Kind != TagDecl::TK_enum || !getLangOptions().CPlusPlus)) {
|
||||
// C.scope.pdecl]p5:
|
||||
// C++ [basic.scope.pdecl]p5:
|
||||
// -- for an elaborated-type-specifier of the form
|
||||
//
|
||||
// class-key identifier
|
||||
|
|
|
@ -941,7 +941,7 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc,
|
|||
DeclPtrTy TagDecl,
|
||||
SourceLocation LBrac,
|
||||
SourceLocation RBrac) {
|
||||
TemplateDecl *Template = AdjustDeclIfTemplate(TagDecl);
|
||||
AdjustDeclIfTemplate(TagDecl);
|
||||
ActOnFields(S, RLoc, TagDecl,
|
||||
(DeclPtrTy*)FieldCollector->getCurFields(),
|
||||
FieldCollector->getCurNumFields(), LBrac, RBrac, 0);
|
||||
|
@ -982,7 +982,7 @@ void Sema::ActOnFinishCXXMemberSpecification(Scope* S, SourceLocation RLoc,
|
|||
}
|
||||
}
|
||||
|
||||
if (!Template)
|
||||
if (!RD->isDependentType())
|
||||
AddImplicitlyDeclaredMembersToClass(RD);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/Parse/DeclSpec.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
|
@ -1117,14 +1118,37 @@ Sema::LookupParsedName(Scope *S, const CXXScopeSpec *SS,
|
|||
DeclarationName Name, LookupNameKind NameKind,
|
||||
bool RedeclarationOnly, bool AllowBuiltinCreation,
|
||||
SourceLocation Loc) {
|
||||
if (SS) {
|
||||
if (SS->isInvalid() || RequireCompleteDeclContext(*SS))
|
||||
if (SS && (SS->isSet() || SS->isInvalid())) {
|
||||
// If the scope specifier is invalid, don't even look for
|
||||
// anything.
|
||||
if (SS->isInvalid())
|
||||
return LookupResult::CreateLookupResult(Context, 0);
|
||||
|
||||
if (SS->isSet()) {
|
||||
return LookupQualifiedName(computeDeclContext(*SS),
|
||||
Name, NameKind, RedeclarationOnly);
|
||||
assert(!isUnknownSpecialization(*SS) && "Can't lookup dependent types");
|
||||
|
||||
if (isDependentScopeSpecifier(*SS)) {
|
||||
// Determine whether we are looking into the current
|
||||
// instantiation.
|
||||
NestedNameSpecifier *NNS
|
||||
= static_cast<NestedNameSpecifier *>(SS->getScopeRep());
|
||||
CXXRecordDecl *Current = getCurrentInstantiationOf(NNS);
|
||||
assert(Current && "Bad dependent scope specifier");
|
||||
|
||||
// We nested name specifier refers to the current instantiation,
|
||||
// so now we will look for a member of the current instantiation
|
||||
// (C++0x [temp.dep.type]).
|
||||
unsigned IDNS = getIdentifierNamespacesFromLookupNameKind(NameKind, true);
|
||||
DeclContext::lookup_iterator I, E;
|
||||
for (llvm::tie(I, E) = Current->lookup(Context, Name); I != E; ++I)
|
||||
if (isAcceptableLookupResult(*I, NameKind, IDNS))
|
||||
return LookupResult::CreateLookupResult(Context, I, E);
|
||||
}
|
||||
|
||||
if (RequireCompleteDeclContext(*SS))
|
||||
return LookupResult::CreateLookupResult(Context, 0);
|
||||
|
||||
return LookupQualifiedName(computeDeclContext(*SS),
|
||||
Name, NameKind, RedeclarationOnly);
|
||||
}
|
||||
|
||||
return LookupName(S, Name, NameKind, RedeclarationOnly,
|
||||
|
@ -1601,4 +1625,3 @@ void Sema::ArgumentDependentLookup(DeclarationName Name,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2170,16 +2170,29 @@ Sema::ActOnTypenameType(SourceLocation TypenameLoc, const CXXScopeSpec &SS,
|
|||
QualType
|
||||
Sema::CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II,
|
||||
SourceRange Range) {
|
||||
if (NNS->isDependent()) // FIXME: member of the current instantiation!
|
||||
return Context.getTypenameType(NNS, &II);
|
||||
CXXRecordDecl *CurrentInstantiation = 0;
|
||||
if (NNS->isDependent()) {
|
||||
CurrentInstantiation = getCurrentInstantiationOf(NNS);
|
||||
|
||||
CXXScopeSpec SS;
|
||||
SS.setScopeRep(NNS);
|
||||
SS.setRange(Range);
|
||||
if (RequireCompleteDeclContext(SS))
|
||||
return QualType();
|
||||
// If the nested-name-specifier does not refer to the current
|
||||
// instantiation, then build a typename type.
|
||||
if (!CurrentInstantiation)
|
||||
return Context.getTypenameType(NNS, &II);
|
||||
}
|
||||
|
||||
DeclContext *Ctx = computeDeclContext(SS);
|
||||
DeclContext *Ctx = 0;
|
||||
|
||||
if (CurrentInstantiation)
|
||||
Ctx = CurrentInstantiation;
|
||||
else {
|
||||
CXXScopeSpec SS;
|
||||
SS.setScopeRep(NNS);
|
||||
SS.setRange(Range);
|
||||
if (RequireCompleteDeclContext(SS))
|
||||
return QualType();
|
||||
|
||||
Ctx = computeDeclContext(SS);
|
||||
}
|
||||
assert(Ctx && "No declaration context?");
|
||||
|
||||
DeclarationName Name(&II);
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// RUN: clang-cc -fsyntax-only -verify %s
|
||||
|
||||
// This test concerns the identity of dependent types within the
|
||||
// canonical type system, specifically focusing on the difference
|
||||
// between members of the current instantiation and membmers of an
|
||||
// unknown specialization. This considers C++ [temp.type], which
|
||||
// specifies type equivalence within a template, and C++0x
|
||||
// [temp.dep.type], which defines what it means to be a member of the
|
||||
// current instantiation.
|
||||
|
||||
template<typename T, typename U>
|
||||
struct X0 {
|
||||
typedef T T_type;
|
||||
typedef U U_type;
|
||||
|
||||
void f0(T&); // expected-note{{previous}}
|
||||
void f0(typename X0::U_type&);
|
||||
void f0(typename X0::T_type&); // expected-error{{redecl}}
|
||||
|
||||
void f1(T&); // expected-note{{previous}}
|
||||
void f1(typename X0::U_type&);
|
||||
void f1(typename X0<T, U>::T_type&); // expected-error{{redecl}}
|
||||
|
||||
void f2(T&); // expected-note{{previous}}
|
||||
void f2(typename X0::U_type&);
|
||||
void f2(typename X0<T_type, U_type>::T_type&); // expected-error{{redecl}}
|
||||
|
||||
void f3(T&); // expected-note{{previous}}
|
||||
void f3(typename X0::U_type&);
|
||||
void f3(typename ::X0<T_type, U_type>::T_type&); // expected-error{{redecl}}
|
||||
|
||||
struct X1 {
|
||||
typedef T my_T_type;
|
||||
|
||||
void g0(T&); // expected-note{{previous}}
|
||||
void g0(typename X0::U_type&);
|
||||
void g0(typename X0::T_type&); // expected-error{{redecl}}
|
||||
|
||||
void g1(T&); // expected-note{{previous}}
|
||||
void g1(typename X0::U_type&);
|
||||
void g1(typename X0<T, U>::T_type&); // expected-error{{redecl}}
|
||||
|
||||
void g2(T&); // expected-note{{previous}}
|
||||
void g2(typename X0::U_type&);
|
||||
void g2(typename X0<T_type, U_type>::T_type&); // expected-error{{redecl}}
|
||||
|
||||
void g3(T&); // expected-note{{previous}}
|
||||
void g3(typename X0::U_type&);
|
||||
void g3(typename ::X0<T_type, U_type>::T_type&); // expected-error{{redecl}}
|
||||
|
||||
void g4(T&); // expected-note{{previous}}
|
||||
void g4(typename X0::U_type&);
|
||||
void g4(typename X1::my_T_type&); // expected-error{{redecl}}
|
||||
|
||||
void g5(T&); // expected-note{{previous}}
|
||||
void g5(typename X0::U_type&);
|
||||
void g5(typename X0::X1::my_T_type&); // expected-error{{redecl}}
|
||||
|
||||
void g6(T&); // expected-note{{previous}}
|
||||
void g6(typename X0::U_type&);
|
||||
void g6(typename X0<T, U>::X1::my_T_type&); // expected-error{{redecl}}
|
||||
|
||||
void g7(T&); // expected-note{{previous}}
|
||||
void g7(typename X0::U_type&);
|
||||
void g7(typename ::X0<typename X1::my_T_type, U_type>::X1::my_T_type&); // expected-error{{redecl}}
|
||||
|
||||
void g8(T&); // expected-note{{previous}}
|
||||
void g8(typename X0<U, T_type>::T_type&);
|
||||
void g8(typename ::X0<typename X0<T_type, U>::X1::my_T_type, U_type>::X1::my_T_type&); // expected-error{{redecl}}
|
||||
};
|
||||
};
|
|
@ -5,7 +5,6 @@
|
|||
// specifies type equivalence within a template.
|
||||
//
|
||||
// FIXME: template template parameters
|
||||
// FIXME: members of the current instantiation
|
||||
|
||||
namespace N {
|
||||
template<typename T>
|
||||
|
|
Loading…
Reference in New Issue