forked from OSchip/llvm-project
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:
parent
e4f361212b
commit
90a1a65194
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -418,7 +418,7 @@ class CXXScopeSpec {
|
|||
void reallocate();
|
||||
|
||||
public:
|
||||
CXXScopeSpec() : NumScopeReps(0), Capacity(4) { }
|
||||
CXXScopeSpec() : Range(), NumScopeReps(0), Capacity(4) { }
|
||||
|
||||
CXXScopeSpec(const CXXScopeSpec &SS);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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];
|
Loading…
Reference in New Issue