Introduce a new expression type, UnresolvedDeclRefExpr, that describes

dependent qualified-ids such as

  Fibonacci<N - 1>::value

where N is a template parameter. These references are "unresolved"
because the name is dependent and, therefore, cannot be resolved to a
declaration node (as we would do for a DeclRefExpr or
QualifiedDeclRefExpr). UnresolvedDeclRefExprs instantiate to
DeclRefExprs, QualifiedDeclRefExprs, etc.

Also, be a bit more careful about keeping only a single set of
specializations for a class template, and instantiating from the
definition of that template rather than a previous declaration. In
general, we need a better solution for this for all TagDecls, because
it's too easy to accidentally look at a declaration that isn't the
definition.

We can now process a simple Fibonacci computation described as a
template metaprogram.

llvm-svn: 67308
This commit is contained in:
Douglas Gregor 2009-03-19 17:26:29 +00:00
parent e4f361212b
commit 90a1a65194
14 changed files with 296 additions and 15 deletions

View File

@ -697,13 +697,31 @@ public:
/// Declaration of a class template.
class ClassTemplateDecl : public TemplateDecl {
protected:
ClassTemplateDecl(DeclContext *DC, SourceLocation L, DeclarationName Name,
TemplateParameterList *Params, NamedDecl *Decl)
: TemplateDecl(ClassTemplate, DC, L, Name, Params, Decl) { }
/// \brief Data that is common to all of the declarations of a given
/// class template.
struct Common {
/// \brief The class template specializations for this class
/// template, including explicit specializations and instantiations.
llvm::FoldingSet<ClassTemplateSpecializationDecl> Specializations;
};
/// \brief The class template specializations for this class
/// template, including explicit specializations and instantiations.
llvm::FoldingSet<ClassTemplateSpecializationDecl> Specializations;
/// \brief Previous declaration of
ClassTemplateDecl *PreviousDeclaration;
/// \brief Pointer to the data that is common to all of the
/// declarations of this class template.
///
/// The first declaration of a class template (e.g., the declaration
/// with no "previous declaration") owns this pointer.
Common *CommonPtr;
ClassTemplateDecl(DeclContext *DC, SourceLocation L, DeclarationName Name,
TemplateParameterList *Params, NamedDecl *Decl,
ClassTemplateDecl *PrevDecl, Common *CommonPtr)
: TemplateDecl(ClassTemplate, DC, L, Name, Params, Decl),
PreviousDeclaration(PrevDecl), CommonPtr(CommonPtr) { }
~ClassTemplateDecl();
public:
/// Get the underlying class declarations of the template.
@ -711,16 +729,17 @@ public:
return static_cast<CXXRecordDecl *>(TemplatedDecl);
}
/// Create a class teplate node.
/// Create a class template node.
static ClassTemplateDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation L,
DeclarationName Name,
TemplateParameterList *Params,
NamedDecl *Decl);
NamedDecl *Decl,
ClassTemplateDecl *PrevDecl);
/// \brief Retrieve the set of specializations of this class template.
llvm::FoldingSet<ClassTemplateSpecializationDecl> &getSpecializations() {
return Specializations;
return CommonPtr->Specializations;
}
// Implement isa/cast/dyncast support
@ -728,6 +747,8 @@ public:
{ return D->getKind() == ClassTemplate; }
static bool classof(const ClassTemplateDecl *D)
{ return true; }
virtual void Destroy(ASTContext& C);
};
} /* end of namespace clang */

View File

