From 90a1a65194237f0fcd2c4bf847e0e8b4c419061c Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 19 Mar 2009 17:26:29 +0000 Subject: [PATCH] Introduce a new expression type, UnresolvedDeclRefExpr, that describes dependent qualified-ids such as Fibonacci::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 --- clang/include/clang/AST/DeclTemplate.h | 39 ++++++++++--- clang/include/clang/AST/ExprCXX.h | 68 ++++++++++++++++++++++ clang/include/clang/AST/StmtNodes.def | 1 + clang/include/clang/Parse/DeclSpec.h | 2 +- clang/lib/AST/DeclTemplate.cpp | 27 ++++++++- clang/lib/AST/ExprCXX.cpp | 32 ++++++++++ clang/lib/AST/StmtPrinter.cpp | 5 ++ clang/lib/Sema/Sema.h | 7 +++ clang/lib/Sema/SemaCXXScopeSpec.cpp | 15 +++++ clang/lib/Sema/SemaDecl.cpp | 14 +++++ clang/lib/Sema/SemaExpr.cpp | 15 +++++ clang/lib/Sema/SemaTemplate.cpp | 2 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 57 +++++++++++++++++- clang/test/SemaTemplate/fibonacci.cpp | 27 +++++++++ 14 files changed, 296 insertions(+), 15 deletions(-) create mode 100644 clang/test/SemaTemplate/fibonacci.cpp diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 68d93ccc7fe6..4638e6bef436 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -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 Specializations; + }; - /// \brief The class template specializations for this class - /// template, including explicit specializations and instantiations. - llvm::FoldingSet 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(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 &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 */ diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 7a8bbb890a30..1292eaf2d282 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -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::value. The difference, however, is that an +/// UnresolvedDeclRefExpr node is used only within C++ templates when +/// the qualification (e.g., X::) refers to a dependent type. In +/// this case, X::value cannot resolve to a declaration because the +/// declaration will differ from on instantiation of X to the +/// next. Therefore, UnresolvedDeclRefExpr keeps track of the qualifier (X::) 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(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 diff --git a/clang/include/clang/AST/StmtNodes.def b/clang/include/clang/AST/StmtNodes.def index cee17b7dd4fd..211e6062866d 100644 --- a/clang/include/clang/AST/StmtNodes.def +++ b/clang/include/clang/AST/StmtNodes.def @@ -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) diff --git a/clang/include/clang/Parse/DeclSpec.h b/clang/include/clang/Parse/DeclSpec.h index 9c19a1a2d5b0..3a6e6f2cffb6 100644 --- a/clang/include/clang/Parse/DeclSpec.h +++ b/clang/include/clang/Parse/DeclSpec.h @@ -418,7 +418,7 @@ class CXXScopeSpec { void reallocate(); public: - CXXScopeSpec() : NumScopeReps(0), Capacity(4) { } + CXXScopeSpec() : Range(), NumScopeReps(0), Capacity(4) { } CXXScopeSpec(const CXXScopeSpec &SS); diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index ac76c258a71d..7389b83d20d2 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -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); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index f18a28884707..53be84b20d2f 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -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(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"); diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 3a6a01c56861..60300ba04ead 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -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()); diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 3755744126dc..df1e7c38a526 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -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 OwningExprResult Clone(T *E) { diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 156f3a12fbb4..9e1196143b6a 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -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 diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 483b79a0984b..18d55d92f47a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index b15f6ab98440..e43d4b8027ff 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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 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); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index b4e505e7c2fe..fd39a9150f98 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -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); diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 7df9941aa5b9..10f9926b3e7b 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -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( + 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 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; +} diff --git a/clang/test/SemaTemplate/fibonacci.cpp b/clang/test/SemaTemplate/fibonacci.cpp new file mode 100644 index 000000000000..b56d8cdf6938 --- /dev/null +++ b/clang/test/SemaTemplate/fibonacci.cpp @@ -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 +struct FibonacciEval; + +template +struct Fibonacci { + enum { value = FibonacciEval::value + FibonacciEval::value }; +}; + +template +struct FibonacciEval { + enum { value = Fibonacci::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];