@ -898,6 +898,74 @@ public:
static QualifiedDeclRefExpr* CreateImpl(llvm::Deserializer& D, ASTContext& C);
};
/// \brief A qualified reference to a name whose declaration cannot
/// yet be resolved.
///
/// UnresolvedDeclRefExpr is similar to QualifiedDeclRefExpr in that
/// it expresses a qualified reference to a declaration such as
/// X<T>::value. The difference, however, is that an
/// UnresolvedDeclRefExpr node is used only within C++ templates when
/// the qualification (e.g., X<T>::) refers to a dependent type. In
/// this case, X<T>::value cannot resolve to a declaration because the
/// declaration will differ from on instantiation of X<T> to the
/// next. Therefore, UnresolvedDeclRefExpr keeps track of the qualifier (X<T>::) and the name of the entity being referenced ("value"). Such expressions will instantiate to QualifiedDeclRefExprs.
class UnresolvedDeclRefExpr : public Expr {
/// The name of the entity we will be referencing.
DeclarationName Name;
/// Location of the name of the declaration we're referencing.
SourceLocation Loc;
/// QualifierRange - The source range that covers the
/// nested-name-specifier.
SourceRange QualifierRange;
/// The number of components in the complete nested-name-specifier.
unsigned NumComponents;
UnresolvedDeclRefExpr(DeclarationName N, QualType T, SourceLocation L,
SourceRange R, const NestedNameSpecifier *Components,
unsigned NumComponents);
public:
static UnresolvedDeclRefExpr *Create(ASTContext &Context, DeclarationName N,
SourceLocation L, SourceRange R,
const NestedNameSpecifier *Components,
unsigned NumComponents);
/// \brief Retrieve the name that this expression refers to.
DeclarationName getDeclName() const { return Name; }
/// \brief Retrieve the location of the name within the expression.
SourceLocation getLocation() const { return Loc; }
/// \brief Retrieve the source range of the nested-name-specifier.
SourceRange getQualifierRange() const { return QualifierRange; }
// Iteration over of the parts of the nested-name-specifier.
typedef const NestedNameSpecifier * iterator;
iterator begin() const {
return reinterpret_cast<const NestedNameSpecifier *>(this + 1);
}
iterator end() const { return begin() + NumComponents; }
unsigned size() const { return NumComponents; }
virtual SourceRange getSourceRange() const {
return SourceRange(QualifierRange.getBegin(), getLocation());
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == UnresolvedDeclRefExprClass;
}
static bool classof(const UnresolvedDeclRefExpr *) { return true; }
virtual StmtIterator child_begin();
virtual StmtIterator child_end();
};
} // end namespace clang
#endif

View File

@ -118,6 +118,7 @@ STMT(CXXDeleteExpr , Expr)
STMT(UnresolvedFunctionNameExpr , Expr)
STMT(UnaryTypeTraitExpr , Expr)
STMT(QualifiedDeclRefExpr , DeclRefExpr)
STMT(UnresolvedDeclRefExpr , Expr)
// Obj-C Expressions.
STMT(ObjCStringLiteral , Expr)

View File

@ -418,7 +418,7 @@ class CXXScopeSpec {
void reallocate();
public:
CXXScopeSpec() : NumScopeReps(0), Capacity(4) { }
CXXScopeSpec() : Range(), NumScopeReps(0), Capacity(4) { }
CXXScopeSpec(const CXXScopeSpec &SS);

View File

@ -93,8 +93,31 @@ ClassTemplateDecl *ClassTemplateDecl::Create(ASTContext &C,
SourceLocation L,
DeclarationName Name,
TemplateParameterList *Params,
NamedDecl *Decl) {
return new (C) ClassTemplateDecl(DC, L, Name, Params, Decl);
NamedDecl *Decl,
ClassTemplateDecl *PrevDecl) {
Common *CommonPtr;
if (PrevDecl)
CommonPtr = PrevDecl->CommonPtr;
else
CommonPtr = new (C) Common;
return new (C) ClassTemplateDecl(DC, L, Name, Params, Decl, PrevDecl,
CommonPtr);
}
ClassTemplateDecl::~ClassTemplateDecl() {
assert(CommonPtr == 0 && "ClassTemplateDecl must be explicitly destroyed");
}
void ClassTemplateDecl::Destroy(ASTContext& C) {
if (!PreviousDeclaration) {
CommonPtr->~Common();
C.Deallocate((void*)CommonPtr);
}
CommonPtr = 0;
this->~ClassTemplateDecl();
C.Deallocate((void*)this);
}
//===----------------------------------------------------------------------===//

View File

@ -49,6 +49,29 @@ QualifiedDeclRefExpr::Create(ASTContext &Context, NamedDecl *d, QualType t,
NumComponents);
}
UnresolvedDeclRefExpr::UnresolvedDeclRefExpr(DeclarationName N, QualType T,
SourceLocation L, SourceRange R,
const NestedNameSpecifier *Components,
unsigned NumComponents)
: Expr(UnresolvedDeclRefExprClass, T, true, true),
Name(N), Loc(L), QualifierRange(R), NumComponents(NumComponents) {
NestedNameSpecifier *Data
= reinterpret_cast<NestedNameSpecifier *>(this + 1);
for (unsigned I = 0; I < NumComponents; ++I)
Data[I] = Components[I];
}
UnresolvedDeclRefExpr *
UnresolvedDeclRefExpr::Create(ASTContext &Context, DeclarationName N,
SourceLocation L, SourceRange R,
const NestedNameSpecifier *Components,
unsigned NumComponents) {
void *Mem = Context.Allocate((sizeof(UnresolvedDeclRefExpr) +
sizeof(NestedNameSpecifier) * NumComponents));
return new (Mem) UnresolvedDeclRefExpr(N, Context.DependentTy, L, R,
Components, NumComponents);
}
//===----------------------------------------------------------------------===//
// Child Iterators for iterating over subexpressions/substatements
//===----------------------------------------------------------------------===//
@ -164,6 +187,15 @@ Stmt::child_iterator UnaryTypeTraitExpr::child_end() {
return child_iterator();
}
// UnresolvedDeclRefExpr
StmtIterator UnresolvedDeclRefExpr::child_begin() {
return child_iterator();
}
StmtIterator UnresolvedDeclRefExpr::child_end() {
return child_iterator();
}
bool UnaryTypeTraitExpr::EvaluateTrait() const {
switch(UTT) {
default: assert(false && "Unknown type trait or not implemented");

View File

@ -537,6 +537,11 @@ void StmtPrinter::VisitQualifiedDeclRefExpr(QualifiedDeclRefExpr *Node) {
OS << D->getNameAsString();
}
void StmtPrinter::VisitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *Node) {
NestedNameSpecifier::Print(OS, Node->begin(), Node->end());
OS << Node->getDeclName().getAsString();
}
void StmtPrinter::VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node) {
if (Node->getBase()) {
PrintExpr(Node->getBase());

View File

@ -1455,6 +1455,7 @@ public:
bool RequireCompleteDeclContext(const CXXScopeSpec &SS);
DeclContext *computeDeclContext(const CXXScopeSpec &SS);
bool isDependentScopeSpecifier(const CXXScopeSpec &SS);
/// ActOnCXXGlobalScopeSpecifier - Return the object that represents the
/// global scope ('::').
@ -1880,6 +1881,12 @@ public:
ClassTemplateSpecializationDecl *ClassTemplateSpec,
bool ExplicitInstantiation);
CXXScopeSpec InstantiateScopeSpecifier(const NestedNameSpecifier *Components,
unsigned NumComponents,
SourceRange Range,
const TemplateArgument *TemplateArgs,
unsigned NumTemplateArgs);
// Simple function for cloning expressions.
template<typename T>
OwningExprResult Clone(T *E) {

View File

@ -29,6 +29,21 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS) {
return NNS.computeDeclContext(Context);
}
bool Sema::isDependentScopeSpecifier(const CXXScopeSpec &SS) {
if (!SS.isSet() || SS.isInvalid())
return false;
NestedNameSpecifier NNS
= NestedNameSpecifier::getFromOpaquePtr(SS.getCurrentScopeRep());
if (Type *T = NNS.getAsType())
return T->isDependentType();
// FIXME: What about the injected-class-name of a class template? It
// is dependent, but we represent it as a declaration.
return false;
}
/// \brief Require that the context specified by SS be complete.
///
/// If SS refers to a type, this routine checks whether the type is

View File

@ -52,6 +52,20 @@ std::string Sema::getDeclName(DeclTy *d) {
/// and then return NULL.
Sema::TypeTy *Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
Scope *S, const CXXScopeSpec *SS) {
// C++ [temp.res]p3:
// A qualified-id that refers to a type and in which the
// nested-name-specifier depends on a template-parameter (14.6.2)
// shall be prefixed by the keyword typename to indicate that the
// 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))
return 0;
NamedDecl *IIDecl = 0;
LookupResult Result = LookupParsedName(S, SS, &II, LookupOrdinaryName,
false, false);

View File

@ -613,6 +613,21 @@ Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc,
// Could be enum-constant, value decl, instance variable, etc.
if (SS && SS->isInvalid())
return ExprError();
// C++ [temp.dep.expr]p3:
// An id-expression is type-dependent if it contains:
// -- a nested-name-specifier that contains a class-name that
// names a dependent type.
if (SS && isDependentScopeSpecifier(*SS)) {
llvm::SmallVector<NestedNameSpecifier, 16> Specs;
for (CXXScopeSpec::iterator Spec = SS->begin(), SpecEnd = SS->end();
Spec != SpecEnd; ++Spec)
Specs.push_back(NestedNameSpecifier::getFromOpaquePtr(*Spec));
return Owned(UnresolvedDeclRefExpr::Create(Context, Name, Loc,
SS->getRange(), &Specs[0],
Specs.size()));
}
LookupResult Lookup = LookupParsedName(S, SS, Name, LookupOrdinaryName,
false, true, Loc);

View File

@ -477,7 +477,7 @@ Sema::ActOnClassTemplate(Scope *S, unsigned TagSpec, TagKind TK,
ClassTemplateDecl *NewTemplate
= ClassTemplateDecl::Create(Context, SemanticContext, NameLoc,
DeclarationName(Name), TemplateParams,
NewClass);
NewClass, PrevClassTemplate);
// Set the lexical context of these templates
NewClass->setLexicalDeclContext(CurContext);

View File

@ -625,6 +625,7 @@ namespace {
OwningExprResult VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E);
OwningExprResult VisitConditionalOperator(ConditionalOperator *E);
OwningExprResult VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E);
OwningExprResult VisitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *E);
OwningExprResult VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *E);
OwningExprResult VisitImplicitCastExpr(ImplicitCastExpr *E);
@ -896,6 +897,26 @@ TemplateExprInstantiator::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) {
return move(Result);
}
Sema::OwningExprResult
TemplateExprInstantiator::VisitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *E) {
CXXScopeSpec SS = SemaRef.InstantiateScopeSpecifier(E->begin(), E->size(),
E->getQualifierRange(),
TemplateArgs,
NumTemplateArgs);
if (SS.isInvalid() || SS.isEmpty())
return SemaRef.ExprError();
// FIXME: We're passing in a NULL scope, because
// ActOnDeclarationNameExpr doesn't actually use the scope when we
// give it a non-empty scope specifier. Investigate whether it would
// be better to refactor ActOnDeclarationNameExpr.
return SemaRef.ActOnDeclarationNameExpr(/*Scope=*/0, E->getLocation(),
E->getDeclName(),
/*HasTrailingLParen=*/false,
&SS,
/*FIXME:isAddressOfOperand=*/false);
}
Sema::OwningExprResult
TemplateExprInstantiator::VisitCXXTemporaryObjectExpr(
CXXTemporaryObjectExpr *E) {
@ -1047,7 +1068,9 @@ Sema::InstantiateClassTemplateSpecialization(
// the best template.
ClassTemplateDecl *Template = ClassTemplateSpec->getSpecializedTemplate();
if (!Template->getTemplatedDecl()->getDefinition(Context)) {
RecordDecl *Pattern = cast_or_null<RecordDecl>(
Template->getTemplatedDecl()->getDefinition(Context));
if (!Pattern) {
Diag(ClassTemplateSpec->getLocation(),
diag::err_template_implicit_instantiate_undefined)
<< Context.getTypeDeclType(ClassTemplateSpec);
@ -1084,7 +1107,6 @@ Sema::InstantiateClassTemplateSpecialization(
// FIXME: Create the injected-class-name for the
// instantiation. Should this be a typedef or something like it?
RecordDecl *Pattern = Template->getTemplatedDecl();
llvm::SmallVector<DeclTy *, 32> Fields;
for (RecordDecl::decl_iterator Member = Pattern->decls_begin(),
MemberEnd = Pattern->decls_end();
@ -1118,3 +1140,34 @@ Sema::InstantiateClassTemplateSpecialization(
return Invalid;
}
/// \brief Instantiate a sequence of nested-name-specifiers into a
/// scope specifier.
CXXScopeSpec
Sema::InstantiateScopeSpecifier(const NestedNameSpecifier *Components,
unsigned NumComponents,
SourceRange Range,
const TemplateArgument *TemplateArgs,
unsigned NumTemplateArgs) {
CXXScopeSpec SS;
for (unsigned Comp = 0; Comp < NumComponents; ++Comp) {
if (Type *T = Components[Comp].getAsType()) {
QualType NewT = InstantiateType(QualType(T, 0), TemplateArgs,
NumTemplateArgs, Range.getBegin(),
DeclarationName());
if (NewT.isNull())
return SS;
NestedNameSpecifier NNS(NewT.getTypePtr());
SS.addScopeRep(NNS.getAsOpaquePtr());
} else {
DeclContext *DC = Components[Comp].getAsDeclContext();
// FIXME: injected-class-name might be dependent, and therefore
// would need instantiation.
NestedNameSpecifier NNS(DC);
SS.addScopeRep(NNS.getAsOpaquePtr());
}
}
SS.setRange(Range);
return SS;
}

View File

@ -0,0 +1,27 @@
// RUN: clang -fsyntax-only %s
// FIXME: The Fibonacci/FibonacciEval dance is here to work around our
// inability to parse injected-class-name<template-argument-list>.
template<unsigned I>
struct FibonacciEval;
template<unsigned I>
struct Fibonacci {
enum { value = FibonacciEval<I-1>::value + FibonacciEval<I-2>::value };
};
template<unsigned I>
struct FibonacciEval {
enum { value = Fibonacci<I>::value };
};
template<> struct Fibonacci<0> {
enum { value = 0 };
};
template<> struct Fibonacci<1> {
enum { value = 1 };
};
int array5[Fibonacci<5>::value == 5? 1 : -1];
int array10[Fibonacci<10>::value == 55? 1 : -1];